Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Revision 0:20fbd6f66b11, committed 2017-01-03
- Comitter:
- janjongboom
- Date:
- Tue Jan 03 13:20:36 2017 +0000
- Child:
- 1:b604a2d6c54d
- Commit message:
- Initial commit
Changed in this revision
--- /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