Hello,
Sorry I had to post here because I dont have the rights to post yet.
Starting with some context, Im on an internship in a small company in Morocco and Im required to grab data from some sensors (for beehives) and send them through a gateway to a network server (TTN which a guy here told me) using LoraWAN (which i know nothing of).
I read a whole 150 pages LoRaWAN book from a university in the alliance, I understood how it operates and everything. And this week I’ve been trying to move to practice. What the company has is HopeRF RFM95 LoRa transceiver modules (plug onto arudino UNOs) and dragino OLG02 gateways.
This week, I first registered my gateway on TTN under the EU863-870 frequency plan and configured the IoT server on my gateway.(It connected)
Primary LoRaWAN Server
Service Provider
Custom / Private LoRaWAN
Server Address
eu1.cloud.thethings.network
Uplink Port
1700
Downlink Port
1700
I then manually configured my gateway (since it has only 2 channels one for TX and one for RX) like in the picture below.
Then I moved to my arduino uno, i plugged the rfm95 on the default spi connections (10-13), and the plugged the DIO0-2 on 2,3,4.
The activation method im using is OTAA.
Im using the latest MCCI LMIC library, and I went to try my setup on the ttn-otaa example of the library.
Since my gateway has hardcore limitations (only one downlink fixed channel, one uplink fixed channel), I manually setup in the code 1 uplink channel on 868.1,SF7Dr and 14 dBm transmit power and setup the RX2 window freq on 869.525 and datarate to SF9.
I also correctly modified the …_config.h :
// project-specific definitions
#define CFG_eu868 1
//#define CFG_us915 1
//#define CFG_au915 1
//#define CFG_as923 1
//#define LMIC_COUNTRY_CODE LMIC_COUNTRY_CODE_JP /* for as923-JP; also define CFG_as923 */
//#define CFG_kr920 1
//#define CFG_in866 1
//#define CFG_sx1272_radio 1
#define CFG_sx1276_radio 1
//#define LMIC_USE_INTERRUPTS
#define DISABLE_PING // Used in class B, useless for A
#define DISABLE_BEACONS //same
#define LMIC_ENABLE_arbitrary_clock_error 1
I’ll come to why I did somethings later. But at that time, I defined the CFG_eu868 and the sx1276 radio (since my hoperf got a rf96 on it, i didnt find othing in the datasheet about sx1276, but chatgpt told me rf96 indicates that it’s a sx1276 ^^')
Here is my code :
/*******************************************************************************
* Copyright (c) 2015 Thomas Telkamp and Matthijs Kooijman
* Copyright (c) 2018 Terry Moore, MCCI
*
* Permission is hereby granted, free of charge, to anyone
* obtaining a copy of this document and accompanying files,
* to do whatever they want with them without any restriction,
* including, but not limited to, copying, modification and redistribution.
* NO WARRANTY OF ANY KIND IS PROVIDED.
*
* This example sends a valid LoRaWAN packet with payload "Hello,
* world!", using frequency and encryption settings matching those of
* the The Things Network.
*
* This uses OTAA (Over-the-air activation), where where a DevEUI and
* application key is configured, which are used in an over-the-air
* activation procedure where a DevAddr and session keys are
* assigned/generated for use with all further communication.
*
* Note: LoRaWAN per sub-band duty-cycle limitation is enforced (1% in
* g1, 0.1% in g2), but not the TTN fair usage policy (which is probably
* violated by this sketch when left running for longer)!
* To use this sketch, first register your application and device with
* the things network, to set or generate an AppEUI, DevEUI and AppKey.
* Multiple devices can use the same AppEUI, but each device has its own
* DevEUI and AppKey.
*
* Do not forget to define the radio type correctly in
* arduino-lmic/project_config/lmic_project_config.h or from your BOARDS.txt.
*
*******************************************************************************/
#include <lmic.h>
#include <hal/hal.h>
#include <SPI.h>
//
// For normal use, we require that you edit the sketch to replace FILLMEIN
// with values assigned by the TTN console. However, for regression tests,
// we want to be able to compile these scripts. The regression tests define
// COMPILE_REGRESSION_TEST, and in that case we define FILLMEIN to a non-
// working but innocuous value.
//
#ifdef COMPILE_REGRESSION_TEST
# define FILLMEIN 0
#else
# warning "You must replace the values marked FILLMEIN with real values from the TTN control panel!"
# define FILLMEIN (#dont edit this, edit the lines that use FILLMEIN)
#endif
// This EUI must be in little-endian format, so least-significant-byte
// first. When copying an EUI from ttnctl output, this means to reverse
// the bytes. For TTN issued EUIs the last bytes should be 0xD5, 0xB3,
// 0x70.
static const u1_t PROGMEM APPEUI[8]={0x34, 0x12, 0x2F, 0xA7, 0x49, 0xB5, 0xBD, 0xC1};
void os_getArtEui (u1_t* buf) { memcpy_P(buf, APPEUI, 8);}
// This should also be in little endian format, see above.
static const u1_t PROGMEM DEVEUI[8]={0x01, 0x8B, 0x06, 0xD0, 0x7E, 0xD5, 0xB3, 0x70};
void os_getDevEui (u1_t* buf) { memcpy_P(buf, DEVEUI, 8);}
// This key should be in big endian format (or, since it is not really a
// number but a block of memory, endianness does not really apply). In
// practice, a key taken from ttnctl can be copied as-is.
static const u1_t PROGMEM APPKEY[16] = {0x22, 0x7F, 0xBA, 0x65, 0xA1, 0xCF, 0xA3, 0x80, 0x07, 0xFE, 0x6A, 0x9C, 0x3B, 0xDA, 0xCC, 0x04};
void os_getDevKey (u1_t* buf) { memcpy_P(buf, APPKEY, 16);}
static uint8_t mydata[] = "Hello, world!";
static osjob_t sendjob;
// Schedule TX every this many seconds (might become longer due to duty
// cycle limitations).
const unsigned TX_INTERVAL = 120;
// Pin mapping
const lmic_pinmap lmic_pins = {
.nss = 10,
.rxtx = LMIC_UNUSED_PIN,
.rst = 5,
.dio = {2, 3, 4},
};
void printHex2(unsigned v) {
v &= 0xff;
if (v < 16)
Serial.print('0');
Serial.print(v, HEX);
}
void onEvent (ev_t ev) {
Serial.print(os_getTime());
Serial.print(": ");
switch(ev) {
case EV_SCAN_TIMEOUT:
Serial.println(F("EV_SCAN_TIMEOUT"));
break;
case EV_BEACON_FOUND:
Serial.println(F("EV_BEACON_FOUND"));
break;
case EV_BEACON_MISSED:
Serial.println(F("EV_BEACON_MISSED"));
break;
case EV_BEACON_TRACKED:
Serial.println(F("EV_BEACON_TRACKED"));
break;
case EV_JOINING:
Serial.println(F("EV_JOINING"));
break;
case EV_JOINED:
Serial.println(F("EV_JOINED"));
{
u4_t netid = 0;
devaddr_t devaddr = 0;
u1_t nwkKey[16];
u1_t artKey[16];
LMIC_getSessionKeys(&netid, &devaddr, nwkKey, artKey);
Serial.print("netid: ");
Serial.println(netid, DEC);
Serial.print("devaddr: ");
Serial.println(devaddr, HEX);
Serial.print("AppSKey: ");
for (size_t i=0; i<sizeof(artKey); ++i) {
if (i != 0)
Serial.print("-");
printHex2(artKey[i]);
}
Serial.println("");
Serial.print("NwkSKey: ");
for (size_t i=0; i<sizeof(nwkKey); ++i) {
if (i != 0)
Serial.print("-");
printHex2(nwkKey[i]);
}
Serial.println();
}
// Disable link check validation (automatically enabled
// during join, but because slow data rates change max TX
// size, we don't use it in this example.
LMIC_setLinkCheckMode(0);
break;
/*
|| This event is defined but not used in the code. No
|| point in wasting codespace on it.
||
|| case EV_RFU1:
|| Serial.println(F("EV_RFU1"));
|| break;
*/
case EV_JOIN_FAILED:
Serial.println(F("EV_JOIN_FAILED"));
break;
case EV_REJOIN_FAILED:
Serial.println(F("EV_REJOIN_FAILED"));
break;
case EV_TXCOMPLETE:
Serial.println(F("EV_TXCOMPLETE (includes waiting for RX windows)"));
if (LMIC.txrxFlags & TXRX_ACK)
Serial.println(F("Received ack"));
if (LMIC.dataLen) {
Serial.print(F("Received "));
Serial.print(LMIC.dataLen);
Serial.println(F(" bytes of payload"));
}
// Schedule next transmission
os_setTimedCallback(&sendjob, os_getTime()+sec2osticks(TX_INTERVAL), do_send);
break;
case EV_LOST_TSYNC:
Serial.println(F("EV_LOST_TSYNC"));
break;
case EV_RESET:
Serial.println(F("EV_RESET"));
break;
case EV_RXCOMPLETE:
// data received in ping slot
Serial.println(F("EV_RXCOMPLETE"));
break;
case EV_LINK_DEAD:
Serial.println(F("EV_LINK_DEAD"));
break;
case EV_LINK_ALIVE:
Serial.println(F("EV_LINK_ALIVE"));
break;
/*
|| This event is defined but not used in the code. No
|| point in wasting codespace on it.
||
|| case EV_SCAN_FOUND:
|| Serial.println(F("EV_SCAN_FOUND"));
|| break;
*/
case EV_TXSTART:
Serial.println(F("EV_TXSTART"));
break;
case EV_TXCANCELED:
Serial.println(F("EV_TXCANCELED"));
break;
case EV_RXSTART:
/* do not print anything -- it wrecks timing */
break;
case EV_JOIN_TXCOMPLETE:
Serial.println(F("EV_JOIN_TXCOMPLETE: no JoinAccept"));
break;
default:
Serial.print(F("Unknown event: "));
Serial.println((unsigned) ev);
break;
}
}
void do_send(osjob_t* j){
// Check if there is not a current TX/RX job running
if (LMIC.opmode & OP_TXRXPEND) {
Serial.println(F("OP_TXRXPEND, not sending"));
} else {
// Prepare upstream data transmission at the next possible time.
LMIC_setTxData2(1, mydata, sizeof(mydata)-1, 0);
Serial.println(F("Packet queued"));
}
// Next TX is scheduled after TX_COMPLETE event.
}
void setup() {
Serial.begin(9600);
Serial.println(F("Starting"));
// interrupt setup
//pinMode(2, INPUT_PULLUP);
//pinMode(3, INPUT_PULLUP);
LMIC_setClockError(MAX_CLOCK_ERROR * 2 / 100);
// LMIC init
os_init();
// Reset the MAC state. Session and pending data transfers will be discarded.
LMIC_reset();
for (int channel = 0; channel < 9; channel++) {
if (channel == 0) {
LMIC_setupChannel(channel, 868100000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI);
} else {
LMIC_disableChannel(channel);
}
}
LMIC_setDrTxpow(DR_SF7, 14);
LMIC.dn2Dr = DR_SF9; // Set downlink datarate for RX2 window (commonly SF12 in EU868)
LMIC.dn2Freq = 869525000;
// Start job (sending automatically starts OTAA too)
do_send(&sendjob);
}
void loop() {
os_runloop_once();
}
I also
I’ll give the chronological orders of events and what I did down below :
-First upload of the code onto my arduino, I got to see an uplink on my gateway on RXTX logread of my gateway, I was happy ^^. So a forward join accept downlink message has been issued by TTN.
- First upset, I dont get any JoinAccepts on my arduino. I first thought there was something wrong with my code, downlink frequency or data rate, but I removed the doubt by seeing it on the frequency plan and also by thinking that if it’s properly (corresponding values) setup on the gateway and in my code then it should work.
-I kept trying uplinks, second upset! My gateway rarely catches my uplinks, I didnt get the motivation to sit yet and keep sending uplinks and see at what rate does my gateway hold onto my uplinks. But what I know for sure is that they are rarely caught (not as rare from what i’ve seen on my ttn history, an average of 6mins between each uplink caught, and it’s not a signal power problem (all the uplinks caught and forwarded to TTN are of a mid quality average SNR of 10 and average RSSI of 65 at a distance of 5m no walls, I mean there is a wall if we draw a straight line but if we take a small curve there is a door ^^). I also dont think it’s a duty cycle issue, cuz I believe that is handled on the hardware side and server side, and that my gateway should be able to intercept every packet sent on that fixed channel by my hardware.
I also stand to say that nobody in my region uses gateways and lorawan (a traditional agriculture area), and I just came to know why OLG02 gateways are bad for the community thanks to this thread. But I am sure that Im not messing with somebodies packets (according to the CEO) and also seen the history of the gateway through the days, nothin except my packets are received.
This said, my gateway shouldnt be busy to the point it catches 1 uplink in a long period and the others are lost.
- I ignored that problem and focused on getting a join accept with those rarely caught uplinks, so I went to see when the “no JoinAccept” is sent to me on the arduino and when does the downlink show on my gateways logread, I found out that a downlink pops on my gateway waaaaaaaaay before the noJoinAccept is sent on my arduino serial monitor, after some research that led to me to the conclusion that it’s a Timing problem.
-So I went to the LMIC library README and went to the Timing part and read it, I read it once and didnt do deeper research to comprehend how it exactly works. But lets say I understood what’s written and nthin deeper than that, so I went on desperately trying the different solutions (that’s why I got the enable arbitrary to disable the clockerr being capped at maxError) like the setClockError, enabling interrupts instead of polling events from the pins, disabling the different features that are not used for class A devices. All for the hope of getting the timing right, and getting that JoinAccept.
Now I ran out of options , and Im seeking help from wise people. I also want to say that im just an intern at a company, so im supposed to make things work with the given material. If I also were to suggest to buy new material (seen that gateways that are not LoraWAN compliant are hated ^^), my internship would come to an end (not stopped) but I just wouldn’t have nothing to do and nothing to say on my internship report (no positive results).
I would appreciate any help and recommandations.