Espnow, cant get it to work

Hello,

i'm stuck trying to get ESPnow communication to work.
My idea is to have 1 Nodemcu to act as a master, to control 10 ESP-01s.

the code i have so far for the sender is:

#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <espnow.h>

const char* ssid = "SquirrelAP";         // Access Point SSID
const char* password = "12345678";       // Access Point password

ESP8266WebServer server(80);

unsigned long timerDuration = 0;
unsigned long timerStartTime = 0;
unsigned long elapsedTime = 0;
unsigned long buttonEndTimes[10][5] = {0};

bool isTimerStarted = false;
bool isRelayOn = false;
String selectedAnimal = "";  // Variable to store the selected animal

// Store the state of each button (true if active, false otherwise)
bool buttonStates[10][5] = {false};  // 10 animals, each with 5 durations

const int relayPin = 0;  // GPIO 0 (connected to a relay, change as per your setup)

struct __attribute__((packed)) espData {
  String animal;
  int duration;
};

// MAC addresses of the 10 ESP8266 devices
uint8_t esp8266Macs[10][6] = {
  {0xEC, 0xFA, 0xBC, 0x27, 0x66, 0x4F},  // Elephant
  {0x12, 0x34, 0x56, 0x78, 0x9A, 0xDE},  // Panda
  {0x12, 0x34, 0x56, 0x78, 0x9A, 0xEF},  // Squirrel
  {0x12, 0x34, 0x56, 0x78, 0x9A, 0xF0},  // DogBrown
  {0x12, 0x34, 0x56, 0x78, 0x9A, 0xF1},  // Sabertooth
  {0x12, 0x34, 0x56, 0x78, 0x9A, 0xF2},  // DogYellow
  {0x12, 0x34, 0x56, 0x78, 0x9A, 0xF3},  // Chipmunk
  {0x12, 0x34, 0x56, 0x78, 0x9A, 0xF4},  // Octopus
  {0xB4, 0xE6, 0x2D, 0x31, 0x3D, 0x67},  // Cow
  {0x12, 0x34, 0x56, 0x78, 0x9A, 0xF5}   // AngryBird
  // Add the MAC addresses for the other 8 ESP8266 devices
};

void OnDataRecv(uint8_t *mac_addr, uint8_t *data, uint8_t len) {
  // Handle data received from ESP8266 devices if needed
  if (len == sizeof(espData)) {
    espData receivedData;
    memcpy(&receivedData, data, sizeof(receivedData));

    Serial.print("Received data from: ");
    for (int i = 0; i < 6; i++) {
      Serial.print(mac_addr[i], HEX);
      if (i < 5) {
        Serial.print(":");
      }
    }
    Serial.println();

    Serial.println("Received Animal: " + receivedData.animal);
    Serial.println("Received Duration: " + String(receivedData.duration));

    // Add your logic to handle the received data as needed
  }
}

void sendCommandToESP8266(String animal, int duration) {
  int animalIndex = -1;
  String animals[] = {"Elephant", "Panda", "Squirrel", "DogBrown", "Sabertooth", "DogYellow", "Chipmunk", "Octopus", "Cow", "AngryBird"};

  for (int i = 0; i < 10; i++) {
    if (animals[i] == animal) {
      animalIndex = i;
      break;
    }
  }

  if (animalIndex != -1) {
    espData data;
    data.animal = animal;
    data.duration = duration;

    // Send data to the corresponding ESP8266
    esp_now_send(esp8266Macs[animalIndex], (uint8_t*)&data, sizeof(data));
  }
}

void handleStartAnimalTimer() {
  String animal = server.arg("animal");
  int duration = server.arg("duration").toInt();

  if (duration > 0) {
    // Send command to the corresponding ESP8266
    sendCommandToESP8266(animal, duration);

    // Local processing
    timerDuration = duration;
    timerStartTime = millis();
    elapsedTime = 0;
    isTimerStarted = true;
    selectedAnimal = animal;  // Update the selected animal
    updateButtonState(animal, duration);  // Set the button state to active
  } else {
    isTimerStarted = false;
  }

  server.sendHeader("Location", "/");
  server.send(302, "text/plain", "");
}

void updateButtonState(String animal, int duration) {
  String animals[] = {"Elephant", "Panda", "Squirrel", "DogBrown", "Sabertooth", "DogYellow", "Chipmunk", "Octopus", "Cow", "AngryBird"};
  int animalIndex = -1;
  for (int i = 0; i < 10; i++) {
    if (animals[i] == animal) {
      animalIndex = i;
      break;
    }
  }

  if (animalIndex != -1) {
    // Reset the state for the animal
    for (int i = 0; i < 5; i++) {
      buttonStates[animalIndex][i] = false;
      buttonEndTimes[animalIndex][i] = 0;  // Reset end times
    }

    // Set the button state to active based on the provided duration
    if (duration == 300) {
      buttonStates[animalIndex][0] = true;
      buttonEndTimes[animalIndex][0] = millis() + duration * 1000;
    } else if (duration == 600) {
      buttonStates[animalIndex][1] = true;
      buttonEndTimes[animalIndex][1] = millis() + duration * 1000;
    } else if (duration == 1200) {
      buttonStates[animalIndex][2] = true;
      buttonEndTimes[animalIndex][2] = millis() + duration * 1000;
    } else if (duration == 1800) {
      buttonStates[animalIndex][3] = true;
      buttonEndTimes[animalIndex][3] = millis() + duration * 1000;
    } else if (duration == 3600) {
      buttonStates[animalIndex][4] = true;
      buttonEndTimes[animalIndex][4] = millis() + duration * 1000;
    }
  }
}

void handleTimer() {
  if (server.method() == HTTP_POST) {
    String durationStr = server.arg("duration");
    timerDuration = durationStr.toInt();
    if (timerDuration > 0) {
      timerStartTime = millis();
      elapsedTime = 0;
      isTimerStarted = true;
    } else {
      isTimerStarted = false;
    }
  }
  server.sendHeader("Location", "/");
  server.send(302);
}

void handleAddThirtySeconds() {
  if (isTimerStarted) {
    unsigned long currentTime = millis();
    unsigned long latestPressTime = 0;

    // Find the latest pressed time among all active buttons
    for (int i = 0; i < 10; i++) {
      for (int j = 0; j < 5; j++) {
        if (buttonStates[i][j] && (buttonEndTimes[i][j] > latestPressTime)) {
          latestPressTime = buttonEndTimes[i][j];
        }
      }
    }

    // Add 30 seconds to all active buttons pressed within 15 seconds of the latest pressed button
    for (int i = 0; i < 10; i++) {
      for (int j = 0; j < 5; j++) {
        if (buttonStates[i][j] && (latestPressTime - buttonEndTimes[i][j] <= 15000)) {
          // Add 30 seconds to the timer for this button
          buttonEndTimes[i][j] += 30 * 1000;  // Update the button end time accordingly
        }
      }
    }

    // Also, update the overall timer duration
    timerDuration += 30;
  }

  server.sendHeader("Location", "/");
  server.send(302);
}

void handleAddOneMinute() {
  if (isTimerStarted) {
    timerDuration += 60; // Add 1 minute (60 seconds) to the timer
  }
  server.sendHeader("Location", "/");
  server.send(302);
}

void handleAddFiveMinutes() {
  if (isTimerStarted) {
    timerDuration += 300; // Add 5 minutes (300 seconds) to the timer
  }
  server.sendHeader("Location", "/");
  server.send(302);
}

void handleStartRelay() {
  isRelayOn = true; // Turn on the relay
  digitalWrite(relayPin, HIGH); // Set the GPIO pin to HIGH to activate the relay
  server.sendHeader("Location", "/");
  server.send(302);
}

void handleStopTimer() {
  timerDuration = 0;
  elapsedTime = 0;
  isTimerStarted = false;
  server.sendHeader("Location", "/");
  server.send(302);
}

void handleRoot() {
  String html = "<html><head><style>";
  html += "body { font-size: 20px; }";
  html += "input[type='submit'] { font-size: 20px; padding: 10px; }";
  html += "table { border-collapse: collapse; width: 100%; }";
  html += "th, td { border: 1px solid black; text-align: center; padding: 15px; }";
  html += ".start-button { background-color: #3498db; color: #fff; }"; // Add a CSS class for the "Start" button
  html += ".active-button { background-color: #2ecc71 !important; color: #fff; }"; // Add a CSS class for active buttons
  html += "</style></head><body>";

  html += "<h1>Manual Time Setting</h1>";
  html += "<form action='/timer' method='POST'>";
  html += "Timer Duration (in seconds): <input type='number' name='duration' min='0'><br><br>";
  html += "<input type='submit' value='Start Timer'>";
  html += "<input type='submit' value='Stop Timer' formaction='/stop-timer'>";
  html += "<input type='submit' value='Start Relay' formaction='/start-relay'>";
  html += "<input type='submit' value='Stop Relay' formaction='/stop-relay'>";
  html += "</form>";

  html += "<h2>Relay Status</h2>";
  html += "<p>Status: " + getRelayStatusHTML() + "</p>";

  html += "<h2>Animal Timers</h2>";
  html += "<table>";
  html += "<tr><th>Animal</th><th>5 Minutes</th><th>10 Minutes</th><th>20 Minutes</th><th>30 Minutes</th><th>60 Minutes</th></tr>";

  String animals[] = {"Elephant", "Panda", "Squirrel", "DogBrown", "Sabertooth", "DogYellow", "Chipmunk", "Octopus", "Cow", "AngryBird"};
  for (int i = 0; i < 10; i++) {
    html += "<tr><td>" + animals[i] + "</td>";

    int durations[] = {5, 10, 20, 30, 60};
    for (int j = 0; j < 5; j++) {
      html += "<td><form class='start-form' action='/start-animal-timer' method='POST'>";
      html += "<input type='hidden' name='animal' value='" + animals[i] + "'>";
      html += "<input type='hidden' name='duration' value='" + String(durations[j] * 60) + "'>";  // Convert to seconds
      html += "<input class='start-button";
      if (buttonStates[i][j]) {
        html += " active-button";  // Add active class if the button is active
      }
      html += "' type='submit' value='Start'></form></td>";
    }

    html += "</tr>";
  }

  html += "</table>";

  html += "<h2>Add Time</h2>";
  html += "<form action='/add-thirty-seconds' method='POST'>";
  html += "<input type='submit' value='Add 30 Seconds'>";
  html += "</form>";

  html += "<form action='/add-one-minute' method='POST'>";
  html += "<input type='submit' value='Add 1 Minute'>";
  html += "</form>";

  html += "<form action='/add-five-minutes' method='POST'>";
  html += "<input type='submit' value='Add 5 Minutes'>";
  html += "</form>";

  html += "<h2>Timer Information</h2>";
  html += "<p>Duration: " + String(timerDuration / 60) + " minutes</p>";  // Convert to minutes
  if (isTimerStarted) {
    html += "<p>Time Elapsed: " + String(getElapsedTime()) + " seconds</p>";
    html += "<p>Time Remaining: " + String(getRemainingMinutes()) + " minutes " + String(getRemainingSeconds()) + " seconds</p>";
    html += "<p>Selected Animal: " + selectedAnimal + "</p>";  // Display selected animal
  } else {
    html += "<p>Time Elapsed: Not started</p>";
    html += "<p>Time Remaining: Not started</p>";
    html += "<p>Selected Animal: Not selected</p>";
  }

  html += "<script>";
  html += "document.addEventListener('DOMContentLoaded', function() {";
  html += "  var startButtons = document.querySelectorAll('.start-form');";
  html += "  startButtons.forEach(function(form) {";
  html += "    form.addEventListener('submit', function(event) {";
  html += "      event.preventDefault();";  // Prevent the default form submission
  html += "      var buttons = form.querySelectorAll('.start-button');";
  html += "      buttons.forEach(function(button) {";
  html += "        button.classList.remove('active-button');"; // Remove active class from all buttons
  html += "      });";
  html += "      form.querySelector('.start-button').classList.add('active-button');"; // Add active class to the pressed button
  html += "      setTimeout(function() {";
  html += "      var duration = " + String(timerDuration * 1000) + ";";  // Timer duration in milliseconds
  html += "      form.querySelector('.start-button').classList.add('active-button');"; // Add active class to the pressed button
  html += "      setTimeout(function() {";
  html += "        form.querySelector('.start-button').classList.remove('active-button');"; // Remove active class after the timer duration
  html += "      }, duration);";
  html += "    });";
  html += "  });";
  html += "});";
  html += "</script>";

  html += "</body></html>";
  server.send(200, "text/html", html);
}

void handleStopRelay() {
  isRelayOn = false; // Turn off the relay
  digitalWrite(relayPin, LOW); // Set the GPIO pin to LOW to deactivate the relay
  server.sendHeader("Location", "/");
  server.send(302);
}

void handleNotFound() {
  server.send(404, "text/plain", "Not found");
}

unsigned long getElapsedTime() {
  unsigned long currentTime = millis();
  elapsedTime = (currentTime - timerStartTime) / 1000;
  return elapsedTime;
}

long getRemainingTime() {
  long remainingTime = timerDuration - getElapsedTime();
  if (remainingTime < 0) {
    remainingTime = 0;
  }
  return remainingTime;
}

int getRemainingMinutes() {
  int remainingMinutes = getRemainingTime() / 60;
  return remainingMinutes;
}

int getRemainingSeconds() {
  int remainingSeconds = getRemainingTime() % 60;
  return remainingSeconds;
}

String getRelayStatusHTML() {
  if (isRelayOn) {
    return "<span style='color: green;'>ON</span>";
  } else {
    return "<span style='color: red;'>OFF</span>";
  }
}

void setup() {
  Serial.begin(115200);

  pinMode(relayPin, OUTPUT);
  digitalWrite(relayPin, LOW);  // Initially turn off the relay

  WiFi.mode(WIFI_AP);
  WiFi.softAP(ssid, password);

  IPAddress apIP = WiFi.softAPIP();
  Serial.print("Access Point IP address: ");
  Serial.println(apIP);

  server.on("/", HTTP_GET, handleRoot);
  server.on("/timer", HTTP_POST, handleTimer);
  server.on("/add-thirty-seconds", HTTP_POST, handleAddThirtySeconds);
  server.on("/add-one-minute", HTTP_POST, handleAddOneMinute);
  server.on("/add-five-minutes", HTTP_POST, handleAddFiveMinutes);
  server.on("/start-relay", HTTP_POST, handleStartRelay);
  server.on("/stop-timer", HTTP_POST, handleStopTimer);
  server.on("/stop-relay", HTTP_POST, handleStopRelay);
  server.on("/start-animal-timer", HTTP_POST, handleStartAnimalTimer);

  server.onNotFound(handleNotFound);

  server.begin();
  Serial.println("HTTP server started");

  // Initialize ESP-NOW
  if (esp_now_init() != 0) {
    Serial.println("ESP-NOW initialization failed");
    // Handle initialization failure
  } else {
    Serial.println("ESP-NOW initialization successful");
  }

  // Register the callback function for receiving data
  esp_now_register_recv_cb(OnDataRecv);
}

void loop() {
  server.handleClient();

  unsigned long currentTime = millis();  // Moved variable declaration here

  if (isTimerStarted && !isRelayOn) {
    for (int i = 0; i < 10; i++) {
      for (int j = 0; j < 5; j++) {
        if (buttonStates[i][j] && currentTime >= buttonEndTimes[i][j]) {
          buttonStates[i][j] = false;  // Reset the button state when the timer expires
        }
      }
    }

    if (currentTime - timerStartTime >= (timerDuration * 1000)) {
      Serial.println("Timer expired!");
      timerDuration = 0;
      isTimerStarted = false;
      isRelayOn = true; // Turn on the relay after the timer expires
      digitalWrite(relayPin, HIGH); // Set the GPIO pin to HIGH to activate the relay
    }
  }
}

And for the receiver i have:

#include <ESP8266WiFi.h>
#include <espnow.h>

uint8_t cowMac[] = {0xEC, 0xFA, 0xBC, 0x27, 0x66, 0x4F};
const int relayPin = 0;  // GPIO 0 (connected to a relay, change as per your setup)

// Highlighted part to add sender's MAC address
uint8_t senderMac[] = {0xBC, 0xDD, 0xC2, 0x9D, 0x29, 0xE2};  // Replace this with your sender's MAC address

struct __attribute__((packed)) espData {
  String animal;
  int duration;
};

unsigned long timerDuration = 0;
unsigned long timerStartTime = 0;
unsigned long elapsedTime = 0;

void startTimer(int duration) {
  timerDuration = duration;
  timerStartTime = millis();
  Serial.println("Timer started. Duration: " + String(timerDuration) + " seconds");
  digitalWrite(relayPin, LOW);  // Set the GPIO pin to LOW when the timer starts
}

void stopTimer() {
  timerDuration = 0;
  Serial.println("Timer stopped");
  digitalWrite(relayPin, HIGH);  // Set the GPIO pin to HIGH when the timer stops
}

void OnDataRecv(uint8_t *mac_addr, uint8_t *data, uint8_t len) {
  Serial.println("Data received!");

  if (len == sizeof(espData)) {
    espData receivedData;
    memcpy(&receivedData, data, sizeof(receivedData));

    Serial.println("Received data from: ");
    for (int i = 0; i < 6; i++) {
      Serial.println(mac_addr[i], HEX);
      if (i < 5) {
        Serial.println(":");
      }
    }
    Serial.println();

    Serial.println("Received Animal: " + receivedData.animal);
    Serial.println("Received Duration: " + String(receivedData.duration));

    // Set timer based on received duration
    startTimer(receivedData.duration);

    // Add your additional logic based on the received data

    // Print a message indicating that an instruction has been received
    Serial.println("Instruction received from sender!");
  }
}
void setup() {
  Serial.begin(115200);  // Initialize serial communication
  delay(10000);  // Delay for 10 seconds
  Serial.println("Initializing...");  // Print initializing message
  pinMode(relayPin, OUTPUT);
  digitalWrite(relayPin, LOW);  // Initially turn off the relay

  if (esp_now_init() != 0) {
    Serial.println("ESP-NOW initialization failed");
  } else {
    Serial.println("ESP-NOW initialization successful");

    // Add the sender's MAC address as a peer
    esp_now_add_peer(senderMac, ESP_NOW_ROLE_SLAVE, 1, NULL, 0);
  }

  esp_now_register_recv_cb(OnDataRecv);
}

void loop() {
  // Your main code goes here
  // This loop can be used for other tasks in your program

  // Check timer status
  if (timerDuration > 0) {
    unsigned long currentTime = millis();
    elapsedTime = (currentTime - timerStartTime) / 1000;

    if (elapsedTime >= timerDuration) {
      // Timer expired
      stopTimer();
    } else {
      // Timer still running
      Serial.println("Time remaining: " + String(timerDuration - elapsedTime) + " seconds");
    }
  }
}

Can someone see what is going wrong?

can't use that String object in you struc..
make it a fixed char animal[20];// 20 can be adjusted to largest size plus 1 for ending null..

don't know too much about esp now though..

good luck.. ~q

i changed it but it still doesn't work,

Interesting new development, sorry..
But really doesn't give me much..

Still trying to wrap my head about what you're trying to accomplish..
Looks like you're using a webserver hosted on one to drive the rest??
If yes and as I no nothing about espnow, I would recommend to use udp to control the mob..
Can send to all at once with a single broadcast..
Got a couple of demos, compiles for esp32..
UDP Sender..
UDP Receiver..

Maybe it helps..

good luck.. ~q

so let me paint the picture. I have a business where i have 10 ride on animals, currently all 10 animals have a Esp-01 build into them that launches a web interface, in which i can set a timer that will trigger a relay that will kill the power to the animal when time runs out.

Each animal creates there own acces point i have to connect and disconnect to if i want to start up a different animals. As an example, i connect to CowAP go to the web interface and start a timer. Now someone hires the Dog, now i have to disconnect from the CowAP and log into the DogAP.

With Espow, i could connect to one MasterESP that will create a webinterface with control functions. The web interface seems to work it creates an AP with all 10 animals listed, with all the available timers behind them. Now, i want that one MasterESP to send the button combination with timers attached to them to the correct animal, hence espnow communication.

You mentioned send to all, i need to send different commands to different animals, like 5 minute timer to Cow, 10 minute to Dog, 30 minutes to Elephant.

i prefer the espnow as it does not use wifi.

when i run the code into chatgpt, it doesnt find any erros, im not sure if its lacking the ability to detect them.
the updated code by now is:
Sender:

#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <espnow.h>

int espNowChannel = 1;  // Set the desired communication channel
uint8_t espNowKey[16] = {0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF, 0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF};  // Set your encryption key
const char* ssid = "SquirrelAP";         // Access Point SSID
const char* password = "12345678";       // Access Point password
const int onboardLedPin = 2;  // GPIO 2

ESP8266WebServer server(80);

unsigned long timerDuration = 0;
unsigned long timerStartTime = 0;
unsigned long elapsedTime = 0;
unsigned long buttonEndTimes[10][5] = {0};

bool isTimerStarted = false;
bool isRelayOn = false;
String selectedAnimal = "";  // Variable to store the selected animal

// Store the state of each button (true if active, false otherwise)
bool buttonStates[10][5] = {false};  // 10 animals, each with 5 durations

const int relayPin = 0;  // GPIO 0 (connected to a relay, change as per your setup)

// Macros for button durations
const int DURATION_5_MINUTES = 300;
const int DURATION_10_MINUTES = 600;
const int DURATION_20_MINUTES = 1200;
const int DURATION_30_MINUTES = 1800;
const int DURATION_60_MINUTES = 3600;

struct __attribute__((packed)) espData {
  char animal[20];  // Adjust the size based on the maximum length of the animal name
  int duration;
};

// MAC addresses of the 10 ESP8266 devices
uint8_t esp8266Macs[10][6] = {
  {0xEC, 0xFA, 0xBC, 0x27, 0x66, 0x4F},  // Elephant
  {0x12, 0x34, 0x56, 0x78, 0x9A, 0xDE},  // Panda
  {0x12, 0x34, 0x56, 0x78, 0x9A, 0xEF},  // Squirrel
  {0x12, 0x34, 0x56, 0x78, 0x9A, 0xF0},  // DogBrown
  {0x12, 0x34, 0x56, 0x78, 0x9A, 0xF1},  // Sabertooth
  {0x12, 0x34, 0x56, 0x78, 0x9A, 0xF2},  // DogYellow
  {0x12, 0x34, 0x56, 0x78, 0x9A, 0xF3},  // Chipmunk
  {0x12, 0x34, 0x56, 0x78, 0x9A, 0xF4},  // Octopus
  {0xB4, 0xE6, 0x2D, 0x31, 0x3D, 0x67},  // Cow
  {0x12, 0x34, 0x56, 0x78, 0x9A, 0xF5}   // AngryBird
  // Add the MAC addresses for the other 8 ESP8266 devices
};

void OnDataRecv(uint8_t *mac_addr, uint8_t *data, uint8_t len) {
    // Handle data received from ESP8266 devices if needed
    if (len == sizeof(espData)) {
        espData receivedData;
        memcpy(&receivedData, data, sizeof(receivedData));

        Serial.print("Received data from: ");
        for (int i = 0; i < 6; i++) {
            Serial.print(mac_addr[i], HEX);
            if (i < 5) {
                Serial.print(":");
            }
        }
        Serial.println();

        // Convert the MAC address to a String before concatenating
        String macAddressStr;
        for (int i = 0; i < 6; i++) {
            macAddressStr += String(mac_addr[i], HEX);
            if (i < 5) {
                macAddressStr += ":";
            }
        }

        Serial.println("Received Animal: " + String(receivedData.animal));
        Serial.println("Received Duration: " + String(receivedData.duration));
        Serial.println("Received MAC Address: " + macAddressStr);  // Use the converted MAC address String
    } else {
        Serial.println("Invalid data length received");
    }
}

void sendCommandToESP8266(String animal, int duration) {
  int animalIndex = -1;
  String animals[] = {"Elephant", "Panda", "Squirrel", "DogBrown", "Sabertooth", "DogYellow", "Chipmunk", "Octopus", "Cow", "AngryBird"};

  for (int i = 0; i < 10; i++) {
    if (animals[i] == animal) {
      animalIndex = i;
      break;
    }
  }

  if (animalIndex != -1) {
    espData data;

    // Convert String to char array
    animal.toCharArray(data.animal, sizeof(data.animal));
    data.duration = duration;

    // Turn on the onboard LED to indicate data transmission
    digitalWrite(onboardLedPin, HIGH);

    // Send data to the corresponding ESP8266
    if (esp_now_send(esp8266Macs[animalIndex], (uint8_t*)&data, sizeof(data)) != 0) {
      Serial.println("Failed to send data via ESP-NOW");
      // Handle send failure
    }

    // Wait for a short time to make the LED blink noticeable
    delay(500);

    // Turn off the onboard LED
    digitalWrite(onboardLedPin, LOW);
  }
}

void handleStartAnimalTimer() {
  String animal = server.arg("animal");
  int duration = server.arg("duration").toInt();

  if (duration > 0) {
    // Send command to the corresponding ESP8266
    sendCommandToESP8266(animal, duration);

    // Local processing
    timerDuration = duration;
    timerStartTime = millis();
    elapsedTime = 0;
    isTimerStarted = true;
    selectedAnimal = animal;  // Update the selected animal
    updateButtonState(animal, duration);  // Set the button state to active
  } else {
    isTimerStarted = false;
  }

  server.sendHeader("Location", "/");
  server.send(302, "text/plain", "");
}

void updateButtonState(String animal, int duration) {
  String animals[] = {"Elephant", "Panda", "Squirrel", "DogBrown", "Sabertooth", "DogYellow", "Chipmunk", "Octopus", "Cow", "AngryBird"};
  int animalIndex = -1;
  for (int i = 0; i < 10; i++) {
    if (animals[i] == animal) {
      animalIndex = i;
      break;
    }
  }

  if (animalIndex != -1) {
    // Reset the state for the animal
    for (int i = 0; i < 5; i++) {
      buttonStates[animalIndex][i] = false;
      buttonEndTimes[animalIndex][i] = 0;  // Reset end times
    }

    // Set the button state to active based on the provided duration
    if (duration == DURATION_5_MINUTES) {
      buttonStates[animalIndex][0] = true;
      buttonEndTimes[animalIndex][0] = millis() + duration * 1000;
    } else if (duration == DURATION_10_MINUTES) {
      buttonStates[animalIndex][1] = true;
      buttonEndTimes[animalIndex][1] = millis() + duration * 1000;
    } else if (duration == DURATION_20_MINUTES) {
      buttonStates[animalIndex][2] = true;
      buttonEndTimes[animalIndex][2] = millis() + duration * 1000;
    } else if (duration == DURATION_30_MINUTES) {
      buttonStates[animalIndex][3] = true;
      buttonEndTimes[animalIndex][3] = millis() + duration * 1000;
    } else if (duration == DURATION_60_MINUTES) {
      buttonStates[animalIndex][4] = true;
      buttonEndTimes[animalIndex][4] = millis() + duration * 1000;
    }
  }
}

void handleTimer() {
  if (server.method() == HTTP_POST) {
    String durationStr = server.arg("duration");
    timerDuration = durationStr.toInt();
    if (timerDuration > 0) {
      timerStartTime = millis();
      elapsedTime = 0;
      isTimerStarted = true;
    } else {
      isTimerStarted = false;
    }
  }
  server.sendHeader("Location", "/");
  server.send(302);
}

void handleAddThirtySeconds() {
  if (isTimerStarted) {
    unsigned long currentTime = millis();
    unsigned long latestPressTime = 0;

    // Find the latest pressed time among all active buttons
    for (int i = 0; i < 10; i++) {
      for (int j = 0; j < 5; j++) {
        if (buttonStates[i][j] && (buttonEndTimes[i][j] > latestPressTime)) {
          latestPressTime = buttonEndTimes[i][j];
        }
      }
    }

    // Add 30 seconds to all active buttons pressed within 15 seconds of the latest pressed button
    for (int i = 0; i < 10; i++) {
      for (int j = 0; j < 5; j++) {
        if (buttonStates[i][j] && (latestPressTime - buttonEndTimes[i][j] <= 15000)) {
          // Add 30 seconds to the timer for this button
          buttonEndTimes[i][j] += 30 * 1000;  // Update the button end time accordingly
        }
      }
    }

    // Also, update the overall timer duration
    timerDuration += 30;
  }

  server.sendHeader("Location", "/");
  server.send(302);
}

void handleAddOneMinute() {
  if (isTimerStarted) {
    timerDuration += 60; // Add 1 minute (60 seconds) to the timer
  }
  server.sendHeader("Location", "/");
  server.send(302);
}

void handleAddFiveMinutes() {
  if (isTimerStarted) {
    timerDuration += 300; // Add 5 minutes (300 seconds) to the timer
  }
  server.sendHeader("Location", "/");
  server.send(302);
}

void handleStartRelay() {
  isRelayOn = true; // Turn on the relay
  digitalWrite(relayPin, HIGH); // Set the GPIO pin to HIGH to activate the relay
  server.sendHeader("Location", "/");
  server.send(302);
}

void handleStopTimer() {
  timerDuration = 0;
  elapsedTime = 0;
  isTimerStarted = false;
  server.sendHeader("Location", "/");
  server.send(302);
}

void handleRoot() {
  String html = "<html><head><style>";
  html += "body { font-size: 20px; }";
  html += "input[type='submit'] { font-size: 20px; padding: 10px; }";
  html += "table { border-collapse: collapse; width: 100%; }";
  html += "th, td { border: 1px solid black; text-align: center; padding: 15px; }";
  html += ".start-button { background-color: #3498db; color: #fff; }"; 
  html += ".active-button { background-color: #2ecc71 !important; color: #fff; }"; 
  html += "</style></head><body>";

  html += "<h1>Manual Time Setting</h1>";
  html += "<form action='/timer' method='POST'>";
  html += "Timer Duration (in seconds): <input type='number' name='duration' min='0'><br><br>";
  html += "<input type='submit' value='Start Timer'>";
  html += "<input type='submit' value='Stop Timer' formaction='/stop-timer'>";
  html += "<input type='submit' value='Start Relay' formaction='/start-relay'>";
  html += "<input type='submit' value='Stop Relay' formaction='/stop-relay'>";
  html += "</form>";

  html += "<h2>Relay Status</h2>";
  html += "<p>Status: " + getRelayStatusHTML() + "</p>";

  html += "<h2>Animal Timers</h2>";
  html += "<table>";
  html += "<tr><th>Animal</th><th>5 Minutes</th><th>10 Minutes</th><th>20 Minutes</th><th>30 Minutes</th><th>60 Minutes</th></tr>";

  String animals[] = {"Elephant", "Panda", "Squirrel", "DogBrown", "Sabertooth", "DogYellow", "Chipmunk", "Octopus", "Cow", "AngryBird"};
  for (int i = 0; i < 10; i++) {
    html += "<tr><td>" + animals[i] + "</td>";

    int durations[] = {5, 10, 20, 30, 60};
    for (int j = 0; j < 5; j++) {
      html += "<td><form class='start-form' action='/start-animal-timer' method='POST'>";
      html += "<input type='hidden' name='animal' value='" + animals[i] + "'>";
      html += "<input type='hidden' name='duration' value='" + String(durations[j] * 60) + "'>";  
      html += "<input class='start-button";
      if (buttonStates[i][j]) {
        html += " active-button";  
      }
      html += "' type='submit' value='Start'></form></td>";
    }

    html += "</tr>";
  }

  html += "</table>";

  html += "<h2>Add Time</h2>";
  html += "<form action='/add-thirty-seconds' method='POST'>";
  html += "<input type='submit' value='Add 30 Seconds'>";
  html += "</form>";

  html += "<form action='/add-one-minute' method='POST'>";
  html += "<input type='submit' value='Add 1 Minute'>";
  html += "</form>";

  html += "<form action='/add-five-minutes' method='POST'>";
  html += "<input type='submit' value='Add 5 Minutes'>";
  html += "</form>";

  html += "<h2>Timer Information</h2>";
  html += "<p>Duration: " + String(timerDuration / 60) + " minutes</p>";  
  if (isTimerStarted) {
    html += "<p>Time Elapsed: " + String(getElapsedTime()) + " seconds</p>";
    html += "<p>Time Remaining: " + String(getRemainingMinutes()) + " minutes " + String(getRemainingSeconds()) + " seconds</p>";
    html += "<p>Selected Animal: " + selectedAnimal + "</p>";  
  } else {
    html += "<p>Time Elapsed: Not started</p>";
    html += "<p>Time Remaining: Not started</p>";
    html += "<p>Selected Animal: Not selected</p>";
  }

  html += "<script>";
  html += "document.addEventListener('DOMContentLoaded', function() {";
  html += "  var startButtons = document.querySelectorAll('.start-form');";
  html += "  startButtons.forEach(function(form) {";
  html += "    form.addEventListener('submit', function(event) {";
  html += "      event.preventDefault();";  
  html += "      var buttons = form.querySelectorAll('.start-button');";
  html += "      buttons.forEach(function(button) {";
  html += "        button.classList.remove('active-button');"; 
  html += "      });";
  html += "      form.querySelector('.start-button').classList.add('active-button');"; 
  html += "      setTimeout(function() {";
  html += "      var duration = " + String(timerDuration * 1000) + ";";  
  html += "      form.querySelector('.start-button').classList.add('active-button');"; 
  html += "      setTimeout(function() {";
  html += "        form.querySelector('.start-button').classList.remove('active-button');"; 
  html += "      }, duration);";
  html += "    });";
  html += "  });";
  html += "});";
  html += "</script>";

  html += "</body></html>";
  server.send(200, "text/html", html);
}

void handleStopRelay() {
  isRelayOn = false; 
  digitalWrite(relayPin, LOW); 
  server.sendHeader("Location", "/");
  server.send(302);
}

void handleNotFound() {
  server.send(404, "text/plain", "Not found");
}

unsigned long getElapsedTime() {
  unsigned long currentTime = millis();
  elapsedTime = (currentTime - timerStartTime) / 1000;
  return elapsedTime;
}

long getRemainingTime() {
  long remainingTime = timerDuration - getElapsedTime();
  if (remainingTime < 0) {
    remainingTime = 0;
  }
  return remainingTime;
}

int getRemainingMinutes() {
  int remainingMinutes = getRemainingTime() / 60;
  return remainingMinutes;
}

int getRemainingSeconds() {
  int remainingSeconds = getRemainingTime() % 60;
  return remainingSeconds;
}

String getRelayStatusHTML() {
  if (isRelayOn) {
    return "<span style='color: green;'>ON</span>";
  } else {
    return "<span style='color: red;'>OFF</span>";
  }
}

void setup() {
  Serial.begin(115200);

  pinMode(relayPin, OUTPUT);
  digitalWrite(relayPin, LOW);  

  WiFi.mode(WIFI_AP);
  WiFi.softAP(ssid, password);

  IPAddress apIP = WiFi.softAPIP();
  Serial.print("Access Point IP address: ");
  Serial.println(apIP);

  pinMode(onboardLedPin, OUTPUT);
  digitalWrite(onboardLedPin, LOW);  

    // Set ESP-NOW channel
    wifi_set_channel(espNowChannel);

    uint8_t key[16] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F};
    esp_now_add_peer(esp8266Macs[0], ESP_NOW_ROLE_COMBO, 1, key, 16);

  server.on("/", HTTP_GET, handleRoot);
  server.on("/timer", HTTP_POST, handleTimer);
  server.on("/add-thirty-seconds", HTTP_POST, handleAddThirtySeconds);
  server.on("/add-one-minute", HTTP_POST, handleAddOneMinute);
  server.on("/add-five-minutes", HTTP_POST, handleAddFiveMinutes);
  server.on("/start-relay", HTTP_POST, handleStartRelay);
  server.on("/stop-timer", HTTP_POST, handleStopTimer);
  server.on("/stop-relay", HTTP_POST, handleStopRelay);
  server.on("/start-animal-timer", HTTP_POST, handleStartAnimalTimer);

  server.onNotFound(handleNotFound);

  server.begin();
  Serial.println("HTTP server started");

  if (esp_now_init() != 0) {
    Serial.println("ESP-NOW initialization failed");
  } else {
    Serial.println("ESP-NOW initialization successful");
  }

  esp_now_register_recv_cb(OnDataRecv);
}

void loop() {
  server.handleClient();

  unsigned long currentTime = millis();  

  if (isTimerStarted && !isRelayOn) {
    for (int i = 0; i < 10; i++) {
      for (int j = 0; j < 5; j++) {
        if (buttonStates[i][j] && currentTime >= buttonEndTimes[i][j]) {
          buttonStates[i][j] = false;  
        }
      }
    }

    if (currentTime - timerStartTime >= (timerDuration * 1000)) {
      Serial.println("Timer expired!");
      timerDuration = 0;
      isTimerStarted = false;
      isRelayOn = true; 
      digitalWrite(relayPin, HIGH); 
    }
  }
}

Receiver:

#include <ESP8266WiFi.h>
#include <espnow.h>

uint8_t cowMac[] = {0xB4, 0xE6, 0x2D, 0x31, 0x3D, 0x67};
const int relayPin = 0;  // GPIO 0 (connected to a relay, change as per your setup)
const int onboardLedPin = LED_BUILTIN;  // Built-in LED pin on NodeMCU

// Highlighted part to add sender's MAC address
uint8_t senderMac[] = {0xBC, 0xDD, 0xC2, 0x9D, 0x29, 0xE2};  // Replace this with your sender's MAC address
int espNowChannel = 1;  // Specify the ESP-NOW channel

struct __attribute__((packed)) espData {
  char animal[20];  // Adjust the size based on your maximum expected length
  int duration;
};

unsigned long timerDuration = 0;
unsigned long timerStartTime = 0;
unsigned long elapsedTime = 0;

void startTimer(int duration) {
  timerDuration = duration;
  timerStartTime = millis();
  Serial.println("Timer started. Duration: " + String(timerDuration) + " seconds");
  digitalWrite(relayPin, LOW);  // Set the GPIO pin to LOW when the timer starts
}

void stopTimer() {
  timerDuration = 0;
  Serial.println("Timer stopped");
  digitalWrite(relayPin, HIGH);  // Set the GPIO pin to HIGH when the timer stops
}

void OnDataRecv(uint8_t *mac_addr, uint8_t *data, uint8_t len) {
    // Handle data received from ESP8266 devices if needed
    if (len == sizeof(espData)) {
        espData receivedData;
        memcpy(&receivedData, data, sizeof(receivedData));

        Serial.print("Received data from MAC: ");
        for (int i = 0; i < 6; i++) {
            Serial.print(mac_addr[i], HEX);
            if (i < 5) {
                Serial.print(":");
            }
        }
        Serial.println();

        // Convert the MAC address to a String before concatenating
        String macAddressStr;
        for (int i = 0; i < 6; i++) {
            macAddressStr += String(mac_addr[i], HEX);
            if (i < 5) {
                macAddressStr += ":";
            }
        }

        Serial.println("Received Animal: " + String(receivedData.animal));
        Serial.println("Received Duration: " + String(receivedData.duration));
        Serial.println("Received MAC Address: " + macAddressStr);  // Use the converted MAC address String
    } else {
        Serial.println("Invalid data length received");
    }
}

void setup() {
  Serial.begin(115200);  // Initialize serial communication
  delay(10000);  // Delay for 10 seconds
  Serial.println("Initializing...");  // Print initializing message
  pinMode(relayPin, OUTPUT);
  digitalWrite(relayPin, LOW);  // Initially turn off the relay
  pinMode(onboardLedPin, OUTPUT);
  digitalWrite(onboardLedPin, LOW);  // Initially turn off the onboard LED

  if (esp_now_init() != 0) {
    Serial.println("ESP-NOW initialization failed");
  } else {
    Serial.println("ESP-NOW initialization successful");

    // Set ESP-NOW channel
    wifi_set_channel(espNowChannel);

    // Set ESP-NOW peer encryption key
    uint8_t key[16] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F};
    esp_now_add_peer(senderMac, ESP_NOW_ROLE_SLAVE, 1, key, 16);

    // Register callback function for receiving data
    esp_now_register_recv_cb(OnDataRecv);
  }
}

void loop() {
  // Your main code goes here
  // This loop can be used for other tasks in your program

  // Check timer status
  if (timerDuration > 0) {
    unsigned long currentTime = millis();
    elapsedTime = (currentTime - timerStartTime) / 1000;

    if (elapsedTime >= timerDuration) {
      // Timer expired
      stopTimer();
    } else {
      // Timer still running
      Serial.println("Time remaining: " + String(timerDuration - elapsedTime) + " seconds");
    }
  }
}

Yes you could and you could also target a certain animal easily too..

really?? what's it using blutooth??
doesn't matter, i understand where you're coming from though, you want espnow..
do you ever receive anything??
anything in the serial prints??
any idea if it's on receiver end or sender??
~q

i think maybe i explained it wrong then, espnow does use wifi to communicate, but i dont want to connect to a wifi network which has other devices on it.
i want the esp to have direct communications between each other.

as for serial prints both of them say :ESP-NOW initialization successful
When i send on the sender it does say: Failed to send data via ESP-NOW"

Curious, did you effectively test espnow to evaluate it..
Looking into it, seems like it may take over the radio..
Did you look at the demo's for the lib??
Multi Slave Master..
That's what you're trying to do I believe..
See if you can get a basic demo going, looks like it may have a few more routines in there that you may still need to add..

~q

1 Like

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