@@ 1,3 1,5 @@
+#include "Adafruit_ST77xx.h"
+#include "hardware/sync.h"
#include <Arduino.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ST7789.h>
@@ 18,8 20,6 @@
#define DISPLAY_CS (17)
#define DISPLAY_DC (16)
-#define DISPLAY_SCLK (18)
-#define DISPLAY_MOSI (19)
#define DISPLAY_BACKLIGHT_EN (20)
#define TEMP_DO (27)
@@ 44,6 44,23 @@
#define TEMPERATURE_WARM (50)
#define TEMPERATURE_HOT (85)
+word colour(byte R, byte G, byte B) {
+ return ( ((R & 0xF8) << 8) | ((G & 0xFC) << 3) | (B >> 3) );
+}
+
+word colour(uint32_t col) {
+ return colour((col & 0xFF0000) >> 4, (col & 0xFF00) >> 2, (col & 0xFF));
+}
+
+static const word kColorText = ST77XX_WHITE;
+static const word kColorBackground = colour(0x1e0010);
+static const word kColorSafeTemp = colour(0x131218);
+static const word kColorWarningTemp = colour(0xDCC8FF);
+static const word kColorDangerTemp = colour(0xC5A3FF);
+static const word kColorGraphLines = colour(0xFFB8D1);
+static const word kColorIdealGraph = colour(0xBFBFBF);
+static const word kColorActualGraph = colour(0xF8F8F2);
+
using std::string;
using std::ostringstream;
@@ 108,7 125,7 @@ class DrawMessage {
queue_t drawing_queue;
// Drawing
-uint16_t text_bg_color = 0x0000;
+uint16_t text_bg_color = kColorBackground;
// State machine
enum State {
@@ 127,7 144,7 @@ State next_state = MAIN_MENU;
// Temperature
int current_temp = -1;
int last_temp = -1;
-uint16_t current_temp_color = 0x0000;
+uint16_t current_temp_color = kColorBackground;
// Calibration
bool is_calibrated = false;
@@ 158,11 175,12 @@ int reflow_duration = 60'000;
int cool_duration = 35'000;
// Reflow meta
-int holding_at_temp = 0;
-int holding_at_time = 0;
-int holding_at_reheat_time = 0;
-int desired_temp = 0;
-int reflow_rise_time = 0;
+bool is_holding = false;
+int holding_at_temp = -1;
+int holding_at_time = -1;
+int holding_at_reheat_time = -1;
+int desired_temp = -1;
+int reflow_rise_time = -1;
unsigned long reflow_state_start_time = 0;
// Menus
@@ 177,7 195,7 @@ void send_clear() {
queue_add_blocking(&drawing_queue, &msg);
}
-void send_text(string text, int x, int y, Justification j, uint16_t fg=0xFFFF, uint16_t bg=0x0000) {
+void send_text(string text, int x, int y, Justification j, uint16_t fg=kColorText, uint16_t bg=kColorBackground) {
DrawMessage msg{
DrawMessage::TEXT,
{
@@ 192,7 210,7 @@ void send_text(string text, int x, int y, Justification j, uint16_t fg=0xFFFF, u
queue_add_blocking(&drawing_queue, &msg);
}
-void send_config(int textSize=1, uint16_t textColor=0xFFFF, const GFXfont *font=NULL) {
+void send_config(int textSize=1, uint16_t textColor=kColorText, const GFXfont *font=NULL) {
DrawMessage msg{
DrawMessage::CONFIG,
{
@@ 281,11 299,11 @@ void draw_temperature() {
int y = 240 - HEADER_FOOTER_SIZE + 2;
send_config();
- send_text(text, x, y, CENTER, 0xFFFF, current_temp_color);
+ send_text(text, x, y, CENTER, kColorText, current_temp_color);
}
void draw_header() {
- uint16_t color_fg = ST77XX_WHITE;
+ uint16_t color_fg = kColorText;
uint16_t color_bg = current_temp_color;
text_bg_color = color_bg;
@@ 346,7 364,7 @@ void draw_header() {
}
void draw_footer() {
- uint16_t color_fg = ST77XX_WHITE;
+ uint16_t color_fg = kColorText;
uint16_t color_bg = current_temp_color;
text_bg_color = color_bg;
@@ 402,15 420,15 @@ void set_elements_state(bool on_or_off) {
uint16_t get_temperature_color() {
if (current_temp == -1 || last_temp == -1) {
- return 0x0000;
+ return kColorBackground;
}
if (current_temp > TEMPERATURE_HOT) {
- return 0x8082;
+ return kColorDangerTemp;
}
if (current_temp > TEMPERATURE_WARM) {
- return 0xBDA3;
+ return kColorWarningTemp;
}
- return 0x1423;
+ return kColorSafeTemp;
}
void update_temperature() {
@@ 439,14 457,14 @@ void update_temperature() {
void main_menu_setup() {
set_elements_state(false);
- send_config(1, 0xFFFF, &FreeSerif18pt7b);
+ send_config(1, kColorText, &FreeSerif18pt7b);
send_print("NEON\nGENESIS\nOVENGELION", 0, 50);
send_config();
if (is_calibrated) {
- send_text("CALIBRATION OK", 320, 220, RIGHT, 0x0000, 0x0F00);
+ send_text("CALIBRATION OK", 320, 220, RIGHT, kColorText, kColorSafeTemp);
} else {
- send_text("NO CALIBRATION", 320, 220, RIGHT, 0x0000, 0xF000);
+ send_text("NO CALIBRATION", 320, 220, RIGHT, kColorText, kColorDangerTemp);
}
num_items = 2;
@@ 458,12 476,12 @@ void main_menu_loop() {
if (last_drawn_selection != selection) {
int fg, bg;
- if (selection == 0) { fg=0x0000; bg=0xFFFF; }
- else { fg=0xFFFF; bg=0x0000; }
+ if (selection == 0) { fg=kColorBackground; bg=kColorText; }
+ else { fg=kColorText; bg=kColorBackground; }
send_text("BAKE", 320 / 2, 165, CENTER, fg, bg);
- if (selection == 1) { fg=0x0000; bg=0xFFFF; }
- else { fg=0xFFFF; bg=0x0000; }
+ if (selection == 1) { fg=kColorBackground; bg=kColorText; }
+ else { fg=kColorText; bg=kColorBackground; }
send_text("CALIBRATE", 320 / 2, 195, CENTER, fg, bg);
last_drawn_selection = selection;
@@ 652,15 670,19 @@ int get_desired_temperature(ReflowState state, unsigned long time_in_state) {
return (int) (desired_temp + 0.5);
}
-void bake_setup() {
- int graph_width = 280;
- int graph_height = 140;
- int graph_x = 20;
- int graph_y = 80;
+int graph_x = 20;
+int graph_y = 80;
+int graph_width = 280;
+int graph_height = 140;
+double ms_per_pixel = 0;
+double degrees_per_pixel = 0;
+unsigned long bake_start_time = 0;
+
+void bake_setup() {
// First draw the axises!
- send_line(graph_x-1,graph_y+graph_height+1,graph_x+graph_width+1,graph_y+graph_height+1,0xF000);
- send_line(graph_x-1,graph_y-1,graph_x-1,graph_y+graph_height+1,0xF000);
+ send_line(graph_x-1,graph_y+graph_height+1,graph_x+graph_width+1,graph_y+graph_height+1,kColorGraphLines);
+ send_line(graph_x-1,graph_y-1,graph_x-1,graph_y+graph_height+1,kColorGraphLines);
// Now draw the pretty ideal curve!
unsigned long total_time =
@@ 668,6 690,10 @@ void bake_setup() {
soak_duration +
reflow_duration +
cool_duration;
+
+ ms_per_pixel = (double) graph_width / (double) total_time;
+ degrees_per_pixel = ((float) graph_height / 275);
+
for (int x = graph_x; x < graph_x + graph_width; x++) {
double progress = (x - graph_x) / (float) graph_width;
unsigned long current_time = total_time * progress;
@@ 689,10 715,12 @@ void bake_setup() {
}
desired_temp = get_desired_temperature(state, time_in_state);
}
- double scaled_temp = ((float) graph_height / 275) * desired_temp;
+ double scaled_temp = degrees_per_pixel * desired_temp;
int y = graph_y + graph_height - scaled_temp;
- send_pixel(x, y, 0xFFFF);
+ send_pixel(x, y, kColorIdealGraph);
}
+
+ bake_start_time = millis();
}
void pick_profile_loop() {
@@ 701,23 729,41 @@ void pick_profile_loop() {
void reflow_loop() {
unsigned long current_time = millis();
+
+ ostringstream debug_str;
+ string text;
+
+ unsigned long time_so_far = current_time - bake_start_time;
+ int y = graph_y + graph_height - (degrees_per_pixel * current_temp);
+ send_pixel(graph_x + ((double) ms_per_pixel * (double) time_so_far), y, kColorActualGraph);
+
switch (reflow_state) {
case PREHEAT:
- if (current_temp > preheat_temp) {
+ debug_str << " PREHEAT ";
+
+ if (current_temp > preheat_temp - calibration_lag_degrees) {
reflow_state = SOAK;
reflow_state_start_time = current_time;
+ text = debug_str.str();
+ send_text(text, 20, 20, LEFT, kColorText, kColorBackground);
return;
}
break;
case SOAK:
+ debug_str << " SOAK ";
+
if (current_time - reflow_state_start_time > soak_duration) {
reflow_state = REFLOW;
reflow_state_start_time = current_time;
+ text = debug_str.str();
+ send_text(text, 20, 20, LEFT, kColorText, kColorBackground);
return;
}
break;
case REFLOW:
- if (reflow_rise_time == 0) {
+ debug_str << " REFLOW ";
+
+ if (reflow_rise_time == -1) {
if (current_temp >= reflow_temp - calibration_lag_degrees) {
reflow_rise_time = current_time - reflow_state_start_time;
}
@@ 740,8 786,11 @@ void reflow_loop() {
}
break;
case COOL:
+ debug_str << " COOL ";
if (current_temp < 150) {
next_state = FINISHED_BAKE;
+ text = debug_str.str();
+ send_text(text, 20, 20, LEFT, kColorText, kColorBackground);
return;
}
break;
@@ 749,24 798,28 @@ void reflow_loop() {
unsigned long time_in_state = current_time - reflow_state_start_time;
int desired_temp = get_desired_temperature(reflow_state, time_in_state);
+ debug_str << " desired: " << desired_temp;
if (desired_temp - current_temp < calibration_lag_degrees && holding_at_time == -1) {
// We are within lag_temp of the temperature we should be aiming for.
// Time to start turning the elements off!
- holding_at_temp = desired_temp;
+ is_holding = true;
holding_at_time = current_time;
}
- if (holding_at_temp == desired_temp) {
+ if (is_holding) {
+ debug_str << " HOLDING ";
// We are currently holding the elements off. But what's the state of
// things?
if (current_temp > desired_temp) {
// We overshot! Keep holding the elements off.
+ debug_str << " (overshot) ";
set_elements_state(false);
} else if (current_temp < desired_temp - calibration_lag_degrees) {
+ debug_str << " (undershot) ";
// We have held off for too long. This hopefully shouldn't happen,
// but if it does we immediately stop holding.
- holding_at_temp = -1;
+ is_holding = false;
holding_at_time = -1;
holding_at_reheat_time = -1;
set_elements_state(true);
@@ 775,10 828,12 @@ void reflow_loop() {
// We have been holding the element off for long enough that it
// should be dropping very soon, or temp is already dropping.
if (holding_at_reheat_time == -1) {
+ debug_str << " (reheat) ";
// Turn the elements back on for a bit.
set_elements_state(true);
holding_at_reheat_time = current_time;
} else if (current_time - holding_at_reheat_time < calibration_heat_lag_time) {
+ debug_str << " (cool) ";
// We have held them on for long enough. Turn them back off,
// and begin the cycle again.
set_elements_state(false);
@@ 788,7 843,14 @@ void reflow_loop() {
}
// Otherwise, we are holding the element off and everything looks okay.
// Stay the course.
+ set_elements_state(false);
+ } else {
+ set_elements_state(true);
}
+
+ debug_str << " ";
+ text = debug_str.str();
+ send_text(text, 20, 20, LEFT, kColorText, kColorBackground);
}
bool read_debounced(int pin) {
@@ 801,7 863,7 @@ void top_left_pushed() {
switch (current_state) {
case MAIN_MENU:
if (selection == 0) {
- next_state = PICK_PROFILE;
+ next_state = BAKE;
}
if (selection == 1) {
next_state = CALIBRATE_1;
@@ 858,7 920,7 @@ void change_state(State new_state) {
send_clear();
draw_header();
draw_footer();
- text_bg_color = 0x0000;
+ text_bg_color = kColorBackground;
// Any menu would want to be reset anyway.
last_drawn_selection = -1;
@@ 870,7 932,7 @@ void change_state(State new_state) {
case MAIN_MENU:
main_menu_setup();
break;
- case PICK_PROFILE:
+ case BAKE:
bake_setup();
break;
case CALIBRATE_1:
@@ 962,7 1024,7 @@ void loop() {
draw_temperature();
}
- text_bg_color = 0x0000;
+ text_bg_color = kColorBackground;
int delay_ms = 100;
switch (current_state) {
@@ 981,6 1043,7 @@ void loop() {
case PICK_PROFILE:
break;
case BAKE:
+ reflow_loop();
break;
case FINISHED_BAKE:
break;
@@ 996,7 1059,7 @@ void loop() {
}
/** SECOND CORE **/
-Adafruit_ST7789 core1_display = Adafruit_ST7789(DISPLAY_CS, DISPLAY_DC, DISPLAY_MOSI, DISPLAY_SCLK);
+Adafruit_ST7789 core1_display = Adafruit_ST7789(DISPLAY_CS, DISPLAY_DC, -1);
void core1_draw_text(const TextType& text, const string& actual_text) {
auto str = actual_text.c_str();
@@ 1019,12 1082,12 @@ void core1_draw_text(const TextType& text, const string& actual_text) {
void setup1() {
core1_display.init(240, 320);
- core1_display.setSPISpeed(62'500'000);
+ //core1_display.setSPISpeed(62'500'000);
core1_display.setRotation(3);
core1_display.setFont();
core1_display.setTextSize(2);
- core1_display.setTextColor(ST77XX_WHITE);
+ core1_display.setTextColor(kColorText);
// Wait for the other core to signal us before starting our loop.
rp2040.fifo.pop();
@@ 1042,7 1105,7 @@ void loop1() {
switch (message.type) {
case DrawMessage::CLEAR:
- core1_display.fillScreen(0x0000);
+ core1_display.fillScreen(kColorBackground);
break;
case DrawMessage::RECT:
core1_display.fillRect(