aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonas Smedegaard <dr@jones.dk>2025-04-20 21:21:02 +0200
committerJonas Smedegaard <dr@jones.dk>2025-04-20 21:21:02 +0200
commit9345c9984644c49cd69ab3636f2a9e546f2ac83b (patch)
tree6458809e8105761bcf68705248362fad59f6b94b
parent09a3fbecce5ae359a4d91d58edc58cbe4310a406 (diff)
update content
-rw-r--r--report.qmd346
1 files changed, 204 insertions, 142 deletions
diff --git a/report.qmd b/report.qmd
index 8c0866a..6bd22d4 100644
--- a/report.qmd
+++ b/report.qmd
@@ -223,7 +223,7 @@ The physical setup of our prototype consists
of a simple Arduino-based circuit.
The following components were used:
-ESP 32: Runs the core program that simulates mussel behavior,
+ESP32: Runs the core program that simulates mussel behavior,
reads sensor data and outputs voting signals.
The logical parts of the ESP32 used in our setup
include Bluetooth, a touch sensor input, LEDC (LED control)
@@ -257,21 +257,23 @@ In our project, we simulate this behavior using programmable hardware.
The system is composed of three major components:
-* p5.js Interface --
- Simulating environmental changes like day and night.
- Allows the user to control the light conditions using a button.
-* Sensor (Arduino) --
- Each sensor simulates a mussel.
- It reads light and touch input,
- changes internal "stress" levels accordingly,
- and outputs a "gape angle" via BLE
- using the Eddystone TLM protocol.
-* Voting System (Arduino) --
- This unit scans BLE beacons sent by sensor mussels.
- Each beacon includes a simulated gape angle.
- The latest measurements per mussel are stored and evaluated,
- if the data is recent and valid.
- It determines whether the water is drinkable using a voting rule.
+p5.js Interface --
+simulates environmental changes like day and night.
+Allows the user to control the light conditions using a button.
+
+Sensor (Arduino) --
+simulates a mussel.
+It reads light and touch input,
+changes internal "stress" levels accordingly,
+and outputs a "gape angle" via BLE
+using the Eddystone TLM protocol.
+
+Voting System (Arduino) --
+scans BLE beacons sent by sensor mussels.
+Each beacon includes a simulated gape angle.
+The latest measurements per mussel are stored and evaluated,
+if the data is recent and valid.
+It determines whether the water is drinkable using a voting rule.
```{.plantuml}
!include components.puml
@@ -280,37 +282,56 @@ The system is composed of three major components:
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.
-
-3. Vote (Arduino)
-
- 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.
-
- * 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.
+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.
+
+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.
+
+Vote (Arduino):
+Takes the sensor data from the mussels and turns it into a voting system
+to decide if the water is drinkable or not.
+
+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 indicates
+that 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
@@ -318,21 +339,27 @@ shows how the different parts of the system talk to each other:
!include Arduino/sensor/sensor.puml
```
-This UML Diagram explains how a sensor system for a mussel behaviour simulator works.
+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.
+Instantiate mussel object --
+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.
+Instantiate bluetooth object --
+sets up the Bluetooth system for communication.
```{.cpp}
BLEDevice::init(BEACON_NAME);
pAdvertising = BLEDevice::getAdvertising(;
```
-**Init** **Block**
+### Init block
-3. *Setup* *mussel* *sensors* - Initializes the sensors attached to the mussel.
+Setup mussel sensors --
+initializes the sensors attached to the mussel.
```{.cpp}
beginPace();
@@ -340,7 +367,8 @@ beginTouchDetection();
```
These set up the LED-based pacemaker and touch sensor.
-4. *Setup* *bluetooth* *beacon* - Preparing the bluetooth device to send data.
+Setup bluetooth beacon --
+prepares the bluetooth device to send data.
```{.cpp}
setBeaconAdvertisement();
@@ -348,30 +376,34 @@ setBeaconServiceData(resolveGapeAngle());
pAdvertising->start();
```
-**Loop (each 500ms)**
+### Loop (each 500ms)
-5. *Read sensors* - Get data from the mussel.
+Read sensors --
+gets 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.
+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.
+Add gape angle to beacon --
+attaches the processed gape angle to the bluetooth beacon.
```{.cpp}
setBeaconServiceData(resolveGapeAngle())
```
-**Parallel 100ms Loop**
+### Parallel 100ms Loop
-8. *Broadcast beacon* - continuously sends bluetooth beacon data
+Broadcast beacon --
+continuously sends bluetooth beacon data.
```{.cpp}
pAdvertising->start()
@@ -385,7 +417,9 @@ BLE stack itself handles frequent broadcasting.
```
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.
+Instantiate mussel object --
+an abstraction using a Voter strct.
+A global array is initialized to hold mussels and their latest votes.
```{.cpp}
struct Voter {
@@ -396,50 +430,39 @@ struct Voter {
Voter voters[VOTER_MAX];
```
-2. *Instantiate bluetooth object* - BLE stack is initialized, responsible for detecting nearby beacons.
+Instantiate bluetooth object --
+BLE stack is initialized, responsible for detecting nearby beacons.
```{.cpp}
BLEDevice::init("");
pBLEScan = BLEDevice::getScan();
```
-**Init**
+### Init
-3. *Setup mussel voting* - **setup()** initializes LEDs, Serial logging, and sets the BLE callbacks.
+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.
+Setup bluetooth scanner --
+`MyAdvertisedDeviceCallbacks()` starts the bluetooth scanning
+to detect the nearby devices.
-**Event-Driven Component**
+### Event-Driven Component
-5. *Each beacon detected (Trigger)* - The conditional event **MyAdvertisedDeviceCallbacks** method is triggered on beacon detection.
+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.
+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).
+`collectBallotData()` adds or updates the voter's record
+with the new vote (FIFO for 5 ballots max).
-**Continuous Loop (500 ms)**
+### 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
+Align beacon data as ballots --
+checks each mussel's latest gape value
+and classifies it as Open, Closed, or Invalid-
```{.cpp}
String state = (latest.measure >= 0 && latest.measure < 40)
@@ -449,7 +472,8 @@ String state = (latest.measure >= 0 && latest.measure < 40)
: "Invalid reading";
```
-8. *Qualify Ballot for a vote* - **qualifyBallot()** checks if the vote is within the 1-minute validity window.
+Qualify Ballot for a vote --
+checks if the vote is within the 1-minute validity window.
```{.cpp}
if (age <= VOTE_TIME_TOLERANCE) {
@@ -458,27 +482,22 @@ if (age <= VOTE_TIME_TOLERANCE) {
return "valid";
```
-9. *Conclude vote result* - **concludeVote()** counts valid ballots and checks how many are "Open". If majority are OPEN the water is drinkable.
+Conclude vote result --
+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.
+act on vote result --
+turns LEDs on/off and prints status
+based on the water dinkablility.
-```{.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**
+### Background Cleanup
-**cleanOldBallotData()** drops ballots older than VOTE_TIME_TOLERANCE and removes voters with no valid data.
+`cleanOldBallotData()` drops ballots
+older than VOTE_TIME_TOLERANCE
+and removes voters with no valid data.
```{.cpp}
if (age < VOTE_TIME_TOLERANCE) {
@@ -490,62 +509,105 @@ if (age < VOTE_TIME_TOLERANCE) {
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.
-**Component 1: p5.js Day/Night Simulation**
+### Component 1: p5.js Day/Night Simulation
+
+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.
+
+### Component 2: Sensor Mussels
-* 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.
+Goal: Validate the mussel's ability to interpret light and touch as stress indicators and output a normalized gape angle.
-**Component 2: Sensor Mussels**
+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.
-* 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
-**Component 3: Voting System**
+Goal: Ensure the system correctly collects, filters, and evaluates gape data
+to output the correct decision via LED.
-* 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.
+Result:
- * Majority logic (≥50% OPEN) correctly activated the green LED for "drinkable" status.
+Valid gape values within the 1-minute window were correctly added to the vote stack.
- * RED LED activated when fewer than half were open.
+Majority logic (≥50% OPEN) correctly activated the green LED for "drinkable" status.
- * Outdated values (older than 1 minute) were dropped out of the stack.
+RED LED activated when fewer than half were open.
-**Stress Testing and Timing**
+Outdated values (older than 1 minute) were dropped out of the stack.
-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.
+### 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.
+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**
+## 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.
+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**
+## 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.
+## User Guide
+
+### 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".
+
+### 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.
+
+### 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 builtin LED.
+
+### Collecting data
+
+Each mussel sends its state (open/closed) through Bluetooth
+to the central voting system.
+
+### 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