AutoFilm/film_processor_cgpt.ino
2024-06-20 11:48:26 +01:00

482 lines
12 KiB
C++

#include <Keypad.h>
#include <LiquidCrystal_I2C.h>
#include <AccelStepper.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_task_wdt.h"
#include <Wire.h>
// 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;
}