127 lines
5.7 KiB
Markdown
127 lines
5.7 KiB
Markdown
|
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!
|