From 2a0571ba87183023b1073af1badb5fb91da763cf Mon Sep 17 00:00:00 2001 From: ConfuSomu Date: Sat, 27 Feb 2021 13:29:37 -0500 Subject: Use an Api class for abstraction It is currently very basic as functions will be added to it when the time comes. The idea is to have a method to, for example, show notifications (and store them in a list with metadata) to the user or also to easily show a message box overlaying the current display. Private class members will permit encapsulations of variables related to these features and limit the privilage that each app has on the device. --- pico-watch.cpp | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) (limited to 'pico-watch.cpp') diff --git a/pico-watch.cpp b/pico-watch.cpp index 243f1e1..e888ba1 100644 --- a/pico-watch.cpp +++ b/pico-watch.cpp @@ -6,49 +6,51 @@ #include "oled/ss_oled.h" #include "init.hpp" +#include "api.hpp" #include "buttons.hpp" #include "apps/main_clock.hpp" #include "apps/home_menu.hpp" int current_app = 0; +Api app_api; #define NUMBER_OF_APPS 2 #define APP_DATA_BUFFER_LEN 256 -int (*APPS_FUNC_INIT[NUMBER_OF_APPS])(SSOLED *oled) = {app_home_menu::init, app_main_clock::init}; -int (*APPS_FUNC_RENDER[NUMBER_OF_APPS])(SSOLED *oled) = {app_home_menu::render, app_main_clock::render}; -int (*APPS_FUNC_BTNPRESS[NUMBER_OF_APPS])(SSOLED *oled, uint gpio) = {app_home_menu::btnpressed, app_main_clock::btnpressed}; -int (*APPS_FUNC_BGREFRESH[NUMBER_OF_APPS])(SSOLED *oled, char in_foreground) = {app_home_menu::bgrefresh, app_main_clock::bgrefresh}; -int (*APPS_FUNC_DESTROY[NUMBER_OF_APPS])(SSOLED *oled) = {app_home_menu::destroy, app_main_clock::destroy}; +int (*APPS_FUNC_INIT[NUMBER_OF_APPS])(Api *app_api) = {app_home_menu::init, app_main_clock::init}; +int (*APPS_FUNC_RENDER[NUMBER_OF_APPS])(Api *app_api) = {app_home_menu::render, app_main_clock::render}; +int (*APPS_FUNC_BTNPRESS[NUMBER_OF_APPS])(Api *app_api, uint gpio) = {app_home_menu::btnpressed, app_main_clock::btnpressed}; +int (*APPS_FUNC_BGREFRESH[NUMBER_OF_APPS])(Api *app_api, char in_foreground) = {app_home_menu::bgrefresh, app_main_clock::bgrefresh}; +int (*APPS_FUNC_DESTROY[NUMBER_OF_APPS])(Api *app_api) = {app_home_menu::destroy, app_main_clock::destroy}; int APPS_DESTROY_ON_EXIT[NUMBER_OF_APPS] = {0, 1}; int APPS_IS_INIT[NUMBER_OF_APPS] = {0, 0}; // Only run in background if init int app_init(int app_id) { - oledFill(&oled, 0,1); // Clear OLED + app_api.dispFill(0,1); // Clear OLED if (!APPS_IS_INIT[app_id]) { APPS_IS_INIT[app_id] = 1; - return (*APPS_FUNC_INIT[app_id])(&oled); + return (*APPS_FUNC_INIT[app_id])(&app_api); } } int app_render(int app_id) { - return (*APPS_FUNC_RENDER[app_id])(&oled); + return (*APPS_FUNC_RENDER[app_id])(&app_api); } int app_btnpressed(int app_id, uint gpio) { - return (*APPS_FUNC_BTNPRESS[app_id])(&oled, gpio); + return (*APPS_FUNC_BTNPRESS[app_id])(&app_api, gpio); } int app_destroy(int app_id) { // TODO: reset APPS_DATA for the app if (APPS_IS_INIT[app_id]) { APPS_IS_INIT[app_id] = 0; - return (*APPS_FUNC_DESTROY[app_id])(&oled); + return (*APPS_FUNC_DESTROY[app_id])(&app_api); } } int app_bgrefresh(int app_id) { if (APPS_IS_INIT[app_id]) - return (*APPS_FUNC_BGREFRESH[app_id])(&oled, app_id==current_app); + return (*APPS_FUNC_BGREFRESH[app_id])(&app_api, app_id==current_app); } bool apps_bgrefresh(struct repeating_timer *t) { // TODO: Refresh done on core1 @@ -69,6 +71,7 @@ void app_switch(int old_appid, int new_appid) { int main() { init_all(); init_buttons(); + app_api.init(); struct repeating_timer timer; add_repeating_timer_ms(250, apps_bgrefresh, NULL, &timer); -- cgit v1.2.3-54-g00ecf From ccf1d4f74eaf9c56fe2ea620ddc6e12746a8b108 Mon Sep 17 00:00:00 2001 From: ConfuSomu Date: Sat, 27 Feb 2021 15:11:38 -0500 Subject: Remove unnecessary includes --- api.hpp | 1 - apps/home_menu.cpp | 1 - apps/main_clock.cpp | 1 - pico-watch.cpp | 1 - 4 files changed, 4 deletions(-) (limited to 'pico-watch.cpp') diff --git a/api.hpp b/api.hpp index 0e4992f..735d55b 100644 --- a/api.hpp +++ b/api.hpp @@ -1,7 +1,6 @@ #ifndef __API_H__ #define __API_H__ -#include "pico/util/datetime.h" #include "oled/ss_oled.h" class Api { diff --git a/apps/home_menu.cpp b/apps/home_menu.cpp index c261611..8f0ee8b 100644 --- a/apps/home_menu.cpp +++ b/apps/home_menu.cpp @@ -4,7 +4,6 @@ extern "C" { #include "hardware/rtc.h" } #include "pico/util/datetime.h" -#include "../oled/ss_oled.h" #include "home_menu.hpp" #include "../api.hpp" diff --git a/apps/main_clock.cpp b/apps/main_clock.cpp index db8e39e..9939e4e 100644 --- a/apps/main_clock.cpp +++ b/apps/main_clock.cpp @@ -4,7 +4,6 @@ extern "C" { #include "hardware/rtc.h" } #include "pico/util/datetime.h" -#include "../oled/ss_oled.h" #include "main_clock.hpp" #include "../api.hpp" diff --git a/pico-watch.cpp b/pico-watch.cpp index e888ba1..181d708 100644 --- a/pico-watch.cpp +++ b/pico-watch.cpp @@ -3,7 +3,6 @@ #include "hardware/i2c.h" #include "hardware/rtc.h" #include "pico/util/datetime.h" -#include "oled/ss_oled.h" #include "init.hpp" #include "api.hpp" -- cgit v1.2.3-54-g00ecf From 85f79065384ba77614ddb0212d22704253321587 Mon Sep 17 00:00:00 2001 From: ConfuSomu Date: Sat, 27 Feb 2021 18:06:46 -0500 Subject: Refactor API function names --- api.cpp | 6 +++--- api.hpp | 6 +++--- apps/home_menu.cpp | 8 ++++---- apps/main_clock.cpp | 10 +++++----- pico-watch.cpp | 2 +- 5 files changed, 16 insertions(+), 16 deletions(-) (limited to 'pico-watch.cpp') diff --git a/api.cpp b/api.cpp index 98b10f1..4be8457 100644 --- a/api.cpp +++ b/api.cpp @@ -23,14 +23,14 @@ void Api::init_display() { //oledSetTextWrap(&oled, true); } -int Api::dispWriteString(int iScrollX, int x, int y, char *szMsg, int iSize, int bInvert, int bRender) { +int Api::display_write_string(int iScrollX, int x, int y, char *szMsg, int iSize, int bInvert, int bRender) { return oledWriteString(&m_oled, iScrollX, x, y, szMsg, iSize, bInvert, bRender); } -void Api::dispFill(unsigned char ucData, int bRender) { +void Api::display_fill(unsigned char ucData, int bRender) { oledFill(&m_oled, ucData, bRender); } -bool Api::getDatetime(datetime_t *t) { +bool Api::datetime_get(datetime_t *t) { return rtc_get_datetime(t); } diff --git a/api.hpp b/api.hpp index 3863388..9616a8d 100644 --- a/api.hpp +++ b/api.hpp @@ -11,9 +11,9 @@ class Api { void init_display(); public: void init(); - int dispWriteString(int iScrollX, int x, int y, char *szMsg, int iSize, int bInvert, int bRender); - void dispFill(unsigned char ucData, int bRender); - bool getDatetime(datetime_t *t); + int display_write_string(int iScrollX, int x, int y, char *szMsg, int iSize, int bInvert, int bRender); + void display_fill(unsigned char ucData, int bRender); + bool datetime_get(datetime_t *t); }; #endif \ No newline at end of file diff --git a/apps/home_menu.cpp b/apps/home_menu.cpp index 97d4423..a3a0691 100644 --- a/apps/home_menu.cpp +++ b/apps/home_menu.cpp @@ -28,18 +28,18 @@ namespace app_home_menu { char datetime_buf[256]; char *datetime_str = &datetime_buf[0]; datetime_t t; - app_api->getDatetime(&t); + app_api->datetime_get(&t); // title with time title_str(datetime_str, sizeof(datetime_buf), &t); - app_api->dispWriteString(0,0,0, datetime_str, FONT_8x8, 0, 1); + app_api->display_write_string(0,0,0, datetime_str, FONT_8x8, 0, 1); } // Rendering of app int render(Api *app_api) { show_title(app_api); - app_api->dispWriteString(0,0,2, pressed_button, FONT_6x8, 0, 1); - app_api->dispWriteString(0,5,3, APPS_NAME[*selected_app], FONT_12x16, 0, 1); + app_api->display_write_string(0,0,2, pressed_button, FONT_6x8, 0, 1); + app_api->display_write_string(0,5,3, APPS_NAME[*selected_app], FONT_12x16, 0, 1); return 0; } diff --git a/apps/main_clock.cpp b/apps/main_clock.cpp index 298fed0..9933775 100644 --- a/apps/main_clock.cpp +++ b/apps/main_clock.cpp @@ -39,22 +39,22 @@ namespace app_main_clock { char datetime_buf[256]; char *datetime_str = &datetime_buf[0]; datetime_t t; - app_api->getDatetime(&t); + app_api->datetime_get(&t); // time time_as_str(datetime_str, sizeof(datetime_buf), &t); - app_api->dispWriteString(0,10,3, datetime_str, FONT_12x16, 0, 1); + app_api->display_write_string(0,10,3, datetime_str, FONT_12x16, 0, 1); // date date_as_str(datetime_str, sizeof(datetime_buf), &t); - app_api->dispWriteString(0,0,7, datetime_str, FONT_8x8, 0, 1); + app_api->display_write_string(0,0,7, datetime_str, FONT_8x8, 0, 1); } // Rendering of the app int render(Api *app_api) { - app_api->dispWriteString(0,15,0, (char *)"Test clock", FONT_8x8, 0, 1); + app_api->display_write_string(0,15,0, (char *)"Test clock", FONT_8x8, 0, 1); show_datetime(app_api); - //app_api->dispWriteString(0,0,0, &data[0], FONT_6x8, 0, 1); + //app_api->display_write_string(0,0,0, &data[0], FONT_6x8, 0, 1); return 0; } diff --git a/pico-watch.cpp b/pico-watch.cpp index 181d708..ebc3f12 100644 --- a/pico-watch.cpp +++ b/pico-watch.cpp @@ -24,7 +24,7 @@ int APPS_DESTROY_ON_EXIT[NUMBER_OF_APPS] = {0, 1}; int APPS_IS_INIT[NUMBER_OF_APPS] = {0, 0}; // Only run in background if init int app_init(int app_id) { - app_api.dispFill(0,1); // Clear OLED + app_api.display_fill(0,1); // Clear OLED if (!APPS_IS_INIT[app_id]) { APPS_IS_INIT[app_id] = 1; return (*APPS_FUNC_INIT[app_id])(&app_api); -- cgit v1.2.3-54-g00ecf From 03c6be69d3ba5ef7070cf857063d2bcfe12d9a3b Mon Sep 17 00:00:00 2001 From: ConfuSomu Date: Mon, 1 Mar 2021 15:13:17 -0500 Subject: Wait for button press then hide popup --- api.cpp | 19 +++++++++++++++++-- api.hpp | 10 +++++++++- apps/home_menu.cpp | 1 + buttons.cpp | 4 +++- pico-watch.cpp | 4 +++- 5 files changed, 33 insertions(+), 5 deletions(-) (limited to 'pico-watch.cpp') diff --git a/api.cpp b/api.cpp index e333272..16d79ad 100644 --- a/api.cpp +++ b/api.cpp @@ -54,6 +54,9 @@ int Api::display_write_pixel(int x, int y, unsigned char ucColor, int bRender) { bool Api::gui_popup_text(std::string title, std::string body){ #define CHARS_PER_LINE 13 + m_button_last_pressed = 0; + m_send_button_press_to_app = false; + oledRectangle(&m_oled, 9,7, 119,63, 0, 1); // Background oledRectangle(&m_oled, 9,7, 119,63, 1, 0); // Popup border oledRectangle(&m_oled, 9,7, 119,16, 1, 1); // Title background @@ -67,7 +70,7 @@ bool Api::gui_popup_text(std::string title, std::string body){ // Make body fit by adding '\n' at a regular interval int since_nl = 0; // Characters since newline - for(std::string::size_type i = 0; i < body.size(); ++i) { + for (std::string::size_type i = 0; i < body.size(); ++i) { if (body[i] == '\n') since_nl = 0; else if (since_nl++ == CHARS_PER_LINE) { @@ -78,9 +81,21 @@ bool Api::gui_popup_text(std::string title, std::string body){ // See https://stackoverflow.com/questions/1986966/does-s0-point-to-contiguous-characters-in-a-stdstring oledWriteString(&m_oled, 0, 15,1, &title[0], FONT_8x8, 1, 1); // Draw title oledWriteString(&m_oled, 0, 13,2, &body[0], FONT_8x8, 0, 1); // Draw body - while(1) {;} // TODO: change, this is temporary, only for this commit! + while (m_button_last_pressed != BUTTON_SELECT) + sleep_ms(50); + // Give back control to running app + oledFill(&m_oled, 0, 1); + m_send_button_press_to_app = true; } bool Api::datetime_get(datetime_t *t) { return rtc_get_datetime(t); } + +uint Api::button_last_get() { + return m_button_last_pressed; +} + +void Api::button_last_set(uint gpio) { + m_button_last_pressed = gpio; +} diff --git a/api.hpp b/api.hpp index 2738688..2fc3bba 100644 --- a/api.hpp +++ b/api.hpp @@ -5,13 +5,17 @@ #include "pico/util/datetime.h" #include "oled/ss_oled.h" +#include "buttons.hpp" + class Api { private: SSOLED m_oled; u_char m_init_done = 0; uint8_t m_ucBuffer[1024] = {0}; + uint m_button_last_pressed = 0; void init_display(); public: + bool m_send_button_press_to_app = true; void init(); int display_write_string(int iScrollX, int x, int y, char *szMsg, int iSize, int bInvert, int bRender); void display_fill(unsigned char ucData, int bRender); @@ -20,13 +24,17 @@ class Api { void display_draw_ellipse(int iCenterX, int iCenterY, int32_t iRadiusX, int32_t iRadiusY, uint8_t ucColor, uint8_t bFilled); void display_write_buffer(uint8_t *pBuffer); int display_write_pixel(int x, int y, unsigned char ucColor, int bRender); - // Display a popup over the current view. + // Display a popup over the current view and wait for select button to be pressed. // This is a blocking function and should be used only in the app's render method. // \param title Popup's title, length should not exceed 13 characters. // \param body String containing the popup's body. The zone has a size of 13×6 characters, so body should not be longer than 78 characters. Newline allows going to the next line and the text is automatically wrapped. // \note Strings longer than 13 and 78 respectively will be truncated. bool gui_popup_text(std::string title, std::string body); bool datetime_get(datetime_t *t); + // Get last button pressed, see buttons.hpp for values + uint button_last_get(); + // Set last button pressed, should only be called by button gpio interrupt. + void button_last_set(uint gpio); }; #endif \ No newline at end of file diff --git a/apps/home_menu.cpp b/apps/home_menu.cpp index 996b713..b2dddb7 100644 --- a/apps/home_menu.cpp +++ b/apps/home_menu.cpp @@ -40,6 +40,7 @@ namespace app_home_menu { show_title(app_api); app_api->display_write_string(0,0,2, pressed_button, FONT_6x8, 0, 1); app_api->display_write_string(0,5,3, APPS_NAME[*selected_app], FONT_12x16, 0, 1); + sleep_ms(500); app_api->gui_popup_text("Title is a string", "Body text, and new line. And this is a pretty long string.\nIterating is very fun!"); return 0; } diff --git a/buttons.cpp b/buttons.cpp index 8ba7b81..cd94504 100644 --- a/buttons.cpp +++ b/buttons.cpp @@ -2,10 +2,12 @@ #include "pico/stdlib.h" #include "buttons.hpp" +#include "api.hpp" // From pico-watch.c: extern int app_btnpressed(int app_id, uint gpio); extern void app_switch(int old_appid, int new_appid); extern int current_app; +extern Api app_api; // Debounce control // See https://www.raspberrypi.org/forums/viewtopic.php?f=145&t=301522#p1812063 @@ -21,7 +23,7 @@ void gpio_interrupt_cb(uint gpio, uint32_t events) { app_switch(current_app, 0); else app_btnpressed(current_app, gpio); - + app_api.button_last_set(gpio); button_time = to_ms_since_boot(get_absolute_time()); } } diff --git a/pico-watch.cpp b/pico-watch.cpp index ebc3f12..7c59d98 100644 --- a/pico-watch.cpp +++ b/pico-watch.cpp @@ -36,7 +36,9 @@ int app_render(int app_id) { } int app_btnpressed(int app_id, uint gpio) { - return (*APPS_FUNC_BTNPRESS[app_id])(&app_api, gpio); + if (app_api.m_send_button_press_to_app) + return (*APPS_FUNC_BTNPRESS[app_id])(&app_api, gpio); + return 2; } int app_destroy(int app_id) { -- cgit v1.2.3-54-g00ecf From 09f67190a934230853e1dddaf65a14c4e9a50999 Mon Sep 17 00:00:00 2001 From: ConfuSomu Date: Mon, 1 Mar 2021 22:31:20 -0500 Subject: Implement API to set/get render interval --- api.cpp | 10 ++++++++++ api.hpp | 8 ++++++++ pico-watch.cpp | 2 +- 3 files changed, 19 insertions(+), 1 deletion(-) (limited to 'pico-watch.cpp') diff --git a/api.cpp b/api.cpp index 1200f16..2d63506 100644 --- a/api.cpp +++ b/api.cpp @@ -134,6 +134,16 @@ bool Api::datetime_get(datetime_t *t) { return rtc_get_datetime(t); } +int Api::performance_render_interval_get() { + return m_app_render_interval; +} + +void Api::performance_render_interval_set(int interval) { + if (interval < 10) + interval = 10; + m_app_render_interval = interval; +} + uint Api::button_last_get() { return m_button_last_pressed; } diff --git a/api.hpp b/api.hpp index f5de57e..20f5c34 100644 --- a/api.hpp +++ b/api.hpp @@ -13,6 +13,7 @@ class Api { u_char m_init_done = 0; uint8_t m_ucBuffer[1024] = {0}; uint m_button_last_pressed = 0; + int m_app_render_interval = 500; void init_display(); public: bool m_send_button_press_to_app = true; @@ -55,7 +56,14 @@ class Api { // \param perf See Api::perf_modes enum for possible values bool performance_set(int perf); bool datetime_get(datetime_t *t); + // Get app's current render interval + // \return Value in millisec + int performance_render_interval_get(); + // Same spirit as performance_set, the app should use the smallest value possible that does not cause a lack of responsiveness in the app's UI. + // \param interval Time to wait in millisec between calls of the app's render function. + void performance_render_interval_set(int interval); // Get last button pressed, see buttons.hpp for values + // \return Last button pressed uint button_last_get(); // Set last button pressed, should only be called by button gpio interrupt. void button_last_set(uint gpio); diff --git a/pico-watch.cpp b/pico-watch.cpp index 7c59d98..adfe2fd 100644 --- a/pico-watch.cpp +++ b/pico-watch.cpp @@ -80,7 +80,7 @@ int main() { while (1) { app_render(current_app); - sleep_ms(500); + sleep_ms(app_api.performance_render_interval_get()); } return 0; } -- cgit v1.2.3-54-g00ecf From b9971cb3b7779d31889379c56afeddc24ff50d04 Mon Sep 17 00:00:00 2001 From: ConfuSomu Date: Mon, 1 Mar 2021 23:01:44 -0500 Subject: Fix race condition when refresh interval is small Possible fix --- pico-watch.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'pico-watch.cpp') diff --git a/pico-watch.cpp b/pico-watch.cpp index adfe2fd..9df6799 100644 --- a/pico-watch.cpp +++ b/pico-watch.cpp @@ -11,6 +11,7 @@ #include "apps/home_menu.hpp" int current_app = 0; +bool app_ready = true; Api app_api; #define NUMBER_OF_APPS 2 @@ -25,6 +26,7 @@ int APPS_IS_INIT[NUMBER_OF_APPS] = {0, 0}; // Only run in background if init int app_init(int app_id) { app_api.display_fill(0,1); // Clear OLED + app_api.performance_render_interval_set(500); // Reset interval if (!APPS_IS_INIT[app_id]) { APPS_IS_INIT[app_id] = 1; return (*APPS_FUNC_INIT[app_id])(&app_api); @@ -42,7 +44,6 @@ int app_btnpressed(int app_id, uint gpio) { } int app_destroy(int app_id) { - // TODO: reset APPS_DATA for the app if (APPS_IS_INIT[app_id]) { APPS_IS_INIT[app_id] = 0; return (*APPS_FUNC_DESTROY[app_id])(&app_api); @@ -62,11 +63,13 @@ bool apps_bgrefresh(struct repeating_timer *t) { // TODO: Refresh done on core1 } void app_switch(int old_appid, int new_appid) { + app_ready = false; if (APPS_DESTROY_ON_EXIT[old_appid]) { app_destroy(old_appid); } app_init(new_appid); current_app = new_appid; + app_ready = true; } int main() { @@ -79,7 +82,8 @@ int main() { app_init(current_app); while (1) { - app_render(current_app); + if (app_ready) + app_render(current_app); // FIXME: This may cause race conditions when switching app sleep_ms(app_api.performance_render_interval_get()); } return 0; -- cgit v1.2.3-54-g00ecf