final? tuning

master
Ondřej Hruška 1 year ago
parent 4018e27c6b
commit 2949c4a2ea
  1. 25
      src/app_config.c
  2. 65
      src/screens/app_gui.c
  3. 7
      src/screens/app_gui.h
  4. 19
      src/screens/screen_cyklus.c
  5. 76
      src/screens/screen_home.c
  6. 19
      src/screens/screen_moisture_calib.c
  7. 6
      src/screens/screen_program_edit.c

@ -13,15 +13,16 @@
struct AppConfig app_config; struct AppConfig app_config;
static const struct AppConfig DEFAULTS = { static const struct AppConfig DEFAULTS = {
.circuit_base_time_s = 10, // for a test .circuit_base_time_s = 300, // for a test
.circuit_time_percent = {30, 60, 90, 100}, // for a test .circuit_time_percent = {100, 100, 100, 100}, // for a test
.scheduler_enable = true,
.schedules = { .schedules = {
{true, 6, 45}, {true, 7, 0},
{false, 0, 0}, {true, 14, 0},
{false, 0, 0}, {true, 20, 0},
{false, 0, 0} {false, 0, 0}
}, },
.moisture_enable = true, .moisture_enable = false,
.moisture_dry = 2800, .moisture_dry = 2800,
.moisture_wet = 1600, .moisture_wet = 1600,
.moisture_threshold_percent = 70, .moisture_threshold_percent = 70,
@ -35,13 +36,13 @@ static void load_fallback_config() {
void appconfig_load() void appconfig_load()
{ {
uint8_t magic = 0; uint8_t magic = 0;
if (0 != ee_read(APPCONFIG_EE_BASE_ADDR, &magic, 1) || magic != APPCONFIG_VERSION) { if (0 > ee_read(APPCONFIG_EE_BASE_ADDR, &magic, 1) || magic != APPCONFIG_VERSION) {
printf("Couldn't read EE to check magic!\r\n"); printf("Couldn't read EE to check magic!\r\n");
load_fallback_config(); load_fallback_config();
return; return;
} }
if (0 != ee_read(APPCONFIG_EE_BASE_ADDR, (uint8_t *) &app_config, APPCONFIG_SIZE)) { if (0 > ee_read(APPCONFIG_EE_BASE_ADDR, (uint8_t *) &app_config, APPCONFIG_SIZE)) {
printf("Couldn't read EE to load config!\r\n"); printf("Couldn't read EE to load config!\r\n");
load_fallback_config(); load_fallback_config();
} }
@ -49,11 +50,11 @@ void appconfig_load()
void appconfig_save() void appconfig_save()
{ {
printf("PERSISTENCE DISABLED - NOT SAVING\r\n"); // printf("PERSISTENCE DISABLED - NOT SAVING\r\n");
return; // FIXME - when all GUIs are finished, add persistence // return; // FIXME - when all GUIs are finished, add persistence
// whatever, just write everything // whatever, just write everything
if (0 != ee_write(APPCONFIG_EE_BASE_ADDR, (const uint8_t *) &app_config, APPCONFIG_SIZE)) { app_config.magic_version = APPCONFIG_VERSION;
if (0 > ee_write(APPCONFIG_EE_BASE_ADDR, (const uint8_t *) &app_config, APPCONFIG_SIZE)) {
printf("Couldn't write EE!"); printf("Couldn't write EE!");
} }
} }

@ -8,33 +8,81 @@
#include "lcd/lcdbuf.h" #include "lcd/lcdbuf.h"
#include "lcd.h" #include "lcd.h"
#include "app_config.h" #include "app_config.h"
#include "app_io.h"
struct State s_app = {}; struct State s_app = {};
struct LcdBuffer lcd = {}; struct LcdBuffer lcd = {};
/** Schedule paint (the screen func will be called with the PAINT event argument */ /** Schedule paint (the screen func will be called with the PAINT event argument */
void request_paint() { void request_paint()
{
s_app.paint_needed = true; s_app.paint_needed = true;
} }
/** Draw the common overlay / HUD (with temperatures and heater status) */ /** Draw the common overlay / HUD (with temperatures and heater status) */
static void draw_common_overlay(); static void draw_common_overlay();
//stratch string buffer
char sbuf[100]; char sbuf[100];
void gui_read_moisture() {
s_app.moisture_pt = moisture_convert(s_app.moisture_raw = moisture_read());
}
static void read_time_and_moisture() {
rtc_get_time(&s_app.rtc_time); // now is a good time to update timestamp - we could just increment it though
gui_read_moisture();
}
/** Main loop */ /** Main loop */
void gui_init() void gui_init()
{ {
switch_screen(screen_home, true); switch_screen(screen_home, true);
rtc_get_time(&s_app.rtc_time);
read_time_and_moisture();
LcdBuffer_Init(&lcd, CGROM_A00, CGRAM_CZ); LcdBuffer_Init(&lcd, CGROM_A00, CGRAM_CZ);
} }
void gui_loop_iter(GuiEvent message) { void gui_loop_iter(GuiEvent message)
{
uint32_t tickNow = timestamp_ms(); uint32_t tickNow = timestamp_ms();
// Time program logic - since this runs very often, we are guaranteed not to miss a cycle - unless there is a power outage
// exactly in the scheduled watering time
if (s_app.screen != screen_cyklus && app_config.scheduler_enable) {
if (s_app.cycle_time_checking) {
if (s_app.rtc_time.hour != s_app.last_cycle_time.hour
|| s_app.rtc_time.minute != s_app.last_cycle_time.minute) {
s_app.cycle_time_checking = false;
}
} else {
for (int i = 0; i < SCHEDULE_COUNT; i++) {
if (!app_config.schedules[i].enable) {
continue;
}
if (s_app.rtc_time.hour == app_config.schedules[i].h
&& s_app.rtc_time.minute == app_config.schedules[i].m) {
// cycle can preempt all screens
// Check if moisture is OK
if (app_config.moisture_enable) {
if (s_app.moisture_pt > app_config.moisture_threshold_percent) {
// watering not needed
break;
}
}
switch_screen(screen_cyklus, true);
break;
}
}
}
}
// screen auto-close // screen auto-close
if (s_app.screen != screen_home && s_app.screen != screen_cyklus) { if (s_app.screen != screen_home && s_app.screen != screen_cyklus) {
if ((tickNow - s_app.screen_open_time) >= (MENU_AUTOEXIT_TIME_S * 1000)) { if ((tickNow - s_app.screen_open_time) >= (MENU_AUTOEXIT_TIME_S * 1000)) {
@ -45,7 +93,7 @@ void gui_loop_iter(GuiEvent message) {
// Read RTC every second // Read RTC every second
if (tickNow - s_app.last_1s_time >= 1000) { if (tickNow - s_app.last_1s_time >= 1000) {
s_app.screen(GUI_EVENT_SCREEN_TICK_1S); s_app.screen(GUI_EVENT_SCREEN_TICK_1S);
rtc_get_time(&s_app.rtc_time); // now is a good time to update timestamp - we could just increment it though read_time_and_moisture();
s_app.last_1s_time = tickNow; s_app.last_1s_time = tickNow;
} }
@ -83,7 +131,8 @@ void gui_loop_iter(GuiEvent message) {
/** Switch to a different screen handler. /** Switch to a different screen handler.
* If "init" is true, immediately call it with the init event. */ * If "init" is true, immediately call it with the init event. */
void switch_screen(screen_t pScreen, bool init) { void switch_screen(screen_t pScreen, bool init)
{
if (s_app.screen == pScreen) { if (s_app.screen == pScreen) {
// already, no op // already, no op
return; return;
@ -107,11 +156,13 @@ void switch_screen(screen_t pScreen, bool init) {
} }
/** Draw GUI common to all screens */ /** Draw GUI common to all screens */
static void draw_common_overlay() { static void draw_common_overlay()
{
// TODO // TODO
} }
/** Play input sound effect if this is an input event */ /** Play input sound effect if this is an input event */
void input_sound_effect() { void input_sound_effect()
{
// TODO // TODO
} }

@ -64,8 +64,14 @@ extern int pgm_edit_slot;
void screen_program_edit(GuiEvent event); void screen_program_edit(GuiEvent event);
// XXX other prototypes // XXX other prototypes
void gui_read_moisture();
struct State { struct State {
struct rtc_time rtc_time; struct rtc_time rtc_time;
uint16_t moisture_raw;
uint16_t moisture_pt;
struct rtc_time last_cycle_time;
bool cycle_time_checking;
/// Repaint was requested from the screen code /// Repaint was requested from the screen code
bool paint_needed; bool paint_needed;
@ -77,6 +83,7 @@ struct State {
uint32_t last_100ms_time; uint32_t last_100ms_time;
uint32_t last_1s_time; uint32_t last_1s_time;
uint32_t screen_open_time; uint32_t screen_open_time;
}; };

@ -11,15 +11,19 @@
#include "app_io.h" #include "app_io.h"
#include "app_config.h" #include "app_config.h"
#define MIN_CYCLE_TIME_S 70 // more than 60
static int phase = 0; static int phase = 0;
static uint32_t phase_seconds = 0; static uint32_t phase_seconds = 0;
static uint32_t phase_seconds_max = 0; static uint32_t phase_seconds_max = 0;
static void end_cycle() { static void end_cycle()
{
open_valve(0); open_valve(0);
} }
static void start_branch() { static void start_branch()
{
phase_seconds_max = ((uint32_t) app_config.circuit_base_time_s * (uint32_t) app_config.circuit_time_percent[phase]) / 100; phase_seconds_max = ((uint32_t) app_config.circuit_base_time_s * (uint32_t) app_config.circuit_time_percent[phase]) / 100;
open_valve(phase + 1); open_valve(phase + 1);
} }
@ -30,6 +34,9 @@ void screen_cyklus(GuiEvent event)
case GUI_EVENT_SCREEN_INIT: case GUI_EVENT_SCREEN_INIT:
phase = 0; phase = 0;
phase_seconds = 0; phase_seconds = 0;
s_app.last_cycle_time.hour = s_app.rtc_time.hour;
s_app.last_cycle_time.minute = s_app.rtc_time.minute;
s_app.cycle_time_checking = true;
start_branch(); start_branch();
break; break;
@ -52,24 +59,24 @@ void screen_cyklus(GuiEvent event)
case GUI_EVENT_PAINT: case GUI_EVENT_PAINT:
LcdBuffer_Write(&lcd, 0, 0, "==ZÁVLAHOVÝ CYKLUS=="); LcdBuffer_Write(&lcd, 0, 0, "==ZÁVLAHOVÝ CYKLUS==");
LcdBuffer_Write(&lcd, 3, 0, "🅳 Ukončit");
snprintf(sbuf, sbuf_len, "Okruh %d/%d %3d/%d s ", phase + 1, 4, phase_seconds, phase_seconds_max); snprintf(sbuf, sbuf_len, "Okruh %d/%d %3d/%d s ", phase + 1, 4, phase_seconds, phase_seconds_max);
LcdBuffer_Write(&lcd, 1, 0, sbuf); LcdBuffer_Write(&lcd, 1, 0, sbuf);
uint32_t pieces = (phase_seconds * 40) / phase_seconds_max; uint32_t pieces = (phase_seconds * 40) / phase_seconds_max;
char *buf = sbuf; char *buf = sbuf;
for (int i = 0; i < 20; i++) { for (int i = 0; i < 20; i++) {
if ((i*2 + 1) == pieces) { if ((i * 2 + 1) == pieces) {
buf += sprintf(buf, ""); buf += sprintf(buf, "");
} else if (i * 2 < pieces) { } else if (i * 2 < pieces) {
buf += sprintf(buf, ""); buf += sprintf(buf, "");
} else { } else {
buf += sprintf(buf, " "); buf += sprintf(buf, " ");
} }
} }
LcdBuffer_Write(&lcd, 2, 0, sbuf); LcdBuffer_Write(&lcd, 2, 0, sbuf);
LcdBuffer_Write(&lcd, 3, 0, "🅳 Ukončit");
break; break;
case GUI_EVENT_KEY_D: case GUI_EVENT_KEY_D:

@ -12,42 +12,82 @@
#include "app_io.h" #include "app_io.h"
#include "app_config.h" #include "app_config.h"
static uint16_t moisture_pt = 0;
void screen_home(GuiEvent event) void screen_home(GuiEvent event)
{ {
switch (event) { switch (event) {
case GUI_EVENT_SCREEN_INIT: case GUI_EVENT_SCREEN_INIT:
// pass // pass
case GUI_EVENT_SCREEN_TICK_100MS: case GUI_EVENT_SCREEN_TICK_100MS:
moisture_pt = moisture_convert(moisture_read()); if (app_config.moisture_enable) {
gui_read_moisture();
}
request_paint(); request_paint();
break; break;
case GUI_EVENT_PAINT: case GUI_EVENT_PAINT:
sprintf(sbuf, " %2d:%02d:%02d %3d%% 🌢 ",
s_app.rtc_time.hour, if (app_config.moisture_enable) {
s_app.rtc_time.minute, sprintf(sbuf, " %2d:%02d:%02d %3d%% 🌢%s",
s_app.rtc_time.second, s_app.rtc_time.hour,
moisture_pt); s_app.rtc_time.minute,
s_app.rtc_time.second,
s_app.moisture_pt,
s_app.moisture_pt > app_config.moisture_threshold_percent ? "" : " "
);
} else {
sprintf(sbuf, " %2d:%02d:%02d",
s_app.rtc_time.hour,
s_app.rtc_time.minute,
s_app.rtc_time.second);
}
LcdBuffer_Write(&lcd, 0, 0, sbuf); LcdBuffer_Write(&lcd, 0, 0, sbuf);
LcdBuffer_Write(&lcd, 1, 0, "Plán. závlaha 7:15"); // Find the next due schedule
LcdBuffer_Write(&lcd, 2, 0, "❶Spustit ❷Přeskočit"); if (!app_config.scheduler_enable) {
LcdBuffer_Write(&lcd, 3, 0, "🅰Nastavení"); LcdBuffer_Write(&lcd, 1, 0, "Čas. program vypnutý");
break; } else {
int found = -1;
int first = -1;
uint16_t m_now = s_app.rtc_time.hour * 60 + s_app.rtc_time.minute;
for (int i = 0; i < SCHEDULE_COUNT; i++) {
if (!app_config.schedules[i].enable) {
continue;
}
case GUI_EVENT_KEY_1: if (first == -1) {
// TODO first = i;
switch_screen(screen_cyklus, true); // fake cycle }
break;
uint16_t m = app_config.schedules[i].h * 60 + app_config.schedules[i].m;
if (m >= m_now) {
found = i;
break;
}
}
case GUI_EVENT_KEY_2: if (found == -1) {
// TODO found = first;
}
if (found == -1) {
LcdBuffer_Write(&lcd, 1, 0, "Program nenastaven!");
} else {
snprintf(sbuf, sbuf_len, "Další závlaha %d:%02d ",
app_config.schedules[found].h, app_config.schedules[found].m);
LcdBuffer_Write(&lcd, 1, 0, sbuf);
}
}
LcdBuffer_Write(&lcd, 2, 0, "🅰Spustit cyklus");
LcdBuffer_Write(&lcd, 3, 0, "🅱Nastavení");
break; break;
case GUI_EVENT_KEY_A: case GUI_EVENT_KEY_A:
switch_screen(screen_cyklus, true); // fake cycle
break;
case GUI_EVENT_KEY_B:
switch_screen(screen_settings, true); switch_screen(screen_settings, true);
break; break;
} }

@ -5,8 +5,6 @@
#include "app_config.h" #include "app_config.h"
static int phase = 0; static int phase = 0;
static int tick_counter = 0;
static uint16_t last_moisture = 0;
static uint16_t sample_dry = 0; static uint16_t sample_dry = 0;
@ -15,15 +13,10 @@ void screen_moisture_calib(GuiEvent event)
switch (event) { switch (event) {
case GUI_EVENT_SCREEN_INIT: case GUI_EVENT_SCREEN_INIT:
phase = 0; phase = 0;
tick_counter = 0;
// pass through // pass through
case GUI_EVENT_SCREEN_TICK_10MS: case GUI_EVENT_SCREEN_TICK_100MS:
tick_counter++; gui_read_moisture();
if (tick_counter == 25) { request_paint(); // will read new measurement
request_paint(); // will read new measurement
tick_counter = 0;
}
last_moisture = moisture_read();
break; break;
case GUI_EVENT_PAINT: case GUI_EVENT_PAINT:
@ -35,7 +28,7 @@ void screen_moisture_calib(GuiEvent event)
LcdBuffer_Write(&lcd, 1, 0, "půdy (vlhkost 100%)"); LcdBuffer_Write(&lcd, 1, 0, "půdy (vlhkost 100%)");
} }
snprintf(sbuf, sbuf_len, "Vlhkost %4d/4095", last_moisture); snprintf(sbuf, sbuf_len, "Vlhkost %4d/4095", s_app.moisture_raw);
LcdBuffer_Write(&lcd, 2, 0, sbuf); LcdBuffer_Write(&lcd, 2, 0, sbuf);
LcdBuffer_Write(&lcd, 3, 0, "🅳Zrušit 🅰Potvrdit"); LcdBuffer_Write(&lcd, 3, 0, "🅳Zrušit 🅰Potvrdit");
@ -43,12 +36,12 @@ void screen_moisture_calib(GuiEvent event)
case GUI_EVENT_KEY_A: // Confirm case GUI_EVENT_KEY_A: // Confirm
if (phase == 0) { if (phase == 0) {
sample_dry = last_moisture; sample_dry = s_app.moisture_raw;
phase++; phase++;
LcdBuffer_Clear(&lcd); // make sure there isnt overlap with previous step LcdBuffer_Clear(&lcd); // make sure there isnt overlap with previous step
} else { } else {
settings_scratch.moisture_dry = sample_dry; settings_scratch.moisture_dry = sample_dry;
settings_scratch.moisture_wet = last_moisture; settings_scratch.moisture_wet = s_app.moisture_raw;
switch_screen(screen_settings, false); switch_screen(screen_settings, false);
} }
break; break;

@ -59,6 +59,12 @@ void screen_program_edit(GuiEvent event)
default: default:
if (event >= '0' && event <= '9') { if (event >= '0' && event <= '9') {
if (!time.enable) {
time.enable = true;
// make sure there is no old content
LcdBuffer_Clear(&lcd);
}
if (cursor == 0) { if (cursor == 0) {
// it showed the current time, but now we are entering something, so it must be reset! // it showed the current time, but now we are entering something, so it must be reset!
time.h = 0; time.h = 0;

Loading…
Cancel
Save