Skip to content

Instantly share code, notes, and snippets.

@Robotto
Last active March 3, 2021 11:41
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save Robotto/5429d0cf58d280a82a3306765ab1689b to your computer and use it in GitHub Desktop.
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.
/**************************************************************************/
/*!
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