kopia lustrzana https://github.com/peterhinch/micropython-samples
Doc improvements. DS3231 portable driver added.
rodzic
a44008832f
commit
618a181fc4
|
@ -0,0 +1,91 @@
|
||||||
|
# The DS3231 real time clock chip
|
||||||
|
|
||||||
|
This is a remarkably inexpensive and easily interfaced battery-backed RTC. It
|
||||||
|
is an ideal way to rapidly calibrate the Pyboard's RTC which can then achieve
|
||||||
|
similar levels of accuracy (+- ~2 mins/year). The chip can also provide
|
||||||
|
accurate time to platforms lacking a good RTC (notably the ESP8266).
|
||||||
|
|
||||||
|
Two drivers are provided:
|
||||||
|
1. `ds3231_port.py` A multi-platform driver.
|
||||||
|
2. `ds3231_pb.py` A Pyboard-specific driver with RTC calibration facility.
|
||||||
|
|
||||||
|
Breakout boards are widely available. The interface is I2C. Pullups to 3.3V
|
||||||
|
(typically 10KΩ) should be provided on the `SCL` and `SDA` lines if these are
|
||||||
|
not supplied on the breakout board.
|
||||||
|
|
||||||
|
Both divers use edge detection to achieve millisecond-level precision from the
|
||||||
|
DS3231. This enables relatively rapid accuracy testing of the platform's RTC,
|
||||||
|
and fast calibration of the Pyboard's RTC.
|
||||||
|
|
||||||
|
###### [Main README](./README.md)
|
||||||
|
|
||||||
|
# 1. The multi-platform driver
|
||||||
|
|
||||||
|
This can use soft I2C so any pins may be used.
|
||||||
|
|
||||||
|
It is based on the assumption that, where a hardware RTC exists, MicroPython's
|
||||||
|
local time (`utime.localtime()`) is based on the RTC time. Changes to local
|
||||||
|
time don't propagate to the RTC which must explicitly be set. This holds for
|
||||||
|
the Pyboard, ESP8266 and ESP32.
|
||||||
|
|
||||||
|
The official ESP32 port currently lacks support for the RTC so the Loboris port
|
||||||
|
should be used for this purpose. The driver supports both but if the official
|
||||||
|
port is used only the local time can be updated from the DS3231.
|
||||||
|
|
||||||
|
## 1.1 The DS3231 class
|
||||||
|
|
||||||
|
Constructor:
|
||||||
|
This takes one mandatory argument, an initialised I2C bus.
|
||||||
|
|
||||||
|
Public methods:
|
||||||
|
1. `get_time` Optional boolean arg `set_rtc=False`. If `set_rtc` is `True` it
|
||||||
|
sets the platform's RTC from the DS3231. It returns the DS3231 time as a tuple
|
||||||
|
in the same format as `utime.localtime()` except that yday (day of year) is 0.
|
||||||
|
So the format is (year, month, day, hour, minute, second, wday, 0).
|
||||||
|
Note that on ports/platforms which don't support an RTC, if `set_rtc` is
|
||||||
|
`True`, the local time will be set from the DS3231.
|
||||||
|
2. `save_time` No args. Sets the DS3231 time from the platform's local time.
|
||||||
|
3. `rtc_test` Optional args: `runtime=600`, `ppm=False`. This tests the
|
||||||
|
platform's local time against the DS3231 returning the error in parts per
|
||||||
|
million (if `ppm` is `True`) or seconds per year. A positive value indicates
|
||||||
|
that the DS3231 clock leads the platform local time.
|
||||||
|
The `runtime` value in seconds defines the duration of the test. The default
|
||||||
|
of 10 minutes provides high accuracy but shorter durations will suffice on
|
||||||
|
devices with poor RTC's (e.g. ESP8266).
|
||||||
|
|
||||||
|
# 2. The Pyboard driver
|
||||||
|
|
||||||
|
The principal reason to use this driver is to calibrate the Pyboard's RTC.
|
||||||
|
|
||||||
|
This assumes that the DS3231 is connected to the hardware I2C port on the `X`
|
||||||
|
or `Y` side of the board, and that the Pyboard's RTC is set to the correct time
|
||||||
|
and date.
|
||||||
|
|
||||||
|
Usage to calibrate the Pyboard's RTC. Takes 5 minutes.
|
||||||
|
|
||||||
|
```python
|
||||||
|
from ds3231_pb import DS3231
|
||||||
|
ds3231 = DS3231('X')
|
||||||
|
ds3231.save_time() # Set DS3231 to match Pyboard RTC
|
||||||
|
ds3231.calibrate()
|
||||||
|
```
|
||||||
|
|
||||||
|
Calibration data is stored in battery-backed memory. So if a backup cell is
|
||||||
|
used the RTC will run accurately in the event of a power outage.
|
||||||
|
|
||||||
|
## 2.1 The DS3231 class
|
||||||
|
|
||||||
|
Constructor:
|
||||||
|
This takes one mandatory argument, a string identifying the Pyboard side in use
|
||||||
|
('X' or 'Y').
|
||||||
|
|
||||||
|
Public methods:
|
||||||
|
1. `get_time` Optional boolean arg `set_rtc=False`. If `set_rtc` is True it
|
||||||
|
sets the Pyboard's RTC from the DS3231. It returns the DS3231 time as a tuple
|
||||||
|
in the same format as `utime.localtime()` except that yday (day of year) is 0.
|
||||||
|
namely (year, month, day, hour, minute, second, wday, 0).
|
||||||
|
2. `save_time` No args. Sets the DS3231 time from the Pyboard's RTC.
|
||||||
|
3. `calibrate` Optional arg `minutes=5`. The time to run. This calculates the
|
||||||
|
calibration factor and applies it to the Pyboard. It returns the calibration
|
||||||
|
factor which may be stored in a file if the calibration needs to survive an
|
||||||
|
outage of all power sources.
|
|
@ -0,0 +1,119 @@
|
||||||
|
# Portable driver for DS3231 precison real time clock.
|
||||||
|
# Adapted from WiPy driver at https://github.com/scudderfish/uDS3231
|
||||||
|
# Includes routine to calibrate the Pyboard's RTC from the DS3231
|
||||||
|
# delta method now operates to 1mS precision
|
||||||
|
# precison of calibration further improved by timing Pyboard RTC transition
|
||||||
|
# Adapted by Peter Hinch, Feb 2017
|
||||||
|
|
||||||
|
import utime
|
||||||
|
import machine
|
||||||
|
import sys
|
||||||
|
DS3231_I2C_ADDR = 104
|
||||||
|
|
||||||
|
class DS3231Exception(OSError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
if sys.platform == 'pyboard':
|
||||||
|
import pyb
|
||||||
|
rtc = pyb.RTC()
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
rtc = machine.RTC()
|
||||||
|
except: # Official ESP32 port
|
||||||
|
print('warning: machine module does not support the RTC.')
|
||||||
|
rtc = None
|
||||||
|
|
||||||
|
def bcd2dec(bcd):
|
||||||
|
return (((bcd & 0xf0) >> 4) * 10 + (bcd & 0x0f))
|
||||||
|
|
||||||
|
def dec2bcd(dec):
|
||||||
|
tens, units = divmod(dec, 10)
|
||||||
|
return (tens << 4) + units
|
||||||
|
|
||||||
|
def tobytes(num):
|
||||||
|
return num.to_bytes(1, 'little')
|
||||||
|
|
||||||
|
class DS3231:
|
||||||
|
def __init__(self, i2c):
|
||||||
|
self.ds3231 = i2c
|
||||||
|
self.timebuf = bytearray(7)
|
||||||
|
if DS3231_I2C_ADDR not in self.ds3231.scan():
|
||||||
|
raise DS3231Exception("DS3231 not found on I2C bus at %d" % DS3231_I2C_ADDR)
|
||||||
|
|
||||||
|
def get_time(self, set_rtc = False):
|
||||||
|
if set_rtc:
|
||||||
|
self.await_transition() # For accuracy set RTC immediately after a seconds transition
|
||||||
|
else:
|
||||||
|
self.ds3231.readfrom_mem_into(DS3231_I2C_ADDR, 0, self.timebuf) # don't wait
|
||||||
|
return self.convert(set_rtc)
|
||||||
|
|
||||||
|
def convert(self, set_rtc=False): # Return a tuple in localtime() format (less yday)
|
||||||
|
data = self.timebuf
|
||||||
|
ss = bcd2dec(data[0])
|
||||||
|
mm = bcd2dec(data[1])
|
||||||
|
if data[2] & 0x40:
|
||||||
|
hh = bcd2dec(data[2] & 0x1f)
|
||||||
|
if data[2] & 0x20:
|
||||||
|
hh += 12
|
||||||
|
else:
|
||||||
|
hh = bcd2dec(data[2])
|
||||||
|
wday = data[3]
|
||||||
|
DD = bcd2dec(data[4])
|
||||||
|
MM = bcd2dec(data[5] & 0x1f)
|
||||||
|
YY = bcd2dec(data[6])
|
||||||
|
if data[5] & 0x80:
|
||||||
|
YY += 2000
|
||||||
|
else:
|
||||||
|
YY += 1900
|
||||||
|
# Time from DS3231 in time.localtime() format (less yday)
|
||||||
|
result = YY, MM, DD, hh, mm, ss, wday -1, 0
|
||||||
|
if set_rtc:
|
||||||
|
if rtc is None:
|
||||||
|
# Best we can do is to set local time
|
||||||
|
secs = utime.mktime(result)
|
||||||
|
utime.localtime(secs)
|
||||||
|
else:
|
||||||
|
if sys.platform == 'pyboard':
|
||||||
|
rtc.datetime((YY, MM, DD, wday, hh, mm, ss, 0))
|
||||||
|
else:
|
||||||
|
rtc.init((YY, MM, DD, hh, mm, ss))
|
||||||
|
return result
|
||||||
|
|
||||||
|
def save_time(self):
|
||||||
|
(YY, MM, mday, hh, mm, ss, wday, yday) = utime.localtime() # Based on RTC
|
||||||
|
self.ds3231.writeto_mem(DS3231_I2C_ADDR, 0, tobytes(dec2bcd(ss)))
|
||||||
|
self.ds3231.writeto_mem(DS3231_I2C_ADDR, 1, tobytes(dec2bcd(mm)))
|
||||||
|
self.ds3231.writeto_mem(DS3231_I2C_ADDR, 2, tobytes(dec2bcd(hh))) # Sets to 24hr mode
|
||||||
|
self.ds3231.writeto_mem(DS3231_I2C_ADDR, 3, tobytes(dec2bcd(wday + 1))) # 1 == Monday, 7 == Sunday
|
||||||
|
self.ds3231.writeto_mem(DS3231_I2C_ADDR, 4, tobytes(dec2bcd(mday))) # Day of month
|
||||||
|
if YY >= 2000:
|
||||||
|
self.ds3231.writeto_mem(DS3231_I2C_ADDR, 5, tobytes(dec2bcd(MM) | 0b10000000)) # Century bit
|
||||||
|
self.ds3231.writeto_mem(DS3231_I2C_ADDR, 6, tobytes(dec2bcd(YY-2000)))
|
||||||
|
else:
|
||||||
|
self.ds3231.writeto_mem(DS3231_I2C_ADDR, 5, tobytes(dec2bcd(MM)))
|
||||||
|
self.ds3231.writeto_mem(DS3231_I2C_ADDR, 6, tobytes(dec2bcd(YY-1900)))
|
||||||
|
|
||||||
|
# Wait until DS3231 seconds value changes before reading and returning data
|
||||||
|
def await_transition(self):
|
||||||
|
self.ds3231.readfrom_mem_into(DS3231_I2C_ADDR, 0, self.timebuf)
|
||||||
|
ss = self.timebuf[0]
|
||||||
|
while ss == self.timebuf[0]:
|
||||||
|
self.ds3231.readfrom_mem_into(DS3231_I2C_ADDR, 0, self.timebuf)
|
||||||
|
return self.timebuf
|
||||||
|
|
||||||
|
# Test hardware RTC against DS3231. Default runtime 10 min. Return amount
|
||||||
|
# by which DS3231 clock leads RTC in PPM or seconds per year.
|
||||||
|
# Precision is achieved by starting and ending the measurement on DS3231
|
||||||
|
# one-seond boundaries and using ticks_ms() to time the RTC.
|
||||||
|
# For a 10 minute measurement +-1ms corresponds to 1.7ppm or 53s/yr. Longer
|
||||||
|
# runtimes improve this, but the DS3231 is "only" good for +-2ppm over 0-40C.
|
||||||
|
def rtc_test(self, runtime=600, ppm=False):
|
||||||
|
factor = 1000000 if ppm else 31557600 # seconds per year
|
||||||
|
self.await_transition() # Start on transition
|
||||||
|
rtc_start = utime.ticks_ms() # and get RTC time NOW
|
||||||
|
ds3231_start = utime.mktime(self.convert())
|
||||||
|
utime.sleep(runtime) # Wait a while (precision doesn't matter)
|
||||||
|
self.await_transition()
|
||||||
|
d_rtc = utime.ticks_diff(utime.ticks_ms(), rtc_start)
|
||||||
|
d_ds3231 = 1000 * (utime.mktime(self.convert()) - ds3231_start)
|
||||||
|
return (d_ds3231 - d_rtc) * factor / d_ds3231
|
|
@ -0,0 +1,30 @@
|
||||||
|
# ds3231_port_test
|
||||||
|
# Test of portable driver for DS3231 precision RTC chip
|
||||||
|
|
||||||
|
from machine import Pin, I2C
|
||||||
|
import utime
|
||||||
|
from ds3231_port import DS3231
|
||||||
|
# Pyboard test
|
||||||
|
#from pyb import RTC
|
||||||
|
#rtc = RTC()
|
||||||
|
#rtc.datetime((2018, 1, 1, 1, 12, 0, 0, 0)) # Force incorrect setting
|
||||||
|
|
||||||
|
# In case pullups are absent.
|
||||||
|
#scl_pin = Pin('X2', pull=Pin.PULL_UP, mode=Pin.OPEN_DRAIN)
|
||||||
|
#sda_pin = Pin('X1', pull=Pin.PULL_UP, mode=Pin.OPEN_DRAIN)
|
||||||
|
scl_pin = Pin(19, pull=Pin.PULL_UP, mode=Pin.OPEN_DRAIN)
|
||||||
|
sda_pin = Pin(18, pull=Pin.PULL_UP, mode=Pin.OPEN_DRAIN)
|
||||||
|
i2c = I2C(-1, scl=scl_pin, sda=sda_pin)
|
||||||
|
ds3231 = DS3231(i2c)
|
||||||
|
|
||||||
|
print('Initial values')
|
||||||
|
print('DS3231 time:', ds3231.get_time())
|
||||||
|
print('RTC time:', utime.localtime())
|
||||||
|
|
||||||
|
print('Setting DS3231 from RTC')
|
||||||
|
ds3231.save_time() # Set DS3231 from RTC
|
||||||
|
print('DS3231 time:', ds3231.get_time())
|
||||||
|
print('RTC time:', utime.localtime())
|
||||||
|
|
||||||
|
print('Running RTC test for 2 mins')
|
||||||
|
print('RTC leads DS3231 by', ds3231.rtc_test(120, True), 'ppm')
|
129
README.md
129
README.md
|
@ -2,98 +2,10 @@
|
||||||
A place for assorted code ideas for MicroPython. Most are targeted at the
|
A place for assorted code ideas for MicroPython. Most are targeted at the
|
||||||
Pyboard variants.
|
Pyboard variants.
|
||||||
|
|
||||||
# fastbuild - Pull and build Pyboard firmware under Linux
|
# Fastbuild
|
||||||
These scripts are intended to speed and simplify rebuilding firmware from
|
|
||||||
source notably where pyboards of different types are in use, or when
|
|
||||||
frozen bytecode necessitates repeated compilation and deployment. In
|
|
||||||
particular ``buildpyb`` will detect the attached Pyboard type, build the
|
|
||||||
appropriate firmware, put the board into DFU mode and deploy it, before
|
|
||||||
launching ``rshell``. The latter step may be removed if ``rshell`` is not in
|
|
||||||
use.
|
|
||||||
|
|
||||||
The scripts should be run as your normal user and can proceed without user
|
Scripts for building MicroPython for various target hardware types and for
|
||||||
interaction.
|
updating your local source. See [docs](./fastbuild/README.md)
|
||||||
|
|
||||||
Includes udev rules to avoid jumps from ``/dev/ttyACM0`` to ``/dev/ttyACM1``
|
|
||||||
and ensuring Pyboards of all types appear as ``/dev/pyboard``. Rules are also
|
|
||||||
offered for USB connected WiPy (V1.0) and FTDI USB/serial adaptors.
|
|
||||||
|
|
||||||
These scripts use Python scripts ``pyb_boot`` to put the Pyboard into DFU mode
|
|
||||||
and ``pyb_check`` to determine the type of attached board. These use the
|
|
||||||
``pyboard.py`` module in the source tree to execute scripts on the attached
|
|
||||||
board.
|
|
||||||
|
|
||||||
### Optional Edits
|
|
||||||
|
|
||||||
In the ``buildpyb`` script you may wish to edit the ``-j 8`` argument to ``make``.
|
|
||||||
This radically speeds build on a multi core PC. Empirically 8 gave the fastest
|
|
||||||
build on my Core i7 4/8 core laptop: adjust to suit your PC. You may also want
|
|
||||||
to remove the call to ``rshell`` if you don't plan on using it.
|
|
||||||
|
|
||||||
This script defaults to a frozen modules directory ``stmhal/modules``. This may
|
|
||||||
be overridden by creating an environment variable FROZEN_DIR: a recent update
|
|
||||||
enabled the directory for frozen to be located anywhere in the filesystem,
|
|
||||||
allowing project specific directories.
|
|
||||||
|
|
||||||
In ``buildnew`` you may wish to delete the unix make commands.
|
|
||||||
|
|
||||||
### Dependencies and setup (on PC)
|
|
||||||
|
|
||||||
Python3
|
|
||||||
The following Bash code installs pyserial, copies ``49-micropython.rules`` to
|
|
||||||
(on most distros) ``/etc/udev/rules.d``. It installs ``rshell`` if you plan to
|
|
||||||
use it (recommended).
|
|
||||||
|
|
||||||
As root:
|
|
||||||
```
|
|
||||||
apt-get install python3-serial
|
|
||||||
pip install pyserial
|
|
||||||
cp 49-micropython.rules /etc/udev/rules.d
|
|
||||||
pip3 install rshell
|
|
||||||
```
|
|
||||||
|
|
||||||
Verify that ``pyboard.py`` works. To do this, close and restart the terminal
|
|
||||||
session. Run Python3, paste the following and check that the red LED lights:
|
|
||||||
|
|
||||||
```python
|
|
||||||
import os
|
|
||||||
mp = os.getenv('MPDIR')
|
|
||||||
sys.path.append(''.join((mp, '/tools')))
|
|
||||||
import pyboard
|
|
||||||
pyb = pyboard.Pyboard('/dev/pyboard')
|
|
||||||
pyb.enter_raw_repl()
|
|
||||||
pyb.exec('pyb.LED(1).on()')
|
|
||||||
pyb.exit_raw_repl()
|
|
||||||
```
|
|
||||||
|
|
||||||
The build scripts expect an environment variable MPDIR holding the path to the
|
|
||||||
MicroPython source tree. To set this up, as normal user issue (edited for your
|
|
||||||
path to the MicroPython source tree):
|
|
||||||
|
|
||||||
```
|
|
||||||
cd ~
|
|
||||||
echo export MPDIR='/mnt/qnap2/data/Projects/MicroPython/micropython' >> .bashrc
|
|
||||||
echo >> .bashrc
|
|
||||||
```
|
|
||||||
|
|
||||||
Close and restart the terminal session before running the scripts.
|
|
||||||
|
|
||||||
### Build script: ``buildpyb``
|
|
||||||
This checks the attached pyboard. If it's a V1.0, V1.1 or Lite it builds the
|
|
||||||
correct firmware and deploys it. Otherwise it produces an error message.
|
|
||||||
|
|
||||||
Optional argument ``--clean`` - if supplied does a ``make clean`` to delete
|
|
||||||
all files produced by the previous build before proceeding.
|
|
||||||
|
|
||||||
### Update source: ``buildnew``
|
|
||||||
|
|
||||||
Report state of master branch, update sources and issue ``make clean`` for
|
|
||||||
Pyboard variants, and ESP8266. Builds cross compiler and unix port.
|
|
||||||
|
|
||||||
### ESP8266 Build
|
|
||||||
|
|
||||||
``buildesp`` A script to build and deploy ESP8266 firmware. Accepts optional
|
|
||||||
``--clean`` argument.
|
|
||||||
|
|
||||||
# ssd1306
|
# ssd1306
|
||||||
|
|
||||||
|
@ -101,31 +13,44 @@ A means of rendering multiple larger fonts to the SSD1306 OLED display. See
|
||||||
[docs](./SSD1306/README.md).
|
[docs](./SSD1306/README.md).
|
||||||
|
|
||||||
# mutex
|
# mutex
|
||||||
A class providing mutual exclusion enabling interrupt handlers and the main program to access shared
|
|
||||||
data in a manner which ensures data integrity.
|
A class providing mutual exclusion enabling interrupt handlers and the main
|
||||||
|
program to access shared data in a manner which ensures data integrity.
|
||||||
|
|
||||||
# watchdog
|
# watchdog
|
||||||
|
|
||||||
Access the simpler of the Pyboard's watchdog timers.
|
Access the simpler of the Pyboard's watchdog timers.
|
||||||
|
|
||||||
# reverse
|
# reverse
|
||||||
|
|
||||||
Fast reverse a bytearray in Arm Thumb assembler.
|
Fast reverse a bytearray in Arm Thumb assembler.
|
||||||
Python code to bit-reverse (fast-ish) 8, 16 and 32 bit words.
|
Python code to bit-reverse (fast-ish) 8, 16 and 32 bit words.
|
||||||
|
|
||||||
# ds3231_pb
|
# DS3231
|
||||||
Driver for the DS3231 low cost precison RTC, including a facility to calibrate the Pyboard's RTC
|
|
||||||
|
This is a low cost precision battery backed real time clock (RTC) accurate to
|
||||||
|
+-2 minutes/year. Two drivers are provided, one portable across platforms and
|
||||||
|
one which is Pyboard specific.
|
||||||
|
|
||||||
|
The Pyboard-specific driver provides a facility to calibrate the Pyboard's RTC
|
||||||
from the DS3231. Calibration to high precision may be achieved in five minutes.
|
from the DS3231. Calibration to high precision may be achieved in five minutes.
|
||||||
|
|
||||||
|
The drivers are [documented here](./DS3231/README.md).
|
||||||
|
|
||||||
# Buildcheck
|
# Buildcheck
|
||||||
|
|
||||||
Raise an exception if a firmware build is earlier than a given date.
|
Raise an exception if a firmware build is earlier than a given date.
|
||||||
|
|
||||||
# timed_function
|
# timed_function
|
||||||
|
|
||||||
Time a function's execution using a decorator.
|
Time a function's execution using a decorator.
|
||||||
|
|
||||||
# ESP8266
|
# ESP8266 (MQTT benchmark)
|
||||||
benchmark.py Tests the performance of MQTT by periodically publishing while subscribed to
|
|
||||||
the same topic. Measures the round-trip delay. Adapt to suit your server address and desired
|
benchmark.py Tests the performance of MQTT by periodically publishing while
|
||||||
QOS (quality of service, 0 and 1 are supported). After 100 messages reports maximum and
|
subscribed to the same topic. Measures the round-trip delay. Adapt to suit your
|
||||||
minimum delays.
|
server address and desired QOS (quality of service, 0 and 1 are supported).
|
||||||
|
After 100 messages reports maximum and minimum delays.
|
||||||
|
|
||||||
conn.py Connect in station mode using saved connection details where possible.
|
conn.py Connect in station mode using saved connection details where possible.
|
||||||
|
|
||||||
|
@ -143,8 +68,8 @@ These were written for encoders producing TTL outputs. For switches, adapt the
|
||||||
pull definition to provide a pull up or pull down as required.
|
pull definition to provide a pull up or pull down as required.
|
||||||
|
|
||||||
The `encoder.portable.py` version should work on all MicroPython platforms.
|
The `encoder.portable.py` version should work on all MicroPython platforms.
|
||||||
Tested on ESP8266. Note that interrupt latency on the ESP8266 limits performance
|
Tested on ESP8266. Note that interrupt latency on the ESP8266 limits
|
||||||
(ESP32 is probably similar).
|
performance. ESP32 is similar.
|
||||||
|
|
||||||
# A pseudo random number generator
|
# A pseudo random number generator
|
||||||
|
|
||||||
|
|
|
@ -3,16 +3,25 @@
|
||||||
The official SSD1306 OLED display driver supports a single 8x8 pixel monospaced
|
The official SSD1306 OLED display driver supports a single 8x8 pixel monospaced
|
||||||
font. Users of the 128x64 displays in particular may wish to use larger fonts.
|
font. Users of the 128x64 displays in particular may wish to use larger fonts.
|
||||||
This provides a means of extending the official driver to support this. Suitable
|
This provides a means of extending the official driver to support this. Suitable
|
||||||
font files may be created from standard ``ttf`` or ``otf`` files using the utility
|
font files may be created from standard `ttf` or `otf` files using the utility
|
||||||
presented [here](https://github.com/peterhinch/micropython-font-to-py.git).
|
presented [here](https://github.com/peterhinch/micropython-font-to-py.git).
|
||||||
|
|
||||||
Requires firmware dated 1st Dec 2017 or later.
|
Requires firmware dated 1st Dec 2017 or later.
|
||||||
|
|
||||||
|
A few users have pointed out limitations in the `Writer` class. While it works
|
||||||
|
it is a rather minimal "proof of concept" for the font creator. PR's offering
|
||||||
|
enhancements will be gratefully considered.
|
||||||
|
|
||||||
|
The font file format is something of a "given" as it is used elsewhere. So any
|
||||||
|
PR requiring this to be changed is unlikely to find favour.
|
||||||
|
|
||||||
![Picture](ssd1306.JPG)
|
![Picture](ssd1306.JPG)
|
||||||
|
|
||||||
|
###### [Main README](./README.md)
|
||||||
|
|
||||||
## Release notes
|
## Release notes
|
||||||
|
|
||||||
V0.21 21st March 2017 The ``Writer`` class now uses the framebuf blit method.
|
V0.21 21st March 2017 The `Writer` class now uses the framebuf blit method.
|
||||||
This works for monochrome devices using 1-bit colour mapping. Example code is
|
This works for monochrome devices using 1-bit colour mapping. Example code is
|
||||||
provided for rendering to colour devices: the framebuf class does not yet offer
|
provided for rendering to colour devices: the framebuf class does not yet offer
|
||||||
an effective way to handle colour mapping when blitting between buffers with
|
an effective way to handle colour mapping when blitting between buffers with
|
||||||
|
@ -23,16 +32,16 @@ This is by design but see issue #2692.
|
||||||
|
|
||||||
# Files
|
# Files
|
||||||
|
|
||||||
1. ssd1306_test.py A simple test program.
|
1. ssd1306_test.py A simple test program.
|
||||||
2. ssd1306.py A snapshot of the current official driver.
|
2. ssd1306.py A snapshot of the current official driver.
|
||||||
3. writer.py A generic Writer class. Keeps track of the text insertion point
|
3. writer.py A generic Writer class. Keeps track of the text insertion point
|
||||||
over multiple fonts, handles newline and vertical scrolling if required.
|
over multiple fonts, handles newline and vertical scrolling if required.
|
||||||
|
|
||||||
In addition several font files are provided as samples.
|
In addition several font files are provided as samples.
|
||||||
|
|
||||||
# Getting started
|
# Getting started
|
||||||
|
|
||||||
The file ``ssd1306_test.py`` may need editing to match your hardware notably
|
The file `ssd1306_test.py` may need editing to match your hardware notably
|
||||||
the values of WIDTH and HEIGHT which are set to 128x64 (w*h) pixels. Wiring
|
the values of WIDTH and HEIGHT which are set to 128x64 (w*h) pixels. Wiring
|
||||||
details are included in code comments but may be changed if required as soft
|
details are included in code comments but may be changed if required as soft
|
||||||
I2C and SPI interfaces are specified.
|
I2C and SPI interfaces are specified.
|
||||||
|
@ -40,7 +49,7 @@ I2C and SPI interfaces are specified.
|
||||||
Its principal testing was performed on Pyboards but I'd expect it to be
|
Its principal testing was performed on Pyboards but I'd expect it to be
|
||||||
portable to any device supporting the official driver and the `machine` module.
|
portable to any device supporting the official driver and the `machine` module.
|
||||||
|
|
||||||
Copy files 1-3 and ``freesans20.py`` to the target and issue
|
Copy files 1-3 and `freesans20.py` to the target and issue
|
||||||
|
|
||||||
```python
|
```python
|
||||||
import ssd1306_test
|
import ssd1306_test
|
||||||
|
@ -54,8 +63,8 @@ Font files are converted to Python modules for ease of use and also (optionally)
|
||||||
to enable the modules to be frozen as bytecode to reduce RAM requirements.
|
to enable the modules to be frozen as bytecode to reduce RAM requirements.
|
||||||
|
|
||||||
The user program should import all fonts which are to be used and declare a
|
The user program should import all fonts which are to be used and declare a
|
||||||
``Writer`` instance for each one. Rendering text at the current insertion point
|
`Writer` instance for each one. Rendering text at the current insertion point
|
||||||
is then simply a matter of issuing the appropriate writer's ``printstring``
|
is then simply a matter of issuing the appropriate writer's `printstring`
|
||||||
method. After issuing all such calls required by your application the display
|
method. After issuing all such calls required by your application the display
|
||||||
should be updated by issuing
|
should be updated by issuing
|
||||||
|
|
||||||
|
@ -69,35 +78,35 @@ The principal interaction with the driver is via this class. One instance should
|
||||||
be created for each font in use. Its function is to keep track of the text
|
be created for each font in use. Its function is to keep track of the text
|
||||||
insertion point over successive uses with multiple fonts and to handle newline
|
insertion point over successive uses with multiple fonts and to handle newline
|
||||||
characters and vertical scrolling. Its behaviour when text overruns the end of
|
characters and vertical scrolling. Its behaviour when text overruns the end of
|
||||||
a line or the bottom of the screen may be controlled using its ``set_clip``
|
a line or the bottom of the screen may be controlled using its `set_clip`
|
||||||
method.
|
method.
|
||||||
|
|
||||||
## Methods
|
## Methods
|
||||||
|
|
||||||
1. ``Constructor`` This takes the ``ssd`` display instance and the font module
|
1. `Constructor` This takes the `ssd` display instance and the font module
|
||||||
as mandatory args.
|
as mandatory args.
|
||||||
2. ``printstring`` Takes a text string as argument and renders it at the current
|
2. `printstring` Takes a text string as argument and renders it at the current
|
||||||
insertion point. Respects newline characters.
|
insertion point. Respects newline characters.
|
||||||
|
|
||||||
## Class methods
|
## Class methods
|
||||||
|
|
||||||
1. ``set_textpos`` Mandatory integer args ``row``, ``col`` defined in pixels
|
1. `set_textpos` Mandatory integer args `row`, `col` defined in pixels
|
||||||
relative to the top left hand corner of the display. Sets the current text
|
relative to the top left hand corner of the display. Sets the current text
|
||||||
insertion point. The coordinates of a glyph refer to its top left corner. The
|
insertion point. The coordinates of a glyph refer to its top left corner. The
|
||||||
initial default is (0,0) with text being rendered at the top left of the display.
|
initial default is (0,0) with text being rendered at the top left of the display.
|
||||||
2. ``set_clip`` Mandatory boolean args ``row_clip``, ``col_clip``. These define
|
2. `set_clip` Mandatory boolean args `row_clip`, `col_clip`. These define
|
||||||
behaviour when text overruns the physical width or height of the display. By
|
behaviour when text overruns the physical width or height of the display. By
|
||||||
default text overrunning the display width will continue on the next row. Setting
|
default text overrunning the display width will continue on the next row. Setting
|
||||||
``col_clip`` overrides this such that overrunning text is lost. Similarly, by
|
`col_clip` overrides this such that overrunning text is lost. Similarly, by
|
||||||
default text overrunning the bottom of the display will cause text above to
|
default text overrunning the bottom of the display will cause text above to
|
||||||
scroll up to accommodate it. Setting ``row_clip`` will override this behaviour
|
scroll up to accommodate it. Setting `row_clip` will override this behaviour
|
||||||
causing text to be clipped.
|
causing text to be clipped.
|
||||||
|
|
||||||
# Use of font_to_py.py
|
# Use of font_to_py.py
|
||||||
|
|
||||||
To convert font files to Python for use with this driver the default (vertical)
|
To convert font files to Python for use with this driver the default (vertical)
|
||||||
mapping and bit order should be used. The only optional argument which may be
|
mapping and bit order should be used. The only optional argument which may be
|
||||||
needed is ``-f`` if fixed-width rendering is desired.
|
needed is `-f` if fixed-width rendering is desired.
|
||||||
|
|
||||||
# License
|
# License
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
# fastbuild - Pull and build Pyboard firmware under Linux
|
||||||
|
|
||||||
|
These scripts are intended to speed and simplify rebuilding firmware from
|
||||||
|
source notably where pyboards of different types are in use, or when
|
||||||
|
frozen bytecode necessitates repeated compilation and deployment. In
|
||||||
|
particular `buildpyb` will detect the attached Pyboard type, build the
|
||||||
|
appropriate firmware, put the board into DFU mode and deploy it, before
|
||||||
|
launching `rshell`. The latter step may be removed if `rshell` is not in
|
||||||
|
use.
|
||||||
|
|
||||||
|
The scripts should be run as your normal user and can proceed without user
|
||||||
|
interaction.
|
||||||
|
|
||||||
|
Includes udev rules to avoid jumps from `/dev/ttyACM0` to `/dev/ttyACM1`
|
||||||
|
and ensuring Pyboards of all types appear as `/dev/pyboard`. Rules are also
|
||||||
|
offered for USB connected WiPy (V1.0) and FTDI USB/serial adaptors.
|
||||||
|
|
||||||
|
These scripts use Python scripts `pyb_boot` to put the Pyboard into DFU mode
|
||||||
|
and `pyb_check` to determine the type of attached board. These use the
|
||||||
|
`pyboard.py` module in the source tree to execute scripts on the attached
|
||||||
|
board.
|
||||||
|
|
||||||
|
###### [Main README](./README.md)
|
||||||
|
|
||||||
|
### Optional Edits
|
||||||
|
|
||||||
|
In the `buildpyb` script you may wish to edit the `-j 8` argument to `make`.
|
||||||
|
This radically speeds build on a multi core PC. Empirically 8 gave the fastest
|
||||||
|
build on my Core i7 4/8 core laptop: adjust to suit your PC. You may also want
|
||||||
|
to remove the call to `rshell` if you don't plan on using it.
|
||||||
|
|
||||||
|
This script defaults to a frozen modules directory `stmhal/modules`. This may
|
||||||
|
be overridden by creating an environment variable FROZEN_DIR: a recent update
|
||||||
|
enabled the directory for frozen to be located anywhere in the filesystem,
|
||||||
|
allowing project specific directories.
|
||||||
|
|
||||||
|
In `buildnew` you may wish to delete the unix make commands.
|
||||||
|
|
||||||
|
### Dependencies and setup (on PC)
|
||||||
|
|
||||||
|
Python3
|
||||||
|
The following Bash code installs pyserial, copies `49-micropython.rules` to
|
||||||
|
(on most distros) `/etc/udev/rules.d`. It installs `rshell` if you plan to
|
||||||
|
use it (recommended).
|
||||||
|
|
||||||
|
As root:
|
||||||
|
```
|
||||||
|
apt-get install python3-serial
|
||||||
|
pip install pyserial
|
||||||
|
cp 49-micropython.rules /etc/udev/rules.d
|
||||||
|
pip3 install rshell
|
||||||
|
```
|
||||||
|
|
||||||
|
Verify that `pyboard.py` works. To do this, close and restart the terminal
|
||||||
|
session. Run Python3, paste the following and check that the red LED lights:
|
||||||
|
|
||||||
|
```python
|
||||||
|
import os
|
||||||
|
mp = os.getenv('MPDIR')
|
||||||
|
sys.path.append(''.join((mp, '/tools')))
|
||||||
|
import pyboard
|
||||||
|
pyb = pyboard.Pyboard('/dev/pyboard')
|
||||||
|
pyb.enter_raw_repl()
|
||||||
|
pyb.exec('pyb.LED(1).on()')
|
||||||
|
pyb.exit_raw_repl()
|
||||||
|
```
|
||||||
|
|
||||||
|
The build scripts expect an environment variable MPDIR holding the path to the
|
||||||
|
MicroPython source tree. To set this up, as normal user issue (edited for your
|
||||||
|
path to the MicroPython source tree):
|
||||||
|
|
||||||
|
```
|
||||||
|
cd ~
|
||||||
|
echo export MPDIR='/mnt/qnap2/data/Projects/MicroPython/micropython' >> .bashrc
|
||||||
|
echo >> .bashrc
|
||||||
|
```
|
||||||
|
|
||||||
|
Close and restart the terminal session before running the scripts.
|
||||||
|
|
||||||
|
### Build script: `buildpyb`
|
||||||
|
|
||||||
|
This checks the attached pyboard. If it's a V1.0, V1.1 or Lite it builds the
|
||||||
|
correct firmware and deploys it. Otherwise it produces an error message.
|
||||||
|
|
||||||
|
Optional argument `--clean` - if supplied does a `make clean` to delete
|
||||||
|
all files produced by the previous build before proceeding.
|
||||||
|
|
||||||
|
### Update source: `buildnew`
|
||||||
|
|
||||||
|
Report state of master branch, update sources and issue `make clean` for
|
||||||
|
Pyboard variants and ESP8266. Builds cross compiler and unix port.
|
||||||
|
|
||||||
|
### ESP8266 Build
|
||||||
|
|
||||||
|
`buildesp` A script to build and deploy ESP8266 firmware. Accepts optional
|
||||||
|
`--clean` argument.
|
Ładowanie…
Reference in New Issue