Advanced Distributed Systems module at HSLU
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.
 
 

510 lines
16 KiB

/*
* Copyright (c) 2019, Erich Styger
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "platform.h"
#if PL_CONFIG_USE_GUI
#include "gui.h"
#include "lv.h"
#include "LittlevGL/lvgl/lvgl.h"
#include "McuRTOS.h"
#include "FreeRTOS.h"
#include "task.h"
#include "oled.h"
#include "McuSSD1306.h"
#include "McuGDisplaySSD1306.h"
#include "McuFontDisplay.h"
#include "McuUtility.h"
#if PL_CONFIG_USE_SHUTDOWN
#include "shutdown.h"
#endif
#if PL_CONFIG_USE_SHT31
#include "gui_tempHum.h"
#endif
#if PL_CONFIG_USE_UPS
#include "gui_ups.h"
#endif
#if PL_CONFIG_USE_GATEWAY
#include "gui_uart.h"
#include "RaspyUART.h"
#endif
#include "toaster.h"
static TaskHandle_t GUI_TaskHndl;
/* task notification bits */
#define GUI_SET_ORIENTATION_LANDSCAPE (1<<0)
#define GUI_SET_ORIENTATION_LANDSCAPE180 (1<<1)
#define GUI_SET_ORIENTATION_PORTRAIT (1<<2)
#define GUI_SET_ORIENTATION_PORTRAIT180 (1<<3)
#if PL_CONFIG_USE_GUI_KEY_NAV
#define GUI_GROUP_NOF_IN_STACK 4
typedef struct {
lv_group_t *stack[GUI_GROUP_NOF_IN_STACK]; /* stack of GUI groups */
uint8_t sp; /* stack pointer, points to next free element */
} GUI_Group_t;
static GUI_Group_t groups;
/* style modification callback for the focus of an element */
static void style_mod_cb(struct _lv_group_t *grp, lv_style_t *style) {
#if LV_COLOR_DEPTH != 1
/*Make the style to be a little bit orange*/
style->body.border.opa = LV_OPA_COVER;
style->body.border.color = LV_COLOR_ORANGE;
/*If not empty or has border then emphasis the border*/
if(style->body.empty == 0 || style->body.border.width != 0) style->body.border.width = LV_DPI / 50;
style->body.main_color = lv_color_mix(style->body.main_color, LV_COLOR_ORANGE, LV_OPA_70);
style->body.grad_color = lv_color_mix(style->body.grad_color, LV_COLOR_ORANGE, LV_OPA_70);
style->body.shadow.color = lv_color_mix(style->body.shadow.color, LV_COLOR_ORANGE, LV_OPA_60);
style->text.color = lv_color_mix(style->text.color, LV_COLOR_ORANGE, LV_OPA_70);
#else
style->body.border.opa = LV_OPA_COVER;
style->body.border.color = LV_COLOR_BLACK;
style->body.border.width = 3;
#endif
}
void GUI_AddObjToGroup(lv_obj_t *obj) {
lv_group_add_obj(GUI_GroupPeek(), obj);
}
void GUI_RemoveObjFromGroup_(lv_obj_t *obj) {
lv_group_remove_obj(obj);
}
lv_group_t *GUI_GroupPeek(void) {
if (groups.sp == 0) {
return NULL;
}
return groups.stack[groups.sp-1];
}
void GUI_GroupPull(void) {
if (groups.sp == 0) {
return;
}
lv_group_del(groups.stack[groups.sp-1]);
groups.sp--;
lv_indev_set_group(LV_GetInputDevice(), groups.stack[groups.sp-1]); /* assign group to input device */
}
void GUI_GroupPush(void) {
lv_group_t *gui_group;
if (groups.sp >= GUI_GROUP_NOF_IN_STACK) {
return;
}
gui_group = lv_group_create();
lv_indev_set_group(LV_GetInputDevice(), gui_group); /* assign group to input device */
/* change the default focus style which is an orange'ish thing */
lv_group_set_style_mod_cb(gui_group, style_mod_cb);
groups.stack[groups.sp] = gui_group;
groups.sp++;
}
#endif /* PL_CONFIG_USE_GUI_KEY_NAV */
void GUI_ChangeOrientation(McuSSD1306_DisplayOrientation orientation) {
switch(orientation) {
case McuSSD1306_ORIENTATION_LANDSCAPE:
(void)xTaskNotify(GUI_TaskHndl, GUI_SET_ORIENTATION_LANDSCAPE, eSetBits);
break;
case McuSSD1306_ORIENTATION_LANDSCAPE180:
(void)xTaskNotify(GUI_TaskHndl, GUI_SET_ORIENTATION_LANDSCAPE180, eSetBits);
break;
case McuSSD1306_ORIENTATION_PORTRAIT:
(void)xTaskNotify(GUI_TaskHndl, GUI_SET_ORIENTATION_PORTRAIT, eSetBits);
break;
case McuSSD1306_ORIENTATION_PORTRAIT180:
(void)xTaskNotify(GUI_TaskHndl, GUI_SET_ORIENTATION_PORTRAIT180, eSetBits);
break;
default:
break;
}
}
#if PL_CONFIG_USE_UPS
static void btn_click_ups_action(struct _lv_obj_t *obj, lv_event_t event) {
if(event == LV_EVENT_CLICKED) {
GUI_UPS_CreateView();
}
}
#endif
#if PL_CONFIG_USE_GATEWAY
static void btn_click_uart_action(struct _lv_obj_t *obj, lv_event_t event) {
if(event == LV_EVENT_CLICKED) {
GUI_UART_CreateView();
}
}
#endif
#if PL_CONFIG_USE_SHT31
static void btn_click_sht31_action(struct _lv_obj_t *obj, lv_event_t event) {
if(event == LV_EVENT_CLICKED) {
GUI_TEMPHUM_CreateView();
}
}
#endif
#if PL_CONFIG_USE_SHUTDOWN
static lv_obj_t *mboxShutdown, *mboxShutdownWait;
static void mbox_shutdownWait_apply_action(lv_obj_t *mbox, lv_event_t event) {
if (event == LV_EVENT_DELETE && mbox==mboxShutdownWait) {
#if PL_CONFIG_USE_GUI_KEY_NAV
GUI_GroupPull();
#endif
mboxShutdownWait = NULL;
} else if (event == LV_EVENT_VALUE_CHANGED) {
const char *txt = lv_mbox_get_active_btn_text(mbox);
if (McuUtility_strcmp(txt, "\221OK")==0) {
lv_obj_del(mboxShutdownWait);
mboxShutdownWait = NULL;
}
}
}
static void Btn_shutdown_CreateWaitBox(void) {
mboxShutdownWait = lv_mbox_create(lv_scr_act(), NULL);
#if PL_CONFIG_USE_POWER_DOWN_RED_LED
lv_mbox_set_text(mboxShutdownWait, "Shutdown in progress. Wait for RED LED."); /* Set the text */
#else
lv_mbox_set_text(mboxShutdownWait, "Shutdown in progress."); /* Set the text */
#endif
/*Add OK button*/
static const char * btns[] ={"\221OK", ""}; /*Button description. '\221' lv_btnm like control char*/
lv_mbox_add_btns(mboxShutdownWait, btns);
lv_obj_set_event_cb(mboxShutdownWait, mbox_shutdownWait_apply_action);
lv_obj_set_width(mboxShutdownWait, 100);
lv_obj_align(mboxShutdownWait, lv_scr_act(), LV_ALIGN_CENTER, 0, 0); /*Align to center */
#if PL_CONFIG_USE_GUI_KEY_NAV
GUI_GroupPush();
GUI_AddObjToGroup(mboxShutdownWait);
#endif
lv_group_focus_obj(mboxShutdownWait);
}
static void mbox_shutdown_apply_action(lv_obj_t *mbox, lv_event_t event) {
if (event == LV_EVENT_DELETE && mbox==mboxShutdown) {
#if PL_CONFIG_USE_GUI_KEY_NAV
GUI_GroupPull();
#endif
mboxShutdown = NULL;
} else if (event == LV_EVENT_VALUE_CHANGED) {
const char *txt = lv_mbox_get_active_btn_text(mbox);
if (McuUtility_strcmp(txt, "\221Yes")==0) {
SHUTDOWN_RequestPowerOff(); /* request shutdown to Linux */
Btn_shutdown_CreateWaitBox();
} else if (McuUtility_strcmp(txt, "\221Cancel")==0) {
lv_obj_del(mboxShutdown);
}
}
}
static void btn_click_shutdown_action(struct _lv_obj_t *obj, lv_event_t event) {
if(event == LV_EVENT_CLICKED) {
mboxShutdown = lv_mbox_create(lv_scr_act(), NULL);
lv_mbox_set_text(mboxShutdown, "Shutdown Raspy?"); /*Set the text*/
/*Add two buttons*/
static const char * btns[] ={"\221Yes", "\221Cancel", ""}; /*Button description. '\221' lv_btnm like control char*/
lv_mbox_add_btns(mboxShutdown, btns);
lv_obj_set_event_cb(mboxShutdown, mbox_shutdown_apply_action);
lv_obj_set_width(mboxShutdown, 100);
lv_obj_align(mboxShutdown, lv_scr_act(), LV_ALIGN_CENTER, 0, 0); /*Align to center */
#if PL_CONFIG_USE_GUI_KEY_NAV
GUI_GroupPush();
GUI_AddObjToGroup(mboxShutdown);
#endif
lv_group_focus_obj(mboxShutdown);
}
}
#endif /* PL_CONFIG_USE_SHUTDOWN */
#if PL_CONFIG_USE_POWER_ON
static lv_obj_t *mboxPoweron, *mboxPoweronWait;
static void mbox_poweronWait_apply_action(lv_obj_t *mbox, lv_event_t event) {
if (event == LV_EVENT_DELETE && mbox==mboxPoweronWait) {
#if PL_CONFIG_USE_GUI_KEY_NAV
GUI_GroupPull();
#endif
mboxPoweronWait = NULL;
} else if (event == LV_EVENT_VALUE_CHANGED) {
const char *txt = lv_mbox_get_active_btn_text(mbox);
if (McuUtility_strcmp(txt, "\221OK")==0) {
lv_obj_del(mboxPoweronWait);
}
}
}
static void Btn_poweron_CreateWaitBox(void) {
mboxPoweronWait = lv_mbox_create(lv_scr_act(), NULL);
lv_mbox_set_text(mboxPoweronWait, "Poweron in progress."); /* Set the text */
/*Add OK button*/
static const char * btns[] ={"\221OK", ""}; /*Button description. '\221' lv_btnm like control char*/
lv_mbox_add_btns(mboxPoweronWait, btns);
lv_obj_set_event_cb(mboxPoweronWait, mbox_poweronWait_apply_action);
lv_obj_set_width(mboxPoweronWait, 100);
lv_obj_align(mboxPoweronWait, lv_scr_act(), LV_ALIGN_CENTER, 0, 0); /*Align to center */
#if PL_CONFIG_USE_GUI_KEY_NAV
GUI_GroupPush();
GUI_AddObjToGroup(mboxPoweronWait);
#endif
lv_group_focus_obj(mboxPoweronWait);
}
static void mbox_poweron_apply_action(lv_obj_t *mbox, lv_event_t event) {
if (event == LV_EVENT_DELETE && mbox==mboxPoweron) {
#if PL_CONFIG_USE_GUI_KEY_NAV
GUI_GroupPull();
#endif
mboxPoweron = NULL;
} else if (event == LV_EVENT_VALUE_CHANGED) {
const char *txt = lv_mbox_get_active_btn_text(mbox);
if (McuUtility_strcmp(txt, "\221Yes")==0) {
SHUTDOWN_RequestPowerOn(); /* request power-on to Linux */
Btn_poweron_CreateWaitBox();
} else if (McuUtility_strcmp(txt, "\221Cancel")==0) {
lv_obj_del(mboxPoweron);
}
}
}
static void btn_click_poweron_action(struct _lv_obj_t *obj, lv_event_t event) {
if(event == LV_EVENT_CLICKED) {
mboxPoweron = lv_mbox_create(lv_scr_act(), NULL);
lv_mbox_set_text(mboxPoweron, "Poweron Raspy?"); /*Set the text*/
/*Add two buttons*/
static const char * btns[] ={"\221Yes", "\221Cancel", ""}; /*Button description. '\221' lv_btnm like control char*/
lv_mbox_add_btns(mboxPoweron, btns);
lv_obj_set_event_cb(mboxPoweron, mbox_poweron_apply_action);
lv_obj_set_width(mboxPoweron, 100);
lv_obj_align(mboxPoweron, lv_scr_act(), LV_ALIGN_CENTER, 0, 0); /*Align to center */
#if PL_CONFIG_USE_GUI_KEY_NAV
GUI_GroupPush();
GUI_AddObjToGroup(mboxPoweron);
#endif
lv_group_focus_obj(mboxPoweron);
}
}
#endif /* #if PL_CONFIG_USE_POWER_ON */
void GUI_MainMenuCreate(void) {
lv_obj_t *gui_win;
#define AUTO_POS (1) /* automatically place GUI elements */
#if PL_CONFIG_USE_GUI_KEY_NAV
GUI_GroupPush();
#endif
/* create window */
gui_win = lv_win_create(lv_scr_act(), NULL);
lv_win_set_title(gui_win, "Main Menu");
lv_win_set_btn_size(gui_win, 15);
lv_win_set_sb_mode(gui_win, LV_SB_MODE_OFF); /* no scroll bar */
/* Make the window content responsive */
#if AUTO_POS
lv_win_set_layout(gui_win, LV_LAYOUT_PRETTY); /* this will arrange the buttons */
#endif
#if PL_CONFIG_USE_SHUTDOWN || PL_CONFIG_USE_POWER_ON || PL_CONFIG_USE_SHT31 || PL_CONFIG_USE_UPS || PL_CONFIG_USE_GATEWAY
lv_obj_t *btn;
lv_obj_t *label;
#endif
#if PL_CONFIG_USE_SHUTDOWN
btn = lv_btn_create(gui_win, NULL);
#if !AUTO_POS
lv_obj_set_size(btn, 45, 20);
lv_obj_set_pos(btn, 5, 0);
#endif
lv_obj_set_event_cb(btn, btn_click_shutdown_action);
lv_btn_set_fit(btn, LV_FIT_TIGHT);
label = lv_label_create(btn, NULL);
lv_label_set_text(label, "Shutdown");
#if PL_CONFIG_USE_GUI_KEY_NAV
GUI_AddObjToGroup(btn);
#endif
#endif /* PL_CONFIG_USE_SHUTDOWN */
#if PL_CONFIG_USE_POWER_ON
btn = lv_btn_create(gui_win, NULL);
lv_obj_set_event_cb(btn, btn_click_poweron_action);
lv_btn_set_fit(btn, LV_FIT_TIGHT);
label = lv_label_create(btn, NULL);
lv_label_set_text(label, "Poweron");
#if PL_CONFIG_USE_GUI_KEY_NAV
GUI_AddObjToGroup(btn);
#endif
#endif /* PL_CONFIG_USE_SHUTDOWN */
#if PL_CONFIG_USE_SHT31
btn = lv_btn_create(gui_win, NULL);
#if !AUTO_POS
lv_obj_set_size(btn, 40, 20);
lv_obj_set_pos(btn, 60, 0);
#endif
label = lv_label_create(btn, NULL);
lv_label_set_text(label, "SHT31");
//lv_obj_set_free_num(btn, 1); /*Set a unique number for the button*/
lv_obj_set_event_cb(btn, btn_click_sht31_action);
lv_btn_set_fit(btn, LV_FIT_TIGHT);
#if PL_CONFIG_USE_GUI_KEY_NAV
GUI_AddObjToGroup(btn);
#endif
#endif /* PL_CONFIG_USE_SHT31 */
#if PL_CONFIG_USE_UPS
btn = lv_btn_create(gui_win, NULL);
#if !AUTO_POS
lv_obj_set_size(btn, 40, 20);
lv_obj_set_pos(btn, 5, 30);
#endif
label = lv_label_create(btn, NULL);
lv_label_set_text(label, "UPS");
lv_obj_set_event_cb(btn, btn_click_ups_action);
lv_btn_set_fit(btn, LV_FIT_TIGHT);
#if PL_CONFIG_USE_GUI_KEY_NAV
GUI_AddObjToGroup(btn);
#endif
#endif /* PL_CONFIG_USE_UPS */
#if PL_CONFIG_USE_GATEWAY
btn = lv_btn_create(gui_win, NULL);
#if !AUTO_POS
lv_obj_set_size(btn, 60, 20);
lv_obj_set_pos(btn, 5, 30);
#endif
label = lv_label_create(btn, NULL);
lv_label_set_text(label, "UART");
lv_obj_set_event_cb(btn, btn_click_uart_action);
lv_btn_set_fit(btn, LV_FIT_TIGHT);
#if PL_CONFIG_USE_GUI_KEY_NAV
GUI_AddObjToGroup(btn);
#endif
#endif /* PL_CONFIG_USE_GATEWAY */
}
static void GuiTask(void *pv) {
// uint32_t notifcationValue;
bool init = true;
(void)pv; /* not used */
vTaskDelay(pdMS_TO_TICKS(1000)); /* give hardware time to power up */
if (init) {
McuSSD1306_Init(); /* requires I2C interrupts enabled if using HW I2C! */
// McuSSD1306_SetDisplayClockDiv(0xA0); /* to increase refresh rate and reduce acoustic noise with white background */
OLED_Init(); /* initializes the needed McuLib components for the OLED */
/* quick test if LCD is working properly */
McuSSD1306_Clear();
McuSSD1306_PrintString(1, 0, (uint8_t*)"hello robot!");
}
GUI_MainMenuCreate();
for(;;) {
#if 0
(void)xTaskNotifyWait(0UL, GUI_SET_ORIENTATION_LANDSCAPE|GUI_SET_ORIENTATION_LANDSCAPE180|GUI_SET_ORIENTATION_PORTRAIT|GUI_SET_ORIENTATION_PORTRAIT180, &notifcationValue, 0); /* check flags */
if (notifcationValue!=0) {
lv_area_t area;
if (notifcationValue&GUI_SET_ORIENTATION_LANDSCAPE) {
McuGDisplaySSD1306_SetDisplayOrientation(McuGDisplaySSD1306_ORIENTATION_LANDSCAPE);
area.x1 = 0;
area.y1 = 0;
area.x2 = McuGDisplaySSD1306_GetWidth()-1;
area.y2 = McuGDisplaySSD1306_GetHeight()-1;
lv_inv_area(NULL, &area);
lv_inv_area(NULL, &area);
} else if (notifcationValue&GUI_SET_ORIENTATION_LANDSCAPE180) {
McuGDisplaySSD1306_SetDisplayOrientation(McuGDisplaySSD1306_ORIENTATION_LANDSCAPE180);
area.x1 = 0;
area.y1 = 0;
area.x2 = McuGDisplaySSD1306_GetWidth()-1;
area.y2 = McuGDisplaySSD1306_GetHeight()-1;
lv_inv_area(NULL, &area);
} else if (notifcationValue&GUI_SET_ORIENTATION_PORTRAIT) {
McuGDisplaySSD1306_SetDisplayOrientation(McuGDisplaySSD1306_ORIENTATION_PORTRAIT);
area.x1 = 0;
area.y1 = 0;
area.x2 = McuGDisplaySSD1306_GetWidth()-1;
area.y2 = McuGDisplaySSD1306_GetHeight()-1;
lv_inv_area(NULL, &area);
} else if (notifcationValue&GUI_SET_ORIENTATION_PORTRAIT180) {
McuGDisplaySSD1306_SetDisplayOrientation(McuGDisplaySSD1306_ORIENTATION_PORTRAIT180);
area.x1 = 0;
area.y1 = 0;
area.x2 = McuGDisplaySSD1306_GetWidth()-1;
area.y2 = McuGDisplaySSD1306_GetHeight()-1;
lv_inv_area(NULL, &area);
lv_obj_invalidate(lv_scr_act());
}
}
#endif
// SEGGER_SYSVIEW_MarkStart(1);
// TOASTER_DisplayToasters();
// SEGGER_SYSVIEW_MarkStop(1);
LV_Task(); /* call this every 1-20 ms */
vTaskDelay(pdMS_TO_TICKS(20));
}
}
#define APP_PERIODIC_TIMER_PERIOD_MS 10
static TimerHandle_t timerHndl;
static void vTimerGuiTickCallbackExpired(TimerHandle_t pxTimer) {
(void)pxTimer; /* not used */
lv_tick_inc(APP_PERIODIC_TIMER_PERIOD_MS);
}
void GUI_Init(void) {
LV_Init(); /* initialize GUI library */
//lv_theme_set_current(lv_theme_night_init(128, NULL));
//lv_theme_set_current(lv_theme_alien_init(128, NULL));
//lv_theme_set_current(lv_theme_default_init(128, NULL));
//lv_theme_set_current(lv_theme_material_init(128, NULL));
lv_theme_set_current(lv_theme_mono_init(128, NULL));
//lv_theme_set_current(lv_theme_zen_init(128, NULL));
//lv_theme_set_current(lv_theme_nemo_init(128, NULL));
//lv_theme_t *th = lv_theme_get_current();
/* change default button style */
// lv_style_btn_rel.body.radius = LV_DPI / 15;
// lv_style_btn_rel.body.padding.hor = LV_DPI / 8;
// lv_style_btn_rel.body.padding.ver = LV_DPI / 12;
if (xTaskCreate(GuiTask, "Gui", 3*1024/sizeof(StackType_t), NULL, tskIDLE_PRIORITY+1, &GUI_TaskHndl) != pdPASS) {
for(;;){} /* error */
}
timerHndl = xTimerCreate( /* timer to handle periodic things */
"guiTick", /* name */
pdMS_TO_TICKS(APP_PERIODIC_TIMER_PERIOD_MS), /* period/time */
pdTRUE, /* auto reload */
(void*)0, /* timer ID */
vTimerGuiTickCallbackExpired); /* callback */
if (timerHndl==NULL) {
for(;;); /* failure! */
}
if (xTimerStart(timerHndl, 0)!=pdPASS) { /* start the timer */
for(;;); /* failure!?! */
}
#if PL_CONFIG_USE_GUI_KEY_NAV
groups.sp = 0;
#endif
}
#endif /* PL_CONFIG_HAS_GUI */