summaryrefslogtreecommitdiff
path: root/main.ino
blob: c67a90fb2deee97fee714bd0116cc873d773e986 (plain)
  1. // Pill dispenser
  2. // Requires libraries EasyButton, BlinkControl, FastLED, ServoEasing
  3. // Optionally requires libraries GxEPD2, GEM
  4. #include <EasyButton.h>
  5. #include <BlinkControl.h>
  6. const int longpress_duration = 2000; // time in milliseconds
  7. // use built-in buttons and LEDs when possible
  8. #if defined(ESP32)
  9. // dirty hack: assume that any ESP32 board is a Lilygo T5 V2.2
  10. #define LILYGO_T5_V22
  11. #include "boards.h" // source: https://github.com/lewisxhe/GxEPD/blob/master/src/boards.h
  12. #define HAS_DISPLAY
  13. const byte BUTTON_PIN_MOVE = BUTTON_1;
  14. const byte BUTTON_PIN_SELECT = BUTTON_2;
  15. const byte BUTTON_PIN_ACTION = BUTTON_3;
  16. BlinkControl led(LED_PIN);
  17. #define LEDS_PIN 12
  18. #define SERVO_PIN 5 // see ServoEasing PinDefinitionsAndMore.h
  19. #else
  20. const byte BUTTON_PIN_MOVE = 4;
  21. const byte BUTTON_PIN_SELECT = 5;
  22. const byte BUTTON_PIN_ACTION = 2;
  23. BlinkControl led(LED_BUILTIN); // pin 13 is built-in LED on many Arduino boards
  24. #define LEDS_PIN 6
  25. #define SERVO_PIN 9 // see ServoEasing PinDefinitionsAndMore.h
  26. #endif
  27. #define LEDS_AMOUNT 10
  28. // serial port speed
  29. #define BAUDRATE 115200
  30. EasyButton button_move(BUTTON_PIN_MOVE);
  31. EasyButton button_select(BUTTON_PIN_SELECT);
  32. EasyButton button_action(BUTTON_PIN_ACTION);
  33. #include <FastLED.h>
  34. CRGB leds[LEDS_AMOUNT];
  35. #if defined(HAS_DISPLAY)
  36. #define ENABLE_GxEPD2_GFX 0
  37. #include <GxEPD2_BW.h>
  38. #include <Fonts/FreeMonoBold9pt7b.h>
  39. // source: https://github.com/ZinggJM/GxEPD2/blob/master/examples/GxEPD2_Example/GxEPD2_display_selection.h#L192
  40. GxEPD2_BW<GxEPD2_290, GxEPD2_290::HEIGHT> displayEPaper(GxEPD2_290(EPD_CS, EPD_DC, EPD_RSET, EPD_BUSY));
  41. // FIXME: really disable GLCD, since it cannot build on ESP32
  42. // as dirty workaround, edit file `libraries/GEM/src/config.h`
  43. #define GEM_DISABLE_GLCD 1
  44. #include <GEM_adafruit_gfx.h>
  45. // Create variables that will be editable through the menu and assign them initial values
  46. int number = -512;
  47. boolean enablePrint = false;
  48. // Create two menu item objects of class GEMItem, linked to number and enablePrint variables
  49. GEMItem menuItemInt("Number:", number);
  50. GEMItem menuItemBool("Enable print:", enablePrint);
  51. // Create menu button that will trigger printData() function. It will print value of our number variable
  52. // to Serial monitor if enablePrint is true. We will write (define) this function later. However, we should
  53. // forward-declare it in order to pass to GEMItem constructor
  54. void printData(); // Forward declaration
  55. GEMItem menuItemButton("Print", printData);
  56. // Create menu page object of class GEMPage. Menu page holds menu items (GEMItem) and represents menu level.
  57. // Menu can have multiple menu pages (linked to each other) with multiple menu items each
  58. GEMPage menuPageMain("Main Menu");
  59. GEM_adafruit_gfx menu(displayEPaper, GEM_POINTER_ROW, GEM_ITEMS_COUNT_AUTO);
  60. #endif
  61. #define MAX_EASING_SERVOS 1
  62. #include "ServoEasing.hpp"
  63. ServoEasing servo;
  64. #define SERVO_POS_INIT 0
  65. #define SERVO_POS_MAX 90
  66. volatile int pills_requested = 0;
  67. void setup() {
  68. Serial.begin(BAUDRATE);
  69. while (!Serial) {
  70. ;
  71. }
  72. Serial.println();
  73. button_move.begin();
  74. button_select.begin();
  75. button_action.begin();
  76. button_move.onPressed(onButtonMovePressed);
  77. button_move.onPressedFor(longpress_duration, onButtonMoveLongpressed);
  78. button_select.onPressed(onButtonSelectPressed);
  79. button_action.onPressed(onButtonActionPressed);
  80. led.begin();
  81. led.breathe(2000);
  82. FastLED.addLeds<NEOPIXEL, LEDS_PIN>(leds, LEDS_AMOUNT);
  83. #if defined(HAS_DISPLAY)
  84. displayEPaper.init(115200, true, 2, false);
  85. displayEPaper.setTextColor(GxEPD_BLACK);
  86. displayEPaper.firstPage();
  87. do
  88. {
  89. displayEPaper.fillScreen(GxEPD_WHITE);
  90. // comment out next line to have no or minimal Adafruit_GFX code
  91. displayEPaper.print("Hello World!");
  92. }
  93. while (displayEPaper.nextPage());
  94. menu.init();
  95. setupMenu();
  96. menu.drawMenu();
  97. #endif
  98. Serial.println(F("Attach servo at pin " STR(SERVO_PIN)));
  99. if (servo.attach(SERVO_PIN, SERVO_POS_INIT) == INVALID_SERVO) {
  100. led.blink2();
  101. Serial.println(F("Error attaching servo"));
  102. }
  103. // Wait for servo to reach start position.
  104. delay(500);
  105. servo.setSpeed(90); // This speed is taken if no further speed argument is given.
  106. }
  107. void loop() {
  108. led.loop();
  109. button_move.read();
  110. button_select.read();
  111. button_action.read();
  112. servoLoop();
  113. }
  114. #if (ESP32)
  115. IRAM_ATTR // avoid crash e.g. when other code reads SD card
  116. #endif
  117. void onButtonMovePressed() {
  118. Serial.println("button MOVE clicked");
  119. led.blink2();
  120. }
  121. #if (ESP32)
  122. IRAM_ATTR // avoid crash e.g. when other code reads SD card
  123. #endif
  124. void onButtonMoveLongpressed() {
  125. Serial.println("button MOVE long-clicked");
  126. led.off();
  127. }
  128. #if (ESP32)
  129. IRAM_ATTR // avoid crash e.g. when other code reads SD card
  130. #endif
  131. void onButtonSelectPressed() {
  132. Serial.println("button SELECT clicked");
  133. led.on();
  134. }
  135. #if (ESP32)
  136. IRAM_ATTR // avoid crash e.g. when other code reads SD card
  137. #endif
  138. void onButtonActionPressed() {
  139. Serial.println("button ACTION clicked");
  140. led.fastBlinking();
  141. pills_requested++;
  142. rolling_green();
  143. }
  144. void rolling_green() {
  145. for (int dot = 0; dot < LEDS_AMOUNT; dot++) {
  146. leds[dot] = CRGB::Green;
  147. FastLED.show();
  148. // clear this LED for the next time around the loop
  149. leds[dot] = CRGB::White;
  150. delay(60);
  151. }
  152. }
  153. #if defined(HAS_DISPLAY)
  154. void setupMenu() {
  155. // Add menu items to menu page
  156. menuPageMain.addMenuItem(menuItemInt);
  157. menuPageMain.addMenuItem(menuItemBool);
  158. menuPageMain.addMenuItem(menuItemButton);
  159. // Add menu page to menu and set it as current
  160. menu.setMenuPageCurrent(menuPageMain);
  161. }
  162. void printData() {
  163. // If enablePrint flag is set to true (checkbox on screen is checked)...
  164. if (enablePrint) {
  165. // ...print the number to Serial
  166. Serial.print("Number is: ");
  167. Serial.println(number);
  168. } else {
  169. Serial.println("Printing is disabled, sorry:(");
  170. }
  171. }
  172. #endif
  173. // source: https://robojax.com/learn/arduino/?vid=robojax_Servo_PB1_move_return
  174. void servoLoop() {
  175. if (pills_requested > 1) {
  176. servo.easeTo(SERVO_POS_MAX);
  177. delay(500);
  178. servo.easeTo(SERVO_POS_INIT);
  179. pills_requested--;
  180. }
  181. }