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?