You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
191 lines
5.2 KiB
191 lines
5.2 KiB
/**
|
|
* TODO file description
|
|
*/
|
|
|
|
#include <stdbool.h>
|
|
#include <string.h>
|
|
#include <hardware/gpio.h>
|
|
#include "app_gui.h"
|
|
#include "lcd/lcdbuf.h"
|
|
#include "lcd.h"
|
|
#include "app_config.h"
|
|
#include "app_io.h"
|
|
#include "pinout.h"
|
|
|
|
struct State s_app = {};
|
|
|
|
struct LcdBuffer lcd = {};
|
|
|
|
/** Schedule paint (the screen func will be called with the PAINT event argument */
|
|
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);
|
|
|
|
read_time_and_moisture();
|
|
|
|
LcdBuffer_Init(&lcd, CGROM_A00, CGRAM_CZ);
|
|
}
|
|
|
|
static bool blink_state = 0;
|
|
|
|
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)) {
|
|
switch_screen(screen_home, true);
|
|
}
|
|
}
|
|
|
|
// Read RTC every second
|
|
if (tickNow - s_app.last_1s_time >= 1000) {
|
|
s_app.screen(GUI_EVENT_SCREEN_TICK_1S);
|
|
read_time_and_moisture();
|
|
s_app.last_1s_time = tickNow;
|
|
|
|
// blinking
|
|
blink_state = !blink_state;
|
|
gpio_put(PIN_LED, blink_state);
|
|
|
|
// also blink with the relay pin, so we can route that to a indicator light in the lid
|
|
// ->
|
|
// check if we have schedule enabled & set
|
|
bool any_sch = false;
|
|
if (app_config.scheduler_enable) {
|
|
for (int i = 0; i < SCHEDULE_COUNT; i++) {
|
|
if (!app_config.schedules[i].enable) {
|
|
continue;
|
|
}
|
|
any_sch = true;
|
|
break;
|
|
}
|
|
}
|
|
gpio_put(PIN_RE5, any_sch && blink_state);
|
|
}
|
|
|
|
// 100ms tick event
|
|
if (tickNow - s_app.last_100ms_time >= 100) {
|
|
s_app.screen(GUI_EVENT_SCREEN_TICK_100MS);
|
|
s_app.last_100ms_time = tickNow;
|
|
}
|
|
|
|
// 10ms tick event
|
|
if (tickNow - s_app.last_10ms_time >= 10) {
|
|
s_app.screen(GUI_EVENT_SCREEN_TICK_10MS);
|
|
s_app.last_10ms_time = tickNow;
|
|
}
|
|
|
|
if (message != GUI_EVENT_NONE) {
|
|
s_app.screen(message);
|
|
}
|
|
|
|
if (message >= 32) { // lazy shortcut so we dont have to list all of them
|
|
// key was pressed
|
|
input_sound_effect();
|
|
}
|
|
|
|
if (s_app.paint_needed) {
|
|
s_app.paint_needed = false;
|
|
|
|
draw_common_overlay();
|
|
s_app.screen(GUI_EVENT_PAINT);
|
|
|
|
// If there is anything to print, do it
|
|
LcdBuffer_Flush(&lcd);
|
|
}
|
|
}
|
|
|
|
/** 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)
|
|
{
|
|
if (s_app.screen == pScreen) {
|
|
// already, no op
|
|
return;
|
|
}
|
|
|
|
s_app.screen_open_time = timestamp_ms();
|
|
s_app.screen = pScreen;
|
|
|
|
LcdBuffer_Clear(&lcd);
|
|
LcdBuffer_SetCursor(&lcd, 0, 0, CURSOR_NONE); // always start with a hidden cursor. If the page wants a visible cursor, it should do that in PAINT
|
|
request_paint();
|
|
|
|
// Reset the timers, so the screen has nicely aligned events
|
|
s_app.last_10ms_time = s_app.last_100ms_time = s_app.last_1s_time = timestamp_ms();
|
|
// also read time so we have the latest greatest
|
|
rtc_get_time(&s_app.rtc_time);
|
|
|
|
if (init) {
|
|
pScreen(GUI_EVENT_SCREEN_INIT);
|
|
}
|
|
}
|
|
|
|
/** Draw GUI common to all screens */
|
|
static void draw_common_overlay()
|
|
{
|
|
// TODO
|
|
}
|
|
|
|
/** Play input sound effect if this is an input event */
|
|
void input_sound_effect()
|
|
{
|
|
// TODO
|
|
}
|
|
|