Code Monkey home page Code Monkey logo

esp-now-sensor-system-with-wifi's Introduction

ESP-Now Sensor system with WiFi

Low power battery operated Sensors based on ESP8266 or ESP32, sending sensor data wireless using ESP-Now to an ESP8266/-32 Gateway connected to the Internet using WiFi (at the same time).

Why this?

  • Based on cheap standard ESP8266 (and ESP32) boards programmed in Arduino IDE
  • 1+ year operation on 1200mAh or 18650 battery
  • For-ever operation with a small solar panel (80x55mm)
  • ESP-Now receiver (gateway) connected to WiFi/Internet - at same time
  • Longer range than WiFi, no need for external antennas

Typical usage

  • Battery and/or solar powered sensors and weather stations connected to home automations, to Blynk apps, to Thingspeak, MQTT, etc.
  • Longer/safer reach and communication vs WiFi.

Code available

  • Code template/demonstrator for ESP8266 and ESP32 Sensor node (sending on ESP-NOW)
  • Code template/demonstrator for ESP8266 and ESP32 Gateway (receiving on ESP-NOW and providing a simple web server over WiFi),

Life time

Typical life time performance of a Sensor with ESP8266 LOLIN D1 Mini Pro V2.0.0 and temp sensor:

Sensor description

While HW is standard ESP boards, the sensor code is optimized for lowest power consumption possible. Sensors operate in Deep Sleep mode in 5 minute cycles. The WiFi module is active in the region of 60 ms per cycle.

The ESP-Now messages are "unnecessary" long and not just a few bytes as would be enough to send sensor data. However, I have standardized on a more general and longer format as the extra time to transmit a larger payload has quite a low impact on total energy consumption and battery lifetime.

The energy consumption can be divided into 3 classes:

  1. Deep Sleep period: 300 secs, 50-200uA.

    Note. Some boards draw much more than this in deep sleep.

  2. Wake time, reading sensors and battery levels etc with WiFi OFF: 100-500ms, 15-30mA.

    Fast sensors are preferred.

  3. Wake time, sending data on ESP-Now with WiFi ON: 60ms, 70-150mA.

    Fast, thanks to ESP-Now.

From the above typical values, it is realized the major energy consumption is in the Deep Sleep mode why it is important to use ESP boards with low deep sleep currents. The LOLIN D1 Mini Pro V2.0.0 is the standard ESP board with the longest battery lifetime I have tested.

Best sensor lifetime performance has been observed using a LOLIN SHT30 Temp and Humid sensor, with a modified driver library. The DS18B20 temp sensor comes very close by and sometimes even better, which probably is due to the chosen pull up resistor value. The BME280, draws a bit more current with a noticeable shorter lifetime. DHT11, DHT21 and DHT22 all consume more power, either by higher standby current or by longer reading times, resulting in shorter battery lifetime. They also produce much more false readings and easily gets over-saturated in humid outdoors conditions.

Note 1. I have a FireBeetle ESP8266 IoT Microcontroller board under test which looks very promising with potentially even longer battery performance.

Note 2. It would be further possible to reduce the deep sleep current by using ESP modules with no USB and LDO chips, or by disabling built-in LDO's replacing with more efficient external ones, etc. However, i haven't tried this very much but instead focused on an optimal SW solution using standard boards.

Gateway summary

A gateway is a slave in ESP-Now terminology and receives sensor data sent to its MAC address. It can be implemented on ESP8266 or ESP32 with a minor code difference in the ESP-Now and WiFi API's.

WiFi and ESP-Now can be activated and operating at the same time. This allows one MCU to send the sensor data further using WiFi. This seems not to be commonly known. And is a bit tricky to get working. See source code how to.

System summary

A system comprise of up to 20 low power sensors and 1 gateway. Each sensor sends a unicast message every 5 minutes to the MAC-adress of the gateway. The gateway receives the messages and can process and send further over WiFi to any local or Internet service. The gateway can receive messages from sensors (on ESP-Now) and communicate over WiFi at the same time without any missed communications. Gateway connected to Blynk to display and monitor sensors is a good example which works very well.

A restriction is that the gateway must use the same WiFi channel on ESP-Now and WiFi. And due to some limitations in the ESP WiFi implementation, this only works on channel 1. (I have read articles saying it should be possible on also other channels, but I have only succeeded on channel 1.) This means the WiFi router must be set to channel 1 on the 2.4GHz band. It cannot be operated on "automatic channel".

There are some tricks involved during WiFi inits to get this working properly. See source code. (Maybe there are other tricks required to operate on any other channel than 1.)

It is possible, and preferred, to use a software defined MAC-address in the gateway instead of the default hardware MAC address. This allows for a replacement of the gateway hardware without any problems.

It is possible to run several systems in paralell. Just use a unique MAC address for each system. I am running 3 separate systems without any problems.

It is said ESP-Now has "3 times longer access range" than WiFi. I've never done any measurements, but I have noted long enough access range for my sensors in my installations.

Note. ESP-Now specification defines max 20 "peers" which is what i understand is "units to send to". A sensor in my system, sends to one (1) slave/gateway. However, there is no specification, what I know about, stating from how many controllers/sensors a slave/gateway can receive from. Maybe this number is larger than 20, and therefore would be possible to use more than 20 sensors per gateway. I have not tested this.

esp-now-sensor-system-with-wifi's People

Contributors

jonasbystrom avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

esp-now-sensor-system-with-wifi's Issues

cannot convert 'esp_interface_t' to 'wifi_interface_t'

Your Code that I edit for ESP32 only and I try Verify ..
and it will error at this line
"esp_wifi_set_mac(ESP_IF_WIFI_AP, &GatewayMac[0]);"

`
#define SKETCH_NAME "ESP-NOW GATEWAY"
#define SKETCH_VERSION "2022-01-01"
#define SKETCH_ABOUT "ESP-Now Gateway template code and demonstrator of simultaneous ESP-Now and WiFi."

/*

  • This is a template code for an ESP-Now Gateway for ESP8266 and ESP32. It assumes Arduino IDE.
  • Note. ESP8266 and ESP32 use different WiFi implementations why you must set the appropriate
  • #define USE_ESP8266 or USE_ESP32. Please remember to also set the appropriate BOARD in the
  • ARDUINO IDE to find the correct include files.
  • The Gateway receives messages on the Espressif proprietary protocol ESP-Now. It also provides a simple WEB server
  • displaying the received temperature data from all sensors. This is to demonstrate a single ESP8266 is capable of
  • communicating on ESP-Now and 2.4 GHz Wifi simoultaneously. One restriction apply, this only works on WiFi channel 1
  • and you must therefore set the router to "fixed" channel 1.
  • In order to perform the demonstration you will need:
    • 1x ESP8266 or ESP32 programmed with this sketch, acting as Gateway (to the WiFi/internet), and
    • 1-20x ESP8266 or ESP32 programmed with the espnow_sensor sketch and equipped with a temperature sensor
  • (or anything else you connect and modify the code for).
  • This is a simple demo sketch but fully working. It demostrates simultaneous WiFi and ESP-Now on ESP8266 and ESP32.
  • You can add other Internet services like MQTT, Blynk, ThingSpeak etc. (I have been running this for 1.5+ years with
  • those services.)
  • AUTHOR:
  • Jonas Byström, https://github.com/jonasbystrom
  • CREDITS:
  • Code related to ESP-Now is partly based on code from:
  • With important info from:
  • And great info from:

#include <esp_now.h>
#include <esp_wifi.h>
#include <WiFi.h>
#include <WiFiMulti.h>
#include <WebServer.h>

#ifndef LED_BUILTIN
#define LED_BUILTIN 5 // Set this to the built-in LED number of your ESP32 board
#endif

// ------------------------------------------------------------------------------------------
// ESP-NOW SYSTEM CONFIGS
// ------------------------------------------------------------------------------------------
//
// This is the MAC address to be installed (sensors shall then send to this MAC address)
uint8_t GatewayMac[] = {0x02, 0x10, 0x11, 0x12, 0x13, 0x14};

/*  Note, the Gateway listens on this MAC address. All sensors shall send to this MAC address.
 *  You can set any MAC address of your choice according to this table of "free-to-use local MAC addresses":
 *    {0x02, any, any, any, any, any}
 *    {0x06, any, ...}
 *    {0x0A, any, ...}
 *    {0x0E, any, ...}
 *  
 *  Further, it would be possible to use the built-in HW-MAC address of the ESP8266. But, in case you would need to  
 *  change the ESP (for any reasons) you would then need to update ALL sensors to the same new MAC address. 
 *  It is therefore better to use a soft MAC defined by code. This will be installed in any new ESP HW you may use.
 *  Just remeber to set a new MAC address for every new system (gateway+sensors) you install in paralell. *  
 */

// ESP-Now message format. Sensor data is transmitted using this struct.
typedef struct sensor_data_t {
int unit; // Unit no to identy which sensor is sending
float temp; // Temperature (C)
float humidity; // Humidity (%)
float baro_pressure; // Barometric pressure (hPa)
int lux; // Light sensor data (lux)
float Vbat; // Battery voltage level (V)
char ID[80]; // Any clear text to identify the unit
int wakeTimeMS; // Sensor wake time until sending data
unsigned long updated; // Epoch time when received by Gateway. Set by gateway/receiver. (Not used by sensor, but part of struct for convenience reasons.)
} sensor_data_t;

// ------------------------------------------------------------------------------------------
// ESP-NOW GATEWAY CONFIGS
// ------------------------------------------------------------------------------------------
// Router WiFi Credentials (runnning on 2.4GHz and Channel=1)
#define SOFTAP_SSID "...YOUR SSID Network Name..."
#define SOFTAP_PASS "...YOUR Network Password ..."

#define UNITS 20 // No of esp-now sensor units supported to receive from. ESP-Now has a maximum of 20

// ------------------------------------------------------------------------------------------
// GLOBALS
// ------------------------------------------------------------------------------------------
WiFiMulti wifiMulti;

sensor_data_t bufSensorData; // buffer for incoming data
sensor_data_t sensorData[UNITS+1]; // buffer for all sensor data

// ------------------------------------------------------------------------------------------
// ESP-NOW functions
// ------------------------------------------------------------------------------------------
//
// This is a callback function from ESP (?), anyway (!IMPORTANT)
//
// (Note. I noted now, 1 1/2 years later, when i clean this up and retest a gateway with ESP32 - this initVariant
// seems not to be called by ESP routines anymore. I have as a fix called this routine manually in the setup.
// This could of course (?) be done also for ESP8266. But i dont want to touch that code since it has been working
// for 1.5 years now.)
//
void initVariant()
{

WiFi.mode(WIFI_AP);
esp_wifi_set_mac(ESP_IF_WIFI_AP, &GatewayMac[0]); //ESP32 code
}

// Callback when data is received from any Sender
void OnDataRecv(const uint8_t *mac_addr, const uint8_t *data, int data_len)
{
digitalWrite (LED_BUILTIN, !HIGH); // Led ON

char macStr[24];
snprintf(macStr, sizeof(macStr), " %02x:%02x:%02x:%02x:%02x:%02x",
mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
Serial.print("\nData received from: "); Serial.println(macStr);
memcpy(&bufSensorData, data, sizeof(bufSensorData));

// Print data
Serial.print ("ID: ");
Serial.print (bufSensorData.ID);
Serial.println ("");
Serial.print ("Unit: ");
Serial.print (bufSensorData.unit);
Serial.print (" Temp: ");
Serial.print (bufSensorData.temp);
Serial.print (" Humidity: ");
Serial.print (bufSensorData.humidity);
Serial.print (" Baro: ");
Serial.print (bufSensorData.baro_pressure);
Serial.print (" Lux: ");
Serial.print (bufSensorData.lux);
Serial.println ("");
Serial.print ("Vbat: ");
Serial.print (bufSensorData.Vbat);
Serial.print (" Wake: ");
Serial.print (bufSensorData.wakeTimeMS);
Serial.println ("");

// Store data
int i = bufSensorData.unit;
if ( (i >= 1) && (i <= UNITS) ) {
memcpy(&sensorData[i], data, sizeof(bufSensorData));
};

digitalWrite (LED_BUILTIN, !LOW); // Led OFF}
}

// ------------------------------------------------------------------------------------
// WEB server functions
// ------------------------------------------------------------------------------------
WebServer server(80);

void handleRoot() {
digitalWrite (LED_BUILTIN, !HIGH); // Led ON

// Create a (very) simple response, just to demonstrate
String msg;
msg = "ESP-Now Gateway \n";
for (int i=1; i<=UNITS; i++) {
String str2 = (i<10)?("0"+String(i)):(String(i)); // Make i to 2 pos string
msg += String("Unit: "+str2+" Temp: "+String(sensorData[i].temp)+" C\n");
}
server.send(200, "text/plain", msg );
Serial.println ("Web server call:" + msg);

digitalWrite (LED_BUILTIN, !LOW); // Led OFF}
}

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()
// ------------------------------------------------------------------------------------
{
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite (LED_BUILTIN, !HIGH); // Led ON (Most often not working on ESP32)

// Init Serial
Serial.begin(115200);
while (!Serial) {};
delay(100); // Needed for some boards
Serial.println("\n\n");

// Print sketch intro ---------------------------
Serial.println();
Serial.println("===========================================");
Serial.println(SKETCH_NAME);
Serial.println(SKETCH_VERSION);
Serial.println(SKETCH_ABOUT);
Serial.println("===========================================");

// initVariant() seems (?) not to be called anymore for ESP32. It used to ...? Anyway, just call it from here.
// (And of course, we could maybe just move all the code here, also for ESP8266. Some other day ...)
initVariant();

// Connect to WiFi ------------------------------
Serial.print("Connecting to WiFi ");

// Set device in AP mode to begin with
WiFi.mode(WIFI_AP_STA); // AP and STA is required (!IMPORTANT)

wifiMulti.addAP(SOFTAP_SSID, SOFTAP_PASS); // I use wifiMulti ... just by habit, i guess ....
while (wifiMulti.run() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}

// Come here - we are connected
Serial.println(" Done");

// Print WiFi data
Serial.println("Set as AP_STA station.");
Serial.print ("SSID: "); Serial.println(WiFi.SSID());
Serial.print ("Channel: "); Serial.println(WiFi.channel());
Serial.print ("IP address: "); Serial.println(WiFi.localIP());
delay(1000);

// Initialize ESP-Now ---------------------------

// Config gateway AP - set SSID and channel
int channel = WiFi.channel();
if (WiFi.softAP(SOFTAP_SSID, SOFTAP_PASS, channel, 1)) {
Serial.println("AP Config Success. AP SSID: " + String(SOFTAP_SSID));
} else {
Serial.println("AP Config failed.");
}

// Print MAC addresses
Serial.print("AP MAC: "); Serial.println(WiFi.softAPmacAddress());
Serial.print("STA MAC: "); Serial.println(WiFi.macAddress());

// Init ESP-Now
#define ESPNOW_SUCCESS ESP_OK

if (esp_now_init() == ESPNOW_SUCCESS) {
Serial.println("ESP - Now Init Success");
} else {
Serial.println("ESP - Now Init Failed");
ESP.restart(); // just restart if we cant init ESP-Now
}

// ESP-Now is now initialized. Register a callback fcn for when data is received
esp_now_register_recv_cb(OnDataRecv);

// Set web server callback functions
server.on("/", handleRoot);
server.onNotFound(handleNotFound);

// Start web server
server.begin();
Serial.print("WEB server started on SSID: "); Serial.print (WiFi.SSID()); Serial.print (" with IP: "); Serial.println(WiFi.localIP());

digitalWrite (LED_BUILTIN, !LOW); // Led OFF
}

// ------------------------------------------------------------------------------------
void loop()
// ------------------------------------------------------------------------------------
{
server.handleClient();
}
`

When I compiled it has error below

Error

Arduino: 1.8.19 (Windows Store 1.8.57.0) (Windows 10), Board: "NodeMCU-32S, 80MHz, 921600, None"

C:\Users\NECGEN~1\AppData\Local\Temp\arduino_modified_sketch_785761\sketch_aug11d.ino: In function 'void initVariant()':

sketch_aug11d:138:50: error: cannot convert 'esp_interface_t' to 'wifi_interface_t'

esp_wifi_set_mac(ESP_IF_WIFI_AP, &GatewayMac[0]); //ESP32 code

                                              ^

In file included from C:\Users\NECGEN~1\AppData\Local\Temp\arduino_modified_sketch_785761\sketch_aug11d.ino:59:

C:\Users\NECgen4UEFI\Documents\ArduinoData\packages\esp32\hardware\esp32\2.0.4/tools/sdk/esp32/include/esp_wifi/include/esp_wifi.h:659:45: note: initializing argument 1 of 'esp_err_t esp_wifi_set_mac(wifi_interface_t, const uint8_t*)'

esp_err_t esp_wifi_set_mac(wifi_interface_t ifx, const uint8_t mac[6]);

                        ~~~~~~~~~~~~~~~~~^~~

Multiple libraries were found for "WiFi.h"

Used: C:\Users\NECgen4UEFI\Documents\ArduinoData\packages\esp32\hardware\esp32\2.0.4\libraries\WiFi

Not used: C:\Program Files\WindowsApps\ArduinoLLC.ArduinoIDE_1.8.57.0_x86__mdqgnx93n4wtt\libraries\WiFi

exit status 1

cannot convert 'esp_interface_t' to 'wifi_interface_t'

This report would have more information with
"Show verbose output during compilation"
option enabled in File -> Preferences.
...
I found some fix this issue, but I dont' know how to use this fix to edit with sketch. please help me,thank you.

I am a newbe to learn about esp-now

--

espnow_sensor.ino - ESPNOW: Peer interface is invalid

I realize that this may be caused updated ArduinoIDE packages and what not, but I'm not sure how to get around it.

Hardware: ESP32 (WROOM32) (Gateway set up with WROOM32 as well)

Code as tested:
NOTE: I've *undefined the sensors and comment out their uses, and simplified the data struct to modify the code as little as possible.

#define SKETCH_NAME     "ESP-Now Sensor"
#define SKETCH_VERSION  "2021-12-31"
#define SKETCH_ABOUT    "Temp sensor, Deep Sleep, ESP-Now, ESP8266 and ESP32."

//
//  This is a template code for an ESP-Now sensor for ESP8266 and ESP32. It Assumes Arduino IDE.
//
//  Note. ESP8266 and ESP32 use different WiFi implementations why you must set the appropriate
//  #define USE_ESP8266 or USE_ESP32. Please remember to also set the appropriate BOARD in the 
//  ARDUINO IDE to find the correct include files.
//
//  The ESP-Now sensor sends messages on ESP-Now to a MAC address of a Gateway which is 
//  connected to WiFi and Internet.
//
//  A sensor unit consists of; ESP8266 board, a Temp Sensor and a LiPo/Li-Ion battery cell. 
//  An optional Solar panel and Solar panel manager/Battery charger can be connected.
//
//  The ESP8266 operates in deep sleep mode and wakes every 5 mins, takes a sensor reading, 
//  transmits the data using ESP-Now to an ESP-Now Gateway and then returns to deep sleep.
//  The design is aimed for Battery Cell and/or Solar Panel powered units with as low power 
//  consumption as possible, using standard ESP8266 boards. No special, or modified, ultra low 
//  power ESP8266 boards are required.
//
//  ESP-Now is much faster than ordinary WiFi (TCP/IP), reducing the "sending energy" dramatically. 
//  The drawback is that ESP-Now is a proprietary protocol of Espressif and is only implemented 
//  (what i know) for ESP MCU's.
//
//  The wakeup time of the MCU is heavily dependent on the temp sensor used. Some temp sensors 
//  will return sensor data within 100 ms or shorter, some requires 500 ms or more. This varies 
//  with sensor type/manufacturer (spec) and samples. Specs often claim a wake-up or stabilization
//  time of upto 2 secs, but most sensors can return a stable (?) measurement in 200-500 ms typically.
//
//  (Generally, SHT30 and DS18B20 temp sensors perform well and decently fast. DHT11/21/22 are slower 
//  and less reliant in my testes. I dont use them in real life.)
//
//  AUTHOR:
//  Jonas Byström, https://github.com/jonasbystrom
//
//  CREDITS:
//  This code is based on code from:
//  - Anthony Elder @ https://github.com/HarringayMakerSpace/ESP-Now/blob/master/espnow-sensor-bme280/espnow-sensor-bme280.ino
//  With important info from:
//  - Erik Bakke @ https://www.bakke.online/index.php/2017/05/21/reducing-wifi-power-consumption-on-esp8266-part-2/
//  And great info from:
//  - ArduinoDIY @ https://arduinodiy.wordpress.com/2020/01/18/very-deepsleep-and-energy-saving-on-esp8266/

//  HARDWARE:
//  - D1 Mini Pro v2.0 (or, D1 Mini v3.1.0), or
//  - Any ESP32 board
//  - SHT30 or DS18B20 temp sensor
//  - 3.7V LiPo/Li-Ion battery with JST connector. Flat 1200mAh, 18650 2200mAh or similar.
//  - Optional: Solar panel and solar panel charger board
//
//  CONNECTIONS:
//
//  D1 Mini Pro V2.0.0:
//  ===================
//  BAT --- A0          (solder/short the "BAT-A0" pad for battery level measurement)
//  RST --- D0/GPIO16   (solder/short the "SLEEP" pad)
//
//
//  D1 Mini SHT30 (If using WEMOS/LOLIN boards, just plug them together.)
//  ======= =====
//  GND     GND
//  3V3     VCC
//  D1/SCL  SCK
//  D2/SDA  SDA
//
//  or, if using a DS18B20
//
//  D1 Mini - DS18B20 (with an internal resistor)
//  =======   =======
//  GND       G (GND)
//  3V        V (VCC)
//  D3
//  D4        S (DATA)
//
//
//  HISTORY:
//  ========
//  2021-12-30  Cleaned for public publish on GitHub.
//  2021-12-31  Support for ESP32 added.
//

// ------------------------------------------------------------------------------------------
// ESP CONFIGS
// ------------------------------------------------------------------------------------------
//
// IMPORTANT. Select ESP8266 or ESP32. - Remember to also set the appropriate ESP8266 or ESP32 board in the ARDUINO IDE !
#define USE_ESP32               // Select (uncomment) one of ESP8266 or ESP32. (And set BOARD in ARDUINO IDE.) 
//#define USE_ESP8266  
      
// Different Wifi and ESP-Now implementations for ESP8266 and ESP32
#ifdef USE_ESP8266
#include <ESP8266WiFi.h>
#include <espnow.h>
#endif

#ifdef USE_ESP32
#include <WiFi.h>
#include <esp_now.h>
#endif


// ------------------------------------------------------------------------------------------
// ESP-NOW SYSTEM CONFIGS
// ------------------------------------------------------------------------------------------
#define WIFI_CHANNEL        1      // Must be 1. (!IMPORTANT)
                                  // ESP-Now can work on other channels, but the receiving ESP Gateway must operate on
                                  // channel 1 in order to also work for TCP/IP WiFi.
                                  // It has been reported to work also for other Channels, but I have only succeeded on ch 1.

//uint8_t Gateway_Mac[] = {0x02, 0x10, 0x11, 0x12, 0x13, 0x14};
uint8_t Gateway_Mac[] = {0xC8, 0x2B, 0x96, 0xA1, 0x93, 0x78};
                                  // MAC Address of the remote ESP Gateway we send to.
                                  // This is the "system address" all Sensors send to and the Gateway listens on.
                                  // (The ESP Gateway will set this "soft" MAC address in its HW. See Gateway code for info.)

typedef struct sensor_data_t {    // Sensor data format for sending on ESP-Now to Gateway
  int           id;               // Unit no to identy which sensor is sending
  int           on_delay;
  bool          active;
  //char          confg[30]; // Meh
  unsigned long updated;            // Epoch time when received by Gateway. Set by gateway/receiver. (Not used by sensor, but part of struct for convenience reasons.)
} sensor_data_t;


// -----------------------------------------------------------------------------------------
// ESP SENSOR CONFIGS
// -----------------------------------------------------------------------------------------
#define UNIT                1       // Sensor unit ID to identify THIS unit for the receiving gateway. Recommended to use [1 -20]

//#define USE_SHT30                   // Select (uncomment) the temp sensor type in use
//#define USE_DS18B20

#define DEBUG_LOG                   // Enable (uncomment) to print debug info. Disabling (comment) debug output saves some 4-5 ms ...

//ADC_MODE(ADC_VCC);                // Uncomment to Enable reading Vcc (if board cannot read VBAT).

#define SLEEP_SECS        5*60-8    // [sec] Sleep time between wake up and readings. Will be 5 mins +/- 8 secs. Varying to avoid transmit collusions.
#define MAX_WAKETIME_MS   1000      // [ms]  Timeout until forced gotosleep if no sending success 



#ifdef USE_SHT30
// -----------------------------------------------------------------------------------------
//  WEMOS SHT30 Temp and Humidity sensor
// -----------------------------------------------------------------------------------------
#include "Wire.h"
#define ADDR              0x45      // I2C address of SHT30.
#endif


#ifdef USE_DS18B20
// -----------------------------------------------------------------------------------------
// DS18B20 - TEMP SENSOR
// -----------------------------------------------------------------------------------------
// Temp probe is connected with onewire interface.
//
#include <OneWire.h>
#include <DallasTemperature.h>

// Data wire is plugged into port D3 & D4 on the ESP8266
#define ONE_WIRE_BUS            D4                // Change to other port if wanted/needed
#define TEMPERATURE_PRECISION   12                // [9-12]. 12 => resolution of 0.0625 C
                      /*  12-bit precision:
                          1-bit for sign, 7-bit for integer part, and 4-bit for fractional part (4-digit after decimal point)
                          Temperature range: xxx.0000 C to xxx.9375 C in 0.0625 C discrete step.
                      */        
OneWire oneWire (ONE_WIRE_BUS);                   // Setup a oneWire instance to communicate with any OneWire devices
DallasTemperature tempSensor(&oneWire);           // Pass our oneWire reference to Dallas Temperature.
DeviceAddress tempDeviceAddress;                  // We'll use this variable to store a found device address

#endif


// -----------------------------------------------------------------------------------------
//  BATTERY LEVEL CALIBRATION
// -----------------------------------------------------------------------------------------
#define CALIBRATION         4.21 / 4.35                       // Measured V by multimeter / reported (raw) V 
                                                              // (Set to 1 if no calibration is needed/wanted)
#define VOLTAGE_DIVIDER     (130+220+100)/100 * CALIBRATION   // D1 Mini Pro voltage divider to A0. 
                                                              // May be different for other boards.


// -----------------------------------------------------------------------------------------
// GLOBALS
// -----------------------------------------------------------------------------------------
sensor_data_t sensorData;
volatile boolean messageSent;     // flag to tell when message is sent out and we can safely goto sleep


#ifdef USE_SHT30
// -----------------------------------------------------------------------------------------
byte sht30_get(float &temp, float &humid)
// -----------------------------------------------------------------------------------------
// Read the SHT30 temp/humid sensor.
// Code is from WEMOS_sht3x.cpp - modified by reducing delays and adjusting types.
{
  unsigned int data[6];

  temp = NAN;
  humid = NAN;

  Wire.beginTransmission(ADDR);           // Start I2C Transmission
  Wire.write(0x2C);                       // Send measurement command
  Wire.write(0x06);
  if (Wire.endTransmission() != 0)        // Stop I2C transmission
    return 1;

  delay(100);         // 100 ms sensor stabilization time. Shorter than spec, but works OK.  Can be tweaked if needed.
                      // (I have seen Tasmota code to read SHT30 a bit different and as it seems even faster - would be interesting to try ...)

  Wire.requestFrom(ADDR, 6);              // Read the 6 data bytes
  for (int i = 0; i < 6; i++) {
    data[i] = Wire.read();
  };

  delay(50);                              // Let it stabilize. Can be tweaked
  if (Wire.available() != 0)
    return 2;
  // Convert the data
  temp = ((((data[0] * 256.0) + data[1]) * 175) / 65535.0) - 45;
  //fahrenheit version: temp = (temp * 1.8) + 32;
  humid = ((((data[3] * 256.0) + data[4]) * 100) / 65535.0);

  return 0;
}
#endif


// -----------------------------------------------------------------------------------------
void setup()
// -----------------------------------------------------------------------------------------
{
  // Disable WiFi until we shall use it, to save energy
  WiFi.persistent( false );         // Dont save WiFi info to Flash - to save time
  #ifdef USE_ESP8266
  WiFi.mode( WIFI_OFF );            // Wifi OFF - during sensor reading - to save current/power
  WiFi.forceSleepBegin();
  delay( 1 );                       // Necessary for the OFF to work. (!IMPORTANT)
  #endif
  
  #ifdef USE_SHT30
  Wire.begin();                     // Prepare the I2C communication
  #endif

  #ifdef DEBUG_LOG
  Serial.begin(115200);
  while (!Serial) {};
  Serial.println("\n\nStart");
  #endif

  #ifdef USE_DS18B20
  // Init sensor/bus
  //pinMode(ONE_WIRE_BUS, OUTPUT);     // use this if using PARASITE mode of DS18B20 (vcc from data line, and probably a pull up resistor ...)
  tempSensor.begin();
  tempSensor.getAddress(tempDeviceAddress, 0);
  tempSensor.setResolution(tempDeviceAddress, TEMPERATURE_PRECISION);
  tempSensor.requestTemperatures();
  #endif

  // read battery voltage
  //int raw = analogRead(A0);
  //sensorData.Vbat = raw * VOLTAGE_DIVIDER / 1023.0;
          // Alternative. If cannot read Battery level on your board, Read Vcc instead
          // const float calVal = 0.001108;     // 3.27/2950=0.001108. Vcc 3.27 on Multimeter, 2950 from getVcc()
          // sensorData.Vbat = ESP.getVcc()/1023; // * calVal;
 
  #ifdef DEBUG_LOG
  //Serial.print("Battery voltage:"); Serial.print(sensorData.Vbat); Serial.println(" V");
  #endif

  // compile message to send
  //strcpy (sensorData.ID, SKETCH_NAME);
  //strcat (sensorData.ID, " - ");
  //strcat (sensorData.ID, SKETCH_VERSION);
  sensorData.id = 1;
  //sensorData.unit = UNIT;

  // Read the temp sensor
  #ifdef USE_SHT30
  if (sht30_get(sensorData.temp, sensorData.humidity) == 0) {
    // reading ok   
  } else {
    // SHT30 reading error, use -999 as "invalid data". (Maybe it is better to use NAN instead (?))
    sensorData.temp = -999;
    sensorData.humidity = -999;
  }
  #elif defined USE_DS18B20
  sensorData.temp     = tempSensor.getTempC(tempDeviceAddress);
  #endif

#ifdef USE_8266
  // WiFi ON                    (This step seems not to be necessary, but anyway ...)
  WiFi.forceSleepWake();
  delay( 1 );
#endif

  // Set up ESP-Now link ---------------------------
  WiFi.mode(WIFI_STA);          // Station mode for esp-now sensor node
  WiFi.disconnect();
  #ifdef DEBUG_LOG
  Serial.printf("My HW mac: %s", WiFi.macAddress().c_str());
  Serial.println("");
  Serial.printf("Sending to MAC: %02x:%02x:%02x:%02x:%02x:%02x", Gateway_Mac[0], Gateway_Mac[1], Gateway_Mac[2], Gateway_Mac[3], Gateway_Mac[4], Gateway_Mac[5]);
  Serial.printf(", on channel: %i\n", WIFI_CHANNEL);
  #endif

  // Initialize ESP-now ----------------------------
  if (esp_now_init() != 0) {
    #ifdef DEBUG_LOG
    Serial.println("*** ESP_Now init failed. Going to sleep");
    #endif
    delay(100);
    gotoSleep();
  }

  #ifdef USE_ESP8266
  esp_now_set_self_role(ESP_NOW_ROLE_CONTROLLER);
  esp_now_add_peer(Gateway_Mac, ESP_NOW_ROLE_SLAVE, WIFI_CHANNEL, NULL, 0);
  #endif
  #ifdef USE_ESP32
  esp_now_peer_info_t gateway;
  memcpy(gateway.peer_addr, Gateway_Mac, 6);
  gateway.channel = WIFI_CHANNEL;
  gateway.encrypt = false;            // no encryption
  //esp_now_add_peer(&gateway);  
  if (esp_now_add_peer(&gateway) != ESP_OK) {
    Serial.println("Failed to add peer");
    return;
  } else {
    Serial.println("Added peer");
  }
  #endif

  #ifdef USE_ESP8266
  esp_now_register_send_cb([](uint8_t* mac, uint8_t sendStatus) {
  #endif
  #ifdef USE_ESP32
  esp_now_register_send_cb([](const uint8_t* mac, esp_now_send_status_t sendStatus) {
  #endif
    // callback for message sent out
    messageSent = true;         // flag message is sent out - we can now safely go to sleep ...
    #ifdef DEBUG_LOG
    Serial.printf("Message sent out, sendStatus = %i\n", sendStatus);
    #endif
  });

  messageSent = false;
  sensorData.id = 1;
  sensorData.on_delay = 12;
  sensorData.active = false;
  sensorData.updated = 16151;
  // Send message -----------------------------------
  #ifdef DEBUG_LOG
  Serial.println("Message Data: " + \
                  String(sensorData.id));// + ", Unit:" + \
                  //String(sensorData.unit) + ", Temp:" + \
                  //String(sensorData.temp) + "C, Hum: " + \
                  //String(sensorData.humidity) + "%, Lux: " + \
                  //String(sensorData.lux) + ", Vbat:" + \
                  //String(sensorData.Vbat) \
                );
  #endif
  
  uint8_t sendBuf[sizeof(sensorData)];          // create a send buffer for sending sensor data (safer)
  //sensorData.wakeTimeMS = millis();             // set wake time until now
  memcpy(sendBuf, &sensorData, sizeof(sensorData));
  #ifdef USE_ESP8266
  uint16_t result = esp_now_send(NULL, sendBuf, sizeof(sensorData));
  #endif
  #ifdef USE_ESP32
  const uint8_t *peer_addr = gateway.peer_addr;
  esp_err_t result=esp_now_send(peer_addr, (uint8_t *) &sensorData, sizeof(sensorData)); 
  #endif
  
  #ifdef DEBUG_LOG
  //Serial.print("Wake: "); Serial.print(sensorData.wakeTimeMS); Serial.println(" ms");
  Serial.print("Sending result: "); Serial.println(result);
  #endif
}

// -----------------------------------------------------------------------------------------
void loop()
// -----------------------------------------------------------------------------------------
{
  // Wait until ESP-Now message is sent, or timeout, then goto sleep
  if (messageSent || (millis() > MAX_WAKETIME_MS)) {
    //gotoSleep();
  }
}


// -----------------------------------------------------------------------------------------
void gotoSleep()
// -----------------------------------------------------------------------------------------
{
  int sleepSecs;

  #ifdef USE_ESP8266
  sleepSecs = SLEEP_SECS + ((uint8_t)RANDOM_REG32 / 16);  // add random time to avoid traffic jam collisions
  #ifdef DEBUG_LOG
  Serial.printf("Up for %i ms, going to deep sleep for %i secs ...\n", millis(), sleepSecs);
  #endif

  ESP.deepSleep(sleepSecs * 1000000, RF_NO_CAL);
  delay (10);                                             // good convention with delay after call to deep sleep.

  // Never return here - ESP will be reset after deep sleep
  #endif

  #ifdef USE_ESP32
  sleepSecs = SLEEP_SECS + ((uint8_t)esp_random()/16);      // add random time to avoid traffic jam collisions
  #ifdef DEBUG_SENSOR      
    Serial.printf("Up for %i ms, going to sleep for %i secs...\n", millis(), sleepSecs); 
  #endif

  esp_sleep_enable_timer_wakeup(sleepSecs * 1000000);
  //esp_sleep_enable_ext0_wakeup((gpio_num_t)BUTTON_PIN, LOW);
  esp_deep_sleep_start(); 

  // Never return here - ESP will be reset after deep sleep  
  #endif
}

The code complies and uploads, but the serial output shows :
15:43:53.886 -> Sending to MAC: c8:2b:96:a1:93:78, on channel: 1 15:43:53.886 -> E (156) ESPNOW: Peer interface is invalid 15:43:53.886 -> Failed to add peer
"Failed to add peer" was added by me to check response code of esp_now_add_peer(&gateway)

ANy help would be greatly appreciated!

Thoughts on further optimizing sleep power.

Your write-up and code are some of the best examples I have seen in either the Arduino or ESP ecosystem.

I have been working on my own power optimized designs. I didn't have a CPU module with low enough power consumption even with deep sleep so I used some other tricks. If you combine what I did with what you did it could provide even longer battery life.

Added a low-side mosfet switch to disconnect sensor power when sensors are not being actively used. Found this necessary for greedy sensors like magnetic field sensors and MPU6050. The CPU powers up the sensors by setting a IO pin HIGH to energize the gate on the N-channel mosfet. I use a 1 meg resistor gate to ground to force sensors off when the CPU is booting. As soon as I finish taking readings I turn of the sensors while the CPU may run a bit longer transmitting the data.

Added a DS3231 clock chip that includes alarm functionality that I use to facilitate power conservation. I wanted it for accurate multi-year time keeping but found that it added some features that make managing the CPU a lot easier like low-voltage reset. It is ultra low power and can run for years on a coin cell.

  • Add a low side n-channel mosfet switch that cuts ground connection between charger module and CPU module reducing power consumption down to the leakage value of the transistor. Call the Gate on this mosfet CPWR
  • The clock chip includes alarms that can be programmed to fire every second, every minute or every hour. When they fire they activate it's INT/SQW pin.
  • When the alarm fires it generates a pulse which energizes CPWR gate on the mosfet that restores power to the CPU. I use a capacitor to hold the CPWR HIGH long enough to allow the CPU to set a PIN to HIGH that keeps CPWR high until the CPU finishes working.
  • The clock can be connected to run direct from the TP4056 but I also used a coin backup because I don't want to loose time and date when the 18650 battery needs to be replaced. No need for a separate regulator for the clock chip because it is 5.5V tolerant.
  • Once the CPU finishes it's work, it sets that pin to LOW which drives CPWR low turning off the mosfet completely disconnecting the CPU until the clock pulse fires again.
  • A manual wake-up button energizes the CPWR mosfet gate to turn the CPU on but I can not always tell if the wake-up was because the button was pressed versus the alarm firing unless the user keeps their finger on the button long enough to boot and take a reading.
  • The clock chip must be in circuit between TP4056 and CPU power mosfet. Otherwise it's alarm pulse can not be used to energize the CPWR mosfet gate.

My sensors are not always in WiFi range so I write data to SPIFFS and send in batch once they can connect again. I extended this so I have a transmit interval that may be longer than my measure interval. Since it takes a little time with CPU active to negotiate the connection this can save a bit more power.

One downside of this approach is that any data you want to retain between wakeup events must be stored in SPIFFS because the CPU can not retain RTC variable storage when completely powered off. I read there is some NVM on this CPU but have not tried using it.

Code?

I see references to existing code, but I see no code in Repo?

Tp4056 is necessary?

Why using a TP4056 when using a solar panel?

Wemos D1 mini pro v 2 includes a battery charger. Won't it be possible to connect the solar panel directly to the D1 mini pro?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.