aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorConfuSomu2021-03-01 23:07:55 -0500
committerConfuSomu2021-03-01 23:07:55 -0500
commit888675141118cb1cabb774cbe0a598ab9b5ca19d (patch)
tree6bd342169c4cd84365981ae071369f931f4f62f1
parent06d5f492eca8e57bc8ce7479ec6149fbc6a15d08 (diff)
parentb9971cb3b7779d31889379c56afeddc24ff50d04 (diff)
downloadpico-watch-888675141118cb1cabb774cbe0a598ab9b5ca19d.tar
pico-watch-888675141118cb1cabb774cbe0a598ab9b5ca19d.tar.gz
pico-watch-888675141118cb1cabb774cbe0a598ab9b5ca19d.zip
Merge branch 'api-class'
-rw-r--r--.vscode/settings.json14
-rw-r--r--CMakeLists.txt2
-rw-r--r--api.cpp153
-rw-r--r--api.hpp72
-rw-r--r--apps/home_menu.cpp35
-rw-r--r--apps/home_menu.hpp12
-rw-r--r--apps/main_clock.cpp30
-rw-r--r--apps/main_clock.hpp12
-rw-r--r--buttons.cpp4
-rw-r--r--init.cpp10
-rw-r--r--init.hpp3
-rw-r--r--oled/ss_oled.c29
-rw-r--r--pico-watch.cpp38
13 files changed, 325 insertions, 89 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/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..2d63506
--- /dev/null
+++ b/api.cpp
@@ -0,0 +1,153 @@
+#include <stdio.h>
+#include "pico/stdlib.h"
+extern "C" {
+#include "hardware/rtc.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);
+ oledSetBackBuffer(&m_oled, m_ucBuffer); // Seems to be required to draw lines, rectangles…
+ //oledSetTextWrap(&oled, true);
+}
+
+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::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::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, FIXME pixel bleeding
+ 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 (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::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::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::performance_set(int perf) {
+ return false;
+}
+
+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;
+}
+
+void Api::button_last_set(uint gpio) {
+ m_button_last_pressed = gpio;
+}
diff --git a/api.hpp b/api.hpp
new file mode 100644
index 0000000..20f5c34
--- /dev/null
+++ b/api.hpp
@@ -0,0 +1,72 @@
+#ifndef __API_H__
+#define __API_H__
+
+#include <iostream>
+#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;
+ int m_app_render_interval = 500;
+ 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);
+ 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);
+ // 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);
+ // 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);
+ // 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);
+ // 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 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);
+};
+
+#endif \ No newline at end of file
diff --git a/apps/home_menu.cpp b/apps/home_menu.cpp
index 7f53447..910ebac 100644
--- a/apps/home_menu.cpp
+++ b/apps/home_menu.cpp
@@ -1,12 +1,8 @@
#include <stdio.h>
#include "pico/stdlib.h"
-extern "C" {
-#include "hardware/rtc.h"
-}
-#include "pico/util/datetime.h"
-#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 +11,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,28 +24,28 @@ 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;
- rtc_get_datetime(&t);
+ app_api->datetime_get(&t);
// title with time
title_str(datetime_str, sizeof(datetime_buf), &t);
- oledWriteString(oled, 0,0,0, datetime_str, FONT_8x8, 0, 1);
+ app_api->gui_header_text((std::string)datetime_str);
}
// 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<char*>(APPS_NAME[0][*selected_app]), FONT_12x16, 0, 1);
+ int render(Api *app_api) {
+ 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);
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 +75,20 @@ namespace app_home_menu {
}
// Initlisation of the app.
- int init(SSOLED *oled) {
- pressed_button = new char; *pressed_button = '*';
- selected_app = new int;
+ 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
}
// 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..895a7b9 100644
--- a/apps/main_clock.cpp
+++ b/apps/main_clock.cpp
@@ -1,12 +1,8 @@
#include <stdio.h>
#include "pico/stdlib.h"
-extern "C" {
-#include "hardware/rtc.h"
-}
-#include "pico/util/datetime.h"
-#include "../oled/ss_oled.h"
#include "main_clock.hpp"
+#include "../api.hpp"
#include "../buttons.hpp"
namespace app_main_clock {
@@ -39,46 +35,46 @@ 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;
- rtc_get_datetime(&t);
+ app_api->datetime_get(&t);
// time
time_as_str(datetime_str, sizeof(datetime_buf), &t);
- oledWriteString(oled, 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);
- oledWriteString(oled, 0,0,7, datetime_str, FONT_8x8, 0, 1);
+ app_api->gui_footer_text((std::string)datetime_str);
}
// 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->gui_header_text("Test clock", 17);
+ show_datetime(app_api);
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) {
+ app_api->performance_set(Api::perf_modes::LOW_POWER);
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/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/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/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);
}
diff --git a/pico-watch.cpp b/pico-watch.cpp
index 243f1e1..9df6799 100644
--- a/pico-watch.cpp
+++ b/pico-watch.cpp
@@ -3,52 +3,56 @@
#include "hardware/i2c.h"
#include "hardware/rtc.h"
#include "pico/util/datetime.h"
-#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;
+bool app_ready = true;
+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.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])(&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);
+ 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) {
- // 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
@@ -59,24 +63,28 @@ 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() {
init_all();
init_buttons();
+ app_api.init();
struct repeating_timer timer;
add_repeating_timer_ms(250, apps_bgrefresh, NULL, &timer);
app_init(current_app);
while (1) {
- app_render(current_app);
- sleep_ms(500);
+ 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;
}