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