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. --- CMakeLists.txt | 2 ++ api.cpp | 28 ++++++++++++++++++++++++++++ api.hpp | 18 ++++++++++++++++++ apps/home_menu.cpp | 23 ++++++++++++----------- apps/home_menu.hpp | 12 +++++++----- apps/main_clock.cpp | 23 ++++++++++++----------- apps/main_clock.hpp | 12 +++++++----- init.cpp | 10 ---------- init.hpp | 3 --- pico-watch.cpp | 25 ++++++++++++++----------- 10 files changed, 100 insertions(+), 56 deletions(-) create mode 100644 api.cpp create mode 100644 api.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 758ac2d..5ba1f5e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,6 +33,8 @@ add_executable(pico-watch init.hpp buttons.cpp buttons.hpp + api.cpp + api.hpp apps/home_menu.cpp apps/home_menu.hpp apps/main_clock.cpp diff --git a/api.cpp b/api.cpp new file mode 100644 index 0000000..60c0bcd --- /dev/null +++ b/api.cpp @@ -0,0 +1,28 @@ +#include +#include "pico/stdlib.h" +#include "oled/ss_oled.h" + +#include "api.hpp" +#include "init.hpp" + +void Api::init() { + if (!m_init_done) { + init_display(); + m_init_done = 1; + } +} + +void Api::init_display() { + oledInit(&m_oled, OLED_128x64, 0x3d, 0, 0, 1, SDA_PIN, SCL_PIN, RESET_PIN, 1000000L); + oledFill(&m_oled, 0,1); + oledSetContrast(&m_oled, OLED_DEFAULT_CONTRAST); + //oledSetTextWrap(&oled, true); +} + +int Api::dispWriteString(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) { + oledFill(&m_oled, ucData, bRender); +} diff --git a/api.hpp b/api.hpp new file mode 100644 index 0000000..0e4992f --- /dev/null +++ b/api.hpp @@ -0,0 +1,18 @@ +#ifndef __API_H__ +#define __API_H__ + +#include "pico/util/datetime.h" +#include "oled/ss_oled.h" + +class Api { + private: + SSOLED m_oled; + u_char m_init_done = 0; + 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); +}; + +#endif \ No newline at end of file diff --git a/apps/home_menu.cpp b/apps/home_menu.cpp index 7f53447..19458e6 100644 --- a/apps/home_menu.cpp +++ b/apps/home_menu.cpp @@ -7,6 +7,7 @@ extern "C" { #include "../oled/ss_oled.h" #include "home_menu.hpp" +#include "../api.hpp" #include "../buttons.hpp" extern void app_switch(int old_appid, int new_appid); @@ -15,7 +16,7 @@ extern bool rtc_get_datetime(datetime_t *t); #define NUMBER_OF_APPS 2 namespace app_home_menu { - const char* APPS_NAME[NUMBER_OF_APPS][12] = {"Home", "Clock"}; + char *APPS_NAME[NUMBER_OF_APPS] = {"Home", "Clock"}; char *pressed_button; int *selected_app; @@ -28,7 +29,7 @@ namespace app_home_menu { t->sec); }; - void show_title(SSOLED *oled) { + void show_title(Api *app_api) { char datetime_buf[256]; char *datetime_str = &datetime_buf[0]; datetime_t t; @@ -36,20 +37,20 @@ namespace app_home_menu { // title with time title_str(datetime_str, sizeof(datetime_buf), &t); - oledWriteString(oled, 0,0,0, datetime_str, FONT_8x8, 0, 1); + app_api->dispWriteString(0,0,0, datetime_str, FONT_8x8, 0, 1); } // Rendering of app - int render(SSOLED *oled) { - show_title(oled); - oledWriteString(oled, 0,0,2, pressed_button, FONT_6x8, 0, 1); - oledWriteString(oled, 0,5,3, const_cast(APPS_NAME[0][*selected_app]), FONT_12x16, 0, 1); + 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); return 0; } // Example of how button inputs could be interpreted. // Drawing on screen should be done in the render function. - int btnpressed(SSOLED *oled, uint gpio) { + int btnpressed(Api *app_api, uint gpio) { switch (gpio) { case BUTTON_HOME: *pressed_button = 'H'; break; @@ -79,19 +80,19 @@ namespace app_home_menu { } // Initlisation of the app. - int init(SSOLED *oled) { + int init(Api *app_api) { pressed_button = new char; *pressed_button = '*'; selected_app = new int; return 0; // return 1 when function not implemented } // Processor intensive operations and functions related to drawing to the screen should only be done when the app is in_foreground(=1). This function is only called when the app is init. - int bgrefresh(SSOLED *oled, char in_foreground) { + int bgrefresh(Api *app_api, char in_foreground) { return 1; } // Destruction of app, deinitlisation should be done here. This is only called if the app's APPS_DESTROY_ON_EXIT is set to 1. When it is not a "service" app. - int destroy(SSOLED *oled) { + int destroy(Api *app_api) { delete pressed_button; pressed_button = nullptr; delete selected_app; selected_app = nullptr; return 1; diff --git a/apps/home_menu.hpp b/apps/home_menu.hpp index ebcc451..033dc6b 100644 --- a/apps/home_menu.hpp +++ b/apps/home_menu.hpp @@ -4,12 +4,14 @@ #include "pico/util/datetime.h" #include "../oled/ss_oled.h" +#include "../api.hpp" + namespace app_home_menu { - int init(SSOLED *oled); - int render(SSOLED *oled); - int btnpressed(SSOLED *oled, uint gpio); - int bgrefresh(SSOLED *oled, char in_foreground); - int destroy(SSOLED *oled); + int init(Api *app_api); + int render(Api *app_api); + int btnpressed(Api *app_api, uint gpio); + int bgrefresh(Api *app_api, char in_foreground); + int destroy(Api *app_api); } #endif diff --git a/apps/main_clock.cpp b/apps/main_clock.cpp index f9be3e5..db8e39e 100644 --- a/apps/main_clock.cpp +++ b/apps/main_clock.cpp @@ -7,6 +7,7 @@ extern "C" { #include "../oled/ss_oled.h" #include "main_clock.hpp" +#include "../api.hpp" #include "../buttons.hpp" namespace app_main_clock { @@ -39,7 +40,7 @@ namespace app_main_clock { DATETIME_DOWS[t->dotw - 1]); }; - void show_datetime(SSOLED *oled) { + void show_datetime(Api *app_api) { char datetime_buf[256]; char *datetime_str = &datetime_buf[0]; datetime_t t; @@ -47,38 +48,38 @@ namespace app_main_clock { // time time_as_str(datetime_str, sizeof(datetime_buf), &t); - oledWriteString(oled, 0,10,3, datetime_str, FONT_12x16, 0, 1); + app_api->dispWriteString(0,10,3, datetime_str, FONT_12x16, 0, 1); // date date_as_str(datetime_str, sizeof(datetime_buf), &t); - oledWriteString(oled, 0,0,7, datetime_str, FONT_8x8, 0, 1); + app_api->dispWriteString(0,0,7, datetime_str, FONT_8x8, 0, 1); } // Rendering of the app - int render(SSOLED *oled) { - oledWriteString(oled, 0,15,0, (char *)"Test clock", FONT_8x8, 0, 1); - show_datetime(oled); - //oledWriteString(oled, 0,0,0, &data[0], FONT_6x8, 0, 1); + int render(Api *app_api) { + app_api->dispWriteString(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); return 0; } // Interpretation of button inputs - int btnpressed(SSOLED *oled, uint gpio) { + int btnpressed(Api *app_api, uint gpio) { return 0; } // Initlisation of the app. - int init(SSOLED *oled) { + int init(Api *app_api) { return 1; // return 1 when function not implemented } // Processor intensive operations and functions related to drawing to the screen should only be done when the app is in_foreground(=1). This function is only called when the app is init. - int bgrefresh(SSOLED *oled, char in_foreground) { + int bgrefresh(Api *app_api, char in_foreground) { return 1; } // Destruction of app, deinitlisation should be done here. This is only called if the app's APPS_DESTROY_ON_EXIT is set to 1. When it is not a "service" app. - int destroy(SSOLED *oled) { + int destroy(Api *app_api) { return 1; } } \ No newline at end of file diff --git a/apps/main_clock.hpp b/apps/main_clock.hpp index 887dfa1..5440c1f 100644 --- a/apps/main_clock.hpp +++ b/apps/main_clock.hpp @@ -4,12 +4,14 @@ #include "pico/util/datetime.h" #include "../oled/ss_oled.h" +#include "../api.hpp" + namespace app_main_clock { - int init(SSOLED *oled); - int render(SSOLED *oled); - int btnpressed(SSOLED *oled, uint gpio); - int bgrefresh(SSOLED *oled, char in_foreground); - int destroy(SSOLED *oled); + int init(Api *app_api); + int render(Api *app_api); + int btnpressed(Api *app_api, uint gpio); + int bgrefresh(Api *app_api, char in_foreground); + int destroy(Api *app_api); } #endif \ No newline at end of file diff --git a/init.cpp b/init.cpp index 11d38a6..eb33533 100644 --- a/init.cpp +++ b/init.cpp @@ -4,17 +4,8 @@ extern "C" { #include "hardware/rtc.h" } -#include "oled/ss_oled.h" #include "init.hpp" -SSOLED oled; - -void init_display() { - oledInit(&oled, OLED_128x64, 0x3d, 0, 0, 1, SDA_PIN, SCL_PIN, RESET_PIN, 1000000L); - oledFill(&oled, 0,1); - oledSetContrast(&oled, OLED_DEFAULT_CONTRAST); - //oledSetTextWrap(&oled, true); -} void init_rtc() { datetime_t init_date = { @@ -31,6 +22,5 @@ void init_rtc() { void init_all() { stdio_init_all(); - init_display(); init_rtc(); } diff --git a/init.hpp b/init.hpp index f63187f..fc2a53f 100644 --- a/init.hpp +++ b/init.hpp @@ -7,7 +7,6 @@ #define SCL_PIN 7 #define RESET_PIN -1 #define OLED_DEFAULT_CONTRAST 40 -extern SSOLED oled; // Initial date & time // The idea is to have the compiler set the date at compile-time. @@ -23,8 +22,6 @@ extern SSOLED oled; // Init every componement void init_all(); -// Init OLED display -void init_display(); // Init onboard RTC void init_rtc(); 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 9ccb4e036c2de27126624498e2441faea3004d1f Mon Sep 17 00:00:00 2001 From: ConfuSomu Date: Sat, 27 Feb 2021 15:05:12 -0500 Subject: Init selected_app in home_menu to a default value When the value references an out of bounds memory address, line 47 in same file may cause hardfault/segmentation fault. --- apps/home_menu.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/home_menu.cpp b/apps/home_menu.cpp index 19458e6..c261611 100644 --- a/apps/home_menu.cpp +++ b/apps/home_menu.cpp @@ -81,8 +81,8 @@ namespace app_home_menu { // Initlisation of the app. int init(Api *app_api) { - pressed_button = new char; *pressed_button = '*'; - selected_app = new int; + pressed_button = new char; *pressed_button = '*'; + selected_app = new int; *selected_app = 0; // Make sure to init the values to something known! return 0; // return 1 when function not implemented } -- 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(-) 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 3dae53c57661168f3638489ca66b68d8076d65b4 Mon Sep 17 00:00:00 2001 From: ConfuSomu Date: Sat, 27 Feb 2021 17:59:45 -0500 Subject: Implement getDatetime function in API --- api.cpp | 8 ++++++++ api.hpp | 2 ++ apps/home_menu.cpp | 6 +----- apps/main_clock.cpp | 6 +----- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/api.cpp b/api.cpp index 60c0bcd..98b10f1 100644 --- a/api.cpp +++ b/api.cpp @@ -1,5 +1,9 @@ #include #include "pico/stdlib.h" +extern "C" { +#include "hardware/rtc.h" +} +#include "pico/util/datetime.h" #include "oled/ss_oled.h" #include "api.hpp" @@ -26,3 +30,7 @@ int Api::dispWriteString(int iScrollX, int x, int y, char *szMsg, int iSize, int void Api::dispFill(unsigned char ucData, int bRender) { oledFill(&m_oled, ucData, bRender); } + +bool Api::getDatetime(datetime_t *t) { + return rtc_get_datetime(t); +} diff --git a/api.hpp b/api.hpp index 735d55b..3863388 100644 --- a/api.hpp +++ b/api.hpp @@ -1,6 +1,7 @@ #ifndef __API_H__ #define __API_H__ +#include "pico/util/datetime.h" #include "oled/ss_oled.h" class Api { @@ -12,6 +13,7 @@ class Api { 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); }; #endif \ No newline at end of file diff --git a/apps/home_menu.cpp b/apps/home_menu.cpp index 8f0ee8b..97d4423 100644 --- a/apps/home_menu.cpp +++ b/apps/home_menu.cpp @@ -1,9 +1,5 @@ #include #include "pico/stdlib.h" -extern "C" { -#include "hardware/rtc.h" -} -#include "pico/util/datetime.h" #include "home_menu.hpp" #include "../api.hpp" @@ -32,7 +28,7 @@ namespace app_home_menu { char datetime_buf[256]; char *datetime_str = &datetime_buf[0]; datetime_t t; - rtc_get_datetime(&t); + app_api->getDatetime(&t); // title with time title_str(datetime_str, sizeof(datetime_buf), &t); diff --git a/apps/main_clock.cpp b/apps/main_clock.cpp index 9939e4e..298fed0 100644 --- a/apps/main_clock.cpp +++ b/apps/main_clock.cpp @@ -1,9 +1,5 @@ #include #include "pico/stdlib.h" -extern "C" { -#include "hardware/rtc.h" -} -#include "pico/util/datetime.h" #include "main_clock.hpp" #include "../api.hpp" @@ -43,7 +39,7 @@ namespace app_main_clock { char datetime_buf[256]; char *datetime_str = &datetime_buf[0]; datetime_t t; - rtc_get_datetime(&t); + app_api->getDatetime(&t); // time time_as_str(datetime_str, sizeof(datetime_buf), &t); -- 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(-) 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 416168bf2b95366a04fed67177294aa2d90a8263 Mon Sep 17 00:00:00 2001 From: ConfuSomu Date: Sun, 28 Feb 2021 13:03:46 -0500 Subject: Expose features from the OLED library in the API --- api.cpp | 23 +++++++++++++++++++++++ api.hpp | 6 ++++++ 2 files changed, 29 insertions(+) diff --git a/api.cpp b/api.cpp index 4be8457..4307ae1 100644 --- a/api.cpp +++ b/api.cpp @@ -20,6 +20,7 @@ void Api::init_display() { oledInit(&m_oled, OLED_128x64, 0x3d, 0, 0, 1, SDA_PIN, SCL_PIN, RESET_PIN, 1000000L); oledFill(&m_oled, 0,1); oledSetContrast(&m_oled, OLED_DEFAULT_CONTRAST); + oledSetBackBuffer(&m_oled, m_ucBuffer); // Seems to be required to draw lines, rectangles… //oledSetTextWrap(&oled, true); } @@ -31,6 +32,28 @@ void Api::display_fill(unsigned char ucData, int bRender) { oledFill(&m_oled, ucData, bRender); } +void Api::display_draw_line(int x1, int y1, int x2, int y2, int bRender) { + oledDrawLine(&m_oled, x1, y1, x2, y2, bRender); +} + +void Api::display_draw_rectange(int x1, int y1, int x2, int y2, uint8_t ucColor, uint8_t bFilled) { + oledRectangle(&m_oled, x1, y1, x2, y2, ucColor, bFilled); + oledDumpBuffer(&m_oled, m_ucBuffer); // Write the back buffer, after experimentation, seems to be required when drawing this shape +} + +void Api::display_draw_ellipse(int iCenterX, int iCenterY, int32_t iRadiusX, int32_t iRadiusY, uint8_t ucColor, uint8_t bFilled) { + oledEllipse(&m_oled, iCenterX, iCenterY, iRadiusX, iRadiusY, ucColor, bFilled); + oledDumpBuffer(&m_oled, m_ucBuffer); +} + +void Api::display_write_buffer(uint8_t *pBuffer) { + oledDumpBuffer(&m_oled, pBuffer); +} + +int Api::display_write_pixel(int x, int y, unsigned char ucColor, int bRender) { + return oledSetPixel(&m_oled, x, y, ucColor, bRender); +} + bool Api::datetime_get(datetime_t *t) { return rtc_get_datetime(t); } diff --git a/api.hpp b/api.hpp index 9616a8d..2b29b95 100644 --- a/api.hpp +++ b/api.hpp @@ -8,11 +8,17 @@ class Api { private: SSOLED m_oled; u_char m_init_done = 0; + uint8_t m_ucBuffer[1024] = {0}; void init_display(); public: 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); + void display_draw_line(int x1, int y1, int x2, int y2, int bRender); + void display_draw_rectange(int x1, int y1, int x2, int y2, uint8_t ucColor, uint8_t bFilled); + 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); bool datetime_get(datetime_t *t); }; -- cgit v1.2.3-54-g00ecf From 2e0650864058f3fb312d2527398fda8d7e97a60d Mon Sep 17 00:00:00 2001 From: ConfuSomu Date: Mon, 1 Mar 2021 14:47:56 -0500 Subject: Implement method in API for drawing a popup I had to modify the OLED library to correctly work with newlines. --- .vscode/settings.json | 14 +++++++++++++- api.cpp | 31 +++++++++++++++++++++++++++++-- api.hpp | 7 +++++++ apps/home_menu.cpp | 1 + oled/ss_oled.c | 29 ++++++++++++++++------------- 5 files changed, 66 insertions(+), 16 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 4cbb933..e63b566 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -19,5 +19,17 @@ "cmake.buildBeforeRun": true, // This may need to be arm-none-eabi-gdb depending on your system "cortex-debug.gdbPath": "gdb-multiarch", - "C_Cpp.errorSquiggles": "Disabled" + "C_Cpp.errorSquiggles": "Disabled", + "files.associations": { + "cctype": "cpp", + "cmath": "cpp", + "cstdarg": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "type_traits": "cpp", + "limits": "cpp", + "*.tcc": "cpp", + "typeinfo": "cpp" + } } \ No newline at end of file diff --git a/api.cpp b/api.cpp index 4307ae1..e333272 100644 --- a/api.cpp +++ b/api.cpp @@ -3,8 +3,6 @@ extern "C" { #include "hardware/rtc.h" } -#include "pico/util/datetime.h" -#include "oled/ss_oled.h" #include "api.hpp" #include "init.hpp" @@ -54,6 +52,35 @@ int Api::display_write_pixel(int x, int y, unsigned char ucColor, int bRender) { return oledSetPixel(&m_oled, x, y, ucColor, bRender); } +bool Api::gui_popup_text(std::string title, std::string body){ +#define CHARS_PER_LINE 13 + 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 + oledDumpBuffer(&m_oled, m_ucBuffer); // Display rectangle + + // Truncate longer strings to avoid wasting time in for loop and drawing on OLED + if (title.size() > 13) + title.resize(13); + if (body.size() > 78) + body.resize(78); + + // 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) { + if (body[i] == '\n') + since_nl = 0; + else if (since_nl++ == CHARS_PER_LINE) { + body.insert(i, 1, '\n'); + since_nl = 0; // Probably unneeded + } + } + // 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! +} + bool Api::datetime_get(datetime_t *t) { return rtc_get_datetime(t); } diff --git a/api.hpp b/api.hpp index 2b29b95..2738688 100644 --- a/api.hpp +++ b/api.hpp @@ -1,6 +1,7 @@ #ifndef __API_H__ #define __API_H__ +#include #include "pico/util/datetime.h" #include "oled/ss_oled.h" @@ -19,6 +20,12 @@ 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. + // 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); }; diff --git a/apps/home_menu.cpp b/apps/home_menu.cpp index a3a0691..996b713 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); + 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/oled/ss_oled.c b/oled/ss_oled.c index 3cd9132..3375bde 100644 --- a/oled/ss_oled.c +++ b/oled/ss_oled.c @@ -1486,20 +1486,23 @@ unsigned char c, *s, ucTemp[40]; if (iScroll < 8) // only display visible characters { c = (unsigned char)szMsg[i]; - iFontOff = (int)(c-32) * 7; - // we can't directly use the pointer to FLASH memory, so copy to a local buffer - ucTemp[0] = 0; - memcpy(&ucTemp[1], &ucFont[iFontOff], 7); - if (bInvert) InvertBytes(ucTemp, 8); - // oledCachedWrite(ucTemp, 8); - iLen = 8 - iFontSkip; - if (pOLED->iCursorX + iLen > pOLED->oled_x) // clip right edge - iLen = pOLED->oled_x - pOLED->iCursorX; - oledWriteDataBlock(pOLED, &ucTemp[iFontSkip], iLen, bRender); // write character pattern - pOLED->iCursorX += iLen; - if (pOLED->iCursorX >= pOLED->oled_x-7 && pOLED->oled_wrap) // word wrap enabled? + if (c != '\n') { // Do not draw the new line char + iFontOff = (int)(c-32) * 7; + // we can't directly use the pointer to FLASH memory, so copy to a local buffer + ucTemp[0] = 0; + memcpy(&ucTemp[1], &ucFont[iFontOff], 7); + if (bInvert) InvertBytes(ucTemp, 8); + // oledCachedWrite(ucTemp, 8); + iLen = 8 - iFontSkip; + if (pOLED->iCursorX + iLen > pOLED->oled_x) // clip right edge + iLen = pOLED->oled_x - pOLED->iCursorX; + oledWriteDataBlock(pOLED, &ucTemp[iFontSkip], iLen, bRender); // write character pattern + pOLED->iCursorX += iLen; + } + + if ((pOLED->iCursorX >= pOLED->oled_x-7 && pOLED->oled_wrap) || c == '\n') // word wrap enabled? or new line char { - pOLED->iCursorX = 0; // start at the beginning of the next line + pOLED->iCursorX = x; // start at the beginning of the next line pOLED->iCursorY++; oledSetPosition(pOLED, pOLED->iCursorX, pOLED->iCursorY, bRender); } -- 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(-) 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 3ac52774118e34c4de48462d961e2a3e5ec700b9 Mon Sep 17 00:00:00 2001 From: ConfuSomu Date: Mon, 1 Mar 2021 15:14:55 -0500 Subject: Remove popup tests from home_menu --- apps/home_menu.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/home_menu.cpp b/apps/home_menu.cpp index b2dddb7..a3a0691 100644 --- a/apps/home_menu.cpp +++ b/apps/home_menu.cpp @@ -40,8 +40,6 @@ 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; } -- cgit v1.2.3-54-g00ecf From db15966f0e0e72b51b359d3c36399f14befe40a9 Mon Sep 17 00:00:00 2001 From: ConfuSomu Date: Mon, 1 Mar 2021 20:06:41 -0500 Subject: Implement API method for displaying footer text --- api.cpp | 21 ++++++++++++++++++++- api.hpp | 7 +++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/api.cpp b/api.cpp index 16d79ad..2617483 100644 --- a/api.cpp +++ b/api.cpp @@ -59,7 +59,7 @@ bool Api::gui_popup_text(std::string title, std::string body){ 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 + oledRectangle(&m_oled, 9,7, 119,16, 1, 1); // Title background, FIXME pixel bleeding oledDumpBuffer(&m_oled, m_ucBuffer); // Display rectangle // Truncate longer strings to avoid wasting time in for loop and drawing on OLED @@ -88,6 +88,25 @@ bool Api::gui_popup_text(std::string title, std::string body){ m_send_button_press_to_app = true; } +bool Api::gui_footer_text(std::string text, int offset_x, int offset_row, int invert) { + // Max chars per line for FONT_8x8 is 16 chars + // Max chars per line for FONT_6x8 is 21 chars + // Truncate longer text + if (text.size() > 21) + text.resize(21); + + // Choose most adapted font size + int font; + if (text.size() > 16) + font = FONT_6x8; + else + font = FONT_8x8; + + oledRectangle(&m_oled, 0,56, 128,64, invert, 1); + oledDumpBuffer(&m_oled, m_ucBuffer); + oledWriteString(&m_oled, 0,offset_x,7-offset_row, &text[0], font, invert, 1); +} + bool Api::datetime_get(datetime_t *t) { return rtc_get_datetime(t); } diff --git a/api.hpp b/api.hpp index 2fc3bba..9dabacf 100644 --- a/api.hpp +++ b/api.hpp @@ -30,6 +30,13 @@ class Api { // \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); + // Display text at the bottom of the screen. + // The font size is automatically choosen based on the text lenght. + // \param text Text to display. Text longer than 21 will be truncated. + // \param offset_x Set a horizental offset, to allow, for example, centering the text + // \param offset_row Allow rendering the text higher. For example, one line higher when `offset_row = 1`. + // \param invert allow inverting text and background color. + bool gui_footer_text(std::string text, int offset_x = 0, int offset_row = 0, int invert = 0); bool datetime_get(datetime_t *t); // Get last button pressed, see buttons.hpp for values uint button_last_get(); -- cgit v1.2.3-54-g00ecf From 7c452f2abe26f7dbcd2646f0be944f03d96f0b44 Mon Sep 17 00:00:00 2001 From: ConfuSomu Date: Mon, 1 Mar 2021 20:28:36 -0500 Subject: Implement API method for display header text --- api.cpp | 19 +++++++++++++++++++ api.hpp | 7 +++++++ 2 files changed, 26 insertions(+) diff --git a/api.cpp b/api.cpp index 2617483..7279f19 100644 --- a/api.cpp +++ b/api.cpp @@ -107,6 +107,25 @@ bool Api::gui_footer_text(std::string text, int offset_x, int offset_row, int in oledWriteString(&m_oled, 0,offset_x,7-offset_row, &text[0], font, invert, 1); } +bool Api::gui_header_text(std::string text, int offset_x, int offset_row, int invert) { + // Max chars per line for FONT_8x8 is 16 chars + // Max chars per line for FONT_6x8 is 21 chars + // Truncate longer text + if (text.size() > 21) + text.resize(21); + + // Choose most adapted font size + int font; + if (text.size() > 16) + font = FONT_6x8; + else + font = FONT_8x8; + + oledRectangle(&m_oled, 0,0, 128,8, invert, 1); + oledDumpBuffer(&m_oled, m_ucBuffer); + oledWriteString(&m_oled, 0,offset_x,0+offset_row, &text[0], font, invert, 1); +} + bool Api::datetime_get(datetime_t *t) { return rtc_get_datetime(t); } diff --git a/api.hpp b/api.hpp index 9dabacf..7e2a3e9 100644 --- a/api.hpp +++ b/api.hpp @@ -37,6 +37,13 @@ class Api { // \param offset_row Allow rendering the text higher. For example, one line higher when `offset_row = 1`. // \param invert allow inverting text and background color. bool gui_footer_text(std::string text, int offset_x = 0, int offset_row = 0, int invert = 0); + // Display text at the top of the screen. + // The font size is automatically choosen based on the text lenght. + // \param text Text to display. Text longer than 21 will be truncated. + // \param offset_x Set a horizental offset, to allow, for example, centering the text + // \param offset_row Render text lines lower. For example, one text line lower with `offset_row = 1`. + // \param invert Invert text and background color. + bool gui_header_text(std::string text, int offset_x = 0, int offset_row = 0, int invert = 0); bool datetime_get(datetime_t *t); // Get last button pressed, see buttons.hpp for values uint button_last_get(); -- cgit v1.2.3-54-g00ecf From c0cec91c3fe6bc991c53c028a21711f21efb3704 Mon Sep 17 00:00:00 2001 From: ConfuSomu Date: Mon, 1 Mar 2021 20:35:59 -0500 Subject: Use header and footer methods in apps --- apps/home_menu.cpp | 2 +- apps/main_clock.cpp | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/apps/home_menu.cpp b/apps/home_menu.cpp index a3a0691..658d911 100644 --- a/apps/home_menu.cpp +++ b/apps/home_menu.cpp @@ -32,7 +32,7 @@ namespace app_home_menu { // title with time title_str(datetime_str, sizeof(datetime_buf), &t); - app_api->display_write_string(0,0,0, datetime_str, FONT_8x8, 0, 1); + app_api->gui_header_text((std::string)datetime_str); } // Rendering of app diff --git a/apps/main_clock.cpp b/apps/main_clock.cpp index 9933775..21c9e5d 100644 --- a/apps/main_clock.cpp +++ b/apps/main_clock.cpp @@ -47,14 +47,13 @@ namespace app_main_clock { // date date_as_str(datetime_str, sizeof(datetime_buf), &t); - app_api->display_write_string(0,0,7, datetime_str, FONT_8x8, 0, 1); + app_api->gui_footer_text((std::string)datetime_str); } // Rendering of the app int render(Api *app_api) { - app_api->display_write_string(0,15,0, (char *)"Test clock", FONT_8x8, 0, 1); + app_api->gui_header_text("Test clock", 17); show_datetime(app_api); - //app_api->display_write_string(0,0,0, &data[0], FONT_6x8, 0, 1); return 0; } -- cgit v1.2.3-54-g00ecf From e63fcd31861aecfd0760d77b8ab0eea0c528ea8c Mon Sep 17 00:00:00 2001 From: ConfuSomu Date: Mon, 1 Mar 2021 20:57:47 -0500 Subject: Implement API method for setting performance This is to lower power consumption by having the apps set the lowest performance they need. --- api.cpp | 4 ++++ api.hpp | 10 ++++++++++ apps/home_menu.cpp | 1 + apps/main_clock.cpp | 1 + 4 files changed, 16 insertions(+) diff --git a/api.cpp b/api.cpp index 7279f19..1200f16 100644 --- a/api.cpp +++ b/api.cpp @@ -126,6 +126,10 @@ bool Api::gui_header_text(std::string text, int offset_x, int offset_row, int in oledWriteString(&m_oled, 0,offset_x,0+offset_row, &text[0], font, invert, 1); } +bool Api::performance_set(int perf) { + return false; +} + bool Api::datetime_get(datetime_t *t) { return rtc_get_datetime(t); } diff --git a/api.hpp b/api.hpp index 7e2a3e9..f5de57e 100644 --- a/api.hpp +++ b/api.hpp @@ -16,6 +16,11 @@ class Api { void init_display(); public: bool m_send_button_press_to_app = true; + enum perf_modes { + LOW_POWER, + NORMAL_PERF, + HIGH_PERF + }; 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); @@ -44,6 +49,11 @@ class Api { // \param offset_row Render text lines lower. For example, one text line lower with `offset_row = 1`. // \param invert Invert text and background color. bool gui_header_text(std::string text, int offset_x = 0, int offset_row = 0, int invert = 0); + // Set performance mode. + // FIXME: function currently does nothing! + // An app should choose the lowest performance that can make it function. Set in init(). Only when required, higher performance should be used. + // \param perf See Api::perf_modes enum for possible values + bool performance_set(int perf); bool datetime_get(datetime_t *t); // Get last button pressed, see buttons.hpp for values uint button_last_get(); diff --git a/apps/home_menu.cpp b/apps/home_menu.cpp index 658d911..910ebac 100644 --- a/apps/home_menu.cpp +++ b/apps/home_menu.cpp @@ -76,6 +76,7 @@ namespace app_home_menu { // Initlisation of the app. int init(Api *app_api) { + app_api->performance_set(Api::perf_modes::LOW_POWER); pressed_button = new char; *pressed_button = '*'; selected_app = new int; *selected_app = 0; // Make sure to init the values to something known! return 0; // return 1 when function not implemented diff --git a/apps/main_clock.cpp b/apps/main_clock.cpp index 21c9e5d..895a7b9 100644 --- a/apps/main_clock.cpp +++ b/apps/main_clock.cpp @@ -64,6 +64,7 @@ namespace app_main_clock { // Initlisation of the app. int init(Api *app_api) { + app_api->performance_set(Api::perf_modes::LOW_POWER); return 1; // return 1 when function not implemented } -- 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(-) 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(-) 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