This shows you the differences between two versions of the page.
| Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
| project:lora_nodes [2018/02/25 12:31] – dp | project:lora_nodes [2018/05/11 10:54] (current) – dp | ||
|---|---|---|---|
| Line 1: | Line 1: | ||
| ====== LoRa TTN node setup ====== | ====== LoRa TTN node setup ====== | ||
| + | Osnovni setup za LoRa TTN nodes... za Heltec i TTGO module, al vjerojatno i za druge. | ||
| - | ===== Arduino | + | ===== Arduino |
| - | Install | + | Instaliraj |
| * https:// | * https:// | ||
| - | Install ESP32 Arduino | + | Instaliraj |
| * https:// | * https:// | ||
| ===== The Things Network account ===== | ===== The Things Network account ===== | ||
| - | Create | + | Kreiraj |
| * https:// | * https:// | ||
| - | Go to TTN Applications | + | Pod TTN Applications |
| * https:// | * https:// | ||
| - | Register Device | + | Registriraj novi device. |
| ===== Example code ===== | ===== Example code ===== | ||
| - | ==== Download example and install missing libraries | + | ==== Spremi primjer |
| - | <file cpp otaa_example.ino> | + | <file cpp otaa_abp_example.ino> |
| #include < | #include < | ||
| #include < | #include < | ||
| Line 34: | Line 36: | ||
| U8X8_SSD1306_128X64_NONAME_SW_I2C u8x8(/* clock=*/ 15, /* data=*/ 4, /* reset=*/ 16); | U8X8_SSD1306_128X64_NONAME_SW_I2C u8x8(/* clock=*/ 15, /* data=*/ 4, /* reset=*/ 16); | ||
| - | // This EUI must be in little-endian format, so least-significant-byte | + | // Schedule TX every this many seconds (might become longer due to duty |
| - | // first. When copying an EUI from ttnctl output, | + | // cycle limitations). |
| - | // the bytes. For TTN issued EUIs the last bytes should be 0xD5, 0xB3, | + | const unsigned TX_INTERVAL |
| - | // 0x70. | + | |
| - | static | + | |
| - | void os_getArtEui (u1_t* buf) { | + | |
| - | memcpy_P(buf, | + | |
| - | } | + | |
| - | // This should also be in little endian format, see above. | + | //#define USE_JOINING |
| - | static const u1_t PROGMEM DEVEUI[8]={ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; | + | |
| - | void os_getDevEui (u1_t* buf) { | + | #ifdef USE_JOINING |
| - | memcpy_P(buf, | + | // OTAA join keys |
| - | } | + | // 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] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; | ||
| + | void os_getArtEui (u1_t* buf) { | ||
| + | memcpy_P(buf, | ||
| + | } | ||
| + | |||
| + | | ||
| + | static const u1_t PROGMEM DEVEUI[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; | ||
| + | void os_getDevEui (u1_t* buf) { | ||
| + | memcpy_P(buf, | ||
| + | } | ||
| + | |||
| + | // 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. | ||
| + | // The key shown here is the semtech default key. | ||
| + | static const u1_t PROGMEM APPKEY[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; | ||
| + | void os_getDevKey (u1_t* buf) { | ||
| + | memcpy_P(buf, | ||
| + | } | ||
| + | |||
| + | #else | ||
| + | // ABP keys | ||
| + | |||
| + | // LoRaWAN NwkSKey, network session key (msb) | ||
| + | static const PROGMEM u1_t NWKSKEY[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; | ||
| + | |||
| + | // LoRaWAN AppSKey, application session key (msb) | ||
| + | static const u1_t PROGMEM APPSKEY[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; | ||
| + | |||
| + | // LoRaWAN end-device address (DevAddr) | ||
| + | static const u4_t DEVADDR = 0xffffffff; | ||
| + | |||
| + | void os_getArtEui (u1_t* buf) { } | ||
| + | void os_getDevEui (u1_t* buf) { } | ||
| + | void os_getDevKey (u1_t* buf) { } | ||
| + | |||
| + | #endif | ||
| - | // 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. | ||
| - | // The key shown here is the semtech default key. | ||
| - | static const u1_t PROGMEM APPKEY[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; | ||
| - | void os_getDevKey (u1_t* buf) { | ||
| - | memcpy_P(buf, | ||
| - | } | ||
| - | static uint8_t mydata[] = " | + | static uint8_t mydata[] = {13, 37}; |
| static osjob_t sendjob; | static osjob_t sendjob; | ||
| - | // Schedule TX every this many seconds (might become longer due to duty | ||
| - | // cycle limitations). | ||
| - | const unsigned TX_INTERVAL = 60; | ||
| // Pin mapping | // Pin mapping | ||
| Line 79: | Line 105: | ||
| Serial.print(": | Serial.print(": | ||
| switch (ev) { | switch (ev) { | ||
| - | case EV_SCAN_TIMEOUT: | + | |
| - | Serial.println(F(" | + | Serial.println(F(" |
| - | u8x8.drawString(0, | + | u8x8.drawString(0, |
| - | break; | + | break; |
| - | case EV_BEACON_FOUND: | + | case EV_BEACON_FOUND: |
| - | Serial.println(F(" | + | Serial.println(F(" |
| - | u8x8.drawString(0, | + | u8x8.drawString(0, |
| - | break; | + | break; |
| - | case EV_BEACON_MISSED: | + | case EV_BEACON_MISSED: |
| - | Serial.println(F(" | + | Serial.println(F(" |
| - | u8x8.drawString(0, | + | u8x8.drawString(0, |
| - | break; | + | break; |
| - | case EV_BEACON_TRACKED: | + | case EV_BEACON_TRACKED: |
| - | Serial.println(F(" | + | Serial.println(F(" |
| - | u8x8.drawString(0, | + | u8x8.drawString(0, |
| - | break; | + | break; |
| - | case EV_JOINING: | + | case EV_JOINING: |
| - | Serial.println(F(" | + | Serial.println(F(" |
| - | u8x8.drawString(0, | + | u8x8.drawString(0, |
| - | break; | + | break; |
| - | case EV_JOINED: | + | case EV_JOINED: |
| - | Serial.println(F(" | + | Serial.println(F(" |
| - | u8x8.drawString(0, | + | u8x8.drawString(0, |
| - | LMIC_setDrTxpow(DR_SF12, 14); //added fixed SF after join for longer range messages | + | LMIC_setDrTxpow(DR_SF7, 14); //added fixed SF after join for longer range messages |
| - | // Disable link check validation (automatically enabled | + | // Disable link check validation (automatically enabled |
| - | // during join, but not supported by TTN at this time). | + | // during join, but not supported by TTN at this time). |
| - | LMIC_setLinkCheckMode(0); | + | LMIC_setLinkCheckMode(0); |
| - | break; | + | break; |
| - | case EV_RFU1: | + | case EV_RFU1: |
| - | Serial.println(F(" | + | Serial.println(F(" |
| - | u8x8.drawString(0, | + | u8x8.drawString(0, |
| - | break; | + | break; |
| - | case EV_JOIN_FAILED: | + | case EV_JOIN_FAILED: |
| - | Serial.println(F(" | + | Serial.println(F(" |
| - | u8x8.drawString(0, | + | u8x8.drawString(0, |
| - | break; | + | break; |
| - | case EV_REJOIN_FAILED: | + | case EV_REJOIN_FAILED: |
| - | Serial.println(F(" | + | Serial.println(F(" |
| - | u8x8.drawString(0, | + | u8x8.drawString(0, |
| - | //break; | + | //break; |
| - | break; | + | break; |
| - | case EV_TXCOMPLETE: | + | case EV_TXCOMPLETE: |
| - | Serial.println(F(" | + | Serial.println(F(" |
| - | u8x8.drawString(0, | + | u8x8.drawString(0, |
| - | digitalWrite(BUILTIN_LED, | + | digitalWrite(BUILTIN_LED, |
| - | if (LMIC.txrxFlags & TXRX_ACK) { | + | if (LMIC.txrxFlags & TXRX_ACK) { |
| - | Serial.println(F(" | + | Serial.println(F(" |
| - | u8x8.drawString(0, | + | u8x8.drawString(0, |
| - | } | + | } |
| - | if (LMIC.dataLen) { | + | if (LMIC.dataLen) { |
| - | Serial.println(F(" | + | Serial.println(F(" |
| - | u8x8.drawString(0, | + | u8x8.drawString(0, |
| - | Serial.println(LMIC.dataLen); | + | Serial.println(LMIC.dataLen); |
| - | u8x8.setCursor(4, | + | u8x8.setCursor(4, |
| - | u8x8.printf(" | + | u8x8.printf(" |
| - | Serial.println(F(" | + | Serial.println(F(" |
| - | u8x8.setCursor(0, | + | u8x8.setCursor(0, |
| - | u8x8.printf(" | + | u8x8.printf(" |
| - | } | + | } |
| - | // Schedule next transmission | + | // Schedule next transmission |
| - | os_setTimedCallback(& | + | os_setTimedCallback(& |
| - | break; | + | break; |
| - | case EV_LOST_TSYNC: | + | case EV_LOST_TSYNC: |
| - | Serial.println(F(" | + | Serial.println(F(" |
| - | u8x8.drawString(0, | + | u8x8.drawString(0, |
| - | break; | + | break; |
| - | case EV_RESET: | + | case EV_RESET: |
| - | Serial.println(F(" | + | Serial.println(F(" |
| - | u8x8.drawString(0, | + | u8x8.drawString(0, |
| - | break; | + | break; |
| - | case EV_RXCOMPLETE: | + | case EV_RXCOMPLETE: |
| - | // data received in ping slot | + | // data received in ping slot |
| - | Serial.println(F(" | + | Serial.println(F(" |
| - | u8x8.drawString(0, | + | u8x8.drawString(0, |
| - | break; | + | break; |
| - | case EV_LINK_DEAD: | + | case EV_LINK_DEAD: |
| - | Serial.println(F(" | + | Serial.println(F(" |
| - | u8x8.drawString(0, | + | u8x8.drawString(0, |
| - | break; | + | break; |
| - | case EV_LINK_ALIVE: | + | case EV_LINK_ALIVE: |
| - | Serial.println(F(" | + | Serial.println(F(" |
| - | u8x8.drawString(0, | + | u8x8.drawString(0, |
| - | break; | + | break; |
| - | default: | + | default: |
| - | Serial.println(F(" | + | Serial.println(F(" |
| - | u8x8.setCursor(0, | + | u8x8.setCursor(0, |
| - | u8x8.printf(" | + | u8x8.printf(" |
| - | break; | + | break; |
| } | } | ||
| } | } | ||
| Line 173: | Line 199: | ||
| // Check if there is not a current TX/RX job running | // Check if there is not a current TX/RX job running | ||
| if (LMIC.opmode & OP_TXRXPEND) { | if (LMIC.opmode & OP_TXRXPEND) { | ||
| - | Serial.println(F(" | + | |
| - | u8x8.drawString(0, | + | u8x8.drawString(0, |
| } else { | } else { | ||
| - | // Prepare upstream data transmission at the next possible time. | + | |
| - | LMIC_setTxData2(1, | + | LMIC_setTxData2(1, |
| - | Serial.println(F(" | + | Serial.println(F(" |
| - | u8x8.drawString(0, | + | u8x8.drawString(0, |
| - | digitalWrite(BUILTIN_LED, | + | digitalWrite(BUILTIN_LED, |
| } | } | ||
| // Next TX is scheduled after TX_COMPLETE event. | // Next TX is scheduled after TX_COMPLETE event. | ||
| } | } | ||
| + | |||
| void setup() { | void setup() { | ||
| Line 191: | Line 218: | ||
| u8x8.begin(); | u8x8.begin(); | ||
| u8x8.setFont(u8x8_font_chroma48medium8_r); | u8x8.setFont(u8x8_font_chroma48medium8_r); | ||
| - | u8x8.drawString(0, | + | u8x8.drawString(0, |
| SPI.begin(5, | SPI.begin(5, | ||
| Line 199: | Line 226: | ||
| // Reset the MAC state. Session and pending data transfers will be discarded. | // Reset the MAC state. Session and pending data transfers will be discarded. | ||
| LMIC_reset(); | LMIC_reset(); | ||
| - | LMIC_setDrTxpow(DR_SF12, 14); //set join at SF12 | + | |
| - | // Start job (sending automatically starts OTAA too) | + | #ifndef USE_JOINING |
| - | | + | #ifdef PROGMEM |
| + | // On AVR, these values are stored in flash and only copied to RAM | ||
| + | // once. Copy them to a temporary buffer here, LMIC_setSession will | ||
| + | // copy them into a buffer of its own again. | ||
| + | uint8_t appskey[sizeof(APPSKEY)]; | ||
| + | uint8_t nwkskey[sizeof(NWKSKEY)]; | ||
| + | memcpy_P(appskey, APPSKEY, sizeof(APPSKEY)); | ||
| + | memcpy_P(nwkskey, | ||
| + | LMIC_setSession (0x1, DEVADDR, nwkskey, appskey); | ||
| + | #else | ||
| + | // If not running an AVR with PROGMEM, just use the arrays directly | ||
| + | LMIC_setSession (0x1, DEVADDR, NWKSKEY, APPSKEY); | ||
| + | #endif | ||
| + | #endif | ||
| + | |||
| + | // Set up the channels used by the Things Network, which corresponds | ||
| + | // to the defaults of most gateways. Without this, only three base | ||
| + | // channels from the LoRaWAN specification are used, which certainly | ||
| + | // works, so it is good for debugging, but can overload those | ||
| + | // frequencies, | ||
| + | // your network here (unless your network autoconfigures them). | ||
| + | // Setting up channels should happen after LMIC_setSession, | ||
| + | | ||
| + | // NA-US channels 0-71 are configured automatically | ||
| + | LMIC_setupChannel(0, | ||
| + | LMIC_setupChannel(1, | ||
| + | LMIC_setupChannel(2, | ||
| + | LMIC_setupChannel(3, | ||
| + | LMIC_setupChannel(4, | ||
| + | LMIC_setupChannel(5, | ||
| + | LMIC_setupChannel(6, | ||
| + | LMIC_setupChannel(7, | ||
| + | LMIC_setupChannel(8, | ||
| + | // TTN defines an additional channel | ||
| + | // devices' | ||
| + | // frequency and support for class B is spotty and untested, so this | ||
| + | // frequency is not configured here. | ||
| + | |||
| + | // Disable link check validation | ||
| + | // | ||
| + | |||
| + | | ||
| + | // | ||
| + | |||
| + | |||
| + | LMIC_setDrTxpow(DR_SF7, 14); //set join at SF12 | ||
| pinMode(BUILTIN_LED, | pinMode(BUILTIN_LED, | ||
| digitalWrite(BUILTIN_LED, | digitalWrite(BUILTIN_LED, | ||
| + | | ||
| + | // Start job (sending automatically starts OTAA too) | ||
| + | do_send(& | ||
| } | } | ||
| Line 210: | Line 285: | ||
| os_runloop_once(); | os_runloop_once(); | ||
| } | } | ||
| + | |||
| </ | </ | ||
| - | ==== Copy EUI & keys from console to code: ==== | + | ==== Za ABP mode ==== |
| - | + | Za korištenje APB u settinzima devicea na TTNu treba označiti APB te će onda biti prikazani i network session key i app session key (kopiraju se u defaultnom '' | |
| + | |||
| + | ==== Za OTTA mode ==== | ||
| + | OTAA mode (sa joinanjem) se enejbla otkomentiravanjem ''"# | ||
| + | |||
| + | Kopraj EUI i ključeve s TTN konzole u kod: | ||
| * Device EUI '' | * Device EUI '' | ||
| * Application EUI '' | * Application EUI '' | ||
| * App Key '' | * App Key '' | ||
| - | ==== Change spreding | + | ==== Podesi spreading |
| Spreading Factor (SF #). The higher the SF (i.e. the slower the transmission), | Spreading Factor (SF #). The higher the SF (i.e. the slower the transmission), | ||
| - | Gain? (second number). | + | Gain? (drugi broj). |
| - | Change in all lines! | + | Promjeni u svim linijama! |
| * za stacionarni node npr '' | * za stacionarni node npr '' | ||
| * za mapiranje iz vožnje '' | * za mapiranje iz vožnje '' | ||
| - | ==== Upload code and check ==== | ||
| - | Check the received data in console | + | {{ : |
| + | |||
| + | LoRa Modem Packet formatting | ||
| + | {{ : | ||
| + | |||
| + | ==== Instaliraj librarije koji nedostaju ==== | ||
| + | Sketch / Include Library / Manage Libraries ... '' | ||
| + | |||
| + | ==== Upload-aj kod ==== | ||
| + | U Arduino IDE-u pod '' | ||
| + | ==== Provjeri primljene poruke u konzoli ==== | ||
| * https:// | * https:// | ||
| Line 237: | Line 327: | ||
| [[https:// | [[https:// | ||
| - | Za pristup pojedinom node-u (korigiraj datum): | + | |
| - | | + | * https:// |
| + | |||
| + | [[https:// | ||
| - | Za pristup pojedinom gateway-u: | + | ===== Random links ===== |
| - | * https://ttnmapper.org/colour-radar/? | + | * https://lcd-web.nl/ttngenerator/ |
| - | Android aplikacija (korištenje GPS-a telefona i linkanje s LoRa node-om) | ||
| - | * https:// | ||