Skip to content

Commit 7ab7f41

Browse files
committed
Add a draft of a new design guide. This is meant to capture the goals of CircuitPython and the corresponding design decisions.
1 parent 597777e commit 7ab7f41

File tree

2 files changed

+197
-0
lines changed

2 files changed

+197
-0
lines changed

docs/design_guide.md

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
# Design Guide
2+
3+
MicroPython has created a great foundation to build upon and to make it even
4+
better for beginners we've created CircuitPython. This guide covers a number of
5+
ways the core and libraries are geared towards beginners.
6+
7+
## Start libraries with the cookiecutter
8+
9+
Cookiecutter is a cool tool that lets you bootstrap a new repo based on another
10+
repo. We've made one [here]() for CircuitPython libraries that include configs
11+
for Travis CI and ReadTheDocs along with a setup.py, license, code of conduct
12+
and readme.
13+
14+
```sh
15+
# The first time
16+
pip install cookiecutter
17+
18+
cookiecutter gh:adafruit/cookiecutter-adafruit-circuitpython
19+
```
20+
21+
## Module Naming
22+
23+
Adafruit funded libraries should be under the
24+
[adafruit organization](https://github.com/adafruit) and have the format
25+
Adafruit_CircuitPython_<name> and have a corresponding `adafruit_<name>`
26+
directory (aka package) or `adafruit_<name>.py` file (aka module).
27+
28+
Community created libraries should have the format CircuitPython_<name> and not
29+
have the `adafruit_` module or package prefix.
30+
31+
Both should have the CircuitPython repository topic on GitHub.
32+
33+
## Lifetime and ContextManagers
34+
35+
A driver should be initialized and ready to use after construction. If the
36+
device requires deinitialization, then provide it through `deinit()` and also
37+
provide `__enter__` and `__exit__` to create a context manager usable with `with`.
38+
39+
## Verify your device
40+
41+
Make sure device you are talking to is the device you expect. If not, raise a
42+
ValueError. Beware that I2C addresses can be identical on different devices so
43+
read registers you know to make sure they match your expectation. Validating
44+
this upfront will help catch mistakes.
45+
46+
## Getters/Setters
47+
48+
When designing a driver for a device, use properties for device state and use
49+
methods for actions that the device performs. Doing this well helps beginners
50+
understand when to use what. It is also consistent with
51+
[Python](https://docs.python.org/3/library/functions.html#property).
52+
53+
## Design for compatibility with CPython
54+
55+
CircuitPython is aimed to be one's first experience with code. It will be the
56+
first step into the world of hardware and software. To ease one's exploration
57+
out from this first step, make sure that functionality shared with CPython shares
58+
the same API. It doesn't need to be the full API it can be a subset. However, do
59+
not add non-CPython APIs to the same modules. Instead, use separate non-CPython
60+
modules to add extra functionality. By distinguishing API boundaries at modules
61+
you increase the likelihood that incorrect expectations are found on import and
62+
not randomly during runtime.
63+
64+
## Document inline
65+
66+
Whenever possible, document your code right next to the code that implements it.
67+
This makes it more likely to stay up to date with the implementation itself. Use
68+
Sphinx's automodule to format these all nicely in ReadTheDocs. The cookiecutter
69+
helps set these up.
70+
71+
Use [rST](http://docutils.sourceforge.net/docs/user/rst/quickref.html) for markup.
72+
73+
### Module description
74+
75+
After the license comment:
76+
77+
```python
78+
"""
79+
`<module name>` - <Short description>
80+
=================================================
81+
<Longer description.>
82+
"""
83+
```
84+
85+
### Class description
86+
87+
```python
88+
class DS3231:
89+
"""Interface to the DS3231 RTC."""
90+
```
91+
92+
### Data descriptor description
93+
94+
Comment is after even though its weird.
95+
96+
```python
97+
lost_power = i2c_bit.RWBit(0x0f, 7)
98+
"""True if the device has lost power since the time was set."""
99+
```
100+
101+
### Property description
102+
103+
```python
104+
@property
105+
def datetime(self):
106+
"""Gets the current date and time or sets the current date and time then starts the clock."""
107+
return self.datetime_register
108+
109+
@datetime.setter
110+
def datetime(self, value):
111+
pass
112+
```
113+
114+
### Method description
115+
116+
```python
117+
def datetime(self):
118+
"""Gets the current date and time or sets the current date and time then starts the clock."""
119+
return self.datetime_register
120+
```
121+
122+
## Use BusDevice
123+
124+
BusDevice is an awesome foundational library that manages talking on a shared
125+
I2C or SPI device for you. Use it to ensure the library can be used in threads
126+
later.
127+
128+
## Lots of small modules
129+
130+
CircuitPython boards tend to have a small amount of internal flash and a small
131+
amount of ram but large amounts of external flash for the file system. So, create
132+
many small libraries that can be loaded as needed instead of one large file that
133+
does everything.
134+
135+
## Speed second
136+
137+
Speed isn't as important as API clarity and code size. So, prefer simple APIs
138+
like properties for state even if it sacrifices a bit of speed.
139+
140+
## Avoid allocations in drivers
141+
142+
Although Python doesn't require managing memory, its still a good practice for
143+
library writers to think about memory allocations. Avoid them in drivers if
144+
you can because you never know how much something will be called. Fewer
145+
allocations means less time spent cleaning up. So, where you can, prefer
146+
bytearray buffers that are created in `__init__` and used throughout the
147+
object with methods that read or write into the buffer instead of creating new
148+
objects. `nativeio` classes are design to read and write to subsections of
149+
buffers.
150+
151+
**However**, this is a memory tradeoff so do not do it for large or rarely used
152+
buffers.
153+
154+
### Examples
155+
156+
#### ustruct.pack
157+
Use `ustruct.pack_into` instead of `ustruct.pack`.
158+
159+
## Sensor properties and units
160+
161+
The [Adafruit Unified Sensor Driver Arduino library](https://learn.adafruit.com/using-the-adafruit-unified-sensor-driver/introduction) has a
162+
[great list](https://learn.adafruit.com/using-the-adafruit-unified-sensor-driver?view=all#standardised-si-units-for-sensor-data)
163+
of measurements and their units. Use the same ones including the property name
164+
itself so that drivers can be used interchangeably when they have the same
165+
properties.
166+
167+
| Property name | Python type | Units |
168+
|---------------|-------------|-------|
169+
| `acceleration` | (float, float, float) | x, y, z meter per second per second |
170+
| `magnetic` | float | micro-Tesla (uT) |
171+
| `orientation` | (float, float, float) | x, y, z degrees |
172+
| `gyro` | (float, float, float) | x, y, z radians per second |
173+
| `temperature` | float | degrees centigrade |
174+
| `distance` | float | centimeters |
175+
| `light` | float | SI lux |
176+
| `pressure` | float | hectopascal (hPa) |
177+
| `relative_humidity` | float | percent |
178+
| `current` | float | milliamps (mA) |
179+
| `voltage` | float | volts (V) |
180+
| `color` | int | RGB, eight bits per channel (0xff0000 is red) |
181+
| `alarm` | (time.struct, str) | Sample alarm time and string to characterize frequency ("secondly", "minutely", "hourly", "daily", "weekly", "monthly") |
182+
| `datetime` | time.struct | date and time |
183+
184+
## Adding native modules
185+
186+
The Python API for a new module should be defined and documented in
187+
`shared-bindings` and define an underlying C API. If the implementation is
188+
port-agnostic or relies on underlying APIs of another module, the code should
189+
live in `shared-module`. If it is port specific then it should live in `common-hal`
190+
within the port's folder. In either case, the file and folder structure should
191+
mimic the structure in `shared-bindings`.
192+
193+
## MicroPython compatibility
194+
195+
Keeping compatibility with MicroPython isn't a high priority. It should be done
196+
when its not in conflict with any of the above goals.

index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ docs are low-level API docs and may link out to separate getting started guides.
3434

3535
shared-bindings/index.rst
3636
docs/common_hal
37+
docs/design_guide
3738
docs/drivers.rst
3839
docs/supported_ports.rst
3940
docs/library/index.rst

0 commit comments

Comments
 (0)