//#define LOG(fmt, ...) #define LOG(fmt, ...) (Serial.printf("%09llu: " fmt "\n", GetTimestamp(), ##__VA_ARGS__)); Serial.flush(); //#define DEBUGLOG(fmt, ...) #define DEBUGLOG(fmt, ...) (Serial.printf("%09llu: [DEBUG] " fmt "\n", GetTimestamp(), ##__VA_ARGS__)); #define GLOBAL_VERBOSE 1//activate if the 'GREEN' status should be globally available #define debounceDelay 1000 //LED shining duration #define globalDelay 20000 #define alarmDelay 50 #define adcDelay 600000UL //10 minutes #define warningDelay 333 const int MAX_PEERS = 20; //Limited due espNOW //static const int WIFIESPNOW_KEYLEN = 16; //Limited due espNOW const uint8_t key[16] = { 0 }; #if defined(ESP8266) #include #include #elif defined(ESP32) #include #endif #include //https://github.com/yoursunny/WifiEspNow #include //Ticker Library //version 1.1 - added originMAC typedef struct { char PREAMBLE1 = 'R'; char PREAMBLE2 = 'M'; uint8_t messageID[20]; char code = 'g'; // [g, y, r, X] bool batteryWarning = false; //ToDo not implemented yet uint8_t originMAC[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; } rm370; rm370 receiveBuffer[MAX_PEERS]; rm370 transmitBuffer; rm370 ownState; //Remember VIL == 0.25Ă—VIO ==> ~0,8275V //Lowest voltage on LED 2,5V ==> 1,6V with level shifter const uint8_t GREEN_Pin = D5; //CAUTION Vext is NOT 3.3V !!! const uint8_t YELLOW_Pin = D6; //CAUTION Vext is NOT 3.3V !!! const uint8_t RED_Pin = D7; //CAUTION Vext is NOT 3.3V !!! const uint8_t BUZZER_Pin = D2; //CAUTION Vext is NOT 3.3V !!! const uint8_t BAT_Pin = A0; //CAUTION Vext is NOT 1.1V !!! Ticker alarmGenerator; Ticker warningGenerator; volatile bool alarmTrigger = false; //global volatile bool warningTrigger = false; //global volatile bool debounceTrigger = false; //real physics workaround volatile unsigned long globalStart = 0; //real physics workaround volatile unsigned long debounceStart = 0; //real physics workaround volatile unsigned long nowMillis = millis(); //real physics workaround volatile unsigned long lastADCRead = 0; volatile byte rxCounter = 0; //marker for the receive interrupt callback volatile byte rxHandlePointer = 0; //marker for internal processing int64_t GetTimestamp() { struct timeval tv; gettimeofday(&tv, NULL); return (tv.tv_sec * 1000LL + (tv.tv_usec / 1000LL)); } void buzzerTone() { //just trigger if externel trigger received if (ownState.code == 'g') { digitalWrite(BUZZER_Pin, !(digitalRead(BUZZER_Pin))); } } void batteryCheck() { int sensorValue = analogRead(BAT_Pin); DEBUGLOG("AnalogRead: %i", sensorValue); lastADCRead = millis(); int voltage = map(sensorValue, 0, 1023, 0, 5000); DEBUGLOG("VoltageMap: %i mV", voltage); if (voltage < 3000) { ownState.batteryWarning = true; } else { ownState.batteryWarning = false; } } void buzzerTest() { LOG("Test alarm buzzer"); debounceTrigger = true; alarmTrigger = true; for (int i = 0; i < (3000 / alarmDelay); i++) { buzzerTone(); delay(alarmDelay); } alarmTrigger = false; LOG("Test warning buzzer"); warningTrigger = true; for (int i = 0; i < (3000 / warningDelay); i++) { buzzerTone(); delay(warningDelay); } warningTrigger = false; //Failsafe digitalWrite(BUZZER_Pin, HIGH); debounceTrigger = false; } void globalStatus() { if (globalStart != 0) { //reset global if ( (nowMillis - globalStart) > globalDelay ) { if (alarmGenerator.active()) { alarmGenerator.detach(); alarmTrigger = false; } if (warningGenerator.active()) { warningGenerator.detach(); warningTrigger = false; } globalStart = 0; //Failsafe digitalWrite(BUZZER_Pin, HIGH); LOG("Global ended"); } if (alarmTrigger && !alarmGenerator.active()) { LOG("Global started"); alarmGenerator.attach(float(alarmDelay / 1000.0), buzzerTone); } //attach ticker if (warningTrigger && !warningGenerator.active()) { LOG("Global started"); warningGenerator.attach(float(warningDelay / 1000.0), buzzerTone); } } } void setup() { pinMode(RED_Pin, INPUT_PULLUP); pinMode(YELLOW_Pin, INPUT_PULLUP); pinMode(GREEN_Pin, INPUT_PULLUP); pinMode(BUZZER_Pin, OUTPUT); digitalWrite(BUZZER_Pin, HIGH); attachInterrupt(digitalPinToInterrupt(RED_Pin), IntREDCallback, FALLING); attachInterrupt(digitalPinToInterrupt(YELLOW_Pin), IntYELLOWCallback, FALLING); attachInterrupt(digitalPinToInterrupt(GREEN_Pin), IntGREENCallback, FALLING); Serial.begin(115200); Serial.flush(); delay(1000); LOG("\n\nInterrupt D7,D6,D5 with Serial.print"); #if defined(GLOBAL_VERBOSE) LOG("\nGlobal verbose ACTIVE"); #endif if (!initCommunication()) { ESP.restart(); } batteryCheck(); buzzerTest(); LOG("Setup completed.\n"); } void loop() { //loop things nowMillis = millis(); WifiEspNowBroadcast.loop(); globalStatus(); if ((nowMillis - lastADCRead) > adcDelay) { batteryCheck(); } //reset debounce, as the LEDs shine a period of time if (debounceTrigger && ((nowMillis - debounceStart) > debounceDelay)) { debounceTrigger = false; if (ownState.code == 'r') { DEBUGLOG("RED LED"); } if (ownState.code == 'y') { DEBUGLOG("YELLOW LED"); } //Todo check for code 'g' if (!(ownState.code == 'r' || ownState.code == 'y')) { DEBUGLOG("GREEN LED"); } //send own status #if defined(GLOBAL_VERBOSE) if (ownState.code == 'r' || ownState.code == 'y' || ownState.code == 'g') { #else if (ownState.code == 'r' || ownState.code == 'y') { #endif //generate unique identifier char messageIDString[65]; itoa(millis(), messageIDString, 10); sha1(messageIDString, ownState.messageID); transmitBuffer = ownState; sendMessage(); } } //process remaining rxBuffer while (rxHandlePointer != rxCounter) { checkMessage(); //shift ring buffer working pointer if (rxHandlePointer + 1 < MAX_PEERS) { rxHandlePointer++; } else { rxHandlePointer = 0; } //forward received messages #if defined(GLOBAL_VERBOSE) if (transmitBuffer.code == 'r' || transmitBuffer.code == 'y' || transmitBuffer.code == 'g') { #else if (transmitBuffer.code == 'r' || transmitBuffer.code == 'y') { #endif sendMessage(); } } } ICACHE_RAM_ATTR void IntREDCallback() { if (!debounceTrigger) { alarmTrigger = true; warningTrigger = false; ownState.code = 'r'; //real physics workaround debounceTrigger = true; debounceStart = millis(); } } ICACHE_RAM_ATTR void IntYELLOWCallback() { if (!debounceTrigger) { if (!alarmTrigger) { warningTrigger = true; } ownState.code = 'y'; //real physics workaround debounceTrigger = true; debounceStart = millis(); } } ICACHE_RAM_ATTR void IntGREENCallback() { if (!debounceTrigger) { ownState.code = 'g'; //real physics workaround debounceTrigger = true; debounceStart = millis(); } }