aboutsummaryrefslogtreecommitdiff
path: root/vote
diff options
context:
space:
mode:
authorJonas Smedegaard <dr@jones.dk>2025-04-16 10:55:42 +0200
committerJonas Smedegaard <dr@jones.dk>2025-04-16 10:55:42 +0200
commitd6a944ecce92f1b5bdd54aaf3ed57808c3286ce0 (patch)
treec248d01d1d8a90cdd48375fdd343e598bbfc2e4a /vote
parent3da3079f0661b2e966ee3e150d7a3c08664046d5 (diff)
add vote as non-library sketch
Diffstat (limited to 'vote')
-rw-r--r--vote/Mussel_Beacon_Voting.md20
-rw-r--r--vote/vote.ino173
-rw-r--r--vote/vote.puml27
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