Contents
De ESP8266-12(e) als monitoring device
De afgelopen periode ben ik druk bezig geweest met verschillende zaken.
Eén van die zaken was het monitoren van de ruimten op ons perceel. Op ons perceel staat uiteraard een huis en een schuur. In de schuur wil ook nog wel eens de lamp blijven branden. Vanuit ons huis hebben we daar namelijk geen zicht op. Dus ook een plek om te monitoren.
Door gebruik te maken van de ESP8266 is het mogelijk om gegevens over en weer te verzenden via wifi van en naar de sensor-pc. Deze pc verzamelt alle gegevens in het hele huis en gaat hier eventueel mee aan de slag.
Wat moet het monitoring device kunnen
Het monitoring device moet het volgende kunnen
- temperatuur meten
- licht meten (lamp aan of uit)
- deze gegevens verzenden via wifi
- Eventueel ontvangst van opdrachten om lampen uit te doen e.d.
- aan en uit kunnen gaan om zo batterij te besparen
Wat moet de sensor-pc kunnen
- data verzamelen van het hele huis
- data opslaan in database
- data kunnen oproepen in verschillende rapporten
- huidige situatie tonen op een scherm
Al met al dus heel wat om te doen, maar nu terug naar de esp8266 als monitoring device
ESP8266-12e Pinout en omschrijving
De ESP8266 -12 is een device wat veel verschillende poorten heeft.
Het is dus zo te zien een erg veelzijdig apparaatje. Hierdoor zijn er ook verschillende mogelijkheden.
Er zijn 11 GPIO-poorten waarvan de RX en de TX ook nog kunnen worden ingezet als GPIO poort. Dus in totaal 13 beschikbare poorten voor digitale in-/output.
Er is 1 ADC poort aanwezig voor het meten van analoge signalen zoals voltage enzovoorts.
Door middel van de SCLK, MOSI, MISO en CS is deze ook te gebruiken voor SPI communicatie wat hier te ver voert om uit te leggen. Daar hebben ze al mooie documenten voor gemaakt.
In mijn project maak ik voor de meetingen gebruik van de volgende poorten:
- GPIO5
- GPIO4
- GPIO0
- GPIO2
- GPIO15
- GND
- RST
- CP_PD
- GPIO16
- GPIO12
- VCC
Al met al, dus een hele boel poorten die worden gebruikt.
Hier een beschrijving waarvoor ze dan worden gebruikt
Voor het normale gebruik van de ESP8266-12E heb ik de volgende connecties al in gebruik. Dit zal waarschijnlijk wel iets minder kunnen zijn, maar het werkt naar behoren:
Hiernaast heb ik nog de volgende aansluitingen er bij zitten
- GPIO5 heeft een koppeling naar de DS18B20 pen 1. Dit is namelijk de aansluiting die op een bepaald moment hoog wordt gezet om de DS18B20 uit te lezen.
- GPIO12 meetpin voor de LDR
- GPIO16 gekoppeld met de RST
In het volgende schema is te zien hoe de aansluitingen zitten
Ik heb een beetje met kleuren gewerkt om het wat duidelijker te maken voor mezelf.
deep sleep mode ESP8266
Tot mijn verbazing heeft de ESP8266 een deep sleep mode. Dit is erg handig als je de ESP8266 op batterijen (3xAA) wilt laten lopen. Dan kan er niet teveel stroom worden gebruikt.
Nu wil het voordeel dat er geen LEDje brand op de print van de ESP8266, waardoor er daarmee ook geen stroom wordt gebruikt 🙂
Om de deep sleep mode te activeren dienen de volgende regels te worden toegevoegd in de sketch (ik maak gebruik van de Arduino IDE):
onder de #includes :
extern "C" { #include "user_interface.h" //for sleep }
In de loop heb ik de volgende regels opgenomen
Serial.println(); Serial.println("going into sleep...."); delay(1000); system_deep_sleep_set_option(0); system_deep_sleep(5*60*1000000); //5 min
Om dit werkend te krijgen dient er ook nog een aanpassing te gebeuren op de print.
GPIO16 moet worden doorverbonden met de RST. En dat is alles
Om er voor te zorgen, dat er ook geen constante stroom staat op de DS18B20 en de LDR, maak ik gebruik van GPIO5 die hoog of laag wordt gezet afhankelijk van het wel of niet meten van de gegevens. Hierdoor bespaar ik weer wat mA’s 🙂
system_deep_sleep(5*60*1000000); //5 min
De regel hierboven bepaald hoelang de ESP in de deep_sleep mode zit. In dit geval 5 minuten
ESP8266 Power Modes
ESP8266 power settings covered here.
The sparsity of information on the subject spurred me to document it here.
There are two basic categories of settings governing power consumption on the ESP8266.
Preface; power consumption seems to vary between test boards, probably due to biasing resistors, used to put the chip in operational mode. For example; board ‘A’ uses 2K resistors, while board ‘B’ uses 10K. For most of the testing 10K was used.
While the Espressif Sleep Mode Function Description claims lower current values, actual real life values, are show here.
In general the chip uses the most power with WiFi enabled. Typical power usage for server-mode ranges between 60mA and 200mA while client-mode demands 70mA – 260mA. Note: spikes can be seen on the scope reaching 300mA, especially while in client mode.
Espressif Claims the following;
Mode | Min | Typical | Max | Units |
---|---|---|---|---|
802.11b, CCK 1Mbps, POUT=+19.5dBm | 215 | mA | ||
802.11b, CCK 11Mbps, POUT=+18.5dBm | 197 | mA | ||
802.11g, OFDM 54Mbps, POUT=+16dBm | 145 | mA | ||
802.11n, MCS7, POUT =+14dBm | 135 | mA | ||
802.11b, packet size of 1024 bytes, -80dBm | 60 | mA | ||
802.11b, packet size of 1024 bytes, -70dBm | 60 | mA | ||
802.11b, packet size of 1024 bytes, -65dBm | 62 | mA | ||
Standby | 0.9 | uA | ||
Deep sleep | 10 | mA | ||
Saving mode DTIM 1 | 1.2 | mA | ||
Saving mode DTIM 3 | 0.86 | mA | ||
Shutdown | 0.5 | uA |
The above power consumption levels often vary based upon distance between modules as well as interference such as walls. These variances will force the client to increase or decrease power to find and maintain connectivity with the server.
To Disabling or Enabling WiFi features the routine, system_deep_sleep, must be called. But before calling this routine the wake mode must be set by calling system_deep_sleep_set_option with the required parameter;
- RF_DEFAULT = 0, //RF_CAL depends on init data from byte 108
- RF_CAL = 1, //RF_CAL enabled causes large current drain after wake 170mA
- RF_NO_CAL = 2, //RF_CAL disabled, small current drain after wake 75mA
- RF_DISABLED = 4 //RF disabled, smallest current drain after wake 15mA
Note: another function, void system_phy_set_rfoption( uint8 option ), seems similar to system_deep_sleep but can only be called from specific location in flash memory. I was unable to test this routine since my current testing framework does not allow writing code for that space. I’m guessing this routine is for running code specifically from that location.
Example 1) Put the chip into lowest power mode
system_deep_sleep_set_option( 4 ); //Will disable WiFi mode on wake.
system_deep_sleep( 1 ); //Go to sleep and wake in 1 uS.
It should be noted, that the ADC feature does not work with RF_DISABLED mode. Reason: unknown at this time?
See the Espressif API documentation for further details.
The esp8266/Arduino communities API provides a simpler calling mechanism where only a single call needs to be made: void ESP.deepSleep(uint32_t time_us, RFMode mode = RF_DEFAULT)
Example 2) the Example 1 equivalent in esp8266/Arduino
ESP.deepSleep(1, 4); // Disable WiFi mode on wake and sleep 1uS before wake.
Usage pattern notes: I found that both the Espressif system_deep_sleep and ESP.deepSleep seem to take time before the chip is actually put to sleep so it is necessary to use a delay(100) immediately after them, so no further code will run, or some other coding strategy.
Note: if the first parameter, for sleep time, contains 0, the chip will not use the WTD/RTC for waking and will rely upon an outside force for waking the chip by pulsing the reset pin low. Minimum pulse time: unknown at this time? However, for any value greater than 0 the WTC/RTC will pulse GPIO_16. So if GPIO_16 is connected to the reset pin it will wake the chip.
Other power consumption settings
When communicating, the ESP can control how much antenna gain is used and has configuration settings, which control the maximum power levels used to do so. Unlike the RF calibration settings and Modem status levels used before, these settings actually dictate the maximum power used when trying to connect or transmitting data.
I don’t have time to test these feature, for now, so I’ve simple provided the prototype signature and its available options.
void system_phy_set_max_tpw(uint8 max_tpw)
options as follow: Unable to find enumerations for these settings so the L-Values follow;
- 0-82: Sets the maximum value of RF TX power, units: 0.25dBm
Other useful routines
struct rst_info* system_get_rst_info(void)
Gets startup information which may help in determining what the circumstances were leading to the boot up.
Structure and enumeration used;
enum rst_reason {
- REANSON_DEFAULT_RST = 0, //Normal startup by power
- REANSON_WDT_RST = 1, //Hardware watch dog reset
- REANSON_EXCEPTION_RST = 2, //Software watch dog reset, GPIO status won’t change
- REANSON_SOFT_WDT_RST = 3, // Software restart, system restart, GPIO status won’t change
- REANSON_SOFT_RESTART = 4, //
- REANSON_DEEP_SLEEP_AWAKE = 5, Wake up from deep-sleep
- REANSON_EXT_SYS_RST = 6 //External system reset }
struct rst_info{
- uint32 reason; //enum rst_reason
- uint32 exccause;
- uint32 epc1; // Address that error occurred
- uint32 epc2; //
- uint32 epc3; //
- uint32 excvaddr;
- //uint32 depc; //
}
Note: Booting due to a pin interrupt i.e. the RTC on GPIO16 the reason will be 1:Hardware watch dog reset.
Booting by removing toggling power will result in 6:External system reset.
The boot immediately after flashing seems to result in 6.
TODO: Below are a few interesting features that may be helpful.
system_get_time
system_get_rtc_time
system_rtc_clock_cali_procsystem_rtc_mem_write
system_rtc_mem_read
os_install_putc1((void *)uart1_write_char) in uart_init will set os_printf to be output from UART 1, otherwise, os_printf default output from UART 0.
wifi_get_opmode: get WiFi current operating mode
1: station
2: soft-ap
3: station + soft
wifi_get_opmode_default: get WiFi operating mode that’s saved in flash
1: station
2: soft-ap
3: station + soft-ap
wifi_station_get_connect_status: Get connection status of WiFi station to AP
STATION_IDLE = 0
STATION_CONNECTING = 1
STATION_WRONG_PASSWORD = 2
STATION_NO_AP_FOUND = 3
STATION_CONNECT_FAIL = 4
STATION_GOT_IP = 5
Difference between reset and restart, reset results in the RTC/Clock time being reset to 0, while restart does not.
Zoals te zien is, is er nog veel in te stellen, en waarschijnlijk kan ik ook nog een andere communicatie methode kiezen om nog meer mA’s te besparen, maar dat gaat me even te ver. Ik ben al lang blij dat het allemaal werkt.
Hieronder de ino die ik heb geschreven. Het is af en toe bagger, maar het werkt naar behoren
#include <ESP8266WiFi.h> #include <WiFiClient.h> #include <ESP8266WebServer.h> #include <ESP8266mDNS.h> #include <OneWire.h> #include <DallasTemperature.h> extern "C" { #include "user_interface.h" //for sleep } const char* ssid = "Ziggo8BF1A8"; const char* password = "9BW9FGFQE4JY"; MDNSResponder mdns; ESP8266WebServer server(80); const char* host = "192.168.2.14"; #define ONE_WIRE_BUS 4 // on pin 4 (a 4.7K resistor is necessary) OneWire ds(4); const int ldr = 12; //init ldr int ProviderPin=5; //to provide voltage float RoomTemp; //temperature from sensor(0) int LdrValue; //high or low ldr String WebString = ""; int kamer=1; // Setup a oneWire instance to communicate with any OneWire devices // (not just Maxim/Dallas temperature ICs) OneWire oneWire(ONE_WIRE_BUS); // Pass our oneWire reference to Dallas Temperature. DallasTemperature sensors(&oneWire); void handleRoot() { LdrValue = digitalRead(ldr); WebString = "Temperatuur is: " + String((int)RoomTemp) + "*C"; WebString += "\n\rVerlichting is: "; WebString += digitalRead(ldr); server.send(200, "text/plain", WebString); } void handleNotFound() { String message = "File Not Found\n\n"; message += "URI: "; message += server.uri(); message += "\nMethod: "; message += (server.method() == HTTP_GET) ? "GET" : "POST"; message += "\nArguments: "; message += server.args(); message += "\n"; for (uint8_t i = 0; i < server.args(); i++) { message += " " + server.argName(i) + ": " + server.arg(i) + "\n"; } server.send(404, "text/plain", message); } void setup(void) { pinMode(ldr, INPUT); // prepare GPIO5 for providing power to ldr and ds18b20 pinMode(ProviderPin, OUTPUT); digitalWrite(ProviderPin, LOW); Serial.begin(115200); WiFi.begin(ssid, password); Serial.println(""); digitalWrite(ProviderPin, HIGH); //provide power to ldr and ds18b20 delay (1000); ds.reset(); sensors.begin(); // Wait for connection while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(""); Serial.print("Connected to "); Serial.println(ssid); Serial.print("IP address: "); Serial.println(WiFi.localIP()); if (mdns.begin("esp8266", WiFi.localIP())) { Serial.println("MDNS responder started"); } server.on("/", handleRoot); server.on("/inline", []() { server.send(200, "text/plain", "this works as well"); }); server.onNotFound(handleNotFound); server.begin(); Serial.println("HTTP server started"); // Start up the library } void CheckTemp() { // call sensors.requestTemperatures() to issue a global temperature // request to all devices on the bus sensors.requestTemperatures(); // Send the command to get temperatures Serial.print("Temperature for Device 1 is: "); Serial.print(sensors.getTempCByIndex(0)); RoomTemp = sensors.getTempCByIndex(0); // Why "byIndex"? // You can have more than one IC on the same bus. // 0 refers to the first IC on the wire LdrValue = digitalRead(ldr); Serial.print("| LDR is: "); Serial.println(LdrValue); delay (1000); digitalWrite(ProviderPin, LOW); //disconnect power from ldr and ds18b20 } void WriteDatabase() { Serial.println("Wake up...."); Serial.println(); Serial.print("connecting to "); Serial.println(host); // Use WiFiClient class to create TCP connections WiFiClient client; const int httpPort = 80; if (!client.connect(host, httpPort)) { Serial.println("connection failed"); return; } client.print("GET /rooms.php?roomnr="); client.print(kamer); client.print("&roomtemp="); client.print(RoomTemp); client.print("&roomlight="); client.print(LdrValue); client.println(" HTTP/1.1"); client.println("Host: 192.168.2.14"); client.println("User-Agent: arduino-ethernet"); client.println("Connection: close"); client.println(); } void loop(void) { delay(1000); CheckTemp(); server.handleClient(); WriteDatabase(); Serial.println(); Serial.println("going into sleep...."); delay(1000); system_deep_sleep_set_option(0); system_deep_sleep(5*60*1000000); //5 min }