Arduino_Chicken_Brooder.ino 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557
  1. /*
  2. Arduino 5 Button LCD Display Menu
  3. By Joe (grugly at sdf dot org)
  4. https://notabug.org/grugly/Arduino_5_Button_16x2_LCD_Menu
  5. */
  6. // this needs to be here for some reason
  7. #ifdef __cplusplus
  8. extern "C" {
  9. #endif
  10. // Include LCD and sensor libraries
  11. #include "LiquidCrystal.h"
  12. #include "DHT.h"
  13. // Pin assignment
  14. #define LCD_PIN_1 8
  15. #define LCD_PIN_2 9
  16. #define LCD_PIN_3 4
  17. #define LCD_PIN_4 5
  18. #define LCD_PIN_5 6
  19. #define LCD_PIN_6 7
  20. #define BACKLIGHT_PIN 10
  21. #define BUTTON_PIN A0
  22. #define FAN_PIN 11
  23. #define LAMP_PIN 12
  24. #define SENSOR_PIN 13
  25. // Button reference voltage
  26. #define BUTTON_NULL 1023
  27. #define BUTTON_SELECT 639
  28. #define BUTTON_LEFT 408
  29. #define BUTTON_DOWN 255
  30. #define BUTTON_UP 98
  31. #define BUTTON_RIGHT 0
  32. // Init LCD and sensor
  33. LiquidCrystal lcd(LCD_PIN_1, LCD_PIN_2, LCD_PIN_3,
  34. LCD_PIN_4, LCD_PIN_5, LCD_PIN_6);
  35. DHT dht;
  36. // Defaults
  37. int backlight = 0; // Holds backlight status
  38. int brightness = 128; // Brightnes of backlight
  39. int sleep_time = 10000; // Time before screen turns off
  40. int refresh_delay = 5000; // Info display refresh delay
  41. int cycle_delay = 2000; // Work cycle refresh delay
  42. int temp_target = 35; // Optimum temp for our enclosure
  43. int temp_variance = 5; // How far off the temp range before we correct
  44. // Variables to hold current data
  45. int temp = 0; // Temperature
  46. int temp_high = 0; // Highest temp
  47. int temp_low = 99; // Lowest temp
  48. int humid = 0; // Humidity
  49. int humid_high = 0; // Highest humidity
  50. int humid_low = 99; // Lowest humidity
  51. String outstr = ""; // String for LCD output lines
  52. int fan_state = 0; // 1 when fan is running
  53. int lamp_state = 0; // 1 when lamp is on
  54. unsigned long fan_time, fan_duration, fan_average,
  55. lamp_time, lamp_duration, lamp_average;
  56. int fan_times, lamp_times = 0;
  57. // Timers
  58. unsigned long run_duration, wake_time, wake_duration,
  59. last_refresh, last_cycle = 0;
  60. // Displayed text for each menu item (16 char max)
  61. String menu_list[] = {
  62. "Target Temp",
  63. "Temp Variance",
  64. "Brightness",
  65. "Standby Delay",
  66. "Uptime",
  67. "Fan Time",
  68. "Fan Average",
  69. "Lamp Time",
  70. "Lamp Average"
  71. };
  72. // Count and store number of menu items
  73. int items = round((sizeof(menu_list) / sizeof(String) - 1));
  74. int item = 0; // Menu position
  75. int menu_displayed = 0; // Menu displayed if 1
  76. // Custom chars for LCD for welcome screen
  77. byte chicken0[8] = { // High bounce
  78. B00000,
  79. B00000,
  80. B01100,
  81. B11100,
  82. B01111,
  83. B01110,
  84. B00100,
  85. B00000
  86. };
  87. byte chicken1[8] = { // Low bounce
  88. B00000,
  89. B00000,
  90. B00000,
  91. B01100,
  92. B11100,
  93. B01111,
  94. B01110,
  95. B00000
  96. };
  97. byte chicken2[8] = { // Squatting to lay
  98. B00000,
  99. B00000,
  100. B00000,
  101. B00000,
  102. B01100,
  103. B11100,
  104. B01111,
  105. B01110
  106. };
  107. byte chicken3[8] = { // Laying the egg
  108. B00000,
  109. B00000,
  110. B01000,
  111. B00001,
  112. B01100,
  113. B11100,
  114. B01111,
  115. B01110
  116. };
  117. byte egg[8] = { // The egg
  118. B00000,
  119. B00000,
  120. B00000,
  121. B00000,
  122. B00100,
  123. B01110,
  124. B01110,
  125. B00100
  126. };
  127. // Welcome message with a chicken laying an egg
  128. void welcome() {
  129. lcd.clear();
  130. lcd.setCursor(0, 0);
  131. lcd.print("Chicken Brooder");
  132. int i;
  133. int anichar = 0; // Make chicken bounce
  134. for (i = 16; i >= 0; i--) {
  135. lcd.setCursor((i + 1), 1);
  136. lcd.print(" ");
  137. lcd.setCursor(i, 1);
  138. lcd.write(anichar);
  139. if (i == 8) { // Lay egg at char 8
  140. lcd.setCursor(i, 1);
  141. lcd.write(2);
  142. delay(500);
  143. lcd.setCursor(i, 1);
  144. lcd.write(3);
  145. delay(500);
  146. lcd.setCursor((i - 1), 1);
  147. lcd.write(anichar);
  148. lcd.write(4);
  149. i--;
  150. }
  151. // Bounce chicken
  152. if (anichar == 0) { anichar++; }
  153. else { anichar--; }
  154. delay(500);
  155. }
  156. }
  157. void readTemp() {
  158. // Read temperature
  159. temp = round(dht.getTemperature());
  160. if (temp > temp_high) { temp_high = temp; }
  161. if (temp < temp_low) { temp_low = temp; }
  162. return;
  163. }
  164. void readHumid() {
  165. // Read humidity
  166. humid = round(dht.getHumidity());
  167. if (humid > humid_high) { humid_high = humid; }
  168. if (humid < humid_low) { humid_low = humid; }
  169. return;
  170. }
  171. void printTemp() {
  172. // Display temperature
  173. lcd.clear();
  174. lcd.setCursor(0, 0);
  175. outstr = "Temperature: " + String(temp) + 'C';
  176. lcd.print(outstr);
  177. lcd.setCursor(0, 1);
  178. outstr = "L: " + String(temp_low) + "C H: " + String(temp_high) + "C";
  179. lcd.print(outstr);
  180. return;
  181. }
  182. void printHumid() {
  183. // Display humidity
  184. lcd.clear();
  185. lcd.setCursor(0, 0);
  186. outstr = "Humidity: " + String(humid) + '%';
  187. lcd.print(outstr);
  188. lcd.setCursor(0, 1);
  189. outstr = "L: " + String(humid_low) + "% H: " + String(humid_high) + "%";
  190. lcd.print(outstr);
  191. return;
  192. }
  193. // Display statistics
  194. int disp_sw = 0;
  195. void displayInfo() {
  196. if ((run_duration - last_refresh) >= refresh_delay) {
  197. if (disp_sw == 0) {
  198. printTemp();
  199. disp_sw++;
  200. }
  201. else {
  202. printHumid();
  203. disp_sw--;
  204. }
  205. // Reset timer
  206. last_refresh = millis();
  207. }
  208. }
  209. // Read sensors, flick switches, etc..
  210. void doWork() {
  211. if ((run_duration - last_cycle) >= cycle_delay) {
  212. // Get the data
  213. readTemp();
  214. readHumid();
  215. // Fan control
  216. if (temp >= (temp_target + temp_variance) && fan_state == 0) {
  217. digitalWrite(FAN_PIN, HIGH);
  218. fan_state = 1;
  219. fan_time = millis();
  220. fan_times++;
  221. }
  222. if (temp <= temp_target && fan_state == 1) {
  223. digitalWrite(FAN_PIN, LOW);
  224. fan_state = 0;
  225. fan_duration += (run_duration - fan_time);
  226. fan_average = (fan_duration / fan_times);
  227. }
  228. // Lamp control
  229. if (temp <= (temp_target - temp_variance) && lamp_state == 0) {
  230. digitalWrite(LAMP_PIN, HIGH);
  231. lamp_state = 1;
  232. lamp_time = millis();
  233. lamp_times++;
  234. }
  235. if (temp >= temp_target && lamp_state == 1) {
  236. digitalWrite(LAMP_PIN, LOW);
  237. lamp_state = 0;
  238. lamp_duration += (run_duration - lamp_time);
  239. lamp_average = (lamp_duration / lamp_times);
  240. }
  241. }
  242. }
  243. // Read a button press and return number of button pressed
  244. int readButton() {
  245. int button_voltage = analogRead(BUTTON_PIN);
  246. int button_pressed = 0;
  247. if (button_voltage <= (BUTTON_NULL + 10)
  248. && button_voltage >= (BUTTON_NULL - 10)) {
  249. button_pressed = 0;
  250. }
  251. if (button_voltage <= (BUTTON_SELECT + 10)
  252. && button_voltage >= (BUTTON_SELECT - 10)) {
  253. button_pressed = 1;
  254. }
  255. if (button_voltage <= (BUTTON_LEFT + 10)
  256. && button_voltage >= (BUTTON_LEFT - 10)) {
  257. button_pressed = 2;
  258. }
  259. if (button_voltage <= (BUTTON_DOWN + 10)
  260. && button_voltage >= (BUTTON_DOWN - 10)) {
  261. button_pressed = 3;
  262. }
  263. if (button_voltage <= (BUTTON_UP + 10)
  264. && button_voltage >= (BUTTON_UP - 10)) {
  265. button_pressed = 4;
  266. }
  267. if (button_voltage <= (BUTTON_RIGHT + 10)
  268. && button_voltage >= (BUTTON_RIGHT - 10)) {
  269. button_pressed = 5;
  270. }
  271. // Wait until button is released before we return
  272. while (button_voltage <= (BUTTON_NULL - 10)) {
  273. button_voltage = analogRead(BUTTON_PIN);
  274. delay(100);
  275. }
  276. if (button_pressed > 0) {
  277. wake_time = millis(); // Reset the backlight timer
  278. }
  279. return button_pressed;
  280. }
  281. // Display the menu item on the LCD
  282. void displayItem(int item) {
  283. // Clear the screen and print the item title
  284. lcd.clear();
  285. lcd.print(String(menu_list[item]));
  286. // Display the current value in a readable form
  287. // Compiler says these need defining outside the case statement!
  288. int percent, sec;
  289. unsigned long d, h, m, s;
  290. switch (item) {
  291. case 0: // Target temperature
  292. lcd.setCursor(0, 1);
  293. lcd.print(String(temp_target) + "C");
  294. break;
  295. case 1: // Temperature variance
  296. lcd.setCursor(0, 1);
  297. lcd.print(String(temp_variance) + "C");
  298. break;
  299. case 2: // Brightness is displayed as a percentage
  300. // We don't want to turn the screen completely off
  301. // and a value of 256 will overflow it so we trim
  302. // the values. I like the result.
  303. percent = round(((brightness - 16) / 224.0) * 100);
  304. lcd.setCursor(0, 1);
  305. lcd.print(String(percent) + "%");
  306. break;
  307. case 3: // Stand-by delay is shown in seconds
  308. if (sleep_time < 21000) {
  309. sec = sleep_time / 1000;
  310. lcd.setCursor(0, 1);
  311. lcd.print(String(sec) + "sec");
  312. }
  313. else {
  314. lcd.setCursor(0, 1);
  315. lcd.print("OFF");
  316. }
  317. break;
  318. case 4: // Display uptime
  319. s = (run_duration / 1000) % 60;
  320. m = (run_duration / 60000) % 60;
  321. h = (run_duration / 3600000) % 24;
  322. d = (run_duration / 86400000) % 12;
  323. lcd.setCursor(0, 1);
  324. lcd.print(String(d) + "d " + String(h) + "h " + String(m) + "m " + String(s) + "s ");
  325. break;
  326. case 5: // Display fan time
  327. s = (fan_duration / 1000) % 60;
  328. m = (fan_duration / 60000) % 60;
  329. h = (fan_duration / 3600000) % 24;
  330. d = (fan_duration / 86400000) % 12;
  331. lcd.setCursor(0, 1);
  332. lcd.print(String(d) + "d " + String(h) + "h " + String(m) + "m " + String(s) + "s ");
  333. break;
  334. case 6: // Display fan average time
  335. s = (fan_average / 1000) % 60;
  336. m = (fan_average / 60000) % 60;
  337. h = (fan_average / 3600000) % 24;
  338. d = (fan_average / 86400000) % 12;
  339. lcd.setCursor(0, 1);
  340. lcd.print(String(d) + "d " + String(h) + "h " + String(m) + "m " + String(s) + "s ");
  341. break;
  342. case 7: // Display lamp time
  343. s = (lamp_duration / 1000) % 60;
  344. m = (lamp_duration / 60000) % 60;
  345. h = (lamp_duration / 3600000) % 24;
  346. d = (lamp_duration / 86400000) % 12;
  347. lcd.setCursor(0, 1);
  348. lcd.print(String(d) + "d " + String(h) + "h " + String(m) + "m " + String(s) + "s ");
  349. break;
  350. case 8: // Display lamp average time
  351. s = (lamp_average / 1000) % 60;
  352. m = (lamp_average / 60000) % 60;
  353. h = (lamp_average / 3600000) % 24;
  354. d = (lamp_average / 86400000) % 12;
  355. lcd.setCursor(0, 1);
  356. lcd.print(String(d) + "d " + String(h) + "h " + String(m) + "m " + String(s) + "s ");
  357. break;
  358. }
  359. }
  360. // Change values of menu items
  361. // Action is 0 for decreasing (left button) and
  362. // 1 for increasing (right button)
  363. void updateItem(int item, int action) {
  364. switch (item) {
  365. case 0: // Target Temperature
  366. // Minimum temperature is 20C
  367. if (action == 0 && temp_target > 20) {
  368. temp_target--;
  369. }
  370. // Max temperature is 40C
  371. else if (action == 1 && temp_target < 40) {
  372. temp_target++;
  373. }
  374. break;
  375. case 1: // Temperature Variance
  376. // Minimum temperature variance is 1C
  377. if (action == 0 && temp_variance > 1) {
  378. temp_variance--;
  379. }
  380. // Max temperature variance is 10C
  381. else if (action == 1 && temp_variance < 10) {
  382. temp_variance++;
  383. }
  384. break;
  385. case 2: // Brightness
  386. // don't dim the screen to off
  387. if (action == 0 && brightness != 16) {
  388. brightness = brightness - 16;
  389. analogWrite(BACKLIGHT_PIN, brightness);
  390. }
  391. // 256 overflows and the screen is quite bright anyway
  392. else if (action == 1 && brightness != 240) {
  393. brightness = brightness + 16;
  394. analogWrite(BACKLIGHT_PIN, brightness);
  395. }
  396. break;
  397. case 3: // Standby Delay
  398. // Minimum delay is 2 seconds
  399. if (action == 0 && sleep_time > 2000) {
  400. sleep_time -= 1000;
  401. }
  402. // Max is 20 seconds (21 is off)
  403. else if (action == 1 && sleep_time < 21000) {
  404. sleep_time += 1000;
  405. }
  406. break;
  407. // To display info, we don't need to modify the value
  408. // so for item[2] (Uptime) we don't take any action.
  409. }
  410. displayItem(item);
  411. }
  412. void setup(void) {
  413. // Setup LCD
  414. lcd.begin(16, 2);
  415. lcd.noCursor();
  416. lcd.createChar(0, chicken0); // Create jumping chicken char
  417. lcd.createChar(1, chicken1); // Create landing chicken char
  418. lcd.createChar(2, chicken2); // Create squatting chicken char
  419. lcd.createChar(3, chicken3); // Create laying chicken char
  420. lcd.createChar(4, egg); // Create egg char
  421. // Setup sensor
  422. dht.setup(SENSOR_PIN);
  423. // Setup button and backlight pins
  424. pinMode(BUTTON_PIN, INPUT);
  425. pinMode(BACKLIGHT_PIN, OUTPUT);
  426. pinMode(LAMP_PIN, OUTPUT);
  427. pinMode(FAN_PIN, OUTPUT);
  428. // Turn backlight on
  429. analogWrite(BACKLIGHT_PIN, brightness);
  430. backlight = 1;
  431. welcome();
  432. wake_time = millis();
  433. readTemp();
  434. return;
  435. }
  436. void loop(void) {
  437. // Note how long we have been running for
  438. run_duration = millis();
  439. // and how long since the last button push
  440. wake_duration = run_duration - wake_time;
  441. // Hide the menu/go to sleep
  442. if (backlight == 1 && wake_duration >= sleep_time) {
  443. if (menu_displayed == 1) {
  444. menu_displayed = 0;
  445. item = 0;
  446. lcd.clear();
  447. wake_time = millis();
  448. } else if (sleep_time != 21000) {
  449. digitalWrite(BACKLIGHT_PIN, LOW);
  450. lcd.clear();
  451. backlight = 0;
  452. wake_time = 0;
  453. }
  454. }
  455. // Read a button press
  456. int button = readButton();
  457. // Wake up when the select button is pushed
  458. if (button == 1 && backlight == 0) {
  459. last_refresh = 0;
  460. analogWrite(BACKLIGHT_PIN, brightness);
  461. backlight = 1;
  462. }
  463. // Show the menu while awake and the select button is pushed
  464. else if (button == 1 && menu_displayed == 0) {
  465. // Set menu flag
  466. menu_displayed = 1;
  467. // Display the first item
  468. displayItem(item);
  469. }
  470. else if (button >= 1 && menu_displayed == 1) {
  471. switch (button) {
  472. case 1: // Select button pushed
  473. // Exit menu
  474. menu_displayed = 0;
  475. item = 0;
  476. lcd.clear();
  477. displayInfo();
  478. return;
  479. break;
  480. case 2: // Left button pushed
  481. // Decrease value
  482. updateItem(item, 0);
  483. break;
  484. case 3: // Down button pushed
  485. // Display next menu item
  486. if (++item > items) { item = 0; }
  487. displayItem(item);
  488. break;
  489. case 4: // Up button pushed
  490. // Display previous menu item
  491. if (--item < 0) { item = items; }
  492. displayItem(item);
  493. break;
  494. case 5: // Right button pushed
  495. // Increase value
  496. updateItem(item, 1);
  497. break;
  498. }
  499. }
  500. // Show info while awake
  501. else if (backlight == 1 && menu_displayed == 0) {
  502. displayInfo();
  503. }
  504. // Always do something usefull..
  505. doWork();
  506. return;
  507. }
  508. #ifdef __cplusplus
  509. }
  510. #endif