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@0:20fbd6f66b11, 2017-01-03 (annotated)
- 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?
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 | |
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 | } |