LMIC os_runloop() conflicts with sensor reading function

Hi folk,

I’m hitting a bit of a dead end trying to work around the os_runloop() routine of LMIC. I’m using an ultrasonic sensor that is based on critical timing as well; this in order to measure distance. When this function is in the setup before doing anything LMIC related, the reading works fine and sensor spit out the right data, so it’s not hardware, nor is my reading function. But, when I do call my sensor read inside the do_send(osjob_t* j) function, before sending a packet, the sensor does not have the time to perform measurement. Note, that my hardware is based on TPL51111 nano timer to raise hw interrupt and wake up my system, all of this works very well and I get very good OTAA stability and very low sleep current, 30uA.

If someone can indicate me where to execute this time critical sensor reading that would be fantastic.

here is my code : https://pastebin.com/fYJKUkXJ

Thanks !

You will probably run in several issues with your code.

First is, that the code which reads sensor data may break LMIC timing or vice versa, e.g. if you’re using blocking routines. Solution is to keep time critical jobs out of an LMIC job. Put it in separate RTOS task instead. Then take care of RTOS task priorities and context switch.

Second is, that your application must take care of some things when putting the CPU to sleep while LMIC is running. Look here for details.

Thank you for your answers! Any suggestion as to how such system could be simply implemented in Arduino dev env ? Couldn’t it be just possible to halt LMIC when I know for sure there is no RX/TX planned. ie: when I just woke up my node, LMIC does not need to do anything ?

Arduino dev does not support real multitasking, as far as i know.
But LMIC implements a simple OS job scheduler osjob_t which you could use.

Put your sensor reading code in a separate osjob_t read_sensor_job(), and remove it from send_job. Take care that your code in read_sensor_job() has no blocking parts, to ensure the LMIC OS scheduler can do it’s job. Build your code e.g. following the concept of an event driven FSM. This way coexistance with LMIC should work.

For sleeping while LMIC is running follow then given link. You will have to implement a solution which advances millis() after wake up from sleep.

Simple solution to avoid advancing of millis() could be to restart the device every time it wakes up from sleep.

About the stability of LMIC after deed sleep, I actually never had any issues and this without implementing any of the recommendation you suggest. It just simply works, packet get sent properly and arrive at destination, this for a long period of time, I have a node running for week every 5min, and never skipped a beat. So I’m not sure wether I should modify the current code? Reseting the device after deed sleep would make me loose my OTAA credential and therefore force me to join every 5min, which is not ideal either.

Thank you for the suggestion with the OS job scheduler, I will see what i can do with this and if it does sort my issues.

You won’t be able to receive downlink data on your node after waking up from sleep.
If you need to receive data only during join for OTAA, this may not affect you. But it may affect your LORAWAN network provider because your node then can’t receive MAC commands sent by the network controller, what could result to your node being blocked from the network.

1 Like

Yes, in my case no downlink data is expected from the gateway, only RX window after join.

Implementing the OS job scheduler does not seem to work and yield same results with the sensor not having the time to perform measurement.

void wakeUp()
{
  detachInterrupt(0);
  do_read(&read_job);
  do_send(&send_job);
}
void do_read(osjob_t* j)
{
    digitalWrite(en_pin, HIGH);
    sensor_data = get_distance();
    data[0] = sensor_data & 255;
    data[1] = (sensor_data >> 8) & 255;   
}

Lots of mistaken information being posted here

For a class A node, the timing-critical part of LMiC is between transmit and receive. Delaying transmit by waiting for something is really not an issue.

Scheduling a another job could be a cleaner solution, but LMiC’s scheduler is cooperative and does not account for the time which jobs will take, thus anything that could result in the sensor job getting run in between transmit and receive would often break things, and there isn’t a real easy way to avoid that with the existing code. Doing your sensor readings on th way to transmitting may be simplest…

In terms of updating millis(), this is primarily an issue if you sleep between transmit and receive - so it’s simplest if you just stay awake for that second.

The second reason for wanting millis() to keep advancing is that if you are in a region where LMiC needs to do duty cycle limiting, not having millis() advance during the sleep will cause it to think time is passing very slowly, and absurdly limit itself compared to the actual advance a time.

Simple solution to avoid advancing of millis() could be to restart the device every time it wakes up from sleep.

This is a truly terrible idea. It fixes the problem with rate limiting only by throwing away the rate limiting history.

Worse, cold-restarting LMiC has to be avoided - a compliant LoRaWAN node must track and not repeat its frame count within a session, so you’d have to have a place to save that and restore it.

Some will try to use OTAA to create a new session instead, but this simply moves the problem and creates a new one. Each join request must use a fresh not-previously-used join nonce, so you then have to keep track of those. And you can only join so many times before the device EUI itself is worn out. Plus joining is very expensive for the network as it costs a bare minimum of one downlink, often more. So don’t do a cold restart and rejoin after each sleep, either.

5 Likes

Digging a little deeper, I think the root issue of my problem is mostly independent from LMIC.

The sensor could run perfectly fine when the en_pin was kept HIGH, it indicates some critical timing upon start-up for the ultrasonic sensor to initialise as the sensor I am using (SRF05) does have a slave MCU to performs the ultrasonic sensor transducer measurement, although no source code or info is available on such timing. So the issue should be that I am not waiting long enough before sending my ping request after waking up. However, and this could be LMIC dependent; I can’t seem to be able to force upon a delay_ms when my node wakes up from deep sleep, any delay I write does not get interpreted (weirdly).

A work around would be to use the interrupt signal from TPL51111 to enable the boost convertor as well as waking up the MCU. And by using an RC circuit or 555 timer to delay the interrupt coming in the MCU to wake up and going un-delayed into the boost enable pin, and tuning the value to a delay that satisfy the slave MCU of the sensor. This somehow seems complicated, but at this point I find such solution more elegant than trying messing around the very fragile LMIC runtime.

If anyone has a better suggestion that does involves an hardware fix; I would be very happy to hear them.

Regarding the other issue of waking up LMIC node from deep sleep and the LoRa WAN standard. I have done some more research and it seems like I am not doing anything wrong. And again, this part of the node works like a charm.

Cheers,

Seems like what you really need to do is understand, preferably by documentation but failing that by experiment, what the actual requirements of your sensor are.

Likely you could wake up, set the sensor pin, go back to sleep, and then wake up to get the answer. With many processors, you wouldn’t actually have to be fully awake to hold a pin in a state, though some of the simpler ones may force that, or keep you in the lightest sleep states to do it.

Realistically though, it’s unlikely that running your main processor at an ordinary clock rate for whatever small amount of time it takes to warm up and prepare the sensor and take a reading is going to be a serious battery expense, compared to transmitting the reading.

Unless your main controller is a bit primitive rather than being a modern traditional MCU, adding hardware is not the answer; understanding the requirement and leveraging the hardware you have is.