ESP32 ESPNOW 1 to Many - Only first address is receiving good data. Other 3 Boards are not receiving much data and very slow

Hi

I have been working on an automatic irrigation system.

I'm mostly there with the code, I have been spending a fair bit of time on the hardware setup recently. A lot of help was given in this post for the coding..

The master ESP32 sends the current hour of the day to 4 boards. Board 1 receives data very well and quickly. But the next 3 don't. They do occasionally receive the data but its not useable. I have 5 seconds for initialization to determine what time it is and then do irrigation or not.

Its always the first address in the list of broadcast addresses that is good. So I can order them 1234 - 1 is good, or 4231 - 4 is good.

Help on this would be appreciated.

Codes below..

Thanks in advance

Mark

Master Code

`#include <TFT_eSPI.h>
#include <WiFi.h>
#include "thingProperties.h"
#include <esp_now.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include "driver/rtc_io.h"
#include <NTPClient.h>
#include <WiFiUdp.h>
#include "time.h"
#include "SPI.h" 
//#include <esp_wifi.h>

int Moisture11;
int Moisture12;
int Moisture13;
int Moisture14;
int Moisture15;
int Moisture16;
int Moisture17;
int Moisture18;
int Moisture19;
int Moisture20;
int Moisture21;
int Moisture22;
int Moisture23;
int Moisture24;

const char* ssid = SECRET_SSID;
const char* password = SECRET_OPTIONAL_PASS;

typedef struct struct_message {
  int id;
  int P1;
  int P2;
  int P3;
  int P4;
  int P5;
  int P6;
  int readingId;
} struct_message;

struct_message incomingReadings;

// Create a structure to hold the readings from each board
struct_message board1;
struct_message board2;
struct_message board3;
struct_message board4;

struct_message boardsStruct[4] = {board1, board2, board3, board4};

void OnDataRecv(const uint8_t * mac_addr, const uint8_t *incomingData, int len) {

// Copies the sender mac address to a string
char macStr[18];
Serial.print("Packet received from: ");
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.println(macStr);

  memcpy(&incomingReadings, incomingData, sizeof(incomingReadings));

  Serial.printf("Board ID %u: %u bytes\n", incomingReadings.id, len);
 // Update the structures with the new incoming data
  boardsStruct[incomingReadings.id-1].P1 = incomingReadings.P1;
  boardsStruct[incomingReadings.id-1].P2 = incomingReadings.P2;
  boardsStruct[incomingReadings.id-1].P3 = incomingReadings.P3;
  boardsStruct[incomingReadings.id-1].P4 = incomingReadings.P4;
  boardsStruct[incomingReadings.id-1].P5 = incomingReadings.P5;
  boardsStruct[incomingReadings.id-1].P6 = incomingReadings.P6;
  //Serial.printf("x value: %d \n", boardsStruct[myData.id-1].P1);
  //Serial.printf("y value: %d \n", boardsStruct[myData.id-1].P2);
  //Serial.printf("x value: %d \n", boardsStruct[myData.id-1].P3);
  //Serial.printf("y value: %d \n", boardsStruct[myData.id-1].P4);
  //Serial.printf("x value: %d \n", boardsStruct[myData.id-1].P5);
  //Serial.printf("y value: %d \n", boardsStruct[myData.id-1].P6);

Serial.println();
}

uint8_t broadcastAddress4[] = {0x08, 0xA6, 0xF7, 0x24, 0xFD, 0xF0};  //s1 08:a6:f7:24:fd:f0
uint8_t broadcastAddress2[] = {0x08, 0xA6, 0xF7, 0x24, 0xF9, 0xF0};  //08:a6:f7:24:f9:f0
uint8_t broadcastAddress3[] = {0x08, 0xA6, 0xF7, 0x24, 0xC7, 0x24};  //08:a6:f7:24:c7:24
uint8_t broadcastAddress1[] = {0x08, 0xA6, 0xF7, 0xA8, 0x72, 0x10};  //08:a6:f7:a8:72:10

typedef struct test_struct {
  int x;
} test_struct;

test_struct test;

esp_now_peer_info_t peerInfo;

// callback when data is sent
void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
  char macStr[18];
  Serial.print("Packet to: ");
  // Copies the sender mac address to a string
  snprintf(macStr, sizeof(macStr), "%02x:%02x:%02x:%02x:%02x:%02x",
           mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3]);
  Serial.print(macStr);
  Serial.print(" send status:\t");
  Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail");
}


TFT_eSPI tft = TFT_eSPI();

#define TFTLEDPIN 32


WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP);

// Variables to save date and time
String formattedDate;
String dayStamp;
String timeStamp;

const int buttonPin = 5;
byte oldButtonState = 0;
int screenNumber = 0;
byte buttonState = 0;

unsigned long lastDebounceTime = 0;  // the last time the output pin was toggled
unsigned long debounceDelay = 200;


  void connect(){
    WiFi.disconnect();

    WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
  delay(1000);
  Serial.println("Setting as a Wi-Fi Station..");
  tft.setCursor(5, 5);
    tft.setTextColor(TFT_WHITE, TFT_BLACK); tft.setTextSize(2);
    tft.println("Wifi Off"); 
		delay(500);
  
}
Serial.print("Station IP Address: ");
Serial.println(WiFi.localIP());
Serial.print("Wi-Fi Channel: ");
Serial.println(WiFi.channel());

    ArduinoCloud.begin(ArduinoIoTPreferredConnection, false);

    while (ArduinoCloud.connected() == 0) 
	{
		ArduinoCloud.update();//required so things don't crash on us
                Serial.println("Waiting for connection to Arduino IoT Cloud");
    tft.setCursor(5, 5);
    tft.setTextColor(TFT_WHITE, TFT_BLACK); tft.setTextSize(2);
    tft.println("Wifi Off"); 
		delay(500);
	}
       Serial.println("Connected to cloud");
  }



void setup() {

  Serial.begin(115200);
  delay(1500); 

  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(TFTLEDPIN, OUTPUT);

  tft.init();
  tft.fillScreen(TFT_BLACK);
  digitalWrite(TFTLEDPIN, HIGH);

  tft.setRotation(1);
  tft.setCursor(110, 20);
  tft.setTextColor(TFT_GREEN);  tft.setTextSize(3);
  tft.println("Moisture Levels");
  tft.setCursor(100, 160);
  tft.setTextColor(TFT_BLUE);  tft.setTextSize(3);
  tft.println("Loading.........");

  // Set the device as a Station and Soft Access Point simultaneously
  WiFi.mode(WIFI_AP_STA);
  
  initProperties();

  connect();

  delay(1000);

    if (esp_now_init() != ESP_OK) {
  Serial.println("Error initializing ESP-NOW");
  return;
}
  esp_now_register_recv_cb(esp_now_recv_cb_t(OnDataRecv));

  timeClient.begin();
  timeClient.setTimeOffset(14400);
  while(!timeClient.update()) {
  timeClient.forceUpdate();
}

  formattedDate = timeClient.getFormattedDate();
  Serial.println(formattedDate);

  tft.fillScreen(TFT_BLACK);

  esp_now_register_send_cb(OnDataSent);
   
  // register peer
  peerInfo.channel = 0;  
  peerInfo.encrypt = false;
  // register first peer  
  memcpy(peerInfo.peer_addr, broadcastAddress1, 6);
  if (esp_now_add_peer(&peerInfo) != ESP_OK){
    Serial.println("Failed to add peer");
    return;
  }
  // register first peer  
  memcpy(peerInfo.peer_addr, broadcastAddress2, 6);
  if (esp_now_add_peer(&peerInfo) != ESP_OK){
    Serial.println("Failed to add peer");
    return;
  }
  // register first peer  
  memcpy(peerInfo.peer_addr, broadcastAddress3, 6);
  if (esp_now_add_peer(&peerInfo) != ESP_OK){
    Serial.println("Failed to add peer");
    return;
  }
  // register first peer  
  memcpy(peerInfo.peer_addr, broadcastAddress4, 6);
  if (esp_now_add_peer(&peerInfo) != ESP_OK){
    Serial.println("Failed to add peer");
    return;
  }
  delay(3000);
}

void loop() {

  test.x = timeClient.getHours();
  
Serial.println(timeClient.getHours());
  
  //Serial.println(WiFi.status());

  if (WiFi.status() != WL_CONNECTED) {
  connect();
  }

  
  ArduinoCloud.update();

  formattedDate = timeClient.getFormattedDate();
  
  buttonState = digitalRead(buttonPin);

  if ((millis() - lastDebounceTime) > debounceDelay){

  if (buttonState != oldButtonState) {
    // reset the debouncing timer
    lastDebounceTime = millis();
  }

  if (buttonState == LOW) {
    if (buttonState == LOW && screenNumber != 3){
      ++screenNumber;}
    else {
      screenNumber = 0;
    }
    }
    }
  
      oldButtonState = buttonState;
  
  if(buttonState == 0){
      tft.fillScreen(TFT_BLACK);
    }
 // Acess the variables for each board
  int board1P1 = boardsStruct[0].P1;
  int board1P2 = boardsStruct[0].P2;
  int board1P3 = boardsStruct[0].P3;
  int board1P4 = boardsStruct[0].P4;
  int board1P5 = boardsStruct[0].P5;
  int board1P6 = boardsStruct[0].P6;
  int board2P1 = boardsStruct[1].P1;
  int board2P2 = boardsStruct[1].P2;
  int board2P3 = boardsStruct[1].P3;
  int board2P4 = boardsStruct[1].P4;
  int board2P5 = boardsStruct[1].P5;
  int board2P6 = boardsStruct[1].P6;
  int board3P1 = boardsStruct[2].P1;
  int board3P2 = boardsStruct[2].P2;
  int board3P3 = boardsStruct[2].P3;
  int board3P4 = boardsStruct[2].P4;
  int board3P5 = boardsStruct[2].P5;
  int board3P6 = boardsStruct[2].P6; 
  int board4P1 = boardsStruct[3].P1;
  int board4P2 = boardsStruct[3].P2;
  int board4P3 = boardsStruct[3].P3;
  int board4P4 = boardsStruct[3].P4;
  int board4P5 = boardsStruct[3].P5;
  int board4P6 = boardsStruct[3].P6;


  if (screenNumber == 0) {
  tft.setRotation(1);
  tft.setCursor(5, 5);
  tft.setTextColor(TFT_WHITE, TFT_BLACK); tft.setTextSize(2);
  tft.print("Wifi Ok  ");
  tft.setCursor(110, 40);
  tft.setTextColor(TFT_WHITE, TFT_BLACK);  tft.setTextSize(3);
  tft.println("Moisture Levels");
  tft.setCursor(200, 90);
  tft.setTextColor(TFT_GREEN);  tft.setTextSize(2);
  tft.println("STATION 1");  
  tft.setCursor(60, 130);
  tft.setTextColor(TFT_BLUE);  tft.setTextSize(2);
  tft.println("Flowers: "); 
  tft.setCursor(190, 130);
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.print(board1P1);tft.println("  ");
  tft.setCursor(281, 130);
  tft.setTextColor(TFT_BLUE);  tft.setTextSize(2);
  tft.println("Rosemary: "); 
  tft.setCursor(415, 130);
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.print(board1P2);tft.println("  ");
  tft.setCursor(60, 180);
  tft.setTextColor(TFT_BLUE);  tft.setTextSize(2);
  tft.println("Lavender: "); 
  tft.setCursor(190, 180);
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.print(board1P3);tft.println("  ");
  tft.setCursor(281, 180);
  tft.setTextColor(TFT_BLUE);  tft.setTextSize(2);
  tft.println("Mint: "); 
  tft.setCursor(415, 180);
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.print(board1P4);tft.println("  ");
  tft.setCursor(60, 230);
  tft.setTextColor(TFT_BLUE);  tft.setTextSize(2);
  tft.print("Strawberry: ");tft.println("  ");
  tft.setCursor(190, 230);
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.print(board1P5);tft.println("  ");
  tft.setCursor(281, 230);
  tft.setTextColor(TFT_BLUE);  tft.setTextSize(2);
  tft.println("Mint: "); 
  tft.setCursor(415, 230);
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.print(board1P6);tft.println("  ");
  }

  else if (screenNumber == 1) {
  tft.setRotation(1);
  tft.setCursor(5, 5);
  tft.setTextColor(TFT_WHITE, TFT_BLACK); tft.setTextSize(2);
  tft.println("Wifi Ok  ");
  tft.setCursor(110, 40);
  tft.setTextColor(TFT_WHITE, TFT_BLACK);  tft.setTextSize(3);
  tft.println("Moisture Levels");
  tft.setCursor(200, 90);
  tft.setTextColor(TFT_GREEN);  tft.setTextSize(2);
  tft.println("STATION 2");  
  tft.setCursor(60, 130);
  tft.setTextColor(TFT_BLUE);  tft.setTextSize(2);
  tft.println("Flowers: "); 
  tft.setCursor(190, 130);
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.print(board2P1);tft.println("  ");
  tft.setCursor(281, 130);
  tft.setTextColor(TFT_BLUE);  tft.setTextSize(2);
  tft.println("Rosemary: "); 
  tft.setCursor(415, 130);
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.print(board2P2);tft.println("  ");
  tft.setCursor(60, 180);
  tft.setTextColor(TFT_BLUE);  tft.setTextSize(2);
  tft.println("Lavender: "); 
  tft.setCursor(190, 180);
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.print(board2P3);tft.println("  ");
  tft.setCursor(281, 180);
  tft.setTextColor(TFT_BLUE);  tft.setTextSize(2);
  tft.println("Mint: "); 
  tft.setCursor(415, 180);
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.print(board2P4);tft.println("  ");
  tft.setCursor(60, 230);
  tft.setTextColor(TFT_BLUE);  tft.setTextSize(2);
  tft.println("Strawberry: "); 
  tft.setCursor(190, 230);
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.print(board2P5);tft.println("  ");
  tft.setCursor(281, 230);
  tft.setTextColor(TFT_BLUE);  tft.setTextSize(2);
  tft.println("Mint: "); 
  tft.setCursor(415, 230);
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.print(board2P6);tft.println("  ");
  
  
  }
  else if (screenNumber == 2) {
  tft.setRotation(1);
  tft.setCursor(5, 5);
  tft.setTextColor(TFT_WHITE, TFT_BLACK); tft.setTextSize(2);
  tft.println("Wifi Ok  ");
  tft.setCursor(110, 40);
  tft.setTextColor(TFT_WHITE, TFT_BLACK);  tft.setTextSize(3);
  tft.println("Moisture Levels");
  tft.setCursor(200, 90);
  tft.setTextColor(TFT_GREEN);  tft.setTextSize(2);
  tft.println("STATION 3");  
  tft.setCursor(60, 130);
  tft.setTextColor(TFT_BLUE);  tft.setTextSize(2);
  tft.println("Flowers: "); 
  tft.setCursor(190, 130);
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.print(board3P1);tft.println("  ");
  tft.setCursor(281, 130);
  tft.setTextColor(TFT_BLUE);  tft.setTextSize(2);
  tft.println("Rosemary: "); 
  tft.setCursor(415, 130);
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.print(board3P2);tft.println("  ");
  tft.setCursor(60, 180);
  tft.setTextColor(TFT_BLUE);  tft.setTextSize(2);
  tft.println("Lavender: "); 
  tft.setCursor(190, 180);
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.print(board3P3);tft.println("  ");
  tft.setCursor(281, 180);
  tft.setTextColor(TFT_BLUE);  tft.setTextSize(2);
  tft.println("Mint: "); 
  tft.setCursor(415, 180);
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.print(board3P4);tft.println("  ");
  tft.setCursor(60, 230);
  tft.setTextColor(TFT_BLUE);  tft.setTextSize(2);
  tft.println("Strawberry: "); 
  tft.setCursor(190, 230);
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.print(board3P5);tft.println("  ");
  tft.setCursor(281, 230);
  tft.setTextColor(TFT_BLUE);  tft.setTextSize(2);
  tft.println("Mint: "); 
  tft.setCursor(415, 230);
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.print(board3P6);tft.println("  ");  
  }
    
  else if (screenNumber == 3) {
  tft.setRotation(1);
  tft.setCursor(5, 5);
  tft.setTextColor(TFT_WHITE, TFT_BLACK); tft.setTextSize(2);
  tft.println("Wifi Ok  ");
  tft.setCursor(110, 40);
  tft.setTextColor(TFT_WHITE, TFT_BLACK);  tft.setTextSize(3);
  tft.println("Moisture Levels");
  tft.setCursor(200, 90);
  tft.setTextColor(TFT_GREEN);  tft.setTextSize(2);
  tft.println("STATION 4");  
  tft.setCursor(50, 130);
  tft.setTextColor(TFT_BLUE);  tft.setTextSize(2);
  tft.println("Bell Pepper: "); 
  tft.setCursor(190, 130);
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.print(board4P1);tft.println("  ");
  tft.setCursor(281, 130);
  tft.setTextColor(TFT_BLUE);  tft.setTextSize(2);
  tft.println("Bell Pepper: "); 
  tft.setCursor(420, 130);
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.print(board4P2);tft.println("  ");
  tft.setCursor(50, 180);
  tft.setTextColor(TFT_BLUE);  tft.setTextSize(2);
  tft.println("Lemon Grass: "); 
  tft.setCursor(190, 180);
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.print(board4P3);tft.println("  ");
  tft.setCursor(281, 180);
  tft.setTextColor(TFT_BLUE);  tft.setTextSize(2);
  tft.println("Tomato: "); 
  tft.setCursor(420, 180);
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.print(board4P4);tft.println("  ");
  tft.setCursor(50, 230);
  tft.setTextColor(TFT_BLUE);  tft.setTextSize(2);
  tft.println("Mangrove: "); 
  tft.setCursor(190, 230);
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.print(board4P5);tft.println("  ");
  tft.setCursor(281, 230);
  tft.setTextColor(TFT_BLUE);  tft.setTextSize(2);
  tft.println("Tomato: "); 
  tft.setCursor(420, 230);
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.print(board4P6);tft.println("  ");   
  }

  esp_err_t result = esp_now_send(0, (uint8_t *) &test, sizeof(test_struct));
   
  if (result == ESP_OK) {
    Serial.println("Sent with success");
  }
  else {
    Serial.println("Error sending the data");
  }


}

`

Please post it, using code tag when you do

Slave(s) Codes

#include "WiFi.h"
#include <esp_now.h>
#include <esp_wifi.h>

int Moisture1;
int Moisture2;
int Moisture3;
int Moisture4;
int Moisture5;
int Moisture6;

// Set your Board and Server ID 
#define BOARD_ID 4
//#define MAX_CHANNEL 13  // 11 in North America or 13 in Europe

uint8_t broadcastAddress[] = {0xCC,0x7B,0x5C,0xFD,0x58,0x70}; // ap CC:7B:5C:FD:58:70

typedef struct struct_message {
  int id;
  int P1;
  int P2;
  int P3;
  int P4;
  int P5;
  int P6;
  int readingId;
} struct_message;

esp_now_peer_info_t peerInfo;

struct_message myData;

unsigned long previousMillis = 0;  // Stores last time temperature was published
const long interval = 500;       // Interval at which to publish sensor readings

unsigned int readingId = 0;

typedef struct test_struct {
  int x;
} test_struct;

//Create a struct_message called myData
test_struct Hour;

constexpr char WIFI_SSID[] = "CornishRes";

const int PowerPin = 17;

String formattedDate;
String dayStamp;
String timeStamp;

unsigned long startMillis;  //some global variables available anywhere in the program
unsigned long currentMillis;

struct plantStation {
  const int SOIL_PIN;
  const int SOL_PIN;
  const int thresholdon;
  const int thresholdint;
  const int thresholdoff;
  const int mapdry;
  const int mapwet;
  const unsigned long MaxRunTime1;
  const unsigned long MaxRunTime2;
};

  const int StartTime1 = 10;
  const int StartTime2 = 13;

plantStation myPlants[6] = {
  { 36, 23, 50, 70, 80, 4095, 800, 10000, 10000 },
  { 39, 22, 50, 70, 80, 4095, 800, 10000, 10000 },
  { 34, 21, 50, 70, 80, 4095, 800, 10000, 10000 },
  { 35, 19, 50, 70, 80, 4095, 800, 10000, 10000 },
  { 32, 18, 50, 70, 80, 4095, 800, 10000, 10000 },
  { 33, 4, 50, 70, 80, 4095, 800, 10000, 10000 },
};

byte numPlants = sizeof(myPlants) / sizeof(*myPlants);

#define TURNON HIGH
#define TURNOFF LOW

int tickMs25 = 0;   // flag
int tickMs100 = 0;  // flag
int tickMs1000 = 0;  // flag
unsigned long timeMs25Prev = 0;
unsigned long timeMs100Prev = 0;
unsigned long timeMs1000Prev = 0;
//int currenthour = myData.x;

enum State {
  SS_INIT,
  SS_WATERING1,
  SS_ENDWATERING1,
  SS_DELAY,
  SS_WATERING2,
  SS_ENDWATERING2,
  SS_SLEEP,
  SS_LAST
};

// used only in debug print ( when changing state )
String stateStrSoil[SS_LAST] = {"Init","Watering1","End Watering1","Delay","Watering2","End Watering2","Sleep"};

State stateSoil = SS_INIT;         // fsm
unsigned long stateTimeSoil = 0;   // fsm

#define INIT_DELAY 5000     // delay at startup ( in milliseconds )
#define DELAY_DELAY 20000    // delay between two cycles ( in milliseconds )

#define uS_TO_S_FACTOR 1000000  /* Conversion factor for micro seconds to seconds */
#define TIME_TO_SLEEP  5
RTC_DATA_ATTR int bootCount = 0;

int soilIndex = 0;

unsigned long getStateTimeSoil()
{
  return millis() - stateTimeSoil;
}

// call this function when you have to change state
void changeStateSoil(State nextState)
{
  Serial.print("State: ");            Serial.print(stateStrSoil[stateSoil]);
  Serial.print(" duration: ");        Serial.println(getStateTimeSoil()/1000.0);
  Serial.print("State change from "); Serial.print(stateStrSoil[stateSoil]);
  Serial.print(" to ");               Serial.print(stateStrSoil[nextState]);
  Serial.print(" Sensor n. = ");      Serial.println(soilIndex);
  
 
  // enable this switch if you have to include any state initialization routine 
  /*switch (stateSoil)
  {
    case SS_INIT:      ctrlInitEntrySoil();  break;
    case SS_WATERING1: ctrlWatering1EntryeSoil();  break;
    case SS_DELAY:     ctrlDelayEntrySoil(); break;
    case SS_WATERING2: ctrlWatering2EntrySoil(); break;
  }*/

  stateTimeSoil = millis();
  stateSoil = nextState;
}

void ctrlInitStateSoil()
{
  Serial.println(Hour.x);
  //Serial.println(currenthour);

{
  if ((getStateTimeSoil() > INIT_DELAY && 1 == StartTime1) || (getStateTimeSoil() > INIT_DELAY && 1 == StartTime2))
    {
    soilIndex = 0;
    changeStateSoil(SS_WATERING1);
    }
  else if (getStateTimeSoil() > INIT_DELAY){
    soilIndex = 0;
      delay(15000);
    changeStateSoil(SS_SLEEP);
  }
}
}

void ctrlDelayStateSoil()
{
  if (getStateTimeSoil() > DELAY_DELAY)
    {
    soilIndex = 0;
    changeStateSoil(SS_WATERING2);
    }
}

void ctrlSleepStateSoil()
{
  Serial.println ("SLEEEEEEEEEEEEEEEEEEEEEEEEEEP");
  esp_deep_sleep_start();

   // restart cycle
  
     //Increment boot number and print it every reboot
  ++bootCount;
  Serial.println("Boot number: " + String(bootCount));
  soilIndex = 0;
  changeStateSoil(SS_WATERING1);
}

void ctrlWatering1StateSoil()
{
  int Soil_Moisture = analogRead(myPlants[soilIndex].SOIL_PIN);
  Soil_Moisture = map(Soil_Moisture, myPlants[soilIndex].mapdry, myPlants[soilIndex].mapwet, 0, 100);
  // Serial.print("Plant "); Serial.print(soilIndex); Serial.print(" Moisture = "); Serial.print(Soil_Moisture); Serial.print(" Sensor n. = "); Serial.println(soilIndex);

  if (Soil_Moisture <= myPlants[soilIndex].thresholdint) 
    {
    digitalWrite(myPlants[soilIndex].SOL_PIN, TURNON); 
    changeStateSoil(SS_ENDWATERING1);
    }
  else
    {
    digitalWrite(myPlants[soilIndex].SOL_PIN, TURNOFF); 
    soilIndex++;
    if (soilIndex >= numPlants ) 
      {changeStateSoil(SS_DELAY);}
    else
      {changeStateSoil(SS_WATERING1);}
    }
  // while (millis() - startMillis <= myPlants[ii].MaxRunTime1);
}

void ctrlEndWatering1StateSoil()
{
  int Soil_Moisture = analogRead(myPlants[soilIndex].SOIL_PIN);
  Soil_Moisture = map(Soil_Moisture, myPlants[soilIndex].mapdry, myPlants[soilIndex].mapwet, 0, 100);
  //Serial.print("Plant "); Serial.print(soilIndex); Serial.print(" Moisture = "); Serial.print(Soil_Moisture); Serial.print(" Sensor n. = "); Serial.println(soilIndex);
  // check soil moisture
  if (Soil_Moisture >= myPlants[soilIndex].thresholdint) 
    {
    digitalWrite(myPlants[soilIndex].SOL_PIN, TURNOFF); 
    soilIndex++;
    if (soilIndex >= numPlants ) 
      {changeStateSoil(SS_DELAY);}
    else
      {changeStateSoil(SS_WATERING1);}
    }
  // check max time
  if (getStateTimeSoil() >= myPlants[soilIndex].MaxRunTime1) 
    {
    digitalWrite(myPlants[soilIndex].SOL_PIN, TURNOFF); 
    soilIndex++;
    if (soilIndex >= numPlants ) 
      {changeStateSoil(SS_DELAY);}
    else
      {changeStateSoil(SS_WATERING1);}
    }
}

void ctrlWatering2StateSoil()
{
  int Soil_Moisture = analogRead(myPlants[soilIndex].SOIL_PIN);
  Soil_Moisture = map(Soil_Moisture, myPlants[soilIndex].mapdry, myPlants[soilIndex].mapwet, 0, 100);
  //Serial.print("Plant "); Serial.print(soilIndex); Serial.print(" Moisture = "); Serial.print(Soil_Moisture); Serial.print(" Sensor n. = "); Serial.println(soilIndex);
  if (Soil_Moisture <= myPlants[soilIndex].thresholdint) 
    {
    digitalWrite(myPlants[soilIndex].SOL_PIN, TURNON); 
    changeStateSoil(SS_ENDWATERING2);
    }
  else
    {
    digitalWrite(myPlants[soilIndex].SOL_PIN, TURNOFF); 
    soilIndex++;
    if (soilIndex >= numPlants ) 
      {changeStateSoil(SS_SLEEP);}
    else
      {changeStateSoil(SS_WATERING2);}
    }
  //while (millis() - startMillis <= myPlants[ii].MaxRunTime1);
}

void ctrlEndWatering2StateSoil()
{
  int Soil_Moisture = analogRead(myPlants[soilIndex].SOIL_PIN);
  Soil_Moisture = map(Soil_Moisture, myPlants[soilIndex].mapdry, myPlants[soilIndex].mapwet, 0, 100);
  //Serial.print("Plant "); Serial.print(soilIndex); Serial.print(" Moisture = "); Serial.print(Soil_Moisture); Serial.print(" Sensor n. = "); Serial.println(soilIndex);
  // check soil moisture
  if (Soil_Moisture >= myPlants[soilIndex].thresholdint) 
    {
    digitalWrite(myPlants[soilIndex].SOL_PIN, TURNOFF); 
    soilIndex++;
    if (soilIndex >= numPlants ) 
      {changeStateSoil(SS_SLEEP);}
    else
      {changeStateSoil(SS_WATERING2);}
    }
  // check max time
  if (getStateTimeSoil() >= myPlants[soilIndex].MaxRunTime2) 
    {
    digitalWrite(myPlants[soilIndex].SOL_PIN, TURNOFF); 
    soilIndex++;
    if (soilIndex >= numPlants ) 
      {changeStateSoil(SS_SLEEP);}
    else
      {changeStateSoil(SS_WATERING2);}
    }
}

void ctrlSoil()
{
  switch (stateSoil)
  {
    case SS_INIT:         ctrlInitStateSoil();  break;
    case SS_WATERING1:    ctrlWatering1StateSoil();  break;
    case SS_ENDWATERING1: ctrlEndWatering1StateSoil();  break;
    case SS_DELAY:        ctrlDelayStateSoil(); break;
    case SS_WATERING2:    ctrlWatering2StateSoil(); break;
    case SS_ENDWATERING2: ctrlEndWatering2StateSoil();  break;
    case SS_SLEEP:        ctrlSleepStateSoil();  break;
  }
}


void ctrlTime()
{
  currentMillis = millis(); 
  unsigned long d = currentMillis / 25; 
  if (d != timeMs25Prev )
    {
    tickMs25 = 1; // elapsed 25mS
    timeMs25Prev = d;
    }
  else
    {tickMs25 = 0;}
  d = currentMillis / 100; 
  if (d != timeMs100Prev )
    {
    tickMs100 = 1; // elapsed 100mS
    timeMs100Prev = d;
    }
  else
    {tickMs100 = 0;}
  d = currentMillis / 1000; 
  if (d != timeMs1000Prev )
    {
    tickMs1000 = 1; // elapsed 1000mS
    timeMs1000Prev = d;
    }
  else
    {tickMs1000 = 0;}
}

int32_t getWiFiChannel(const char *ssid) {
  if (int32_t n = WiFi.scanNetworks()) {
    for (uint8_t i=0; i<n; i++) {
      if (!strcmp(ssid, WiFi.SSID(i).c_str())) {
        return WiFi.channel(i);
      }
    }
  }
  return 0;
}

void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
  //Serial.print("\r\nLast Packet Send Status:\t");
  //Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail");
}

//callback function that will be executed when data is received
void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) {
  memcpy(&Hour, incomingData, sizeof(Hour));
  Serial.print("Bytes received: ");
  Serial.println(len);
  Serial.print("x: ");
  Serial.println(Hour.x);
  Serial.println();
}

void setup() {
  // put your setup code here, to run once:
Serial.begin(115200);
delay(1000);

pinMode(PowerPin, OUTPUT);

digitalWrite(PowerPin, HIGH);

analogRead(27);

float batteryLevel = (3.3/4095*(analogRead(27)));

Serial.println(batteryLevel);

WiFi.mode(WIFI_STA);

int32_t channel = getWiFiChannel(WIFI_SSID);

WiFi.printDiag(Serial); // Uncomment to verify channel number before
esp_wifi_set_promiscuous(true);
esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE);
esp_wifi_set_promiscuous(false);
WiFi.printDiag(Serial); // Uncomment to verify channel change after

if (esp_now_init() != ESP_OK) {
  Serial.println("Error initializing ESP-NOW");
  return;
}
esp_now_register_send_cb(OnDataSent);

// Register peer
memcpy(peerInfo.peer_addr, broadcastAddress, 6);
peerInfo.encrypt = false;

// Add peer
if (esp_now_add_peer(&peerInfo) != ESP_OK){
  Serial.println("Failed to add peer");
  return;
}

  Serial.println("Starting Soil Master");
  Serial.print("sensors n. ="); Serial.println(numPlants);

  for (byte ii = 0; ii < numPlants; ii++) {
    pinMode(myPlants[ii].SOL_PIN, OUTPUT);
    digitalWrite(myPlants[ii].SOL_PIN, LOW);
  }
  startMillis = millis();  
  changeStateSoil(SS_INIT);

  esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
  // esp_sleep_enable_ext0_wakeup(WAKEUP_GPIO, 0);  //1 = High, 0 = Low
  // Configure pullup/downs via RTCIO to tie wakeup pins to inactive level during deepsleep.
  // EXT0 resides in the same power domain (RTC_PERIPH) as the RTC IO pullup/downs.
  // No need to keep that power domain explicitly, unlike EXT1.
  // rtc_gpio_pullup_en(WAKEUP_GPIO);
  // rtc_gpio_pulldown_dis(WAKEUP_GPIO);

    esp_now_register_recv_cb(esp_now_recv_cb_t(OnDataRecv));


  }


void loop() {
  // put your main code here, to run repeatedly:

    Moisture1 = analogRead(myPlants[0].SOIL_PIN);
  Moisture1 = map(Moisture1, myPlants[0].mapdry, myPlants[0].mapwet, 0, 100);
  Moisture2 = analogRead(myPlants[1].SOIL_PIN);
  Moisture2 = map(Moisture2, myPlants[1].mapdry, myPlants[1].mapwet, 0, 100);
  Moisture3 = analogRead(myPlants[2].SOIL_PIN);
  Moisture3 = map(Moisture3, myPlants[2].mapdry, myPlants[2].mapwet, 0, 100);
  Moisture4 = analogRead(myPlants[3].SOIL_PIN);
  Moisture4 = map(Moisture4, myPlants[3].mapdry, myPlants[3].mapwet, 0, 100);
  Moisture5 = analogRead(myPlants[4].SOIL_PIN);
  Moisture5 = map(Moisture5, myPlants[4].mapdry, myPlants[5].mapwet, 0, 100);
  Moisture6 = analogRead(myPlants[5].SOIL_PIN);
  Moisture6 = map(Moisture6, myPlants[5].mapdry, myPlants[5].mapwet, 0, 100);

  myData.P1 = Moisture1;
  myData.P2 = Moisture2;
  myData.P3 = Moisture3;
  myData.P4 = Moisture4;
  myData.P5 = Moisture5;
  myData.P6 = Moisture6;

  unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval) {
  // Save the last time a new reading was published
  previousMillis = currentMillis;

      myData.id = BOARD_ID;


// Send message via ESP-NOW
esp_err_t result = esp_now_send(broadcastAddress, (uint8_t *) &myData, sizeof(myData));
if (result == ESP_OK) {
  //Serial.println("Sent with success");
}
else {
  Serial.println("Error sending the data");
}
}
 
 //Serial.println(Hour.x);

ctrlTime();
  if (tickMs25)
    {
    // elaborations need to be done every 25mS
    //readButtons();
    ctrlSoil();
    }
  if (tickMs100)
    {
    // elaborations need to be done every 100mS
    //ctrlLeds();
    }
  if (tickMs1000)
    {
    // elaborations need to be done every second
  //ctrlLcd();
    }
}

I will add data FROM all 4 slaves gets the the Master with no issues

Mark

send your time information as broadcast - all 4 nodes will receive that information. Just don't send it one by one, send to the broadcast MAC.

(And by the way - to get the time on the master via NTP you don't need a 3rd party library. The ESP core comes with all needed code already.)

Would you be so kind as to elaborate/explain (show me what you mean) :slight_smile:

I read before that I can broadcast to 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF.

Is that what you mean?

Mark

could the problem be distance between the nodes?
how far apart are they?
possibly ESP-WiFi-MESH could be a solution?

yes - use the broadcast for general information.

Not at the moment. Very close by for testing

what is your protocol?
does the master poll each slave in turn for its information?
or can the slaves transmit at any time?

Thanks @noiasca Works very quickly now on all boards.

New Codes below..

Mark

1 Like

Master

`#include <TFT_eSPI.h>
#include <WiFi.h>
#include "thingProperties.h"
#include <esp_now.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include "driver/rtc_io.h"
#include <NTPClient.h>
#include <WiFiUdp.h>
#include "time.h"
#include "SPI.h" 
//#include <esp_wifi.h>

int Moisture11;
int Moisture12;
int Moisture13;
int Moisture14;
int Moisture15;
int Moisture16;
int Moisture17;
int Moisture18;
int Moisture19;
int Moisture20;
int Moisture21;
int Moisture22;
int Moisture23;
int Moisture24;

const char* ssid = SECRET_SSID;
const char* password = SECRET_OPTIONAL_PASS;

typedef struct struct_message {
  int id;
  int P1;
  int P2;
  int P3;
  int P4;
  int P5;
  int P6;
  int readingId;
} struct_message;

struct_message incomingReadings;

// Create a structure to hold the readings from each board
struct_message board1;
struct_message board2;
struct_message board3;
struct_message board4;

struct_message boardsStruct[4] = {board1, board2, board3, board4};

void OnDataRecv(const uint8_t * mac_addr, const uint8_t *incomingData, int len) {

// Copies the sender mac address to a string
char macStr[18];
Serial.print("Packet received from: ");
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.println(macStr);

  memcpy(&incomingReadings, incomingData, sizeof(incomingReadings));

  Serial.printf("Board ID %u: %u bytes\n", incomingReadings.id, len);
 // Update the structures with the new incoming data
  boardsStruct[incomingReadings.id-1].P1 = incomingReadings.P1;
  boardsStruct[incomingReadings.id-1].P2 = incomingReadings.P2;
  boardsStruct[incomingReadings.id-1].P3 = incomingReadings.P3;
  boardsStruct[incomingReadings.id-1].P4 = incomingReadings.P4;
  boardsStruct[incomingReadings.id-1].P5 = incomingReadings.P5;
  boardsStruct[incomingReadings.id-1].P6 = incomingReadings.P6;
  //Serial.printf("x value: %d \n", boardsStruct[myData.id-1].P1);
  //Serial.printf("y value: %d \n", boardsStruct[myData.id-1].P2);
  //Serial.printf("x value: %d \n", boardsStruct[myData.id-1].P3);
  //Serial.printf("y value: %d \n", boardsStruct[myData.id-1].P4);
  //Serial.printf("x value: %d \n", boardsStruct[myData.id-1].P5);
  //Serial.printf("y value: %d \n", boardsStruct[myData.id-1].P6);

Serial.println();
}

// REPLACE WITH YOUR RECEIVER MAC Address
uint8_t broadcastAddress[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};

typedef struct test_struct {
  int x;
} test_struct;

test_struct test;

esp_now_peer_info_t peerInfo;

// callback when data is sent
void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
  Serial.print("\r\nLast Packet Send Status:\t");
  Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail");
}
 

TFT_eSPI tft = TFT_eSPI();

#define TFTLEDPIN 32

WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP);

// Variables to save date and time
String formattedDate;
String dayStamp;
String timeStamp;

const int buttonPin = 5;
byte oldButtonState = 0;
int screenNumber = 0;
byte buttonState = 0;

unsigned long lastDebounceTime = 0;  // the last time the output pin was toggled
unsigned long debounceDelay = 200;


  void connect(){
    WiFi.disconnect();

    WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
  delay(1000);
  Serial.println("Setting as a Wi-Fi Station..");
  tft.setCursor(5, 5);
    tft.setTextColor(TFT_WHITE, TFT_BLACK); tft.setTextSize(2);
    tft.println("Wifi Off"); 
		delay(500);
  
}
Serial.print("Station IP Address: ");
Serial.println(WiFi.localIP());
Serial.print("Wi-Fi Channel: ");
Serial.println(WiFi.channel());

    ArduinoCloud.begin(ArduinoIoTPreferredConnection, false);

    while (ArduinoCloud.connected() == 0) 
	{
		ArduinoCloud.update();//required so things don't crash on us
                Serial.println("Waiting for connection to Arduino IoT Cloud");
    tft.setCursor(5, 5);
    tft.setTextColor(TFT_WHITE, TFT_BLACK); tft.setTextSize(2);
    tft.println("Wifi Off"); 
		delay(500);
	}
       Serial.println("Connected to cloud");
  }



void setup() {

  Serial.begin(115200);
  delay(1500); 

  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(TFTLEDPIN, OUTPUT);

  tft.init();
  tft.fillScreen(TFT_BLACK);
  digitalWrite(TFTLEDPIN, HIGH);

  tft.setRotation(1);
  tft.setCursor(110, 20);
  tft.setTextColor(TFT_GREEN);  tft.setTextSize(3);
  tft.println("Moisture Levels");
  tft.setCursor(100, 160);
  tft.setTextColor(TFT_BLUE);  tft.setTextSize(3);
  tft.println("Loading.........");

  // Set the device as a Station and Soft Access Point simultaneously
  WiFi.mode(WIFI_AP_STA);
  
  initProperties();

  connect();

  delay(1000);

    if (esp_now_init() != ESP_OK) {
  Serial.println("Error initializing ESP-NOW");
  return;
}
  esp_now_register_recv_cb(esp_now_recv_cb_t(OnDataRecv));

  timeClient.begin();
  timeClient.setTimeOffset(14400);
  while(!timeClient.update()) {
  timeClient.forceUpdate();
}

  formattedDate = timeClient.getFormattedDate();
  Serial.println(formattedDate);

  tft.fillScreen(TFT_BLACK);

  esp_now_register_send_cb(OnDataSent);

  // Register peer
  memcpy(peerInfo.peer_addr, broadcastAddress, 6);
  peerInfo.channel = 0;  
  peerInfo.encrypt = false;
  
  // Add peer        
  if (esp_now_add_peer(&peerInfo) != ESP_OK){
    Serial.println("Failed to add peer");
    return;
  }
}

void loop() {

  test.x = timeClient.getHours();
  
Serial.println(timeClient.getHours());
  
  //Serial.println(WiFi.status());

  if (WiFi.status() != WL_CONNECTED) {
  connect();
  }

  
  ArduinoCloud.update();

  formattedDate = timeClient.getFormattedDate();
  
  buttonState = digitalRead(buttonPin);

  if ((millis() - lastDebounceTime) > debounceDelay){

  if (buttonState != oldButtonState) {
    // reset the debouncing timer
    lastDebounceTime = millis();
  }

  if (buttonState == LOW) {
    if (buttonState == LOW && screenNumber != 3){
      ++screenNumber;}
    else {
      screenNumber = 0;
    }
    }
    }
  
      oldButtonState = buttonState;
  
  if(buttonState == 0){
      tft.fillScreen(TFT_BLACK);
    }
 // Acess the variables for each board
  int board1P1 = boardsStruct[0].P1;
  int board1P2 = boardsStruct[0].P2;
  int board1P3 = boardsStruct[0].P3;
  int board1P4 = boardsStruct[0].P4;
  int board1P5 = boardsStruct[0].P5;
  int board1P6 = boardsStruct[0].P6;
  int board2P1 = boardsStruct[1].P1;
  int board2P2 = boardsStruct[1].P2;
  int board2P3 = boardsStruct[1].P3;
  int board2P4 = boardsStruct[1].P4;
  int board2P5 = boardsStruct[1].P5;
  int board2P6 = boardsStruct[1].P6;
  int board3P1 = boardsStruct[2].P1;
  int board3P2 = boardsStruct[2].P2;
  int board3P3 = boardsStruct[2].P3;
  int board3P4 = boardsStruct[2].P4;
  int board3P5 = boardsStruct[2].P5;
  int board3P6 = boardsStruct[2].P6; 
  int board4P1 = boardsStruct[3].P1;
  int board4P2 = boardsStruct[3].P2;
  int board4P3 = boardsStruct[3].P3;
  int board4P4 = boardsStruct[3].P4;
  int board4P5 = boardsStruct[3].P5;
  int board4P6 = boardsStruct[3].P6;


  if (screenNumber == 0) {
  tft.setRotation(1);
  tft.setCursor(5, 5);
  tft.setTextColor(TFT_WHITE, TFT_BLACK); tft.setTextSize(2);
  tft.print("Wifi Ok  ");
  tft.setCursor(110, 40);
  tft.setTextColor(TFT_WHITE, TFT_BLACK);  tft.setTextSize(3);
  tft.println("Moisture Levels");
  tft.setCursor(200, 90);
  tft.setTextColor(TFT_GREEN);  tft.setTextSize(2);
  tft.println("STATION 1");  
  tft.setCursor(60, 130);
  tft.setTextColor(TFT_BLUE);  tft.setTextSize(2);
  tft.println("Flowers: "); 
  tft.setCursor(190, 130);
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.print(board1P1);tft.println("  ");
  tft.setCursor(281, 130);
  tft.setTextColor(TFT_BLUE);  tft.setTextSize(2);
  tft.println("Rosemary: "); 
  tft.setCursor(415, 130);
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.print(board1P2);tft.println("  ");
  tft.setCursor(60, 180);
  tft.setTextColor(TFT_BLUE);  tft.setTextSize(2);
  tft.println("Lavender: "); 
  tft.setCursor(190, 180);
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.print(board1P3);tft.println("  ");
  tft.setCursor(281, 180);
  tft.setTextColor(TFT_BLUE);  tft.setTextSize(2);
  tft.println("Mint: "); 
  tft.setCursor(415, 180);
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.print(board1P4);tft.println("  ");
  tft.setCursor(60, 230);
  tft.setTextColor(TFT_BLUE);  tft.setTextSize(2);
  tft.print("Strawberry: ");tft.println("  ");
  tft.setCursor(190, 230);
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.print(board1P5);tft.println("  ");
  tft.setCursor(281, 230);
  tft.setTextColor(TFT_BLUE);  tft.setTextSize(2);
  tft.println("Mint: "); 
  tft.setCursor(415, 230);
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.print(board1P6);tft.println("  ");
  }

  else if (screenNumber == 1) {
  tft.setRotation(1);
  tft.setCursor(5, 5);
  tft.setTextColor(TFT_WHITE, TFT_BLACK); tft.setTextSize(2);
  tft.println("Wifi Ok  ");
  tft.setCursor(110, 40);
  tft.setTextColor(TFT_WHITE, TFT_BLACK);  tft.setTextSize(3);
  tft.println("Moisture Levels");
  tft.setCursor(200, 90);
  tft.setTextColor(TFT_GREEN);  tft.setTextSize(2);
  tft.println("STATION 2");  
  tft.setCursor(60, 130);
  tft.setTextColor(TFT_BLUE);  tft.setTextSize(2);
  tft.println("Flowers: "); 
  tft.setCursor(190, 130);
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.print(board2P1);tft.println("  ");
  tft.setCursor(281, 130);
  tft.setTextColor(TFT_BLUE);  tft.setTextSize(2);
  tft.println("Rosemary: "); 
  tft.setCursor(415, 130);
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.print(board2P2);tft.println("  ");
  tft.setCursor(60, 180);
  tft.setTextColor(TFT_BLUE);  tft.setTextSize(2);
  tft.println("Lavender: "); 
  tft.setCursor(190, 180);
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.print(board2P3);tft.println("  ");
  tft.setCursor(281, 180);
  tft.setTextColor(TFT_BLUE);  tft.setTextSize(2);
  tft.println("Mint: "); 
  tft.setCursor(415, 180);
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.print(board2P4);tft.println("  ");
  tft.setCursor(60, 230);
  tft.setTextColor(TFT_BLUE);  tft.setTextSize(2);
  tft.println("Strawberry: "); 
  tft.setCursor(190, 230);
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.print(board2P5);tft.println("  ");
  tft.setCursor(281, 230);
  tft.setTextColor(TFT_BLUE);  tft.setTextSize(2);
  tft.println("Mint: "); 
  tft.setCursor(415, 230);
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.print(board2P6);tft.println("  ");
  
  
  }
  else if (screenNumber == 2) {
  tft.setRotation(1);
  tft.setCursor(5, 5);
  tft.setTextColor(TFT_WHITE, TFT_BLACK); tft.setTextSize(2);
  tft.println("Wifi Ok  ");
  tft.setCursor(110, 40);
  tft.setTextColor(TFT_WHITE, TFT_BLACK);  tft.setTextSize(3);
  tft.println("Moisture Levels");
  tft.setCursor(200, 90);
  tft.setTextColor(TFT_GREEN);  tft.setTextSize(2);
  tft.println("STATION 3");  
  tft.setCursor(60, 130);
  tft.setTextColor(TFT_BLUE);  tft.setTextSize(2);
  tft.println("Flowers: "); 
  tft.setCursor(190, 130);
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.print(board3P1);tft.println("  ");
  tft.setCursor(281, 130);
  tft.setTextColor(TFT_BLUE);  tft.setTextSize(2);
  tft.println("Rosemary: "); 
  tft.setCursor(415, 130);
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.print(board3P2);tft.println("  ");
  tft.setCursor(60, 180);
  tft.setTextColor(TFT_BLUE);  tft.setTextSize(2);
  tft.println("Lavender: "); 
  tft.setCursor(190, 180);
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.print(board3P3);tft.println("  ");
  tft.setCursor(281, 180);
  tft.setTextColor(TFT_BLUE);  tft.setTextSize(2);
  tft.println("Mint: "); 
  tft.setCursor(415, 180);
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.print(board3P4);tft.println("  ");
  tft.setCursor(60, 230);
  tft.setTextColor(TFT_BLUE);  tft.setTextSize(2);
  tft.println("Strawberry: "); 
  tft.setCursor(190, 230);
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.print(board3P5);tft.println("  ");
  tft.setCursor(281, 230);
  tft.setTextColor(TFT_BLUE);  tft.setTextSize(2);
  tft.println("Mint: "); 
  tft.setCursor(415, 230);
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.print(board3P6);tft.println("  ");  
  }
    
  else if (screenNumber == 3) {
  tft.setRotation(1);
  tft.setCursor(5, 5);
  tft.setTextColor(TFT_WHITE, TFT_BLACK); tft.setTextSize(2);
  tft.println("Wifi Ok  ");
  tft.setCursor(110, 40);
  tft.setTextColor(TFT_WHITE, TFT_BLACK);  tft.setTextSize(3);
  tft.println("Moisture Levels");
  tft.setCursor(200, 90);
  tft.setTextColor(TFT_GREEN);  tft.setTextSize(2);
  tft.println("STATION 4");  
  tft.setCursor(50, 130);
  tft.setTextColor(TFT_BLUE);  tft.setTextSize(2);
  tft.println("Bell Pepper: "); 
  tft.setCursor(190, 130);
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.print(board4P1);tft.println("  ");
  tft.setCursor(281, 130);
  tft.setTextColor(TFT_BLUE);  tft.setTextSize(2);
  tft.println("Bell Pepper: "); 
  tft.setCursor(420, 130);
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.print(board4P2);tft.println("  ");
  tft.setCursor(50, 180);
  tft.setTextColor(TFT_BLUE);  tft.setTextSize(2);
  tft.println("Lemon Grass: "); 
  tft.setCursor(190, 180);
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.print(board4P3);tft.println("  ");
  tft.setCursor(281, 180);
  tft.setTextColor(TFT_BLUE);  tft.setTextSize(2);
  tft.println("Tomato: "); 
  tft.setCursor(420, 180);
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.print(board4P4);tft.println("  ");
  tft.setCursor(50, 230);
  tft.setTextColor(TFT_BLUE);  tft.setTextSize(2);
  tft.println("Mangrove: "); 
  tft.setCursor(190, 230);
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.print(board4P5);tft.println("  ");
  tft.setCursor(281, 230);
  tft.setTextColor(TFT_BLUE);  tft.setTextSize(2);
  tft.println("Tomato: "); 
  tft.setCursor(420, 230);
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.print(board4P6);tft.println("  ");   
  }

  // Send message via ESP-NOW
  esp_err_t result = esp_now_send(broadcastAddress, (uint8_t *) &test, sizeof(test));
   
  if (result == ESP_OK) {
    Serial.println("Sent with success");
  }
  else {
    Serial.println("Error sending the data");
  }


}

`

Slave

#include "WiFi.h"
#include <esp_now.h>
#include <esp_wifi.h>

int Moisture1;
int Moisture2;
int Moisture3;
int Moisture4;
int Moisture5;
int Moisture6;

// Set your Board and Server ID 
#define BOARD_ID 4
//#define MAX_CHANNEL 13  // 11 in North America or 13 in Europe

uint8_t broadcastAddress[] = {0xCC,0x7B,0x5C,0xFC,0x8E,0x8C}; // ap cc:7b:5c:fc:8e:8c

typedef struct struct_message {
  int id;
  int P1;
  int P2;
  int P3;
  int P4;
  int P5;
  int P6;
  int readingId;
} struct_message;

esp_now_peer_info_t peerInfo;

struct_message myData;

unsigned long previousMillis = 0;  // Stores last time temperature was published
const long interval = 500;       // Interval at which to publish sensor readings

unsigned int readingId = 0;

typedef struct test_struct {
  int x;
} test_struct;

//Create a struct_message called myData
test_struct Hour;

constexpr char WIFI_SSID[] = "CornishRes";

const int PowerPin = 17;

String formattedDate;
String dayStamp;
String timeStamp;

unsigned long startMillis;  //some global variables available anywhere in the program
unsigned long currentMillis;

struct plantStation {
  const int SOIL_PIN;
  const int SOL_PIN;
  const int thresholdon;
  const int thresholdint;
  const int thresholdoff;
  const int mapdry;
  const int mapwet;
  const unsigned long MaxRunTime1;
  const unsigned long MaxRunTime2;
};

  const int StartTime1 = 10;
  const int StartTime2 = 13;

plantStation myPlants[6] = {
  { 36, 23, 50, 70, 80, 4095, 800, 10000, 10000 },
  { 39, 22, 50, 70, 80, 4095, 800, 10000, 10000 },
  { 34, 21, 50, 70, 80, 4095, 800, 10000, 10000 },
  { 35, 19, 50, 70, 80, 4095, 800, 10000, 10000 },
  { 32, 18, 50, 70, 80, 4095, 800, 10000, 10000 },
  { 33, 4, 50, 70, 80, 4095, 800, 10000, 10000 },
};

byte numPlants = sizeof(myPlants) / sizeof(*myPlants);

#define TURNON HIGH
#define TURNOFF LOW

int tickMs25 = 0;   // flag
int tickMs100 = 0;  // flag
int tickMs1000 = 0;  // flag
unsigned long timeMs25Prev = 0;
unsigned long timeMs100Prev = 0;
unsigned long timeMs1000Prev = 0;
//int currenthour = myData.x;

enum State {
  SS_INIT,
  SS_WATERING1,
  SS_ENDWATERING1,
  SS_DELAY,
  SS_WATERING2,
  SS_ENDWATERING2,
  SS_SLEEP,
  SS_LAST
};

// used only in debug print ( when changing state )
String stateStrSoil[SS_LAST] = {"Init","Watering1","End Watering1","Delay","Watering2","End Watering2","Sleep"};

State stateSoil = SS_INIT;         // fsm
unsigned long stateTimeSoil = 0;   // fsm

#define INIT_DELAY 5000     // delay at startup ( in milliseconds )
#define DELAY_DELAY 20000    // delay between two cycles ( in milliseconds )

#define uS_TO_S_FACTOR 1000000  /* Conversion factor for micro seconds to seconds */
#define TIME_TO_SLEEP  5
RTC_DATA_ATTR int bootCount = 0;

int soilIndex = 0;

unsigned long getStateTimeSoil()
{
  return millis() - stateTimeSoil;
}

// call this function when you have to change state
void changeStateSoil(State nextState)
{
  Serial.print("State: ");            Serial.print(stateStrSoil[stateSoil]);
  Serial.print(" duration: ");        Serial.println(getStateTimeSoil()/1000.0);
  Serial.print("State change from "); Serial.print(stateStrSoil[stateSoil]);
  Serial.print(" to ");               Serial.print(stateStrSoil[nextState]);
  Serial.print(" Sensor n. = ");      Serial.println(soilIndex);
  
 
  // enable this switch if you have to include any state initialization routine 
  /*switch (stateSoil)
  {
    case SS_INIT:      ctrlInitEntrySoil();  break;
    case SS_WATERING1: ctrlWatering1EntryeSoil();  break;
    case SS_DELAY:     ctrlDelayEntrySoil(); break;
    case SS_WATERING2: ctrlWatering2EntrySoil(); break;
  }*/

  stateTimeSoil = millis();
  stateSoil = nextState;
}

void ctrlInitStateSoil()
{
  Serial.println(Hour.x);
  //Serial.println(currenthour);

{
  if ((getStateTimeSoil() > INIT_DELAY && 1 == StartTime1) || (getStateTimeSoil() > INIT_DELAY && 1 == StartTime2))
    {
    soilIndex = 0;
    changeStateSoil(SS_WATERING1);
    }
  else if (getStateTimeSoil() > INIT_DELAY){
    soilIndex = 0;
      delay(15000);
    changeStateSoil(SS_SLEEP);
  }
}
}

void ctrlDelayStateSoil()
{
  if (getStateTimeSoil() > DELAY_DELAY)
    {
    soilIndex = 0;
    changeStateSoil(SS_WATERING2);
    }
}

void ctrlSleepStateSoil()
{
  Serial.println ("SLEEEEEEEEEEEEEEEEEEEEEEEEEEP");
  esp_deep_sleep_start();

   // restart cycle
  
     //Increment boot number and print it every reboot
  ++bootCount;
  Serial.println("Boot number: " + String(bootCount));
  soilIndex = 0;
  changeStateSoil(SS_WATERING1);
}

void ctrlWatering1StateSoil()
{
  int Soil_Moisture = analogRead(myPlants[soilIndex].SOIL_PIN);
  Soil_Moisture = map(Soil_Moisture, myPlants[soilIndex].mapdry, myPlants[soilIndex].mapwet, 0, 100);
  // Serial.print("Plant "); Serial.print(soilIndex); Serial.print(" Moisture = "); Serial.print(Soil_Moisture); Serial.print(" Sensor n. = "); Serial.println(soilIndex);

  if (Soil_Moisture <= myPlants[soilIndex].thresholdint) 
    {
    digitalWrite(myPlants[soilIndex].SOL_PIN, TURNON); 
    changeStateSoil(SS_ENDWATERING1);
    }
  else
    {
    digitalWrite(myPlants[soilIndex].SOL_PIN, TURNOFF); 
    soilIndex++;
    if (soilIndex >= numPlants ) 
      {changeStateSoil(SS_DELAY);}
    else
      {changeStateSoil(SS_WATERING1);}
    }
  // while (millis() - startMillis <= myPlants[ii].MaxRunTime1);
}

void ctrlEndWatering1StateSoil()
{
  int Soil_Moisture = analogRead(myPlants[soilIndex].SOIL_PIN);
  Soil_Moisture = map(Soil_Moisture, myPlants[soilIndex].mapdry, myPlants[soilIndex].mapwet, 0, 100);
  //Serial.print("Plant "); Serial.print(soilIndex); Serial.print(" Moisture = "); Serial.print(Soil_Moisture); Serial.print(" Sensor n. = "); Serial.println(soilIndex);
  // check soil moisture
  if (Soil_Moisture >= myPlants[soilIndex].thresholdint) 
    {
    digitalWrite(myPlants[soilIndex].SOL_PIN, TURNOFF); 
    soilIndex++;
    if (soilIndex >= numPlants ) 
      {changeStateSoil(SS_DELAY);}
    else
      {changeStateSoil(SS_WATERING1);}
    }
  // check max time
  if (getStateTimeSoil() >= myPlants[soilIndex].MaxRunTime1) 
    {
    digitalWrite(myPlants[soilIndex].SOL_PIN, TURNOFF); 
    soilIndex++;
    if (soilIndex >= numPlants ) 
      {changeStateSoil(SS_DELAY);}
    else
      {changeStateSoil(SS_WATERING1);}
    }
}

void ctrlWatering2StateSoil()
{
  int Soil_Moisture = analogRead(myPlants[soilIndex].SOIL_PIN);
  Soil_Moisture = map(Soil_Moisture, myPlants[soilIndex].mapdry, myPlants[soilIndex].mapwet, 0, 100);
  //Serial.print("Plant "); Serial.print(soilIndex); Serial.print(" Moisture = "); Serial.print(Soil_Moisture); Serial.print(" Sensor n. = "); Serial.println(soilIndex);
  if (Soil_Moisture <= myPlants[soilIndex].thresholdint) 
    {
    digitalWrite(myPlants[soilIndex].SOL_PIN, TURNON); 
    changeStateSoil(SS_ENDWATERING2);
    }
  else
    {
    digitalWrite(myPlants[soilIndex].SOL_PIN, TURNOFF); 
    soilIndex++;
    if (soilIndex >= numPlants ) 
      {changeStateSoil(SS_SLEEP);}
    else
      {changeStateSoil(SS_WATERING2);}
    }
  //while (millis() - startMillis <= myPlants[ii].MaxRunTime1);
}

void ctrlEndWatering2StateSoil()
{
  int Soil_Moisture = analogRead(myPlants[soilIndex].SOIL_PIN);
  Soil_Moisture = map(Soil_Moisture, myPlants[soilIndex].mapdry, myPlants[soilIndex].mapwet, 0, 100);
  //Serial.print("Plant "); Serial.print(soilIndex); Serial.print(" Moisture = "); Serial.print(Soil_Moisture); Serial.print(" Sensor n. = "); Serial.println(soilIndex);
  // check soil moisture
  if (Soil_Moisture >= myPlants[soilIndex].thresholdint) 
    {
    digitalWrite(myPlants[soilIndex].SOL_PIN, TURNOFF); 
    soilIndex++;
    if (soilIndex >= numPlants ) 
      {changeStateSoil(SS_SLEEP);}
    else
      {changeStateSoil(SS_WATERING2);}
    }
  // check max time
  if (getStateTimeSoil() >= myPlants[soilIndex].MaxRunTime2) 
    {
    digitalWrite(myPlants[soilIndex].SOL_PIN, TURNOFF); 
    soilIndex++;
    if (soilIndex >= numPlants ) 
      {changeStateSoil(SS_SLEEP);}
    else
      {changeStateSoil(SS_WATERING2);}
    }
}

void ctrlSoil()
{
  switch (stateSoil)
  {
    case SS_INIT:         ctrlInitStateSoil();  break;
    case SS_WATERING1:    ctrlWatering1StateSoil();  break;
    case SS_ENDWATERING1: ctrlEndWatering1StateSoil();  break;
    case SS_DELAY:        ctrlDelayStateSoil(); break;
    case SS_WATERING2:    ctrlWatering2StateSoil(); break;
    case SS_ENDWATERING2: ctrlEndWatering2StateSoil();  break;
    case SS_SLEEP:        ctrlSleepStateSoil();  break;
  }
}


void ctrlTime()
{
  currentMillis = millis(); 
  unsigned long d = currentMillis / 25; 
  if (d != timeMs25Prev )
    {
    tickMs25 = 1; // elapsed 25mS
    timeMs25Prev = d;
    }
  else
    {tickMs25 = 0;}
  d = currentMillis / 100; 
  if (d != timeMs100Prev )
    {
    tickMs100 = 1; // elapsed 100mS
    timeMs100Prev = d;
    }
  else
    {tickMs100 = 0;}
  d = currentMillis / 1000; 
  if (d != timeMs1000Prev )
    {
    tickMs1000 = 1; // elapsed 1000mS
    timeMs1000Prev = d;
    }
  else
    {tickMs1000 = 0;}
}

int32_t getWiFiChannel(const char *ssid) {
  if (int32_t n = WiFi.scanNetworks()) {
    for (uint8_t i=0; i<n; i++) {
      if (!strcmp(ssid, WiFi.SSID(i).c_str())) {
        return WiFi.channel(i);
      }
    }
  }
  return 0;
}

void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
  //Serial.print("\r\nLast Packet Send Status:\t");
  //Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail");
}

//callback function that will be executed when data is received
void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) {
  memcpy(&Hour, incomingData, sizeof(Hour));
  Serial.print("Bytes received: ");
  Serial.println(len);
  Serial.print("x: ");
  Serial.println(Hour.x);
  Serial.println();
}

void setup() {
  // put your setup code here, to run once:
Serial.begin(115200);
delay(1000);

pinMode(PowerPin, OUTPUT);

digitalWrite(PowerPin, HIGH);

analogRead(27);

float batteryLevel = (3.3/4095*(analogRead(27)));

Serial.println(batteryLevel);

WiFi.mode(WIFI_STA);

int32_t channel = getWiFiChannel(WIFI_SSID);

WiFi.printDiag(Serial); // Uncomment to verify channel number before
esp_wifi_set_promiscuous(true);
esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE);
esp_wifi_set_promiscuous(false);
WiFi.printDiag(Serial); // Uncomment to verify channel change after

if (esp_now_init() != ESP_OK) {
  Serial.println("Error initializing ESP-NOW");
  return;
}
esp_now_register_send_cb(OnDataSent);

// Register peer
memcpy(peerInfo.peer_addr, broadcastAddress, 6);
peerInfo.encrypt = false;

// Add peer
if (esp_now_add_peer(&peerInfo) != ESP_OK){
  Serial.println("Failed to add peer");
  return;
}

  Serial.println("Starting Soil Master");
  Serial.print("sensors n. ="); Serial.println(numPlants);

  for (byte ii = 0; ii < numPlants; ii++) {
    pinMode(myPlants[ii].SOL_PIN, OUTPUT);
    digitalWrite(myPlants[ii].SOL_PIN, LOW);
  }
  startMillis = millis();  
  changeStateSoil(SS_INIT);

  esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
  // esp_sleep_enable_ext0_wakeup(WAKEUP_GPIO, 0);  //1 = High, 0 = Low
  // Configure pullup/downs via RTCIO to tie wakeup pins to inactive level during deepsleep.
  // EXT0 resides in the same power domain (RTC_PERIPH) as the RTC IO pullup/downs.
  // No need to keep that power domain explicitly, unlike EXT1.
  // rtc_gpio_pullup_en(WAKEUP_GPIO);
  // rtc_gpio_pulldown_dis(WAKEUP_GPIO);

    esp_now_register_recv_cb(esp_now_recv_cb_t(OnDataRecv));


  }


void loop() {
  // put your main code here, to run repeatedly:

    Moisture1 = analogRead(myPlants[0].SOIL_PIN);
  Moisture1 = map(Moisture1, myPlants[0].mapdry, myPlants[0].mapwet, 0, 100);
  Moisture2 = analogRead(myPlants[1].SOIL_PIN);
  Moisture2 = map(Moisture2, myPlants[1].mapdry, myPlants[1].mapwet, 0, 100);
  Moisture3 = analogRead(myPlants[2].SOIL_PIN);
  Moisture3 = map(Moisture3, myPlants[2].mapdry, myPlants[2].mapwet, 0, 100);
  Moisture4 = analogRead(myPlants[3].SOIL_PIN);
  Moisture4 = map(Moisture4, myPlants[3].mapdry, myPlants[3].mapwet, 0, 100);
  Moisture5 = analogRead(myPlants[4].SOIL_PIN);
  Moisture5 = map(Moisture5, myPlants[4].mapdry, myPlants[5].mapwet, 0, 100);
  Moisture6 = analogRead(myPlants[5].SOIL_PIN);
  Moisture6 = map(Moisture6, myPlants[5].mapdry, myPlants[5].mapwet, 0, 100);

  myData.P1 = Moisture1;
  myData.P2 = Moisture2;
  myData.P3 = Moisture3;
  myData.P4 = Moisture4;
  myData.P5 = Moisture5;
  myData.P6 = Moisture6;

  unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval) {
  // Save the last time a new reading was published
  previousMillis = currentMillis;

      myData.id = BOARD_ID;


// Send message via ESP-NOW
esp_err_t result = esp_now_send(broadcastAddress, (uint8_t *) &myData, sizeof(myData));
if (result == ESP_OK) {
  //Serial.println("Sent with success");
}
else {
  Serial.println("Error sending the data");
}
}
 
 //Serial.println(Hour.x);

ctrlTime();
  if (tickMs25)
    {
    // elaborations need to be done every 25mS
    //readButtons();
    ctrlSoil();
    }
  if (tickMs100)
    {
    // elaborations need to be done every 100mS
    //ctrlLeds();
    }
  if (tickMs1000)
    {
    // elaborations need to be done every second
  //ctrlLcd();
    }
}

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.