-
Notifications
You must be signed in to change notification settings - Fork 26
Home
Welcome to the low_power_voltage_measurement wiki!
In this tutorial, we describe the procedure for designing ultra-low power sensors with ESP8266. There are in general 2 types of sensors:
- The system is woken up by events which power on ESP8266. Depending on the conditions, the system may send an alert to the server.
- The system is woken up periodically, to measure some parameters. The system may store some parameters in the RTC memory. The system periodically sends the data to the server.
In this article, we focus on the second type of solution, where we program ESP8266 to wake up every 1 minute, measures its own supply voltage and sends measurement data to the server at every 20th iterations.
First logon to http://IOT.ESPRESSIF.CN. This is Espressif’s own data server for internal testing, and open to public as well. It’s free; and there is no guarantee with regards to this service provided. We can find some information about this service at https://IOT.ESPRESSIF.CN/#/help-en/.
If you have already setup an account, and wish to configure the language settings, please login and go to settings and configure your profile. If you have not, please create an account at the above webpage.
Next we have to create a device instance as shown in Figure 1. If we need to create many devices, e.g. in production, we can use batch create.
After the device is created, we have to create the datastream. In our example, we create a 2-dimensional datastream for our device, because the device sends a set of 20 measurements to the server each time, with the same timestamp. By adding another dimension, which is an index number, we can infer the actual timestamp for each individual measurement. Please take note of the datastream field, because we will need it in the source codes to send the data to this.
There are 2 device keys that we need to care about, of which the master device key is needed for the device:
- Device Key (Master): Used by the device to identify itself to the server. It is to be stored in the device’s flash.
- Device Key (Owner): Used by the mobile device to identify itself as the owner of this device. This owner has root access and can request the server to generate other device keys with lower privileges that can be shared to other users.
For our application, we have a use the master device key to read the data from the server, even though we really shouldn't. (see script/get_sensor_data.py.)
First in order to compile programs for ESP8266, we need to build the XTENSA CROSSTOOL-NG toolchain. The instructions for doing so can be found at:
- Mac OS: http://tuanpm.net/esp8266-development-kit-on-mac-os-yosemite-and-eclipse-ide (I have tried this, and it works.)
- Linux: https://github.com/esp8266/esp8266-wiki/wiki/Toolchain (I have not tried the instructions here, but it should work. Let me know if you have issues.)
Next, we download the source codes for our particular application from this repository.
The sleep timing and polling rate can be adjusted in the LOW_POWER_SENSOR_DEMO_DIR/app/include/user_config.h file.
#if HUMITURE_SUB_DEVICE
#define SENSOR_DEEP_SLEEP_TIME 60000000 // Count in microseconds, i.e. 1 min.
#define SENSOR_CONNECT_TIME 10000000
The amount of data to be stored is found in app/include/driver/sensor_lp.h.
#define SENSOR_DATA_NUM 20 // No of iterations to store in RTC memory
#define SENSOR_DATA_MEM_ADDR 120
#define INIT_MAGIC 0x7E7E55AA
#define RTC_CNT_ADDR 120
#define DSLEEP_TIME SENSOR_DEEP_SLEEP_TIME
#define SENSOR_DATA_SET_LEN 50 //max LEN of data, e.g. {“x”:1,”y”:351}
To simplify the application, the program flow is contained within a single function; to change the sensor device or the actions to perform, we have to modify the data_func() function in
__LOW_POWER_SENSOR_DEMO_DIR__/app/driver/sensor_lp.c.
We list this function here with comments:
#include "ets_sys.h"
#include "osapi.h"
#include "c_types.h"
#include "user_interface.h"
#include "user_devicefind.h"
#include "user_webserver.h"
#include "user_esp_platform.h"
#include "driver/sensor_lp.h"
SENSOR_DATA_RTC_MEM sensor_data ;
SENSOR_DATA_RTC_MEM* ICACHE_FLASH_ATTR get_sensor_data() { return &sensor_data; }
void ICACHE_FLASH_ATTR data_func() {
// Read out the sensor data structure from RTC memory
system_rtc_mem_read( SENSOR_DATA_MEM_ADDR, &sensor_data, sizeof(SENSOR_DATA_RTC_MEM) );
// When the system powers on for the first time, the data in the rtc memory is random.
struct esp_platform_saved_param esp_param_t;
user_esp_platform_load_param(&esp_param_t); // Stored in flash
// Load user params to check if the device was successfully registered to the server
// If it wasn't, it usually returns 255 (from the flash.)
if(sensor_data.init_flg!=INIT_MAGIC || sensor_data.cnt>SENSOR_DATA_NUM ) {
// This case runs when we first power on or when it time to flush the RTC memory of old data.
if(esp_param_t.activeflag!=1) { // If registered & activated
user_esp_platform_init(); // Router is not configured. Setup softAP. Wait for config.
#ifdef SERVER_SSL_ENABLE
user_webserver_init(SERVER_SSL_PORT);
#else
user_webserver_init(SERVER_PORT);
#endif
} else { // was connected! So we set init magic to exit the setup loop
sensor_data.init_flg = INIT_MAGIC;
sensor_data.cnt = 0;
system_rtc_mem_write(SENSOR_DATA_MEM_ADDR, &sensor_data, sizeof(SENSOR_DATA_RTC_MEM));
__SET__DEEP_SLEEP__WAKEUP_NO_RF__;
system_deep_sleep(100000);
}
} else { // This is where the measurements are made
uint16 vdd_val = 0;
if(sensor_data.cnt<0 || sensor_data.cnt>=SENSOR_DATA_NUM)
sensor_data.cnt=0; // range check and resets counter if needed
/* Reads power supply voltage, byte 107 of init_data.bin should be set to 0xFF.
* Replace with your own code.*/
sensor_data.data[sensor_data.cnt++] = (uint16)(phy_get_vdd33());
system_rtc_mem_write( SENSOR_DATA_MEM_ADDR, &sensor_data, sizeof(SENSOR_DATA_RTC_MEM) );
// Setup next sleep cycle
if(sensor_data.cnt==SENSOR_DATA_NUM-1) { __SET__DEEP_SLEEP__WAKEUP_NORMAL__; }
else { __SET__DEEP_SLEEP__WAKEUP_NO_RF__; }
// Uploads or go to sleep
if(sensor_data.cnt == SENSOR_DATA_NUM) { user_esp_platform_init(); }
else { system_deep_sleep(SENSOR_DEEP_SLEEP_TIME); }
}
}
To change the format of the data to upload, edit the LOW_POWER_SENSOR_DEMO_DIR/app/include/driver/sensor_lp.h file. For instance, for our particular application, we used:
#define SENSOR_DATA_STREAM "supply-voltage"
where "supply-voltage", matches the datastream field that we used when we created the device.
The datastream supply-voltage is a 2-dimensional variable, because we are uploading an array of voltages, stored in the RTC memory all at the same time. Each time we upload a set of values, they have the same timestamp; we have to infer their true timestamp from their indices.
We can configure the chip to perform other functions upon waking up, e.g. to turn on some external regulators, or sensors. We can configure the chip to perform other functions before going to sleep, e.g. to turn off some external regulators or sensors to save power. This will be the subject of our next project.
To compile the source code, open a terminal, and go to the directory:
cd __LOW_POWER_SENSOR_DEMO_DIR__/app
Next, make sure that the compile script has executable permission and run it:
chmod u+rx gen_misc.sh
./gen_misc.sh
We will be prompted with the following options:
STEP 1: choose boot version (0=boot_v1.1, 1=boot_v1.2+, 2=none)
STEP 2: choose bin generate (0=eagle.flash.bin+eagle.irom0text.bin, 1=user1.bin, 2=user2.bin)
STEP 3: choose SPI speed (0=20MHz, 1=26.7MHz, 2=40MHz, 3=80MHz)
STEP 4: choose SPI mode (0=QIO, 1=QOUT, 2=DIO, 3=DOUT)
STEP 5: choose SPI size (0=256KB, 1=512KB, 2=1024KB, 3=2048KB, 4=4096KB)
The script compiles several versions of binary. We note the following:
- eagle.flash.bin + eagle.irom0text.bin
- The earliest version of binaries.
- Only support 512kB flash map.
- If our flash device is >512kB, the script with still work if we select 512kB.
- Does not support OTA upgrade.
- boot_v1.1.bin + user1.old.bin/user2.old.bin
- Add a bootstrap to load programs from user1.bin or user2.bin.
- Supports over the air (OTA) upgrades.
- Supports different flash size.
- boot_v1.2.bin + user1.new.bin/user2.new.bin
- Functionally the same as boot_v1.1 but more optimized.
Based on our characterization of many flash devices, we recommend that setting 40MHz for the flash interface speed. By using the default values for the compile script, it should usually work. Thereafter, the system compiles the codes and generates the bin files and reports accordingly:
No boot needed.
Generate eagle.flash.bin and eagle.irom0text.bin successfully in folder bin.
eagle.flash.bin-------->0x00000
eagle.irom0text.bin---->0x40000
file name : ../bin/eagle.flash.bin
We can now write the program into the Flash device of the ESP8266 module. For my Mac OS X Yosemite setup, I used the following command:
cd __LOW_POWER_SENSOR_DEMO_DIR__/bin
./write_flash.sh
The write_flash.sh command now runs ../tools/add_disable_rf_cmd.py automatically. So you don't have to do this anymore. However, you have to check the frequency of your crystal on the ESP8266 board. If it is 40MHz, you have to edit the write_flash.sh command to use "esp_init_data_vdd33.bin_40M" for 0x7C000. If the crystal is 26MHz, use "esp_init_data_vd33.bin" instead. They only differ at byte 48, which helps the system to distinguish between the 2.
There are many versions of ESPTOOL available online; my favorite version is the original version by TheMadInventor: https://github.com/themadinventor/esptool
write_flash.sh uses ESPTOOL.py. Please download the file and put in the tools directory.
Please modify the contents of write_flash.sh as per your requirement. /dev/tty.usbserial-AL00D9W2 is the name that my computer assigns to the USB dongle. We have to generate our own device key as well, and replace master_device_key.txt with our version, and run gen_key_bin.py, to generate our key.bin file.
When the system boots up, we can configure ESP8266 to connect to the router via LOW_POWER_SENSOR_DEMO_DIR/scripts/setup_AP_information.py. To do so, we edit the script to fill in the router SSID and password.
In order to enable some undocumented low power features of ESP8266, we have added the following codes in the LOW_POWER_SENSOR_DEMO_DIR/app/Makefile, that modifies part of the flash content.
@python ../tools/add_disab1e_rf_cmd.py ../bin/upgrade/$(BIN_HOME).bin
This magic sauce command, lowers the power consumption of the chip by 30% when it wakes up to perform sensor measurement.
If all goes well, the sensor will start reading our supply voltage once every minute, and after 20 readings, it will send the data to the server. We can read the data by using LOW_POWER_SENSOR_DEMO_DIR/scripts/plot_sensor_voltage.py and LOW_POWER_SENSOR_DEMO_DIR/scripts/get_sensor_voltage.py.
These scripts automatically checks for the device key from the bin directory -- they look for the key.bin file.
Check if you have connected the RESET pin to GPIO16. GPIO16 is used to wake the system. For this application, if we have not done so, it will sleep forever. However, when writing to flash, I think we should not connect the RESET pin to GPIO16.
If you are not getting any intelligible data via UART when debugging, check that you have used the correct version of esp_init_data_vdd33.bin in your write_flash.sh script. Check the section "Writing into Flash".
#What Has Changed? So what has changed? We have lowered the standby current, by simply turning off the flash device when we are asleep. The codes are cleaner now. Hopefully, they are comprehensible and clear. The data uploading is embedded within the user_esp_platform.c file. It's rather complicated and is done via callbacks. I hope to clean part up as well. Soon.