IoT Cricket looks very interesting

cricket.png

This looks like a very interesting device. It’s called the IoT Cricket from Things on Edge. It looks like it brings Lora levels of battery performance to a WiFi connected device. It apparently has the ability to wake up, connect to a WiFi network and push out a packet of MQTT data in around 8 seconds. Which is pretty darned fast. There is only very simple PIO available for data inputs but there is onboard temperature and a clock that turns everything off between readings. You can power the device from one or two AA batteries or a LiPo and they are talking about battery lifetimes in the months and years.

It’s only 16 quid to get one, which will be a good price if it does what they claim. I’ve ordered one, looking forward to having a play.

M5 Atom and Tailbat

m5 Atom Tailbat.png

The M5 Atom is a neat little ESP32 based device. They now do a version with 25 multi-coloured leds on it which is rather nice. You can also get a battery pack with the wonderful name of the “Tailbat” to plug into it and keep it going. You can program the device in Arduino C++, use MicroPython or even the UiFlow block based environment, all over the air from a browser.

Great fun and loads of potential.

ArduinoJson is awesome

arduinojson.png

I’ve probably raved about this before. But, in case you missed it, here it is again. Arduino Json is awesome. Awesome, awesome. I’m using OpenWeatherMap to get some weather data into my ESP32 program. It arrives as a Json formatted string that I need to convert into values to use in my program. With ArduinoJson I can just pop their include file in the same folder as my application and away I go. No messing around installing libraries and wondering if they are the right ones. The decoder stays right with your code.

They’ve made it incredibly to create the code that you need to unpick your data. You just have to take the Json you want to work with and paste it into their web page. Then it comes up with the C++ code for you to drop into your solution to decode and encode the Json stream. Wonderful.

Disabling the ESP32 Brownout detector

I hate it when things fail when they are not supposed to. I’ve now got some code that uses deep sleep on an ESP 32 to drop power consumption to a tiny amount between the readings made by the environmental sensor hardware that Brian has built.

Today I was doing some battery testing and I hit a snag. When the device is running from battery power the deep sleep mode breaks. The device does a power on reset rather than waking from its deep sleep as it is supposed to. Of course, this only happens when it is not connected to a computer, so I can’t use any of my debugging tools to find out what is going on. In the end I had to resort to flashing the LED on the device to indicate the reason for the reboot.

void flashLed(int flashes)
{
    pinMode(LED_BUILTIN, OUTPUT);
    for (int i = 0; i < flashes; i++)
    {
        digitalWrite(LED_BUILTIN, true);
        delay(500);
        digitalWrite(LED_BUILTIN, false);
        delay(500);
    }
}

void flashBootReasonOnBuiltInLed()
{
    esp_reset_reason_t reset_reason = esp_reset_reason();
    flashLed(reset_reason);

    delay(2000);

    esp_sleep_wakeup_cause_t wakeup_reason;
    wakeup_reason = esp_sleep_get_wakeup_cause();

    flashLed(wakeup_reason);
}

This is the code that I ended up writing. On usb power I get a reset reason of 8 flashes (deepsleep) and a wakeup reason of 4 flashes (timer). All good. On battery power I get a reset reason of 9 (brownout).

OK, so what is a brownout? A brownout is when the supply voltage drops a bit low. It’s something you need to worry about because low voltage can cause your processor to do some very strange things. It might get its sums wrong or forget stuff. The ESP32 has hardware that checks the supply voltage and resets the processor if it detects that the power supply is failing. Software in the device can then detect this and go about shutting down safely.

In the case of our sensor the brownout is caused by one specific event. It occurs when the program turns on the power supply that drives the particle sensor. The particle sensor has a fan in it, which takes quite a chunk of current just as it spins up. This sudden load causes the battery voltage to drop for a fraction of a second. The sequence that we have is as follows:

  1. Sensor wakes up from a deep sleep.

  2. Sensor runs the code that turns on the power to the particle sensor.

  3. Supply voltage drops and triggers the brownout sensor which resets the ESP32.

  4. The ESP32 wakes up as from a brownout reset, not a deep sleep.

  5. Sensor runs the code that turns on the power again, but this time it doesn’t trigger the brownout sensor because the power supply has a little bit of residual power in it from the recent (failed) startup.

I’ve fixed the problem by turning off the brownout detector when I turn the power on.

WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //disable brownout detector   

// turn on the power
digitalWrite(powerControlSettings.powerControlOutputPin, HIGH);

// let things power up
delay(500);

WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 1); //enable brownout detector

This is the code that does the deed. The power is controlled by a pin that is lifted high to turn it on. So the program turns of the brownout detector, turns the power on, waits half a second for things to settle and then turns the brownout detector back on again.

I’m not sure whether to be proud of this code or not. But in the end I think I am. If you accuse me of masking a hardware problem with a bit of software then I’m going to plead guilty as charged. I could probably fix the hardware cause by adding a wacking great capacitor over the supply to stop the voltage dropping. But this might lead to other problems. And anyway, if you look at the programs in any of the devices that you use on a daily basis you will find bits of code like this. Little bits of behaviour that are only there because the hardware doesn’t work without them.

Oh, and as a bonus (and so I can find it again if I ever need it) here’s a chunk of C that prints out the reset reason for an ESP32.

void printBootReason()
{
    esp_reset_reason_t reset_reason = esp_reset_reason();

    switch (reset_reason)
    {
    case ESP_RST_UNKNOWN:    Serial.println("Reset reason can not be determined"); break;
    case ESP_RST_POWERON:    Serial.println("Reset due to power-on event"); break;
    case ESP_RST_EXT:        Serial.println("Reset by external pin (not applicable for ESP32)"); break;
    case ESP_RST_SW:         Serial.println("Software reset via esp_restart"); break;
    case ESP_RST_PANIC:      Serial.println("Software reset due to exception/panic"); break;
    case ESP_RST_INT_WDT:    Serial.println("Reset (software or hardware) due to interrupt watchdog"); break;
    case ESP_RST_TASK_WDT:   Serial.println("Reset due to task watchdog"); break;
    case ESP_RST_WDT:        Serial.println("Reset due to other watchdogs"); break;
    case ESP_RST_DEEPSLEEP:  Serial.println("Reset after exiting deep sleep mode"); break;
    case ESP_RST_BROWNOUT:   Serial.println("Brownout reset (software or hardware)"); break;
    case ESP_RST_SDIO:       Serial.println("Reset over SDIO"); break;
    }

    if (reset_reason == ESP_RST_DEEPSLEEP)
    {
        esp_sleep_wakeup_cause_t wakeup_reason = esp_sleep_get_wakeup_cause();

        switch(wakeup_reason)
        { 
            case ESP_SLEEP_WAKEUP_UNDEFINED:    Serial.println("In case of deep sleep: reset was not caused by exit from deep sleep"); break;
            case ESP_SLEEP_WAKEUP_ALL:          Serial.println("Not a wakeup cause: used to disable all wakeup sources with esp_sleep_disable_wakeup_source"); break;
            case ESP_SLEEP_WAKEUP_EXT0:         Serial.println("Wakeup caused by external signal using RTC_IO"); break;
            case ESP_SLEEP_WAKEUP_EXT1:         Serial.println("Wakeup caused by external signal using RTC_CNTL"); break;
            case ESP_SLEEP_WAKEUP_TIMER:        Serial.println("Wakeup caused by timer"); break;
            case ESP_SLEEP_WAKEUP_TOUCHPAD:     Serial.println("Wakeup caused by touchpad"); break;
            case ESP_SLEEP_WAKEUP_ULP:          Serial.println("Wakeup caused by ULP program"); break;
            case ESP_SLEEP_WAKEUP_GPIO:         Serial.println("Wakeup caused by GPIO (light sleep only)"); break;
            case ESP_SLEEP_WAKEUP_UART:         Serial.println("Wakeup caused by UART (light sleep only)"); break;
        }
    }
}

You’re welcome.

LMIC Frame Counter Problem

This is a fairly exotic post, in that I doubt that many readers will be concerned how they can maintain the frame count of LoRa packets sent using the LMIC library when a device goes into deep sleep. However, I’m putting it here so that in six months time, when I probably need to do this again, I can just search the internets and find this article.

A LoRa application maintains a frame count for each connected device. This is a security measure. Any frames that that the application receives with a frame count that is lower than the highest frame count seen so far will be rejected by the application. You can turn off this frame count checking for a device, but it is not advised because it makes things a bit less secure. I’ve turned it off for my devices for now, which meant that the frame count just increases when the device is running and goes back to 0 if the device is reset.

This is kind of OK by me. I’m not overly concerned with security issues at this level. However, when I started using deep sleep mode I found that every frame that was sent had a frame counter of 0, and I really didn’t like that. The problem is caused by the way that the device is completely restarted between each transmission. This restarts the LMIC library each time, setting the frame count to 0 for every frame.

It turns out that a program can reset the LMIC frame count value by accessing a member of the LMIC object. All my program needs to do is create a variable in the RTC memory to hold the current frame count:

RTC_DATA_ATTR u4_t lmicSeqNumber = 0;

The program can then store the frame count in this variable after each LoRa message has been transmitted:

lmicSeqNumber = LMIC.seqnoUp;

Now, during the reset process after a deep sleep I can put the frame count back:

LMIC.seqnoUp = lmicSeqNumber;

I’ve tested this and it seems to work fine. The frame counter is retained when the device goes to sleep. Of course if I turn the device off the frame counter resets back to 0. I could fix this by storing the frame count in EEPROM storage but I’m not too keen on doing this because I’d have to update the value after each LoRa message and might lead to lots of writes which might “wear out” the EEPROM storage.

ESP32 Retaining timing over deep sleep

In the last post we saw how easy it is to make an ESP32 processor sleep for a particular time. However, we also noticed that at the end of the sleep the processor is reset and all the variables in the program are reset. This is difficult if you want to keep track of time in your application. However, it is possible to get around this limitation by storing a time value in memory that is retained during sleep.

RTC_DATA_ATTR unsigned long millisOffset=0;

The statement above declares a variable called millisOffset. The attribute RTC_DATA_ATTR tells the compiler that the variable should be stored in the real time clock data area. This is a small area of storage in the ESP32 processor that is part of the Real Time Clock. This means that the value will be set to zero when the ESP32 first powers up but will retain its value after a deep sleep.

We can use this variable to create an offsetMillis function that gives a millisecond count which is adjusted by the offset:

unsigned long offsetMillis()
{
    return millis() + millisOffset;
}

Our program is going to call this function when it wants to know how much time has elapsed since the application was started. The final part of the solution is to update the offset each time our processor is put to sleep.

void sleepSensor(unsigned long sleepMillis)
{
    esp_sleep_enable_timer_wakeup(sleepMillis * uS_TO_mS_FACTOR);

    millisOffset = offsetMillis() + sleepMillis;

    esp_deep_sleep_start();
}

Now, when our application goes to sleep it records the value that the millisecond timer should have when the device wakes from sleep. This value is retained in non-volatile memory and used to offset time values when the ESP32 restarts.

#include <Arduino.h>

RTC_DATA_ATTR unsigned long millisOffset = 0;

unsigned long offsetMillis()
{
    return millis() + millisOffset;
}

#define uS_TO_mS_FACTOR 1000  /* Conversion factor for micro seconds to miliseconds */

void sleepSensor(unsigned long sleepMillis)
{
    esp_sleep_enable_timer_wakeup(sleepMillis * uS_TO_mS_FACTOR);

    millisOffset = offsetMillis() + sleepMillis;

    esp_deep_sleep_start();
}

void printTime()
{
    unsigned long seconds, sec, min, hrs;

    seconds = offsetMillis() / 1000;

    sec = seconds % 60;
    seconds /= 60;
    min = seconds % 60;    
    seconds /= 60;
    hrs = seconds % 24;

    Serial.printf("%02d:%02d:%02d\n", hrs, min, sec);
}


void setup() {
    Serial.begin(115200);
    printTime();
    sleepSensor(30000);
}

void loop() {
}

The complete program above shows how it all fits together. The program sleeps the device for 30 seconds but the time value is maintained after each reset. Note that the loop function is empty because the program never gets this far. The repeating behaviour of the program is caused by the reset after each sleep.

I was quite surprised just how poor the time keeping is when I ran this program. This is because the timing is not provided by a crystal but by an oscillator which is fitted onto the ESP32 chip. The timing can be out by a second or so over short intervals. Try timing the above program to see what I mean.

The millis function in the Arduino library returns an unsigned long integer value that holds the number of milliseconds since a device was turned on. The program above works by adding an offset to this millis value which reflects how long the device has been put to sleep. Of course, what with data storage in any computer being a finite size, there will come a point where the millis value will not fit in an unsigned long variable and so it will wrap round and go back to 0. This occurs after 4,294,967,295 milliseconds or around 50 days. If your program does any kind of calculation with the timing values you will need to make sure that the wrap round doesn’t cause problems.

ESP32 Deep Sleep Mode

I'm very proud of the picture above. It shows that I'm getting around 0.1ma current consumption on our new environmental sensor when it is in deep sleep mode.

It's very easy to put an ESP 32 into deep sleep mode. This is the code that I'm using:

#define uS_TO_mS_FACTOR 1000  /* Conversion factor for micro seconds to miliseconds */

void sleepSensor(unsigned long sleepMillis)
{
    esp_sleep_enable_timer_wakeup(sleepMillis * uS_TO_mS_FACTOR);

    esp_deep_sleep_start();
}

The sleepSensor function is called with a parameter that gives the number of milliseconds for the sleep duration. It sets up a timer wakeup for that duration and then starts the deep sleep process.

The functions esp_sleep_enable_timer_wakeup and esp_deep_sleep_start are in the ESP32 library that is added to your program when you select a device based on the ESP32 processor. If you want to use them you have to include the Arduiono libraries by putting this statement at the start of your program:

#include <Arduino.h>

When the ESP32 "wakes up" at the end of the sleep the processor is restarted. The timing of the sleep duration is not particularly accurate, certainly not as accurate as the internal clock you get when the ESP 32 is running. You can make the wakeup trigger a button press rather than a timeout if you wish.

When an ESP 32 is restarted after a sleep all the varaibles are re-initialised. However, there is a way that your application can preserve some variable values in Real Time Clock memory. More of this later.

Arduino debugging 2: Using #define to manage debugging output

Last time, in the post here we discovered how the __FILE__ and __LINE__ symbols make it possible for a program to print out a tracing information. This can be very useful if, as is the case with Arduino development, there is no easy way to step through your code. The problem that we now have is that we need to add calls to the trace method, and we will have to remove them later.

show_location(__FILE__, __LINE__);

However, there is another feature of the C compiler that we can use which makes it very easy to add and remove debugging statements. This feature is provided by the C compiler pre-processor. The pre-processor, as the name implies, is the part of the compiler that takes in the source code file and can do some processing on the program text. Commands to the pre-processor are pre-ceded by the # character.

One pre-processor directive (something we use to tell the pre-processor what to do) is #define, which defines a symbol. If we get bored with typing lots of digits of PI (3.141592654) we can use #define to tell the pre-processor to do the hard work for us.

#define PI 3.141592654

Now, whenever the pre-processor sees the symbol PI it will replace it with the digit sequence. So, consider if my program contained the following statement (assuming I have variables called circ and rad declared somewhere):

circ = rad * 2 * PI;

The symbol PI is replaced by the number sequence that it was defined with, so that the circumference of the circle is calculated using the value of PI as defined.

The #define pre-processor directive is really powerful, and should probably come with some warnings. Here are a few:

  • There is nothing to stop a silly programmer re-defining the value of the PI symbol at any point in your program. They could define it as 4 which would cause your program to produce the wrong results, or they could define it as “chicken” which would cause your program to produce some very interesting compilation errors as your program was now trying to use the word “chicken” in arithmetic statements.

  • The defined symbol must be an identifier that “makes sense” in C. There is a convention that #defined symbols are given names that are in all capitals, with words separated by underscore so that they stand out in your code

  • The pre-processor does not replace defined symbols inside strings. In other words the statement:

    Serial.println(“Have some PI”);

    - just prints the message “Have some PI”. However, you can use #define to create symbols that are complete strings:

    #define WELCOME_MESSAGE “Hello”

    I can now write:

    Serial.println(WELCOME_MESSAGE);

  • I regard the #define statement as strong magic. I do all my #defines in one place in each source file and I only use them sparingly.

Anyhoo, back to how to use #define to make our lives easier. We can save typing by replacing the call of the show_location with #define symbol:

#define TRACE show_location(__FILE__, __LINE__)

Now, when I want to produce trace output to show where the program has reached I can just use the TRACE statement:

TRACE ;

The pre-processor will extend this and enter the text of a call of the show_location method.

It gets better. If I want to turn off all the trace statements I can just remove the TRACE symbol from my program by defining it as an empty string:

#define TRACE

This means that the pre-processor will now replace the symbol TRACE with nothing so that the trace statements are no longer present in the source of the code. A program can re-define a symbol as it is compiled, so you can enable and disable the trace behaviour in different parts of the code if you like.

Arduino debugging 1: Getting your Arduino program to tell you where it has got to

Why not have a mini-sequence of posts about Arduino debugging. Why not indeed?

Let’s start with a consideration of why the Arduino is hard to debug. The main reason is that you have no idea what is going in inside your code. If you’re writing a program on a computer with a keyboard and screen you can use any number of tools to find out what broke. There are debugging tools in Visual Studio that let you watch your program statements run one at a time and view (and even change) the values held in program variables.

The Arduino device has nothing like this. If you want to find out where your program has reached you have to put your own debugging statements. You end up writing things like this:

Serial.println(“I got this far”);

This prints out a message to the serial port tell us how far the program got. If you use the Arduino serial monitor you can view this message and know that at least the program has got this far.

This works and can be very useful. Developers call it “code instrumentation”. However, these messages can get confusing if you lots of them. What I’d really like to know is which line in the code that the program has got to. Then I can look this up in the source file. Turns out that this is quite easy. The compiler maintains two symbols called __FILE__ and __LINE__ which hold the name of the source file and the line number at that point in the program source.

So you can print this information out in your code:

Serial.print("Program is in file ");
Serial.print( __FILE__);
Serial.print(" at line " );
Serial.println(__LINE__);

These statements will tell you exactly where you have got to:

Program is in file C:\Users\Rob\Documents\Arduino\blah\blah.ino at line 12

This works well, but we can make it easier to use by putting the behaviour into a function:

void show_location(char * file, int line)
{
Serial.print("Program is in file ");
Serial.print(file);
Serial.print(" at line " );
Serial.println(line);
}

Now we can find out where our program has got to by just calling this function and passing the file and line values:

show_location(__FILE__, __LINE__);

We can now drop calls of show_location in our program to tell us exactly where the program has got to.

Of course, we have a problem in that we have to go and remove all these statements when we have finished debugging, but tomorrow I’ll show you how to make this easy too.

Green is not always my favourite colour

So I’m testing the NeoPixel drivers of an ESP32 board that Brian has designed. Everything works fine, except that the first pixel in the chain of pixels is always green.

I’m OK with green in general. I can’t imagine grass being any other colour. However, I prefer it not to show up when my program is trying to display other colours.

So… I spent quite a long time trying to work out how my code could be doing this. Was a rogue routine setting my first pixel green every now and then? Was my understanding of RGB more GRB? Nope. Everything worked. Which was annoying.

After a while I decided go search for “first neopixel is always green” and it turns out that this is a thing. This is one of these bugs that not only shouldn’t happen, but also I can’t work out how it could happen.

Anyhoo, I switched from the Adafruit neopixel drivers to the FastLed ones and the problem went away. Hardware development is like this. Sometimes you end up reconciling yourself to the fact that you have to do things that you don’t really understand simply to get the thing to work.

Measuring Power Consumption

You may be wondering how I can be so sure about the power consumption of my various devices, what with the amounts being so tiny. Well, I’m using one of these. It is supposed to be used for testing batteries and power supplies, but it gives a rather useful four digits of power consumption information. I’ve made up a special connector from an old USB cable and I’m getting depressingly accurate readings.

In case you are wondering about the device at the top, that is something you can use to apply a hefty load to a USB power source. If you do decide to use it, remember that the resistors (the two green things) will get very hot.

Send for the FireBeetle

If you hit a really nasty problem when you are building something, a good way to attack it is to try to change the problem to one that you think will be easier to solve. You can’t always do this; some customers really want particular deliverables. However, you should never make the mistake of assuming that things that are important to you are also important to the customer. In my earlier days I spent ages getting something to work which was dismissed as “..not really something that we want thanks”. If I’d asked the question “How much to you need this?” earlier I’d have saved myself a lot of effort.

In the case of my environmental sensor I’ve had a chat with the “customer” and discovered that the first set of sensors that are required for testing can be WiFi connected. That means that I can put the Heltec Cube back in its box for a while and switch my attention to something that works over WiFi. I’ve had a play with the DFRobot FireBeetle in the past and so I’ve built a sensor that uses that. The FireBeetle has some nice design tweaks that reduce sleep power consumption. It was fairly simple to port over the software I’ve already written, and I’ve designed a little carrier board that takes the Plantronics PMS5003 sensor along with a FireBeetle and a BME280. And it all works.

The only snag is that I’ve discovered that some device specifications have a lot in common with the current political situation. They are full of things that aren’t true when you test them. The FireBeetle consumes a thrifty 1.5 milliamps when asleep, which is just about viable for our application. Unfortunately the Plantronics PMS5003 sensor, specially selected for its low power features, consumes 4.5ma when I turn off the enable signal. This is much, much more than the amount claimed on the data sheet. Oh well. It turns out that to find out how hard it is to build something you have to build something….

Not all Arduino C++ Compilers are the same

When I was writing the code for the Heltec Cube I came across something that has caused me problems before. When you switch from one Arduino device to another you sometimes change compilers.

The compiler is the program that takes your source code and converts it into the low level instructions that the hardware will follow when the program runs. Different processors have different compilers and, what with C++ being a language with a few grey areas, this can change what happens when your program compiles.

In the case of the Heltec Cube I got errors when using structures in function parameters. In C++ you make a structure like this:

struct InputSwitchSettings {
int controlInputPin;
boolean controlInputPinActiveLow;
};

This is the type that describes the input port that allows the user to switch the device into configuration mode. It sets the physical pin number and also whether that pin is active low. Once I’ve got my structure I can then create a variable of that type:

struct InputSwitchSettings configPin;

I might make a function that wants to use the value of configPn, which means the function must have a parameter which is a reference to the variable:

void showPinSettings (struct InputSwitchSettings * switchRef)
{
Serial.println(switchRef->controlInputPin);
Serial.println(switchRef->contrlInputPinActiveLow);
}

The function showPinSettings will print the settings of the given input switch. Note that in the declaration of the function I’ve told the compiler that InputSwitchSettings is a structure (that’s what the word struct does).

However, some compilers let you leave this struct out (because the compiler can work out this information for itself). The compiler for the ESP32 is relaxed like this. However, the Cube compiler insists on having struct there. It took me a while to work out why code that I’d been using for ages suddenly broke. I guess it serves me right for being lazy.

If you start having problems when you target a different device on the Arduino platform, remember that you are not in Kansas any more where compilers are concerned.

Low Power Cube Problems

After the euphoria of getting my Heltec Cube Sensor transmitting data over the weekend I thought I’d take a look at the low power capabilities of the processor. I want to drop the power consumption of our sensor down to tiny levels so that it can run for a very long time on batteries. The device is billed as having a power consumption that drops to micro-amps when in “sleep” mode, and I was keen to try this out.

It was a rather fraught business. Firstly the power consumption never seems to drop below around 20 milliamps, even when in sleep mode. Secondly (and rather annoyingly) when the device wakes up from sleep some of the internal processes don’t seem to work properly - notably the millisecond timer. This might be down to my code though.

The device is very new and definitely a work in progress. I’m hoping that going forwards I can work out what is going on and properly realise its potential.

Flying Blind with the Heltec Cube

Now that I’ve got the Heltec Cube sending messages to the Things Network, the next thing to do was to get the device reading from my particle sensor. This was tricky because the sensor uses the one serial port on the Cube. That’s the same port as is used for programming and sending diagnostics. So I had to fly my code “blind”. Fortunately the Cube has a single neopixel on the board which I can control from software. And by using that, plus testing the code on another device first, I managed to get it working.

If you watch the two second video above you can see the pixel flash green when the message goes through. I’m very proud of that….

Heltec Cube Wrestling

A while back I ordered a Heltec Cube Cell device. It’s a very low Arduino compatible device with LoRa built in. I thought it might make a fine platform for an environmental sensor.

Today I got around to having a play with it. The LoRa support is built in to the device, which means no need for any LMIC libraries (which I use with the Semtech devices). In theory you can send AT commands to the device to configure LoRa and set things up. I say “in theory” because I couldn’t get it to work.

However, it turns out that you can also configure LoRa by using a file called commissioning.h which is deep in the innards of the CubeCell hardware libraries. By modifying this and using a rather useful howto I found here I got the device happily sending messages to the Things Network. Tomorrow I’m going to investigate low power mode.

Heltec Cube Cell

I quite like Heltec devices. They have a rough and ready quality to their construction, but their designs are well thought out and once you get your head around how they work you can use them as the basis for some neat devices. Four Heltec LoRa devices are presently powering our Air Quality sensors in Hull.

Heltec have just released a new device, the Cube Cell. It has LoRa built in and it has a stupidly low power consumption when in standby. It also has the electronics that allow the direct attachment of a solar panel.

It’s a more constrained platform than the devices based on the ESP32. There is no WiFi or Bluetooth, In fact the processor is built into the LoRa chip. You can find out more here.

In terms of what we want to do with our environmental sensors it looks very interesting. We should be able to port most of the code that we’ve written over to the platform as it is programmed in C++ using the Arduiono IDE. I’ve ordered one, it will be interesting to see what it is like when it arrives.