From c56d1aa0b21f7dc080ef30c1bf77a98da4d6ae01 Mon Sep 17 00:00:00 2001 From: Adam Pippin Date: Sun, 19 Apr 2020 02:30:03 -0700 Subject: [PATCH] Add README --- README.md | 239 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 239 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..25a46c5 --- /dev/null +++ b/README.md @@ -0,0 +1,239 @@ +# watchos + +A simple framework for developing on the M5StickC. + +## Features + + * Kernel which handles task scheduling, running, signalling, and more. + * Eeprom abstraction layer which creates an allocation table to allow multiple + modules to share storage without hardcoding offsets or addresses everywhere. + * User Input module which handles tracking button presses, debouncing, and + converting the inputs into inter-process signals to provide a unified way + for handling events. + * Interface library which provides an easy way to handle creating tiling layouts + and rotation. + * Power management library to handle screen dimming and putting the processor + into deep sleep to conserve battery while giving tasks an opportunity to + save their state. + * The base system uses less than 10% of the ESP32's storage and 5% of its RAM. + +### Kernel + +The Kernel is the core of watchos. It provides a kind of cooperative multitasking. + +Tasks can be registered and set to run on a schedule or based on interrupts. Tasks +are given the opportunity for clean startup and shutdown, to handle user input, +to handle updating the display, and more. Tasks can be paused and resumed and +exited tasks can return an exit code consumable by other processes. + +The Kernel tries to intelligently conserve power by putting the processor into +light sleep in between scheduled executions, while making use of hardware features +to wake up sooner if events of interest occur. + +### EAT + +The "EEPROM Allocation Table" a la FAT/File Allocation Table. A few bytes are +reserved at the start of flash storage to track how many bytes have been allocated +and to which module. A read/write abstraction is provided to allow modules to +read/write from their allocated space. This serves to prevent conflicts between +multiple independent pieces of code attempting to use the storage device. + +### User Input + +A simple module that tracks if either button has been pressed, handles debouncing +(not detecting another press until they have first been released), and sends +interprocess signals to other processes when the events occur. This simplifies +input handling and prevents the need for duplication of things like debounce +logic. + +### Interface + +A take on a tiling window manager supplies a simple way to lay out multiple +modules on the display in a flexible way as well as provide for adjusting the +layout for various display rotations. Each module can register as many screen +sections as it wants as well as as many nested tiles as it wants in order to +create a layout. + +### Power Management + +A simple power management library which handles tracking idle time and taking +appropriate actions when the device is no longer in use. After a short delay, +the display's backlight is dimmed. After a longer delay it will signal all +running tasks to exit (giving them a chance to save state to eeprom or quickly +finish any outstanding tasks) then the processor will be placed into deep sleep +and the display and backlight powered off. This, combined with the Kernel's +intelligent use of light sleep helps to extend the M5StickC's battery life well +beyond what is typically expected. + +### Resource Efficient + +The base system (Kernel, EAT, User Input, User Interface, Power Management) uses +only around 10% of available program flash and 5% of dynamic memory leaving +plenty of room to build on top of. + +## Getting Started + +### Your App + +Include all of the watchos files in your project, then simply initialize the +kernel and register your tasks in your `setup()` method, then call the kernel +during each invocation of your `loop()` method. For example: + +``` +#include +#include "Kernel.h" + +int MyTask(int pid, unsigned int signal) +{ + return 0; +} + +void setup() +{ + M5.begin(); + Kernel_setup(); + Kernel_start(&MyTask, 5000); +} + +void loop() +{ + Kernel_tick(); +} +``` + +If your tasks make use of the EAT module, it will need to be initialized before +first use and a call to load the allocation table will need to be made on startup. +You can either add the initialization call to run once then remove from your code +later, or provide the ability to trigger initialization by, for example, holding +a button during power up. For example: + +``` +void setup() +{ + M5.begin(); + Kernel_setup(); + Kernel_start(&MyTask, 5000); + + M5.update(); + if (M5.BtnB.isPressed()) + { + EAT_initialize(); + } + else + { + EAT_load(); + } +} +``` + +If the side button is held during power up, the EEPROM will be reinitialized with +an empty allocation table, otherwise the existing table will be loaded (causing a +panic if no table exits or is valid). + +### Writing Your Task + +Each task method requires a simple signature: + +``` +int MyTask(int pid, unsigned int signal) +{ + [...] +} +``` + +This method takes its own process id and any pending signals as arguments, and +is expected to return an exit code as its return value. + +The id is generated each time the task is started. If you expect to start a +task multiple times, this allows you to manage keeping independent data stores +for each invocation. + +The signals are a way for the kernel and other tasks to notify you of events. +This will tell you whether your task was just started, is being requested to +shutdown, has reached its invocation interval, needs to redraw the display, etc. + +The returned exit code should always be `0` if your program expects to continue. +Any other value will signal the kernel to stop executing your task, and that value +will be available to other running tasks. + +#### Signals + +Signals are defined in `Kernel.h` and include: + + * `SIGNAL_TICK` - your task's configured run interval has elapsed + * `SIGNAL_START` - this is the first invocation of your task method and it + should perform first-startup initialization; this will only be sent once + * `SIGNAL_STOP` - your task is requested to shutdown; you *should* return a + non-zero exit code in response to this, though if your task needs to continue + running for a brief time to finish shutting down cleanly it may return 0 and + quit on a later invocation + * `SIGNAL_INPUT_A` - the main (home) button has been pressed; only delivered + once per press + * `SIGNAL_INPUT_B` - the secondary (side) button has been pressed; only delivered + once per press + * `SIGNAL_REDRAW` - the display needs to be redrawn and you should redraw + anything you have on the screen -- you should only ever perform draws in + response to this signal + +To see the full benefit of this system, you should read and understand the +further documentation on the kernel and how exactly you should use and respond +to these signals. + +##### Signal Mask + +Your application may not be interested in all of these signals. To avoid your +method being called unnecessarily, you can set a signal mask -- this tells the +kernel what signals you're interested in. The mask should include all signals +you *do* want to receive. For example: + +``` +Kernel_signal_mask(mypid, SIGNAL_START | SIGNAL_STOP | SIGNAL_TICK); +``` + +#### Example + +A simple framework for a method: + +``` +#include "Kernel.h" + +int MyTask(int pid, unsigned long signal) +{ + if (signal & SIGNAL_START) + { + // Perform startup here + } + + if (signal & SIGNAL_STOP) + { + // Perform shutdown here + return 255; + } + + if (signal & SIGNAL_TICK) + { + // Your requested run interval has transpired; perform any regular + // logic here to update state. + + // If you need to update the screen, do *not* do it here, instead trigger + // a redraw so the entire screen can be cleared and redrawn: + Kernel_signal(SIGNAL_REDRAW); + // Your application will also receive this REDRAW signal and can perform its + // draws in response to that. + } + + if (signal & SIGNAL_REDRAW) + { + // Update the screen from your state variables here. + } + + return 0; +} +``` + +## TODO: + + * Better kernel module system to support EAT as well as wifi/bluetooth/etc. + * More efficient draws: if we're using the tiling wm, no reason we need to + clear the entire screen; if we know which parts need a redraw we could + instead drawRect and only redraw those.