257 lines
7.0 KiB
C++
257 lines
7.0 KiB
C++
//#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 <ESP8266WiFi.h>
|
||
#include <Hash.h>
|
||
#elif defined(ESP32)
|
||
#include <WiFi.h>
|
||
#endif
|
||
#include <WifiEspNowBroadcast.h> //https://github.com/yoursunny/WifiEspNow
|
||
#include <Ticker.h> //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();
|
||
}
|
||
}
|