User Tools

Site Tools


Sidebar

projekte:grossprojekte:lorawan:7_lora_node_scetch

7 LoRa Node Sketch

Was bei allen Nodes, egal ob auf Arduino, ESP, Raspi oder anderen Controllern basierend, gleich ist, ist die Notwendigkeit, die erfassten Daten in das LoRaWAN Netzwerk zu senden. Dazu bedarf es etwas Software, die die Chips (z.B. SX1276 bzw. RFM95W) ansteuern kann.

Hat man nun die entspechende Hardware zusammengebaut und die entsprechende SW aufgesetzt erhält man einen “Rohling”. Eine Node, die sich im The Things Network anmelden und Daten absenden kann. Wir wollen hier immer nur mit TTN zusammenarbeiten, daher nachfolgend keine Betrachtung anderer Dienste.

Rohling auf Ardino Basis

Als Hardware benötigen wir einen Arduino (z.B. Uno oder kompatibel), das Dragino LoRa-Shield für Europa (868 MHz), eine Antenne und eine Batterie (hier zu Testzwecken erstmal ein Akku) - fertig ist die Hardware.













Das Dragino-Shield beinhaltet auch schon den LoRoa-Chip, Pegelwandler von 5 V auf 3,3 V bzw. 3,3 V Versorgung für den LoRo-Chip sowie einen SMA Antennenanschluß. Bei einigen Lieferanten wird noch eine Antenne beigelegt (siehe Bild). Diese können wir meist getrost wegwerfen, außer wir haben nur wenige zig Meter zum nächsten Gateway. Diese beigelegten Antennen sind oft nicht auf die Frequenz angepasst und müssen evtl. gegen hochwertige Antennen ersetzt werden.

Wie kommen wir nun zu unserem Sketch?
Glücklicherweise hat sich vor längerer Zeit die Firma IBM (IBM Zurich) mit LoRa beschäftigt und ein dazugehöriges Programm geschrieben (The IBM Zurich LMiC codebase). Allerdings ist es recht groß und passt nicht zu unserer Arduino IDE. Aber einige Leute (z.B. Matthis Kooijman, Terry Moore, ChaeHee Won, Frank Rose) haben sich die Mühe gemacht das Programm zu entschlacken und für unsere Arduino-IDE anzupassen. Mit dieser Bibliothek und dem Example “ttn-abp.ino” können wir einen Rohling aufsetzen.

Zuerst die Bibliothek installieren
Geht in die IDE-Bibliotheksverwaltung und sucht mit dem Stichwort “LMIC”. Wird euch die Biblitohek “MCCI LoRaWAN LMIC library by IBM, Matthis Kooijman, Terry Moore, ChaeHee Won, Frank Rose ……..”, “Arduino port of the LMIC (LoraWAN-MAC-in-C) framework provided by IBM…..” angeboten, seid ihr richtig. Installiert die neueste Version. Solte die Bibliothek nicht gefunden werden, laden wir sie direkt von GitHub herunter. Die Bibliothek finden wir bei GitHub. Nach dem Import in die Arduino-IDE können wir schon fast loslegen.

Fast?
Ja fast, denn da es weltweit diverse Sendefrequenzen gibt und der LoRa-Chip an unterschiedliche Arduino-Pins angeschlossen werden kann, muß man das noch entsprechend in der Bibliothek ändern bzw. im Sketch eintragen.

Zuerst in der Bibliothek.
In der Bibliothek, nachdem sie installiert ist, müssen wir eintragen, welchen Standard wir benutzen - d.h. EU 868 MHz. Die Bibliothek ist vermutlich zu finden unter ../Arduino/Sketche/libraries. Das variiert je nach Installation der Arduino-IDE.
Nebenbei, ist die IDE schon aktuell? Wenn nicht erst ein Update durchführen. Auch solltet ihr nicht die IDE aus dem Microsoft Store installieren, sondern den Download von der Arduino Homepage.
Sucht dann im Ordner libraries nach dem Ordner MCCI_LoRaWAN_LMIC_library und darin den Ordner project_config. Öffnet die Datei “lmic_project_config.h” mit einem Dooppelklick und ändert sie folgendermaßen ab.

// project-specific definitions
#define CFG_eu868 1
//#define CFG_us915 1
//#define CFG_au921 1
//#define CFG_as923 1
// #define LMIC_COUNTRY_CODE LMIC_COUNTRY_CODE_JP	/* for as923-JP */
//#define CFG_kr920 1
//#define CFG_in866 1
#define CFG_sx1276_radio 1
//#define LMIC_USE_INTERRUPTS

Entfernt die beiden Schrägstriche vor der Zeile #define CFG_eu868 1. Speichern - fertig.

Nach einem Update der Bibliothek prüft, ob die Datei “lmic_project_config.h” nicht überschrieben wurde.


Das waren alle Vorbereitungen in der Bibliothek. Alle weitere Anpassungen werden im Sketch vorgenommen.

Vom Beispiel zum SW-Rohling

Nun laden wir ein Beispiel aus der Bibliothek. Sucht dazu die Datei “ttn-otaa.ino” und öffnet sie durch Doppelklick in der Arduino-IDE. Da wir diese Datei noch verändern müssen, damit sie für unsere Arduino-Node passt, speichern wir sie erst einmal unter einem eigenen Namen ab, damit das Original erhalten bleibt. Ich nenn das einfach mal “LoRa_node_roh”.

Der Sketch schaut dann so aus:

  1. /*******************************************************************************
  2.  * Copyright (c) 2015 Thomas Telkamp and Matthijs Kooijman
  3.  * Copyright (c) 2018 Terry Moore, MCCI
  4.  *
  5.  * Permission is hereby granted, free of charge, to anyone
  6.  * obtaining a copy of this document and accompanying files,
  7.  * to do whatever they want with them without any restriction,
  8.  * including, but not limited to, copying, modification and redistribution.
  9.  * NO WARRANTY OF ANY KIND IS PROVIDED.
  10.  *
  11.  * This example sends a valid LoRaWAN packet with payload "Hello,
  12.  * world!", using frequency and encryption settings matching those of
  13.  * the The Things Network.
  14.  *
  15.  * This uses OTAA (Over-the-air activation), where where a DevEUI and
  16.  * application key is configured, which are used in an over-the-air
  17.  * activation procedure where a DevAddr and session keys are
  18.  * assigned/generated for use with all further communication.
  19.  *
  20.  * Note: LoRaWAN per sub-band duty-cycle limitation is enforced (1% in
  21.  * g1, 0.1% in g2), but not the TTN fair usage policy (which is probably
  22.  * violated by this sketch when left running for longer)!
  23.  
  24.  * To use this sketch, first register your application and device with
  25.  * the things network, to set or generate an AppEUI, DevEUI and AppKey.
  26.  * Multiple devices can use the same AppEUI, but each device has its own
  27.  * DevEUI and AppKey.
  28.  *
  29.  * Do not forget to define the radio type correctly in
  30.  * arduino-lmic/project_config/lmic_project_config.h or from your BOARDS.txt.
  31.  *
  32.  *******************************************************************************/
  33.  
  34. #include <lmic.h>
  35. #include <hal/hal.h>
  36. #include <SPI.h>
  37.  
  38. //
  39. // For normal use, we require that you edit the sketch to replace FILLMEIN
  40. // with values assigned by the TTN console. However, for regression tests,
  41. // we want to be able to compile these scripts. The regression tests define
  42. // COMPILE_REGRESSION_TEST, and in that case we define FILLMEIN to a non-
  43. // working but innocuous value.
  44. //
  45. #ifdef COMPILE_REGRESSION_TEST
  46. # define FILLMEIN 0
  47. #else
  48. # warning "You must replace the values marked FILLMEIN with real values from the TTN control panel!"
  49. # define FILLMEIN (#dont edit this, edit the lines that use FILLMEIN)
  50. #endif
  51.  
  52. // This EUI must be in little-endian format, so least-significant-byte
  53. // first. When copying an EUI from ttnctl output, this means to reverse
  54. // the bytes. For TTN issued EUIs the last bytes should be 0xD5, 0xB3,
  55. // 0x70.
  56. static const u1_t PROGMEM APPEUI[8]={ FILLMEIN };
  57. void os_getArtEui (u1_t* buf) { memcpy_P(buf, APPEUI, 8);}
  58.  
  59. // This should also be in little endian format, see above.
  60. static const u1_t PROGMEM DEVEUI[8]={ FILLMEIN };
  61. void os_getDevEui (u1_t* buf) { memcpy_P(buf, DEVEUI, 8);}
  62.  
  63. // This key should be in big endian format (or, since it is not really a
  64. // number but a block of memory, endianness does not really apply). In
  65. // practice, a key taken from ttnctl can be copied as-is.
  66. static const u1_t PROGMEM APPKEY[16] = { FILLMEIN };
  67. void os_getDevKey (u1_t* buf) { memcpy_P(buf, APPKEY, 16);}
  68.  
  69. static uint8_t mydata[] = "Hello, world!";
  70. static osjob_t sendjob;
  71.  
  72. // Schedule TX every this many seconds (might become longer due to duty
  73. // cycle limitations).
  74. const unsigned TX_INTERVAL = 60;
  75.  
  76. // Pin mapping
  77. const lmic_pinmap lmic_pins = {
  78. .nss = 6,
  79. .rxtx = LMIC_UNUSED_PIN,
  80. .rst = 5,
  81. .dio = {2, 3, 4},
  82. };
  83.  
  84. void onEvent (ev_t ev) {
  85. Serial.print(os_getTime());
  86. Serial.print(": ");
  87. switch(ev) {
  88. case EV_SCAN_TIMEOUT:
  89. Serial.println(F("EV_SCAN_TIMEOUT"));
  90. break;
  91. case EV_BEACON_FOUND:
  92. Serial.println(F("EV_BEACON_FOUND"));
  93. break;
  94. case EV_BEACON_MISSED:
  95. Serial.println(F("EV_BEACON_MISSED"));
  96. break;
  97. case EV_BEACON_TRACKED:
  98. Serial.println(F("EV_BEACON_TRACKED"));
  99. break;
  100. case EV_JOINING:
  101. Serial.println(F("EV_JOINING"));
  102. break;
  103. case EV_JOINED:
  104. Serial.println(F("EV_JOINED"));
  105. {
  106. u4_t netid = 0;
  107. devaddr_t devaddr = 0;
  108. u1_t nwkKey[16];
  109. u1_t artKey[16];
  110. LMIC_getSessionKeys(&netid, &devaddr, nwkKey, artKey);
  111. Serial.print("netid: ");
  112. Serial.println(netid, DEC);
  113. Serial.print("devaddr: ");
  114. Serial.println(devaddr, HEX);
  115. Serial.print("artKey: ");
  116. for (size_t i=0; i<sizeof(artKey); ++i) {
  117. Serial.print(artKey[i], HEX);
  118. }
  119. Serial.println("");
  120. Serial.print("nwkKey: ");
  121. for (size_t i=0; i<sizeof(nwkKey); ++i) {
  122. Serial.print(nwkKey[i], HEX);
  123. }
  124. Serial.println("");
  125. }
  126. // Disable link check validation (automatically enabled
  127. // during join, but because slow data rates change max TX
  128. // size, we don't use it in this example.
  129. LMIC_setLinkCheckMode(0);
  130. break;
  131. /*
  132.   || This event is defined but not used in the code. No
  133.   || point in wasting codespace on it.
  134.   ||
  135.   || case EV_RFU1:
  136.   || Serial.println(F("EV_RFU1"));
  137.   || break;
  138.   */
  139. case EV_JOIN_FAILED:
  140. Serial.println(F("EV_JOIN_FAILED"));
  141. break;
  142. case EV_REJOIN_FAILED:
  143. Serial.println(F("EV_REJOIN_FAILED"));
  144. break;
  145. case EV_TXCOMPLETE:
  146. Serial.println(F("EV_TXCOMPLETE (includes waiting for RX windows)"));
  147. if (LMIC.txrxFlags & TXRX_ACK)
  148. Serial.println(F("Received ack"));
  149. if (LMIC.dataLen) {
  150. Serial.print(F("Received "));
  151. Serial.print(LMIC.dataLen);
  152. Serial.println(F(" bytes of payload"));
  153. }
  154. // Schedule next transmission
  155. os_setTimedCallback(&sendjob, os_getTime()+sec2osticks(TX_INTERVAL), do_send);
  156. break;
  157. case EV_LOST_TSYNC:
  158. Serial.println(F("EV_LOST_TSYNC"));
  159. break;
  160. case EV_RESET:
  161. Serial.println(F("EV_RESET"));
  162. break;
  163. case EV_RXCOMPLETE:
  164. // data received in ping slot
  165. Serial.println(F("EV_RXCOMPLETE"));
  166. break;
  167. case EV_LINK_DEAD:
  168. Serial.println(F("EV_LINK_DEAD"));
  169. break;
  170. case EV_LINK_ALIVE:
  171. Serial.println(F("EV_LINK_ALIVE"));
  172. break;
  173. /*
  174.   || This event is defined but not used in the code. No
  175.   || point in wasting codespace on it.
  176.   ||
  177.   || case EV_SCAN_FOUND:
  178.   || Serial.println(F("EV_SCAN_FOUND"));
  179.   || break;
  180.   */
  181. case EV_TXSTART:
  182. Serial.println(F("EV_TXSTART"));
  183. break;
  184. default:
  185. Serial.print(F("Unknown event: "));
  186. Serial.println((unsigned) ev);
  187. break;
  188. }
  189. }
  190.  
  191. void do_send(osjob_t* j){
  192. // Check if there is not a current TX/RX job running
  193. if (LMIC.opmode & OP_TXRXPEND) {
  194. Serial.println(F("OP_TXRXPEND, not sending"));
  195. } else {
  196. // Prepare upstream data transmission at the next possible time.
  197. LMIC_setTxData2(1, mydata, sizeof(mydata)-1, 0);
  198. Serial.println(F("Packet queued"));
  199. }
  200. // Next TX is scheduled after TX_COMPLETE event.
  201. }
  202.  
  203. void setup() {
  204. Serial.begin(9600);
  205. Serial.println(F("Starting"));
  206.  
  207. #ifdef VCC_ENABLE
  208. // For Pinoccio Scout boards
  209. pinMode(VCC_ENABLE, OUTPUT);
  210. digitalWrite(VCC_ENABLE, HIGH);
  211. delay(1000);
  212. #endif
  213.  
  214. // LMIC init
  215. os_init();
  216. // Reset the MAC state. Session and pending data transfers will be discarded.
  217. LMIC_reset();
  218.  
  219. // Start job (sending automatically starts OTAA too)
  220. do_send(&sendjob);
  221. }
  222.  
  223. void loop() {
  224. os_runloop_once();
  225. }



Wenn man sich den Sketch anschaut, fällt auf, dass die in der Arduino-IDE übliche Reihenfolge nicht eingehalten wurde. Das liegt daran, dass das Programm ursprünglich nicht dafür entwickelt wurde, und bei C-Compilern erst alle Definitionen / Funktionen etc. vorgenommen werden müssen, bevor eine Funktion verwendet werden kann. Bei der Arduino-IDE ist das nicht nötig. Schauen wir einmal von oben nach unten durch. Ich habe einige wichtige Zeilen / Bereiche gelb hinterlegt.

In den Zeilen 56, 60 und 66 müssen wir irgendwelche Adressen eintragen. Dazu später mehr. Um dies vornehmen zu können, müssen wir vorher dies hier abgearbeitet haben.

In Zeile 69 wird es schon etwas interessanter: Hello Wolrd! LOL Das ist der “Datensatz” der später in den LoRaWAN-Netzwerk gesendet werden soll - natürlich als Test. Später sollen es ja Messwerte von Sensoren sein.

Zeile 74 erklärt sich auch fast von selbst. Wir erinnern uns, die Belegunbgszeit eines Nodes auf den Funkfrequenzen unterliegt gewissen Einschränkungen. Also wird man nur ab und an senden - hier momentan alle 60 Sekunden.

Zeile 77 bis 82. Etwas ganz Wichtiges. Hiermit definieren wir die Pins, an die der LoRa Chip angeschlossen ist. Da wir ein Graduino-Shield benutzen, ist hier unbedingt einen Anpassung nötig. Ersetzt diese Zeilen mit diesen Zeilen:

  1. const lmic_pinmap lmic_pins = {
  2. .nss = 10,
  3. .rxtx = LMIC_UNUSED_PIN,
  4. .rst = 9,
  5. .dio = {2, 6, 7},
  6. };


Die letzte, sehr wichtige Zeile ist die 155. Hier wird nach erfolgreicher Übermittlung der Daten das nächste Datenpaket fertig gemacht und verschickt. Die entsprechende Funktion erhält die Daten und die Wartezeit bis zur nächsten Übertragung und führt sie dann aus.

Schon hier merken wir, dass für die zeitliche Einteilung einer Datenübertragung die Loop-Schleife nicht verwendet werden kann. Für die Bereitstellung der Daten müssen wir also an anderer Stelle entsprechenden Code einfügen. Dies wäre z.B. bei Zeile 154. Dazu später mehr.

Nehmen wir an, wir hätten das Thema Adressen geklärt und der Sketch würde compiliert werden, dann erhielten wir die Nachricht, dass auf dem Uno nur noch sehr wenig Speicherplatz übrig bliebe. Sollten wir also Sendoren mit etwas umfangreicher Software verwenden, müssten wir Speicherplatz sparen, oder auf einen Mega ausweichen. Etwas Speicher würde frei werden, wenn wir die Kommunikation in den seriellen Monitor entfernen würden. Im richtigen Leben hängt der Node auch nicht am Rechner und wir lesen mit. Eine Idee wäre dieses über einen Compiler-Schalter zu bewerkstelligen. Dann könnte man auch gleich die “leeren” Case Fälle entfernen. Ich habe das mal ganz brutal gelöscht und die wichtigen Passagen für die Erweiterung für Sensoren markiert. Dieser Sketch läßt genau 378 Byte für Variablen übrig.

Hier also der Sketch LoRa “LoRa_node_roh_ohne_COM”

  1. /*******************************************************************************
  2.   Copyright (c) 2015 Thomas Telkamp and Matthijs Kooijman
  3.   Copyright (c) 2018 Terry Moore, MCCI
  4.  
  5.   Permission is hereby granted, free of charge, to anyone
  6.   obtaining a copy of this document and accompanying files,
  7.   to do whatever they want with them without any restriction,
  8.   including, but not limited to, copying, modification and redistribution.
  9.   NO WARRANTY OF ANY KIND IS PROVIDED.
  10.  
  11.   This example sends a valid LoRaWAN packet with payload "Hello,
  12.   world!", using frequency and encryption settings matching those of
  13.   the The Things Network.
  14.  
  15.   This uses OTAA (Over-the-air activation), where where a DevEUI and
  16.   application key is configured, which are used in an over-the-air
  17.   activation procedure where a DevAddr and session keys are
  18.   assigned/generated for use with all further communication.
  19.  
  20.   Note: LoRaWAN per sub-band duty-cycle limitation is enforced (1% in
  21.   g1, 0.1% in g2), but not the TTN fair usage policy (which is probably
  22.   violated by this sketch when left running for longer)!
  23.  
  24.   To use this sketch, first register your application and device with
  25.   the things network, to set or generate an AppEUI, DevEUI and AppKey.
  26.   Multiple devices can use the same AppEUI, but each device has its own
  27.   DevEUI and AppKey.
  28.  
  29.   Do not forget to define the radio type correctly in
  30.   arduino-lmic/project_config/lmic_project_config.h or from your BOARDS.txt.
  31.  
  32.  *******************************************************************************/
  33.  
  34. #include <lmic.h>
  35. #include <hal/hal.h>
  36. #include <SPI.h>
  37.  
  38.  
  39. // Applcation EUI Reihenfolge LSB MSB
  40. static const u1_t PROGMEM APPEUI[8] = { 0xxx, 0xxx, 0xxx, 0xxx, 0xxx, 0xxx, 0xxx, 0xxx };
  41. void os_getArtEui (u1_t* buf) {
  42. memcpy_P(buf, APPEUI, 8);
  43. }
  44.  
  45. //Device EUI Reihenfolge LSB MSB
  46. static const u1_t PROGMEM DEVEUI[8] = { 0xxx, 0xxx, 0xxx, 0xxx, 0xxx, 0xxx, 0xxx, 0xxx };
  47. void os_getDevEui (u1_t* buf) {
  48. memcpy_P(buf, DEVEUI, 8);
  49. }
  50.  
  51. // Application Key Achtung: Reihenfolge MSB LSB
  52. static const u1_t PROGMEM APPKEY[16] = { 0xxx, 0xxx, 0xxx, 0xxx, 0xxx, 0xxx, 0xxx, 0xxx, 0xxx, 0xxx, 0xxx, 0xxx, 0xxx, 0xxx, 0xxx, 0xxx };
  53. void os_getDevKey (u1_t* buf) {
  54. memcpy_P(buf, APPKEY, 16);
  55. }
  56.  
  57. static uint8_t mydata[] = "Hello World!";
  58. static osjob_t sendjob;
  59.  
  60. // Schedule TX every this many seconds (might become longer due to duty
  61. // cycle limitations).
  62. const unsigned TX_INTERVAL = 60;
  63.  
  64. // Pin Mapping für Graduino LoRa Shield for Arduino
  65. const lmic_pinmap lmic_pins = {
  66. .nss = 10,
  67. .rxtx = LMIC_UNUSED_PIN,
  68. .rst = 9,
  69. .dio = {2, 6, 7},
  70. };
  71.  
  72. void onEvent (ev_t ev) {
  73. switch (ev) {
  74. case EV_JOINED:
  75. {
  76. u4_t netid = 0;
  77. devaddr_t devaddr = 0;
  78. u1_t nwkKey[16];
  79. u1_t artKey[16];
  80. LMIC_getSessionKeys(&netid, &devaddr, nwkKey, artKey);
  81. }
  82. // Disable link check validation (automatically enabled
  83. // during join, but because slow data rates change max TX
  84. // size, we don't use it in this example.
  85. LMIC_setLinkCheckMode(0);
  86. break;
  87. case EV_TXCOMPLETE:
  88. // Schedule next transmission
  89. os_setTimedCallback(&sendjob, os_getTime() + sec2osticks(TX_INTERVAL), do_send);
  90. break;
  91. default:
  92. break;
  93. }
  94. }
  95.  
  96. void do_send(osjob_t* j) {
  97. // Check if there is not a current TX/RX job running
  98. if (LMIC.opmode & OP_TXRXPEND) {
  99. } else {
  100. // Prepare upstream data transmission at the next possible time.
  101. LMIC_setTxData2(1, mydata, sizeof(mydata) - 1, 0);
  102. }
  103. // Next TX is scheduled after TX_COMPLETE event.
  104. }
  105.  
  106. void setup() {
  107.  
  108. #ifdef VCC_ENABLE
  109. // For Pinoccio Scout boards
  110. pinMode(VCC_ENABLE, OUTPUT);
  111. digitalWrite(VCC_ENABLE, HIGH);
  112. delay(1000);
  113. #endif
  114.  
  115. // LMIC init
  116. os_init();
  117. // Reset the MAC state. Session and pending data transfers will be discarded.
  118. LMIC_reset();
  119. LMIC_setClockError(MAX_CLOCK_ERROR * 2 / 100);
  120. LMIC_setupChannel(0, 868100000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band
  121. LMIC_setupChannel(1, 868300000, DR_RANGE_MAP(DR_SF12, DR_SF7B), BAND_CENTI); // g-band
  122. LMIC_setupChannel(2, 868500000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band
  123. LMIC_setupChannel(3, 867100000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band
  124. LMIC_setupChannel(4, 867300000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band
  125. LMIC_setupChannel(5, 867500000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band
  126. LMIC_setupChannel(6, 867700000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band
  127. LMIC_setupChannel(7, 867900000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band
  128. LMIC_setupChannel(8, 868800000, DR_RANGE_MAP(DR_FSK, DR_FSK), BAND_MILLI); // g2-band
  129.  
  130. LMIC_setLinkCheckMode(0);
  131. LMIC.dn2Dr = SF9;
  132. LMIC_setDrTxpow(DR_SF7, 14);
  133. // Start job (sending automatically starts OTAA too)
  134. do_send(&sendjob);
  135. }
  136.  
  137. void loop() {
  138. os_runloop_once();
  139. }

Schon etwas kürzer. Dieser Sketch sendet alle 60 Sekunden ein “Hello World!” ins TTN-Netzwerk bzw. in die TTN-Cloud. Sofern - sofern die nötigen Adressangaben vorgenommen wurde. Die echten Daten habe ich mit xx ersetzt. Denn diese Angaben sind individuell.

Spätestens jetzt müssen wir unbedingt über die Adressen reden. Von daher machen wir einen Sprung zu der Seite TTN Konto und kommen danach wieder zurück.

Eintragen der nötigen Adressen / Keys

Wie wir am obigen Listing sehen, müssen wir in den Sketch drei Daten eintragen, die wir aus unserem TTN-Konto auslesen müssen. Die application EUI, den application key und die device address.
Weiter müssen die Daten in einem bestimmten Format eingetragen werden.

{ 0x34, 0x3C, 0x12, 0x27, 0x66, 0x23, 0x45, 0x13 };

(Diese Daten sind willkürlich, real wirds dann gleich.)
Dieses Format kennen wir und es wird oft verwendet. Allerdings ist zu beachten, dass die Bytes in folgender Reihenfolge eingetragen werden:

Application EUI Reihenfolge LSB MSB
Device EUI Reihenfolge LSB MSB
Application Key Achtung: Reihenfolge MSB LSB


Glücklicherweise ermöglicht es uns TTN diese Daten direkt mit copy zu übernehmen und mit paste einzusetzen - ergibt schon keine Tippfehler.

Holen wir uns also die Daten. In TTN anmelden, die gewünschte Applikation wählen und dann das Device für das wir die Daten benötigen. Auf der Übersichtsseite für das Device finden wir nun die Daten.


Da die Daten zu einer echten Node gehören, sind sie unkenntlich gemacht.

Wir benötigen nun:

Application EUI Reihenfolge LSB MSB ← roter Pfeil
Device EUI Reihenfolge LSB MSB ← blauer Pfeil
Application Key Achtung: Reihenfolge MSB LSB ← grüner Pfeil

Die Daten liegen aber nicht im richtigen Format vor und müssen daher erst korrekt angezeigt werden. Beim Application Key müssen sie sogar erst einmal sichtbar gemacht werden.

Das geht folgendermaßen:

Es gibt vier Steuerbuttons, mit denen man dies alles bewerkstelligen kann.

  • A schaltet die Schreibweise einzelner Bytes um (wir benötigen immer die Schreibweise 0x12)
  • B ändert die Reihenfolge MSB→LSB bzw. LSB→MSB (was wir jeweils benötigen steht weiter oben)
  • C schaltet die geheimen Daten auf sichtbar bzw. unsichtbar (sofern vorhanden)
  • D zeigt an, welche Bytefolge vorliegt lsb (LSB → MSB) oder msb (MSB → LSB)
  • E kopiert den angezeigten Datensatz genau so in die Zwischenablage (d.h. mit CTL V kann man die Daten in den Sketch kopieren)


Haben wir die Daten in den Sketch übertragen, können wir ihn kompilieren und auf unseren Node übertragen.
Sobald der Node anfängt zu senden, meldet er sich erstmalig am System an, erhält eine Systemantwort (OTAA - over the air activation, siehe auch Bild weiter oben: Activation Method: OTAA) und startet dann mit der zyklischen Übertragung der Daten.
Schalten wir in unserem TTN-Konto die Device-Ansicht von Overview auf Data um, können wir dieses Vorgehen beobachten.

Kurz nachdem die Node eingeschaltet ist, läuft der erste Datensatz ein. Mit jedem Datensatz wächst die Liste. Neue Datensätze sind oben, ältere weiter unten.

Der erste Datensatz ist links mit einem gelben Doppelblitz markiert - dem Zeichen für die Aktivierung.



Daneben stehen die bereits bekannten Daten, mit denen man den Node indentifiziert. Klickt man auf das Doppelblitzsymbol öffnet sich die Detailansicht dieses Datensatzes.



Zusätzlich werden die Metadaten der Übertragung angezeigt.




Der zweite Datensatz in diesem Beispiel trägt einen blauen Pfeil, der nach oben zeigt - dies bedeutet Uplink. D.h. vom Node zum Gateway. Diesmal ist die Anzeige anders. Man sieht nur Counter, Port und Payload. Klickt man auf den blauen Pfeil, öffnen sich ebenfalls die Detaildaten.



Metadaten, Payload


Metadaten
Betrachtet man die Metadaten, so fällt auf, dass sich die Sendefrequenz geändert hat. Wir erinnern uns, nach jedem Sendevorgang wird der Kanal gewechselt, um das Risiko zu minimieren Daten auf Grund eines belegten Kanals zu verlieren.

Man erkennt auch die ID des Gateways über das die Datenpakete empfangen wurden. Sollten mehrere Gateways im Sendebereich der Node gewesen sein und diese ebenfalls die Daten empfangen haben, so werden auch diese Gateways in den Metadatensätzen aufgelistet.
Weitere Informationen sind u.a. die Empfangszeit, Modulationsart, Spreizungsfaktor, Feldstärke etc.

Was ist nun die Payload?
Das sind nichts anderes als die Daten, die die Node gesendet hat.
In diesem Fall stand im Datensatz (im Gegensatz zu Hello World vom obigen Beispielcode) ein einfaches “Hi”. Die “48” und “69” sind nichts anderes als die Ascii Codes der beiden Buchstaben “H” und “i”.

Was bleibt noch offen


Natürlich bleiben noch einige Fragen bzw. Aufgaben offen.

  • Einbinden des Programmcodes für diverse Sensoren
  • Setzen des Nodes in den Schlafzustand außerhalb der Sendezyklen (z.B. um Batterie zu sparen)
  • Mögliche Umstrukturierung der Software, um evtl doch die Loop-Schleife zu verwenden



Stand: 05.11.2019

projekte/grossprojekte/lorawan/7_lora_node_scetch.txt · Last modified: 2019/11/05 14:47 by fablab_wiki