Advertisement
Guest User

Arduino PWM Fan Control with LCD and UI

a guest
May 8th, 2017
10,273
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 14.55 KB | None | 0 0
  1. // Arduino PWM Fan Control Beta by Adrian Black, May 2017
  2. // Sourcecode can be freely used for your own projects
  3.  
  4. // Needs 4-pin PC fan with PWM control (25khz)
  5.  
  6. // ALso uses I2C 16x2 LCD module
  7.  
  8. // 3 buttons for UI control
  9.  
  10. // DHT-22 used to temperature sensing
  11.  
  12. // Electronics run on 5v but FAN runs on 12. PWM logic signal is 5v. Use voltage regulator or buck
  13. // converter to allow this to work.
  14.  
  15. #include <Wire.h>
  16. #include <LCD.h>
  17. #include <LiquidCrystal_I2C.h>
  18. #include "DHT.h"
  19. #include <EEPROM.h>
  20.  
  21. #define DHTPIN 4 // pin DHT sensor connected to
  22. #define DHTTYPE DHT22 // DHT 11 in use. See library documentation for other types
  23. const int PWMPin = 3; // FAN PWM output 25khz signal
  24. const int RightSW = 5; // Right switch
  25. const int MidSW = 6; // Middle switch
  26. const int LeftSW = 7; // Left switch
  27.  
  28. boolean dotprint = LOW; // used to put flashing . on LCD during normal opearationg
  29.  
  30. boolean RightSWState = LOW;
  31. boolean MidSWState = LOW;
  32. boolean LeftSWState = LOW;
  33.  
  34. boolean waitHere = HIGH; // used in the menu loop to wait for input
  35.  
  36. int debouncecounter = 0; // how many times we have seen new value
  37. int debouncereading; // the current value read from the input pin
  38. long debouncetime = 0; // the last time the output pin was sampled
  39. int debounce_count = 10; // number of millis/samples to consider before declaring a debounced input
  40.  
  41. const int LoopSpeed = 1000;
  42. // Controls speed of main loop
  43. int RampSpeed = 1;
  44. // Controls the changing fan speed
  45. int TempCal = 0;
  46. int TempMin = 73;
  47. int TempMax = 77;
  48.  
  49. int FanMin = 30; // Minimum fan speed 0-79
  50. int FanMax = 79; // Maximum fan speed 0-79
  51. int CurrentSpeed = 79; // The Current fan speed duty cycle (0-79)
  52. int DesiredSpeed = 1; // The desired fan speed
  53. boolean FanStop = HIGH; // HIGH = fan stops when set to 0, LOW = fan doesn't stop
  54. boolean BacklightLED = HIGH;
  55. // LCD Display Backlight HIGH = on, LOW = off
  56. boolean RunState = HIGH; // not used
  57. float DisplayDutyCycle; // Duty cycle displayed on LCD
  58. int buttonInput = 0; // Used to read the button input
  59.  
  60. unsigned long time; // used for button repeat
  61. const unsigned long repeatdelay = 500;
  62. // 500ms for repeat
  63.  
  64. // end varibles //
  65.  
  66. LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);
  67.  
  68. // Initialize DHT sensor for normal 16mhz Arduino
  69. DHT dht(DHTPIN, DHTTYPE);
  70. void setup() {
  71. time = millis(); // setup time variable for button pushes
  72.  
  73. if (BacklightLED) {
  74. lcd.backlight(); // NOTE: You can turn the backlight off by setting it to LOW instead of HIGH
  75. } else {
  76. lcd.noBacklight();
  77. }
  78.  
  79. // generate 25kHz PWM pulse rate on Pin 3
  80. pinMode(PWMPin, OUTPUT); // OCR2B sets duty cycle
  81. // Set up Fast PWM on Pin 3
  82. TCCR2A = 0x23; // COM2B1, WGM21, WGM20
  83. // Set prescaler
  84. TCCR2B = 0x0A; // WGM21, Prescaler = /8
  85. // Set TOP and initialize duty cycle to zero(0)
  86. OCR2A = 79; // TOP DO NOT CHANGE, SETS PWM PULSE RATE
  87. OCR2B = CurrentSpeed; // duty cycle for Pin 3 (0-79) generates 1 500nS pulse even when 0 :(
  88.  
  89. lcd.begin(16, 2);
  90. lcd.clear();
  91.  
  92. // Load values from EEPROM
  93. eepromRead(); // Restore values from EEPROM
  94.  
  95. lcd.clear();
  96. }
  97.  
  98. void loop() {
  99.  
  100. float h = dht.readHumidity();
  101. // Read temperature as Celsius
  102. float t = dht.readTemperature();
  103. // Read temperature as Fahrenheit
  104. float f = dht.readTemperature(true);
  105. f = f + TempCal; // Apply sensor calibration
  106.  
  107. if (f < TempMin) {
  108. if (FanStop) {
  109. DesiredSpeed = 0;
  110. } else {
  111. DesiredSpeed = FanMin;
  112. }
  113. }
  114. if (f > TempMax) {
  115. DesiredSpeed = FanMax;
  116. }
  117.  
  118. if (f <= TempMax && f >= TempMin) {
  119. DesiredSpeed = map(f, TempMin, TempMax, FanMin, FanMax); // Map the Fan Speed to the Duty Cycle
  120. }
  121.  
  122.  
  123. if ((DesiredSpeed < CurrentSpeed) && ((CurrentSpeed - RampSpeed) > RampSpeed) ) {
  124. CurrentSpeed = CurrentSpeed - (1 * RampSpeed);
  125. } else if (DesiredSpeed < CurrentSpeed) {
  126. CurrentSpeed = CurrentSpeed - 1;
  127. }
  128.  
  129. if ((DesiredSpeed > CurrentSpeed) && ((CurrentSpeed + RampSpeed) > RampSpeed) ) {
  130. CurrentSpeed = CurrentSpeed + (1 * RampSpeed);
  131. } else if (DesiredSpeed > CurrentSpeed) {
  132. CurrentSpeed = CurrentSpeed + 1;
  133. }
  134.  
  135. lcd.setCursor(0,0);
  136. lcd.print("Fan Speed: ");
  137.  
  138. if (FanStop) {
  139. OCR2B = 0; // Stop the fan entirely
  140. lcd.print("OFF ");
  141. } else {
  142. OCR2B = CurrentSpeed; // set duty cycle
  143. }
  144.  
  145. if (!FanStop) {
  146. if (CurrentSpeed <= 0) {
  147. lcd.print("STOP ");
  148. } else {
  149. DisplayDutyCycle = CurrentSpeed * 1.2658227;
  150. lcd.print(DisplayDutyCycle, 1);
  151. lcd.print("% ");
  152. }
  153. }
  154.  
  155. if (isnan(h) || isnan(t) || isnan(f)) {
  156. lcd.setCursor(0,1);
  157. lcd.print("DHT Read Fail ");
  158. delay (500);
  159. return;
  160. } else {
  161. lcd.setCursor(0,1);
  162. lcd.print("T:");
  163. lcd.print(f,1); // use t instead of f for degrees C
  164. lcd.print("F H:");
  165. lcd.print(h,0);
  166. lcd.print("% ");
  167. }
  168.  
  169.  
  170. buttonInput = readButtons();
  171. if (buttonInput == 1) { // toggle the backlight
  172. BacklightLED = !BacklightLED;
  173. EEPROM.write(8,BacklightLED);
  174. if (BacklightLED) {
  175. lcd.backlight(); // NOTE: You can turn the backlight off by setting it to LOW instead of HIGH
  176. } else {
  177. lcd.noBacklight();
  178. }
  179. }
  180. else if (buttonInput == 2) {FanStop = !FanStop; EEPROM.write(7,FanStop);} // stop the fan
  181. else if (buttonInput == 3) {fanMenu(); waitHere = 1;} // enter the setup menu
  182. else {
  183. if (dotprint) {
  184. lcd.setCursor(15,1);
  185. lcd.print((char)126);
  186. dotprint = !dotprint;
  187. } else {
  188. lcd.setCursor(15,1);
  189. lcd.print(" ");
  190. dotprint = !dotprint;
  191. }
  192. delay(LoopSpeed); }
  193. }
  194.  
  195. void fanMenu() {
  196.  
  197. // Menu starts here!
  198. lcd.clear();
  199. lcd.setCursor(0,0);
  200. lcd.print("Fan Control");
  201. lcd.setCursor(0,1);
  202. lcd.print("RUN SETUP");
  203. delay(1000);
  204. while (waitHere) {
  205. buttonInput = readButtons();
  206. switch (buttonInput) {
  207. case 0: break;
  208. case 1: waitHere = 0; return 0;
  209. case 2: break;
  210. case 3: waitHere = 0;
  211. }
  212. delay(10);
  213. }
  214.  
  215. lcd.clear();
  216. lcd.print("Fan L Spd: ");
  217. lcd.setCursor(0,1);
  218. lcd.print("- + Next");
  219. buttonInput = 0; // Reset button input routing
  220. while (buttonInput != 3) {
  221. lcd.setCursor(12,0); lcd.print(FanMin);lcd.print(" ");
  222. buttonInput = readButtons();
  223. switch (buttonInput) {
  224. case 1:
  225. FanMin--;
  226. break;
  227. case 2:
  228. FanMin++;
  229. break;
  230. }
  231. }
  232.  
  233. lcd.clear();
  234. lcd.setCursor(0,0);
  235. lcd.print("Fan L Temp: ");
  236. lcd.setCursor(0,1);
  237. lcd.print("- + Next");
  238. buttonInput = 0; // Reset button input routing
  239. while (buttonInput != 3) {
  240. lcd.setCursor(12,0); lcd.print(TempMin);lcd.print(" ");
  241. buttonInput = readButtons();
  242. switch (buttonInput) {
  243. case 1:
  244. TempMin--;
  245. break;
  246. case 2:
  247. TempMin++;
  248. break;
  249. }
  250. }
  251.  
  252.  
  253. lcd.clear();
  254. lcd.setCursor(0,0);
  255. lcd.print("Fan H Spd: ");
  256. lcd.setCursor(0,1);
  257. lcd.print("- + Next");
  258. buttonInput = 0; // Reset button input routing
  259. while (buttonInput != 3) {
  260. lcd.setCursor(12,0); lcd.print(FanMax);lcd.print(" ");
  261. buttonInput = readButtons();
  262. switch (buttonInput) {
  263. case 1:
  264. FanMax--;
  265. break;
  266. case 2:
  267. FanMax++;
  268. break;
  269. }
  270. }
  271.  
  272. lcd.clear();
  273. lcd.setCursor(0,0);
  274. // lcd.print(" ");
  275. lcd.print("Fan H Temp: ");
  276. lcd.setCursor(0,1);
  277. lcd.print("- + Next");
  278. buttonInput = 0; // Reset button input routing
  279. while (buttonInput != 3) {
  280. lcd.setCursor(12,0); lcd.print(TempMax);lcd.print(" ");
  281. buttonInput = readButtons();
  282. switch (buttonInput) {
  283. case 1:
  284. TempMax--;
  285. break;
  286. case 2:
  287. TempMax++;
  288. break;
  289. }
  290. }
  291.  
  292. lcd.clear();
  293. lcd.setCursor(0,0);
  294. // lcd.print(" ");
  295. lcd.print("Sensor Cal: ");
  296. lcd.setCursor(0,1);
  297. lcd.print("- + Next");
  298. buttonInput = 0; // Reset button input routing
  299. while (buttonInput != 3) {
  300. lcd.setCursor(12,0); lcd.print(TempCal);lcd.print(" ");
  301. buttonInput = readButtons();
  302. switch (buttonInput) {
  303. case 1:
  304. TempCal--;
  305. break;
  306. case 2:
  307. TempCal++;
  308. break;
  309. }
  310. }
  311.  
  312.  
  313. lcd.clear();
  314. lcd.setCursor(0,0);
  315. // lcd.print(" ");
  316. lcd.print("Spd Cange X: ");
  317. lcd.setCursor(0,1);
  318. lcd.print("- + Next");
  319. buttonInput = 0; // Reset button input routing
  320. while (buttonInput != 3) {
  321. lcd.setCursor(13,0); lcd.print(RampSpeed);lcd.print(" ");
  322. buttonInput = readButtons();
  323. switch (buttonInput) {
  324. case 1:
  325. if (RampSpeed > 1) {RampSpeed--;}
  326. break;
  327. case 2:
  328. RampSpeed++;
  329. break;
  330. }
  331. }
  332.  
  333. lcd.clear();
  334. lcd.setCursor(0,0);
  335. lcd.print("Save settings? ");
  336. lcd.setCursor(0,1);
  337. lcd.print("SAVE DISCARD");
  338. buttonInput = 0; // Reset button input routing
  339. delay(1000); // testing
  340. while (buttonInput != 1 && buttonInput != 3) {
  341. lcd.setCursor(12,0);
  342. buttonInput = readButtons();
  343. switch (buttonInput) {
  344. case 1:
  345. eepromSave(); // Save new values to EEPROM
  346. delay (2000);
  347. return(0);
  348. case 3:
  349. eepromRead(); // Reread stored values
  350. delay (2000);
  351. return(0);
  352. break;
  353. }
  354. }
  355. }
  356.  
  357.  
  358. void eepromErase() {
  359. // routing not used in this program. Just here in case you want to clear the rom.
  360. lcd.clear();
  361. lcd.setCursor(0,0);
  362. lcd.print("Erase EEPROM");
  363. lcd.setCursor(0,1);
  364. lcd.print("Size= "); lcd.print(EEPROM.length());
  365. delay(1000);
  366.  
  367. for (int i = 0 ; i < EEPROM.length() ; i++) {
  368. EEPROM.write(i, 0);
  369. }
  370. }
  371.  
  372. void eepromSave() {
  373. lcd.clear();
  374. lcd.setCursor(0,0);
  375. lcd.print("Saving to EEPROM");
  376. EEPROM.write(0, 254); // this is the value I'll be looking for to know this data is correct
  377. EEPROM.write(1, RampSpeed);
  378. EEPROM.write(2, TempCal);
  379. EEPROM.write(3, TempMin);
  380. EEPROM.write(4, TempMax);
  381. EEPROM.write(5, FanMin);
  382. EEPROM.write(6, FanMax);
  383. if (FanStop) {
  384. EEPROM.write(7, 1);
  385. } else {
  386. EEPROM.write(7, 0);
  387. }
  388.  
  389. if (BacklightLED) {
  390. EEPROM.write(8, 1);
  391. } else {
  392. EEPROM.write(8, 0);
  393. }
  394.  
  395. if (RunState) {
  396. EEPROM.write(9, 1);
  397. } else {
  398. EEPROM.write(9, 0);
  399. }
  400.  
  401. lcd.setCursor(0,1);
  402. lcd.print("Done!");
  403. delay(1000);
  404. }
  405.  
  406. void eepromRead() {
  407. lcd.clear();
  408. lcd.setCursor(0,0);
  409. lcd.print("Reading EEPROM");
  410.  
  411. if (EEPROM.read(0) == 254) {
  412. RampSpeed = EEPROM.read(1);
  413. TempCal = EEPROM.read(2);
  414. TempMin = EEPROM.read(3);
  415. TempMax = EEPROM.read(4);
  416. FanMin = EEPROM.read(5);
  417. FanMax = EEPROM.read(6);
  418. FanStop = EEPROM.read(7);
  419. BacklightLED = EEPROM.read(8);
  420. RunState = EEPROM.read(9);
  421. delay(500);
  422. if (BacklightLED) {
  423. lcd.backlight(); // NOTE: You can turn the backlight off by setting it to LOW instead of HIGH
  424. } else {
  425. lcd.noBacklight();
  426. }
  427. lcd.setCursor(0,1);
  428. lcd.print("Done!");
  429. delay(1000);
  430. } else {
  431. lcd.setCursor(0,1);
  432. lcd.print("Defaults used.");
  433. delay(1000);
  434. }
  435. }
  436.  
  437. int readButtons() {
  438. // If we have gone on to the next millisecond
  439. // const int RightSW = 5; // Right switch or Switch 0
  440. // const int MidSW = 6; // Middle switch or Switch 1
  441. // const int LeftSW = 7; // Left switch or Switch 2
  442. // boolean RightSWState = LOW;
  443. // boolean MidSWState = LOW;
  444. // boolean LeftSWState = LOW;
  445. // int debouncecounter = 0; // how many times we have seen new value
  446. // int debouncereading; // the current value read from the input pin
  447. // long debouncetime = 0; // the last time the output pin was sampled
  448. // int debounce_count = 10; // number of millis/samples to consider before declaring a debounced input
  449.  
  450. //unsigned long time; // used for button repeat
  451. //const unsigned long repeatdelay = 2000;
  452.  
  453.  
  454.  
  455. // Right Button
  456. if (digitalRead(RightSW) && (millis() - time >= repeatdelay)) {
  457. for(int i = 0; i < 10; i++)
  458. {
  459. debouncereading = digitalRead(RightSW);
  460.  
  461. if(!debouncereading && debouncecounter > 0)
  462. {
  463. debouncecounter--;
  464. }
  465. if(debouncereading)
  466. {
  467. debouncecounter++;
  468. }
  469. // If the Input has shown the same value for long enough let's switch it
  470. if(debouncecounter >= debounce_count)
  471. {
  472. time = millis();
  473. debouncecounter = 0;
  474. RightSWState = 1;
  475. }
  476. delay (10); // wait 10ms
  477. }
  478. } else {
  479. RightSWState = 0;
  480. }
  481.  
  482.  
  483. if (digitalRead(MidSW) && (millis() - time >= repeatdelay)) {
  484. for(int i = 0; i < 10; i++)
  485. {
  486. debouncereading = digitalRead(MidSW);
  487.  
  488. if(!debouncereading && debouncecounter > 0)
  489. {
  490. debouncecounter--;
  491. }
  492. if(debouncereading)
  493. {
  494. debouncecounter++;
  495. }
  496. // If the Input has shown the same value for long enough let's switch it
  497. if(debouncecounter >= debounce_count)
  498. {
  499. time = millis();
  500. debouncecounter = 0;
  501. MidSWState = 1;
  502. }
  503. delay (10); // wait 10ms
  504. }
  505. } else {
  506. MidSWState = 0;
  507. }
  508.  
  509. if (digitalRead(LeftSW) && (millis() - time >= repeatdelay)) {
  510. for(int i = 0; i < 10; i++)
  511. {
  512. debouncereading = digitalRead(LeftSW);
  513.  
  514. if(!debouncereading && debouncecounter > 0)
  515. {
  516. debouncecounter--;
  517. }
  518. if(debouncereading)
  519. {
  520. debouncecounter++;
  521. }
  522. // If the Input has shown the same value for long enough let's switch it
  523. if(debouncecounter >= debounce_count)
  524. {
  525. time = millis();
  526. debouncecounter = 0;
  527. LeftSWState = 1;
  528. }
  529. delay (10); // wait 10ms
  530. }
  531. } else {
  532. LeftSWState = 0;
  533. }
  534.  
  535. if (RightSWState) { return 1; }
  536. else if (MidSWState) { return 2; }
  537. else if (LeftSWState) { return 3; }
  538. else { return 0; }
  539. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement