diff options
Diffstat (limited to 'Arduino/sensor/sensor.ino')
-rw-r--r-- | Arduino/sensor/sensor.ino | 215 |
1 files changed, 215 insertions, 0 deletions
diff --git a/Arduino/sensor/sensor.ino b/Arduino/sensor/sensor.ino new file mode 100644 index 0000000..8d43b22 --- /dev/null +++ b/Arduino/sensor/sensor.ino @@ -0,0 +1,215 @@ +// 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 + +/// Sensor mussel - an Arduino sketch to emulate a mussel biosensor +/// +/// @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 "BLEBeacon.h" +#include "BLEAdvertising.h" +#include "BLEEddystoneTLM.h" + +// Adjust these for production use +// +// * BEACON_NAME must be unique within deployment +// * BEACON_UUID should be unique for each deployment +// +// @see https://www.uuidgenerator.net/ +#define BEACON_NAME "Dummy mussel sensor" +#define BEACON_UUID "00000000-0000-0000-0000-000000000000" + +// maximum accumulated stress +#define STRESS_MAX 50 + +// light sensor +#define LIGHT_PIN 34 +#define DARKNESS_MAX 1000 + +// arduino-esp32 Touch sensor +#define TOUCH_PIN T0 // T0 is GPIO4 +#define TOUCH_THRESHOLD 40 + +// arduino-esp32 LED PWM Controller (LEDC) as pacemaker for gaping rhythm +#define LED_PIN LED_BUILTIN +#define LEDC_BITS 7 +#define LEDC_FREQ 500 +#define LEDC_START_DUTY 0 +#define LEDC_TARGET_DUTY 90 +#define LEDC_CALM_PACE 3000 +#define LEDC_STRESSED_PACE 400 + +// pacemaker variables +int stress = 0; +bool touch_detected = false; +int pace = LEDC_STRESSED_PACE; +bool fade_ended = false; +bool fade_in = true; + +// pointer to control Bluetooth networking +BLEAdvertising *pAdvertising; + +// Touch sensor callback +void gotTouch() { +// keepPace(); + touch_detected = true; + pace = LEDC_STRESSED_PACE; +} + +// pacemaker end-of-fade Interrupt Service Routine (ISR) a.k.a. callback +void ARDUINO_ISR_ATTR LED_FADE_ISR() { + fade_ended = true; + keepPace(); +} + +// stress-inducing touch callback +void beginTouchDetection() { + touchAttachInterrupt(TOUCH_PIN, gotTouch, TOUCH_THRESHOLD); + log_d("touch detected"); +} + +// pacemaker initialization +void beginPace() { + + // Setup pacemaker timer + ledcAttach(LED_PIN, LEDC_FREQ, LEDC_BITS); + + // fade in once uncontrolled and then begin fade out with ISR + ledcFade(LED_PIN, LEDC_START_DUTY, LEDC_TARGET_DUTY, pace); + delay(pace); + ledcFadeWithInterrupt(LED_PIN, LEDC_TARGET_DUTY, LEDC_START_DUTY, + pace, LED_FADE_ISR); +} + +// pacemaker maintenance +void keepPace() { + +// if (fade_ended || touch_detected) { + if (fade_ended) { + fade_ended = false; + + // stress management + if (touch_detected) { + touch_detected = false; + log_i("Stressed by touch!"); + if (stress < STRESS_MAX) { + stress = stress + 10; + } + } else if (stress > 0) { + stress--; + if (stress <= 0) { + pace = LEDC_CALM_PACE; + log_i("Calmed down..."); + } else { + log_i("Still stressed..."); + } + } else { + pace = LEDC_CALM_PACE; + } + + // begin fade at decided direction and pace + ledcFadeWithInterrupt(LED_PIN, + fade_in ? LEDC_START_DUTY : LEDC_TARGET_DUTY, + fade_in ? LEDC_TARGET_DUTY : LEDC_START_DUTY, + pace, LED_FADE_ISR); + + // remember next fade direction + fade_in = !fade_in; + } +} + +// read light intensity and return its non-zero capped value +int getLightIntensity() { + int value = analogRead(LIGHT_PIN); + if (value > DARKNESS_MAX) + value = DARKNESS_MAX; + log_i("light intensity: %d", value); + + return DARKNESS_MAX - value; +} + +// fake gape angle as pacemaker position dampened by light intensity +int resolveGapeAngle() { + int paceAngle = ledcRead(LED_PIN); + log_i("pacemaker value: %d", value); + + int lightIntensity = getLightIntensity(); + log_i("light intensity: %d", value); + + int gapeAngle = paceAngle * lightIntensity / DARKNESS_MAX; + + // misuse error-only log level for plot-friendly output +#if ARDUHAL_LOG_LEVEL == ARDUHAL_LOG_LEVEL_ERROR + Serial.printf("pace_angle:%d light/10:%d gape_angle:%d\n", + paceAngle, lightIntensity/10, gapeAngle); +#endif + + return gapeAngle; +} + +// Encode static Bluetooth beacon advertisement data +void setBeaconAdvertisement() { + BLEAdvertisementData oAdvertisementData = BLEAdvertisementData(); + oAdvertisementData.setName(BEACON_NAME); + pAdvertising->setAdvertisementData(oAdvertisementData); +} + +// Encode variable Bluetooth beacon service data +void setBeaconServiceData(int angle) { + BLEEddystoneTLM EddystoneTLM; + EddystoneTLM.setTemp(angle); + log_i("Gape angle: %.2f°", EddystoneTLM.getTemp()); + + BLEAdvertisementData oScanResponseData = BLEAdvertisementData(); + oScanResponseData.setServiceData( + BLEUUID((uint16_t)0xFEAA), + String( + EddystoneTLM.getData().c_str(), + EddystoneTLM.getData().length())); + pAdvertising->setScanResponseData(oScanResponseData); +} + +void setup() { + + // enable logging to serial + Serial.begin(115200); + esp_log_level_set("*", ESP_LOG_DEBUG); + if (BEACON_UUID == "00000000-0000-0000-0000-000000000000") + Serial.println("Please set a deployment-wide unique BEACON_UUID"); + + beginPace(); + beginTouchDetection(); + + // setup Bluetooth + BLEDevice::init(BEACON_NAME); + pAdvertising = BLEDevice::getAdvertising(); + setBeaconAdvertisement(); + setBeaconServiceData(resolveGapeAngle()); + pAdvertising->start(); +} + +void loop() { + + // update Bluetooth beacon service data + setBeaconServiceData(resolveGapeAngle()); + + delay(500); +} |