Last active
March 3, 2021 11:41
-
-
Save Robotto/5429d0cf58d280a82a3306765ab1689b to your computer and use it in GitHub Desktop.
ESP modules with a PIR sensor that broadcast if the PIR has been triggered, and listen for other devices trigger broadcasts. Also: a relay turns on if triggered.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/**************************************************************************/ | |
/*! | |
naive brodacast/listener implementation with simple UDP broadcast. | |
Each device: | |
-must have a unique ID! (device_ID) | |
-Connects to wifi and waits for incoming UDP packets. | |
-can have a list of IDs that it subscribes to, so that only received IDs in the list will trigger the device. | |
Anything else is ignored. | |
-Sends a UDP packet with device ID if a PIR module is triggered. (two byte payload: txBuffer=[0]='ID', txBuffer[1]='\n' | |
This implementation will only broadcast when its PIR sensor is triggered, NOT when the PIR calms down again, | |
so listeners turn off their relay automagically after relayActiveDuration milliseconds. | |
future upgrades: | |
-Verification of some sort, perhaps with some sort of UUID instead of a single letter? | |
-recieve queue? - perhaps event-based. | |
*/ | |
/**************************************************************************/ | |
#include <ESP8266WiFi.h> | |
#include <WiFiUdp.h> | |
#include <WiFiClient.h> | |
#include <ArduinoOTA.h> | |
#include "user_interface.h" //used to prevent internal power management from messing with UDP broacast reception https://arduino.stackexchange.com/questions/39957/esp8266-udp-multicast-doesnt-receive-packets/58268#58268 | |
#define PIR_ACTIVE LOW //testing with a button using INPUT_PULLUP | |
#define PIR_INACTIVE HIGH | |
#define RELAY_ACTIVE HIGH | |
#define RELAY_INACTIVE LOW | |
String device_ID = "B"; //make sure this is unique per device. | |
String device_name = "ESP_PIR_"+device_ID; | |
String SSID = "testWifi"; | |
String PSK = "pleaseIgnore"; | |
int PIRPin=D1; | |
int RelayPin=D8; | |
unsigned long relayActiveDuration=5000; //how long should the relay stay on after activation? (milliseconds) | |
unsigned long delayedResponseTime = 0; //how long should this device wait to react to any (both UDP and PIR) trigger | |
//Subscriptions, i.e. which device_IDs should trigger this device's relay, supports multiple IDs | |
char subscribedToDevices[] = {'A','C'}; | |
//UDP stuff: | |
WiFiUDP Udp; | |
const unsigned int udpPort = 1337; | |
const int UDP_PACKET_SIZE = 2; //change to whatever you need. | |
byte rxBuffer[ UDP_PACKET_SIZE ]; //buffer to hold outgoing packets | |
byte txBuffer[ UDP_PACKET_SIZE ]; //buffer to hold outgoing packets | |
IPAddress broadCastIP; //broadcast IP is determined dynamically from localIP() after successfull wifi connection. | |
void setup(void) { | |
pinMode(RelayPin,OUTPUT); | |
pinMode(PIRPin,INPUT_PULLUP); | |
//WiFi.persistent(false); | |
WiFi.mode(WIFI_STA); | |
wifi_set_sleep_type(NONE_SLEEP_T); //LIGHT_SLEEP_T and MODE_SLEEP_T - https://arduino.stackexchange.com/questions/39957/esp8266-udp-multicast-doesnt-receive-packets/58268#58268 | |
Serial.begin(115200); | |
Serial.println("Hello!"); | |
WiFi.hostname(device_name.c_str()); | |
WiFi.begin(SSID, PSK); | |
Serial.print("Connecting"); | |
while (WiFi.status() != WL_CONNECTED) | |
{ | |
delay(500); | |
Serial.print("."); | |
} | |
Serial.println(); | |
Serial.print("Connected, IP address: "); | |
Serial.println(WiFi.localIP()); | |
//OTA: | |
// Port defaults to 8266 | |
// ArduinoOTA.setPort(8266); | |
// Hostname defaults to esp8266-[ChipID] | |
ArduinoOTA.setHostname(device_name.c_str()); | |
// No authentication by default | |
//ArduinoOTA.setPassword((const char *)"testing123"); | |
ArduinoOTA.onStart([]() { | |
Serial.println("OTA START!"); | |
delay(500); | |
}); | |
ArduinoOTA.onEnd([]() { | |
Serial.println("OTA End.. brace for reset"); | |
ESP.restart(); | |
}); | |
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { | |
Serial.printf("Progress: %u%%\r", (progress / (total / 100))); | |
}); | |
ArduinoOTA.onError([](ota_error_t error) { | |
String buffer=String("Error[") + String(error) + String("]: "); | |
if (error == OTA_AUTH_ERROR) buffer+=String("Auth Failed"); | |
else if (error == OTA_BEGIN_ERROR) buffer+=String("Begin Failed"); | |
else if (error == OTA_CONNECT_ERROR) buffer+=String("Connect Failed"); | |
else if (error == OTA_RECEIVE_ERROR) buffer+=String("Receive Failed"); | |
else if (error == OTA_END_ERROR) buffer+=String("End Failed"); | |
Serial.println(buffer); | |
}); | |
ArduinoOTA.begin(); | |
Udp.begin(udpPort); | |
//determine the four bytes of the broadcast IP: | |
broadCastIP[0] = WiFi.localIP()[0]; | |
broadCastIP[1] = WiFi.localIP()[1]; | |
broadCastIP[2] = WiFi.localIP()[2]; | |
broadCastIP[3] = 255; | |
} | |
bool TRIGGERED=false; | |
unsigned long triggerReceivedAt=0; | |
unsigned long relayActivatedAt=0; | |
void loop(void) { | |
ArduinoOTA.handle(); //allow firmware updates over wifi | |
if (Udp.parsePacket()) UDPrx(); //listen for UDP packets | |
if(digitalRead(PIRPin) == PIR_ACTIVE) //if PIR activated | |
{ | |
delay(20); //debounce - for testing with button. | |
Serial.println("PIR activated"); | |
triggered(); //comment out if this device should not activate relay based on own PIR input. | |
broadcast(); | |
while(digitalRead(PIRPin) == PIR_ACTIVE) { //wait for the PIR to calm down again :) | |
yield(); //let ESP core do wifi housekeeping. | |
} | |
} | |
//using rollover safe comparisons of "durations" rather than timestamps. see: https://arduino.stackexchange.com/a/12588 | |
//react to trigger after delayedResponseTime milliseconds | |
if(TRIGGERED && millis() - triggerReceivedAt > delayedResponseTime){ | |
digitalWrite(RelayPin, RELAY_ACTIVE); | |
Serial.println("Relay ON"); | |
relayActivatedAt = millis(); | |
TRIGGERED=false; | |
} | |
//after relayActiveDuration has passed: | |
if(digitalRead(RelayPin) == RELAY_ACTIVE && millis() - relayActivatedAt > relayActiveDuration){ | |
digitalWrite(RelayPin, RELAY_INACTIVE); | |
Serial.println("Relay OFF"); | |
} | |
} | |
void broadcast() | |
{ | |
Serial.println("Sending UDP packet..."); | |
txBuffer[0] = device_ID.charAt(0); //get first character of device ID string .. should be 'A' or 'B' etc.. | |
txBuffer[1] = '\n'; | |
Serial.println("Transmitting: " + String((char)txBuffer[0]) + String((char)txBuffer[1])); | |
Udp.beginPacket(broadCastIP, udpPort); | |
//Udp.beginPacket(doorAddress,remotePort); | |
Udp.write(txBuffer, UDP_PACKET_SIZE); | |
Udp.endPacket(); | |
} | |
void UDPrx(){ | |
Serial.println("udpRX!"); | |
memset(rxBuffer, 0, UDP_PACKET_SIZE); //reset packet buffer | |
int read_bytes = Udp.read(rxBuffer, UDP_PACKET_SIZE); // read the packet into the buffer | |
//this is a naive implementation that will probably break if another trigger is received before the last is handled.. | |
//some sort of queue system could be implemented, but is it neccesary? probably not... | |
if(read_bytes == 2 && rxBuffer[1] == '\n'){ | |
Serial.println("Received trigger from: " + String((char)rxBuffer[0]) + "! Checking Subscriptions.."); | |
for (int i=0; i<sizeof(subscribedToDevices); i++){ | |
Serial.print("Checking " + String(subscribedToDevices[i]) + "... "); | |
if (rxBuffer[0] == subscribedToDevices[i]){ | |
Serial.println("Match!"); | |
triggered(); | |
Udp.flush(); //empty the udp receive buffer. | |
return; //only react to firs encountered trigger... might break. | |
} | |
Serial.println("Nope."); | |
} | |
} | |
else Serial.println("UDP Parsing failed."); | |
} | |
void triggered() | |
{ | |
TRIGGERED = true; | |
triggerReceivedAt = millis(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment