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