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