final? tuning

master
Ondřej Hruška 2 years 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;
static const struct AppConfig DEFAULTS = {
.circuit_base_time_s = 10, // for a test
.circuit_time_percent = {30, 60, 90, 100}, // for a test
.circuit_base_time_s = 300, // for a test
.circuit_time_percent = {100, 100, 100, 100}, // for a test
.scheduler_enable = true,
.schedules = {
{true, 6, 45},
{false, 0, 0},
{false, 0, 0},
{true, 7, 0},
{true, 14, 0},
{true, 20, 0},
{false, 0, 0}
},
.moisture_enable = true,
.moisture_enable = false,
.moisture_dry = 2800,
.moisture_wet = 1600,
.moisture_threshold_percent = 70,
@ -35,13 +36,13 @@ static void load_fallback_config() {
void appconfig_load()
{
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");
load_fallback_config();
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");
load_fallback_config();
}
@ -49,11 +50,11 @@ void appconfig_load()
void appconfig_save()
{
printf("PERSISTENCE DISABLED - NOT SAVING\r\n");
return; // FIXME - when all GUIs are finished, add persistence
// printf("PERSISTENCE DISABLED - NOT SAVING\r\n");
// return; // FIXME - when all GUIs are finished, add persistence
// 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!");
}
}

@ -8,33 +8,81 @@
#include "lcd/lcdbuf.h"
#include "lcd.h"
#include "app_config.h"
#include "app_io.h"
struct State s_app = {};
struct LcdBuffer lcd = {};
/** Schedule paint (the screen func will be called with the PAINT event argument */
void request_paint() {
void request_paint()
{
s_app.paint_needed = true;
}
/** Draw the common overlay / HUD (with temperatures and heater status) */
static void draw_common_overlay();
//stratch string buffer
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 */
void gui_init()
{
switch_screen(screen_home, true);
rtc_get_time(&s_app.rtc_time);
read_time_and_moisture();
LcdBuffer_Init(&lcd, CGROM_A00, CGRAM_CZ);
}
void gui_loop_iter(GuiEvent message) {
void gui_loop_iter(GuiEvent message)
{
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
if (s_app.screen != screen_home && s_app.screen != screen_cyklus) {
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
if (tickNow - s_app.last_1s_time >= 1000) {
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;
}
@ -83,7 +131,8 @@ void gui_loop_iter(GuiEvent message) {
/** Switch to a different screen handler.
* 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) {
// already, no op
return;
@ -107,11 +156,13 @@ void switch_screen(screen_t pScreen, bool init) {
}
/** Draw GUI common to all screens */
static void draw_common_overlay() {
static void draw_common_overlay()
{
// TODO
}
/** Play input sound effect if this is an input event */
void input_sound_effect() {
void input_sound_effect()
{
// TODO
}

@ -64,8 +64,14 @@ extern int pgm_edit_slot;
void screen_program_edit(GuiEvent event);
// XXX other prototypes
void gui_read_moisture();
struct State {
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
bool paint_needed;
@ -77,6 +83,7 @@ struct State {
uint32_t last_100ms_time;
uint32_t last_1s_time;
uint32_t screen_open_time;
};

@ -11,15 +11,19 @@
#include "app_io.h"
#include "app_config.h"
#define MIN_CYCLE_TIME_S 70 // more than 60
static int phase = 0;
static uint32_t phase_seconds = 0;
static uint32_t phase_seconds_max = 0;
static void end_cycle() {
static void end_cycle()
{
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;
open_valve(phase + 1);
}
@ -30,6 +34,9 @@ void screen_cyklus(GuiEvent event)
case GUI_EVENT_SCREEN_INIT:
phase = 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();
break;
@ -52,24 +59,24 @@ void screen_cyklus(GuiEvent event)
case GUI_EVENT_PAINT:
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);
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;
for (int i = 0; i < 20; i++) {
if ((i*2 + 1) == pieces) {
if ((i * 2 + 1) == pieces) {
buf += sprintf(buf, "");
} else if (i * 2 < pieces) {
buf += sprintf(buf, "");
} else {
} else {
buf += sprintf(buf, " ");
}
}
LcdBuffer_Write(&lcd, 2, 0, sbuf);
LcdBuffer_Write(&lcd, 3, 0, "🅳 Ukončit");
break;
case GUI_EVENT_KEY_D:

@ -12,42 +12,82 @@
#include "app_io.h"
#include "app_config.h"
static uint16_t moisture_pt = 0;
void screen_home(GuiEvent event)
{
switch (event) {
case GUI_EVENT_SCREEN_INIT:
// pass
case GUI_EVENT_SCREEN_TICK_100MS:
moisture_pt = moisture_convert(moisture_read());
if (app_config.moisture_enable) {
gui_read_moisture();
}
request_paint();
break;
case GUI_EVENT_PAINT:
sprintf(sbuf, " %2d:%02d:%02d %3d%% 🌢 ",
s_app.rtc_time.hour,
s_app.rtc_time.minute,
s_app.rtc_time.second,
moisture_pt);
if (app_config.moisture_enable) {
sprintf(sbuf, " %2d:%02d:%02d %3d%% 🌢%s",
s_app.rtc_time.hour,
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, 1, 0, "Plán. závlaha 7:15");
LcdBuffer_Write(&lcd, 2, 0, "❶Spustit ❷Přeskočit");
LcdBuffer_Write(&lcd, 3, 0, "🅰Nastavení");
break;
// Find the next due schedule
if (!app_config.scheduler_enable) {
LcdBuffer_Write(&lcd, 1, 0, "Čas. program vypnutý");
} 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:
// TODO
switch_screen(screen_cyklus, true); // fake cycle
break;
if (first == -1) {
first = i;
}
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:
// TODO
if (found == -1) {
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;
case GUI_EVENT_KEY_A:
switch_screen(screen_cyklus, true); // fake cycle
break;
case GUI_EVENT_KEY_B:
switch_screen(screen_settings, true);
break;
}

@ -5,8 +5,6 @@
#include "app_config.h"
static int phase = 0;
static int tick_counter = 0;
static uint16_t last_moisture = 0;
static uint16_t sample_dry = 0;
@ -15,15 +13,10 @@ void screen_moisture_calib(GuiEvent event)
switch (event) {
case GUI_EVENT_SCREEN_INIT:
phase = 0;
tick_counter = 0;
// pass through
case GUI_EVENT_SCREEN_TICK_10MS:
tick_counter++;
if (tick_counter == 25) {
request_paint(); // will read new measurement
tick_counter = 0;
}
last_moisture = moisture_read();
case GUI_EVENT_SCREEN_TICK_100MS:
gui_read_moisture();
request_paint(); // will read new measurement
break;
case GUI_EVENT_PAINT:
@ -35,7 +28,7 @@ void screen_moisture_calib(GuiEvent event)
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, 3, 0, "🅳Zrušit 🅰Potvrdit");
@ -43,12 +36,12 @@ void screen_moisture_calib(GuiEvent event)
case GUI_EVENT_KEY_A: // Confirm
if (phase == 0) {
sample_dry = last_moisture;
sample_dry = s_app.moisture_raw;
phase++;
LcdBuffer_Clear(&lcd); // make sure there isnt overlap with previous step
} else {
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);
}
break;

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

Loading…
Cancel
Save