Two way data over LoRaWAN using Multitech mDot

Dependencies:   libmDot-mbed5

This is example firmware for the Multitech mDot. It demonstrates how to:

  • Do two-way data.
  • Sleep aggressively and only wake up when the wake-up pin is triggered.
  • Handle errors, retries and duty cycle errors.
  • Cache data in non-volatile storage.

Based on mbed OS 5.1, hard faults against mbed OS 5.3 unfortunately. Can be compiled with GCC and ARMCC (but not IAR).

To do a new transmission, short pin D6 / PA_1.

Committer:
Jan Jongboom
Date:
Wed Jan 04 13:45:49 2017 +0000
Revision:
4:0fd5e5e121ea
Parent:
3:ac5101a47080
Retry OTA join when fails during startup

Who changed what in which revision?

UserRevisionLine numberNew contents of line
janjongboom 0:20fbd6f66b11 1 #include "mbed.h"
janjongboom 0:20fbd6f66b11 2 #include "mDot.h"
janjongboom 0:20fbd6f66b11 3 #include "MTSLog.h"
janjongboom 0:20fbd6f66b11 4 #include <string>
janjongboom 0:20fbd6f66b11 5 #include <vector>
janjongboom 0:20fbd6f66b11 6 #include <algorithm>
janjongboom 0:20fbd6f66b11 7 #include "mbed.h"
janjongboom 0:20fbd6f66b11 8 #include "parse_keys.h"
janjongboom 0:20fbd6f66b11 9
janjongboom 0:20fbd6f66b11 10 #define BUILTIN_LED_ON 0
janjongboom 0:20fbd6f66b11 11 #define BUILTIN_LED_OFF 1
janjongboom 0:20fbd6f66b11 12
Jan Jongboom 2:ff17ce021cfb 13 static const char APP_EUI[] = "BE7A000000000393";
Jan Jongboom 3:ac5101a47080 14 static const char APP_KEY[] = "3FE0040E234141A08A583D5F508B5781";
janjongboom 0:20fbd6f66b11 15
janjongboom 0:20fbd6f66b11 16 static mDot* dot;
janjongboom 0:20fbd6f66b11 17
janjongboom 0:20fbd6f66b11 18 // so we have some state
janjongboom 0:20fbd6f66b11 19 static uint16_t counter = 0; // always persisted to non-volatile mem
janjongboom 0:20fbd6f66b11 20
janjongboom 0:20fbd6f66b11 21 static bool woke_from_interrupt = false;
janjongboom 0:20fbd6f66b11 22
janjongboom 0:20fbd6f66b11 23 static InterruptIn btn(PA_1); /* D6 */
janjongboom 0:20fbd6f66b11 24 static DigitalOut led(PC_13, BUILTIN_LED_OFF); /* D2 */
janjongboom 0:20fbd6f66b11 25
janjongboom 0:20fbd6f66b11 26 static void read_counter() {
janjongboom 0:20fbd6f66b11 27 char buffer[2];
janjongboom 0:20fbd6f66b11 28 bool res = dot->readUserFile("counter", &buffer, 2);
janjongboom 0:20fbd6f66b11 29 if (res) {
janjongboom 0:20fbd6f66b11 30 counter = (buffer[0] << 8) + buffer[1];
janjongboom 0:20fbd6f66b11 31 }
janjongboom 0:20fbd6f66b11 32 else {
janjongboom 0:20fbd6f66b11 33 counter = 0;
janjongboom 0:20fbd6f66b11 34 }
janjongboom 0:20fbd6f66b11 35 }
janjongboom 0:20fbd6f66b11 36
janjongboom 0:20fbd6f66b11 37 static void up_counter() {
janjongboom 0:20fbd6f66b11 38 logInfo("up counter");
janjongboom 0:20fbd6f66b11 39
janjongboom 0:20fbd6f66b11 40 read_counter();
janjongboom 0:20fbd6f66b11 41
janjongboom 0:20fbd6f66b11 42 counter++;
janjongboom 0:20fbd6f66b11 43
janjongboom 0:20fbd6f66b11 44 char buffer[2];
janjongboom 0:20fbd6f66b11 45 buffer[0] = counter >> 8 & 0xff;
janjongboom 0:20fbd6f66b11 46 buffer[1] = counter & 0xff;
janjongboom 0:20fbd6f66b11 47
janjongboom 0:20fbd6f66b11 48 logInfo("new counter value is: %d", counter);
janjongboom 0:20fbd6f66b11 49
janjongboom 0:20fbd6f66b11 50 dot->saveUserFile("counter", &buffer, 2);
janjongboom 0:20fbd6f66b11 51 }
janjongboom 0:20fbd6f66b11 52
janjongboom 0:20fbd6f66b11 53 static void btn_rise() {
janjongboom 0:20fbd6f66b11 54 woke_from_interrupt = true; // will be woken by STM32
janjongboom 0:20fbd6f66b11 55 }
janjongboom 0:20fbd6f66b11 56
janjongboom 0:20fbd6f66b11 57 static bool send_data(void) {
janjongboom 0:20fbd6f66b11 58 int32_t ret;
janjongboom 0:20fbd6f66b11 59
Jan Jongboom 4:0fd5e5e121ea 60 // check join state, @todo: add a timeout here, join is very expensive on the battery...
Jan Jongboom 4:0fd5e5e121ea 61 if (!dot->getNetworkJoinStatus()) {
Jan Jongboom 4:0fd5e5e121ea 62 logInfo("trying to send before joining, retry join...");
Jan Jongboom 4:0fd5e5e121ea 63 if ((ret = dot->joinNetwork()) != mDot::MDOT_OK ) {
Jan Jongboom 4:0fd5e5e121ea 64 logError("failed to join network %d:%s", ret, mDot::getReturnCodeString(ret).c_str());
Jan Jongboom 4:0fd5e5e121ea 65 return false;
Jan Jongboom 4:0fd5e5e121ea 66 }
Jan Jongboom 4:0fd5e5e121ea 67 else {
Jan Jongboom 4:0fd5e5e121ea 68 logInfo("joined network successfully");
Jan Jongboom 4:0fd5e5e121ea 69 }
Jan Jongboom 4:0fd5e5e121ea 70 }
Jan Jongboom 4:0fd5e5e121ea 71
janjongboom 0:20fbd6f66b11 72 std::vector<uint8_t> data;
janjongboom 0:20fbd6f66b11 73 data.push_back(counter >> 8 & 0xff);
janjongboom 0:20fbd6f66b11 74 data.push_back(counter & 0xff);
janjongboom 0:20fbd6f66b11 75
janjongboom 0:20fbd6f66b11 76 logInfo("sending %d bytes", data.size());
janjongboom 0:20fbd6f66b11 77 if ((ret = dot->send(data)) != mDot::MDOT_OK) {
janjongboom 0:20fbd6f66b11 78 logError("failed to send %d:%s", ret, mDot::getReturnCodeString(ret).c_str());
janjongboom 0:20fbd6f66b11 79 return false;
janjongboom 0:20fbd6f66b11 80 } else {
janjongboom 0:20fbd6f66b11 81 logInfo("successfully sent data to gateway");
janjongboom 0:20fbd6f66b11 82
janjongboom 0:20fbd6f66b11 83 std::vector<uint8_t> recv_data;
janjongboom 0:20fbd6f66b11 84 if ((ret = dot->recv(recv_data)) != mDot::MDOT_OK) {
janjongboom 0:20fbd6f66b11 85 logError("failed to recv %d:%s", ret, mDot::getReturnCodeString(ret).c_str());
janjongboom 0:20fbd6f66b11 86 return true; // sending succeeded, just recv failed
janjongboom 0:20fbd6f66b11 87 }
Jan Jongboom 2:ff17ce021cfb 88
janjongboom 0:20fbd6f66b11 89 if (recv_data.size() > 0) {
janjongboom 0:20fbd6f66b11 90 printf("[INFO] received %d bytes:", recv_data.size());
janjongboom 0:20fbd6f66b11 91 for (size_t ix = 0; ix < recv_data.size(); ix++) {
janjongboom 0:20fbd6f66b11 92 printf(" %02x", recv_data[ix]);
janjongboom 0:20fbd6f66b11 93 }
janjongboom 0:20fbd6f66b11 94 printf("\r\n");
Jan Jongboom 2:ff17ce021cfb 95
janjongboom 0:20fbd6f66b11 96 if (recv_data[0] == 1) {
janjongboom 0:20fbd6f66b11 97 led = BUILTIN_LED_ON;
janjongboom 0:20fbd6f66b11 98 }
janjongboom 0:20fbd6f66b11 99 else {
janjongboom 0:20fbd6f66b11 100 led = BUILTIN_LED_OFF;
janjongboom 0:20fbd6f66b11 101 }
janjongboom 0:20fbd6f66b11 102 }
Jan Jongboom 2:ff17ce021cfb 103
janjongboom 0:20fbd6f66b11 104 return true;
janjongboom 0:20fbd6f66b11 105 }
janjongboom 0:20fbd6f66b11 106 }
janjongboom 0:20fbd6f66b11 107
janjongboom 0:20fbd6f66b11 108 static void wakeUpCallback() {
janjongboom 0:20fbd6f66b11 109 logInfo("woke up, fromInterrupt=%d", woke_from_interrupt);
Jan Jongboom 2:ff17ce021cfb 110
janjongboom 0:20fbd6f66b11 111 bool wfi = woke_from_interrupt;
Jan Jongboom 2:ff17ce021cfb 112
janjongboom 0:20fbd6f66b11 113 // if we were woken up by RTC_ALARM, first up the counter
janjongboom 0:20fbd6f66b11 114 if (wfi) {
janjongboom 0:20fbd6f66b11 115 // reset the interrupt var
janjongboom 0:20fbd6f66b11 116 woke_from_interrupt = false;
janjongboom 0:20fbd6f66b11 117
janjongboom 0:20fbd6f66b11 118 up_counter();
janjongboom 0:20fbd6f66b11 119 }
Jan Jongboom 2:ff17ce021cfb 120
janjongboom 0:20fbd6f66b11 121
janjongboom 0:20fbd6f66b11 122 bool sent = send_data();
janjongboom 0:20fbd6f66b11 123 // not sent? try again in 5 minutes...
janjongboom 0:20fbd6f66b11 124 if (!sent) {
janjongboom 0:20fbd6f66b11 125 uint32_t sleep_time = 5 * 60;
Jan Jongboom 2:ff17ce021cfb 126
janjongboom 0:20fbd6f66b11 127 // if woke from button press, check duty cycle first...
janjongboom 0:20fbd6f66b11 128 if (wfi) {
janjongboom 0:20fbd6f66b11 129 // hmm.. something went wrong. Probably duty cycle, see next Tx frame
janjongboom 0:20fbd6f66b11 130 // get the next transmission frame (in whole seconds)
janjongboom 0:20fbd6f66b11 131 sleep_time = ceil(static_cast<float>(dot->getNextTxMs()) / 1000.0f);
Jan Jongboom 2:ff17ce021cfb 132
janjongboom 0:20fbd6f66b11 133 // Tx window open, but no success? Try again in 30s.
janjongboom 0:20fbd6f66b11 134 if (sleep_time == 0) sleep_time = 30;
janjongboom 0:20fbd6f66b11 135 }
Jan Jongboom 2:ff17ce021cfb 136
janjongboom 0:20fbd6f66b11 137 logInfo("Going back to sleep (RTC_ALARM), time=%d", sleep_time);
janjongboom 0:20fbd6f66b11 138 dot->sleep(sleep_time, mDot::RTC_ALARM, false);
janjongboom 0:20fbd6f66b11 139 }
janjongboom 0:20fbd6f66b11 140 else {
janjongboom 0:20fbd6f66b11 141 logInfo("Going back to sleep (INTERRUPT)");
janjongboom 0:20fbd6f66b11 142
janjongboom 0:20fbd6f66b11 143 // go back to sleep (wait for an interrupt to happen)
janjongboom 0:20fbd6f66b11 144 dot->sleep(0, mDot::INTERRUPT, false);
janjongboom 0:20fbd6f66b11 145 }
janjongboom 0:20fbd6f66b11 146 }
janjongboom 0:20fbd6f66b11 147
janjongboom 0:20fbd6f66b11 148
janjongboom 0:20fbd6f66b11 149 int main() {
janjongboom 0:20fbd6f66b11 150 int32_t ret;
janjongboom 0:20fbd6f66b11 151 printf("Entering main()\r\n");
janjongboom 0:20fbd6f66b11 152
janjongboom 0:20fbd6f66b11 153 btn.rise(&btn_rise);
janjongboom 0:20fbd6f66b11 154
janjongboom 0:20fbd6f66b11 155 // get a mDot handle
janjongboom 0:20fbd6f66b11 156 dot = mDot::getInstance();
janjongboom 0:20fbd6f66b11 157
janjongboom 0:20fbd6f66b11 158 dot->setLogLevel(mts::MTSLog::DEBUG_LEVEL);
janjongboom 0:20fbd6f66b11 159
janjongboom 0:20fbd6f66b11 160 // print library version information
janjongboom 0:20fbd6f66b11 161 logInfo("version: %s", dot->getId().c_str());
janjongboom 0:20fbd6f66b11 162
Jan Jongboom 2:ff17ce021cfb 163 std::vector<uint8_t> devEui = dot->getDeviceId();
Jan Jongboom 2:ff17ce021cfb 164 logInfo("device eui: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
Jan Jongboom 2:ff17ce021cfb 165 devEui[0], devEui[1], devEui[2], devEui[3], devEui[4], devEui[5], devEui[6], devEui[7]);
Jan Jongboom 2:ff17ce021cfb 166
janjongboom 0:20fbd6f66b11 167 //*******************************************
janjongboom 0:20fbd6f66b11 168 // configuration
janjongboom 0:20fbd6f66b11 169 //*******************************************
janjongboom 0:20fbd6f66b11 170 // reset to default config so we know what state we're in
janjongboom 0:20fbd6f66b11 171 dot->resetConfig();
janjongboom 0:20fbd6f66b11 172
janjongboom 0:20fbd6f66b11 173 logInfo("frequencyBand: %d", dot->getFrequencyBand());
janjongboom 0:20fbd6f66b11 174
janjongboom 0:20fbd6f66b11 175 // set up the mDot with our network information: frequency sub band, network name, and network password
janjongboom 0:20fbd6f66b11 176 // these can all be saved in NVM so they don't need to be set every time - see mDot::saveConfig()
janjongboom 0:20fbd6f66b11 177
janjongboom 0:20fbd6f66b11 178 logInfo("setting public network");
janjongboom 0:20fbd6f66b11 179 if ((ret = dot->setPublicNetwork(true)) != mDot::MDOT_OK) {
janjongboom 0:20fbd6f66b11 180 logError("failed to set public network %d:%s", ret, mDot::getReturnCodeString(ret).c_str());
janjongboom 0:20fbd6f66b11 181 }
janjongboom 0:20fbd6f66b11 182
janjongboom 0:20fbd6f66b11 183 logInfo("setting tx power to 20");
janjongboom 0:20fbd6f66b11 184 if ((ret = dot->setTxPower(18)) != mDot::MDOT_OK) {
janjongboom 0:20fbd6f66b11 185 logError("failed to set tx power %d:%s", ret, mDot::getReturnCodeString(ret).c_str());
janjongboom 0:20fbd6f66b11 186 }
janjongboom 0:20fbd6f66b11 187
janjongboom 0:20fbd6f66b11 188 // set up the network keys
Jan Jongboom 2:ff17ce021cfb 189 ParseKeys::initializeOta(dot, APP_EUI, APP_KEY);
janjongboom 0:20fbd6f66b11 190
janjongboom 0:20fbd6f66b11 191 // a higher spreading factor allows for longer range but lower throughput
janjongboom 0:20fbd6f66b11 192 // in the 915 (US) frequency band, spreading factors 7 - 10 are available
janjongboom 0:20fbd6f66b11 193 // in the 868 (EU) frequency band, spreading factors 7 - 12 are available
janjongboom 0:20fbd6f66b11 194 logInfo("setting TX spreading factor");
janjongboom 0:20fbd6f66b11 195 if ((ret = dot->setTxDataRate(mDot::SF_9)) != mDot::MDOT_OK) {
janjongboom 0:20fbd6f66b11 196 logError("failed to set TX datarate %d:%s", ret, mDot::getReturnCodeString(ret).c_str());
janjongboom 0:20fbd6f66b11 197 }
janjongboom 0:20fbd6f66b11 198
janjongboom 0:20fbd6f66b11 199 // request receive confirmation of packets from the gateway
janjongboom 0:20fbd6f66b11 200 logInfo("enabling ACKs");
janjongboom 0:20fbd6f66b11 201 if ((ret = dot->setAck(1)) != mDot::MDOT_OK) {
janjongboom 0:20fbd6f66b11 202 logError("failed to enable ACKs %d:%s", ret, mDot::getReturnCodeString(ret).c_str());
janjongboom 0:20fbd6f66b11 203 }
janjongboom 0:20fbd6f66b11 204
janjongboom 0:20fbd6f66b11 205 logInfo("enabling ADR");
janjongboom 0:20fbd6f66b11 206 if ((ret = dot->setAdr(1)) != mDot::MDOT_OK) {
janjongboom 0:20fbd6f66b11 207 logError("failed to enable ADR %d:%s", ret, mDot::getReturnCodeString(ret).c_str());
janjongboom 0:20fbd6f66b11 208 }
janjongboom 0:20fbd6f66b11 209
janjongboom 0:20fbd6f66b11 210 // save this configuration to the mDot's NVM
janjongboom 0:20fbd6f66b11 211 logInfo("saving config");
janjongboom 0:20fbd6f66b11 212 if (! dot->saveConfig()) {
janjongboom 0:20fbd6f66b11 213 logError("failed to save configuration");
janjongboom 0:20fbd6f66b11 214 }
janjongboom 0:20fbd6f66b11 215
Jan Jongboom 2:ff17ce021cfb 216 // OTA JOIN sequence, remove when using personalized mode
Jan Jongboom 2:ff17ce021cfb 217 logInfo("joining network");
Jan Jongboom 2:ff17ce021cfb 218 if ((ret = dot->joinNetwork()) != mDot::MDOT_OK ) {
Jan Jongboom 2:ff17ce021cfb 219 logError("failed to join network %d:%s", ret, mDot::getReturnCodeString(ret).c_str());
Jan Jongboom 2:ff17ce021cfb 220 }
Jan Jongboom 4:0fd5e5e121ea 221 else {
Jan Jongboom 4:0fd5e5e121ea 222 logInfo("joined network successfully");
Jan Jongboom 4:0fd5e5e121ea 223 }
Jan Jongboom 2:ff17ce021cfb 224
janjongboom 0:20fbd6f66b11 225 //*******************************************
janjongboom 0:20fbd6f66b11 226 // end of configuration
janjongboom 0:20fbd6f66b11 227 //*******************************************
janjongboom 0:20fbd6f66b11 228
janjongboom 0:20fbd6f66b11 229 read_counter();
janjongboom 0:20fbd6f66b11 230
janjongboom 0:20fbd6f66b11 231 dot->setWakeupCallback(&wakeUpCallback);
janjongboom 0:20fbd6f66b11 232
janjongboom 0:20fbd6f66b11 233 dot->sleep(0, mDot::INTERRUPT, false);
janjongboom 0:20fbd6f66b11 234
janjongboom 0:20fbd6f66b11 235 while (true) {
janjongboom 0:20fbd6f66b11 236 wait_ms(1000);
janjongboom 0:20fbd6f66b11 237 }
janjongboom 0:20fbd6f66b11 238 }