Two way data over LoRaWAN using Multitech mDot
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.
main.cpp@2:ff17ce021cfb, 2017-01-03 (annotated)
- Committer:
- Jan Jongboom
- Date:
- Tue Jan 03 16:05:47 2017 +0000
- Revision:
- 2:ff17ce021cfb
- Parent:
- 0:20fbd6f66b11
- Child:
- 3:ac5101a47080
Switch to OTA
Who changed what in which revision?
User | Revision | Line number | New 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 |
2:ff17ce021cfb | 14 | static const char APP_KEY[] = "CF977DE56163FB1055A530CF7B82A56A"; |
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 | |
janjongboom | 0:20fbd6f66b11 | 60 | std::vector<uint8_t> data; |
janjongboom | 0:20fbd6f66b11 | 61 | data.push_back(counter >> 8 & 0xff); |
janjongboom | 0:20fbd6f66b11 | 62 | data.push_back(counter & 0xff); |
janjongboom | 0:20fbd6f66b11 | 63 | |
janjongboom | 0:20fbd6f66b11 | 64 | logInfo("sending %d bytes", data.size()); |
janjongboom | 0:20fbd6f66b11 | 65 | if ((ret = dot->send(data)) != mDot::MDOT_OK) { |
janjongboom | 0:20fbd6f66b11 | 66 | logError("failed to send %d:%s", ret, mDot::getReturnCodeString(ret).c_str()); |
janjongboom | 0:20fbd6f66b11 | 67 | return false; |
janjongboom | 0:20fbd6f66b11 | 68 | } else { |
janjongboom | 0:20fbd6f66b11 | 69 | logInfo("successfully sent data to gateway"); |
janjongboom | 0:20fbd6f66b11 | 70 | |
janjongboom | 0:20fbd6f66b11 | 71 | std::vector<uint8_t> recv_data; |
janjongboom | 0:20fbd6f66b11 | 72 | if ((ret = dot->recv(recv_data)) != mDot::MDOT_OK) { |
janjongboom | 0:20fbd6f66b11 | 73 | logError("failed to recv %d:%s", ret, mDot::getReturnCodeString(ret).c_str()); |
janjongboom | 0:20fbd6f66b11 | 74 | return true; // sending succeeded, just recv failed |
janjongboom | 0:20fbd6f66b11 | 75 | } |
Jan Jongboom |
2:ff17ce021cfb | 76 | |
janjongboom | 0:20fbd6f66b11 | 77 | if (recv_data.size() > 0) { |
janjongboom | 0:20fbd6f66b11 | 78 | printf("[INFO] received %d bytes:", recv_data.size()); |
janjongboom | 0:20fbd6f66b11 | 79 | for (size_t ix = 0; ix < recv_data.size(); ix++) { |
janjongboom | 0:20fbd6f66b11 | 80 | printf(" %02x", recv_data[ix]); |
janjongboom | 0:20fbd6f66b11 | 81 | } |
janjongboom | 0:20fbd6f66b11 | 82 | printf("\r\n"); |
Jan Jongboom |
2:ff17ce021cfb | 83 | |
janjongboom | 0:20fbd6f66b11 | 84 | if (recv_data[0] == 1) { |
janjongboom | 0:20fbd6f66b11 | 85 | led = BUILTIN_LED_ON; |
janjongboom | 0:20fbd6f66b11 | 86 | } |
janjongboom | 0:20fbd6f66b11 | 87 | else { |
janjongboom | 0:20fbd6f66b11 | 88 | led = BUILTIN_LED_OFF; |
janjongboom | 0:20fbd6f66b11 | 89 | } |
janjongboom | 0:20fbd6f66b11 | 90 | } |
Jan Jongboom |
2:ff17ce021cfb | 91 | |
janjongboom | 0:20fbd6f66b11 | 92 | return true; |
janjongboom | 0:20fbd6f66b11 | 93 | } |
janjongboom | 0:20fbd6f66b11 | 94 | } |
janjongboom | 0:20fbd6f66b11 | 95 | |
janjongboom | 0:20fbd6f66b11 | 96 | static void wakeUpCallback() { |
janjongboom | 0:20fbd6f66b11 | 97 | logInfo("woke up, fromInterrupt=%d", woke_from_interrupt); |
Jan Jongboom |
2:ff17ce021cfb | 98 | |
janjongboom | 0:20fbd6f66b11 | 99 | bool wfi = woke_from_interrupt; |
Jan Jongboom |
2:ff17ce021cfb | 100 | |
janjongboom | 0:20fbd6f66b11 | 101 | // if we were woken up by RTC_ALARM, first up the counter |
janjongboom | 0:20fbd6f66b11 | 102 | if (wfi) { |
janjongboom | 0:20fbd6f66b11 | 103 | // reset the interrupt var |
janjongboom | 0:20fbd6f66b11 | 104 | woke_from_interrupt = false; |
janjongboom | 0:20fbd6f66b11 | 105 | |
janjongboom | 0:20fbd6f66b11 | 106 | up_counter(); |
janjongboom | 0:20fbd6f66b11 | 107 | } |
Jan Jongboom |
2:ff17ce021cfb | 108 | |
janjongboom | 0:20fbd6f66b11 | 109 | |
janjongboom | 0:20fbd6f66b11 | 110 | bool sent = send_data(); |
janjongboom | 0:20fbd6f66b11 | 111 | // not sent? try again in 5 minutes... |
janjongboom | 0:20fbd6f66b11 | 112 | if (!sent) { |
janjongboom | 0:20fbd6f66b11 | 113 | uint32_t sleep_time = 5 * 60; |
Jan Jongboom |
2:ff17ce021cfb | 114 | |
janjongboom | 0:20fbd6f66b11 | 115 | // if woke from button press, check duty cycle first... |
janjongboom | 0:20fbd6f66b11 | 116 | if (wfi) { |
janjongboom | 0:20fbd6f66b11 | 117 | // hmm.. something went wrong. Probably duty cycle, see next Tx frame |
janjongboom | 0:20fbd6f66b11 | 118 | // get the next transmission frame (in whole seconds) |
janjongboom | 0:20fbd6f66b11 | 119 | sleep_time = ceil(static_cast<float>(dot->getNextTxMs()) / 1000.0f); |
Jan Jongboom |
2:ff17ce021cfb | 120 | |
janjongboom | 0:20fbd6f66b11 | 121 | // Tx window open, but no success? Try again in 30s. |
janjongboom | 0:20fbd6f66b11 | 122 | if (sleep_time == 0) sleep_time = 30; |
janjongboom | 0:20fbd6f66b11 | 123 | } |
Jan Jongboom |
2:ff17ce021cfb | 124 | |
janjongboom | 0:20fbd6f66b11 | 125 | logInfo("Going back to sleep (RTC_ALARM), time=%d", sleep_time); |
janjongboom | 0:20fbd6f66b11 | 126 | dot->sleep(sleep_time, mDot::RTC_ALARM, false); |
janjongboom | 0:20fbd6f66b11 | 127 | } |
janjongboom | 0:20fbd6f66b11 | 128 | else { |
janjongboom | 0:20fbd6f66b11 | 129 | logInfo("Going back to sleep (INTERRUPT)"); |
janjongboom | 0:20fbd6f66b11 | 130 | |
janjongboom | 0:20fbd6f66b11 | 131 | // go back to sleep (wait for an interrupt to happen) |
janjongboom | 0:20fbd6f66b11 | 132 | dot->sleep(0, mDot::INTERRUPT, false); |
janjongboom | 0:20fbd6f66b11 | 133 | } |
janjongboom | 0:20fbd6f66b11 | 134 | } |
janjongboom | 0:20fbd6f66b11 | 135 | |
janjongboom | 0:20fbd6f66b11 | 136 | |
janjongboom | 0:20fbd6f66b11 | 137 | int main() { |
janjongboom | 0:20fbd6f66b11 | 138 | int32_t ret; |
janjongboom | 0:20fbd6f66b11 | 139 | printf("Entering main()\r\n"); |
janjongboom | 0:20fbd6f66b11 | 140 | |
janjongboom | 0:20fbd6f66b11 | 141 | btn.rise(&btn_rise); |
janjongboom | 0:20fbd6f66b11 | 142 | |
janjongboom | 0:20fbd6f66b11 | 143 | // get a mDot handle |
janjongboom | 0:20fbd6f66b11 | 144 | dot = mDot::getInstance(); |
janjongboom | 0:20fbd6f66b11 | 145 | |
janjongboom | 0:20fbd6f66b11 | 146 | dot->setLogLevel(mts::MTSLog::DEBUG_LEVEL); |
janjongboom | 0:20fbd6f66b11 | 147 | |
janjongboom | 0:20fbd6f66b11 | 148 | // print library version information |
janjongboom | 0:20fbd6f66b11 | 149 | logInfo("version: %s", dot->getId().c_str()); |
janjongboom | 0:20fbd6f66b11 | 150 | |
Jan Jongboom |
2:ff17ce021cfb | 151 | std::vector<uint8_t> devEui = dot->getDeviceId(); |
Jan Jongboom |
2:ff17ce021cfb | 152 | logInfo("device eui: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", |
Jan Jongboom |
2:ff17ce021cfb | 153 | devEui[0], devEui[1], devEui[2], devEui[3], devEui[4], devEui[5], devEui[6], devEui[7]); |
Jan Jongboom |
2:ff17ce021cfb | 154 | |
janjongboom | 0:20fbd6f66b11 | 155 | //******************************************* |
janjongboom | 0:20fbd6f66b11 | 156 | // configuration |
janjongboom | 0:20fbd6f66b11 | 157 | //******************************************* |
janjongboom | 0:20fbd6f66b11 | 158 | // reset to default config so we know what state we're in |
janjongboom | 0:20fbd6f66b11 | 159 | dot->resetConfig(); |
janjongboom | 0:20fbd6f66b11 | 160 | |
janjongboom | 0:20fbd6f66b11 | 161 | logInfo("frequencyBand: %d", dot->getFrequencyBand()); |
janjongboom | 0:20fbd6f66b11 | 162 | |
janjongboom | 0:20fbd6f66b11 | 163 | // set up the mDot with our network information: frequency sub band, network name, and network password |
janjongboom | 0:20fbd6f66b11 | 164 | // these can all be saved in NVM so they don't need to be set every time - see mDot::saveConfig() |
janjongboom | 0:20fbd6f66b11 | 165 | |
janjongboom | 0:20fbd6f66b11 | 166 | logInfo("setting public network"); |
janjongboom | 0:20fbd6f66b11 | 167 | if ((ret = dot->setPublicNetwork(true)) != mDot::MDOT_OK) { |
janjongboom | 0:20fbd6f66b11 | 168 | logError("failed to set public network %d:%s", ret, mDot::getReturnCodeString(ret).c_str()); |
janjongboom | 0:20fbd6f66b11 | 169 | } |
janjongboom | 0:20fbd6f66b11 | 170 | |
janjongboom | 0:20fbd6f66b11 | 171 | logInfo("setting tx power to 20"); |
janjongboom | 0:20fbd6f66b11 | 172 | if ((ret = dot->setTxPower(18)) != mDot::MDOT_OK) { |
janjongboom | 0:20fbd6f66b11 | 173 | logError("failed to set tx power %d:%s", ret, mDot::getReturnCodeString(ret).c_str()); |
janjongboom | 0:20fbd6f66b11 | 174 | } |
janjongboom | 0:20fbd6f66b11 | 175 | |
janjongboom | 0:20fbd6f66b11 | 176 | // set up the network keys |
Jan Jongboom |
2:ff17ce021cfb | 177 | ParseKeys::initializeOta(dot, APP_EUI, APP_KEY); |
janjongboom | 0:20fbd6f66b11 | 178 | |
janjongboom | 0:20fbd6f66b11 | 179 | // a higher spreading factor allows for longer range but lower throughput |
janjongboom | 0:20fbd6f66b11 | 180 | // in the 915 (US) frequency band, spreading factors 7 - 10 are available |
janjongboom | 0:20fbd6f66b11 | 181 | // in the 868 (EU) frequency band, spreading factors 7 - 12 are available |
janjongboom | 0:20fbd6f66b11 | 182 | logInfo("setting TX spreading factor"); |
janjongboom | 0:20fbd6f66b11 | 183 | if ((ret = dot->setTxDataRate(mDot::SF_9)) != mDot::MDOT_OK) { |
janjongboom | 0:20fbd6f66b11 | 184 | logError("failed to set TX datarate %d:%s", ret, mDot::getReturnCodeString(ret).c_str()); |
janjongboom | 0:20fbd6f66b11 | 185 | } |
janjongboom | 0:20fbd6f66b11 | 186 | |
janjongboom | 0:20fbd6f66b11 | 187 | // request receive confirmation of packets from the gateway |
janjongboom | 0:20fbd6f66b11 | 188 | logInfo("enabling ACKs"); |
janjongboom | 0:20fbd6f66b11 | 189 | if ((ret = dot->setAck(1)) != mDot::MDOT_OK) { |
janjongboom | 0:20fbd6f66b11 | 190 | logError("failed to enable ACKs %d:%s", ret, mDot::getReturnCodeString(ret).c_str()); |
janjongboom | 0:20fbd6f66b11 | 191 | } |
janjongboom | 0:20fbd6f66b11 | 192 | |
janjongboom | 0:20fbd6f66b11 | 193 | logInfo("enabling ADR"); |
janjongboom | 0:20fbd6f66b11 | 194 | if ((ret = dot->setAdr(1)) != mDot::MDOT_OK) { |
janjongboom | 0:20fbd6f66b11 | 195 | logError("failed to enable ADR %d:%s", ret, mDot::getReturnCodeString(ret).c_str()); |
janjongboom | 0:20fbd6f66b11 | 196 | } |
janjongboom | 0:20fbd6f66b11 | 197 | |
janjongboom | 0:20fbd6f66b11 | 198 | // save this configuration to the mDot's NVM |
janjongboom | 0:20fbd6f66b11 | 199 | logInfo("saving config"); |
janjongboom | 0:20fbd6f66b11 | 200 | if (! dot->saveConfig()) { |
janjongboom | 0:20fbd6f66b11 | 201 | logError("failed to save configuration"); |
janjongboom | 0:20fbd6f66b11 | 202 | } |
janjongboom | 0:20fbd6f66b11 | 203 | |
Jan Jongboom |
2:ff17ce021cfb | 204 | // OTA JOIN sequence, remove when using personalized mode |
Jan Jongboom |
2:ff17ce021cfb | 205 | logInfo("joining network"); |
Jan Jongboom |
2:ff17ce021cfb | 206 | if ((ret = dot->joinNetwork()) != mDot::MDOT_OK ) { |
Jan Jongboom |
2:ff17ce021cfb | 207 | logError("failed to join network %d:%s", ret, mDot::getReturnCodeString(ret).c_str()); |
Jan Jongboom |
2:ff17ce021cfb | 208 | return false; |
Jan Jongboom |
2:ff17ce021cfb | 209 | } |
Jan Jongboom |
2:ff17ce021cfb | 210 | logInfo("joined network successfully"); |
Jan Jongboom |
2:ff17ce021cfb | 211 | |
janjongboom | 0:20fbd6f66b11 | 212 | //******************************************* |
janjongboom | 0:20fbd6f66b11 | 213 | // end of configuration |
janjongboom | 0:20fbd6f66b11 | 214 | //******************************************* |
janjongboom | 0:20fbd6f66b11 | 215 | |
janjongboom | 0:20fbd6f66b11 | 216 | read_counter(); |
janjongboom | 0:20fbd6f66b11 | 217 | |
janjongboom | 0:20fbd6f66b11 | 218 | dot->setWakeupCallback(&wakeUpCallback); |
janjongboom | 0:20fbd6f66b11 | 219 | |
janjongboom | 0:20fbd6f66b11 | 220 | dot->sleep(0, mDot::INTERRUPT, false); |
janjongboom | 0:20fbd6f66b11 | 221 | |
janjongboom | 0:20fbd6f66b11 | 222 | while (true) { |
janjongboom | 0:20fbd6f66b11 | 223 | wait_ms(1000); |
janjongboom | 0:20fbd6f66b11 | 224 | } |
janjongboom | 0:20fbd6f66b11 | 225 | } |