#include #include #include #include #include #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_task_wdt.h" #include // Disable the watchdog timers void disableWatchdogTimers() { // Disable the Task Watchdog Timer esp_task_wdt_deinit(); // Disable the Idle Task Watchdog Timer on both cores TaskHandle_t idle_0 = xTaskGetIdleTaskHandleForCPU(0); TaskHandle_t idle_1 = xTaskGetIdleTaskHandleForCPU(1); esp_task_wdt_delete(idle_0); esp_task_wdt_delete(idle_1); } const char* version = "V0.1 20240613"; // Define keypad matrix const byte ROWS = 5; const byte COLS = 4; char keys[ROWS][COLS] = { { 'F', 'E', '#', '*' }, { '1', '2', '3', 'U' }, { '4', '5', '6', 'D' }, { '7', '8', '9', 'X' }, { 'L', '0', 'R', 'E' } }; struct devSequence { char name[7]; int cycles; unsigned long int processTime[20]; char processName[20][10]; float processCycle[2][20]; float processTemp[3][20]; }; struct MotorTaskParams { float cwRotations; float ccwRotations; unsigned long processEndTime; }; // Create custom characters byte thermometer[8] = { B00100, B01100, B00100, B01100, B00100, B01110, B01110, B01110 }; int run; byte rowPins[ROWS] = { 19, 18, 5, 17, 16 }; byte colPins[COLS] = { 15, 2, 0, 4 }; // Define keypad properties Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS); // Define LCD properties #define LCD_COLUMNS 20 #define LCD_ROWS 4 LiquidCrystal_I2C lcd(0x27, LCD_COLUMNS, LCD_ROWS); // Define stepper motor pins #define PULSE_PIN 12 #define DIR_PIN 14 #define EN_PIN 27 // Define stepper motor properties #define STEPS_PER_REV 4800 // We are using 1600 microsteps, but a 33:99 gear, so this gives us revolutions of the tank #define RPM 60 #define SPEED (RPM * STEPS_PER_REV / 60.0) // Create an instance of the AccelStepper class AccelStepper stepper(AccelStepper::DRIVER, PULSE_PIN, DIR_PIN); // Define the temperature sensor pin #define TEMP_SENSOR_PIN 13 // Define the OneWire bus for temperature sensor OneWire oneWire(TEMP_SENSOR_PIN); // Create a DallasTemperature object to control the temperature sensor DallasTemperature sensors(&oneWire); String inputString = ""; // String to hold input String devPgm = ""; // String to hold programme type TaskHandle_t motorTaskHandle = NULL; unsigned long processStartTime; unsigned long processTimeMillis; void setup() { Serial.begin(115200); // Disable the watchdog timers disableWatchdogTimers(); // Initialize LCD Wire.begin(21, 22); lcd.init(); lcd.backlight(); lcd.createChar(0, thermometer); // Set pin modes pinMode(EN_PIN, OUTPUT); digitalWrite(EN_PIN, HIGH); // Disable motor initially // Set stepper motor properties stepper.setMaxSpeed(SPEED); stepper.setAcceleration(9600); // Set acceleration // Display welcome message lcd.setCursor(6, 1); lcd.print("AUTOFILM"); lcd.setCursor((20 - strlen(version)) / 2, 3); lcd.print(version); delay(1000); lcd.clear(); } void loop() { startingMenu(); } void processHeadings() { lcd.clear(); lcd.setCursor(0, 0); lcd.print("Step"); lcd.setCursor(10, 0); lcd.print("Time"); lcd.setCursor(16, 0); lcd.print("Temp"); } void startingMenu() { lcd.clear(); lcd.setCursor(0, 0); lcd.print("Select Programme:"); lcd.setCursor(0, 1); lcd.print("1. C41"); lcd.setCursor(0, 2); lcd.print("2. E6"); lcd.setCursor(0, 3); lcd.print("3. B&W"); lcd.setCursor(10, 1); lcd.print("4. ECN-2"); lcd.setCursor(10, 2); lcd.print("5. Custom"); lcd.setCursor(10, 3); while (devPgm == "") { getMenuInput(); } lcd.print(devPgm); if (devPgm == "C41") { c41Dev(); } else if (devPgm == "E6") { e6Dev(); } else if (devPgm == "B&W") { bwDev(); } else if (devPgm == "ECN-2") { ecn2Dev(); } else if (devPgm == "Custom") { customDev(); } } void getMenuInput() { char key = keypad.getKey(); if (key != NO_KEY) { // Check if the key is 1-5 if (key >= '1' && key <= '5') { if (key == '1') { devPgm = "C41"; } else if (key == '2') { devPgm = "E6"; } else if (key == '3') { devPgm = "B&W"; } else if (key == '4') { devPgm = "ECN-2"; } else if (key == '5') { devPgm = "Custom"; } } } } char getScrollEntEscInput() { while (true) { // Loop indefinitely until a valid key is found for (int i = 1; i <= 10000; i++) { if (i == 10000) { readTemperature(); } char key = keypad.getKey(); // Assume keypad.getKey() is properly defined and returns a character if (key != NO_KEY) { // Check if a key has been pressed // Check if the key is valid if (key == 'U' || key == 'D' || key == 'X' || key == 'E') { return key; // Return the valid key } } } } } char getEntEscInput() { while (true) { // Loop indefinitely until a valid key is found for (int i = 1; i <= 10000; i++) { if (i == 10000) { readTemperature(); } char key = keypad.getKey(); // Assume keypad.getKey() is properly defined and returns a character if (key != NO_KEY) { // Check if a key has been pressed // Check if the key is valid if (key == 'X' || key == 'E') { return key; // Return the valid key } } } } } void readTemperature() { sensors.requestTemperatures(); float temperatureC = sensors.getTempCByIndex(0); lcd.setCursor(13, 3); lcd.write(byte(0)); if (temperatureC == DEVICE_DISCONNECTED_C) { lcd.print("--"); } else { lcd.print(temperatureC, 1); lcd.print("C"); } } void c41Dev() { //assign processing parameters struct devSequence c41DevSequence = { { "C41" }, { 7 }, { 180, 195, 45, 180, 60, 60, 30 }, //processTime { "Prewarm", "Developer", "Bleach", "Fix", "Rinse 1", "Rinse 2", "Fin Rinse" }, //processName { { 1, 5.5, 5.5, 5.5, 3.5, 3.5, 1 }, //CW rotations processCycle { 1, 5, 5, 5, 3, 3, 1 } }, //CCW rotations processCycle { { 37.8, 37.8, 32, 32, 32, 32, 32 }, //min processTemp { 38, 38, 38, 38, 38, 38, 38 }, //preferred processTemp { 38.2, 38.2, 38.2, 38.2, 38.2, 38.2, 38.2 } //max processTemp } }; struct devSequence e6DevSequence = { { "C41" }, { 7 }, { 180, 195, 45, 180, 60, 60, 30 }, //processTime { "Prewarm", "First Dev", "Wash", "Reversal", "Colour Dev", "Pre-Bleach", "Bleach", "Fix", "Rinse 1", "Rinse 2", "Rinse 3", "Fin Rinse" }, //processName { { 1, 5.5, 5.5, 5.5, 3.5, 3.5, 1 }, //CW rotations processCycle { 1, 5, 5, 5, 3, 3, 1 } }, //CCW rotations processCycle { { 37.7, 37.8, 32, 32, 32, 32, 32 }, //min processTemp { 38, 38, 38, 38, 38, 38, 38 }, //preferred processTemp { 38.3, 38.3, 38.2, 38.2, 38.2, 38.2, 38.2 } //max processTemp } }; for (int i = 0; i < c41DevSequence.cycles; i++) { if (run == 1) { startProcessing(c41DevSequence, i); continue; } processHeadings(); lcd.setCursor(0, 1); lcd.print(c41DevSequence.processName[i]); for (int x = strlen(c41DevSequence.processName[i]); x < 10; x++) { lcd.print(" "); } lcd.setCursor(10, 1); lcd.print(secondsToMinutesSeconds(c41DevSequence.processTime[i])); lcd.setCursor(16, 1); lcd.print(" " + (String)(int)c41DevSequence.processTemp[1][i] + "C"); // lcd.setCursor(0, 2); // lcd.print((String)c41DevSequence.processCycle[0][i] + ":" + (String)c41DevSequence.processCycle[1][i]); readTemperature(); lcd.setCursor(0, 2); lcd.print("Scroll / Esc / Ent"); char key = getScrollEntEscInput(); if (key == 'U') { if (i > 0) { i--; } i--; } else if (key == 'D' && i < c41DevSequence.cycles - 1) { continue; } else if (key == 'D' && i == c41DevSequence.cycles - 1) { i--; continue; } else if (key == 'X') { devPgm = ""; run = 0; return; } else if (key == 'E') { int run = 1; startProcessing(c41DevSequence, i); } } devPgm = ""; run = 0; } int startProcessing(struct devSequence sequence, int sequenceStep) { run = 1; lcd.clear(); lcd.setCursor(0, 0); lcd.print(sequence.processName[sequenceStep]); for (int x = strlen(sequence.processName[sequenceStep]); x < 10; x++) { lcd.print(" "); } lcd.setCursor(10, 0); lcd.print(secondsToMinutesSeconds(sequence.processTime[sequenceStep])); lcd.setCursor(16, 0); lcd.print(" " + (String)(int)sequence.processTemp[1][sequenceStep] + "C"); lcd.setCursor(0, 1); lcd.print("Ent:start Esc:quit"); readTemperature(); char key = getEntEscInput(); if (key == 'X') { devPgm = ""; run = 0; return 0; } processTimeMillis = (unsigned long)sequence.processTime[sequenceStep] * 1000; processStartTime = millis(); MotorTaskParams* params = new MotorTaskParams(); params->cwRotations = sequence.processCycle[0][sequenceStep]; params->ccwRotations = sequence.processCycle[1][sequenceStep]; params->processEndTime = processStartTime + processTimeMillis; // Debugging output Serial.print("Starting Motor Task with CW Rotations: "); Serial.print(params->cwRotations); Serial.print(", CCW Rotations: "); Serial.print(params->ccwRotations); Serial.print(", Process End Time: "); Serial.println(params->processEndTime); xTaskCreatePinnedToCore( runMotorTask, "MotorTask", 8192, // Increased stack size (void*)params, 1, &motorTaskHandle, 0); while (millis() < processStartTime + processTimeMillis) { for (int i = 1; i <= 5; i++) { lcd.setCursor(0, 1); lcd.print("Remaining: " + (String)secondsToMinutesSeconds((processStartTime + processTimeMillis - millis()) / 1000) + " "); if (i != 5) { delay(1000); } else { readTemperature(); } } } // Terminate the motor task if (motorTaskHandle != NULL) { vTaskDelete(motorTaskHandle); motorTaskHandle = NULL; } delete params; return 1; } void runMotorTask(void* parameter) { MotorTaskParams* params = (MotorTaskParams*)parameter; // Enable the motor digitalWrite(EN_PIN, LOW); while (true) { // Rotate clockwise Serial.println("Rotating CW" + (String)params->cwRotations); stepper.setCurrentPosition(0); stepper.moveTo(STEPS_PER_REV * params->cwRotations); while (stepper.distanceToGo() != 0) { stepper.run(); } // Rotate counter-clockwise Serial.println("Rotating CCW" + (String)params->ccwRotations); stepper.setCurrentPosition(0); stepper.moveTo(-STEPS_PER_REV * params->ccwRotations); while (stepper.distanceToGo() != 0) { stepper.run(); } } // Disable the motor digitalWrite(EN_PIN, HIGH); vTaskDelete(NULL); // Delete the task when done } void ecn2Dev() { lcd.clear(); lcd.setCursor(0, 0); lcd.print(devPgm); devPgm = ""; } void e6Dev() { lcd.clear(); lcd.setCursor(0, 0); lcd.print(devPgm); devPgm = ""; } void bwDev() { lcd.clear(); lcd.setCursor(0, 0); lcd.print(devPgm); devPgm = ""; } void customDev() { lcd.clear(); lcd.setCursor(0, 0); lcd.print(devPgm); devPgm = ""; } char* secondsToMinutesSeconds(int seconds) { // Allocate memory for the resulting string // MM:SS + null terminator -> total 6 characters char* result = (char*)malloc(6 * sizeof(char)); if (result == NULL) { // Handle memory allocation failure return NULL; } int minutes = seconds / 60; int secs = seconds % 60; // Format the string as "MM:SS" snprintf(result, 6, "%02d:%02d", minutes, secs); return result; }