// SPDX-FileCopyrightText: 2025 Amal Mazrah // SPDX-FileCopyrightText: 2025 Jonas Smedegaard // SPDX-FileCopyrightText: 2025 Mennatullah Hatim Kassim // SPDX-FileCopyrightText: 2025 Noor Ahmad // SPDX-FileCopyrightText: 2025 Tanishka Suwalka // SPDX-License-Identifier: GPL-3.0-or-later /// Sensor mussel - an Arduino sketch to emulate a mussel biosensor /// /// * v0.0.3 /// * rewrite sensor as sketch, /// using ESP32 LEDC, Touch sensor and Logging library /// /// * v0.0.2 /// * rewrite attitude #2 to also handle button press /// * add voting functions and example /// /// * v0.0.1 /// * initial release to radicle /// /// @version 0.0.3 /// @see /// @see #include "mylog.h" #include "BLEDevice.h" #include "BLEBeacon.h" #include "BLEAdvertising.h" #include "BLEEddystoneTLM.h" // Adjust these for production use // // * BEACON_UUID should be unique for each deployment // * INSTANCE_ID should be unique for each sensor // // @see https://www.uuidgenerator.net/ #define BEACON_NAME "Dummy mussel sensor" #define BEACON_UUID "00000000-0000-0000-0000-000000000000" #define INSTANCE_ID "0xbeac0101" #define STRESS_MAX 50 // touch sensor using ESP32 Touch sensor // https://docs.espressif.com/projects/arduino-esp32/en/latest/api/touch.html #define TOUCH_PIN T0 // T0 is GPIO4 #define TOUCH_THRESHOLD 40 // pacemaker using ESP32 LED PWM Controller (LEDC) // https://docs.espressif.com/projects/arduino-esp32/en/latest/api/ledc.html #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 // Bluetooth beacon #define BEACON_POWER ESP_PWR_LVL_N12 int stress = 0; bool touch_detected = false; int pace = LEDC_STRESSED_PACE; bool fade_ended = false; bool fade_in = true; BLEAdvertising *pAdvertising; // Touch sensor callback void gotTouch() { // keepPace(); touch_detected = true; pace = LEDC_STRESSED_PACE; } // pacemaker Interrupt Service Routine (ISR) void ARDUINO_ISR_ATTR LED_FADE_ISR() { fade_ended = true; keepPace(); } 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 supervised 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 in opposite direction at decided pace ledcFadeWithInterrupt(LED_PIN, fade_in ? LEDC_START_DUTY : LEDC_TARGET_DUTY, fade_in ? LEDC_TARGET_DUTY : LEDC_START_DUTY, pace, LED_FADE_ISR); fade_in = !fade_in; } } // Sensor reading int getGapeAngle() { return ledcRead(LED_PIN); } // Construct Bluetooth beacon void setBeacon(int angle) { BLEEddystoneTLM EddystoneTLM; EddystoneTLM.setTemp(angle); log_i("Gape angle: %.2f°", EddystoneTLM.getTemp()); BLEAdvertisementData oAdvertisementData = BLEAdvertisementData(); BLEAdvertisementData oScanResponseData = BLEAdvertisementData(); oScanResponseData.setServiceData(BLEUUID( (uint16_t)0xFEAA), String(EddystoneTLM.getData().c_str(), EddystoneTLM.getData().length())); oAdvertisementData.setName(BEACON_NAME); pAdvertising->setAdvertisementData(oAdvertisementData); pAdvertising->setScanResponseData(oScanResponseData); } void setup() { // enable logging (when also raised in IDE: Tools -> Core Debug Level) Serial.begin(115200); esp_log_level_set("*", ESP_LOG_DEBUG); Serial.begin(115200); beginPace(); beginTouchDetection(); // setup Bluetooth if (BEACON_UUID == "00000000-0000-0000-0000-000000000000") log_w("Please set a unique BEACON_UUID"); BLEDevice::init("TLMBeacon"); BLEDevice::setPower(BEACON_POWER); pAdvertising = BLEDevice::getAdvertising(); setBeacon(getGapeAngle()); pAdvertising->start(); } void loop() { setBeacon(getGapeAngle()); delay(500); }