/// 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 /// /// * v0.0.1 /// * initial release to radicle /// /// @version 0.0.1 /// @see /// @see #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; }