From 237cf6cc8cbb10e63cb9ca99fb70f2c6367facc2 Mon Sep 17 00:00:00 2001
From: Jonas Smedegaard <dr@jones.dk>
Date: Sun, 20 Apr 2025 00:53:11 +0200
Subject: update content

---
 report.qmd | 328 ++++++++++++++++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 281 insertions(+), 47 deletions(-)

(limited to 'report.qmd')

diff --git a/report.qmd b/report.qmd
index 7123ff1..b84f28d 100644
--- a/report.qmd
+++ b/report.qmd
@@ -35,9 +35,19 @@ detected and examined by biomonitoring.
 We have defined the prototype as a "research archetype 3",
 meaning that the driving force for this project
 is illustration and demostration purposes.
+As part of the project requirements,
+we implemented three core components:
+ESP32 for hardware interaction,
+p5.js for visual representation,
+and Bluetooth for wireless communication.
+The system simulates mussel movements as simple votes,
+green for drinkable, red for undrinkable water,
+translated into a visual interface.
+The outcome highlights
+how environmental signals can be simplified
+and communicated through interactive design.
+
 
-*TODO: come back and write about each step,
-OUR THREE REQUIRED COMPONENTS,and a conclusion*
 
 # Introduction
 
@@ -100,7 +110,7 @@ that concerns this technology and actual authentic mussel behaviour.
 
 ## Use of course curriculum
 
-The beggining of our work process
+The beginning of our work process
 consisted of researching bioindication as a concept
 and the associated technology.
 Then we tried to define the purpose of our prototype.
@@ -128,7 +138,7 @@ in both a physical and abstract sense
 Next we each had an early prototyping and pseudocoding phase,
 were we worked on the sensor behaviour,
 on the logic behind the voting mechanism
-and the possible communication-methods between them.
+and the possible communication methods between them.
 
 Our project group has received a brief introduction
 to APIs during a lecture session
@@ -190,7 +200,8 @@ is to collect data at a sample rate of 0.5 seconds, i.e. 2 Hz,
 and convert that into gape angle per second (CHIGA)
 to then monitor gape movement instead of gape position.
 
-Additionally, we wish to refer to the bachelor thesis @GarciaHuertes2016
+Additionally, we wish to refer
+to the bachelor thesis @GarciaHuertes2016
 that covers some of the same themes as our report
 but are prototyping with real mussels.
 The report covers mussel behaviour and stress,
@@ -234,11 +245,9 @@ if the water quality is good or not.
 Light Sensor: Detects environmental input
 and triggers a behavioral change in the simulated mussel.
 
-P5.js: Used to indicate
-
-*FIXME: above paragraph is unfinished*
+P5.js: Used to represent if it is daytime or nightime.
 
-# Coding approach - Tanishka
+# Coding approach
 
 The coding approach for this project is centered
 around simulating behavioral response of mussels
@@ -266,32 +275,44 @@ The system is composed of three major components:
   if the data is recent and valid.
   It determines whether the water is drinkable using a voting rule.
 
-*TODO: here we need to introduce the source we know this from,
-and to give a little insight
-into why there is different roles to choose from*
-
 ```{.plantuml}
 !include components.puml
 ```
 
-*TODO: if the plantUML is here,
-we should have a text section that explains it also,
-and show snippets
-of our most important/difficult code for each-ish file.*
+This UML diagram
+shows how the different parts of the system talk to each other:
+
+1. Light (p5.js)
+  * There's a button that lets you switch between daylight and moonlight.
+  * When you click it, it changes the "light" condition
+    and sends that to the sensor mussel.
+2. Sensor (Arduino)
+   Uses an Arduino to act like a mussel biosensor.
+   It's responsible for detecting light (using a light sensor)
+   and touch,
+   simulating the way a real mussel might react to environmental changes
+  * READ: Reads the light level
+    using a light sensor attached to the mussel
+    and detects the touch input.
+  * NORMALIZE: It calculates the raw data
+    based on touch and light data
+    and converts that into a gape measure --
+    how much the mussel opens and closes.
+
+    Then the data (Mussel ID, time and the gape measure) is sent to Vote via Bluetooth Low energy (BLE) protocol.
 
-The p5.js code shows a button
-where, when you press it, it changes from day to night.
-This change helps the mussel to know *how to think?*
-The p5.js code is connected to the sensor system.
-Here it reads the data from the the p5.js and normalizes it.
+3. Vote (Arduino)
 
-*FIXME: above paragraph need revisiting:
-It seems wrong that the sun affects mussels' ability to think,
-and the p5.js code does not normalize anything --
-probably an interesting and valid point was intended,
-but it is unclear with current phrasing what that is.*
+    This part takes the sensor data from the mussels and turns it into a voting system to decide if the water is drinkable or not.
 
-*TODO*
+    * COLLECT: Recives the mussel's data (ID, time and gape measure) and stores it into a stack.Each mussel can store up to 5 recent readings. If a mussel isn’t already in the system, it gets added.
+    * ALIGN: Looks at the most recent reading (vote) from each mussel and checks if the gape angle means the mussel is Open (gape between 40–90) or Closed (gape between 0–39). This helps classify each mussel's current state.
+    * QUALIFY: Filters out old votes. A vote is valid only if it's less than 1 minute old. This ensures the decision is based on real-time conditions.
+    * CONCLUDE: Counts how many valid votes are “Open.” If half or more of the valid votes are Open, it decides that the water is drinkable (GREEN light). If fewer than half are Open, the system decides the water is not drinkable (RED light).
+
+    The LEDs on the Arduino are used to show this decision:
+
+    Green = Drinkable, red = Undrinkable.
 
 ## Sensor system
 
@@ -299,34 +320,234 @@ but it is unclear with current phrasing what that is.*
 !include Arduino/sensor/sensor.puml
 ```
 
-## Voting system
+This UML Diagram explains how a sensor system for a mussel behaviour simulator works.
+
+1. *Instantiate* *mussel* *object* -  This refers to initializing mussel's gaping rhymthm control and sensors. We have used functions such as
+**keepPace**(), **beginPace**() and **resolveGapeAngle**() for simulating the mussel's behaviour.
+
+2. *Instantiate* *bluetooth* *object* - Setting up the Bluetooth system for communication.
+
+```{.cpp}
+BLEDevice::init(BEACON_NAME);
+pAdvertising = BLEDevice::getAdvertising(;
+```
+
+**Init** **Block**
+
+3. *Setup* *mussel* *sensors* - Initializes the sensors attached to the mussel.
+
+```{.cpp}
+beginPace();
+beginTouchDetection();
+```
+These set up the LED-based pacemaker and touch sensor.
+
+4. *Setup* *bluetooth* *beacon* - Preparing the bluetooth device to send data.
+
+```{.cpp}
+setBeaconAdvertisement();
+setBeaconServiceData(resolveGapeAngle());
+pAdvertising->start();
+```
 
-The voting system is the client that scans after network
-to find "is there any beacons here?".
-The sensor system is our beacon and it works like a lighthouse.
-It sends signals out to say "im here, i exist".
-Then Boom! a bluetooth connection is made.
+**Loop (each 500ms)**
 
-*TODO: "... connection is made" can mislead the reader
-into expecting a two-way connection
-which is not the case in our deliberately loose coupling.
-Probably better to phrase it like
-"...nearby Bluetooth receivers can capture the beacon message" --
-smooth and quiet, no "Boom!"*
+5. *Read sensors* - Get data from the mussel.
+
+```{.cpp}
+resolveGapeAngle()
+```
+This function reads the light sensor and LED Output to estimate the gape angle.
+
+6. *Normalize sensor data as a gape angle* - Processes sensor values into a meaningful format.
+
+```{.python}
+int gapeAngle = paceAngle * lightIntensity
+```
+
+7. *Add gape angle to beacon* - Attaches the processed gape angle to the bluetooth beacon.
+
+```{.cpp}
+setBeaconServiceData(resolveGapeAngle())
+```
+
+**Parallel 100ms Loop**
+
+8. *Broadcast beacon* - continuously sends bluetooth beacon data
+
+```{.cpp}
+pAdvertising->start()
+```
+BLE stack itself handles frequent broadcasting.
+
+## Voting system
 
 ```{.plantuml}
 !include Arduino/vote/vote.puml
 ```
+This UML Diagram outlines a bluetooth-based mussel biomonitoring voting system.
+
+1. *Instantiate mussel object* - Mussels are abstracted using a Voter strct. A global array is initialized to hold mussels and their latest votes.
+
+```{.cpp}
+struct Voter {
+  String id;               // Mussel ID
+  Vote votes[BALLOT_MAX];  // Last 5 sensor readings
+  int voteCount = 0;       // Number of readings stored
+};
+Voter voters[VOTER_MAX];
+```
+
+2. *Instantiate bluetooth object* - BLE stack is initialized, responsible for detecting nearby beacons.
+
+```{.cpp}
+BLEDevice::init("");
+pBLEScan = BLEDevice::getScan();
+```
+
+**Init**
+
+3. *Setup mussel voting* - **setup()** initializes LEDs, Serial logging, and sets the BLE callbacks.
+
+4. *Setup bluetooth scanner* - **MyAdvertisedDeviceCallbacks()** starts the bluetooth scanning to detect the nearby devices.
+
+**Event-Driven Component**
+
+5. *Each beacon detected (Trigger)* - The conditional event **MyAdvertisedDeviceCallbacks** method is triggered on beacon detection.
+
+6. *Collect beacon data* - Reads and stores the information (Mussel ID, timestamp and Gape measure) from each detected beacon.
+
+```{.cpp}
+void onResult(BLEAdvertisedDevice advertisedDevice) {
+    if (advertisedDevice.haveName()
+      && advertisedDevice.getFrameType() == BLE_EDDYSTONE_TLM_FRAME
+    ) {
+      BLEEddystoneTLM EddystoneTLM(&advertisedDevice);
+      // misuse error-only log level for plot-friendly output
+#if ARDUHAL_LOG_LEVEL == ARDUHAL_LOG_LEVEL_ERROR
+      String id_mangled = advertisedDevice.getName();
+      id_mangled.replace(' ', '_');
+      id_mangled.replace(':', '=');
+      Serial.println(id_mangled + ":" + EddystoneTLM.getTemp());
+#endif
+      unsigned long now = millis();
+      String musselID = advertisedDevice.getName();
+      int gape = EddystoneTLM.getTemp();
+      collectBallotData(musselID, now, gape);
+    }
+  }
+```
+**collectBallotData()** adds or updates the voter’s record with the new vote (FIFO for 5 ballots max).
+
+**Continuous Loop (500 ms)**
+
+7. *Align beacon data as ballots* - **alignVotes()** checks each mussel’s latest gape value and classifies it as Open, Closed, or Invalid
+
+```{.cpp}
+String state = (latest.measure >= 0 && latest.measure < 40)
+      ? "Closed"
+      : (latest.measure >= 40 && latest.measure <= 90)
+        ? "Open"
+        : "Invalid reading";
+```
+
+8. *Qualify Ballot for a vote* - **qualifyBallot()** checks if the vote is within the 1-minute validity window.
+
+```{.cpp}
+if (age <= VOTE_TIME_TOLERANCE) {
+    log_i("VALID: Ballot is within 1 minute (age: %lums)",
+      age);
+    return "valid";
+```
+
+9. *Conclude vote result* - **concludeVote()** counts valid ballots and checks how many are "Open". If majority are OPEN the water is drinkable.
+
+```{.cpp}
+waterIsDrinkable = (openVotes >= threshold);
+```
+10. *act on vote result* - Turns LEDs on/off and prints status.
+
+```{.cpp}
+if (waterIsDrinkable) {
+    digitalWrite(LED2_PIN, HIGH); // GREEN ON
+    digitalWrite(LED1_PIN, LOW);  // RED OFF
+    Serial.println("Water is DRINKABLE - GREEN LED ON");
+  } else {
+    digitalWrite(LED2_PIN, LOW);  // GREEN OFF
+    digitalWrite(LED1_PIN, HIGH); // RED ON
+    Serial.println("Water is NOT DRINKABLE - RED LED ON");
+  }
+```
+**Background Cleanup**
+
+**cleanOldBallotData()** drops ballots older than VOTE_TIME_TOLERANCE and removes voters with no valid data.
+
+```{.cpp}
+if (age < VOTE_TIME_TOLERANCE) {
+        voter.votes[newCount++] = voter.votes[j];
+      }
+```
+
+# Testing
 
-*TODO*
+Testing was carried out using a breadboard setup with an ESP32 board simulating a mussel, equipped with both a light sensor and a wire touch sensor.
 
-# Testing - Tanishka
+**Component 1: p5.js Day/Night Simulation**
 
-*TODO*
+* Goal : Verify that the virtual environment affects the mussel’s state.
+* Result: The light sensor reliably registered a change in light level, leading to a detectable difference in gape angle, confirming successful integration between p5.js and sensor logic.
 
-# Product Use - Tanishka
+**Component 2: Sensor Mussels**
 
-*TODO*
+* Goal: Validate the mussel’s ability to interpret light and touch as stress indicators and output a normalized gape angle.
+* Result: The sensor system correctly responded to light changes and touch input, adjusting the internal pace and generating gape angles in the expected range (0–90). All data was successfully encoded in the TLM frame of the BLE beacon.
+
+**Component 3: Voting System**
+
+* Goal: Ensure the system correctly collects, filters, and evaluates gape data to output the correct decision via LED.
+* Result:
+    * Valid gape values within the 1-minute window were correctly added to the vote stack.
+
+    * Majority logic (≥50% OPEN) correctly activated the green LED for "drinkable" status.
+
+    * RED LED activated when fewer than half were open.
+
+    * Outdated values (older than 1 minute) were dropped out of the stack.
+
+**Stress Testing and Timing**
+
+We also tested time-sensitive behavior by introducing delays between beacon broadcasts. The system was able to correctly validate and invalidate (remove from the stack) votes based on the 1-minute age threshold, ensuring decisions are made in real-time.
+
+Overall, the system passed all critical functional tests.
+
+# Program Use Case
+
+This project simulates how mussels can be used to monitor water quality. Each mussel is represented by a small setup made with an ESP32 board, sensors (light and touch), and LED lights.
+
+**Purpose**
+
+The system acts like a group of mussels reacting to their environment. When mussels sense good water conditions, they open more often. This prototype mimics that behavior using sensors and lights, helping users visualize how environmental stress can lead to a “vote” to determine the water quality.
+
+**Users**
+
+Researchers, students, or testers interacting with the system
+
+**User Guide**
+
+1. Set Up the Hardware
+    * Each mussel is made with an ESP32, a light sensor, a touch wire, and LEDs.
+    * ESP32’s built-in LED also shows how often the mussel is “opening” and “closing.”
+2. Start the System
+    * Upload and run the sensor sketch on each ESP32. This sketch makes them behave like mussels that respond to light and touch.
+    * Upload and run the voting sketch on another Arduino that collects input from the mussel ESP32s.
+3. Triggering a Mussel
+    * You can touch the wire or change the light on the sensor to stress the mussel. The frequency of opening and closing reflects on the inbulit LED.
+4. Collecting data
+    * Each mussel sends its state (open/closed) through Bluetooth to the central voting system.
+5. Voting on Water Quality
+    * If the majority of mussels are “open,” the system shows a green LED, meaning the water is drinkable.
+
+The system uses simple sensor input (light and touch) to simulate natural mussel behavior and combines multiple inputs to make a collective decision, just like mussels do in real environments.
 
 # Discussion and reflections
 
@@ -363,7 +584,21 @@ mainly by rewriting the function `alignVotes()`
 
 # Conclusion - Menna
 
-*TODO*
+In answering the  question,
+"How do we simulate and visualise an existing biomonitoring technology
+for its core purpose",
+the prototype demonstrates
+that Arduino and Bluetooth can effectively simulate such interactions.
+Inspired by SYMBIO,
+the project reimagines mussel movements as a form of communication,
+green indicating drinkable water, red signaling otherwise.
+The system explored
+how mussel shell openness could represent a "vote" on water drinkability.
+The voting mechanism was useful
+in making environmental data more interpretable.
+While the biological accuracy is simplified,
+the core concept remains powerful,
+showcasing how biofeedback can be expressed through design.
 
 # Bibliography {.appendix}
 
@@ -389,4 +624,3 @@ mainly by rewriting the function `alignVotes()`
 
 ```{.cpp include="Arduino/vote/vote.ino" code-line-numbers="true"}
 ```
-
-- 
cgit v1.2.3

