fang chen / Mbed OS mdot_two_way

Dependencies:   libmDot-mbed5

Files at this revision

API Documentation at this revision

Comitter:
janjongboom
Date:
Tue Jan 03 13:20:36 2017 +0000
Child:
1:b604a2d6c54d
Commit message:
Initial commit

Changed in this revision

README.md Show annotated file Show diff for this revision Revisions of this file
libmDot.lib Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed-rtos.lib Show annotated file Show diff for this revision Revisions of this file
mbed.bld Show annotated file Show diff for this revision Revisions of this file
mbed_settings.py Show annotated file Show diff for this revision Revisions of this file
parse_keys.h Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/README.md	Tue Jan 03 13:20:36 2017 +0000
@@ -0,0 +1,52 @@
+# LoRa Soap dispenser
+
+Smart soap dispenser that sends out stuff over LoRa. Does not build with GCC, only ARMCC (also with mbed CLI).
+
+## Setup
+
+1. Set jumper JP95 (next to RESET button) to the two pins closest to the mDot.
+2. Attach something that generates an interrupt on D6 / PA_1.
+3. RFID code needs to be implemented, currently spits out a random ID.
+
+To get data into Device Connector:
+
+1. First get keys by exchanging a device cert for LoRa keys at http://apm-lora-eu2.cloudapp.net:5101.
+2. Add the keys to ``main.cpp``.
+3. Then under 'Resources' add:
+
+    ```
+    {
+        "dispenser/0/value": {
+            "mode": "r",
+            "defaultValue": "0"
+        },
+        "meta/0/timestamp": {
+            "mode": "r",
+            "defaultValue": ""
+        },
+        "meta/0/ids_seen": {
+            "mode": "r",
+            "defaultValue": ""
+        }
+    }
+    ```
+
+4. Under 'Process Data' add:
+
+    ```js
+    function (bytes) {
+        var ids = [];
+        for (var i = 0; i < (bytes.length - 2) / 4; i++) {
+            var c = bytes.slice(2 + (i * 4), 2 + (i * 4) + 4);
+            ids.push((c[0] << 24) + (c[1] << 16) + (c[2] << 8) + (c[3]));
+        }
+
+        return {
+            "dispenser/0/value": (bytes[0] << 8) + bytes[1],
+            "meta/0/timestamp": new Date().toISOString(),
+            "meta/0/ids_seen": ids.join(',')
+        };
+    }
+    ```
+
+5. Click 'Save' and your data should show up in Device Connector (after the next transmission).
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmDot.lib	Tue Jan 03 13:20:36 2017 +0000
@@ -0,0 +1,1 @@
+https://developer.mbed.org/teams/MultiTech/code/libmDot/#121e4c454964
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Tue Jan 03 13:20:36 2017 +0000
@@ -0,0 +1,220 @@
+#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 LORIOT_DEV_ADDR[]  = "01F83E96"; // Use the big endian version here
+static const char LORIOT_NWK_S_KEY[] = "6B11E4D0814BE7BC5707920B4B3545D6"; // NWKSKEY
+static const char LORIOT_APP_S_KEY[] = "5C79DCD7E2CCB93A83730BC7A9BC6CFE"; // APPSKEY
+
+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;
+
+    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());
+
+    //*******************************************
+    // 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()
+
+    // set join mode to MANUAL so the mDot doesn't have to rejoin after sleeping
+    logInfo("setting join mode to MANUAL");
+    if ((ret = dot->setJoinMode(mDot::MANUAL)) != mDot::MDOT_OK) {
+        logError("failed to set join mode %d:%s", ret, mDot::getReturnCodeString(ret).c_str());
+    }
+
+    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::initialize(dot, LORIOT_DEV_ADDR, LORIOT_NWK_S_KEY, LORIOT_APP_S_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");
+    }
+
+    //*******************************************
+    // end of configuration
+    //*******************************************
+
+    read_counter();
+
+    dot->setWakeupCallback(&wakeUpCallback);
+
+    dot->sleep(0, mDot::INTERRUPT, false);
+
+    while (true) {
+        wait_ms(1000);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed-rtos.lib	Tue Jan 03 13:20:36 2017 +0000
@@ -0,0 +1,1 @@
+https://developer.mbed.org/users/mbed_official/code/mbed-rtos/#d7bd06319118
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed.bld	Tue Jan 03 13:20:36 2017 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/mbed_official/code/mbed/builds/4f6c30876dfa
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed_settings.py	Tue Jan 03 13:20:36 2017 +0000
@@ -0,0 +1,45 @@
+"""
+mbed SDK
+Copyright (c) 2016 ARM Limited
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+"""
+
+from os.path import join, abspath, dirname
+
+#ROOT = abspath(join(dirname(__file__), "."))
+
+##############################################################################
+# Build System Settings
+##############################################################################
+#BUILD_DIR = abspath(join(ROOT, "build"))
+
+# ARM
+#ARM_PATH = "C:/Program Files/ARM"
+
+# GCC ARM
+#GCC_ARM_PATH = ""
+
+# GCC CodeRed
+#GCC_CR_PATH = "C:/code_red/RedSuite_4.2.0_349/redsuite/Tools/bin"
+
+# IAR
+#IAR_PATH = "C:/Program Files (x86)/IAR Systems/Embedded Workbench 7.0/arm"
+
+# Goanna static analyser. Please overload it in private_settings.py
+#GOANNA_PATH = "c:/Program Files (x86)/RedLizards/Goanna Central 3.2.3/bin"
+
+#BUILD_OPTIONS = []
+
+# mbed.org username
+#MBED_ORG_USER = ""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/parse_keys.h	Tue Jan 03 13:20:36 2017 +0000
@@ -0,0 +1,57 @@
+#ifndef DISPENSER_PARSE_KEYS_H
+#define DISPENSER_PARSE_KEYS_H
+
+#include "mDot.h"
+
+class ParseKeys {
+public:
+    static bool initialize(mDot* dot, const char* aDevAddr, const char* aNwkSKey, const char* aAppSKey) {
+        int32_t ret;
+
+        // parse devkey
+        std::vector<uint8_t> loriot_nwk_skey(16);
+        for (uint8_t ni = 0; ni < 16; ni++)
+        {
+            const char hex[] = { '0', 'x', aNwkSKey[ni * 2], aNwkSKey[ni * 2 + 1], '\0' };
+            loriot_nwk_skey[ni] = strtoul(hex, NULL, 16);
+        }
+
+        // parse appkey
+        std::vector<uint8_t> loriot_app_skey(16);
+        for (uint8_t ai = 0; ai < 16; ai++)
+        {
+            const char hex[] = { '0', 'x', aAppSKey[ai * 2], aAppSKey[ai * 2 + 1], '\0' };
+            loriot_app_skey[ai] = strtoul(hex, NULL, 16);
+        }
+
+        // parse dev addr
+        std::vector<uint8_t> loriot_dev_addr(4);
+        for (uint8_t ai = 0; ai < 4; ai++)
+        {
+            const char hex[] = { '0', 'x', aDevAddr[ai * 2], aDevAddr[ai * 2 + 1], '\0' };
+            loriot_dev_addr[ai] = strtoul(hex, NULL, 16);
+        }
+
+        logInfo("setting network address");
+        if ((ret = dot->setNetworkAddress(loriot_dev_addr)) != mDot::MDOT_OK) {
+            logError("failed to set network address %d:%s", ret, mDot::getReturnCodeString(ret).c_str());
+            return false;
+        }
+
+        logInfo("setting network session key");
+        if ((ret = dot->setNetworkSessionKey(loriot_nwk_skey)) != mDot::MDOT_OK) {
+            logError("failed to set network session key %d:%s", ret, mDot::getReturnCodeString(ret).c_str());
+            return false;
+        }
+
+        logInfo("setting app session key");
+        if ((ret = dot->setDataSessionKey(loriot_app_skey)) != mDot::MDOT_OK) {
+            logError("failed to set app session key %d:%s", ret, mDot::getReturnCodeString(ret).c_str());
+            return false;
+        }
+
+        return true;
+    }
+};
+
+#endif // DISPENSER_PARSE_KEYS_H