Chicken_Brooder.cpp 13 KB

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