diff options
author | Jonas Smedegaard <dr@jones.dk> | 2025-04-16 10:55:42 +0200 |
---|---|---|
committer | Jonas Smedegaard <dr@jones.dk> | 2025-04-16 10:55:42 +0200 |
commit | d6a944ecce92f1b5bdd54aaf3ed57808c3286ce0 (patch) | |
tree | c248d01d1d8a90cdd48375fdd343e598bbfc2e4a /vote | |
parent | 3da3079f0661b2e966ee3e150d7a3c08664046d5 (diff) |
add vote as non-library sketch
Diffstat (limited to 'vote')
-rw-r--r-- | vote/Mussel_Beacon_Voting.md | 20 | ||||
-rw-r--r-- | vote/vote.ino | 173 | ||||
-rw-r--r-- | vote/vote.puml | 27 |
3 files changed, 220 insertions, 0 deletions
diff --git a/vote/Mussel_Beacon_Voting.md b/vote/Mussel_Beacon_Voting.md new file mode 100644 index 0000000..33a0893 --- /dev/null +++ b/vote/Mussel_Beacon_Voting.md @@ -0,0 +1,20 @@ +## Mussel Beacon Voting + +1. Scans bluetooth network for beacons. + +2. Collects mussel name and gape angle +as decoded from each detected beacon, +together with the time of detection in milliseconds since boot. + +3. Aligns the collected data +to the format of ballots for a water quality vote. + +4. Qualifies the ballots for criteria of the water quality vote +(e.g. timeliness and sanity of gape angles). + +5. Concludes a vote based on collected, aligned and qualified ballots. + +6. Acts on the voting result, +e.g. turns on a steady light for "code green" +or a blinking light for "code yellow", +or turns on a blinking light and shuts off a valve for "code red". diff --git a/vote/vote.ino b/vote/vote.ino new file mode 100644 index 0000000..44fd179 --- /dev/null +++ b/vote/vote.ino @@ -0,0 +1,173 @@ +// SPDX-FileCopyrightText: 2025 Amal Mazrah <mazrah@ruc.dk> +// SPDX-FileCopyrightText: 2025 Jonas Smedegaard <dr@jones.dk> +// SPDX-FileCopyrightText: 2025 Mennatullah Hatim Kassim <stud-mennatulla@ruc.dk> +// SPDX-FileCopyrightText: 2025 Noor Ahmad <noora@ruc.dk> +// SPDX-FileCopyrightText: 2025 Tanishka Suwalka <tanishkas@ruc.dk> +// SPDX-License-Identifier: GPL-3.0-or-later + +/// Mussel vote - an Arduino sketch to monitor mussel biosensors +/// +/// @version 0.0.3 +/// @see <https://app.radicle.xyz/nodes/seed.radicle.garden/rad:z2tFBF4gN7ziG9oXtUytVQNYe3VhQ> +/// @see <https://moodle.ruc.dk/course/view.php?id=23504> + +// 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 <esp32-hal-log.h> +#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 <BLEDevice.h> +#include <BLEScan.h> +#include <BLEAdvertisedDevice.h> +#include <BLEEddystoneTLM.h> +#include <BLEBeacon.h> + +#define MUSSEL_VOTE_TIME_AHEAD 60000U // 1 minute +#define MUSSEL_VOTE_TIME_BEHIND 120000U // 2 minutes + +// Limited size NOW!! can be transformed into infinite array +#define STACK_SIZE 1000 + +struct Vote { + String id; + unsigned long timestamp; + int measure; +}; + +void begin(); +String desc(); +int read(); +String debug(); +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 <= MUSSEL_VOTE_TIME_AHEAD) { + return true; + } + // If the vote's timestamp is older than 2 minutes, count it as NO + else if (currentTime - vote.timestamp > MUSSEL_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; +} + +/// Dump internal variables, formatted for use with Serial Plotter +/// +/// @return internal variables as String +String debug() { + return static_cast<String>( + "pin:") + _pin + + "\tboolState:" + _boolState + + "\ttime:" + _time + + "\tcount:" + _count; +} + +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(); + + printStack(); + + delay(500); +} diff --git a/vote/vote.puml b/vote/vote.puml new file mode 100644 index 0000000..a4958f7 --- /dev/null +++ b/vote/vote.puml @@ -0,0 +1,27 @@ +@startuml +:instantiate mussel object; +:instantiate bluetooth object; +group init +:setup mussel voting; +:setup bluetooth scanner; +end group +split +while (each beacon detected) +group "bluetooth callback" { +:collect beacon data; +end group +endwhile +-[hidden]-> +kill +split again +while (each 500ms) +group loop { +:allign beacon data as ballots; +:qualify ballots for a vote; +:conclude vote result; +:act on vote result; +end group +endwhile +-[hidden]-> +kill +@enduml |