mDot Sensornode Firmware Ready to use

Happy to annouce you that my Firmware is now able to
:tada: receive and process Downlink Messages.:tada:

More infos on how to use it can be found on my Wiki LoRa Downlink Messages Page I wrote.

If you have some Questions or Feedback don’t hesitate to post it in this thread.


Need help to debug… using mdot and custom channel lib

[details=Summary]#include “mbed.h”
#include “mDot.h”
#include “MTSLog.h”
#include “mbed.h”
#include “parse_keys.h”
#include “CustomChannelPlan_KR920.h”

#define BUILTIN_LED_ON 0

static const char APP_EUI[] = “BE7A000000000393”;
static const char APP_KEY[] = “3FE0040E234141A08A583D5F508B5781”;

Serial debug(USBTX, USBRX);

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”);



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]);

        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;


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”);


// 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]);
lora::CustomChannelPlan_KR920* plan = new lora::CustomChannelPlan_KR920(*dot->getRadio(), *dot->getSettings());

// configuration
// reset to default config so we know what state we're in

logInfo("frequencyBand: %d", dot->getFrequencyBand());

  logInfo("setting frequency sub band");    
if ((ret = dot->setFrequencySubBand(1)) != mDot::MDOT_OK) {
    logError("failed to set frequency sub band %d:%s", ret, mDot::getReturnCodeString(ret).c_str());

// 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());

logInfo("setting rx join freq");
if ((ret = dot->setJoinRx2Frequency(921900000)) != mDot::MDOT_OK) {
    logError("failed setting rx join freq %d:%s", ret, mDot::getReturnCodeString(ret).c_str());

logInfo("setting  join retry");
if ((ret = dot->setJoinRetries(5)) != mDot::MDOT_OK) {
    logError("failed setting  join retr %d:%s", ret, mDot::getReturnCodeString(ret).c_str());

logInfo("setting rx join dr");
if ((ret = dot->setJoinRx2DataRate(0)) != mDot::MDOT_OK) {
    logError("failed setting rx join dr %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) {
if ((ret = dot->setTxDataRate(mDot::DR5)) != 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->sleep(0, mDot::INTERRUPT, false);

// while (true) {
// wait_ms(1000);
// }


Hi shmrymbd,

Whats your question? You aren’t using my the firmware i’ve written?

hi. i need a custom channel… for as920-923 channeling. so i dont know which code is good to start with. thansk anyway for the reply

Hello man i used you code ,im using Bme 280 to gather pressure temp this what i get it doesnt make sense right?

Hi. Glad you are using it. Your data seems legit. These are the hex values you see. For example 42 31 3A is B 1 : .
To get a human readable format i suggest you to log into your ttn console then in your application go to payloadformats and under decoder you enter this code part:

function Decoder(bytes) {
var result="";
for (var i = 0; i < bytes.length; i++) {
result += String.fromCharCode(parseInt(bytes[i]));
return {
payload: result,

This will decode the hex values in to ascii and you will be able to read the payload.

If any further questions don’t hesitate to ask.

you are really helpful thanks ,am doing research after i gather data am going to use it and do some machine learning am kind of lost so i might ask you questions along the way ,thank you very much

so B1 is temp and B 2 is pressure?

Check main.h there are all IDs defined.

  • @defgroup LoRa Message IDs
  • @{

#define MAX44009_MESSAGE_ID “L”
#define MPU9250_X_TESLA_MESSAGE_ID “M7”
#define MPU9250_Y_TESLA_MESSAGE_ID “M8”
#define MPU9250_Z_TESLA_MESSAGE_ID “M9”



you can change them in your project to any id if you would like to: e.g.

#define BME280_TEMPERATURE_MESSAGE_ID “whateveryoulike”

“payload”: “B1:23.22,B2:96029.30,”
hey thank you very much i really apperictare what are you doing here.

i got the pressure as 96029.30 is that for example air pressure in Pa

You’re welcome.

Dont know if you are aware of the api documentation I wrote

How much programming experience do you have?
If you check the code of BME280.h you will notice in the function comment that it returns the pressure in hPa.

i dont have much am learning, i asked because my readings are giving me 95681.64 hPa which for me it doesnt make sense right? its about 95.6 bar

even in the above post by @
skramer she has the same readings maybe its a mistake in the program ?

You’re right. The value actualy seems to be to big by a factor of 100. I will take a deeper look into the code this weekend if i find some time. For now I guess you just have to divide it by 100.
Edit: i guess the comment in the code is wrong and its actually Pa not hPa then.

I never tried them. However theoretically you can connect almost any sensor to the mdot you just have to check if there is an interface for it. For example if you sensor needs pulse input you have to check if there is one gpio available on the mdot which can act like that. Then you only have to write a driver for it and add it to my existing code or do your own stuff.

1 Like

how can i create a driver like you said i got a library for flowmeter from github. i can add flowmeter.h and flowmeter.cpp then i dont know what to write in main please help!!

You can give me the link to the github repo and I will take a look at it.

1 Like Thank you i appreciate it

I took a look at it. It’s a library for Arduino. However it is theoretically possible to use it as part of my firmware but it needs some C++ in Embedded Systems programming experience in doing it since you need to use interrupts refactor the code a little bit and add a new Task and some other config stuff in order to integrate it properly. But I will not be able to do that in the next days/weeks since I have a lot of other stuff to do. If you need it urgent maybe someone else here in the forum with C++ knowledge can do it for you. If I did it I would need the sensor too in order to be able to test it properly.

Sure there’s also a possibility to quick and dirty hack it into the firmware. But from my experience in the long term it often takes as much or even more time than doing it right at the first time. But If you know somebody with C++ knowlegde and embedded systems he should be able to do it for you.

Or if you only need to use this Flowmeter I recommend you to check the forum here for arduino Lora Firmware. I’m pretty sure there is an Arduino project somewhere that uses Lora. Then you would only need to add the flowmeter stuff. Arduino has a big beginner friendly community its likely that you’ll find people here or in the internet who can help you if you struggle.

Edit: maybe worth a try: How to build your first TTN node: Arduino + RN2483

Thank you so much , I can send you a sensor if you have free time after a month or so, my deadline is beginning of December.? , I’ll try start working on it just one question what pin can I use as interrupt for flowmeter on mdot ? Am using UDK