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
- Committer:
- Jan Jongboom
- Date:
- 2017-01-04
- Revision:
- 4:0fd5e5e121ea
- Parent:
- 3:ac5101a47080
File content as of revision 4:0fd5e5e121ea:
#include "mbed.h" #include "mDot.h" #include "MTSLog.h" #include <string> #include <vector> #include <algorithm> #include "mbed.h" #include "parse_keys.h" #define BUILTIN_LED_ON 0 #define BUILTIN_LED_OFF 1 static const char APP_EUI[] = "BE7A000000000393"; static const char APP_KEY[] = "3FE0040E234141A08A583D5F508B5781"; static mDot* dot; // so we have some state static uint16_t counter = 0; // always persisted to non-volatile mem static bool woke_from_interrupt = false; static InterruptIn btn(PA_1); /* D6 */ static DigitalOut led(PC_13, BUILTIN_LED_OFF); /* D2 */ static void read_counter() { char buffer[2]; bool res = dot->readUserFile("counter", &buffer, 2); if (res) { counter = (buffer[0] << 8) + buffer[1]; } else { counter = 0; } } static void up_counter() { logInfo("up counter"); read_counter(); counter++; char buffer[2]; buffer[0] = counter >> 8 & 0xff; buffer[1] = counter & 0xff; logInfo("new counter value is: %d", counter); dot->saveUserFile("counter", &buffer, 2); } static void btn_rise() { woke_from_interrupt = true; // will be woken by STM32 } static bool send_data(void) { int32_t ret; // check join state, @todo: add a timeout here, join is very expensive on the battery... if (!dot->getNetworkJoinStatus()) { logInfo("trying to send before joining, retry join..."); if ((ret = dot->joinNetwork()) != mDot::MDOT_OK ) { logError("failed to join network %d:%s", ret, mDot::getReturnCodeString(ret).c_str()); return false; } else { logInfo("joined network successfully"); } } std::vector<uint8_t> data; data.push_back(counter >> 8 & 0xff); data.push_back(counter & 0xff); logInfo("sending %d bytes", data.size()); if ((ret = dot->send(data)) != mDot::MDOT_OK) { logError("failed to send %d:%s", ret, mDot::getReturnCodeString(ret).c_str()); return false; } else { logInfo("successfully sent data to gateway"); std::vector<uint8_t> recv_data; if ((ret = dot->recv(recv_data)) != mDot::MDOT_OK) { logError("failed to recv %d:%s", ret, mDot::getReturnCodeString(ret).c_str()); return true; // sending succeeded, just recv failed } if (recv_data.size() > 0) { printf("[INFO] received %d bytes:", recv_data.size()); for (size_t ix = 0; ix < recv_data.size(); ix++) { printf(" %02x", recv_data[ix]); } printf("\r\n"); if (recv_data[0] == 1) { led = BUILTIN_LED_ON; } else { led = BUILTIN_LED_OFF; } } return true; } } static void wakeUpCallback() { logInfo("woke up, fromInterrupt=%d", woke_from_interrupt); bool wfi = woke_from_interrupt; // if we were woken up by RTC_ALARM, first up the counter if (wfi) { // reset the interrupt var woke_from_interrupt = false; up_counter(); } bool sent = send_data(); // not sent? try again in 5 minutes... if (!sent) { uint32_t sleep_time = 5 * 60; // if woke from button press, check duty cycle first... if (wfi) { // hmm.. something went wrong. Probably duty cycle, see next Tx frame // get the next transmission frame (in whole seconds) sleep_time = ceil(static_cast<float>(dot->getNextTxMs()) / 1000.0f); // Tx window open, but no success? Try again in 30s. if (sleep_time == 0) sleep_time = 30; } logInfo("Going back to sleep (RTC_ALARM), time=%d", sleep_time); dot->sleep(sleep_time, mDot::RTC_ALARM, false); } else { logInfo("Going back to sleep (INTERRUPT)"); // go back to sleep (wait for an interrupt to happen) dot->sleep(0, mDot::INTERRUPT, false); } } int main() { int32_t ret; printf("Entering main()\r\n"); btn.rise(&btn_rise); // get a mDot handle dot = mDot::getInstance(); dot->setLogLevel(mts::MTSLog::DEBUG_LEVEL); // print library version information logInfo("version: %s", dot->getId().c_str()); std::vector<uint8_t> devEui = dot->getDeviceId(); logInfo("device eui: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", devEui[0], devEui[1], devEui[2], devEui[3], devEui[4], devEui[5], devEui[6], devEui[7]); //******************************************* // configuration //******************************************* // reset to default config so we know what state we're in dot->resetConfig(); logInfo("frequencyBand: %d", dot->getFrequencyBand()); // set up the mDot with our network information: frequency sub band, network name, and network password // these can all be saved in NVM so they don't need to be set every time - see mDot::saveConfig() logInfo("setting public network"); if ((ret = dot->setPublicNetwork(true)) != mDot::MDOT_OK) { logError("failed to set public network %d:%s", ret, mDot::getReturnCodeString(ret).c_str()); } logInfo("setting tx power to 20"); if ((ret = dot->setTxPower(18)) != mDot::MDOT_OK) { logError("failed to set tx power %d:%s", ret, mDot::getReturnCodeString(ret).c_str()); } // set up the network keys ParseKeys::initializeOta(dot, APP_EUI, APP_KEY); // a higher spreading factor allows for longer range but lower throughput // in the 915 (US) frequency band, spreading factors 7 - 10 are available // in the 868 (EU) frequency band, spreading factors 7 - 12 are available logInfo("setting TX spreading factor"); if ((ret = dot->setTxDataRate(mDot::SF_9)) != mDot::MDOT_OK) { logError("failed to set TX datarate %d:%s", ret, mDot::getReturnCodeString(ret).c_str()); } // request receive confirmation of packets from the gateway logInfo("enabling ACKs"); if ((ret = dot->setAck(1)) != mDot::MDOT_OK) { logError("failed to enable ACKs %d:%s", ret, mDot::getReturnCodeString(ret).c_str()); } logInfo("enabling ADR"); if ((ret = dot->setAdr(1)) != mDot::MDOT_OK) { logError("failed to enable ADR %d:%s", ret, mDot::getReturnCodeString(ret).c_str()); } // save this configuration to the mDot's NVM logInfo("saving config"); if (! dot->saveConfig()) { logError("failed to save configuration"); } // OTA JOIN sequence, remove when using personalized mode logInfo("joining network"); if ((ret = dot->joinNetwork()) != mDot::MDOT_OK ) { logError("failed to join network %d:%s", ret, mDot::getReturnCodeString(ret).c_str()); } else { logInfo("joined network successfully"); } //******************************************* // end of configuration //******************************************* read_counter(); dot->setWakeupCallback(&wakeUpCallback); dot->sleep(0, mDot::INTERRUPT, false); while (true) { wait_ms(1000); } }