// SPDX-FileCopyrightText: 2025 Amal Mazrah // SPDX-FileCopyrightText: 2025 Jonas Smedegaard // SPDX-FileCopyrightText: 2025 Mennatullah Hatim Kassim // SPDX-FileCopyrightText: 2025 Noor Ahmad // SPDX-FileCopyrightText: 2025 Tanishka Suwalka // SPDX-License-Identifier: GPL-3.0-or-later /// Mussel vote - an Arduino sketch to monitor mussel biosensors /// /// @version 0.0.3 /// @see /// @see // arduino-esp32 Logging system // activate in Arduino IDE: Tools -> Core Debug Level // special: set Core Debug Level to Error for plot-friendly output #define CONFIG_ARDUHAL_ESP_LOG 1 #define LOG_LOCAL_LEVEL CORE_DEBUG_LEVEL #include #undef ARDUHAL_LOG_FORMAT #define ARDUHAL_LOG_FORMAT(letter, format) \ ARDUHAL_LOG_COLOR_##letter "[" #letter "] %s(): " format \ ARDUHAL_LOG_RESET_COLOR "\r\n", __FUNCTION__ // arduino-esp32 Bluetooth Low Energy (BLE) networking stack #include #include #include #include #include // Validity timing thresholds const unsigned long VOTE_TIME_AHEAD = 1 * 60 * 1000; // 1 minute const unsigned long VOTE_TIME_BEHIND = 2 * 60 * 1000; // 2 minutes // Limited size NOW!! can be transformed into infinite array #define STACK_SIZE 1000 struct Vote { String id; unsigned long timestamp; int measure; }; bool push(String id, unsigned long timestamp, int measure); void printStack(); bool qualifyVote(Vote vote, unsigned long currentTime); int _attitude; int _pin; bool _boolState; byte _count; unsigned long _time; // Array to store ID strings String idStack[STACK_SIZE]; unsigned long timeStack[STACK_SIZE]; int measureStack[STACK_SIZE]; // Index of the top element in the stack, -1 means stack is empty int top = -1; int scanTime = 1; //In seconds // pointer to control Bluetooth networking BLEScan *pBLEScan; // Bluetooth beacon discovery callbacks class MyAdvertisedDeviceCallbacks : public BLEAdvertisedDeviceCallbacks { // decode name and temperature from Eddystone TLM advertisement void onResult(BLEAdvertisedDevice advertisedDevice) { if (advertisedDevice.haveName() && advertisedDevice.getFrameType() == BLE_EDDYSTONE_TLM_FRAME ) { BLEEddystoneTLM EddystoneTLM(&advertisedDevice); push( advertisedDevice.getName(), millis(), EddystoneTLM.getTemp() ); } } }; /// Function to push data onto the stack bool push(String id, unsigned long timestamp, int measure) { // Check if stack is full if (top >= STACK_SIZE - 1) { Serial.println("Stack Full"); // Return false if stack is full return false; } top++; idStack[top] = id; timeStack[top] = timestamp; measureStack[top] = measure; // Return true on successful push return true; } /// Function to print stack contents void printStack() { for (int i = top; i >= 0; i--) { Serial.print("ID: "); Serial.print(idStack[i]); Serial.print(", Time: "); Serial.print(timeStack[i]); Serial.print(", measure: "); Serial.println(measureStack[i]); } } bool qualifyVote(Vote vote, unsigned long currentTime) { // If the measure is 42 (YES), check timestamp validity if (vote.measure == 42) { // If the vote's timestamp is within 1 minute, count it as YES if (currentTime - vote.timestamp <= VOTE_TIME_AHEAD) { return true; } // If the vote's timestamp is older than 2 minutes, count it as NO else if (currentTime - vote.timestamp > VOTE_TIME_BEHIND) { return false; } } // If the measure is 2, always count the vote as NO if (vote.measure == 2) { return false; } // Default case: vote is invalid if no conditions are met return false; } void setup() { // enable logging to serial Serial.begin(115200); esp_log_level_set("*", ESP_LOG_DEBUG); // setup Bluetooth BLEDevice::init(""); pBLEScan = BLEDevice::getScan(); pBLEScan->setAdvertisedDeviceCallbacks( new MyAdvertisedDeviceCallbacks()); pBLEScan->setActiveScan(true); pBLEScan->setInterval(100); pBLEScan->setWindow(99); } void loop() { BLEScanResults *foundDevices = pBLEScan->start(scanTime, false); pBLEScan->clearResults(); // misuse error-only log level for plot-friendly output #if ARDUHAL_LOG_LEVEL == ARDUHAL_LOG_LEVEL_ERROR Serial.printf("pin:%d boolState:%d time:%d count:%d\n", _pin, _boolState, _time, _count); #endif delay(500); }