Unterschiede
Hier werden die Unterschiede zwischen zwei Versionen angezeigt.
Beide Seiten der vorigen RevisionVorhergehende ÜberarbeitungNächste Überarbeitung | Vorhergehende Überarbeitung | ||
thethingsnetwork:esp32_mit_868_mhz_lora_modul [2017/12/22 21:04] – Konfiguration PlatformIO octoate | thethingsnetwork:esp32_mit_868_mhz_lora_modul [2018/04/01 15:30] (aktuell) – [TTN mit OTAA] Code eingefügt octoate | ||
---|---|---|---|
Zeile 24: | Zeile 24: | ||
Will man das Board unter PlatformIO nutzen, so legt man einfach ein neues Projekt an und wählt dort " | Will man das Board unter PlatformIO nutzen, so legt man einfach ein neues Projekt an und wählt dort " | ||
+ | |||
+ | ==== OLED Display ansteuern ==== | ||
+ | |||
+ | Um das eingebaute OLED Display anzusteuern, | ||
+ | |||
+ | pio lib install u8g2 | ||
+ | | ||
+ | im PlatformIO Terminal, wird die entsprechende Bibliothek installiert. | ||
+ | |||
+ | Jetzt einfach noch den folgenden Sketch reinkopieren und schon sieht man das "Hello World!" | ||
+ | |||
+ | {{: | ||
+ | |||
+ | <code c 1 main.cpp> | ||
+ | #include < | ||
+ | #include < | ||
+ | |||
+ | #define OLED_SCL 15 // GPIO 15 | ||
+ | #define OLED_SDA | ||
+ | #define OLED_RST 16 // GPIO 16 | ||
+ | |||
+ | // define the display type that we use | ||
+ | U8X8_SSD1306_128X64_NONAME_SW_I2C u8x8(/* clock=*/ OLED_SCL, /* data=*/ OLED_SDA, /* reset=*/ OLED_RST); | ||
+ | |||
+ | void setup() { | ||
+ | // set up the display | ||
+ | u8x8.begin(); | ||
+ | u8x8.setPowerSave(0); | ||
+ | } | ||
+ | |||
+ | void loop() { | ||
+ | // Yay... a "Hello World!" | ||
+ | u8x8.setFont(u8x8_font_chroma48medium8_r); | ||
+ | u8x8.drawString(0, | ||
+ | delay(2000); | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ==== TTN mit ABP ==== | ||
+ | |||
+ | Nachdem das Display ja schon läuft, ist es an der Zeit das Modul mit dem TTN zu verbinden. Ich habe für mein momentan zum Testen genutztes Single-Channel-Gateway erstmal nur ABP (Activation By Personalization) zur Anmeldung am Netzwerk verwendet. Da ich kein Fan von den Single-Channel-Gateways bin, werde ich aber einen Sketch zu OTAA (Over The Air Activation) noch nachreichen. | ||
+ | |||
+ | Zuerst muss die Arduino LMIC Bibliothek von IBM eingebunden werden. Das geht unter PlatformIO mit folgendem Befehl: | ||
+ | |||
+ | pio lib install 852 | ||
+ | | ||
+ | Die ID für die Arduino LMIC Bibliothek (ID = 852) kann man mit folgendem Befehl herausfinden: | ||
+ | |||
+ | pio lib search lmic | ||
+ | | ||
+ | Der dazugehörige Sketch sendet einfach fortlaufend nummerierte Pakete mit dem Inhalt " | ||
+ | Auf dem Modul sieht das dann so aus: | ||
+ | |||
+ | {{: | ||
+ | |||
+ | <code c 1 main.cpp> | ||
+ | #include < | ||
+ | |||
+ | // OLED | ||
+ | #include < | ||
+ | |||
+ | // LMIC | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | |||
+ | // OLED Pins | ||
+ | #define OLED_SCL 15 // GPIO 15 | ||
+ | #define OLED_SDA | ||
+ | #define OLED_RST 16 // GPIO 16 | ||
+ | |||
+ | // LoRa Pins | ||
+ | #define LoRa_RST | ||
+ | #define LoRa_CS | ||
+ | #define LoRa_DIO0 26 // GPIO 26 | ||
+ | #define LoRa_DIO1 33 // GPIO 33 | ||
+ | #define LoRa_DIO2 32 // GPIO 32 | ||
+ | |||
+ | // define the display type that we use | ||
+ | U8X8_SSD1306_128X64_NONAME_SW_I2C u8x8(/* clock=*/ OLED_SCL, /* data=*/ OLED_SDA, /* reset=*/ OLED_RST); | ||
+ | |||
+ | // LoRaWAN NwkSKey, network session key | ||
+ | static const PROGMEM u1_t NWKSKEY[16] = { 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0x0E, 0xCC, 0x93, 0x3C, 0xAB, 0x0C, 0x24, 0x52 }; | ||
+ | |||
+ | // LoRaWAN AppSKey, application session key | ||
+ | static const u1_t PROGMEM APPSKEY[16] = { 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xE6, 0x65, 0x0E, 0x4E, 0x14, 0x25, 0xF1, 0x5B }; | ||
+ | |||
+ | // LoRaWAN end-device address (DevAddr) | ||
+ | static const u4_t DEVADDR = 0xAAFFAAFF ; | ||
+ | |||
+ | // These callbacks are only used in over-the-air activation, so they are | ||
+ | // left empty here (we cannot leave them out completely unless | ||
+ | // DISABLE_JOIN is set in config.h, otherwise the linker will complain). | ||
+ | void os_getArtEui (u1_t* buf) { } | ||
+ | void os_getDevEui (u1_t* buf) { } | ||
+ | void os_getDevKey (u1_t* buf) { } | ||
+ | |||
+ | static char mydata[14 + 1]; // " | ||
+ | static uint16_t packetNumber = 0; | ||
+ | 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 | ||
+ | const lmic_pinmap lmic_pins = { | ||
+ | .nss = LoRa_CS, | ||
+ | .rxtx = LMIC_UNUSED_PIN, | ||
+ | .rst = LoRa_RST, | ||
+ | .dio = { LoRa_DIO0, LoRa_DIO1, LoRa_DIO2 }, | ||
+ | }; | ||
+ | |||
+ | 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(" | ||
+ | } else { | ||
+ | // Prepare upstream data transmission at the next possible time. | ||
+ | sprintf(mydata, | ||
+ | LMIC_setTxData2(1, | ||
+ | Serial.println(F(" | ||
+ | packetNumber++; | ||
+ | } | ||
+ | // Next TX is scheduled after TX_COMPLETE event. | ||
+ | } | ||
+ | |||
+ | void onEvent(ev_t ev) { | ||
+ | Serial.print(os_getTime()); | ||
+ | Serial.print(": | ||
+ | switch(ev) { | ||
+ | case EV_SCAN_TIMEOUT: | ||
+ | Serial.println(F(" | ||
+ | break; | ||
+ | case EV_BEACON_FOUND: | ||
+ | Serial.println(F(" | ||
+ | break; | ||
+ | case EV_BEACON_MISSED: | ||
+ | Serial.println(F(" | ||
+ | break; | ||
+ | case EV_BEACON_TRACKED: | ||
+ | Serial.println(F(" | ||
+ | break; | ||
+ | case EV_JOINING: | ||
+ | Serial.println(F(" | ||
+ | break; | ||
+ | case EV_JOINED: | ||
+ | Serial.println(F(" | ||
+ | break; | ||
+ | case EV_RFU1: | ||
+ | Serial.println(F(" | ||
+ | break; | ||
+ | case EV_JOIN_FAILED: | ||
+ | Serial.println(F(" | ||
+ | break; | ||
+ | case EV_REJOIN_FAILED: | ||
+ | Serial.println(F(" | ||
+ | break; | ||
+ | case EV_TXCOMPLETE: | ||
+ | Serial.println(F(" | ||
+ | if (LMIC.txrxFlags & TXRX_ACK) | ||
+ | Serial.println(F(" | ||
+ | if (LMIC.dataLen) { | ||
+ | Serial.println(F(" | ||
+ | Serial.println(LMIC.dataLen); | ||
+ | Serial.println(F(" | ||
+ | } | ||
+ | // Schedule next transmission | ||
+ | os_setTimedCallback(& | ||
+ | break; | ||
+ | case EV_LOST_TSYNC: | ||
+ | Serial.println(F(" | ||
+ | break; | ||
+ | case EV_RESET: | ||
+ | Serial.println(F(" | ||
+ | break; | ||
+ | case EV_RXCOMPLETE: | ||
+ | // data received in ping slot | ||
+ | Serial.println(F(" | ||
+ | break; | ||
+ | case EV_LINK_DEAD: | ||
+ | Serial.println(F(" | ||
+ | break; | ||
+ | case EV_LINK_ALIVE: | ||
+ | Serial.println(F(" | ||
+ | break; | ||
+ | | ||
+ | Serial.println(F(" | ||
+ | break; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | void setup() { | ||
+ | // init packet counter | ||
+ | sprintf(mydata, | ||
+ | |||
+ | Serial.begin(115200); | ||
+ | Serial.println(F(" | ||
+ | |||
+ | // set up the display | ||
+ | u8x8.begin(); | ||
+ | u8x8.setPowerSave(0); | ||
+ | |||
+ | // LMIC init | ||
+ | os_init(); | ||
+ | // Reset the MAC state. Session and pending data transfers will be discarded. | ||
+ | LMIC_reset(); | ||
+ | |||
+ | // Set static session parameters. Instead of dynamically establishing a session | ||
+ | // by joining the network, precomputed session parameters are be provided. | ||
+ | #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, | ||
+ | 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 | ||
+ | |||
+ | // 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, | ||
+ | // configures the minimal channel set. | ||
+ | // 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, | ||
+ | |||
+ | // disable channels (only use channel 0 - 868.1 MHz - for my single channel gateway!!!) | ||
+ | for (int channel = 1; channel <= 8; channel++) { | ||
+ | LMIC_disableChannel(channel); | ||
+ | } | ||
+ | |||
+ | // Disable link check validation | ||
+ | LMIC_setLinkCheckMode(0); | ||
+ | |||
+ | // TTN uses SF9 for its RX2 window. | ||
+ | LMIC.dn2Dr = DR_SF9; | ||
+ | |||
+ | // Set data rate and transmit power for uplink (note: txpow seems to be ignored by the library) | ||
+ | LMIC_setDrTxpow(DR_SF7, | ||
+ | |||
+ | // Start job | ||
+ | do_send(& | ||
+ | } | ||
+ | |||
+ | void loop() { | ||
+ | u8x8.setFont(u8x8_font_amstrad_cpc_extended_r); | ||
+ | u8x8.drawString(0, | ||
+ | u8x8.drawString(0, | ||
+ | |||
+ | u8x8.drawString(0, | ||
+ | |||
+ | os_runloop_once(); | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | |||
+ | ==== TTN mit OTAA ==== | ||
+ | Natürlich funktioniert auch die Over The Air Activation (OTAA) mit dem Modul. Zur Nutzung des Sketches müsst ihr die gleichen Bibliotheken installieren, | ||
+ | |||
+ | <code c 1 main.cpp> | ||
+ | #include < | ||
+ | #include < | ||
+ | |||
+ | // OLED | ||
+ | #include < | ||
+ | |||
+ | // LMIC | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | |||
+ | // OLED Pins | ||
+ | #define OLED_SCL 15 // GPIO 15 | ||
+ | #define OLED_SDA | ||
+ | #define OLED_RST 16 // GPIO 16 | ||
+ | |||
+ | // LoRa Pins | ||
+ | #define LoRa_RST | ||
+ | #define LoRa_CS | ||
+ | #define LoRa_DIO0 26 // GPIO 26 | ||
+ | #define LoRa_DIO1 33 // GPIO 33 | ||
+ | #define LoRa_DIO2 32 // GPIO 32 | ||
+ | |||
+ | // define the display type that we use | ||
+ | U8X8_SSD1306_128X64_NONAME_SW_I2C u8x8(/* clock=*/ OLED_SCL, /* data=*/ OLED_SDA, /* reset=*/ OLED_RST); | ||
+ | |||
+ | // 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]= { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; | ||
+ | void os_getArtEui (u1_t* buf) { memcpy_P(buf, | ||
+ | |||
+ | // This should also be in little endian format, see above. | ||
+ | static const u1_t PROGMEM DEVEUI[8]= { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; | ||
+ | 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] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; | ||
+ | void os_getDevKey (u1_t* buf) { memcpy_P(buf, | ||
+ | |||
+ | static uint8_t mydata[] = " | ||
+ | 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 | ||
+ | const lmic_pinmap lmic_pins = { | ||
+ | .nss = LoRa_CS, | ||
+ | .rxtx = LMIC_UNUSED_PIN, | ||
+ | .rst = LoRa_RST, | ||
+ | .dio = { LoRa_DIO0, LoRa_DIO1, LoRa_DIO2 }, | ||
+ | }; | ||
+ | |||
+ | void showDatarate() | ||
+ | { | ||
+ | switch (LMIC.datarate) | ||
+ | { | ||
+ | case DR_SF7: | ||
+ | u8x8.drawString(0, | ||
+ | break; | ||
+ | |||
+ | case DR_SF8: | ||
+ | u8x8.drawString(0, | ||
+ | break; | ||
+ | |||
+ | case DR_SF9: | ||
+ | u8x8.drawString(0, | ||
+ | break; | ||
+ | |||
+ | case DR_SF10: | ||
+ | u8x8.drawString(0, | ||
+ | break; | ||
+ | |||
+ | case DR_SF11: | ||
+ | u8x8.drawString(0, | ||
+ | break; | ||
+ | |||
+ | case DR_SF12: | ||
+ | u8x8.drawString(0, | ||
+ | break; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | void showFrequency() | ||
+ | { | ||
+ | // | ||
+ | char frequency[10]; | ||
+ | itoa(LMIC.freq, | ||
+ | u8x8.drawString(0, | ||
+ | } | ||
+ | |||
+ | 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(" | ||
+ | } else { | ||
+ | showDatarate(); | ||
+ | showFrequency(); | ||
+ | |||
+ | // Prepare upstream data transmission at the next possible time. | ||
+ | LMIC_setTxData2(1, | ||
+ | Serial.println(F(" | ||
+ | } | ||
+ | // Next TX is scheduled after TX_COMPLETE event. | ||
+ | } | ||
+ | |||
+ | void onEvent (ev_t ev) { | ||
+ | Serial.print(os_getTime()); | ||
+ | Serial.print(": | ||
+ | switch(ev) { | ||
+ | case EV_SCAN_TIMEOUT: | ||
+ | Serial.println(F(" | ||
+ | break; | ||
+ | case EV_BEACON_FOUND: | ||
+ | Serial.println(F(" | ||
+ | break; | ||
+ | case EV_BEACON_MISSED: | ||
+ | Serial.println(F(" | ||
+ | break; | ||
+ | case EV_BEACON_TRACKED: | ||
+ | Serial.println(F(" | ||
+ | break; | ||
+ | case EV_JOINING: | ||
+ | Serial.println(F(" | ||
+ | u8x8.drawString(0, | ||
+ | break; | ||
+ | case EV_JOINED: | ||
+ | Serial.println(F(" | ||
+ | |||
+ | // Disable link check validation (automatically enabled | ||
+ | // during join, but not supported by TTN at this time). | ||
+ | u8x8.drawString(0, | ||
+ | LMIC_setLinkCheckMode(0); | ||
+ | break; | ||
+ | case EV_RFU1: | ||
+ | Serial.println(F(" | ||
+ | break; | ||
+ | case EV_JOIN_FAILED: | ||
+ | Serial.println(F(" | ||
+ | break; | ||
+ | case EV_REJOIN_FAILED: | ||
+ | Serial.println(F(" | ||
+ | break; | ||
+ | break; | ||
+ | case EV_TXCOMPLETE: | ||
+ | Serial.println(F(" | ||
+ | if (LMIC.txrxFlags & TXRX_ACK) | ||
+ | Serial.println(F(" | ||
+ | if (LMIC.dataLen) { | ||
+ | Serial.println(F(" | ||
+ | Serial.println(LMIC.dataLen); | ||
+ | Serial.println(F(" | ||
+ | } | ||
+ | // Schedule next transmission | ||
+ | os_setTimedCallback(& | ||
+ | break; | ||
+ | case EV_LOST_TSYNC: | ||
+ | Serial.println(F(" | ||
+ | break; | ||
+ | case EV_RESET: | ||
+ | Serial.println(F(" | ||
+ | break; | ||
+ | case EV_RXCOMPLETE: | ||
+ | // data received in ping slot | ||
+ | Serial.println(F(" | ||
+ | break; | ||
+ | case EV_LINK_DEAD: | ||
+ | Serial.println(F(" | ||
+ | break; | ||
+ | case EV_LINK_ALIVE: | ||
+ | Serial.println(F(" | ||
+ | break; | ||
+ | | ||
+ | Serial.println(F(" | ||
+ | break; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | void setup() { | ||
+ | // set up the display | ||
+ | u8x8.begin(); | ||
+ | u8x8.setPowerSave(0); | ||
+ | u8x8.setFont(u8x8_font_chroma48medium8_r); | ||
+ | |||
+ | Serial.begin(115200); | ||
+ | Serial.println(F(" | ||
+ | |||
+ | #ifdef VCC_ENABLE | ||
+ | // For Pinoccio Scout boards | ||
+ | pinMode(VCC_ENABLE, | ||
+ | digitalWrite(VCC_ENABLE, | ||
+ | delay(1000); | ||
+ | #endif | ||
+ | |||
+ | // LMIC init | ||
+ | os_init(); | ||
+ | |||
+ | // Reset the MAC state. Session and pending data transfers will be discarded. | ||
+ | LMIC_reset(); | ||
+ | LMIC_setClockError(MAX_CLOCK_ERROR * 1 / 100); | ||
+ | // 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, | ||
+ | // configures the minimal channel set. | ||
+ | |||
+ | 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 at 869.525Mhz using SF9 for class B | ||
+ | // devices' | ||
+ | // frequency and support for class B is spotty and untested, so this | ||
+ | // frequency is not configured here. | ||
+ | |||
+ | // Disable link check validation | ||
+ | LMIC_setLinkCheckMode(0); | ||
+ | |||
+ | // TTN uses SF9 for its RX2 window. | ||
+ | LMIC.dn2Dr = DR_SF9; | ||
+ | |||
+ | // Set data rate and transmit power for uplink (note: txpow seems to be ignored by the library) | ||
+ | // | ||
+ | LMIC_setDrTxpow(DR_SF9, | ||
+ | |||
+ | // Start job (sending automatically starts OTAA too) | ||
+ | do_send(& | ||
+ | } | ||
+ | |||
+ | void loop() { | ||
+ | u8x8.setFont(u8x8_font_amstrad_cpc_extended_r); | ||
+ | u8x8.drawString(0, | ||
+ | u8x8.drawString(0, | ||
+ | |||
+ | os_runloop_once(); | ||
+ | } | ||
+ | |||
+ | </ | ||
+ | ===== Links ===== | ||
+ | |||
+ | * Single-Channel-Gateway - [[https:// | ||
+ |