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:
janjongboom
Date:
Tue Jan 03 13:20:36 2017 +0000
Revision:
0:20fbd6f66b11
Child:
2:ff17ce021cfb
Initial commit

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