summaryrefslogtreecommitdiff
path: root/main.ino
blob: e5566de432db545d46212baa16c0aaccd15d57c1 (plain)
  1. // Pill dispenser
  2. // Inspired by article "Blink Without Delay"
  3. // https://docs.arduino.cc/built-in-examples/digital/BlinkWithoutDelay
  4. // Inspired by basic usage documentation of GEM library
  5. // https://github.com/Spirik/GEM
  6. #include <EasyButton.h>
  7. #include <BlinkControl.h>
  8. const int longpress_duration = 2000; // time in milliseconds
  9. // use built-in buttons and LEDs when possible
  10. #if defined(ESP32)
  11. // dirty hack: assume that any ESP32 board is a Lilygo T5 V2.2
  12. #define LILYGO_T5_V22
  13. #include "boards.h" // source: https://github.com/lewisxhe/GxEPD/blob/master/src/boards.h
  14. #define HAS_DISPLAY
  15. const byte BUTTON_PIN_MOVE = BUTTON_1;
  16. const byte BUTTON_PIN_SELECT = BUTTON_2;
  17. const byte BUTTON_PIN_ACTION = BUTTON_3;
  18. BlinkControl led(LED_PIN);
  19. #define SERVO_PIN 3
  20. #else
  21. const byte BUTTON_PIN_MOVE = 4;
  22. const byte BUTTON_PIN_SELECT = 5;
  23. const byte BUTTON_PIN_ACTION = 2;
  24. BlinkControl led(LED_BUILTIN); // pin 13 is built-in LED on many Arduino boards
  25. #define SERVO_PIN 3
  26. #endif
  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. #if defined(HAS_DISPLAY)
  33. #define ENABLE_GxEPD2_GFX 0
  34. #include <GxEPD2_BW.h>
  35. #include <Fonts/FreeMonoBold9pt7b.h>
  36. // source: https://github.com/ZinggJM/GxEPD2/blob/master/examples/GxEPD2_Example/GxEPD2_display_selection.h#L192
  37. GxEPD2_BW<GxEPD2_290, GxEPD2_290::HEIGHT> displayEPaper(GxEPD2_290(EPD_CS, EPD_DC, EPD_RSET, EPD_BUSY));
  38. // FIXME: really disable GLCD, since it cannot build on ESP32
  39. // as dirty workaround, edit file `libraries/GEM/src/config.h`
  40. #define GEM_DISABLE_GLCD 1
  41. #include <GEM_adafruit_gfx.h>
  42. // Create variables that will be editable through the menu and assign them initial values
  43. int number = -512;
  44. boolean enablePrint = false;
  45. // Create two menu item objects of class GEMItem, linked to number and enablePrint variables
  46. GEMItem menuItemInt("Number:", number);
  47. GEMItem menuItemBool("Enable print:", enablePrint);
  48. // Create menu button that will trigger printData() function. It will print value of our number variable
  49. // to Serial monitor if enablePrint is true. We will write (define) this function later. However, we should
  50. // forward-declare it in order to pass to GEMItem constructor
  51. void printData(); // Forward declaration
  52. GEMItem menuItemButton("Print", printData);
  53. // Create menu page object of class GEMPage. Menu page holds menu items (GEMItem) and represents menu level.
  54. // Menu can have multiple menu pages (linked to each other) with multiple menu items each
  55. GEMPage menuPageMain("Main Menu");
  56. GEM_adafruit_gfx menu(displayEPaper, GEM_POINTER_ROW, GEM_ITEMS_COUNT_AUTO);
  57. #endif
  58. #include <Servo.h>
  59. Servo servo;
  60. int servo_angle = 89;
  61. int servo_angle_delta = 3;
  62. const int servo_angle_min = 0;
  63. const int servo_angle_max = 90;
  64. const int servo_mode = 1;
  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. #if defined(HAS_DISPLAY)
  82. displayEPaper.init(115200, true, 2, false);
  83. displayEPaper.setTextColor(GxEPD_BLACK);
  84. displayEPaper.firstPage();
  85. do
  86. {
  87. displayEPaper.fillScreen(GxEPD_WHITE);
  88. // comment out next line to have no or minimal Adafruit_GFX code
  89. displayEPaper.print("Hello World!");
  90. }
  91. while (displayEPaper.nextPage());
  92. menu.init();
  93. setupMenu();
  94. menu.drawMenu();
  95. #endif
  96. servo.attach(SERVO_PIN);
  97. servo.write(servo_angle); // place serve at initial position
  98. }
  99. void loop() {
  100. led.loop();
  101. button_move.read();
  102. button_select.read();
  103. button_action.read();
  104. servoLoop();
  105. }
  106. #if (ESP32)
  107. IRAM_ATTR // avoid crash e.g. when other code reads SD card
  108. #endif
  109. void onButtonMovePressed() {
  110. Serial.println("button MOVE clicked");
  111. led.blink2();
  112. }
  113. #if (ESP32)
  114. IRAM_ATTR // avoid crash e.g. when other code reads SD card
  115. #endif
  116. void onButtonMoveLongpressed() {
  117. Serial.println("button MOVE long-clicked");
  118. led.off();
  119. }
  120. #if (ESP32)
  121. IRAM_ATTR // avoid crash e.g. when other code reads SD card
  122. #endif
  123. void onButtonSelectPressed() {
  124. Serial.println("button SELECT clicked");
  125. led.on();
  126. }
  127. #if (ESP32)
  128. IRAM_ATTR // avoid crash e.g. when other code reads SD card
  129. #endif
  130. void onButtonActionPressed() {
  131. Serial.println("button ACTION clicked");
  132. led.fastBlinking();
  133. servo_should_move = 1;
  134. }
  135. #if defined(HAS_DISPLAY)
  136. void setupMenu() {
  137. // Add menu items to menu page
  138. menuPageMain.addMenuItem(menuItemInt);
  139. menuPageMain.addMenuItem(menuItemBool);
  140. menuPageMain.addMenuItem(menuItemButton);
  141. // Add menu page to menu and set it as current
  142. menu.setMenuPageCurrent(menuPageMain);
  143. }
  144. void printData() {
  145. // If enablePrint flag is set to true (checkbox on screen is checked)...
  146. if (enablePrint) {
  147. // ...print the number to Serial
  148. Serial.print("Number is: ");
  149. Serial.println(number);
  150. } else {
  151. Serial.println("Printing is disabled, sorry:(");
  152. }
  153. }
  154. #endif
  155. void servoLoop() {
  156. if (servo_should_move == 1) {
  157. servo_angle = servo_angle + servo_angle_delta;
  158. if (servo_angle >= servo_angle_max) {
  159. servo_angle_delta = -servo_angle_delta;
  160. if (servo_mode == 1) {
  161. servo_should_move = 0;
  162. }
  163. }
  164. if (servo_angle <= servo_angle_min) {
  165. servo_angle_delta = -servo_angle_delta;
  166. if (servo_mode == 2) {
  167. servo_should_move = 0;
  168. }
  169. }
  170. servo.write(servo_angle);
  171. delay(10);
  172. }
  173. }