From 02876f961fc8b68f2cb43a23a1d98d89fce11858 Mon Sep 17 00:00:00 2001 From: Jonas Smedegaard Date: Tue, 11 Mar 2025 12:02:07 +0100 Subject: add library Mussel --- Mussel/Mussel.cpp | 205 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 205 insertions(+) create mode 100644 Mussel/Mussel.cpp (limited to 'Mussel/Mussel.cpp') diff --git a/Mussel/Mussel.cpp b/Mussel/Mussel.cpp new file mode 100644 index 0000000..3e13316 --- /dev/null +++ b/Mussel/Mussel.cpp @@ -0,0 +1,205 @@ +/// Mussel - a small library for Arduino to emulate a mussel biosensor +/// +/// 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 + +#include "Mussel.h" +#include "Arduino.h" + +/// Main constructor +/// +/// @param attitude behavioral profile as integer +Mussel::Mussel(int attitude) { + _attitude = attitude; +} + +/// Constructor for attitudes using a pin +/// +/// @param attitude behavioral profile as integer +/// @param pin Used pin as uint8_t +Mussel::Mussel(int attitude, uint8_t pin) { + _attitude = attitude; + _pin = pin; +} + +/// Constructor for attitudes using a pin and certain type of sensor +/// +/// @param attitude behavioral profile as integer +/// @param pin Used pin as uint8_t +/// @param type type of sensor as uint8_t +Mussel::Mussel(int attitude, uint8_t pin, uint8_t type) +#ifdef DHT_H + : mussel_dht(pin, type) +#endif +{ + _attitude = attitude; + _pin = pin; +} + +/// Setup function +void Mussel::begin() { + switch(_attitude) { +#ifdef DHT_H + case 3: + mussel_dht.begin(); + break; +#endif + case 4: + // use INPUT_PULLDOWN to signal when the button is held down + // (not PULLUP which signals when it is released) + pinMode(_pin, INPUT_PULLDOWN); + break; + case 5: + _boolState = HIGH; + _count = 0; + _time = 0; + + // Enable internal pull-up resistor for button + pinMode(_pin, INPUT_PULLUP); + break; + case 6: + _boolState = HIGH; + break; + } +} + +/// Description of mussel +/// +/// @return name and attitude of mussel as String +String Mussel::desc() { + String _str; + + switch(_attitude) { + case 1: + _str = "closed 10 seconds every 50 seconds"; + break; + case 2: + _str = "closed 4 seconds every 8 seconds"; + break; + case 3: + _str = "closed when cold"; + break; + case 4: + _str = "closed when button is pushed"; + break; + case 5: + _str = "changes state when button is pushed"; + break; + case 6: + _str = "changes state on ON/OFF command"; + break; + default: + _str = "undefined [" + static_cast(_attitude) + "]"; + break; + } + + return _str; +} + +/// Sensor reading +/// +/// * Values 0-99 is a measured relative mussel gape size +/// * Values 100-254 are reserved for future use +/// * Value 255 is an internal error +/// +/// @return relative gape size as value 0-255 encoded as byte +byte Mussel::read() { + byte _byte; + + switch(_attitude) { + case 1: + // 42 if current second modulo 60 is below 50, else 2 + _byte = static_cast( + (static_cast(millis() / 1000) % 60) < 50 + ? 42 + : 2); + break; + case 2: + // 42 if current second modulo 12 is below 9, else 2 + _byte = static_cast( + (static_cast(millis() / 1000) % 12) < 9 + ? 42 + : 2); + break; + case 3: +#ifdef DHT_H + // temperature in Celsius + _byte = static_cast( + mussel_dht.readTemperature()); +#else + _byte = 255; +#endif + break; + case 4: + // 2 if button is pressed, else 42 + _byte = static_cast( + digitalRead(_pin) == HIGH + ? 2 + : 42); + break; + case 5: { + bool _reading = digitalRead(_pin); // Read button state + + // Debounce logic: + // Ensures a single press isn't detected multiple times + if (_reading != _boolState) { + _time = millis(); // Reset debounce timer + } + + if ((millis() - _time) > MUSSEL_DEBOUNCE_DELAY) { + // Check for button press (transition from HIGH to LOW) + if (_reading == LOW && _boolState == HIGH) { + _count++; // Increment click count + if (_count > 3) { + _count = 0; // Reset cycle after 3 clicks + } + switch (_count) { + case 1: _byte = 2; break; // State: Angry + case 2: _byte = 42; break; // State: Happy + case 3: _byte = 15; break; // State: Unsure + case 0: _byte = 99; break; // State: Off + } + } + } + + _boolState = _reading; // Update button state + break; + } + case 6: + if (Serial.available() > 0) { + String command = Serial.readStringUntil('\n'); + command.trim(); + + if (command.equalsIgnoreCase("ON")) { + _boolState = HIGH; + } + else if (command.equalsIgnoreCase("OFF")) { + _boolState = LOW; + } + } + _byte = _boolState == HIGH + ? 42 + : 2; + break; + default: + _byte = 255; + break; + } + + return _byte; +} + +/// Dump internal variables, formatted for use with Serial Plotter +/// +/// @return internal variables as String +String Mussel::debug() { + return static_cast( + "pin:") + _pin + + "\tboolState:" + _boolState + + "\ttime:" + _time + + "\tcount:" + _count; +} -- cgit v1.2.3