diff options
author | ConfuSomu | 2022-04-09 16:44:53 -0400 |
---|---|---|
committer | ConfuSomu | 2022-04-09 16:44:53 -0400 |
commit | f42e495f5eaf61c0fbd3f19000a8f7bb52503b29 (patch) | |
tree | 59de145acc9f546d439ca3acda65a34b80ca79e7 | |
parent | a687b2a05730d1ff7075695eae5437badc7bf8ad (diff) | |
download | pico-watch-f42e495f5eaf61c0fbd3f19000a8f7bb52503b29.tar pico-watch-f42e495f5eaf61c0fbd3f19000a8f7bb52503b29.tar.gz pico-watch-f42e495f5eaf61c0fbd3f19000a8f7bb52503b29.zip |
Add readme and documentation
-rw-r--r-- | docs/apps.md | 60 | ||||
-rw-r--r-- | docs/list_widget.md | 29 | ||||
-rw-r--r-- | docs/todo.md | 148 | ||||
-rw-r--r-- | readme.md | 85 |
4 files changed, 322 insertions, 0 deletions
diff --git a/docs/apps.md b/docs/apps.md new file mode 100644 index 0000000..b44afc8 --- /dev/null +++ b/docs/apps.md @@ -0,0 +1,60 @@ +Apps +==== + +Apps are classes that are derived from a virtual `base_app` class. Each of these derived classes have to define a set of methods to have a functional app. The mandatory methods are the app's render method (`render(Api*)`) and a method to retrieve the app's attributes (`app_get_attributes()`). + + +App attributes +---- + +Each app has attributes that define how the rest of the system must interface with it and how it is identified. The currently defined attributes are: + +- `uint id`: a unique unsigned int that is used to uniquely identify the current app. See *Adding apps*. +- `bool destroy_on_exit`: set to `true` if the app should be closed when run; set to `false` if the app should continue to run when closed by pressing the home button. See *Background refresh*. +- `uint render_interval`: frequency in milliseconds at which the render method should be called. See *Render method*. + +These attributes are stored in an `AppAttributes` struct. The app has to implement the virtual `app_get_attributes()` method to make sure that its (and not the virtual class') `AppAttribute` struct pointer is returned. More testing (and C++ standard reading) should be done to see if this is a real problem. + +Thus, app attributes related elements can be implemented as: +```cpp +// in the app class definition: +private: +// ... +AppAttributes app_attributes = {1, true}; +public: + const AppAttributes& app_get_attributes() { + return app_attributes; + } +``` + +Render method +---- + +The render method has the duty of rendering the app on the display. This is where the UI and any other graphics will be shown to the user. A pointer to `app_api` is passed as argument to allow drawing graphics and text on-screen and access other system facilities. + +This method should not take too much time as it is what dictates the app's frame rate. Offload setting application member variables to `btn_pressed`, for example, if a variable has to immediately change because of a user interaction, could be a wise choice. + +The frequency at which this method is called is determined by `AppAttributes::render_interval` struct member. Please set the render frequency to the highest value that still provides a pretty smooth UI. The bigger the value, the more time the CPU can spend sleeping, thus saving more power and having a longer battery life. + +Button pressed & released methods +---- + +The methods `btn_pressed()` and `btn_released()` allow an app to know which buttons have been pressed and then released by the user, respectively. These methods are called by the pin interrupt callback when a button is pressed and released. A gpio number is passed as argument with the time since *a* button has been last released or pressed, for each respective method. A pointer to `app_api` is also given as it might be helpful in offloading code from the render method and could help in having cleaner code. + +The button that has been pressed can be found by comparing `uint gpio` with the valus of the `BUTTON_` defines included within `app/base_app.hpp`. Only the app which has a value of `app_id` equal to 0 can receive the home button press; for other apps, pressing on the home button closes it. See *Background refresh* for modifying the home button behaviour. + +Please note that these two app methods may be called at any moment when the app is in foreground. It might be called when the render method is running or between render calls. Apps in background are guaranteed to never receive button presses. + +Background refresh +---- + +Applications have a facility that allow them to update while in the background. When running in the background, they shouldn't draw to the screen as this will interfere with the foreground app. + +To refresh in the background, an app must have set `AppAttributes::destroy_on_exit` to `false` to not be freed when closed (by the user or programmatically). Additionally, the `background_refresh()` method must be non-empty, else there's no use for running in the background. When running, an app can always close itself, to conserve system resources, by returning the right value. See *Return values*. + +Adding apps +---- + +Currently to add an app, `app_manager.cpp` has to be edited, with the app constructor added to `app_mgr::app_create`, under an unused `app_id`. The app's header, containing its class derived from `base_app`, has to be included and the `NUMBER_OF_APPS` define incremented. Furthermore, `apps/home_menu/main.hpp` has to be edited to be able to access the app: the app's name has to be added to the `APPS_NAME` array, at the same index as its `app_id` and `#define NUMBER_OF_APPS` has to be updated to be able to fully navigate the list of apps. + +In the future, this should be simplified by using a pre compile script to generate the relevant files. diff --git a/docs/list_widget.md b/docs/list_widget.md new file mode 100644 index 0000000..b213b17 --- /dev/null +++ b/docs/list_widget.md @@ -0,0 +1,29 @@ +ℹ This widget does not yet exist. This is just a to-do list on how I'd like to implement the widget. + +It hasn't been implemented as it is impossible to place text at arbitrary coordinates with `ss_oled`. See *App API* in todo.md. + +API List widget +==== + +Widget headers along with their source file will be stored in /hal/ui/widget. So `ListWidget` will be under `/hal/ui/widget/list.hpp` and `/hal/ui/widget/list.cpp`. + +There will also be a `ListWidgetElement` class for each list element. + +See the QT list widget class for ideas: https://doc.qt.io/qt-5/qlistwidget.html + +- The list widget will be a class +- The constructor will take a pointer to `app_api`, the widget's top-left x, y coordinates and width, height. +- Styling will be done by the implementation. The only exposed option would be to possibly invert the whole widget colour. +- To add elements to list (methods): + - All return `ListWidgetElement*` + - `element_append(ListWidgetElement*)` + - `element_insert() // insert at specific pos` + - `element_append_list() // add a whole array of elements (struct) to the widget` +- To scroll the widget (methods): + - `scroll() // num of elements to scroll down by (can be negative)` + - `scroll_to(ListWidgetElement* element) // scroll to a specific element` +- To change geometry (methods): + - `geometry(x, y, width, height)` +- To draw (methods): + - `paint()` +- ... diff --git a/docs/todo.md b/docs/todo.md new file mode 100644 index 0000000..da5bed5 --- /dev/null +++ b/docs/todo.md @@ -0,0 +1,148 @@ +To-do +==== + +This document contains a list of tasks that have to be done in order to have a fun and fully featured watch. As this project is composed of two main sections, the hardware and software, the tasks have been divided in two categories. Some tasks are relevant to both spheres, so they have been included in both places. + +Hardware +==== + +- [ ] Design a 3D printed enclosure. +- [ ] Design the circuit schematic. + - A Shottkey diode will be necessary for better power efficiency. See [this](https://electronics.stackexchange.com/questions/548990/which-shottky-diode-do-i-need-for-redundant-power-to-a-raspberry-pi-pico) and section 4.5 "Powering Pico" of the "[Raspberry Pi Pico Datasheet](https://datasheets.raspberrypi.com/pico/pico-datasheet.pdf)". + - A lithium polymer battery coupled with a solar panel might be an interesting idea.These online resources have implementation ideas: [Raspberry Pi Forum](https://www.raspberrypi.org/forums/viewtopic.php?f=144&t=305398) and [Adafruit](https://www.adafruit.com/product/4755). +- [ ] Choose a suitable IR or NFC IC or daughter board. + - Maybe the Pico can emulate NFC in software or via the PIO? To check. RF tuning won't be very easy, so an I2C/SPI module could help in adding the interface. + - See *Hardware ideas* section for more details; as they also concern software. +- [ ] Add buzzer support + - This will require API methods and other software support to be usable by apps. +- [ ] Add RGB LED support + - This is required to move the project to a Pimoroni Tiny 2040, as it has an RGB LED. + - This will require API methods and other software support to be usable by apps. +- [ ] Choose the right battery type + - [ ] Measure current consumption. + +Software +==== + +Home menu app +---- + +- [ ] Open the clock app when pressing home button while being on home menu. + +Clock app +---- + +- [ ] Clean up app. +- [ ] Add more features. + - [ ] Mode to save current time and date with an option to add a note later. When saving data, a default name/note should be given (such as "A") instead of prompting for one to make saving a quick action. Have a list consisting of saved timestamps and the ability to modify each data note. This list should be exportable as a CSV encoded in a QR code. + +Settings app +---- + +- [ ] Add way to reset display when there is a problem where display becomes "desynced" (a problem that's visible when the upper pixel line does not correspond with the top of the display, and seems to start at the middle) or flipped. + - [ ] Provide screenshot of the problem. + - I believe that it might be an I2C connection problem on my breadboard. + +App manager (`AppMgr`) +---- + +- [ ] Have a per app CMakeLists.txt file. +- [ ] Create pre-build script to construct `app_mgr::app_create()` from text file or from `apps` directory. + - [ ] Move `app_create` to another file to help. + +Background refresh (`bg_refresh`) +---- + +- [ ] Run `cb_status_check` on second core. (This might not be easy because of possible race conditions.) + - Send messages, such as button presses, via queues. + +App API +----- + +- [ ] Method that returns an std::list of apps that are currently installed. This will simplify implementation of home menu apps and reduce places where a list of apps has to be maintained. +- [ ] Make a list widget to aid in app UI development. See `todo/list_widget.md`. + - [ ] Modify `ss_oled` to allow writing text at arbitrary coordinates. + - [ ] Implement `BaseWidget` class, which has a paint virtual method. + - [ ] Implement list widget. +- [ ] Ring widget + - See Apple Watch "fitness rings" and Twitter character left indicator for inspiration. +- [ ] Method to disable automatic screen timeout. + - Might be useful in multimedia applications, such as watching animations or reading. +- [ ] Pop-up for entering text. + - Method will have an argument for limiting string length. + - Returns pointer to `std::string`. +- [ ] QR generation. See *Hardware ideas*. + - [This code](https://github.com/cyberman54/ESP32-Paxcounter/blob/e299444a894d38f70be872f489a07be0890345cc/src/display.cpp#L629) could give a good starting point. +- [ ] Notification support. Allow apps to display on-screen notifications. See shell applet for implementation. +- [ ] A shell applet--accessed by holding on the home button, à la Nintendo Switch--will allow users to view quick information about the system, such as battery level, time and notifications. The applet will be drawn over the current running, which will still stay in foreground, but won't receive any button presses. + - If the applet can be compact enough, it will be possible to render the foreground app behind it and grey it out with the help of a checkered pattern. This could be a nice visual effect. Though, this will not help in terms of battery life. + - The shell applet will be derived from `BaseApp` but its methods will be called a bit differently by the system. + +BaseApp class specification +---- + +- [ ] Pass an enum representing a button instead of an arbitrary gpio number to `btn_pressed()` and `btn_released()`. This will require a rewrite of all `btn_...` app method implementations. +- [ ] `AppAttribute` that, when `true`, causes the render method to only be called on button press. This can save a lot of power as the app won't be rendered when there is no change; the CPU will have more time to sleep. + - Instead of adding a new struct member, this could be enabled when `AppAttribute::render_interval = 0`. + +Main function +---- + +- [ ] Change app render loop to not use `sleep_ms()`. It might help reduce power consumption and have a more consistent framerate in apps. On the other hoof, I don't see if it is possible to change the timer interval when creating a timer with `add_repeating_timer_ms()`; testing has to be done to see if modifying the `repeating_timer` struct causes any problems. + - I don't know if I should implement this. + +Debugging facilities +---- + +- [ ] Have a way to take screen captures. + - I wrote the OLED buffer using the GDB command `dump binary value raw_screencap app_api.m_ucBuffer`. Then I converted the 1-bit depth image to a PNG with imagemagick: `convert -size 128x64 -depth 1 gray:raw_screencap test.png`. Unfortunately, this didn't give a useful result because of how the data is ordered when used by the display. + - Either I should make a script that's run on the host computer to reorder the bits in the buffer dump to produce a desirable results, or I should write a function, run on the watch, that creates a valid array that can be dumped by GDB and immediately converted. + - According to the current maintainer and developer of the *ss_oled* library (transcribed a little): + - The internal buffer has its memory laid out in the same way as the physical display. + - The display memory is 8 rows of 128 bytes. + - Pixel 0,0 is bit 0 of byte 0, but bit 1 of byte 0 is pixel 0,1 (the next line) + - Each row of 128 bytes represents pixels from 0,0 to 127,7 + - Each row has the byte oriented vertically with bit 0 as the top row and bit 7 as the bottom row. + - Thus: you need to write some code to re-orient the bits if you're going to output the image as a "normal" bitmap which has the bytes oriented horizontally with bit 7 on the left and bit 0 on the right. + +App ideas +---- + +- [ ] Basic watch functions + - [ ] Clock app + - [ ] Stopwatch + - [ ] Timer + - [ ] Alarm clock (needs additional app APIs) +- [ ] World clock to show time in multiple timezones. +- Fun! + - [ ] Draw the Mandelbrot set. + - See these links for inspiration, in order of usefulness: + - https://medium.com/farouk-ounanes-home-on-the-internet/mandelbrot-set-in-c-from-scratch-c7ad6a1bf2d9 + - https://jonisalonen.com/2013/lets-draw-the-mandelbrot-set/ + - https://codereview.stackexchange.com/questions/124358/mandelbrot-image-generator-and-viewer + - https://www.geeksforgeeks.org/fractals-in-cc/ + - https://mobile.codeguru.com/cpp/g-m/article.php/c19871/Mandelbrot-Using-C-AMP.htm + - [ ] Text file reader. The files could either be linked in the binary or read from an SD card via SPI. + - They could also be transferred into RAM via IR or NFC. + - [ ] Random quote viewer + - [ ] DVD screensaver effect, with the DVD logo. + - [On updating the logo's position](https://github.com/foone/Win95Uptime/blob/5a58d134d5b4ec3a10736b6e608ebc9178521b03/source/Win95Uptime.cpp#L390) +- Games! + - [ ] A TI-84 game called "Falldown". See branch `app-game_falldown` for a work in progress. + - A [Basic version of the game](https://www.ticalc.org/archives/files/fileinfo/239/23954.html), with screenshots. A [screenshot](https://tiwizard-media.s3.amazonaws.com/2012/11/falldown.gif) of what I would like to implement, from another version of the game. + - [ ] Snake + - [ ] Doom-esque first-person shooter + - Two buttons would be used to turn the camera to the left and right, a button would be used to go forward, and another button could serve to fire (use item) at ennemis. Holding the last button could open the game's menu. + - Thus, the buttons would be: forward, left (camera), right (camera), fire (hold for menu). + - Other control schemes could be envisaged. + - This would require us to do 3D in software! I think that the hardware should be powerful enough. There are two cores available. + - [ ] Flappy bird + +Hardware ideas +---- + +- [ ] IR or NFC support for data exchange + - This could be used to exchange GPG information, business cards (vCard), or maybe even 1-bit images and text to read on the watch. + - NFC has the advantage of being now widely supported by smartphones. Mobile operating systems, such as Android and iOS, allow writing to NFC devices using specialized apps. + - QR codes could also be used for one way data transfer. This method has the advantage of not requiring any additional hardware. Thought, the low screen resolution limit how much data can be displayed on a single QR code, so data might need to be divided between multiple QR codes. QR code generation and display should be implemented as an API method to facilitation adoption by apps. + - Maybe the data matrix format has a bigger data density? It has different text modes that allow packing more text in less space, but it is less supported by smartphones and might require the installation of an app to be readable by a mobile phone. diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..8fdd5fa --- /dev/null +++ b/readme.md @@ -0,0 +1,85 @@ +Pico Watch +======= + +Pico Watch is a watch based on the Raspberry Pi Pico platform. The watch is composed of an 128x64 OLED display and five buttons. A buzzer and RGB LED are planned to be added later on to provide additional features, such as notifications from running apps and alarms. + +The RP2040 used in the Pico has an official SDK that is extensively documented and has good community support for a new platform, since being released in January 2021. + +Hardware +---- + +The final design is to be composed of Pimoroni's Tiny 2040, five buttons, an 128x64 SSD1306 OLED display and a piezo buzzer. The RGB LED is embedded in the Tiny 2040 board. + +The five buttons function are up, down, select, mode and home. The home button is not accessible by the foreground application, except if it is the home menu app. + +Power will be provided by either coin cell batteries (CR2032), AAA batteries or more obscure types. It depends on power consumption. If all fails, lithium polymer or lithium-ion batteries could be used. + +Software +---- + +The software is made to be modular by adopting a concept where every user interface is composed of apps. These apps have access to an API that facilitates programming by providing varying layers of abstraction such as modal dialogue boxes. App management, including instantiation and freeing, is managed by custom code.[1] Thus, on start-up, the home menu app is loaded. + +The current available apps are the home menu (`home_menu`), a clock (`main_clock`) and settings (`settings`). + +See file `docs/apps.md` for more information, including on how to add apps. + +User settings are stored in a global `g_s` struct. They currently include: + +- The time format (24h or 12h) +- The OLED brightness +- The OLED screen timeout + +The settings app allows the user to change these settings in addition to the current date and time. + +Building +---- + +The project uses CMake, as recommended by the Raspberry Pi Pico C/C++ SDK documentation. Thus, the `CMakeLists.txt` file orchestrates the whole building process. + +See section 2.1 "The Build System", page 8, of "[Pico C/C++ SDK](https://datasheets.raspberrypi.com/)" for instructions on how to build Pico projects. + +Debugging +---- + +When working on the project, I use Microsoft VSCode for programming as it provides easily useable debugging features and code autocompletion. + +I use a GDB script that allows debugging STL containers. The script is available in `utility/stl_gdb_utils`. To use it, one has to copy the contents to their .gdbinit file, that's probably located at `~/.gdbinit`. + +### Debugging tips + +- Access tty: `minicom -b 115200 -o -D /dev/ttyS0` +- In GDB: + - Use `pvector app_mgr::open_apps` to print current open apps. You need the GDB helper scripts. + - Use `print malloc(500)` to debug memory allocation failures. You can malloc more than `PICO_HEAP_SIZE` as this only guaranties a minimum heap size, see [here](https://www.raspberrypi.org/forums/viewtopic.php?f=144&t=318030). + - `dump binary value <filename> <symbol>` allows writing the contents of a variable to disk. The contents can later be written back into memory. + +To-do +---- + +- [x] Check method and function names in documentation. +- [x] Check links in docs to make sure that they are relevant and not dead. +- [x] Fix the TODOs! +- [ ] Add licence of libraries in "Libraries used" section. +- [ ] Add project license. + +See file under `docs/todo.md` for more tasks. + +Libraries used +---- + +- [RP2040 ss_oled fork](https://github.com/bitbank2/Pi_Pico_C_Projects/tree/master/ss_oled) by bitbank ([Github](https://github.com/bitbank2), [Twitter](https://twitter.com/@fast_code_r_us)) + - Relevant [forum topic](https://forums.raspberrypi.com/viewtopic.php?f=144&t=299754). + - I am using a modified version, based on commit `18ccaba` in the ss_oled git repository. +- [Raspberry Pi Pico C/C++ SDK](https://github.com/raspberrypi/pico-sdk). + - This project's code was last build against version (git tag) `1.0.1` of the SDK, released February 1 2021. + +Inspiration +---- + +These projects were a good source of inspiration: +- [Open Smartwatch](https://github.com/Open-Smartwatch/open-smartwatch-os) +- [Sensor Watch](https://twitter.com/josecastillo/status/1476336711416397824) ([Crowd Supply campaign](https://www.crowdsupply.com/oddly-specific-objects/sensor-watch)). This is a replacement PCB for the Casio F-91W. It uses an Atmel ARM Cortex M0+ microcontroller. + +---- + +[1]: I might have accidentally created a custom microkernel(?) because of the features that manage running apps. I won't use this term and instead prefer to use the word "system" to designate the code that's not in the `apps` subdirectory. |