summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorPaul Sokolovsky <pfalcon@users.sourceforge.net>2016-11-06 22:08:35 +0300
committerPaul Sokolovsky <pfalcon@users.sourceforge.net>2016-11-06 22:08:35 +0300
commit8f068e84eedd90146b66b9ffd8193e2e37a24b4d (patch)
treeb7d770ec9a15680d15d61bce6588511238f952b1
parent5c3d75c937c1e7d220dcb8fb4230dd8280b1453f (diff)
downloadmicropython-8f068e84eedd90146b66b9ffd8193e2e37a24b4d.tar.gz
micropython-8f068e84eedd90146b66b9ffd8193e2e37a24b4d.zip
examples/hwapi: Example showing best practices for HW API usage in apps.
Showing and providing detailed instructions and motivation.
-rw-r--r--examples/hwapi/README.md126
-rw-r--r--examples/hwapi/hwconfig_esp8266_esp12.py5
-rw-r--r--examples/hwapi/hwconfig_z_frdm_k64f.py5
-rw-r--r--examples/hwapi/soft_pwm.py38
4 files changed, 174 insertions, 0 deletions
diff --git a/examples/hwapi/README.md b/examples/hwapi/README.md
new file mode 100644
index 0000000000..1992eb6609
--- /dev/null
+++ b/examples/hwapi/README.md
@@ -0,0 +1,126 @@
+This directory shows the best practices for using MicroPython hardware API
+(`machine` module). `machine` module strives to provide consistent API
+across various boards, with the aim to enable writing portable applications,
+which would work from a board to board, from a system to another systems.
+This is inherently a hard problem, because hardware is different from one
+board type to another, and even from examplar of board to another. For
+example, if your app requires an external LED, one user may connect it
+to one GPIO pin, while another user may find it much more convinient to
+use another pin. This of course applies to relays, buzzers, sensors, etc.
+
+With complications above in mind, it's still possible to write portable
+applications by using "low[est] denominator" subset of hardware API and
+following simple rules outlined below. The applications won't be able
+to rely on advanced hardware capabilities of a particular board and
+will be limited to generic capabilities, but it's still possible to
+write many useful applications in such a way, with the obvious benefit of
+"write once - run everywhere" approach (only configuration for a particular
+board is required).
+
+The key to this approach is splitting your application into (at least)
+2 parts:
+
+* main application logic
+* hardware configuration
+
+The key point is that hardware configuration should be a separate file
+(module in Python terms). A good name would be `hwconfig.py`, and that's
+how we'll call it from now on. Another key point is that main application
+should never instantiate (construct) hardware objects directly. Instead,
+they should be defined in `hwconfig.py`, and main application should
+import and reference hardware objects via this module. The simplest
+application of this idea would look like:
+
+`hwconfig.py`:
+
+ from machine import Pin
+
+ LED = Pin("A3", Pin.OUT)
+
+`app.py`:
+
+ from hwconfig import *
+ import utime
+
+ while True:
+ LED.value(1)
+ utime.sleep_ms(500)
+ LED.value(0)
+ utime.sleep_ms(500)
+
+
+To deploy this application to a particular board, a user will need:
+
+1. Edit `hwconfig.py` to adjust Pin and other hardware peripheral
+ parameters and locations.
+2. Actually deploy `hwconfig.py` and `app.py` to a board (e.g. copy to
+ board's filesystem, or build new firmware with these modules frozen
+ into it).
+
+Note that there's no need to edit the main application code! (Which may
+be complex, while `hwconfig.py` should usually remain short enough, and
+focused solely on hardware configuration).
+
+An obvious improvement to this approach is the following. There're few
+well-known boards which run MicroPython, and most of them include an
+onboard LED. So, to help users of these boards to do configuration
+quickly (that's especially important for novice users, for who may
+be stumped by the need to reach out to a board reference to find LED
+pin assignments), `hwconfig.py` your application ships may include
+commented out sections with working configurations for different
+boards. The step 1 above then will be:
+
+1. Look thru `hwconfig.py` to find a section which either exactly
+ matches your board, or the closest to it. Uncomment, and if any
+ adjustments required, apply them.
+
+It's important to keep in mind that adjustments may be always required,
+and that there may be users whose configuration doesn't match any of
+the available. So, always include a section or instructions for them.
+Consider for example that even on a supported board, user may want to
+blink not an on-board LED, but the one they connected externally.
+MicroPython's Hardware API offers portability not just among "supported"
+boards, but to any board at all, so make sure users can enjoy it.
+
+There's next step of improvement to make. While having one `hwconfig.py`
+with many sections would work for smaller projects with few hardware
+objects, it may become more cumbersome to maintain both on programmer's
+and user's sides for larger projects. Then instead of single
+`hwconfig.py` file, you can provide few "template" ones for well-known
+boards:
+
+* `hwconfig_pyboard.py`
+* `hwconfig_wipy.py`
+* `hwconfig_esp8266.py`
+* etc.
+
+Then step 1 above will be:
+
+1. Look thru available `hwconfig_*.py` files and find one which matches
+ your board the best, then rename to `hwconfig.py` and make adjustments,
+ if any.
+
+Again, please keep in mind that there may be users whose hardware will be
+completely unlike you heard of. Give them some helpful hints too, perhaps
+provide `hwconfig_custom.py` with some instructions.
+
+That's where we stop with improvements to the "separate file for hardware
+configuration" idea, as it is already pretty flexible and viable. An
+application in this directory shows it in practice, using slightly less
+trivial example than just a blinking LED: `soft_pwm.py` implements a
+software PWM (pulse width modulation) to produce an LED fade-in/fade-out
+effect - without any dependence on hardware PWM availability.
+
+Note that improvements to board configuration handling may continue further.
+For example, one may invent a "configuration manager" helper module which will
+try to detect current board (among well-known ones), and load appropriate
+`hwconfig_*.py` - this assumes that a user would lazily deploy them all
+(or that application will be automatically installed, e.g. using MicroPython's
+`upip` package manager). The key point in this case remains the same as
+elaborated above - always assume there can, and will be a custom configuration,
+and it should be well supported. So, any automatic detection should be
+overridable by a user, and instructions how to do so are among the most
+important you may provide for your application.
+
+By following these best practices, you will use MicroPython at its full
+potential, and let users enjoy it too. Good luck!
diff --git a/examples/hwapi/hwconfig_esp8266_esp12.py b/examples/hwapi/hwconfig_esp8266_esp12.py
new file mode 100644
index 0000000000..e8cf2d12e1
--- /dev/null
+++ b/examples/hwapi/hwconfig_esp8266_esp12.py
@@ -0,0 +1,5 @@
+from machine import Pin
+
+# ESP12 module as used by many boards
+# Blue LED on pin 2
+LED = Pin(2, Pin.OUT)
diff --git a/examples/hwapi/hwconfig_z_frdm_k64f.py b/examples/hwapi/hwconfig_z_frdm_k64f.py
new file mode 100644
index 0000000000..c45e3e7567
--- /dev/null
+++ b/examples/hwapi/hwconfig_z_frdm_k64f.py
@@ -0,0 +1,5 @@
+from machine import Pin
+
+# Freescale/NXP FRDM-K64F board
+# Blue LED on port B, pin 21
+LED = Pin(("GPIO_1", 21), Pin.OUT)
diff --git a/examples/hwapi/soft_pwm.py b/examples/hwapi/soft_pwm.py
new file mode 100644
index 0000000000..a9a5561717
--- /dev/null
+++ b/examples/hwapi/soft_pwm.py
@@ -0,0 +1,38 @@
+import utime
+from hwconfig import LED
+
+
+# Using sleep_ms() gives pretty poor PWM resolution and
+# brightness control, but we use it in the attempt to
+# make this demo portable to even more boards (e.g. to
+# those which don't provide sleep_us(), or provide, but
+# it's not precise, like would be on non realtime OSes).
+# We otherwise use 20ms period, to make frequency not less
+# than 50Hz to avoid visible flickering (you may still see
+# if you're unlucky).
+def pwm_cycle(led, duty, cycles):
+ duty_off = 20 - duty
+ for i in range(cycles):
+ if duty:
+ led.value(1)
+ utime.sleep_ms(duty)
+ if duty_off:
+ led.value(0)
+ utime.sleep_ms(duty_off)
+
+
+# At the duty setting of 1, an LED is still pretty bright, then
+# at duty 0, it's off. This makes rather unsmooth transition, and
+# breaks fade effect. So, we avoid value of 0 and oscillate between
+# 1 and 20. Actually, highest values like 19 and 20 are also
+# barely distinguishible (like, both of them too bright and burn
+# your eye). So, improvement to the visible effect would be to use
+# more steps (at least 10x), and then higher frequency, and use
+# range which includes 1 but excludes values at the top.
+while True:
+ # Fade in
+ for i in range(1, 21):
+ pwm_cycle(LED, i, 2)
+ # Fade out
+ for i in range(20, 0, -1):
+ pwm_cycle(LED, i, 2)