jonasbystrom / ESP-Now-Sensor-system-with-WiFi

ESP-Now is used for battery operated Sensors (based on ESP8266, ESP32) sending to a Gateway which also is connected with WiFi to the Internet (at the same time).
MIT License
37 stars 7 forks source link

espnow_sensor.ino - ESPNOW: Peer interface is invalid #5

Open Picalz opened 6 months ago

Picalz commented 6 months ago

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!