From 1fc4257110c9f5b5280b122f30f72f58097779c8 Mon Sep 17 00:00:00 2001 From: Starbeamrainbowlabs Date: Fri, 21 Jun 2019 14:59:57 +0100 Subject: [PATCH 01/34] Create LoRaWAN TTN ABP test program, but it needs some debugging. --- .gitignore | 2 + iot/TTNTest/TTNTest.ino | 252 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 254 insertions(+) create mode 100644 iot/TTNTest/TTNTest.ino diff --git a/.gitignore b/.gitignore index 0a00e2c..3214de6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +config.custom.h + Reports/*.pdf # Created by https://www.gitignore.io/api/libreoffice diff --git a/iot/TTNTest/TTNTest.ino b/iot/TTNTest/TTNTest.ino new file mode 100644 index 0000000..be8ccd0 --- /dev/null +++ b/iot/TTNTest/TTNTest.ino @@ -0,0 +1,252 @@ +/******************************************************************************* + * 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 ABP (Activation-by-personalisation), where a DevAddr and + * Session keys are preconfigured (unlike OTAA, where a DevEUI and + * application key is configured, while the DevAddr and session keys are + * assigned/generated in the over-the-air-activation procedure). + * + * 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 a DevAddr, NwkSKey and + * AppSKey. Each device should have their own unique values for these + * fields. + * + * Do not forget to define the radio type correctly in + * arduino-lmic/project_config/lmic_project_config.h or from your BOARDS.txt. + * + *******************************************************************************/ + +// References: +// [feather] adafruit-feather-m0-radio-with-lora-module.pdf + +#include +#include +#include + +#include "config.custom.h" + + +// 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 arduino-lmic/project_config/lmic_project_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 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 = 60; + +// Pin mapping +// Adapted for the Dragino LoRa Shield +const lmic_pinmap lmic_pins = { + .nss = 10, // SPI Chip select - aka NSS + .rxtx = LMIC_UNUSED_PIN, + .rst = 9, // Reset pin + .dio = {2, 6, 7}, +}; + +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")); + 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.println(F("Received ")); + Serial.println(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; + 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() { +// pinMode(13, OUTPUT); + while (!Serial); // wait for Serial to be initialized + Serial.begin(115200); + delay(100); // per sample code on RF_95 test + Serial.println(F("Starting")); + + #ifdef VCC_ENABLE + // For Pinoccio Scout boards + pinMode(VCC_ENABLE, OUTPUT); + digitalWrite(VCC_ENABLE, HIGH); + delay(1000); + #endif + + // 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, APPSKEY, sizeof(APPSKEY)); + memcpy_P(nwkskey, NWKSKEY, sizeof(NWKSKEY)); + LMIC_setSession (0x13, DEVADDR, nwkskey, appskey); + #else + // If not running an AVR with PROGMEM, just use the arrays directly + LMIC_setSession (0x13, DEVADDR, NWKSKEY, APPSKEY); + #endif + + #if defined(CFG_eu868) + // 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, so be sure to configure the full frequency range of + // your network here (unless your network autoconfigures them). + // Setting up channels should happen after LMIC_setSession, as that + // configures the minimal channel set. + LMIC_setupChannel(0, 868100000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band + LMIC_setupChannel(1, 868300000, DR_RANGE_MAP(DR_SF12, DR_SF7B), BAND_CENTI); // g-band + LMIC_setupChannel(2, 868500000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band + LMIC_setupChannel(3, 867100000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band + LMIC_setupChannel(4, 867300000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band + LMIC_setupChannel(5, 867500000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band + LMIC_setupChannel(6, 867700000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band + LMIC_setupChannel(7, 867900000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band + LMIC_setupChannel(8, 868800000, DR_RANGE_MAP(DR_FSK, DR_FSK), BAND_MILLI); // g2-band + // TTN defines an additional channel at 869.525Mhz using SF9 for class B + // devices' ping slots. LMIC does not have an easy way to define set this + // frequency and support for class B is spotty and untested, so this + // frequency is not configured here. + #elif defined(CFG_us915) + // NA-US channels 0-71 are configured automatically + // but only one group of 8 should (a subband) should be active + // TTN recommends the second sub band, 1 in a zero based count. + // https://github.com/TheThingsNetwork/gateway-conf/blob/master/US-global_conf.json + LMIC_selectSubBand(1); + #endif + + // 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 + LMIC_setDrTxpow(DR_SF7,14); + + // Start job + do_send(&sendjob); +} + +void loop() { + unsigned long now; + now = millis(); + if ((now & 512) != 0) { + digitalWrite(13, HIGH); + } + else { + digitalWrite(13, LOW); + } + + os_runloop_once(); + +} From 74d09eac050bdae543748fe8bda3ee34828a0eed Mon Sep 17 00:00:00 2001 From: Starbeamrainbowlabs Date: Fri, 21 Jun 2019 17:04:48 +0100 Subject: [PATCH 02/34] Update TTN LoRaWAN test script --- iot/TTNTest/TTNTest.ino | 105 +++++++++---------------------------- iot/libraries/arduino-lmic | 1 - 2 files changed, 24 insertions(+), 82 deletions(-) delete mode 160000 iot/libraries/arduino-lmic diff --git a/iot/TTNTest/TTNTest.ino b/iot/TTNTest/TTNTest.ino index be8ccd0..2b30122 100644 --- a/iot/TTNTest/TTNTest.ino +++ b/iot/TTNTest/TTNTest.ino @@ -1,6 +1,5 @@ /******************************************************************************* * 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, @@ -10,41 +9,27 @@ * * This example sends a valid LoRaWAN packet with payload "Hello, * world!", using frequency and encryption settings matching those of - * the The Things Network. + * the (early prototype version of) The Things Network. * - * This uses ABP (Activation-by-personalisation), where a DevAddr and - * Session keys are preconfigured (unlike OTAA, where a DevEUI and - * application key is configured, while the DevAddr and session keys are - * assigned/generated in the over-the-air-activation procedure). + * Note: LoRaWAN per sub-band duty-cycle limitation is enforced (1% in g1, + * 0.1% in g2). * - * 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)! + * Change DEVADDR to a unique address! + * See http://thethingsnetwork.org/wiki/AddressSpace * - * To use this sketch, first register your application and device with - * the things network, to set or generate a DevAddr, NwkSKey and - * AppSKey. Each device should have their own unique values for these - * fields. - * - * Do not forget to define the radio type correctly in - * arduino-lmic/project_config/lmic_project_config.h or from your BOARDS.txt. + * Do not forget to define the radio type correctly in config.h. * *******************************************************************************/ -// References: -// [feather] adafruit-feather-m0-radio-with-lora-module.pdf - #include #include #include #include "config.custom.h" - // 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 arduino-lmic/project_config/lmic_project_config.h, -// otherwise the linker will complain). +// 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) { } @@ -57,11 +42,10 @@ static osjob_t sendjob; const unsigned TX_INTERVAL = 60; // Pin mapping -// Adapted for the Dragino LoRa Shield const lmic_pinmap lmic_pins = { - .nss = 10, // SPI Chip select - aka NSS + .nss = 10, .rxtx = LMIC_UNUSED_PIN, - .rst = 9, // Reset pin + .rst = 9, .dio = {2, 6, 7}, }; @@ -87,28 +71,23 @@ void onEvent (ev_t ev) { case EV_JOINED: Serial.println(F("EV_JOINED")); 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_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; + 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.println(F("Received ")); - Serial.println(LMIC.dataLen); - Serial.println(F(" bytes of payload")); + if(LMIC.dataLen) { + // data received in rx slot after tx + Serial.print(F("Data Received: ")); + Serial.write(LMIC.frame+LMIC.dataBeg, LMIC.dataLen); + Serial.println(); } // Schedule next transmission os_setTimedCallback(&sendjob, os_getTime()+sec2osticks(TX_INTERVAL), do_send); @@ -129,20 +108,8 @@ void onEvent (ev_t ev) { 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; - default: - Serial.print(F("Unknown event: ")); - Serial.println((unsigned) ev); + default: + Serial.println(F("Unknown event")); break; } } @@ -160,10 +127,7 @@ void do_send(osjob_t* j){ } void setup() { -// pinMode(13, OUTPUT); - while (!Serial); // wait for Serial to be initialized Serial.begin(115200); - delay(100); // per sample code on RF_95 test Serial.println(F("Starting")); #ifdef VCC_ENABLE @@ -188,13 +152,12 @@ void setup() { uint8_t nwkskey[sizeof(NWKSKEY)]; memcpy_P(appskey, APPSKEY, sizeof(APPSKEY)); memcpy_P(nwkskey, NWKSKEY, sizeof(NWKSKEY)); - LMIC_setSession (0x13, DEVADDR, nwkskey, appskey); + LMIC_setSession (0x1, DEVADDR, nwkskey, appskey); #else - // If not running an AVR with PROGMEM, just use the arrays directly - LMIC_setSession (0x13, DEVADDR, NWKSKEY, APPSKEY); + // If not running an AVR with PROGMEM, just use the arrays directly + LMIC_setSession (0x1, DEVADDR, NWKSKEY, APPSKEY); #endif - #if defined(CFG_eu868) // 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 @@ -216,21 +179,11 @@ void setup() { // devices' ping slots. LMIC does not have an easy way to define set this // frequency and support for class B is spotty and untested, so this // frequency is not configured here. - #elif defined(CFG_us915) - // NA-US channels 0-71 are configured automatically - // but only one group of 8 should (a subband) should be active - // TTN recommends the second sub band, 1 in a zero based count. - // https://github.com/TheThingsNetwork/gateway-conf/blob/master/US-global_conf.json - LMIC_selectSubBand(1); - #endif // 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 + // Set data rate and transmit power (note: txpow seems to be ignored by the library) LMIC_setDrTxpow(DR_SF7,14); // Start job @@ -238,15 +191,5 @@ void setup() { } void loop() { - unsigned long now; - now = millis(); - if ((now & 512) != 0) { - digitalWrite(13, HIGH); - } - else { - digitalWrite(13, LOW); - } - os_runloop_once(); - } diff --git a/iot/libraries/arduino-lmic b/iot/libraries/arduino-lmic deleted file mode 160000 index 9f01615..0000000 --- a/iot/libraries/arduino-lmic +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 9f016150100afcde4d3def8abae234c6efc46e0c From 1591011e3334f3f7095366fee2d6640905061f69 Mon Sep 17 00:00:00 2001 From: Starbeamrainbowlabs Date: Fri, 21 Jun 2019 17:07:55 +0100 Subject: [PATCH 03/34] Remove new lmic library submodule --- .gitmodules | 3 --- 1 file changed, 3 deletions(-) diff --git a/.gitmodules b/.gitmodules index 29a9c01..c8b4f65 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,9 +4,6 @@ [submodule "iot/libraries/Entropy"] path = iot/libraries/Entropy url = https://github.com/taoyuan/Entropy.git -[submodule "iot/libraries/arduino-lmic"] - path = iot/libraries/arduino-lmic - url = https://github.com/mcci-catena/arduino-lmic.git [submodule "iot/libraries/TinyGPSPlus"] path = iot/libraries/TinyGPSPlus url = https://github.com/mikalhart/TinyGPSPlus.git From bfbcde4619d6394e44d679148aa5e7c2078c65f3 Mon Sep 17 00:00:00 2001 From: Starbeamrainbowlabs Date: Fri, 21 Jun 2019 17:08:34 +0100 Subject: [PATCH 04/34] Add old lmic library --- .gitmodules | 3 +++ iot/libraries/arduino-lmic | 1 + 2 files changed, 4 insertions(+) create mode 160000 iot/libraries/arduino-lmic diff --git a/.gitmodules b/.gitmodules index c8b4f65..c410298 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,6 @@ [submodule "iot/libraries/TinyGPSPlus"] path = iot/libraries/TinyGPSPlus url = https://github.com/mikalhart/TinyGPSPlus.git +[submodule "iot/libraries/arduino-lmic"] + path = iot/libraries/arduino-lmic + url = https://github.com/matthijskooijman/arduino-lmic.git diff --git a/iot/libraries/arduino-lmic b/iot/libraries/arduino-lmic new file mode 160000 index 0000000..ba1265d --- /dev/null +++ b/iot/libraries/arduino-lmic @@ -0,0 +1 @@ +Subproject commit ba1265d5d2f775177cdc7c82186724e4f0bdc3a8 From 9c65f1e588a7e368a8c0c507647d4ca6f0deca31 Mon Sep 17 00:00:00 2001 From: Starbeamrainbowlabs Date: Fri, 21 Jun 2019 17:11:44 +0100 Subject: [PATCH 05/34] Add example ttn test config file --- iot/TTNTest/config.custom.h.example | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 iot/TTNTest/config.custom.h.example diff --git a/iot/TTNTest/config.custom.h.example b/iot/TTNTest/config.custom.h.example new file mode 100644 index 0000000..65a82ea --- /dev/null +++ b/iot/TTNTest/config.custom.h.example @@ -0,0 +1,15 @@ +// Copy this file to have the name "config.custom.h", and fill in the 3 sections below + +// LoRaWAN NwkSKey, network session key +// Big endian (i.e. msb) +static const PROGMEM u1_t NWKSKEY[16] = { .... }; + +// LoRaWAN AppSKey, application session key +// Big endian (i.e. msb) +static const u1_t PROGMEM APPSKEY[16] = { .... }; + +// LoRaWAN end-device address (DevAddr) +// See http://thethingsnetwork.org/wiki/AddressSpace +// The library converts the address to network byte order as needed. +// Big endian (i.e. msb) +static const u4_t DEVADDR = 0x....; // <-- Change this address for every node! From 31a372e641461baffc42faadeca2fdc206b13002 Mon Sep 17 00:00:00 2001 From: Starbeamrainbowlabs Date: Mon, 24 Jun 2019 11:58:59 +0100 Subject: [PATCH 06/34] Add explicit chip select thingy --- iot/TTNTest/TTNTest.ino | 267 +++++++++++++++++++++------------------- 1 file changed, 137 insertions(+), 130 deletions(-) diff --git a/iot/TTNTest/TTNTest.ino b/iot/TTNTest/TTNTest.ino index 2b30122..d3fbc6f 100644 --- a/iot/TTNTest/TTNTest.ino +++ b/iot/TTNTest/TTNTest.ino @@ -43,153 +43,160 @@ const unsigned TX_INTERVAL = 60; // Pin mapping const lmic_pinmap lmic_pins = { - .nss = 10, - .rxtx = LMIC_UNUSED_PIN, - .rst = 9, - .dio = {2, 6, 7}, + .nss = 10, + .rxtx = LMIC_UNUSED_PIN, + .rst = 9, + .dio = {2, 6, 7}, }; 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")); - break; - 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; - break; - case EV_TXCOMPLETE: - Serial.println(F("EV_TXCOMPLETE (includes waiting for RX windows)")); - if(LMIC.dataLen) { - // data received in rx slot after tx - Serial.print(F("Data Received: ")); - Serial.write(LMIC.frame+LMIC.dataBeg, LMIC.dataLen); - Serial.println(); - } - // 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; - default: - Serial.println(F("Unknown event")); - break; - } + 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")); + break; + 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; + break; + case EV_TXCOMPLETE: + Serial.println(F("EV_TXCOMPLETE (includes waiting for RX windows)")); + if(LMIC.dataLen) { + // data received in rx slot after tx + Serial.print(F("Data Received: ")); + Serial.write(LMIC.frame+LMIC.dataBeg, LMIC.dataLen); + Serial.println(); + } + // 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; + default: + Serial.println(F("Unknown event")); + 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. + // 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(115200); - Serial.println(F("Starting")); + Serial.begin(115200); + Serial.println(F("Starting")); + + #define PIN_CS 10 + #define PIN_CS_2 3 + pinMode(PIN_CS, OUTPUT); + pinMode(PIN_CS_2, OUTPUT); + digitalWrite(PIN_CS, LOW); // We want to talk to the RFM 95 + digitalWrite(PIN_CS_2, HIGH); - #ifdef VCC_ENABLE - // For Pinoccio Scout boards - pinMode(VCC_ENABLE, OUTPUT); - digitalWrite(VCC_ENABLE, HIGH); - delay(1000); - #endif + #ifdef VCC_ENABLE + // For Pinoccio Scout boards + pinMode(VCC_ENABLE, OUTPUT); + digitalWrite(VCC_ENABLE, HIGH); + delay(1000); + #endif - // LMIC init - os_init(); - // Reset the MAC state. Session and pending data transfers will be discarded. - LMIC_reset(); + // 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, APPSKEY, sizeof(APPSKEY)); - memcpy_P(nwkskey, NWKSKEY, sizeof(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 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, APPSKEY, sizeof(APPSKEY)); + memcpy_P(nwkskey, NWKSKEY, sizeof(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, so be sure to configure the full frequency range of - // your network here (unless your network autoconfigures them). - // Setting up channels should happen after LMIC_setSession, as that - // configures the minimal channel set. - LMIC_setupChannel(0, 868100000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band - LMIC_setupChannel(1, 868300000, DR_RANGE_MAP(DR_SF12, DR_SF7B), BAND_CENTI); // g-band - LMIC_setupChannel(2, 868500000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band - LMIC_setupChannel(3, 867100000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band - LMIC_setupChannel(4, 867300000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band - LMIC_setupChannel(5, 867500000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band - LMIC_setupChannel(6, 867700000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band - LMIC_setupChannel(7, 867900000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band - LMIC_setupChannel(8, 868800000, DR_RANGE_MAP(DR_FSK, DR_FSK), BAND_MILLI); // g2-band - // TTN defines an additional channel at 869.525Mhz using SF9 for class B - // devices' ping slots. LMIC does not have an easy way to define set this - // frequency and support for class B is spotty and untested, so this - // frequency is not configured here. + // 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, so be sure to configure the full frequency range of + // your network here (unless your network autoconfigures them). + // Setting up channels should happen after LMIC_setSession, as that + // configures the minimal channel set. + LMIC_setupChannel(0, 868100000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band + LMIC_setupChannel(1, 868300000, DR_RANGE_MAP(DR_SF12, DR_SF7B), BAND_CENTI); // g-band + LMIC_setupChannel(2, 868500000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band + LMIC_setupChannel(3, 867100000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band + LMIC_setupChannel(4, 867300000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band + LMIC_setupChannel(5, 867500000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band + LMIC_setupChannel(6, 867700000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band + LMIC_setupChannel(7, 867900000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band + LMIC_setupChannel(8, 868800000, DR_RANGE_MAP(DR_FSK, DR_FSK), BAND_MILLI); // g2-band + // TTN defines an additional channel at 869.525Mhz using SF9 for class B + // devices' ping slots. LMIC does not have an easy way to define set this + // frequency and support for class B is spotty and untested, so this + // frequency is not configured here. - // Disable link check validation - LMIC_setLinkCheckMode(0); + // Disable link check validation + LMIC_setLinkCheckMode(0); - // Set data rate and transmit power (note: txpow seems to be ignored by the library) - LMIC_setDrTxpow(DR_SF7,14); + // Set data rate and transmit power (note: txpow seems to be ignored by the library) + LMIC_setDrTxpow(DR_SF7,14); - // Start job - do_send(&sendjob); + // Start job + do_send(&sendjob); } void loop() { - os_runloop_once(); + os_runloop_once(); } From cda73e37cefd3bdb0d673aaede9b0ac9f27c3fbf Mon Sep 17 00:00:00 2001 From: Starbeamrainbowlabs Date: Mon, 24 Jun 2019 13:52:33 +0100 Subject: [PATCH 07/34] Start working on integrating LMIC into the main program, but there are lots of bugs. --- iot/TTNTest/TTNTest.ino | 110 +++++++++++----- iot/TTNTestOTAA/TTNTestOTAA.ino | 159 +++++++++++++++++++++++ iot/TTNTestOTAA/config.custom.h.example | 26 ++++ iot/main/main.ino | 16 ++- iot/main/peripheral.cpp | 20 +++ iot/main/peripheral.h | 24 ++++ iot/main/radio.cpp | 160 ++++++++++++++++++++++++ iot/main/radio.h | 13 ++ iot/main/settings.h | 33 ++++- 9 files changed, 527 insertions(+), 34 deletions(-) create mode 100644 iot/TTNTestOTAA/TTNTestOTAA.ino create mode 100644 iot/TTNTestOTAA/config.custom.h.example create mode 100644 iot/main/peripheral.cpp create mode 100644 iot/main/peripheral.h create mode 100644 iot/main/radio.cpp create mode 100644 iot/main/radio.h diff --git a/iot/TTNTest/TTNTest.ino b/iot/TTNTest/TTNTest.ino index d3fbc6f..408a713 100644 --- a/iot/TTNTest/TTNTest.ino +++ b/iot/TTNTest/TTNTest.ino @@ -1,5 +1,6 @@ /******************************************************************************* * 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, @@ -9,18 +10,30 @@ * * This example sends a valid LoRaWAN packet with payload "Hello, * world!", using frequency and encryption settings matching those of - * the (early prototype version of) The Things Network. + * the The Things Network. * - * Note: LoRaWAN per sub-band duty-cycle limitation is enforced (1% in g1, - * 0.1% in g2). + * This uses ABP (Activation-by-personalisation), where a DevAddr and + * Session keys are preconfigured (unlike OTAA, where a DevEUI and + * application key is configured, while the DevAddr and session keys are + * assigned/generated in the over-the-air-activation procedure). * - * Change DEVADDR to a unique address! - * See http://thethingsnetwork.org/wiki/AddressSpace + * 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)! * - * Do not forget to define the radio type correctly in config.h. + * To use this sketch, first register your application and device with + * the things network, to set or generate a DevAddr, NwkSKey and + * AppSKey. Each device should have their own unique values for these + * fields. + * + * Do not forget to define the radio type correctly in + * arduino-lmic/project_config/lmic_project_config.h or from your BOARDS.txt. * *******************************************************************************/ + // References: + // [feather] adafruit-feather-m0-radio-with-lora-module.pdf + #include #include #include @@ -29,7 +42,8 @@ // 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). +// DISABLE_JOIN is set in arduino-lmic/project_config/lmic_project_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) { } @@ -71,23 +85,28 @@ void onEvent (ev_t ev) { case EV_JOINED: Serial.println(F("EV_JOINED")); break; - case EV_RFU1: - Serial.println(F("EV_RFU1")); - 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; - break; case EV_TXCOMPLETE: Serial.println(F("EV_TXCOMPLETE (includes waiting for RX windows)")); - if(LMIC.dataLen) { - // data received in rx slot after tx - Serial.print(F("Data Received: ")); - Serial.write(LMIC.frame+LMIC.dataBeg, LMIC.dataLen); - Serial.println(); + if (LMIC.txrxFlags & TXRX_ACK) + Serial.println(F("Received ack")); + if (LMIC.dataLen) { + Serial.println(F("Received ")); + Serial.println(LMIC.dataLen); + Serial.println(F(" bytes of payload")); } // Schedule next transmission os_setTimedCallback(&sendjob, os_getTime()+sec2osticks(TX_INTERVAL), do_send); @@ -108,8 +127,20 @@ void onEvent (ev_t ev) { case EV_LINK_ALIVE: Serial.println(F("EV_LINK_ALIVE")); break; - default: - Serial.println(F("Unknown event")); + /* + || 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; + default: + Serial.print(F("Unknown event: ")); + Serial.println((unsigned) ev); break; } } @@ -127,15 +158,17 @@ void do_send(osjob_t* j){ } void setup() { +// pinMode(13, OUTPUT); + while (!Serial); // wait for Serial to be initialized Serial.begin(115200); + delay(100); // per sample code on RF_95 test Serial.println(F("Starting")); - #define PIN_CS 10 - #define PIN_CS_2 3 - pinMode(PIN_CS, OUTPUT); - pinMode(PIN_CS_2, OUTPUT); - digitalWrite(PIN_CS, LOW); // We want to talk to the RFM 95 - digitalWrite(PIN_CS_2, HIGH); + // Activate the right SPI device + pinMode(10, OUTPUT); + pinMode(3, OUTPUT); + digitalWrite(10, LOW); + digitalWrite(3, HIGH); #ifdef VCC_ENABLE // For Pinoccio Scout boards @@ -159,12 +192,13 @@ void setup() { uint8_t nwkskey[sizeof(NWKSKEY)]; memcpy_P(appskey, APPSKEY, sizeof(APPSKEY)); memcpy_P(nwkskey, NWKSKEY, sizeof(NWKSKEY)); - LMIC_setSession (0x1, DEVADDR, nwkskey, appskey); + LMIC_setSession (0x13, DEVADDR, nwkskey, appskey); #else - // If not running an AVR with PROGMEM, just use the arrays directly - LMIC_setSession (0x1, DEVADDR, NWKSKEY, APPSKEY); + // If not running an AVR with PROGMEM, just use the arrays directly + LMIC_setSession (0x13, DEVADDR, NWKSKEY, APPSKEY); #endif + #if defined(CFG_eu868) // 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 @@ -186,11 +220,21 @@ void setup() { // devices' ping slots. LMIC does not have an easy way to define set this // frequency and support for class B is spotty and untested, so this // frequency is not configured here. + #elif defined(CFG_us915) + // NA-US channels 0-71 are configured automatically + // but only one group of 8 should (a subband) should be active + // TTN recommends the second sub band, 1 in a zero based count. + // https://github.com/TheThingsNetwork/gateway-conf/blob/master/US-global_conf.json + LMIC_selectSubBand(1); + #endif // Disable link check validation LMIC_setLinkCheckMode(0); - // Set data rate and transmit power (note: txpow seems to be ignored by the library) + // TTN uses SF9 for its RX2 window. + LMIC.dn2Dr = DR_SF9; + + // Set data rate and transmit power for uplink LMIC_setDrTxpow(DR_SF7,14); // Start job @@ -198,5 +242,15 @@ void setup() { } void loop() { + unsigned long now; + now = millis(); + if ((now & 512) != 0) { + digitalWrite(13, HIGH); + } + else { + digitalWrite(13, LOW); + } + os_runloop_once(); + } diff --git a/iot/TTNTestOTAA/TTNTestOTAA.ino b/iot/TTNTestOTAA/TTNTestOTAA.ino new file mode 100644 index 0000000..db59ca6 --- /dev/null +++ b/iot/TTNTestOTAA/TTNTestOTAA.ino @@ -0,0 +1,159 @@ +/******************************************************************************* + * Copyright (c) 2015 Thomas Telkamp and Matthijs Kooijman + * + * 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 config.h. + * + *******************************************************************************/ +#define DISABLE_PING +#define DISABLE_BEACONS + +#include +#include +#include + +#include "config.custom.h" + +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 = 60; + + +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")); + + // Disable link check validation (automatically enabled + // during join, but not supported by TTN at this time). + LMIC_setLinkCheckMode(0); + break; + 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; + 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.println(F("Received ")); + Serial.println(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; + default: + Serial.println(F("Unknown event")); + 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(115200); + Serial.println(F("Starting")); + + // Activate the right SPI device + pinMode(10, OUTPUT); + pinMode(3, OUTPUT); + digitalWrite(10, LOW); + digitalWrite(3, HIGH); + + #ifdef VCC_ENABLE + // For Pinoccio Scout boards + pinMode(VCC_ENABLE, OUTPUT); + digitalWrite(VCC_ENABLE, HIGH); + delay(1000); + #endif + + // LMIC init + os_init(); + // Reset the MAC state. Session and pending data transfers will be discarded. + LMIC_reset(); + LMIC_setClockError(5 * MAX_CLOCK_ERROR / 100); + + // Start job (sending automatically starts OTAA too) + do_send(&sendjob); +} + +void loop() { + os_runloop_once(); +} diff --git a/iot/TTNTestOTAA/config.custom.h.example b/iot/TTNTestOTAA/config.custom.h.example new file mode 100644 index 0000000..43e3409 --- /dev/null +++ b/iot/TTNTestOTAA/config.custom.h.example @@ -0,0 +1,26 @@ + +// 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]={ ..... }; +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]={ ...... }; +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. +// The key shown here is the semtech default key. +static const u1_t PROGMEM APPKEY[16] = { ...... }; +void os_getDevKey (u1_t* buf) { memcpy_P(buf, APPKEY, 16);} + + +const lmic_pinmap lmic_pins = { + .nss = 10, + .rxtx = LMIC_UNUSED_PIN, + .rst = 9, + .dio = {2, 6, 7}, +}; diff --git a/iot/main/main.ino b/iot/main/main.ino index 73b100b..0d0698f 100644 --- a/iot/main/main.ino +++ b/iot/main/main.ino @@ -7,6 +7,7 @@ // BAD PRACTICE: For some extremely strange reason, the Arduino IDE doesn't pick up random.cpp like it does our other source files - so we've got to explicitly include it here. If we had control over the build process (which we don't), we've use a Makefile here that handled this better. #include "random.cpp" #include "gps.h" +#include "peripheral.h" void setup() { @@ -15,6 +16,9 @@ void setup() { random_begin(); + peripheral_register(PIN_SPI_CS_RFM95); + peripheral_register(PIN_SPI_CS_SD); + gps_begin(); TinyGPSPlus gps_data = gps_location(); @@ -27,7 +31,10 @@ void setup() { Serial.print("[main] id: "); Serial.println(id); - store_init(); + // Activate microSD card breakout board on the SPI bus + peripheral_unsilence(PIN_SPI_CS_SD); + + store_init(); store_reading(id, gps_data.location); char debug_message[64]; int chars = snprintf(debug_message, 64, "%d-%d-%d %d:%d:%d | %f %f", @@ -43,6 +50,13 @@ void setup() { store_debug(debug_message, chars); store_close(); + // ------------------------------------------------------------------------ + + // Activate the RFM95 + peripheral_unsilence(PIN_SPI_CS_RFM95); + + + power_off(); // Doesn't return } diff --git a/iot/main/peripheral.cpp b/iot/main/peripheral.cpp new file mode 100644 index 0000000..f4ecfa2 --- /dev/null +++ b/iot/main/peripheral.cpp @@ -0,0 +1,20 @@ +#include + +void peripheral_register(int pin_number) { + pinMode(OUTPUT, pin_number); + // Disable the device by default to avoid issues + digitalWrite(pin_number, HIGH); +} + +void peripheral_unsilence(int pin_number) { + digitalWrite(pin_number, LOW); +} + +void peripheral_silence(int pin_number) { + digitalWrite(pin_number, HIGH); +} + +void peripheral_switch(int pin_number_old, int pin_number_new) { + digitalWrite(pin_number_old, HIGH); + digitalWrite(pin_number_new, LOW); +} diff --git a/iot/main/peripheral.h b/iot/main/peripheral.h new file mode 100644 index 0000000..d186a72 --- /dev/null +++ b/iot/main/peripheral.h @@ -0,0 +1,24 @@ +#pragma once + +/** + * Register a new SPI peripheral. + * @param pin_number The pin number of the device's chip select pin. + */ +void peripheral_register(int pin_number); + +/** + * Allows the device with the given chip select pin to talk on the SPI bus. + * @param pin_number The pin number of the chip select pin of the device to allow to talk. + */ +void peripheral_unsilence(int pin_number); +/** + * Stops the device with the given chip select pin from talking on the SPI bus. + * @param pin_number The chip-select pin number of the device to stop. + */ +void peripheral_silence(int pin_number); +/** + * Switches the active device from one to another on the SPI bus. + * @param pin_number_old The chip-select pin number of the old device to switch out from. + * @param pin_number_new The chip-select pin number of the new device to switch in to. + */ +void peripheral_switch(int pin_number_old, int pin_number_new); diff --git a/iot/main/radio.cpp b/iot/main/radio.cpp new file mode 100644 index 0000000..a83ca9e --- /dev/null +++ b/iot/main/radio.cpp @@ -0,0 +1,160 @@ +#pragma once + +#include +#include +#include + +// Global static variable that's used to detect when LMIC has finished doing it's thing +static bool is_sending_complete = false; + + +static osjob_t sendjob; + +void radio_init() { + // 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, APPSKEY, sizeof(APPSKEY)); + memcpy_P(nwkskey, NWKSKEY, sizeof(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, so be sure to configure the full frequency range of + // your network here (unless your network autoconfigures them). + // Setting up channels should happen after LMIC_setSession, as that + // configures the minimal channel set. + LMIC_setupChannel(0, 868100000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band + LMIC_setupChannel(1, 868300000, DR_RANGE_MAP(DR_SF12, DR_SF7B), BAND_CENTI); // g-band + LMIC_setupChannel(2, 868500000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band + LMIC_setupChannel(3, 867100000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band + LMIC_setupChannel(4, 867300000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band + LMIC_setupChannel(5, 867500000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band + LMIC_setupChannel(6, 867700000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band + LMIC_setupChannel(7, 867900000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band + LMIC_setupChannel(8, 868800000, DR_RANGE_MAP(DR_FSK, DR_FSK), BAND_MILLI); // g2-band + // TTN defines an additional channel at 869.525Mhz using SF9 for class B + // devices' ping slots. LMIC does not have an easy way to define set this + // frequency and support for class B is spotty and untested, so this + // frequency is not configured here. + + // Disable link check validation + LMIC_setLinkCheckMode(0); + + // Set data rate and transmit power (note: txpow seems to be ignored by the library) + LMIC_setDrTxpow(DR_SF7,14); +} + +/** + * Sends a specified message via LoRaWAN. + * @param data The message to send. + * @param length The length of the given message. + */ +bool radio_send(uint8_t* data, int length) { + // Check if there is not a current TX/RX job running + if (LMIC.opmode & OP_TXRXPEND) { + Serial.println(F("OP_TXRXPEND: There's already a job running, not sending")); + return false; + } + // Prepare upstream data transmission at the next possible time. + LMIC_setTxData2(1, data, length, 0); + Serial.println(F("Packet queued")); + + // Run the LMIC loop, but only until it's finished sending the packet + while (!is_sending_complete) { + os_runloop_once(); + } + + // Reset it for next time (just in case) + is_sending_complete = false; + + return true; +} + +// 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) { } + +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")); + break; + 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; + break; + case EV_TXCOMPLETE: + Serial.println(F("EV_TXCOMPLETE (includes waiting for RX windows)")); + if(LMIC.dataLen) { + // data received in rx slot after tx + Serial.print(F("Data Received: ")); + Serial.write(LMIC.frame+LMIC.dataBeg, LMIC.dataLen); + Serial.println(); + } + // We're done! + is_sending_complete = true; + 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; + default: + Serial.println(F("Unknown event")); + break; + } +} diff --git a/iot/main/radio.h b/iot/main/radio.h new file mode 100644 index 0000000..d572f63 --- /dev/null +++ b/iot/main/radio.h @@ -0,0 +1,13 @@ +#pragma once + +/** + * Initialises the RFM95 LoRa radio. + */ +void radio_init(); + +/** + * Sends a specified message via LoRaWAN. + * @param data The message to send. + * @param length The length of the given message. + */ +void radio_send(byte* data, int length); diff --git a/iot/main/settings.h b/iot/main/settings.h index df2f353..d49ac0e 100644 --- a/iot/main/settings.h +++ b/iot/main/settings.h @@ -1,5 +1,7 @@ #pragma once +#include + ////////////////////////////////// ////////////// Main ////////////// ////////////////////////////////// @@ -7,23 +9,44 @@ // The speed at which we should talk over our main hardware serial connection. #define BAUD_PC 115200 -// Multiple devices can use the same SPI data pin AFAIKT, but some libraries *cough* SD *cough* are too stupid to figure out which pin it is on their own. +// Multiple devices can use the same SPI data pin AFAIKT, but some libraries +// *cough* SD *cough* are too stupid to figure out which pin it is on their own. #define PIN_SPI_DATA 9 +// The 'done' pin to pulse to signal to the TPL5111 #define PIN_TPL_DONE 8 +///////////// +/// RFM95 /// +///////////// + +// Pin mapping +const lmic_pinmap lmic_pins = { + .nss = 10, + .rxtx = LMIC_UNUSED_PIN, + .rst = 9, + .dio = {2, 6, 7}, +}; + +// The SPI chip-select pin for the RFM 95 +#define PIN_SPI_CS_RFM95 10 + ///////////////////////////////// ////////////// GPS ////////////// ///////////////////////////////// // The *TX* gin of the GPS device. -// This is swapped because we receive the GPS device's message on our side on the RX pin, and the GPS device transmits messages on the TX. +// This is swapped because we receive the GPS device's message on our side on +// the RX pin, and the GPS device transmits messages on the TX. #define PIN_GPS_RX 5 // The *RX* pin on the GPS device. -// This is swapped because where the GPs device is receiving, we aresending and vice versa. +// This is swapped because where the GPs device is receiving, we aresending and +// vice versa. // The TX / RX here are according to *our* side, not the GPS device's side. #define PIN_GPS_TX 4 -// The speed at which we should talk to the GPS device. Some GPS devices require a certain speed in order to use certain commands, so it's important that you check the datasheets for the device you're using. +// The speed at which we should talk to the GPS device. Some GPS devices +// require a certain speed in order to use certain commands, so it's important +// that you check the datasheets for the device you're using. // 9600 is the correct speed for a NEO-6M. #define BAUD_GPS 9600 @@ -32,7 +55,7 @@ ////////////////////////////////// // The chip select pin that activates the connection to the microSD card over SPI. -#define PIN_SD_SPI_CHIP_SELECT 3 +#define PIN_SPI_CS_SD 3 // The filename on the microSD card to store data in. #define SD_FILENAME "data.tsv" From b0f4b56c32c7bc8b9a9a5aa449e1bca9fe5dcf55 Mon Sep 17 00:00:00 2001 From: Starbeamrainbowlabs Date: Mon, 24 Jun 2019 14:11:31 +0100 Subject: [PATCH 08/34] Create moar lmic test programs --- iot/TTNTest/TTNTest.ino | 105 ++------- iot/TTNTestABPNew/TTNTest.ino | 256 ++++++++++++++++++++++ iot/TTNTestABPNew/config.custom.h.example | 15 ++ 3 files changed, 293 insertions(+), 83 deletions(-) create mode 100644 iot/TTNTestABPNew/TTNTest.ino create mode 100644 iot/TTNTestABPNew/config.custom.h.example diff --git a/iot/TTNTest/TTNTest.ino b/iot/TTNTest/TTNTest.ino index 408a713..a1585ca 100644 --- a/iot/TTNTest/TTNTest.ino +++ b/iot/TTNTest/TTNTest.ino @@ -1,6 +1,5 @@ /******************************************************************************* * 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, @@ -10,30 +9,18 @@ * * This example sends a valid LoRaWAN packet with payload "Hello, * world!", using frequency and encryption settings matching those of - * the The Things Network. + * the (early prototype version of) The Things Network. * - * This uses ABP (Activation-by-personalisation), where a DevAddr and - * Session keys are preconfigured (unlike OTAA, where a DevEUI and - * application key is configured, while the DevAddr and session keys are - * assigned/generated in the over-the-air-activation procedure). + * Note: LoRaWAN per sub-band duty-cycle limitation is enforced (1% in g1, + * 0.1% in g2). * - * 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)! + * Change DEVADDR to a unique address! + * See http://thethingsnetwork.org/wiki/AddressSpace * - * To use this sketch, first register your application and device with - * the things network, to set or generate a DevAddr, NwkSKey and - * AppSKey. Each device should have their own unique values for these - * fields. - * - * Do not forget to define the radio type correctly in - * arduino-lmic/project_config/lmic_project_config.h or from your BOARDS.txt. + * Do not forget to define the radio type correctly in config.h. * *******************************************************************************/ - // References: - // [feather] adafruit-feather-m0-radio-with-lora-module.pdf - #include #include #include @@ -42,8 +29,7 @@ // 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 arduino-lmic/project_config/lmic_project_config.h, -// otherwise the linker will complain). +// 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) { } @@ -85,28 +71,23 @@ void onEvent (ev_t ev) { case EV_JOINED: Serial.println(F("EV_JOINED")); 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_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; + 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.println(F("Received ")); - Serial.println(LMIC.dataLen); - Serial.println(F(" bytes of payload")); + if(LMIC.dataLen) { + // data received in rx slot after tx + Serial.print(F("Data Received: ")); + Serial.write(LMIC.frame+LMIC.dataBeg, LMIC.dataLen); + Serial.println(); } // Schedule next transmission os_setTimedCallback(&sendjob, os_getTime()+sec2osticks(TX_INTERVAL), do_send); @@ -127,20 +108,8 @@ void onEvent (ev_t ev) { 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; - default: - Serial.print(F("Unknown event: ")); - Serial.println((unsigned) ev); + default: + Serial.println(F("Unknown event")); break; } } @@ -158,17 +127,8 @@ void do_send(osjob_t* j){ } void setup() { -// pinMode(13, OUTPUT); - while (!Serial); // wait for Serial to be initialized Serial.begin(115200); - delay(100); // per sample code on RF_95 test Serial.println(F("Starting")); - - // Activate the right SPI device - pinMode(10, OUTPUT); - pinMode(3, OUTPUT); - digitalWrite(10, LOW); - digitalWrite(3, HIGH); #ifdef VCC_ENABLE // For Pinoccio Scout boards @@ -192,13 +152,12 @@ void setup() { uint8_t nwkskey[sizeof(NWKSKEY)]; memcpy_P(appskey, APPSKEY, sizeof(APPSKEY)); memcpy_P(nwkskey, NWKSKEY, sizeof(NWKSKEY)); - LMIC_setSession (0x13, DEVADDR, nwkskey, appskey); + LMIC_setSession (0x1, DEVADDR, nwkskey, appskey); #else - // If not running an AVR with PROGMEM, just use the arrays directly - LMIC_setSession (0x13, DEVADDR, NWKSKEY, APPSKEY); + // If not running an AVR with PROGMEM, just use the arrays directly + LMIC_setSession (0x1, DEVADDR, NWKSKEY, APPSKEY); #endif - #if defined(CFG_eu868) // 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 @@ -220,21 +179,11 @@ void setup() { // devices' ping slots. LMIC does not have an easy way to define set this // frequency and support for class B is spotty and untested, so this // frequency is not configured here. - #elif defined(CFG_us915) - // NA-US channels 0-71 are configured automatically - // but only one group of 8 should (a subband) should be active - // TTN recommends the second sub band, 1 in a zero based count. - // https://github.com/TheThingsNetwork/gateway-conf/blob/master/US-global_conf.json - LMIC_selectSubBand(1); - #endif // 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 + // Set data rate and transmit power (note: txpow seems to be ignored by the library) LMIC_setDrTxpow(DR_SF7,14); // Start job @@ -242,15 +191,5 @@ void setup() { } void loop() { - unsigned long now; - now = millis(); - if ((now & 512) != 0) { - digitalWrite(13, HIGH); - } - else { - digitalWrite(13, LOW); - } - os_runloop_once(); - } diff --git a/iot/TTNTestABPNew/TTNTest.ino b/iot/TTNTestABPNew/TTNTest.ino new file mode 100644 index 0000000..408a713 --- /dev/null +++ b/iot/TTNTestABPNew/TTNTest.ino @@ -0,0 +1,256 @@ +/******************************************************************************* + * 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 ABP (Activation-by-personalisation), where a DevAddr and + * Session keys are preconfigured (unlike OTAA, where a DevEUI and + * application key is configured, while the DevAddr and session keys are + * assigned/generated in the over-the-air-activation procedure). + * + * 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 a DevAddr, NwkSKey and + * AppSKey. Each device should have their own unique values for these + * fields. + * + * Do not forget to define the radio type correctly in + * arduino-lmic/project_config/lmic_project_config.h or from your BOARDS.txt. + * + *******************************************************************************/ + + // References: + // [feather] adafruit-feather-m0-radio-with-lora-module.pdf + +#include +#include +#include + +#include "config.custom.h" + +// 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 arduino-lmic/project_config/lmic_project_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 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 = 60; + +// Pin mapping +const lmic_pinmap lmic_pins = { + .nss = 10, + .rxtx = LMIC_UNUSED_PIN, + .rst = 9, + .dio = {2, 6, 7}, +}; + +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")); + 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.println(F("Received ")); + Serial.println(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; + 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() { +// pinMode(13, OUTPUT); + while (!Serial); // wait for Serial to be initialized + Serial.begin(115200); + delay(100); // per sample code on RF_95 test + Serial.println(F("Starting")); + + // Activate the right SPI device + pinMode(10, OUTPUT); + pinMode(3, OUTPUT); + digitalWrite(10, LOW); + digitalWrite(3, HIGH); + + #ifdef VCC_ENABLE + // For Pinoccio Scout boards + pinMode(VCC_ENABLE, OUTPUT); + digitalWrite(VCC_ENABLE, HIGH); + delay(1000); + #endif + + // 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, APPSKEY, sizeof(APPSKEY)); + memcpy_P(nwkskey, NWKSKEY, sizeof(NWKSKEY)); + LMIC_setSession (0x13, DEVADDR, nwkskey, appskey); + #else + // If not running an AVR with PROGMEM, just use the arrays directly + LMIC_setSession (0x13, DEVADDR, NWKSKEY, APPSKEY); + #endif + + #if defined(CFG_eu868) + // 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, so be sure to configure the full frequency range of + // your network here (unless your network autoconfigures them). + // Setting up channels should happen after LMIC_setSession, as that + // configures the minimal channel set. + LMIC_setupChannel(0, 868100000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band + LMIC_setupChannel(1, 868300000, DR_RANGE_MAP(DR_SF12, DR_SF7B), BAND_CENTI); // g-band + LMIC_setupChannel(2, 868500000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band + LMIC_setupChannel(3, 867100000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band + LMIC_setupChannel(4, 867300000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band + LMIC_setupChannel(5, 867500000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band + LMIC_setupChannel(6, 867700000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band + LMIC_setupChannel(7, 867900000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band + LMIC_setupChannel(8, 868800000, DR_RANGE_MAP(DR_FSK, DR_FSK), BAND_MILLI); // g2-band + // TTN defines an additional channel at 869.525Mhz using SF9 for class B + // devices' ping slots. LMIC does not have an easy way to define set this + // frequency and support for class B is spotty and untested, so this + // frequency is not configured here. + #elif defined(CFG_us915) + // NA-US channels 0-71 are configured automatically + // but only one group of 8 should (a subband) should be active + // TTN recommends the second sub band, 1 in a zero based count. + // https://github.com/TheThingsNetwork/gateway-conf/blob/master/US-global_conf.json + LMIC_selectSubBand(1); + #endif + + // 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 + LMIC_setDrTxpow(DR_SF7,14); + + // Start job + do_send(&sendjob); +} + +void loop() { + unsigned long now; + now = millis(); + if ((now & 512) != 0) { + digitalWrite(13, HIGH); + } + else { + digitalWrite(13, LOW); + } + + os_runloop_once(); + +} diff --git a/iot/TTNTestABPNew/config.custom.h.example b/iot/TTNTestABPNew/config.custom.h.example new file mode 100644 index 0000000..65a82ea --- /dev/null +++ b/iot/TTNTestABPNew/config.custom.h.example @@ -0,0 +1,15 @@ +// Copy this file to have the name "config.custom.h", and fill in the 3 sections below + +// LoRaWAN NwkSKey, network session key +// Big endian (i.e. msb) +static const PROGMEM u1_t NWKSKEY[16] = { .... }; + +// LoRaWAN AppSKey, application session key +// Big endian (i.e. msb) +static const u1_t PROGMEM APPSKEY[16] = { .... }; + +// LoRaWAN end-device address (DevAddr) +// See http://thethingsnetwork.org/wiki/AddressSpace +// The library converts the address to network byte order as needed. +// Big endian (i.e. msb) +static const u4_t DEVADDR = 0x....; // <-- Change this address for every node! From b9122f99f071ac118db55b1ebbc65b8a0a22b5b5 Mon Sep 17 00:00:00 2001 From: Starbeamrainbowlabs Date: Mon, 24 Jun 2019 14:12:04 +0100 Subject: [PATCH 09/34] Rename test program --- iot/TTNTestABPNew/{TTNTest.ino => TTNTestABPNew.ino} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename iot/TTNTestABPNew/{TTNTest.ino => TTNTestABPNew.ino} (100%) diff --git a/iot/TTNTestABPNew/TTNTest.ino b/iot/TTNTestABPNew/TTNTestABPNew.ino similarity index 100% rename from iot/TTNTestABPNew/TTNTest.ino rename to iot/TTNTestABPNew/TTNTestABPNew.ino From 18ba73c99b1d21c27e49cd83a36a33840a09f3c0 Mon Sep 17 00:00:00 2001 From: Starbeamrainbowlabs Date: Mon, 24 Jun 2019 14:27:22 +0100 Subject: [PATCH 10/34] [iot/main] Fix compilation errors --- .gitignore | 1 + iot/main/gps.h | 2 +- iot/main/radio.cpp | 4 ++-- iot/main/radio.h | 2 +- iot/main/settings.custom.h.example | 13 +++++++++++++ iot/main/settings.h | 20 ++++++++++++++++---- 6 files changed, 34 insertions(+), 8 deletions(-) create mode 100644 iot/main/settings.custom.h.example diff --git a/.gitignore b/.gitignore index 3214de6..e1478a7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ config.custom.h +settings.custom.h Reports/*.pdf diff --git a/iot/main/gps.h b/iot/main/gps.h index 40d2ee4..85c79b5 100644 --- a/iot/main/gps.h +++ b/iot/main/gps.h @@ -22,7 +22,7 @@ void gps_fetch(); * Call gps_fetch() first. * @return TinyGPSLocation The current location. */ -TinyGPSLocation gps_info(); +TinyGPSPlus gps_info(); /** * Ends the connection to the GPS device and puts it to sleep in order to save diff --git a/iot/main/radio.cpp b/iot/main/radio.cpp index a83ca9e..6f76a70 100644 --- a/iot/main/radio.cpp +++ b/iot/main/radio.cpp @@ -1,9 +1,9 @@ -#pragma once - #include #include #include +#include "settings.h" + // Global static variable that's used to detect when LMIC has finished doing it's thing static bool is_sending_complete = false; diff --git a/iot/main/radio.h b/iot/main/radio.h index d572f63..3f1db5e 100644 --- a/iot/main/radio.h +++ b/iot/main/radio.h @@ -10,4 +10,4 @@ void radio_init(); * @param data The message to send. * @param length The length of the given message. */ -void radio_send(byte* data, int length); +bool radio_send(byte* data, int length); diff --git a/iot/main/settings.custom.h.example b/iot/main/settings.custom.h.example new file mode 100644 index 0000000..63bad2b --- /dev/null +++ b/iot/main/settings.custom.h.example @@ -0,0 +1,13 @@ + +// LoRaWAN NwkSKey, network session key +// This is the default Semtech key, which is used by the early prototype TTN +// network. +static const PROGMEM u1_t NWKSKEY[16] = { .... }; + +// LoRaWAN AppSKey, application session key +// This is the default Semtech key, which is used by the early prototype TTN +// network. +static const u1_t PROGMEM APPSKEY[16] = { ..... }; + +// LoRaWAN end-device address (DevAddr) +static const u4_t DEVADDR = 0x....; // <-- Change this address for every node! diff --git a/iot/main/settings.h b/iot/main/settings.h index d49ac0e..a93a3b3 100644 --- a/iot/main/settings.h +++ b/iot/main/settings.h @@ -1,6 +1,17 @@ #pragma once -#include +#include +#include +#include + +#include "settings.custom.h" + +/* + * This is the main settings file - customise it to match your setup. + * Don't forget that you need to take a copy of settings.custom.h.example as + * settings.custom.h, and fill in the private keys the program needs to talk + * over LORaWAN. + */ ////////////////////////////////// ////////////// Main ////////////// @@ -20,16 +31,17 @@ /// RFM95 /// ///////////// +// The SPI chip-select pin for the RFM 95 +#define PIN_SPI_CS_RFM95 10 + // Pin mapping const lmic_pinmap lmic_pins = { - .nss = 10, + .nss = PIN_SPI_CS_RFM95, .rxtx = LMIC_UNUSED_PIN, .rst = 9, .dio = {2, 6, 7}, }; -// The SPI chip-select pin for the RFM 95 -#define PIN_SPI_CS_RFM95 10 ///////////////////////////////// ////////////// GPS ////////////// From 6a203233fd002785f0bafe21f2324111784b1fb9 Mon Sep 17 00:00:00 2001 From: Starbeamrainbowlabs Date: Mon, 24 Jun 2019 14:31:33 +0100 Subject: [PATCH 11/34] Fix compilation errors --- iot/main/main.ino | 8 +++++--- iot/main/storage.cpp | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/iot/main/main.ino b/iot/main/main.ino index 0d0698f..c2b1a2b 100644 --- a/iot/main/main.ino +++ b/iot/main/main.ino @@ -8,6 +8,8 @@ #include "random.cpp" #include "gps.h" #include "peripheral.h" +#include "storage.h" +#include "power.h" void setup() { @@ -21,10 +23,10 @@ void setup() { gps_begin(); - TinyGPSPlus gps_data = gps_location(); + TinyGPSPlus gps_data = gps_info(); gps_end(); - Serial.print("[main] Location: ("); Serial.print(loc.lat()); Serial.print(", "); Serial.print(loc.lng()); Serial.println(")"); + Serial.print("[main] Location: ("); Serial.print(gps_data.location.lat()); Serial.print(", "); Serial.print(gps_data.location.lng()); Serial.println(")"); uint32_t id = random_get(); @@ -45,7 +47,7 @@ void setup() { gps_data.time.minute(), gps_data.time.second(), gps_data.location.lat(), - gps_data.location.lng(), + gps_data.location.lng() ); store_debug(debug_message, chars); store_close(); diff --git a/iot/main/storage.cpp b/iot/main/storage.cpp index 3448d1a..8acbbaa 100644 --- a/iot/main/storage.cpp +++ b/iot/main/storage.cpp @@ -8,7 +8,7 @@ File _debug_handle; void store_init() { // NOTE: If this doesn't work, then we need to use pinMode() & specify the data pin here instead. - if(!SD.begin(PIN_SD_SPI_CHIP_SELECT)) { + if(!SD.begin(PIN_SPI_CS_SD)) { Serial.println(F("Error: Failed to initialise connection to microSD card")); while(true) { delay(1); } // Pseudo-halt, but uses delay() to ensure we keep passing back control to allow easy re-programming } From 1cf542adcd8dd2fec3690720635940eef97a6efa Mon Sep 17 00:00:00 2001 From: Starbeamrainbowlabs Date: Mon, 24 Jun 2019 14:47:14 +0100 Subject: [PATCH 12/34] Fix seriously nasty linking errors --- .gitignore | 2 +- ...ustom.h.example => settings.custom.cpp.example} | 13 +++++++++++++ iot/main/settings.custom.h | 14 ++++++++++++++ iot/main/settings.h | 12 +++--------- iot/main/{radio.cpp => transmission.cpp} | 4 ++-- iot/main/{radio.h => transmission.h} | 4 ++-- 6 files changed, 35 insertions(+), 14 deletions(-) rename iot/main/{settings.custom.h.example => settings.custom.cpp.example} (71%) create mode 100644 iot/main/settings.custom.h rename iot/main/{radio.cpp => transmission.cpp} (98%) rename iot/main/{radio.h => transmission.h} (75%) diff --git a/.gitignore b/.gitignore index e1478a7..75bcb86 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ config.custom.h -settings.custom.h +settings.custom.cpp Reports/*.pdf diff --git a/iot/main/settings.custom.h.example b/iot/main/settings.custom.cpp.example similarity index 71% rename from iot/main/settings.custom.h.example rename to iot/main/settings.custom.cpp.example index 63bad2b..11fdf42 100644 --- a/iot/main/settings.custom.h.example +++ b/iot/main/settings.custom.cpp.example @@ -1,3 +1,9 @@ +#include +#include +#include + +#include "settings.h" + // LoRaWAN NwkSKey, network session key // This is the default Semtech key, which is used by the early prototype TTN @@ -11,3 +17,10 @@ static const u1_t PROGMEM APPSKEY[16] = { ..... }; // LoRaWAN end-device address (DevAddr) static const u4_t DEVADDR = 0x....; // <-- Change this address for every node! + +const lmic_pinmap lmic_pins = { + .nss = PIN_SPI_CS_RFM95, + .rxtx = LMIC_UNUSED_PIN, + .rst = 9, + .dio = {2, 6, 7}, +}; diff --git a/iot/main/settings.custom.h b/iot/main/settings.custom.h new file mode 100644 index 0000000..28e4fee --- /dev/null +++ b/iot/main/settings.custom.h @@ -0,0 +1,14 @@ +#include +#include +#include + +/** + * Don't edit this file - you want to make a copy of settings.custom.cpp, call + * it settings.cpp, and edit that instead. + */ + +extern const PROGMEM u1_t NWKSKEY[16]; +extern const u1_t PROGMEM APPSKEY[16]; +extern const u4_t DEVADDR; + +extern const lmic_pinmap lmic_pins; diff --git a/iot/main/settings.h b/iot/main/settings.h index a93a3b3..4fa4001 100644 --- a/iot/main/settings.h +++ b/iot/main/settings.h @@ -8,8 +8,8 @@ /* * This is the main settings file - customise it to match your setup. - * Don't forget that you need to take a copy of settings.custom.h.example as - * settings.custom.h, and fill in the private keys the program needs to talk + * Don't forget that you need to take a copy of settings.custom.cpp.example as + * settings.custom.cpp, and fill in the private keys the program needs to talk * over LORaWAN. */ @@ -34,13 +34,7 @@ // The SPI chip-select pin for the RFM 95 #define PIN_SPI_CS_RFM95 10 -// Pin mapping -const lmic_pinmap lmic_pins = { - .nss = PIN_SPI_CS_RFM95, - .rxtx = LMIC_UNUSED_PIN, - .rst = 9, - .dio = {2, 6, 7}, -}; +// Pin mapping - see settings.custom.cpp ///////////////////////////////// diff --git a/iot/main/radio.cpp b/iot/main/transmission.cpp similarity index 98% rename from iot/main/radio.cpp rename to iot/main/transmission.cpp index 6f76a70..c7aa1c1 100644 --- a/iot/main/radio.cpp +++ b/iot/main/transmission.cpp @@ -10,7 +10,7 @@ static bool is_sending_complete = false; static osjob_t sendjob; -void radio_init() { +void transmit_init() { // LMIC init os_init(); // Reset the MAC state. Session and pending data transfers will be discarded. @@ -66,7 +66,7 @@ void radio_init() { * @param data The message to send. * @param length The length of the given message. */ -bool radio_send(uint8_t* data, int length) { +bool transmit_send(uint8_t* data, int length) { // Check if there is not a current TX/RX job running if (LMIC.opmode & OP_TXRXPEND) { Serial.println(F("OP_TXRXPEND: There's already a job running, not sending")); diff --git a/iot/main/radio.h b/iot/main/transmission.h similarity index 75% rename from iot/main/radio.h rename to iot/main/transmission.h index 3f1db5e..38a9bc4 100644 --- a/iot/main/radio.h +++ b/iot/main/transmission.h @@ -3,11 +3,11 @@ /** * Initialises the RFM95 LoRa radio. */ -void radio_init(); +void transmit_init(); /** * Sends a specified message via LoRaWAN. * @param data The message to send. * @param length The length of the given message. */ -bool radio_send(byte* data, int length); +bool transmit_send(byte* data, int length); From b779d16690a654dc1295867ece16277bba5ac768 Mon Sep 17 00:00:00 2001 From: Starbeamrainbowlabs Date: Mon, 24 Jun 2019 15:43:04 +0100 Subject: [PATCH 14/34] Weave LMIC into the main sketch, but its now made it too big :-/ --- README.md | 14 ++++++++++++++ iot/main/main.ino | 9 +++++++-- iot/main/settings.h | 1 - iot/main/transmission.cpp | 1 + 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 52b0f53..9d9ccad 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,20 @@ - [Project box](https://smile.amazon.co.uk/waterproof-plastic-Enclosure-Power-junction-Grey/dp/B01JHBHNMA/) - if we can't 3D print one - [TPL-5111](https://www.adafruit.com/product/3573) - also available from Pimoroni +## Getting Started +Run `bash ./build setup` from the command line at the root of this repository. + +### IoT Device + - Copy `settings.custom.cpp.example` to `settings.custom.cpp` and fill in the fields there + - Review `settings.h` to make sure it matches your setup + - Edit `iot/libraries/arduino-lmic/src/lmic/config.h`, and add the following to the bottom: + +```c++ +#define DISABLE_PING +#define DISABLE_BEACONS +``` + + - Copy the folders in `iot/libraries` to your Arduino IDE libraries folder ## Useful Links - Entropy extraction from the watchdog timer vs the internal clock: https://github.com/taoyuan/Entropy diff --git a/iot/main/main.ino b/iot/main/main.ino index c2b1a2b..9556f69 100644 --- a/iot/main/main.ino +++ b/iot/main/main.ino @@ -1,8 +1,8 @@ +#include "settings.h" #include #include #include -#include "settings.h" #include "random.h" // BAD PRACTICE: For some extremely strange reason, the Arduino IDE doesn't pick up random.cpp like it does our other source files - so we've got to explicitly include it here. If we had control over the build process (which we don't), we've use a Makefile here that handled this better. #include "random.cpp" @@ -10,6 +10,7 @@ #include "peripheral.h" #include "storage.h" #include "power.h" +#include "transmission.h" void setup() { @@ -55,8 +56,12 @@ void setup() { // ------------------------------------------------------------------------ // Activate the RFM95 - peripheral_unsilence(PIN_SPI_CS_RFM95); + peripheral_switch(PIN_SPI_CS_SD, PIN_SPI_CS_RFM95); + uint8_t message[] = "testing"; + + transmit_init(); + transmit_send(message, 7); power_off(); // Doesn't return diff --git a/iot/main/settings.h b/iot/main/settings.h index 4fa4001..3808602 100644 --- a/iot/main/settings.h +++ b/iot/main/settings.h @@ -36,7 +36,6 @@ // Pin mapping - see settings.custom.cpp - ///////////////////////////////// ////////////// GPS ////////////// ///////////////////////////////// diff --git a/iot/main/transmission.cpp b/iot/main/transmission.cpp index c7aa1c1..5f61a6c 100644 --- a/iot/main/transmission.cpp +++ b/iot/main/transmission.cpp @@ -11,6 +11,7 @@ static bool is_sending_complete = false; static osjob_t sendjob; void transmit_init() { + Serial.println(F("Initialising LMIC")); // LMIC init os_init(); // Reset the MAC state. Session and pending data transfers will be discarded. From 159a87db17233d0abf759bb44e98121c5e1b80d8 Mon Sep 17 00:00:00 2001 From: Starbeamrainbowlabs Date: Mon, 24 Jun 2019 15:47:00 +0100 Subject: [PATCH 15/34] Reduce memory usage --- iot/main/gps.cpp | 4 ++-- iot/main/main.ino | 6 +++--- iot/main/power.cpp | 13 +++++++------ iot/main/power.h | 4 ++-- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/iot/main/gps.cpp b/iot/main/gps.cpp index bbfe2dd..4f49d58 100644 --- a/iot/main/gps.cpp +++ b/iot/main/gps.cpp @@ -12,7 +12,7 @@ void gps_begin() { } void gps_fetch() { - Serial.print("[gps] Getting location: "); + Serial.print(F("[gps] Getting location: ")); uint32_t ms_last_update = millis(); // We WILL discover our location - if it's the last thing we do! while(true) { @@ -40,7 +40,7 @@ void gps_fetch() { } // Hooray! - Serial.println("ok"); + Serial.println(F("ok")); return; } } diff --git a/iot/main/main.ino b/iot/main/main.ino index 9556f69..7dcd296 100644 --- a/iot/main/main.ino +++ b/iot/main/main.ino @@ -15,7 +15,7 @@ void setup() { Serial.begin(BAUD_PC); - Serial.println("[main] Beginning collection sequence."); + Serial.println(F("[main] Beginning collection sequence.")); random_begin(); @@ -27,11 +27,11 @@ void setup() { TinyGPSPlus gps_data = gps_info(); gps_end(); - Serial.print("[main] Location: ("); Serial.print(gps_data.location.lat()); Serial.print(", "); Serial.print(gps_data.location.lng()); Serial.println(")"); + Serial.print(F("[main] Location: (")); Serial.print(gps_data.location.lat()); Serial.print(F(", ")); Serial.print(gps_data.location.lng()); Serial.println(F(")")); uint32_t id = random_get(); - Serial.print("[main] id: "); + Serial.print(F("[main] id: ")); Serial.println(id); // Activate microSD card breakout board on the SPI bus diff --git a/iot/main/power.cpp b/iot/main/power.cpp index 7dbf3f0..082e395 100644 --- a/iot/main/power.cpp +++ b/iot/main/power.cpp @@ -2,19 +2,20 @@ #include "settings.h" - +/* void power_gps_wakeup() { - Serial.println("Warn: GPS wakeup isn't implemented yet."); + Serial.println(F("Warn: GPS wakeup isn't implemented yet.")); } void power_gps_standby() { - Serial.println("Warn: GPS standby isn't implemented yet."); + Serial.println(F("Warn: GPS standby isn't implemented yet.")); } +*/ void power_off() { - Serial.println(F("[power] Beginning shutdown sequence")); - Serial.println(F("[power] Switching GPS module to standby")); - power_gps_standby(); + Serial.println(F("[power] Shutting down")); + // Serial.println(F("[power] Switching GPS module to standby")); + // power_gps_standby(); Serial.println(F("[power] Signalling TPL5111")); pinMode(PIN_TPL_DONE, OUTPUT); diff --git a/iot/main/power.h b/iot/main/power.h index 0f9da8f..6e4f006 100644 --- a/iot/main/power.h +++ b/iot/main/power.h @@ -2,11 +2,11 @@ /** * Wakes the GPS module up from standby. */ -void power_gps_wakeup(); +// void power_gps_wakeup(); /** * Puts the GPS module into standby. */ -void power_gps_standby(); +// void power_gps_standby(); /** * Signals that our work is complete and that we can turn off now to the From bdf9d4dd161721add7cc281c15fe4236e77e9009 Mon Sep 17 00:00:00 2001 From: Starbeamrainbowlabs Date: Wed, 26 Jun 2019 15:46:07 +0100 Subject: [PATCH 17/34] Extensively refactor to reduce resource usage. Looks like LMIC is still an issue though :-/ --- .gitmodules | 3 +++ iot/libraries/Arduino-MemoryFree | 1 + iot/main/gps.cpp | 46 +++++++++++++++++++++----------- iot/main/gps.h | 19 +++++++------ iot/main/main.ino | 37 +++++++++++++------------ iot/main/peripheral.cpp | 8 +++--- iot/main/peripheral.h | 8 +++--- iot/main/power.cpp | 10 ------- iot/main/power.h | 9 ------- iot/main/settings.h | 10 +++++-- iot/main/storage.cpp | 19 ++++++------- iot/main/storage.h | 2 +- iot/main/transmission.cpp | 33 ++++++++--------------- 13 files changed, 101 insertions(+), 104 deletions(-) create mode 160000 iot/libraries/Arduino-MemoryFree diff --git a/.gitmodules b/.gitmodules index c410298..9bf6efd 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,3 +10,6 @@ [submodule "iot/libraries/arduino-lmic"] path = iot/libraries/arduino-lmic url = https://github.com/matthijskooijman/arduino-lmic.git +[submodule "iot/libraries/Arduino-MemoryFree"] + path = iot/libraries/Arduino-MemoryFree + url = https://github.com/mpflaga/Arduino-MemoryFree.git diff --git a/iot/libraries/Arduino-MemoryFree b/iot/libraries/Arduino-MemoryFree new file mode 160000 index 0000000..aed5cc5 --- /dev/null +++ b/iot/libraries/Arduino-MemoryFree @@ -0,0 +1 @@ +Subproject commit aed5cc5e28f8f01afc41a625151c184a8e94e32e diff --git a/iot/main/gps.cpp b/iot/main/gps.cpp index 4f49d58..77b0d5a 100644 --- a/iot/main/gps.cpp +++ b/iot/main/gps.cpp @@ -1,33 +1,35 @@ #include "gps.h" #include "settings.h" -// The GPS message decoder -TinyGPSPlus gps; // The serial connection to the GPS device -SoftwareSerial serial_gps(PIN_GPS_RX, PIN_GPS_TX); +SoftwareSerial* serial_gps; void gps_begin() { + serial_gps = new SoftwareSerial(PIN_GPS_RX, PIN_GPS_TX); // Initialise the software-based serial connection to the GPS device - serial_gps.begin(BAUD_GPS); + serial_gps->begin(BAUD_GPS); } -void gps_fetch() { - Serial.print(F("[gps] Getting location: ")); +GPSLocation gps_fetch() { + // The GPS message decoder + TinyGPSPlus gps; + GPSLocation result; + + Serial.print(F("[gps] Working: ")); uint32_t ms_last_update = millis(); // We WILL discover our location - if it's the last thing we do! while(true) { if(millis() > 5000 && gps.charsProcessed() < 10) { - Serial.println(F("\n[error] Failed to initialise GPS device.")); + Serial.println(F("\n[error] GPS device init failed")); while(true) { delay(1); } } // Make sure there's something to read - if(serial_gps.available() <= 0) { + if(serial_gps->available() <= 0) continue; - } // If it failed, go around again - if(!gps.encode(serial_gps.read())) + if(!gps.encode(serial_gps->read())) continue; // If we haven't acquired a lock yet, go around again @@ -41,14 +43,26 @@ void gps_fetch() { // Hooray! Serial.println(F("ok")); - return; + + result.lat = gps.location.lat(); + result.lng = gps.location.lng(); + #ifdef SD_DEBUG_ENABLED + snprintf(result.time, 19, "%04d-%02d-%02d %02d:%02d:%02d", + gps.date.year(), + gps.date.month(), + gps.date.day(), + gps.time.hour(), + gps.time.minute(), + gps.time.second() + ); + #endif + return result; } } -TinyGPSPlus gps_info() { - return gps; -} - void gps_end() { - Serial.println(F("[warning] Putting the GPS device to sleep isn't implemented yet.")); + serial_gps->end(); + delete serial_gps; + //delete gps; + // Serial.println(F("[warning] Putting the GPS device to sleep isn't implemented yet.")); } diff --git a/iot/main/gps.h b/iot/main/gps.h index 85c79b5..a67187b 100644 --- a/iot/main/gps.h +++ b/iot/main/gps.h @@ -6,6 +6,16 @@ #include "settings.h" +// A lightweight struct to hold location information. +// TinyGPS++ actually uses a *ton* of RAM so we can't keep the instance around as a global variable +struct GPSLocation { + float lat; + float lng; + #ifdef SD_DEBUG_ENABLED + char time[20]; + #endif +}; + /** * Initialises the connection to the GPS device. */ @@ -15,14 +25,7 @@ void gps_begin(); * Fetches new data from the GPS module. * May take a moment, as the GPS device needs time to acquire a satellite fix. */ -void gps_fetch(); - -/** - * Fetches the latest information from the GPS device. - * Call gps_fetch() first. - * @return TinyGPSLocation The current location. - */ -TinyGPSPlus gps_info(); +GPSLocation gps_fetch(); /** * Ends the connection to the GPS device and puts it to sleep in order to save diff --git a/iot/main/main.ino b/iot/main/main.ino index 7dcd296..d81f564 100644 --- a/iot/main/main.ino +++ b/iot/main/main.ino @@ -2,6 +2,7 @@ #include #include #include +#include #include "random.h" // BAD PRACTICE: For some extremely strange reason, the Arduino IDE doesn't pick up random.cpp like it does our other source files - so we've got to explicitly include it here. If we had control over the build process (which we don't), we've use a Makefile here that handled this better. @@ -15,43 +16,41 @@ void setup() { Serial.begin(BAUD_PC); - Serial.println(F("[main] Beginning collection sequence.")); + Serial.println(F("[main] Starting")); random_begin(); + Serial.println(freeMemory(), DEC); // 1 peripheral_register(PIN_SPI_CS_RFM95); peripheral_register(PIN_SPI_CS_SD); + Serial.println(freeMemory(), DEC); // 2 gps_begin(); - TinyGPSPlus gps_data = gps_info(); + Serial.println(freeMemory(), DEC); // 3 + GPSLocation gps_data = gps_fetch(); + Serial.println(freeMemory(), DEC); // 4 gps_end(); + Serial.println(freeMemory(), DEC); // 5 - Serial.print(F("[main] Location: (")); Serial.print(gps_data.location.lat()); Serial.print(F(", ")); Serial.print(gps_data.location.lng()); Serial.println(F(")")); + Serial.print(F("[main] Location ")); Serial.print(gps_data.lat); Serial.print(F(", ")); Serial.println(gps_data.lng); uint32_t id = random_get(); - Serial.print(F("[main] id: ")); + Serial.print(F("[main] Id ")); Serial.println(id); // Activate microSD card breakout board on the SPI bus peripheral_unsilence(PIN_SPI_CS_SD); store_init(); - store_reading(id, gps_data.location); - char debug_message[64]; - int chars = snprintf(debug_message, 64, "%d-%d-%d %d:%d:%d | %f %f", - gps_data.date.year(), - gps_data.date.month(), - gps_data.date.day(), - gps_data.time.hour(), - gps_data.time.minute(), - gps_data.time.second(), - gps_data.location.lat(), - gps_data.location.lng() - ); - store_debug(debug_message, chars); + Serial.println(freeMemory(), DEC); // 6 + store_reading(id, gps_data); + #ifdef SD_DEBUG_ENABLED + store_debug(gps_data.time, 19); + #endif store_close(); + Serial.println(freeMemory(), DEC); // 7 // ------------------------------------------------------------------------ @@ -60,8 +59,8 @@ void setup() { uint8_t message[] = "testing"; - transmit_init(); - transmit_send(message, 7); + // transmit_init(); + // transmit_send(message, 7); power_off(); // Doesn't return diff --git a/iot/main/peripheral.cpp b/iot/main/peripheral.cpp index f4ecfa2..2899b9c 100644 --- a/iot/main/peripheral.cpp +++ b/iot/main/peripheral.cpp @@ -1,20 +1,20 @@ #include -void peripheral_register(int pin_number) { +void peripheral_register(uint8_t pin_number) { pinMode(OUTPUT, pin_number); // Disable the device by default to avoid issues digitalWrite(pin_number, HIGH); } -void peripheral_unsilence(int pin_number) { +void peripheral_unsilence(uint8_t pin_number) { digitalWrite(pin_number, LOW); } -void peripheral_silence(int pin_number) { +void peripheral_silence(uint8_t pin_number) { digitalWrite(pin_number, HIGH); } -void peripheral_switch(int pin_number_old, int pin_number_new) { +void peripheral_switch(uint8_t pin_number_old, uint8_t pin_number_new) { digitalWrite(pin_number_old, HIGH); digitalWrite(pin_number_new, LOW); } diff --git a/iot/main/peripheral.h b/iot/main/peripheral.h index d186a72..58fb084 100644 --- a/iot/main/peripheral.h +++ b/iot/main/peripheral.h @@ -4,21 +4,21 @@ * Register a new SPI peripheral. * @param pin_number The pin number of the device's chip select pin. */ -void peripheral_register(int pin_number); +void peripheral_register(uint8_t pin_number); /** * Allows the device with the given chip select pin to talk on the SPI bus. * @param pin_number The pin number of the chip select pin of the device to allow to talk. */ -void peripheral_unsilence(int pin_number); +void peripheral_unsilence(uint8_t pin_number); /** * Stops the device with the given chip select pin from talking on the SPI bus. * @param pin_number The chip-select pin number of the device to stop. */ -void peripheral_silence(int pin_number); +void peripheral_silence(uint8_t pin_number); /** * Switches the active device from one to another on the SPI bus. * @param pin_number_old The chip-select pin number of the old device to switch out from. * @param pin_number_new The chip-select pin number of the new device to switch in to. */ -void peripheral_switch(int pin_number_old, int pin_number_new); +void peripheral_switch(uint8_t pin_number_old, uint8_t pin_number_new); diff --git a/iot/main/power.cpp b/iot/main/power.cpp index 082e395..d5a281d 100644 --- a/iot/main/power.cpp +++ b/iot/main/power.cpp @@ -2,16 +2,6 @@ #include "settings.h" -/* -void power_gps_wakeup() { - Serial.println(F("Warn: GPS wakeup isn't implemented yet.")); -} - -void power_gps_standby() { - Serial.println(F("Warn: GPS standby isn't implemented yet.")); -} -*/ - void power_off() { Serial.println(F("[power] Shutting down")); // Serial.println(F("[power] Switching GPS module to standby")); diff --git a/iot/main/power.h b/iot/main/power.h index 6e4f006..94e8717 100644 --- a/iot/main/power.h +++ b/iot/main/power.h @@ -1,13 +1,4 @@ -/** - * Wakes the GPS module up from standby. - */ -// void power_gps_wakeup(); -/** - * Puts the GPS module into standby. - */ -// void power_gps_standby(); - /** * Signals that our work is complete and that we can turn off now to the * TPL5111. diff --git a/iot/main/settings.h b/iot/main/settings.h index 3808602..7cfcdc2 100644 --- a/iot/main/settings.h +++ b/iot/main/settings.h @@ -31,6 +31,9 @@ /// RFM95 /// ///////////// +// Whether to actually enable LMIC-based transmission or not. +#define ENABLE_RADIO + // The SPI chip-select pin for the RFM 95 #define PIN_SPI_CS_RFM95 10 @@ -63,7 +66,10 @@ #define PIN_SPI_CS_SD 3 // The filename on the microSD card to store data in. -#define SD_FILENAME "data.tsv" +#define SD_FILENAME F("data.tsv") + +// Whether to write debug information to the filename below. This could compromise privacy, so it should be commented out for production. +#define SD_DEBUG_ENABLED // The filename on the microSD card to store debug information in -#define SD_FILENAME_DEBUG "debug.log" +#define SD_FILENAME_DEBUG F("debug.log") diff --git a/iot/main/storage.cpp b/iot/main/storage.cpp index 8acbbaa..3a7d312 100644 --- a/iot/main/storage.cpp +++ b/iot/main/storage.cpp @@ -3,29 +3,28 @@ #include #include "settings.h" - -File _debug_handle; +#include "gps.h" void store_init() { // NOTE: If this doesn't work, then we need to use pinMode() & specify the data pin here instead. if(!SD.begin(PIN_SPI_CS_SD)) { - Serial.println(F("Error: Failed to initialise connection to microSD card")); + Serial.println(F("Error: MicroSD card init failed")); while(true) { delay(1); } // Pseudo-halt, but uses delay() to ensure we keep passing back control to allow easy re-programming } } -void store_reading(uint32_t id, TinyGPSLocation location) { +void store_reading(uint32_t id, GPSLocation location) { // Open the file to write the data to. If it doesn't exist it will be created. // Unlike elsewhere, FILE_WRITE opens the file with the cursor starting at the end, so it's basically actually equivalent to FILE_APPEND that we use in other environments. Confusing, I know. File handle = SD.open(SD_FILENAME, FILE_WRITE); handle.print(id); - handle.print("\t"); + handle.print(F("\t")); - handle.print(location.lat()); - handle.print("\t"); + handle.print(location.lat); + handle.print(F("\t")); - handle.print(location.lng()); + handle.print(location.lng); handle.println(); handle.close(); @@ -33,7 +32,9 @@ void store_reading(uint32_t id, TinyGPSLocation location) { void store_debug(char* buffer, int size) { File handle = SD.open(SD_FILENAME_DEBUG, FILE_WRITE); - + handle.write(buffer, size); + handle.println(); + handle.close(); } void store_close() { diff --git a/iot/main/storage.h b/iot/main/storage.h index d5386c4..b957de7 100644 --- a/iot/main/storage.h +++ b/iot/main/storage.h @@ -7,7 +7,7 @@ void store_init(); -void store_reading(uint32_t id, TinyGPSLocation location); +void store_reading(uint32_t id, GPSLocation location); void store_debug(char* buffer, int size); diff --git a/iot/main/transmission.cpp b/iot/main/transmission.cpp index 5f61a6c..ad32f50 100644 --- a/iot/main/transmission.cpp +++ b/iot/main/transmission.cpp @@ -5,13 +5,10 @@ #include "settings.h" // Global static variable that's used to detect when LMIC has finished doing it's thing -static bool is_sending_complete = false; - - -static osjob_t sendjob; +bool is_sending_complete = false; void transmit_init() { - Serial.println(F("Initialising LMIC")); + Serial.println(F("[LMIC] Init")); // LMIC init os_init(); // Reset the MAC state. Session and pending data transfers will be discarded. @@ -70,7 +67,7 @@ void transmit_init() { bool transmit_send(uint8_t* data, int length) { // Check if there is not a current TX/RX job running if (LMIC.opmode & OP_TXRXPEND) { - Serial.println(F("OP_TXRXPEND: There's already a job running, not sending")); + Serial.println(F("OP_TXRXPEND: Job running, can't send")); return false; } // Prepare upstream data transmission at the next possible time. @@ -88,16 +85,9 @@ bool transmit_send(uint8_t* data, int length) { return true; } -// 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) { } - void onEvent (ev_t ev) { - Serial.print(os_getTime()); - Serial.print(": "); + Serial.print(millis()); + Serial.print(" "); switch(ev) { case EV_SCAN_TIMEOUT: Serial.println(F("EV_SCAN_TIMEOUT")); @@ -111,27 +101,26 @@ void onEvent (ev_t ev) { case EV_BEACON_TRACKED: Serial.println(F("EV_BEACON_TRACKED")); break; - case EV_JOINING: + /*case EV_JOINING: Serial.println(F("EV_JOINING")); break; case EV_JOINED: Serial.println(F("EV_JOINED")); - break; + break;*/ case EV_RFU1: Serial.println(F("EV_RFU1")); break; - case EV_JOIN_FAILED: + /*case EV_JOIN_FAILED: Serial.println(F("EV_JOIN_FAILED")); break; case EV_REJOIN_FAILED: Serial.println(F("EV_REJOIN_FAILED")); - break; - break; + break;*/ case EV_TXCOMPLETE: - Serial.println(F("EV_TXCOMPLETE (includes waiting for RX windows)")); + Serial.println(F("EV_TXCOMPLETE (incl. RX window wait)")); if(LMIC.dataLen) { // data received in rx slot after tx - Serial.print(F("Data Received: ")); + Serial.print(F("Received: ")); Serial.write(LMIC.frame+LMIC.dataBeg, LMIC.dataLen); Serial.println(); } From 35995fda3a972b6790846fc4e5a5b5d8944b6325 Mon Sep 17 00:00:00 2001 From: Starbeamrainbowlabs Date: Wed, 26 Jun 2019 15:59:49 +0100 Subject: [PATCH 18/34] Refactor gps system again to save memory --- iot/main/gps.cpp | 43 +++++++++++++++++++++---------------------- iot/main/gps.h | 5 +++-- iot/main/main.ino | 17 ++++++----------- 3 files changed, 30 insertions(+), 35 deletions(-) diff --git a/iot/main/gps.cpp b/iot/main/gps.cpp index 77b0d5a..df3049a 100644 --- a/iot/main/gps.cpp +++ b/iot/main/gps.cpp @@ -1,18 +1,19 @@ #include "gps.h" #include "settings.h" -// The serial connection to the GPS device -SoftwareSerial* serial_gps; - -void gps_begin() { - serial_gps = new SoftwareSerial(PIN_GPS_RX, PIN_GPS_TX); +SoftwareSerial gps_begin() { + SoftwareSerial serial_gps(PIN_GPS_RX, PIN_GPS_TX); // Initialise the software-based serial connection to the GPS device - serial_gps->begin(BAUD_GPS); + serial_gps.begin(BAUD_GPS); + return serial_gps; } GPSLocation gps_fetch() { + // The serial connection to the GPS module + SoftwareSerial serial_gps = gps_begin(); // The GPS message decoder TinyGPSPlus gps; + // The struct that will hold the result GPSLocation result; Serial.print(F("[gps] Working: ")); @@ -25,17 +26,20 @@ GPSLocation gps_fetch() { } // Make sure there's something to read - if(serial_gps->available() <= 0) + if(serial_gps.available() <= 0) continue; // If it failed, go around again - if(!gps.encode(serial_gps->read())) + if(!gps.encode(serial_gps.read())) continue; // If we haven't acquired a lock yet, go around again - // Also go around again if the location is the same hasn't yet been updated (GPS devices are strange) - if(!gps.location.isValid()) { - // NOTE: It can really take a rather long time to get a lock. Just wait patiently :-) + if(!gps.location.isValid() + #ifdef SD_DEBUG_ENABLED + || !gps.time.isValid() + #endif + ) { + // NOTE: It can take a rather long time to get a lock. Just wait patiently :-) if(millis() - ms_last_update > 500) Serial.print("."); ms_last_update = millis(); continue; @@ -48,21 +52,16 @@ GPSLocation gps_fetch() { result.lng = gps.location.lng(); #ifdef SD_DEBUG_ENABLED snprintf(result.time, 19, "%04d-%02d-%02d %02d:%02d:%02d", - gps.date.year(), - gps.date.month(), - gps.date.day(), - gps.time.hour(), - gps.time.minute(), - gps.time.second() + gps.date.year(), gps.date.month(), gps.date.day(), + gps.time.hour(), gps.time.minute(), gps.time.second() ); #endif return result; } + + gps_end(serial_gps); } -void gps_end() { - serial_gps->end(); - delete serial_gps; - //delete gps; - // Serial.println(F("[warning] Putting the GPS device to sleep isn't implemented yet.")); +void gps_end(SoftwareSerial serial_gps) { + serial_gps.end(); } diff --git a/iot/main/gps.h b/iot/main/gps.h index a67187b..0e1c17e 100644 --- a/iot/main/gps.h +++ b/iot/main/gps.h @@ -19,7 +19,7 @@ struct GPSLocation { /** * Initialises the connection to the GPS device. */ -void gps_begin(); +SoftwareSerial gps_begin(); /** * Fetches new data from the GPS module. @@ -34,5 +34,6 @@ GPSLocation gps_fetch(); * system and so doesn't get turned off after each measurement, as it takes * ~30s to reacquire a lock when it first starts up - hence why we put it to * sleep instead. + * TODO: Connect the gps module to the timed power rail instead, as its; got an onboard battery. */ -void gps_end(); +void gps_end(SoftwareSerial gps); diff --git a/iot/main/main.ino b/iot/main/main.ino index d81f564..beb7796 100644 --- a/iot/main/main.ino +++ b/iot/main/main.ino @@ -19,19 +19,10 @@ void setup() { Serial.println(F("[main] Starting")); random_begin(); - Serial.println(freeMemory(), DEC); // 1 - peripheral_register(PIN_SPI_CS_RFM95); - peripheral_register(PIN_SPI_CS_SD); - - Serial.println(freeMemory(), DEC); // 2 - - gps_begin(); - Serial.println(freeMemory(), DEC); // 3 + Serial.println(freeMemory(), DEC); GPSLocation gps_data = gps_fetch(); - Serial.println(freeMemory(), DEC); // 4 - gps_end(); - Serial.println(freeMemory(), DEC); // 5 + Serial.println(freeMemory(), DEC); Serial.print(F("[main] Location ")); Serial.print(gps_data.lat); Serial.print(F(", ")); Serial.println(gps_data.lng); @@ -41,6 +32,10 @@ void setup() { Serial.println(id); // Activate microSD card breakout board on the SPI bus + + peripheral_register(PIN_SPI_CS_RFM95); + peripheral_register(PIN_SPI_CS_SD); + peripheral_unsilence(PIN_SPI_CS_SD); store_init(); From a29fba2fa26979b6e7ced5d4049365ea1d8f15fb Mon Sep 17 00:00:00 2001 From: Starbeamrainbowlabs Date: Wed, 26 Jun 2019 16:28:35 +0100 Subject: [PATCH 19/34] It's the SD card library that's using all our RAM! --- iot/main/main.ino | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/iot/main/main.ino b/iot/main/main.ino index beb7796..38394cd 100644 --- a/iot/main/main.ino +++ b/iot/main/main.ino @@ -17,7 +17,6 @@ void setup() { Serial.begin(BAUD_PC); Serial.println(F("[main] Starting")); - random_begin(); Serial.println(freeMemory(), DEC); @@ -39,13 +38,15 @@ void setup() { peripheral_unsilence(PIN_SPI_CS_SD); store_init(); - Serial.println(freeMemory(), DEC); // 6 + Serial.println(freeMemory(), DEC); store_reading(id, gps_data); + Serial.println(freeMemory(), DEC); #ifdef SD_DEBUG_ENABLED store_debug(gps_data.time, 19); #endif + Serial.println(freeMemory(), DEC); store_close(); - Serial.println(freeMemory(), DEC); // 7 + Serial.println(freeMemory(), DEC); // ------------------------------------------------------------------------ From cf2af7668e2ee171484e52a12d7fde9be1fb3bbc Mon Sep 17 00:00:00 2001 From: Starbeamrainbowlabs Date: Wed, 26 Jun 2019 18:08:42 +0100 Subject: [PATCH 20/34] Start replacing SD with SdFat, but it's not finished yet --- .gitmodules | 3 +++ iot/libraries/SdFat | 1 + iot/main/storage.cpp | 17 ++++------------- 3 files changed, 8 insertions(+), 13 deletions(-) create mode 160000 iot/libraries/SdFat diff --git a/.gitmodules b/.gitmodules index 9bf6efd..bfab7ac 100644 --- a/.gitmodules +++ b/.gitmodules @@ -13,3 +13,6 @@ [submodule "iot/libraries/Arduino-MemoryFree"] path = iot/libraries/Arduino-MemoryFree url = https://github.com/mpflaga/Arduino-MemoryFree.git +[submodule "iot/libraries/SdFat"] + path = iot/libraries/SdFat + url = https://github.com/greiman/SdFat.git diff --git a/iot/libraries/SdFat b/iot/libraries/SdFat new file mode 160000 index 0000000..3b79f38 --- /dev/null +++ b/iot/libraries/SdFat @@ -0,0 +1 @@ +Subproject commit 3b79f38cb554809a45a0ccd6d9d6752b6ad948c2 diff --git a/iot/main/storage.cpp b/iot/main/storage.cpp index 3a7d312..4e79855 100644 --- a/iot/main/storage.cpp +++ b/iot/main/storage.cpp @@ -1,19 +1,14 @@ #include -#include -#include +#include #include "settings.h" #include "gps.h" -void store_init() { - // NOTE: If this doesn't work, then we need to use pinMode() & specify the data pin here instead. - if(!SD.begin(PIN_SPI_CS_SD)) { - Serial.println(F("Error: MicroSD card init failed")); - while(true) { delay(1); } // Pseudo-halt, but uses delay() to ensure we keep passing back control to allow easy re-programming - } -} +SdFat* card = NULL; void store_reading(uint32_t id, GPSLocation location) { + SdFat card; + card.begin() // Open the file to write the data to. If it doesn't exist it will be created. // Unlike elsewhere, FILE_WRITE opens the file with the cursor starting at the end, so it's basically actually equivalent to FILE_APPEND that we use in other environments. Confusing, I know. File handle = SD.open(SD_FILENAME, FILE_WRITE); @@ -36,7 +31,3 @@ void store_debug(char* buffer, int size) { handle.println(); handle.close(); } - -void store_close() { - SD.end(); -} From 151b9544f2042a0dc0ece72fa64e82daa1e1dac8 Mon Sep 17 00:00:00 2001 From: Starbeamrainbowlabs Date: Wed, 26 Jun 2019 19:34:29 +0100 Subject: [PATCH 21/34] Add todo note --- iot/main/storage.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/iot/main/storage.cpp b/iot/main/storage.cpp index 4e79855..d50a3f2 100644 --- a/iot/main/storage.cpp +++ b/iot/main/storage.cpp @@ -8,7 +8,10 @@ SdFat* card = NULL; void store_reading(uint32_t id, GPSLocation location) { SdFat card; - card.begin() + card.begin(PIN_SPI_CS_SD); + + // Port the rest of this to SdFat from SD + // Open the file to write the data to. If it doesn't exist it will be created. // Unlike elsewhere, FILE_WRITE opens the file with the cursor starting at the end, so it's basically actually equivalent to FILE_APPEND that we use in other environments. Confusing, I know. File handle = SD.open(SD_FILENAME, FILE_WRITE); From 18342c55fc0328a968ffc3c5e9275b90775d73b5 Mon Sep 17 00:00:00 2001 From: Starbeamrainbowlabs Date: Thu, 27 Jun 2019 15:49:39 +0100 Subject: [PATCH 22/34] Finish SdFat refactor, but it's untested. --- iot/main/storage.cpp | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/iot/main/storage.cpp b/iot/main/storage.cpp index d50a3f2..7781fa0 100644 --- a/iot/main/storage.cpp +++ b/iot/main/storage.cpp @@ -1,36 +1,43 @@ #include #include +// #include #include "settings.h" #include "gps.h" -SdFat* card = NULL; +// FUTURE: We might be able to trim it down if we disable long filenames with #define USE_LONG_FILE_NAMES 0 +SdFat* card = nullptr; + +void store_init() { + card = new SdFat(); + card->begin(PIN_SPI_CS_SD); +} void store_reading(uint32_t id, GPSLocation location) { - SdFat card; - card.begin(PIN_SPI_CS_SD); - // Port the rest of this to SdFat from SD // Open the file to write the data to. If it doesn't exist it will be created. // Unlike elsewhere, FILE_WRITE opens the file with the cursor starting at the end, so it's basically actually equivalent to FILE_APPEND that we use in other environments. Confusing, I know. - File handle = SD.open(SD_FILENAME, FILE_WRITE); + File handle = card->open(SD_FILENAME, O_APPEND | O_CREAT | O_WRONLY); - handle.print(id); - handle.print(F("\t")); + char line[33]; + int chars = snprintf(line, 33, "%d\t%06d\t%06d\n", + id, + location.lat, location.lng + ); + handle.write(line, chars); - handle.print(location.lat); - handle.print(F("\t")); - - handle.print(location.lng); - handle.println(); - - handle.close(); + handle.close(); // Syncs implicitly } void store_debug(char* buffer, int size) { - File handle = SD.open(SD_FILENAME_DEBUG, FILE_WRITE); + File handle = card->open(SD_FILENAME_DEBUG, O_APPEND | O_CREAT | O_WRONLY); handle.write(buffer, size); - handle.println(); + handle.write('\n'); handle.close(); } + +void store_end() { + card.end(); + delete card; +} From bbf12da323bc6748f6d66bdeceac3fc9f59ccc7f Mon Sep 17 00:00:00 2001 From: Starbeamrainbowlabs Date: Thu, 27 Jun 2019 15:58:01 +0100 Subject: [PATCH 23/34] Fix compilation warnings --- iot/main/main.ino | 3 +-- iot/main/storage.cpp | 9 +++++++-- iot/main/storage.h | 4 ++-- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/iot/main/main.ino b/iot/main/main.ino index 38394cd..5d0594e 100644 --- a/iot/main/main.ino +++ b/iot/main/main.ino @@ -1,7 +1,6 @@ #include "settings.h" #include #include -#include #include #include "random.h" @@ -45,7 +44,7 @@ void setup() { store_debug(gps_data.time, 19); #endif Serial.println(freeMemory(), DEC); - store_close(); + store_end(); Serial.println(freeMemory(), DEC); // ------------------------------------------------------------------------ diff --git a/iot/main/storage.cpp b/iot/main/storage.cpp index 7781fa0..974b112 100644 --- a/iot/main/storage.cpp +++ b/iot/main/storage.cpp @@ -10,7 +10,10 @@ SdFat* card = nullptr; void store_init() { card = new SdFat(); - card->begin(PIN_SPI_CS_SD); + if(!card->begin(PIN_SPI_CS_SD)) { + Serial.println("Error: MicroSD card init failed"); + while(true) delay(100); + } } void store_reading(uint32_t id, GPSLocation location) { @@ -38,6 +41,8 @@ void store_debug(char* buffer, int size) { } void store_end() { - card.end(); + // Apparently we'ree fine so long as we don't have any open file handles - there's no end() method on the SdFat class + // card->end(); delete card; + card = nullptr; } diff --git a/iot/main/storage.h b/iot/main/storage.h index b957de7..cf4e88f 100644 --- a/iot/main/storage.h +++ b/iot/main/storage.h @@ -1,7 +1,7 @@ #pragma once #include -#include +#include #include @@ -11,4 +11,4 @@ void store_reading(uint32_t id, GPSLocation location); void store_debug(char* buffer, int size); -void store_close(); +void store_end(); From 5b4b36ddad852e4462df1bbff1de2e5faf79f000 Mon Sep 17 00:00:00 2001 From: Starbeamrainbowlabs Date: Thu, 27 Jun 2019 16:27:33 +0100 Subject: [PATCH 24/34] Refactor gps a teeny bit --- iot/main/gps.cpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/iot/main/gps.cpp b/iot/main/gps.cpp index df3049a..51949ec 100644 --- a/iot/main/gps.cpp +++ b/iot/main/gps.cpp @@ -18,6 +18,7 @@ GPSLocation gps_fetch() { Serial.print(F("[gps] Working: ")); uint32_t ms_last_update = millis(); + uint8_t ticks = 0; // We WILL discover our location - if it's the last thing we do! while(true) { if(millis() > 5000 && gps.charsProcessed() < 10) { @@ -40,7 +41,19 @@ GPSLocation gps_fetch() { #endif ) { // NOTE: It can take a rather long time to get a lock. Just wait patiently :-) - if(millis() - ms_last_update > 500) Serial.print("."); + if(millis() - ms_last_update > 500) { + ticks++; + Serial.print(gps.time.isValid() ? '-' : '.'); + if(ticks > 80) { + Serial.println(); + ticks = 0; + } + // Serial.println(gps.location.lat()); + // Serial.println(gps.location.lng()); + // Serial.println(gps.date.year()); + // Serial.println(gps.time.hour()); + // Serial.println(gps.time.minute()); + } ms_last_update = millis(); continue; } From 4e6da5604ef12591654fd52ec57e2565e51e275c Mon Sep 17 00:00:00 2001 From: Starbeamrainbowlabs Date: Thu, 27 Jun 2019 16:28:28 +0100 Subject: [PATCH 25/34] Don't use gps.time unless we're in debug mode --- iot/main/gps.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/iot/main/gps.cpp b/iot/main/gps.cpp index 51949ec..1ae7e85 100644 --- a/iot/main/gps.cpp +++ b/iot/main/gps.cpp @@ -43,7 +43,11 @@ GPSLocation gps_fetch() { // NOTE: It can take a rather long time to get a lock. Just wait patiently :-) if(millis() - ms_last_update > 500) { ticks++; + #ifdef SD_DEBUG_ENABLED Serial.print(gps.time.isValid() ? '-' : '.'); + #else + Serial.print('.'); + #endif if(ticks > 80) { Serial.println(); ticks = 0; From 3e3f0b6bb3dd54f8325e65a6c91e203c97d38c69 Mon Sep 17 00:00:00 2001 From: Starbeamrainbowlabs Date: Thu, 27 Jun 2019 17:26:22 +0100 Subject: [PATCH 26/34] microSD card writing is now functional. Hooray :D --- iot/main/main.ino | 7 ++----- iot/main/storage.cpp | 30 +++++++++++++++--------------- iot/main/storage.h | 2 +- 3 files changed, 18 insertions(+), 21 deletions(-) diff --git a/iot/main/main.ino b/iot/main/main.ino index 5d0594e..485051d 100644 --- a/iot/main/main.ino +++ b/iot/main/main.ino @@ -36,16 +36,13 @@ void setup() { peripheral_unsilence(PIN_SPI_CS_SD); - store_init(); Serial.println(freeMemory(), DEC); store_reading(id, gps_data); Serial.println(freeMemory(), DEC); #ifdef SD_DEBUG_ENABLED - store_debug(gps_data.time, 19); + store_debug(gps_data.time, 19 - 1); // Don't print the null byte + Serial.println(freeMemory(), DEC); #endif - Serial.println(freeMemory(), DEC); - store_end(); - Serial.println(freeMemory(), DEC); // ------------------------------------------------------------------------ diff --git a/iot/main/storage.cpp b/iot/main/storage.cpp index 974b112..b636e21 100644 --- a/iot/main/storage.cpp +++ b/iot/main/storage.cpp @@ -5,44 +5,44 @@ #include "settings.h" #include "gps.h" -// FUTURE: We might be able to trim it down if we disable long filenames with #define USE_LONG_FILE_NAMES 0 -SdFat* card = nullptr; +// FUTURE: We might be able to trim it down if we disable long filenames with #define -void store_init() { - card = new SdFat(); - if(!card->begin(PIN_SPI_CS_SD)) { +SdFat store_init() { + SdFat card; + if(!card.begin(PIN_SPI_CS_SD)) { Serial.println("Error: MicroSD card init failed"); while(true) delay(100); } + return card; } void store_reading(uint32_t id, GPSLocation location) { // Port the rest of this to SdFat from SD + SdFat card = store_init(); // Open the file to write the data to. If it doesn't exist it will be created. // Unlike elsewhere, FILE_WRITE opens the file with the cursor starting at the end, so it's basically actually equivalent to FILE_APPEND that we use in other environments. Confusing, I know. - File handle = card->open(SD_FILENAME, O_APPEND | O_CREAT | O_WRONLY); - - char line[33]; - int chars = snprintf(line, 33, "%d\t%06d\t%06d\n", - id, - location.lat, location.lng - ); - handle.write(line, chars); - + File handle = card.open(SD_FILENAME, O_APPEND | O_CREAT | O_WRONLY); + uint8_t places = 6; + handle.printField(id, '\t'); + handle.printField(location.lat, '\t', places); + handle.printField(location.lng, '\n', places); handle.close(); // Syncs implicitly } void store_debug(char* buffer, int size) { - File handle = card->open(SD_FILENAME_DEBUG, O_APPEND | O_CREAT | O_WRONLY); + SdFat card = store_init(); + File handle = card.open(SD_FILENAME_DEBUG, O_APPEND | O_CREAT | O_WRONLY); handle.write(buffer, size); handle.write('\n'); handle.close(); } +/* void store_end() { // Apparently we'ree fine so long as we don't have any open file handles - there's no end() method on the SdFat class // card->end(); delete card; card = nullptr; } +*/ diff --git a/iot/main/storage.h b/iot/main/storage.h index cf4e88f..8a3ea67 100644 --- a/iot/main/storage.h +++ b/iot/main/storage.h @@ -11,4 +11,4 @@ void store_reading(uint32_t id, GPSLocation location); void store_debug(char* buffer, int size); -void store_end(); +// void store_end(); From 35b2dd447ceac3b47fbf1f9247bc0b355c0364cb Mon Sep 17 00:00:00 2001 From: Starbeamrainbowlabs Date: Fri, 28 Jun 2019 13:33:16 +0100 Subject: [PATCH 27/34] Add automatic library configuration to build setup task --- README.md | 7 ------- build | 52 +++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 47 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 9d9ccad..b0bc453 100644 --- a/README.md +++ b/README.md @@ -21,13 +21,6 @@ Run `bash ./build setup` from the command line at the root of this repository. ### IoT Device - Copy `settings.custom.cpp.example` to `settings.custom.cpp` and fill in the fields there - Review `settings.h` to make sure it matches your setup - - Edit `iot/libraries/arduino-lmic/src/lmic/config.h`, and add the following to the bottom: - -```c++ -#define DISABLE_PING -#define DISABLE_BEACONS -``` - - Copy the folders in `iot/libraries` to your Arduino IDE libraries folder ## Useful Links diff --git a/build b/build index 958cdf0..38c5ef4 100755 --- a/build +++ b/build @@ -43,18 +43,60 @@ fi ############################################################################### -task_setup() { - task_begin "Setting up"; +# Toggles commenting and uncommenting lines in a file that contain a specific +# substring. Checks for word boundaries either side of the substring. +# From https://stackoverflow.com/a/24901636/1460422 +# $1 - Filename +# $2 - Search string +comment_toggle() { + filename="${1}"; + search_string="${2}"; + awk -v commentId='//' -v word="${search_string}" ' + $0 ~ "(^|[[:punct:][:space:]])" word "($|[[:punct:][:space:]])" { + if (match($0, "^[[:space:]]*" commentId)) + $0 = substr($0, RSTART + RLENGTH) + else + $0 = commentId $0 + } + { print }' "${filename}" > tmpfile.$$ && mv tmpfile.$$ "${filename}" +} + +task_setup() { + stage_begin "Setting up"; + + task_begin "Checking Environment"; check_command git true; + check_command awk true; check_command pdflatex true; check_command bibtex true; + task_end $?; - subtask_begin "Initialising submodules"; + task_begin "Initialising submodules"; git submodule update --init; - subtask_end $?; + task_end $?; - task_end 0; + task_begin "Preconfiguring libraries"; + config_file_directory="./iot/libraries/arduino-lmic/src/lmic/"; + config_file_name="config.h"; + cd "${config_file_directory}"; + git reset --hard; + # Disable OTAA + comment_toggle "${config_file_name}" "#define DISABLE_JOIN"; + # Disable class b stuff + comment_toggle "${config_file_name}" "#define DISABLE_PING"; + comment_toggle "${config_file_name}" "#define DISABLE_BEACONS"; + # Disable other misc. stuff we're not likely to use + comment_toggle "${config_file_name}" "#define DISABLE_MCMD_DCAP_REQ"; # Duty cycle cap - won't work anyway 'cause we're shutting down in between + comment_toggle "${config_file_name}" "#define DISABLE_MCMD_DN2P_SET"; # Receiving stuff + + # echo "#define DISABLE_JOIN" >>"${config_file_name}"; + # echo "#define DISABLE_PING" >>"${config_file_name}"; + # echo "#define DISABLE_BEACONS" >>"${config_file_name}"; + cd -; + task_end $?; + + stage_end 0; } task_render() { From 34bee82abed7887b294a8ed8f72addc7e42f865d Mon Sep 17 00:00:00 2001 From: Starbeamrainbowlabs Date: Fri, 28 Jun 2019 13:34:13 +0100 Subject: [PATCH 28/34] Add TinyGPS as it's got a smaller program size then TinyGPS++ The intent is to switch TinyGPS++ out for TinyGPS (it's predecessor) --- .gitmodules | 3 +++ iot/libraries/TinyGPS | 1 + 2 files changed, 4 insertions(+) create mode 160000 iot/libraries/TinyGPS diff --git a/.gitmodules b/.gitmodules index bfab7ac..c8dfb1d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -16,3 +16,6 @@ [submodule "iot/libraries/SdFat"] path = iot/libraries/SdFat url = https://github.com/greiman/SdFat.git +[submodule "iot/libraries/TinyGPS"] + path = iot/libraries/TinyGPS + url = https://github.com/mikalhart/TinyGPS.git diff --git a/iot/libraries/TinyGPS b/iot/libraries/TinyGPS new file mode 160000 index 0000000..db4ef9c --- /dev/null +++ b/iot/libraries/TinyGPS @@ -0,0 +1 @@ +Subproject commit db4ef9c97af767e7345f5ccb277ac3bd1a2eb81f From 04cc89ea50aa71dfd009cd441f0570aba9fed80a Mon Sep 17 00:00:00 2001 From: Starbeamrainbowlabs Date: Fri, 28 Jun 2019 13:34:30 +0100 Subject: [PATCH 29/34] Build the raw byte message ahead of transmitting it --- iot/main/main.ino | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/iot/main/main.ino b/iot/main/main.ino index 485051d..621fe73 100644 --- a/iot/main/main.ino +++ b/iot/main/main.ino @@ -49,10 +49,23 @@ void setup() { // Activate the RFM95 peripheral_switch(PIN_SPI_CS_SD, PIN_SPI_CS_RFM95); - uint8_t message[] = "testing"; + const uint8_t message_size = sizeof(uint32_t) + sizeof(float)*2; + uint8_t message[message_size]; - // transmit_init(); - // transmit_send(message, 7); + const uint8_t* bytes_id = reinterpret_cast(&id); + const uint8_t* bytes_lat = reinterpret_cast(&(gps_data.lat)); + const uint8_t* bytes_lng = reinterpret_cast(&(gps_data.lat)); + + for(int i = 0; i < sizeof(uint32_t); i++) { + message[i] = bytes_id[i]; + } + for(int i = 0; i < sizeof(float); i++) { + message[sizeof(uint32_t) + i] = bytes_lat[i]; + message[sizeof(uint32_t) + sizeof(float) + i] = bytes_lng[i]; + } + + transmit_init(); + transmit_send(message, message_size); power_off(); // Doesn't return From 40f4e9130b5e4b4fcc0a90ec5072896ac42a88d4 Mon Sep 17 00:00:00 2001 From: Starbeamrainbowlabs Date: Fri, 28 Jun 2019 13:57:39 +0100 Subject: [PATCH 30/34] Rewrite to use TinyGPS instead of TinyGPS++, but it's still not small enough! --- iot/main/gps.cpp | 41 +++++++++++++++++++++++++---------------- iot/main/gps.h | 2 -- iot/main/main.ino | 1 - iot/main/storage.h | 2 +- 4 files changed, 26 insertions(+), 20 deletions(-) diff --git a/iot/main/gps.cpp b/iot/main/gps.cpp index 1ae7e85..1189565 100644 --- a/iot/main/gps.cpp +++ b/iot/main/gps.cpp @@ -1,7 +1,10 @@ #include "gps.h" #include "settings.h" +#include + SoftwareSerial gps_begin() { + // FUTURE: If this doesn't work as expected, we may want to find a more reliable SoftwareSerial library SoftwareSerial serial_gps(PIN_GPS_RX, PIN_GPS_TX); // Initialise the software-based serial connection to the GPS device serial_gps.begin(BAUD_GPS); @@ -12,16 +15,24 @@ GPSLocation gps_fetch() { // The serial connection to the GPS module SoftwareSerial serial_gps = gps_begin(); // The GPS message decoder - TinyGPSPlus gps; + TinyGPS gps; // The struct that will hold the result GPSLocation result; Serial.print(F("[gps] Working: ")); - uint32_t ms_last_update = millis(); + uint32_t start = millis(), ms_last_update = millis(); uint8_t ticks = 0; + unsigned long fix_age = TinyGPS::GPS_INVALID_AGE; + unsigned long chars; + unsigned short sentences, failed_checksum; + + + // We WILL discover our location - if it's the last thing we do! while(true) { - if(millis() > 5000 && gps.charsProcessed() < 10) { + gps.stats(&chars, &sentences, &failed_checksum); + + if(millis() - start > 5000 && chars < 10) { Serial.println(F("\n[error] GPS device init failed")); while(true) { delay(1); } } @@ -34,20 +45,14 @@ GPSLocation gps_fetch() { if(!gps.encode(serial_gps.read())) continue; + gps.f_get_position(&(result.lat), &(result.lng), &fix_age); + // If we haven't acquired a lock yet, go around again - if(!gps.location.isValid() - #ifdef SD_DEBUG_ENABLED - || !gps.time.isValid() - #endif - ) { + if(fix_age == TinyGPS::GPS_INVALID_AGE) { // NOTE: It can take a rather long time to get a lock. Just wait patiently :-) if(millis() - ms_last_update > 500) { ticks++; - #ifdef SD_DEBUG_ENABLED - Serial.print(gps.time.isValid() ? '-' : '.'); - #else Serial.print('.'); - #endif if(ticks > 80) { Serial.println(); ticks = 0; @@ -65,12 +70,16 @@ GPSLocation gps_fetch() { // Hooray! Serial.println(F("ok")); - result.lat = gps.location.lat(); - result.lng = gps.location.lng(); + // gps.f_get_position pushes the values into result directly + #ifdef SD_DEBUG_ENABLED + int year; + byte month, day, hour, minute, second, _hundredths; + gps.crack_datetime(&year, &month, &day, &hour, &minute, &second, &_hundredths, &fix_age); + snprintf(result.time, 19, "%04d-%02d-%02d %02d:%02d:%02d", - gps.date.year(), gps.date.month(), gps.date.day(), - gps.time.hour(), gps.time.minute(), gps.time.second() + year, month, day, + hour, minute, second ); #endif return result; diff --git a/iot/main/gps.h b/iot/main/gps.h index 0e1c17e..5da0e1a 100644 --- a/iot/main/gps.h +++ b/iot/main/gps.h @@ -2,8 +2,6 @@ #include -#include - #include "settings.h" // A lightweight struct to hold location information. diff --git a/iot/main/main.ino b/iot/main/main.ino index 621fe73..008bde5 100644 --- a/iot/main/main.ino +++ b/iot/main/main.ino @@ -1,6 +1,5 @@ #include "settings.h" #include -#include #include #include "random.h" diff --git a/iot/main/storage.h b/iot/main/storage.h index 8a3ea67..bb0a476 100644 --- a/iot/main/storage.h +++ b/iot/main/storage.h @@ -3,7 +3,7 @@ #include #include -#include +#include "gps.h" void store_init(); From 1bbb1fe5354f2206e5a6689b56599d837616567a Mon Sep 17 00:00:00 2001 From: Starbeamrainbowlabs Date: Fri, 28 Jun 2019 14:02:44 +0100 Subject: [PATCH 31/34] Comment out ram monitoring code with macro --- iot/main/main.ino | 19 ++++++++++++------- iot/main/settings.h | 3 +++ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/iot/main/main.ino b/iot/main/main.ino index 008bde5..013955f 100644 --- a/iot/main/main.ino +++ b/iot/main/main.ino @@ -1,6 +1,11 @@ #include "settings.h" #include -#include +#ifdef ENABLE_MEMORY_DIAGNOSTICS +// #include +#define DISPLAY_FREE_MEMORY() Serial.println(freeMemory(), DEC); +#else +#define DISPLAY_FREE_MEMORY() // noop +#endif #include "random.h" // BAD PRACTICE: For some extremely strange reason, the Arduino IDE doesn't pick up random.cpp like it does our other source files - so we've got to explicitly include it here. If we had control over the build process (which we don't), we've use a Makefile here that handled this better. @@ -17,10 +22,10 @@ void setup() { Serial.println(F("[main] Starting")); random_begin(); - Serial.println(freeMemory(), DEC); + DISPLAY_FREE_MEMORY(); GPSLocation gps_data = gps_fetch(); - Serial.println(freeMemory(), DEC); - + DISPLAY_FREE_MEMORY(); + Serial.print(F("[main] Location ")); Serial.print(gps_data.lat); Serial.print(F(", ")); Serial.println(gps_data.lng); uint32_t id = random_get(); @@ -35,12 +40,12 @@ void setup() { peripheral_unsilence(PIN_SPI_CS_SD); - Serial.println(freeMemory(), DEC); + DISPLAY_FREE_MEMORY(); store_reading(id, gps_data); - Serial.println(freeMemory(), DEC); + DISPLAY_FREE_MEMORY(); #ifdef SD_DEBUG_ENABLED store_debug(gps_data.time, 19 - 1); // Don't print the null byte - Serial.println(freeMemory(), DEC); + DISPLAY_FREE_MEMORY(); #endif // ------------------------------------------------------------------------ diff --git a/iot/main/settings.h b/iot/main/settings.h index 7cfcdc2..a6396fb 100644 --- a/iot/main/settings.h +++ b/iot/main/settings.h @@ -27,6 +27,9 @@ // The 'done' pin to pulse to signal to the TPL5111 #define PIN_TPL_DONE 8 +// Uncomment to print RAM diagnostics at regular intervals. +//#define ENABLE_MEMORY_DIAGNOSTICS + ///////////// /// RFM95 /// ///////////// From eb3f377d28af5a0e6e07bb74cb10e94bf326ec75 Mon Sep 17 00:00:00 2001 From: Starbeamrainbowlabs Date: Fri, 28 Jun 2019 14:04:21 +0100 Subject: [PATCH 32/34] Make ENABLE_RADIO debug symbol actually do something --- iot/main/main.ino | 3 ++- iot/main/settings.h | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/iot/main/main.ino b/iot/main/main.ino index 013955f..b4cb88e 100644 --- a/iot/main/main.ino +++ b/iot/main/main.ino @@ -68,9 +68,10 @@ void setup() { message[sizeof(uint32_t) + sizeof(float) + i] = bytes_lng[i]; } + #ifdef ENABLE_RADIO transmit_init(); transmit_send(message, message_size); - + #endif power_off(); // Doesn't return } diff --git a/iot/main/settings.h b/iot/main/settings.h index a6396fb..d245323 100644 --- a/iot/main/settings.h +++ b/iot/main/settings.h @@ -72,7 +72,7 @@ #define SD_FILENAME F("data.tsv") // Whether to write debug information to the filename below. This could compromise privacy, so it should be commented out for production. -#define SD_DEBUG_ENABLED +// #define SD_DEBUG_ENABLED // The filename on the microSD card to store debug information in #define SD_FILENAME_DEBUG F("debug.log") From a24fd9894c98b7719d87c59d013e95bdf2a0d88a Mon Sep 17 00:00:00 2001 From: Starbeamrainbowlabs Date: Fri, 28 Jun 2019 14:11:25 +0100 Subject: [PATCH 33/34] Add todo note --- iot/main/gps.h | 1 + 1 file changed, 1 insertion(+) diff --git a/iot/main/gps.h b/iot/main/gps.h index 5da0e1a..da2cac5 100644 --- a/iot/main/gps.h +++ b/iot/main/gps.h @@ -6,6 +6,7 @@ // A lightweight struct to hold location information. // TinyGPS++ actually uses a *ton* of RAM so we can't keep the instance around as a global variable +// FUTURE: Rewrite this to use TinyGPS's integer system instead? struct GPSLocation { float lat; float lng; From 97a89ebf026b0ee9f899a8991183caa18d6a96fc Mon Sep 17 00:00:00 2001 From: Starbeamrainbowlabs Date: Mon, 1 Jul 2019 12:33:57 +0100 Subject: [PATCH 34/34] Add small debug message --- iot/main/main.ino | 1 + 1 file changed, 1 insertion(+) diff --git a/iot/main/main.ino b/iot/main/main.ino index b4cb88e..9648ded 100644 --- a/iot/main/main.ino +++ b/iot/main/main.ino @@ -69,6 +69,7 @@ void setup() { } #ifdef ENABLE_RADIO + Serial.println(F("[main] Transmitting")); transmit_init(); transmit_send(message, message_size); #endif