New repository for old PyCoffee Machine.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

506 lines
14 KiB

  1. // Pinout configuration
  2. const int grinderPin = 9, pumpPin = 8, boilerPin = 6, powderPin = 7, wheelPin = 10, invertWheelPin = 11, powderSensorPin = 2, vaporSensorPin = 5, tempSensorPin = A7, wheelStartSensorPin = 4, wheelEndSensorPin = 3, redLED = 13, greenLED = 12;
  3. // Global string
  4. String readString;
  5. // Thermistor config
  6. // WARNING! while the old coffee machine used a PTC thermistor, this was replaced with an NTC one as it was more readily available
  7. // Code must be adjusted accordingly if a PTC resistor is used once more!
  8. int Vo;
  9. float R1 = 10000; // <-- change this value to the resistance of the fixed resistor (so don't change PLS!)
  10. float logR2, R2, T, Tc, Told;
  11. float c1 = 8.5e-04, c2 = 1.85e-04, c3 = 2e-07; // Here's where you can perform actual calibration
  12. // Variable to store desired Max boiler Temp
  13. int desiredTemp = 70;
  14. // Variables to Store errors:
  15. int unrecoverableErr = 0;
  16. int warning = 0;
  17. int warned = 0; // support variable needed to prevent arduino from making second coffee after refill. this is a shitty hack and resets the board.
  18. // Variables for cleaning and other maintenance
  19. int dry = 0;
  20. int noCoffee = 0;
  21. // Milliseconds to delay between each cycle
  22. const int milliseconds = 10;
  23. int timeratio = 1;
  24. // pumpRatio is = seconds to pump water for and will be updated over serial
  25. int pumpRatio = 15;
  26. // This next variable is used to get current "time" in ms and break out of while cycles that need a time limit safeguard
  27. unsigned long startTime;
  28. //_____________________________________________________________________________
  29. void(* resetFunc) (void) = 0; // declare reset fuction at address 0
  30. //_____________________________________________________________________________
  31. void setup() {
  32. // put your setup code here, to run once:
  33. // first we set pins as I/O and initialize outputs LOW
  34. pinMode(boilerPin, OUTPUT); pinMode(pumpPin, OUTPUT); pinMode(grinderPin, OUTPUT); pinMode(powderPin, OUTPUT); pinMode(wheelPin, OUTPUT); pinMode(invertWheelPin, OUTPUT); pinMode (redLED, OUTPUT); pinMode (greenLED, OUTPUT);
  35. pinMode(powderSensorPin, INPUT); pinMode(vaporSensorPin, INPUT); pinMode(tempSensorPin, INPUT); pinMode(wheelStartSensorPin, INPUT); pinMode(wheelEndSensorPin, INPUT);
  36. digitalWrite(grinderPin, LOW); digitalWrite(pumpPin, LOW); digitalWrite(boilerPin, LOW); digitalWrite(powderPin, LOW); digitalWrite(wheelPin, LOW); digitalWrite(invertWheelPin, LOW); digitalWrite(redLED, LOW); digitalWrite(greenLED, LOW);
  37. // timeratio easily allows to determine how many cycles are required to make 1s pass (ms * ratio = 1s)
  38. timeratio = 1000/milliseconds;
  39. // initialize serial:
  40. Serial.begin(9600);
  41. delay(100);
  42. Serial.write("Arduino running :)\n");
  43. }
  44. //_____________________________________________________________________________
  45. void loop() {
  46. // put your main code here, to run repeatedly:
  47. digitalWrite(greenLED, HIGH);
  48. // Check if unrecoverable error has occurred:
  49. if (unrecoverableErr == 1) {
  50. startTime = millis();
  51. delay(100);
  52. Serial.write("Error has occurred.\n"); delay(100); Serial.write("Check machine and restart\n");
  53. digitalWrite(greenLED, LOW);
  54. while (true){
  55. digitalWrite(redLED, HIGH);
  56. delay(1000);
  57. digitalWrite(redLED, LOW);
  58. delay(1000);
  59. // reset arduino after 30s
  60. if (millis() - startTime == 30000) {
  61. break;
  62. }
  63. }
  64. resetFunc(); //call reset.
  65. }
  66. // check if data has been sent on serial from ESP or PC:
  67. if (Serial.available() > 0) {
  68. // read the incoming data: (we only care about the first few characters, I've chosen 4)
  69. readString = "";
  70. serialRead();
  71. String incomingData = readString.substring(0,4);
  72. //if (incomingData == "read") {
  73. //digitalWrite(greenLED, HIGH);
  74. //}
  75. if (incomingData == "rist") {
  76. // set parameters for ristretto:
  77. delay(100);
  78. Serial.write("Ristretto\n");
  79. pumpRatio = 10;
  80. }
  81. if (incomingData == "espr") {
  82. // set parameters for espresso:
  83. delay(100);
  84. Serial.write("Espresso\n");
  85. pumpRatio = 13;
  86. }
  87. if (incomingData == "long") {
  88. // set parameters for lungo:
  89. delay(100);
  90. Serial.write("Lungo\n");
  91. pumpRatio = 16;
  92. }
  93. // check if the incoming data is "make":
  94. if (incomingData == "make") {
  95. // run the code to make coffee:
  96. delay(100);
  97. Serial.write("Making some coffee!\n");
  98. makeCoffee();
  99. }
  100. if (incomingData == "dryr") {
  101. // run the code to make a dry run (no powder, no water):
  102. delay(100);
  103. Serial.write("DryRun\n");
  104. dry = 1;
  105. makeCoffee();
  106. }
  107. if (incomingData == "noco") {
  108. // run the code to make a dry run (no powder, nor water):
  109. delay(100);
  110. Serial.write("NoCo\n");
  111. pumpRatio = 12;
  112. noCoffee = 1;
  113. makeCoffee();
  114. }
  115. if (incomingData == "grin") {
  116. // only grind
  117. Grind();
  118. }
  119. if (incomingData == "pump") {
  120. // run the code to just pump water:
  121. pumpRatio = 15;
  122. Pump();
  123. }
  124. if (incomingData == "pres") {
  125. // only press
  126. Press();
  127. }
  128. if (incomingData == "unpr") {
  129. // only unpress
  130. unPress();
  131. }
  132. if (incomingData == "heat") {
  133. // only heat
  134. desiredTemp = 80;
  135. Heat();
  136. }
  137. if (incomingData == "drop") {
  138. // only drop
  139. Drop();
  140. }
  141. if (incomingData == "clea") {
  142. // clean machine
  143. delay(100);
  144. Serial.write("s-clean\n");
  145. dry = 1;
  146. makeCoffee();
  147. noCoffee = 1;
  148. makeCoffee();
  149. delay(100);
  150. Serial.write("S-cleaning done.\n");
  151. }
  152. }
  153. // making steam:
  154. while (digitalRead(vaporSensorPin) == HIGH) {
  155. desiredTemp = 90;
  156. Serial.write("Vapor heating\n");
  157. delay(500);
  158. Heat(); // Heat the boiler to vapor temperature
  159. delay(20000);
  160. if (unrecoverableErr == 1) {
  161. break;
  162. }
  163. }
  164. delay(milliseconds);
  165. }
  166. //_____________________________________________________________________________
  167. // this is how we read the input
  168. void serialRead() {
  169. while (Serial.available()) {
  170. delay(10);
  171. if (Serial.available() > 0) {
  172. char c = Serial.read();
  173. readString += c;}
  174. }
  175. }
  176. //_____________________________________________________________________________
  177. void Grind() {
  178. digitalWrite(greenLED, LOW);
  179. delay(100);
  180. Serial.write("grinding...\n");
  181. digitalWrite(grinderPin, HIGH);
  182. startTime = millis();
  183. while (true) {
  184. delay(milliseconds);
  185. if (digitalRead(powderSensorPin) == HIGH) {
  186. digitalWrite(grinderPin, LOW);
  187. delay(100);
  188. Serial.write("Grinding Done\n");
  189. break;
  190. }
  191. if (millis() - startTime > 15000) {
  192. Serial.write("Warning, grinding took too long!\n");
  193. digitalWrite(grinderPin, LOW);
  194. delay(100);
  195. Serial.write("Out of Coffee?\n");
  196. warning = 1;
  197. warned = 1;
  198. break;
  199. }
  200. }
  201. digitalWrite(greenLED, HIGH);
  202. }
  203. //_____________________________________________________________________________
  204. void Drop() {
  205. digitalWrite(greenLED, LOW);
  206. delay(100);
  207. Serial.write("dropping...\n");
  208. digitalWrite(powderPin, HIGH);
  209. delay(1000);
  210. digitalWrite(powderPin, LOW);
  211. delay(100);
  212. Serial.write("Dropped\n");
  213. digitalWrite(greenLED, HIGH);
  214. }
  215. //_____________________________________________________________________________
  216. void Heat() {
  217. digitalWrite(greenLED, LOW);
  218. digitalWrite(redLED, HIGH);
  219. startTime = millis();
  220. delay(100);
  221. Serial.write("Heating to ");
  222. delay(100);
  223. Serial.print(desiredTemp);
  224. delay(100);
  225. Tc = 0;
  226. Told = - 100;
  227. // monitor temperature and adjust boilerPin as needed:
  228. while (true) {
  229. // read temperature from tempSensorPin:
  230. Vo = analogRead(tempSensorPin);
  231. R2 = R1 * (1023.0 / (float)Vo - 1.0);
  232. logR2 = log(R2);
  233. T = (1.0 / (c1 + c2*logR2 + c3*logR2*logR2*logR2));
  234. Tc = (T - 273.15);
  235. if (millis() - startTime == 1000) {
  236. Told = Tc; // support variable to store temp at beginning, so that we can be sure it's increasing
  237. }
  238. // check if temperature is within the acceptable range and break out of the loop without error if done heating:
  239. if (Tc > desiredTemp) {
  240. // temperature is within range, so break out of the loop:
  241. digitalWrite(boilerPin, LOW);
  242. delay(100);
  243. Serial.write("reached desired temp\n");
  244. delay(100);
  245. Serial.print(Tc);
  246. delay(100);
  247. digitalWrite(redLED, LOW);
  248. break;
  249. }
  250. if (Tc < -100) {
  251. delay(100);
  252. Serial.write("u-Thermocouple: unplugged or failed\n");
  253. digitalWrite(boilerPin, LOW);
  254. unrecoverableErr = 1;
  255. break;
  256. }
  257. if (millis() - startTime > 20000 && Tc - Told < 1) {
  258. delay(100);
  259. Serial.write("p-Thermocouple: positioning or relay fault\n");
  260. digitalWrite(boilerPin, LOW);
  261. unrecoverableErr = 1;
  262. break;
  263. }
  264. if (millis() - startTime > 30000) {
  265. delay(100);
  266. Serial.write("h-taking too long, continuing...\n");
  267. digitalWrite(boilerPin, LOW);
  268. digitalWrite(redLED, LOW);
  269. break;
  270. }
  271. // We do this at the end so that the relay is not uselessly cycled
  272. digitalWrite(boilerPin, HIGH);
  273. delay(milliseconds);
  274. }
  275. digitalWrite(greenLED, HIGH);
  276. }
  277. //_____________________________________________________________________________
  278. void Press() {
  279. digitalWrite(greenLED, LOW);
  280. delay(100);
  281. Serial.write("pressing...\n");
  282. // Reset Press before every coffee
  283. digitalWrite(invertWheelPin, HIGH);
  284. delay(1000);
  285. digitalWrite(invertWheelPin, LOW);
  286. digitalWrite(wheelPin, HIGH);
  287. startTime = millis();
  288. while (true) {
  289. delay(milliseconds);
  290. if (digitalRead(wheelEndSensorPin) == HIGH) {
  291. delay(900); // extra delay added to compensate for slightly incorrect endstop placement after repair
  292. digitalWrite(wheelPin, LOW);
  293. delay(100);
  294. Serial.write("Pressed\n");
  295. break;
  296. }
  297. if (millis() - startTime > 15000) {
  298. delay(100);
  299. Serial.write("p-end of pressing not detected\n");
  300. digitalWrite(wheelPin, LOW);
  301. unrecoverableErr = 1;
  302. break;
  303. }
  304. }
  305. digitalWrite(greenLED, HIGH);
  306. }
  307. //_____________________________________________________________________________
  308. void unPress() {
  309. digitalWrite(greenLED, LOW);
  310. delay(100);
  311. Serial.write("unPressing...\n");
  312. // Reset Press before every coffee
  313. digitalWrite(wheelPin, HIGH);
  314. delay(1500);
  315. digitalWrite(wheelPin, LOW);
  316. digitalWrite(invertWheelPin, HIGH);
  317. startTime = millis();
  318. while (true) {
  319. delay(milliseconds);
  320. if (digitalRead(wheelStartSensorPin) == HIGH) {
  321. delay(1500);
  322. digitalWrite(invertWheelPin, LOW);
  323. delay(100);
  324. Serial.write("UnPressed\n");
  325. break;
  326. }
  327. if (millis() - startTime > 15000) {
  328. delay(100);
  329. Serial.write("u-end of unPressing not detected\n");
  330. digitalWrite(invertWheelPin, LOW);
  331. unrecoverableErr = 1;
  332. break;
  333. }
  334. }
  335. digitalWrite(greenLED, HIGH);
  336. }
  337. //_____________________________________________________________________________
  338. void Pump() {
  339. digitalWrite(greenLED, LOW);
  340. delay(100);
  341. Serial.write("pumping Water...\n");
  342. digitalWrite(pumpPin, HIGH);
  343. delay(1000*pumpRatio);
  344. digitalWrite(pumpPin, LOW);
  345. delay(100);
  346. Serial.write("Pumping water done\n");
  347. digitalWrite(greenLED, HIGH);
  348. }
  349. //_____________________________________________________________________________
  350. void makeCoffee() {
  351. // code to make coffee goes here...
  352. // It would be much smarter to break down complicated functions into subroutines: press/unpress etc...
  353. // Hence that's what we'll have done by the time the first release goes public ;)
  354. // makeCoffee() can still run all the same, but clean() can use the same press/unpress code!
  355. // care must be taken to place these functions earlier in the code, or the compiler will rightfully freak out
  356. while (true) {
  357. desiredTemp = 75;
  358. Heat(); // First we pre-heat the boiler
  359. if (unrecoverableErr == 1) {
  360. break;
  361. }
  362. if (dry == 0 && noCoffee == 0) {
  363. Grind(); // Grinding some nice roasted coffee beans!
  364. }
  365. // If grinder won't stop, coffee probably needs to be refilled.
  366. if (warning == 1) {
  367. delay(100);
  368. Serial.write("refill coffee and send cont command\n");
  369. while (true) {
  370. // check if data has been sent on serial from ESP or PC:
  371. if (Serial.available() > 0) {
  372. // read the incoming data: (we only care about the first few characters, I've chosen 4)
  373. readString = "";
  374. serialRead();
  375. String incomingData = readString.substring(0,4);
  376. if (incomingData == "cont") {
  377. // coffee refilled, making is allowed to continue
  378. Serial.write("Refilled\n");
  379. warning = 0;
  380. Grind(); // Grinding some nice roasted coffee beans!
  381. if (warning == 1) {
  382. unrecoverableErr = 1;
  383. Serial.write("g-failure: second grinding failure, check grinder!\n");
  384. }
  385. break;
  386. }
  387. }
  388. }
  389. }
  390. // Stop if grinding failed a second time
  391. if (unrecoverableErr == 1) {
  392. break;
  393. }
  394. delay(1000); // this delay is mandatory to prevent powder spillage caused by grinder inertia.
  395. unPress(); // unpressing the press to reset the machine
  396. if (unrecoverableErr == 1) {
  397. break;
  398. }
  399. Drop(); // Dropping the powder in the press
  400. delay(1000);
  401. Press(); // Self explainatory
  402. if (unrecoverableErr == 1) {
  403. break;
  404. }
  405. desiredTemp = 85;
  406. Heat(); // First we pre-heat the boiler
  407. if (unrecoverableErr == 1) {
  408. break;
  409. }
  410. if (dry == 0) {
  411. digitalWrite(boilerPin, HIGH);
  412. delay(100);
  413. Pump(); // Pump ratio (in seconds) will be read from serial input in final release
  414. digitalWrite(boilerPin, LOW);
  415. }
  416. delay(3000); // Allow pressure to wane
  417. unPress(); // unpressing the press to reset the machine
  418. if (unrecoverableErr == 1) {
  419. break;
  420. }
  421. delay(100);
  422. Serial.write("Complete!\n");
  423. delay(1000);
  424. dry = 0;
  425. noCoffee = 0;
  426. if (warned == 1) {
  427. resetFunc(); //call reset. I am ashamed of this, but it works.
  428. }
  429. break;
  430. }
  431. }