summaryrefslogtreecommitdiff
path: root/docs/appguide.rst
diff options
context:
space:
mode:
authorDaniel Thompson <daniel@redfelineninja.org.uk>2020-05-10 13:10:04 (GMT)
committerDaniel Thompson <daniel@redfelineninja.org.uk>2020-05-11 20:44:30 (GMT)
commita2fd52ef2b8a95de9115e8b1c69ea82513356a66 (patch)
treeb1f78453148ca365a13c0a9104fd63afe62291a6 /docs/appguide.rst
parentbb8e95cdd488cd8451eeac96810823dd4dd6e0aa (diff)
docs: Introduce the Application Writer's Guide.
Diffstat (limited to 'docs/appguide.rst')
-rw-r--r--docs/appguide.rst350
1 files changed, 350 insertions, 0 deletions
diff --git a/docs/appguide.rst b/docs/appguide.rst
new file mode 100644
index 0000000..8e7b469
--- /dev/null
+++ b/docs/appguide.rst
@@ -0,0 +1,350 @@
+Application Writer's Guide
+==========================
+
+.. contents::
+ :local:
+
+Introduction
+------------
+
+WASP, the Watch Application System in Python, has one pervasive goal that
+influences almost everything about it, from its name to its development
+roadmap: make writing applications easy (and fun).
+
+Applications that can be loaded, changed, adapted and remixed by the user
+are what **really** distinguishes a smart watch from a "feature watch"[#]_.
+In other words if we want a watch built around a tiny microcontroller to be
+sufficiently "smart" then it has to be all about the applications.
+
+This guide will help you get started writing applications for WASP. Have fun!
+
+.. [#] The fixed function mobile phones that existed before iOS and Android
+ took over the industry were retrospectively renamed "feature phones" to
+ distinguish them from newer devices. Many of them were superficially similar
+ to early Android devices but is was the application ecosystem that really
+ made smart phones smart.
+
+Hello World for WASP
+~~~~~~~~~~~~~~~~~~~~
+
+Let's start by examining a simple "Hello, World!" application for WASP.
+
+.. literalinclude:: hello.py
+ :linenos:
+
+There are a couple of points of interest:
+
+1. Applications have a :py:data:`~.TemplateApp.NAME`, which is shown in the
+ launcher. Most applications also provide an :py:data:`~.TemplateApp.ICON`
+ but a default is displayed if this is omitted.
+2. This example uses :py:meth:`~.TemplateApp.__init__` to initialize
+ the state of the application, this ensure the state remains "sticky"
+ when the application is activated and deactivated.
+3. :py:meth:`~.TemplateApp.foreground` is the only mandatory application entry
+ point and is responsible for redrawing the screen. This application does
+ not implement :py:meth:`~.TemplateApp.background` because there is nothing
+ for us to do!
+4. The use of :py:meth:`~.TemplateApp._draw` is optional. We could just do
+ the work in :py:meth:`~.TemplateApp.foreground` but this application follows
+ a common WASP idiom that is normally used to pattern to distinguish a full
+ refresh of the screen from an fast update (a redraw).
+
+Application life-cycle
+----------------------
+
+Applications in WASP are triggered by and do all their processing
+from calls their entry points. The entry points can be coarsely categorized
+event notifications, timer callbacks (the application tick) and
+system notifications.
+
+System notifications control the application life-cycle and the entry point
+calls, together with the implicit application states are shown below.
+
+.. graphviz::
+
+ digraph lifecycle {
+ START -> BACKGROUND [ label=" __init__() " ];
+ BACKGROUND -> START [ label=" __del__() " ];
+ BACKGROUND -> ACTIVE [ label=" foreground() " ];
+ ACTIVE -> BACKGROUND [ label=" background() " ];
+ ACTIVE -> GO_TO_CLOCK [ label=" sleep() -> False " ];
+ GO_TO_CLOCK -> BACKGROUND [ label=" background() " ];
+ ACTIVE -> SLEEPING [ label=" sleep() -> True " ];
+ SLEEPING -> ACTIVE [ label=" wake() " ];
+
+ START [ shape=box ];
+ BACKGROUND [ shape=box, style=rounded ]
+ ACTIVE [ shape=box, style=rounded ]
+ SLEEPING [ shape=box, style=rounded ]
+ GO_TO_CLOCK [ label="GOTO ClockApp" ];
+ }
+
+When an application is initialized is enters the ``BACKGROUND`` state. A
+backgrounded application will not execute but it should nevertheless
+maintain its user visible state whilst in the background. To conserve
+memory WASP does not permit two applications to run simultaneously but
+because each application preserves its state when in the background it will
+appear to the user as though all applications are running all the time.
+
+For example, a stopwatch application should record the time that it was started
+and remember that start time, regardless of it's state, until either the
+stopwatch is stopped of the application is destroyed.
+
+A backgrounded application can enter the ``ACTIVE`` state via a call to
+:py:meth:`~.TemplateApp.foreground`. When it is active the application owns the
+screen and should draw and maintain its UI.
+
+If the system manager want to put an active application to sleep then it will
+call :py:meth:`~.TemplateApp.sleep`. If the application returns True then the
+application will stop running (e.g. receive no events and no application tick)
+but instead must wait to receive a notification via
+:py:meth:`~.TemplateApp.wake` telling the application that the device
+is waking up and that it may update the screen if needed.
+
+If an application does not support sleeping then it can simply not implement
+:py:meth:`~.TemplateApp.sleep` (or :py:meth:`~.TemplateApp.wake`) although it
+can also return False from :py:meth:`~.TemplateApp.sleep` if this is preferred.
+In this case the system manager will automatically return to the default
+application, typically the main clock face.
+
+Note: *Most applications do not need to support sleep() since it is often
+a better user experience for the watch to return to the default application
+when they complete an interaction.*
+
+API primer
+----------
+
+This API primer introduces some of the most important and frequently used
+interfaces for WASP. For more comprehensive coverage see the
+:ref:`WASP Reference Manual` which contains extensive API documentation
+covering the entire of WASP, including its drivers.
+
+System management
+~~~~~~~~~~~~~~~~~
+
+The system management API does provide a number of low-level calls that
+can register new applications and navigate between them. However most
+applications need not use these. Instead most applications use a small
+set of methods. In particular almost all applictions need to call a couple of
+methods from :py:meth:`~.TemplateApp.foreground` in order to register
+for notifications:
+
+* :py:meth:`~.Manager.request_event` - register for UI events such as button
+ presses and touch screen activity.
+* :py:meth:`~.Manager.request_tick` - register to receive an application tick
+ and specify the tick frequency.
+
+Additionally if your application is a game or a similar program that should
+not allow the watch to go to sleep then it should arrange to call
+:py:meth:`~.Manager.keep_awake` from the application's
+:py:meth:`~.TemplateApp.tick` method.
+
+Drawing
+~~~~~~~
+
+Most applications using the drawing toolbox, :py:data:`wasp.watch.drawable`,
+in order to handle the display. Applications are permitted to directly access
+:py:data:`wasp.watch.display` if they require direct pixel access or want to
+exploit specific features of the display hardware (inverse video, partial
+display, etc) but for simple applications then the following simple drawing
+functions are sufficient.
+
+* :py:meth:`~.Draw565.blit` - blit an image to the display at specified (x, y)
+ coordinates, image type is detected automatically
+* :py:meth:`~.Draw565.fill` - fill a rectangle, without arguments the default
+ is a black rectangle covering the entire screen which is useful to clear
+ the screen prior to an update
+* :py:meth:`~.Draw565.string` - render a string, optionally centring it
+ automatically
+* :py:meth:`~.Draw565.wrap` - automatically determine where to break a string
+ so it can be rendered to a specified width
+
+Most applications run some variant of the following code from their
+:py:meth:`~.TemplateApp.foreground` or :py:meth:`~.TemplateApp._draw` methods
+in order to clear the display ready for a redraw.
+
+.. code-block:: python
+
+ draw = wasp.watch.drawable
+ draw.fill()
+ # now use draw to render the rest of the screen
+
+Some applications customize the above code slightly if they require a custom
+background colour and it may even be omitted entirely if the application
+explicitly draws every pixel on the display.
+
+Finally, WASP provides a small number of widgets that allow common fragments of
+logic and redrawing code to be shared between applications:
+
+* :py:class:`.BatteryMeter`
+* :py:class:`.ScrollingIndicator`
+
+MicroPython
+~~~~~~~~~~~
+
+Many of the features of WASP are inherited directly from MicroPython_. It is
+useful to have a basic understanding of MicroPython and, in particular, put
+in a little time learning the best ways to copy with running
+`MicroPython on microcontrollers`__.
+
+.. _MicroPython: https://micropython.org/
+__ http://docs.micropython.org/en/latest/reference/constrained.html
+
+
+How to run your application
+---------------------------
+
+Testing on the simulator
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+WASP provides a simulator that can be used to test applications before
+downloading them to the device. The simulator is useful for ensuring the
+code is syntactically correct and that there are not major runtime problems
+(e.g. missing symbols).
+
+Note: *The simulator does not model the RAM or code size limits of the
+real device. It may still be necessary to tune the application for minimal
+footprint after testing on the simulator.*
+
+Firstly launch the simulator:
+
+.. code-block:: sh
+
+ sh$ make sim
+ PYTHONDONTWRITEBYTECODE=1 PYTHONPATH=.:wasp/boards/simulator:wasp \\
+ python3 -i wasp/main.py
+ MOTOR: set on
+ BACKLIGHT: 2
+ Watch is running, use Ctrl-C to stop
+
+From the simulator console we can register the application with the following
+code:
+
+.. code-block:: python
+ :linenos:
+
+ ^C
+ Traceback (most recent call last):
+ ...
+ KeyboardInterrupt
+ >>> from myapp import MyApp
+ >>> wasp.system.register(MyApp())
+ >>> wasp.system.run()
+ Watch is running, use Ctrl-C to stop
+
+When an application is registered it does not start automatically but it will
+have been added to the launcher and you will be able to select in the simulator
+by using the Arrow keys to bring up the launcher and then clicking on your
+application.
+
+The application can also be registered automatically when you load the
+simulator if you add it to ``wasp/main.py``. Try adding lines 5 and 6 from
+the above example into this file (between ``import wasp`` and
+``wasp.system.run()``).
+
+Testing on the device
+~~~~~~~~~~~~~~~~~~~~~
+
+If we have an application under development when we can launch a quick test
+that does not result in the application being permanently stored on the device.
+Providing there is enough available RAM then this can lead to a very quick
+edit-test cycles.
+
+Try:
+
+.. code-block:: sh
+
+ sh$ tools/wasptool \\
+ --exec myapp.py \\
+ --eval "wasp.system.register(MyApp())"
+ Preparing to run myapp.py:
+ [##################################################] 100%
+
+Like the simulator, when an application is registered it does not start
+automatically but it will have been added to the launcher and can be launched
+using the normal gestures to control the device.
+
+Note: *If the progress bar jams at the same point each time then it is likely
+your application is too large to be compiled on the target. You may have to
+adopt the frozen module approach from the next section.*
+
+Making it permanent
+~~~~~~~~~~~~~~~~~~~
+
+To ensure you application survives a system reset (press the hardware
+button for around five seconds until the splash screen is seen, wait
+five seconds and then press again) then we must copy it to the device
+and ensure it gets launched during system startup.
+
+Note: *Applications stored in external FLASH have a greater RAM overhead
+than applications that are frozen into the WASP binary. See next section
+for additional details.*
+
+To copy your application to the external FLASH try:
+
+.. code-block:: sh
+
+ sh$ ./tools/wasptool --upload myapp.py
+ Uploading myapp.py:
+ [##################################################] 100%
+
+At this point your application is stored on the external FLASH but it will
+not automatically be loaded. This requires you to update the ``main.py`` file
+stored in the external FLASH. When WASP runs for the first time it
+automatically generates this file (using ``wasp/main.py`` as a template)
+and copies it to the external FLASH. After this point ``main.py`` is user
+modifiable and can be used to tweak the configuration of the watch before
+it starts running.
+
+Edit ``wasp/main.py`` to add the following two lines between ``import wasp``
+and the ``wasp.system.run()``:
+
+.. code-block:: python
+
+ from myapp import MyApp
+ wasp.system.register(MyApp())
+
+Having done that we can use ``wasptool`` to upload the modified file
+to the watch:
+
+.. code-block:: sh
+
+ sh$ ./tools/wasptool --upload wasp/main.py
+ Uploading wasp/main.py:
+ [##################################################] 100%
+
+Note: *If the new code on the watch throws an exception (including
+an out-of-memory exception) then your watch will display a black
+screen at startup. If that happens, and you don't know how to debug
+the problem, then you can use wasptool to restore the original version
+of main.py .*
+
+Freezing your application into the WASP binary
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Freezing your application causes it to consume dramatically less RAM. That is
+because the code is both pre-compiled (meaning we don't need any RAM budget to
+run the compiler) **and** it can execute directly from the internal FLASH
+memory.
+
+Freezing your application simply requires you to modify the ``manifest.py``
+file for your board (e.g. ``wasp/boards/pinetime/manifest.py``) to include
+your application and then the whole binary must be re-compiled as normal.
+
+After that you an use the same technique described in the previous
+section to add an import and register for you application to ``main.py``
+
+Note: *The micropython import path "prefers" frozen modules to those
+found in the external filesystem. If your application is both frozen and
+copied to external FLASH then the frozen version will be loaded.*
+
+Application entry points
+------------------------
+
+Applications provide entry points for the system manager to use to notify
+the application of a change in system state or an user interface event.
+
+.. automodule:: apps.template
+ :members:
+ :private-members:
+ :special-members: