ESP32 Shield – a Low Power Journey.

This post is about the build of a very low sleep current ESP32 shield. This is my second version of the shield and incorporates improvements discovered the first build. The original shield seemed to be a surprise to the outside World, the power consumption was in the region of 8uA when complete with SSD1306 display, FRAM, TC75 temperature sensor and SX127x LoRa device.

This blog is a description of the build of version 2 of the shield stage by stage and the measured sleep current as more parts are added.

Sleep currents were measured with a multimeter. I built an adapter that goes inline with the battery with connections for the multimeter and a toggle switch. The switch allows the multimeter to be shorted. Thus with the switch shorting the multimeter there are  no burden voltage issues caused by the multimeter. When the ESP32 goes into deep sleep just flip the switch and read the current (in uA) from the multimeter. With the multimeter measuring a a sleep current of 8uA the voltage across the multimeter was only 0.8mV, which is less than would occur if I was using my uCurrent Gold on the 1mV per uA range.

I started the build by adding fitting the  MCP1700 3.3V regulator. The current consumed with nothing connected to the output of the regulator was 1.6uA, a good starting point.

I fitted the ESP32 and the components required to allow the ESP32 to be programmed via a plug in USB to serial adapter, see picture.

The test program was a version the Andreas Spiess used in the video on deep sleep of the ESP32, see the link below;

I changed the program so that at  start the LED would flash and the ESP32 would go into deep sleep for 30 seconds, when the LED stopped flashing I knew to flip the switch shorting the meter and take the current reading. 

ESP32 just module

OK, so lets add a SSD1306 OLED. The Adafruit library has an option that in effect turns the display off; ssd1306_command(SSD1306_DISPLAYOFF);

The test program writes some text to the screen then flashed the LED and goes into a 30 second deep sleep, this is the result, 7.7uA;


The ESP32 does have 8Kbytes of RTC RAM that will survive deep sleep, so this could be useful for keeping track of what a program is doing when going into and out of deep sleep, however this RAM will not survive reset or power down, so I prefer to add a FRAM to projects. These FRAMs are like EEPROM but they have a limited life, maybe 1,000,000 writes. FRAMs have a write endurance many times greater, up to 100,000,000,000,000 writes, so can be treated as RAM. I added a MB85RC16PNF 2Kbyte FRAM and measured the sleep current, it was only 0.1uA extra;


So 7.8uA for an ESP32, SSD1306 display and a 2Kbyte FRAM, looks promising.

Next:  Adding SPI devices.

Power measurements can be useful !

A previous blog post looked at how you can use a cheap (£20) power meter to make accurate measurments on the power output of LoRa radio devices.

I was following the guide and program for a creating a simple TTN node that Andreas Spiess presented in a video;

The program provided uses the LMIC library. The program works fine and the packets show up in the TTN device console.

I wanted to add some of my own diagnostics, so I borrowed some routines from my tracker software thats reads back the LoRa device registers after a packet send and displays the frequency, bandwidth, spreading factor, coding rate and power level the LoRa device was set to.

I had assumed the power level for a TTN node would be set to 14dBm/25mW due to legal restrictions, but the LoRa device was inmdicating it was s set to 16dBm/40mW .

I knew that my ‘calibrated’ 868mhz LoRa module was about 1dBm below the power it should be (see the previous blog post) so I hooked that module and the TTN node software to the power meter. The node program copde was set to send a SF12 packet at start up so there would be enough time for the power meter to display it.

The power meter displayed 15dBm, which is consistent to it actually being programmed for 16dBm as my diagnostic code had suggested. 

Now the appropriate register for power level is RegPaConfig and its set in radio.c of the LMIC library;

static void configPower () {
#ifdef CFG_sx1276_radio
// no boost used for now
s1_t pw = (s1_t)LMIC.txpow;
if(pw >= 17) {
pw = 15;
} else if(pw < 2) {
pw = 2;
// check board type for BOOST pin
writeReg(RegPaConfig, (u1_t)(0x80|(pw&0xf)));
writeReg(RegPaDac, readReg(RegPaDac)|0x4);

#elif CFG_sx1272_radio
// set PA config (2-17 dBm using PA_BOOST)
s1_t pw = (s1_t)LMIC.txpow;
if(pw > 17) {
pw = 17;
} else if(pw < 2) {
pw = 2;
writeReg(RegPaConfig, (u1_t)(0x80|(pw-2)));
#error Missing CFG_sx1272_radio/CFG_sx1276_radio
#endif /* CFG_sx1272_radio */

I have CFG_sx1276_radio selected so the power level is set by this line of code;

writeReg(RegPaConfig, (u1_t)(0x80|(pw&0xf)));

Which writes the pw variable into the power setting register. If pw is 14 (for 14dBm?) the power level of the device is according to the datasheet;

Pout=17-(15-OutputPower). ‘OutputPower’ is what is written to bits 3-0 of RegPaConfig and if its 14 the actual power level selected appears to be;

Pout=17-(15-14) = 16dBm.

This is in line with my diagnostic print, which suggests the power level is being set to 16dBm when 14dbm is expected.

Is it possible the LMIC code should be;

writeReg(RegPaConfig, (u1_t)(0x80|(pw&0xf)-2));

Measuring Power

When your using or experimenting with radio devices there is often the need to measure power levels at radio frequencies. This can be the power output of a small radio module, 10mW at 433Mhz or the signal power seen by a receiver, –100dBm. The receiver power is often referred to as the RSSI.

Measuring RF power under 100mW, with accuracy, is not so easy as a lot of RF power meters of the type used by radio amateurs often have a minimum power input in the 1W region.

There are now available some low cost RF power meters, which have a wide frequency range of up to 8Ghz and measure power in dBm from –5dBm to –55dBm. One such is shown in the picture, it was purchased on eBay for around £20.

If these meters can measure only from –5dBm to –55dBm, and you want to measure the 2dBm output from a LoRa module you need to use an inline attenuator, 20dB or 30dB for example. With the power meter connected through the attenuator and the LoRa module set to transmit a 2dBm carrier,  if the power meter is accurate, then it ought to display –28dBm. See picture for the setup. The LoRa module is on a plug in Mikrobus module, so it can easily be moved between different controller boards.


And here lies a problem, how do you know your (cheap) power meter is accurate?

The simple way is to take the power meter somewhere and get it calibrated, but I decided it would be more useful to calibrate the transmitted power output of a LoRa module and then keep it as a reference. If I know that a specific LoRa module puts out 9.8dBm when set to 10dBm, I can be keept as a long term reference and as long as I take care when using it, always have an antenna connected for instance, then it ought to be stable for quite some time.

In the centre of Cardiff is Eagle labs;

It’s run by Barclays and Legal & General with RadioSpares providing equipment in the electronics lab. Part of this equipment is a spectrum analyser (RSSA PRO 3032X) and it’s got a  proven calibration history. So my plan was to book a session at Eagle labs and use their spectrum analyser  to ‘calibrate’ the power output of a 434Mhz and 868Mhz LoRa module.

I wrote a simple test program that for the 433Mhz and 868mHz modules that transmitted RF carrier for a couple of seconds at 17dBm, 10dBm and 2dBm, I used a 20dB attenuator between the LoRa module and the spectrum analyser.

I got these results for 434mhz;

LoRa device programmed power output       17dBm          10dBm               2dBm

Measured power 434mHz                            -4.6dBm        -11.3dBm          -18.6dbm

LoRa device actual power 434mHz              15.4dBm         8.7dBm            1.4dBm   


For the 868Mhz tests I have included the readings of the power meter with a total of 30dB of attenuator in line, these were the results;

LoRa device programmed power output       17dBm           10dBm               2dBm

Measured power 868mHz                            -4.1dBm         -10.3dBm          -18.5dbm

LoRa device actual power 868mhz               15.9dBm         9.7dBm             1.5dBm    

Power meter indication (30dB att)                -13.1dBm       -19.3dBm           -27.6dBm

Power Meter corrected  +29dBm                   15.9dBm        10.7dBm             2.6dBm  


The key point from the above measurements is that I know that my 868mHz module puts out 15.9dBm when set to 17dBm. 

With a 30dB attenuator in use the power meter indicates –13.1dBm. Now 30dBm (because of the attenuator) below an actual power of 15.9dBm is –14.1dBm, so with the power meter actually reading –13.1dBm, its reading 1dBm lower than it should be. The power meter has an offset you can set, so if I set the offset to 29dBm, then when the actual power is 15.9dBm, the power meter will read 15.9dBm due to the effect of the attenuators. 

I know that all sounds a bit long winded, but the end result is that I have a relatively cheap power meter (£20) that has been adjusted to give accurate results. Of course its entirely possible to calibrate the power meter directly, but since I now have a LoRa module that puts out a known amount of power I can keep it and use it to check this or other power meter or attenuators in the future. And then of course with enough attenuators in line, I can also use the calibrated LoRa modules to check the RSSI readings of various radio receivers.

Phantom Packets – Is this the reason ?

After consulting Semtech, the reason why these phantom packets are seen becomes clear.

This is my current understanding of the issue but testing is continuing …………….

The LoRa receiver will occasionally falsely see noise from the receiver itself as a packet preamble and header. Since its noise from the receiver this explains why the phantom packets are seen even in heavily screened receivers underground.

The header is protected with only a 6 bit CRC so there is a 1:64 chance that this noise will pass a valid header check.

When sending LoRa packets you have the option of using a payload CRC (16 bit) or not. If there is a payload CRC enabled a flag bit is set in the header. When receiving the packet the LoRa receiver tests for this payload CRC enabled flag and carries out the CRC check on the payload.

The trap you can fall into is in assuming that since your application only sends packets with CRC on the payload enabled, on packet receipt you only need to check the RegIrqFlags register PayloadCrcError bit. If its set there is a CRC error, if it clear then you assume your packet is valid. But it may not be.

When one of the phantom packets is received (which are just noise remember) there is a probability that the false header both passes the header CRC check and does not have the payload CRC enabled bit set. If this bit is not set in the header then the LoRa receiver does not carry out a payload CRC check and the CRC error flag is left clear.

There are some LoRa software libraries that on packet receipt appear to check the header for the payload CRC enabled flag and then check the payload PayloadCrcError flag as appropriate. This makes sense if your receiver software is designed to automatically cope with packets that use a payload CRC and those that do not.

So in summary to ensure that you do not read phantom packets as valid, you should consider;

1. Sending packets with the payload CRC enabled.

2. Checking the RegHopChannel register RxPayloadCrcOn bit, if its is clear, dump the packet, it could be a phantom.

3. If the RegHopChannel register RxPayloadCrcOn bit is set, check the RegIrqFlags register for the PayloadCrcError bit, if that is set dump the packet, it could be a phantom.