aboutsummaryrefslogtreecommitdiff
path: root/Mussel/Mussel.cpp
blob: 3e13316e62f8f472b1870c39857238e6bf3b3458 (plain)
  1. /// Mussel - a small library for Arduino to emulate a mussel biosensor
  2. ///
  3. /// SPDX-FileCopyrightText: 2025 Amal Mazrah <mazrah@ruc.dk>
  4. /// SPDX-FileCopyrightText: 2025 Jonas Smedegaard <dr@jones.dk>
  5. /// SPDX-FileCopyrightText: 2025 Mennatullah Hatim Kassim <stud-mennatulla@ruc.dk>
  6. /// SPDX-FileCopyrightText: 2025 Noor Ahmad <noora@ruc.dk>
  7. /// SPDX-FileCopyrightText: 2025 Tanishka Suwalka <tanishkas@ruc.dk>
  8. /// SPDX-License-Identifier: GPL-3.0-or-later
  9. #include "Mussel.h"
  10. #include "Arduino.h"
  11. /// Main constructor
  12. ///
  13. /// @param attitude behavioral profile as integer
  14. Mussel::Mussel(int attitude) {
  15. _attitude = attitude;
  16. }
  17. /// Constructor for attitudes using a pin
  18. ///
  19. /// @param attitude behavioral profile as integer
  20. /// @param pin Used pin as uint8_t
  21. Mussel::Mussel(int attitude, uint8_t pin) {
  22. _attitude = attitude;
  23. _pin = pin;
  24. }
  25. /// Constructor for attitudes using a pin and certain type of sensor
  26. ///
  27. /// @param attitude behavioral profile as integer
  28. /// @param pin Used pin as uint8_t
  29. /// @param type type of sensor as uint8_t
  30. Mussel::Mussel(int attitude, uint8_t pin, uint8_t type)
  31. #ifdef DHT_H
  32. : mussel_dht(pin, type)
  33. #endif
  34. {
  35. _attitude = attitude;
  36. _pin = pin;
  37. }
  38. /// Setup function
  39. void Mussel::begin() {
  40. switch(_attitude) {
  41. #ifdef DHT_H
  42. case 3:
  43. mussel_dht.begin();
  44. break;
  45. #endif
  46. case 4:
  47. // use INPUT_PULLDOWN to signal when the button is held down
  48. // (not PULLUP which signals when it is released)
  49. pinMode(_pin, INPUT_PULLDOWN);
  50. break;
  51. case 5:
  52. _boolState = HIGH;
  53. _count = 0;
  54. _time = 0;
  55. // Enable internal pull-up resistor for button
  56. pinMode(_pin, INPUT_PULLUP);
  57. break;
  58. case 6:
  59. _boolState = HIGH;
  60. break;
  61. }
  62. }
  63. /// Description of mussel
  64. ///
  65. /// @return name and attitude of mussel as String
  66. String Mussel::desc() {
  67. String _str;
  68. switch(_attitude) {
  69. case 1:
  70. _str = "closed 10 seconds every 50 seconds";
  71. break;
  72. case 2:
  73. _str = "closed 4 seconds every 8 seconds";
  74. break;
  75. case 3:
  76. _str = "closed when cold";
  77. break;
  78. case 4:
  79. _str = "closed when button is pushed";
  80. break;
  81. case 5:
  82. _str = "changes state when button is pushed";
  83. break;
  84. case 6:
  85. _str = "changes state on ON/OFF command";
  86. break;
  87. default:
  88. _str = "undefined [" + static_cast<String>(_attitude) + "]";
  89. break;
  90. }
  91. return _str;
  92. }
  93. /// Sensor reading
  94. ///
  95. /// * Values 0-99 is a measured relative mussel gape size
  96. /// * Values 100-254 are reserved for future use
  97. /// * Value 255 is an internal error
  98. ///
  99. /// @return relative gape size as value 0-255 encoded as byte
  100. byte Mussel::read() {
  101. byte _byte;
  102. switch(_attitude) {
  103. case 1:
  104. // 42 if current second modulo 60 is below 50, else 2
  105. _byte = static_cast<byte>(
  106. (static_cast<unsigned long>(millis() / 1000) % 60) < 50
  107. ? 42
  108. : 2);
  109. break;
  110. case 2:
  111. // 42 if current second modulo 12 is below 9, else 2
  112. _byte = static_cast<byte>(
  113. (static_cast<unsigned long>(millis() / 1000) % 12) < 9
  114. ? 42
  115. : 2);
  116. break;
  117. case 3:
  118. #ifdef DHT_H
  119. // temperature in Celsius
  120. _byte = static_cast<byte>(
  121. mussel_dht.readTemperature());
  122. #else
  123. _byte = 255;
  124. #endif
  125. break;
  126. case 4:
  127. // 2 if button is pressed, else 42
  128. _byte = static_cast<byte>(
  129. digitalRead(_pin) == HIGH
  130. ? 2
  131. : 42);
  132. break;
  133. case 5: {
  134. bool _reading = digitalRead(_pin); // Read button state
  135. // Debounce logic:
  136. // Ensures a single press isn't detected multiple times
  137. if (_reading != _boolState) {
  138. _time = millis(); // Reset debounce timer
  139. }
  140. if ((millis() - _time) > MUSSEL_DEBOUNCE_DELAY) {
  141. // Check for button press (transition from HIGH to LOW)
  142. if (_reading == LOW && _boolState == HIGH) {
  143. _count++; // Increment click count
  144. if (_count > 3) {
  145. _count = 0; // Reset cycle after 3 clicks
  146. }
  147. switch (_count) {
  148. case 1: _byte = 2; break; // State: Angry
  149. case 2: _byte = 42; break; // State: Happy
  150. case 3: _byte = 15; break; // State: Unsure
  151. case 0: _byte = 99; break; // State: Off
  152. }
  153. }
  154. }
  155. _boolState = _reading; // Update button state
  156. break;
  157. }
  158. case 6:
  159. if (Serial.available() > 0) {
  160. String command = Serial.readStringUntil('\n');
  161. command.trim();
  162. if (command.equalsIgnoreCase("ON")) {
  163. _boolState = HIGH;
  164. }
  165. else if (command.equalsIgnoreCase("OFF")) {
  166. _boolState = LOW;
  167. }
  168. }
  169. _byte = _boolState == HIGH
  170. ? 42
  171. : 2;
  172. break;
  173. default:
  174. _byte = 255;
  175. break;
  176. }
  177. return _byte;
  178. }
  179. /// Dump internal variables, formatted for use with Serial Plotter
  180. ///
  181. /// @return internal variables as String
  182. String Mussel::debug() {
  183. return static_cast<String>(
  184. "pin:") + _pin
  185. + "\tboolState:" + _boolState
  186. + "\ttime:" + _time
  187. + "\tcount:" + _count;
  188. }