add gui system and demo screen with handled keyboard input

master
Ondřej Hruška 1 year ago
parent ceb18c7c80
commit 149a116ef7
  1. 5
      CMakeLists.txt
  2. 30
      src/app_io.c
  3. 17
      src/app_io.h
  4. 83
      src/main.c
  5. 83
      src/screens/app_gui.c
  6. 63
      src/screens/app_gui.h
  7. 38
      src/screens/gui_event.h
  8. 59
      src/screens/screen_home.c

@ -20,10 +20,13 @@ add_executable(zavlaha
src/lcd/cgram.c
src/lcd/cgrom.c
src/lcd/lcdbuf.c
)
src/screens/app_gui.c
src/screens/screen_home.c src/app_io.c)
# Add pico_stdlib library which aggregates commonly used features
target_link_libraries(zavlaha pico_stdlib hardware_i2c hardware_adc hardware_irq)
target_include_directories(zavlaha PRIVATE src)
# create map/bin/hex/uf2 file in addition to ELF.
pico_add_extra_outputs(zavlaha)

@ -0,0 +1,30 @@
/**
* TODO file description
*/
#include <stdbool.h>
#include <hardware/gpio.h>
#include <hardware/adc.h>
#include "app_io.h"
#include "pinout.h"
void set_relays(bool one, bool two, bool three, bool four) {
gpio_put(PIN_RE1, one);
gpio_put(PIN_RE2, two);
gpio_put(PIN_RE3, three);
gpio_put(PIN_RE1, four);
}
void open_valve(uint8_t num) {
gpio_put(PIN_RE1, num == 1);
gpio_put(PIN_RE2, num == 2);
gpio_put(PIN_RE3, num == 3);
gpio_put(PIN_RE4, num == 4);
}
uint16_t moisture_read() {
adc_select_input(ADC_NUM_MOISTURE);
return adc_read();
}

@ -0,0 +1,17 @@
/**
* TODO file description
*/
#ifndef ZAVLAHA_APP_IO_H
#define ZAVLAHA_APP_IO_H
#include <stdint.h>
#include <stdbool.h>
void set_relays(bool one, bool two, bool three, bool four);
void open_valve(uint8_t num);
uint16_t moisture_read();
#endif //ZAVLAHA_APP_IO_H

@ -4,11 +4,13 @@
#include <hardware/i2c.h>
#include <hardware/adc.h>
#include <hardware/irq.h>
#include <hardware/sync.h>
#include "pinout.h"
#include "lcd.h"
#include "ds_rtc.h"
#include "ee.h"
#include "lcd/lcdbuf.h"
#include "screens/app_gui.h"
/*
@ -49,35 +51,24 @@ void setup_output(uint num)
gpio_set_function(num, GPIO_FUNC_SIO);
}
#define KEYPAD_BUFFER_LEN 10
static uint8_t keypad_buffer[KEYPAD_BUFFER_LEN];
static uint8_t keypad_buffer_wp = 0;
static uint8_t keypad_buffer_rp = 0;
// RX interrupt handler
void irq_uart() {
while (uart_is_readable(uart0)) {
uint8_t ch = uart_getc(uart0);
// TODO do something useful
if (uart_is_writable(uart0)) {
uart_putc(uart0, ch);
if (keypad_buffer[keypad_buffer_wp] == 0) {
keypad_buffer[keypad_buffer_wp] = ch;
keypad_buffer_wp++;
if (keypad_buffer_wp >= KEYPAD_BUFFER_LEN) {
keypad_buffer_wp = 0;
}
}
}
}
void set_relays(bool one, bool two, bool three, bool four) {
gpio_put(PIN_RE1, one);
gpio_put(PIN_RE2, two);
gpio_put(PIN_RE3, three);
gpio_put(PIN_RE1, four);
}
void open_valve(uint num) {
gpio_put(PIN_RE1, num == 1);
gpio_put(PIN_RE2, num == 2);
gpio_put(PIN_RE3, num == 3);
gpio_put(PIN_RE4, num == 4);
}
uint16_t moisture_read() {
adc_select_input(ADC_NUM_MOISTURE);
return adc_read();
}
int main()
@ -134,24 +125,44 @@ int main()
lcd_init();
struct LcdBuffer lcd;
LcdBuffer_Init(&lcd, CGROM_A00, CGRAM_CZ);
LcdBuffer_Write(&lcd, 0, 0, "ěščřŽÝÁÍ pyčo!");
LcdBuffer_Flush(&lcd);
char buf[100];
while (1) {
struct rtc_time time;
if (0 == rtc_get_time(&time)) {
sprintf(buf, "čas: %02d:%02d:%02d", time.hour, time.minute, time.second);
LcdBuffer_Write(&lcd, 2, 2, buf);
gui_init();
for(;;) {
uint32_t interrupts = save_and_disable_interrupts();
GuiEvent event = GUI_EVENT_NONE;
if (keypad_buffer[keypad_buffer_rp] != 0) {
char ch = keypad_buffer[keypad_buffer_rp];
keypad_buffer[keypad_buffer_rp] = 0;
keypad_buffer_rp++;
if (keypad_buffer_rp >= KEYPAD_BUFFER_LEN) {
keypad_buffer_rp = 0;
}
event = ch; // there is 1:1 mapping for keypad ASCII
}
LcdBuffer_Flush(&lcd);
restore_interrupts(interrupts);
sleep_ms(1000);
gui_loop_iter(event);
}
//
// struct LcdBuffer lcd;
// LcdBuffer_Init(&lcd, CGROM_A00, CGRAM_CZ);
//
// LcdBuffer_Write(&lcd, 0, 0, "ěščřŽÝÁÍ pyčo!");
// LcdBuffer_Flush(&lcd);
//
// char buf[100];
// while (1) {
// struct rtc_time time;
// if (0 == rtc_get_time(&time)) {
// sprintf(buf, "čas: %02d:%02d:%02d", time.hour, time.minute, time.second);
// LcdBuffer_Write(&lcd, 2, 2, buf);
// }
// LcdBuffer_Flush(&lcd);
//
// sleep_ms(1000);
// }
#if 0
// while (1) {

@ -0,0 +1,83 @@
/**
* TODO file description
*/
#include <stdbool.h>
#include <string.h>
#include "app_gui.h"
#include "../lcd/lcdbuf.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();
char stmp[100];
/** Main loop */
void gui_init()
{
switch_screen(screen_home, true);
s_app.last_tick_time = timestamp();
LcdBuffer_Init(&lcd, CGROM_A00, CGRAM_CZ);
}
void gui_loop_iter(GuiEvent message) {
uint32_t tickNow = timestamp();
// 10ms tick event
if (tickNow - s_app.last_tick_time > 10) {
s_app.screen(GUI_EVENT_SCREEN_TICK);
s_app.last_tick_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) {
s_app.screen = pScreen;
LcdBuffer_Clear(&lcd);
request_paint();
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
}

@ -0,0 +1,63 @@
/**
* TODO file description
*/
#ifndef ZAVLAHA_APP_GUI_H
#define ZAVLAHA_APP_GUI_H
#include <stdbool.h>
#include <stdint.h>
#include <pico/time.h>
#include "gui_event.h"
#include "lcd/lcdbuf.h"
/// Temporary scratch buffer
extern char stmp[100];
extern struct LcdBuffer lcd;
/**
* Screen callback type. The event is either INIT, PAINT, or one of the input events.
*/
typedef void (*screen_t)(GuiEvent event);
static inline uint32_t timestamp() {
return to_ms_since_boot(get_absolute_time());
}
/** Input beep (push or knob turn) */
void input_sound_effect();
void gui_loop_iter(GuiEvent message);
void gui_init();
/** Switch to a different screen. Handles initial push state handling (so release
* does not cause a "click" event).
*
* @param pScreen - screen to switch to
* @param init - call the INIT event immediately after
*/
void switch_screen(screen_t pScreen, bool init);
void request_paint();
// prototypes for screen handlers
void screen_home(GuiEvent event);
// XXX other prototypes
struct State {
/// Repaint was requested from the screen code
bool paint_needed;
/// Pointer to the currently active screen func
screen_t screen;
uint32_t last_tick_time;
};
extern struct State s_app;
#endif //ZAVLAHA_APP_GUI_H

@ -0,0 +1,38 @@
//
// Created by MightyPork on 2023/04/09.
//
#ifndef ZAVLAHA_GUI_EVENT_H
#define ZAVLAHA_GUI_EVENT_H
// sent through the notify queue
typedef enum GuiEvent {
/// No event, zero; This is a default value.
GUI_EVENT_NONE = 0,
/// Cause redraw
GUI_EVENT_PAINT = 0,
/// Used as the argument when initing a screen
GUI_EVENT_SCREEN_INIT = 1,
/// Time tick, used to carry timing to the screen functions.
/// This tick has 10ms interval
GUI_EVENT_SCREEN_TICK = 2,
/// Keypad
GUI_EVENT_KEY_0 = '0',
GUI_EVENT_KEY_1,
GUI_EVENT_KEY_2,
GUI_EVENT_KEY_3,
GUI_EVENT_KEY_4,
GUI_EVENT_KEY_5,
GUI_EVENT_KEY_6,
GUI_EVENT_KEY_7,
GUI_EVENT_KEY_8,
GUI_EVENT_KEY_9,
GUI_EVENT_KEY_A = 'A',
GUI_EVENT_KEY_B,
GUI_EVENT_KEY_C,
GUI_EVENT_KEY_D,
GUI_EVENT_KEY_STAR = '*',
GUI_EVENT_KEY_HASH = '#',
} GuiEvent;
#endif //ZAVLAHA_GUI_EVENT_H

@ -0,0 +1,59 @@
/**
* TODO file description
*/
//
// Created by MightyPork on 2023/04/09.
//
#include <stdio.h>
#include <string.h>
#include "app_gui.h"
#include "gui_event.h"
#include "ds_rtc.h"
#include "app_io.h"
static uint32_t last_time = 0;
static struct rtc_time rtc_time = {};
static uint16_t moisture = 0;
static char showbuf[20];
static uint8_t showbuf_wp = 0;
void screen_home(GuiEvent event)
{
uint32_t now = timestamp();
uint32_t elapsed = now - last_time;
switch (event) {
case GUI_EVENT_SCREEN_INIT:
memset(showbuf, ' ', sizeof(showbuf));
// pass
case GUI_EVENT_SCREEN_TICK:
if (elapsed >= 100) {
last_time = now;
rtc_get_time(&rtc_time);
moisture = moisture_read();
request_paint();
}
break;
case GUI_EVENT_PAINT:;
LcdBuffer_Write(&lcd, 0, 0, showbuf);
sprintf(stmp, "Čas: %02d:%02d:%02d", rtc_time.hour, rtc_time.minute, rtc_time.second);
LcdBuffer_Write(&lcd, 1, 0, stmp);
sprintf(stmp, "Vlhkost: %4d", moisture);
LcdBuffer_Write(&lcd, 2, 0, stmp);
break;
default:
if (event >= 32) {
showbuf[showbuf_wp++] = event;
if (showbuf_wp == 20) {
showbuf_wp = 0;
}
request_paint();
}
}
}
Loading…
Cancel
Save