Initial backup of LTP-305G matrix clock setup on matrixpi
Captures everything needed to redeploy the two-display clock (hour on I2C 0x61, minute on I2C 0x63) on a fresh Pi: - Both systemd units (matrix0x61.service, matrix0x63.service) - Deployed Pimoroni script tree, including the local %I (12-hour) clock customization - Vendored upstream sources (ltp305-python, breakout-garden) so restore is fully offline-capable - Boot config snippet enabling I2C - install.sh that wires it all back up idempotently - Inventory doc cross-referencing every live-system path Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
.DS_Store
|
||||||
62
README.md
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
# matrix-pi
|
||||||
|
|
||||||
|
Backup of the Pimoroni LTP-305G LED matrix display setup running on `matrixpi`
|
||||||
|
under the `dissimulo` user. Two LTP-305G modules are wired to the I2C bus and
|
||||||
|
display the current time as a clock — hour on the left module (I2C `0x61`),
|
||||||
|
minute on the right (I2C `0x63`).
|
||||||
|
|
||||||
|
## Hardware
|
||||||
|
|
||||||
|
- Raspberry Pi (running Debian Bookworm, kernel 6.6.x)
|
||||||
|
- 2 × Pimoroni LTP-305G breakouts on I2C bus 1
|
||||||
|
- `0x61` — hour digits
|
||||||
|
- `0x63` — minute digits
|
||||||
|
|
||||||
|
## What's in here
|
||||||
|
|
||||||
|
| Path | Purpose |
|
||||||
|
| --- | --- |
|
||||||
|
| `systemd/` | The two systemd units that run the clock at boot |
|
||||||
|
| `deployed/` | Mirror of the on-disk script tree the units invoke (includes the local `%I` 12-hour customization to `clock.py`) |
|
||||||
|
| `vendor/ltp305-python/` | Vendored copy of the Pimoroni `ltp305` Python library (offline install fallback) |
|
||||||
|
| `vendor/breakout-garden/` | Vendored copy of Pimoroni's Breakout Garden tooling (used for I2C autodetect) |
|
||||||
|
| `config/boot-firmware-config.txt.snippet` | The `/boot/firmware/config.txt` lines that enable I2C |
|
||||||
|
| `install.sh` | Idempotent restore script |
|
||||||
|
| `docs/inventory.md` | Source-of-truth inventory of every LTP-305G-related file on the live system |
|
||||||
|
|
||||||
|
## Restoring on a fresh Pi
|
||||||
|
|
||||||
|
1. Flash Raspberry Pi OS (Bookworm or later), create user `dissimulo`.
|
||||||
|
2. Ensure I2C is enabled — either via `raspi-config` or by appending the line
|
||||||
|
from `config/boot-firmware-config.txt.snippet` to `/boot/firmware/config.txt`
|
||||||
|
and rebooting.
|
||||||
|
3. Wire the two LTP-305G breakouts; confirm with `i2cdetect -y 1` that they
|
||||||
|
appear at `0x61` and `0x63`. (Install `i2c-tools` if missing.)
|
||||||
|
4. From a clone of this repo, run:
|
||||||
|
|
||||||
|
```
|
||||||
|
sudo ./install.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
This installs the `ltp305` Python package from `vendor/`, drops the
|
||||||
|
customized scripts into `/home/dissimulo/Pimoroni/ltp305/`, and enables both
|
||||||
|
systemd units.
|
||||||
|
|
||||||
|
5. Verify:
|
||||||
|
|
||||||
|
```
|
||||||
|
systemctl status matrix0x61.service matrix0x63.service
|
||||||
|
```
|
||||||
|
|
||||||
|
## Local customization vs. upstream
|
||||||
|
|
||||||
|
The deployed `clock.py` differs from the upstream Pimoroni `ltp305-python`
|
||||||
|
example by one line — the `hour` format is `%I` (12-hour) instead of `%H`
|
||||||
|
(24-hour). This is preserved in `deployed/home/dissimulo/Pimoroni/ltp305/examples/clock.py`.
|
||||||
|
|
||||||
|
## Upstream provenance
|
||||||
|
|
||||||
|
Vendored sources were captured from these commits:
|
||||||
|
|
||||||
|
- `pimoroni/ltp305-python` @ `bf859fe6cddf8cffcb12d62bad8f21e0bbb9fc42`
|
||||||
|
- `pimoroni/breakout-garden` @ `9c331458ee8c4732dbc783a6409ef53d671d321b`
|
||||||
3
config/boot-firmware-config.txt.snippet
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
dtparam=i2c_arm=on
|
||||||
|
dtoverlay=vc4-kms-v3d
|
||||||
|
dtoverlay=dwc2,dr_mode=host
|
||||||
126
deployed/home/dissimulo/Pimoroni/ltp305/docs.html
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Python: package ltp305</title>
|
||||||
|
</head><body>
|
||||||
|
|
||||||
|
<table class="heading">
|
||||||
|
<tr class="heading-text decor">
|
||||||
|
<td class="title"> <br><strong class="title">ltp305</strong> (version 0.0.1)</td>
|
||||||
|
<td class="extra"><a href=".">index</a><br><a href="file:/usr/local/lib/python3.11/dist-packages/ltp305/__init__.py">/usr/local/lib/python3.11/dist-packages/ltp305/__init__.py</a></td></tr></table>
|
||||||
|
<p></p>
|
||||||
|
<p>
|
||||||
|
<table class="section">
|
||||||
|
<tr class="decor pkg-content-decor heading-text">
|
||||||
|
<td class="section-title" colspan=3> <br><strong class="bigsection">Package Contents</strong></td></tr>
|
||||||
|
|
||||||
|
<tr><td class="decor pkg-content-decor"><span class="code"> </span></td><td> </td>
|
||||||
|
<td class="singlecolumn"><table><tr><td class="multicolumn"><a href="ltp305.font.html">font</a><br>
|
||||||
|
</td><td class="multicolumn"></td><td class="multicolumn"></td><td class="multicolumn"></td></tr></table></td></tr></table><p>
|
||||||
|
<table class="section">
|
||||||
|
<tr class="decor index-decor heading-text">
|
||||||
|
<td class="section-title" colspan=3> <br><strong class="bigsection">Classes</strong></td></tr>
|
||||||
|
|
||||||
|
<tr><td class="decor index-decor"><span class="code"> </span></td><td> </td>
|
||||||
|
<td class="singlecolumn"><dl>
|
||||||
|
<dt class="heading-text"><a href="builtins.html#object">builtins.object</a>
|
||||||
|
</dt><dd>
|
||||||
|
<dl>
|
||||||
|
<dt class="heading-text"><a href="ltp305.html#LTP305">LTP305</a>
|
||||||
|
</dt></dl>
|
||||||
|
</dd>
|
||||||
|
</dl>
|
||||||
|
<p>
|
||||||
|
<table class="section">
|
||||||
|
<tr class="decor title-decor heading-text">
|
||||||
|
<td class="section-title" colspan=3> <br><a name="LTP305">class <strong>LTP305</strong></a>(<a href="builtins.html#object">builtins.object</a>)</td></tr>
|
||||||
|
|
||||||
|
<tr><td class="decor title-decor" rowspan=2><span class="code"> </span></td>
|
||||||
|
<td class="decor title-decor" colspan=2><span class="code"><a href="#LTP305">LTP305</a>(address=97, brightness=0.5)<br>
|
||||||
|
<br>
|
||||||
|
_buf_matrix_left = [<br>
|
||||||
|
# Row 7654321<br>
|
||||||
|
0b01111111, # col 1, bottom = msb<br>
|
||||||
|
0b01111111, # col 2<br>
|
||||||
|
0b01111111, # col 3<br>
|
||||||
|
0b01111111, # col 4<br>
|
||||||
|
0b01111111, # col 5<br>
|
||||||
|
0b00000000,<br>
|
||||||
|
0b00000000,<br>
|
||||||
|
0b01000000 # bit 7 = decimal dot<br>
|
||||||
|
]<br>
|
||||||
|
<br>
|
||||||
|
_buf_matrix_right = [<br>
|
||||||
|
# Col 12345<br>
|
||||||
|
0b00011111, # row 1<br>
|
||||||
|
0b00011111, # row 2<br>
|
||||||
|
0b00011111, # row 3<br>
|
||||||
|
0b00011111, # row 4<br>
|
||||||
|
0b00011111, # row 5<br>
|
||||||
|
0b00011111, # row 6<br>
|
||||||
|
0b10011111, # row 7 + bit 8 = decimal dot<br>
|
||||||
|
0b00000000<br>
|
||||||
|
]<br> </span></td></tr>
|
||||||
|
<tr><td> </td>
|
||||||
|
<td class="singlecolumn">Methods defined here:<br>
|
||||||
|
<dl><dt><a name="LTP305-__init__"><strong>__init__</strong></a>(self, address=97, brightness=0.5)</dt><dd><span class="code"><a href="#LTP305">LTP305</a> 5x7 x 2 Matrix Driver<br>
|
||||||
|
<br>
|
||||||
|
:param address: i2c address, one of 0x61, 0x62 or 0x63 (default 0x61)<br>
|
||||||
|
:param brightness: LED brightness from 0.0 to 1.0 (default 0.5)</span></dd></dl>
|
||||||
|
|
||||||
|
<dl><dt><a name="LTP305-clear"><strong>clear</strong></a>(self)</dt><dd><span class="code">Clear both LED matrices.<br>
|
||||||
|
<br>
|
||||||
|
Must call .<a href="#LTP305-show">show</a>() to display changes.</span></dd></dl>
|
||||||
|
|
||||||
|
<dl><dt><a name="LTP305-get_shape"><strong>get_shape</strong></a>(self)</dt><dd><span class="code">Set the width/height of the display.</span></dd></dl>
|
||||||
|
|
||||||
|
<dl><dt><a name="LTP305-set_brightness"><strong>set_brightness</strong></a>(self, brightness, update=False)</dt><dd><span class="code">Set brightness of both LED matrices.<br>
|
||||||
|
<br>
|
||||||
|
:param brightnes: LED brightness from 0.0 to 1.0<br>
|
||||||
|
:param update: Push change to display immediately (otherwise you must call .<a href="#LTP305-show">show</a>())</span></dd></dl>
|
||||||
|
|
||||||
|
<dl><dt><a name="LTP305-set_character"><strong>set_character</strong></a>(self, x, char)</dt><dd><span class="code">Set a single character.<br>
|
||||||
|
<br>
|
||||||
|
:param x: x position, 0 for left, 5 for right, or in between if you fancy<br>
|
||||||
|
:param char: string character or char ordinal</span></dd></dl>
|
||||||
|
|
||||||
|
<dl><dt><a name="LTP305-set_decimal"><strong>set_decimal</strong></a>(self, left=None, right=None)</dt><dd><span class="code">Set decimal of left and/or right matrix.<br>
|
||||||
|
<br>
|
||||||
|
:param left: State of left decimal dot<br>
|
||||||
|
:param right: State of right decimal dot</span></dd></dl>
|
||||||
|
|
||||||
|
<dl><dt><a name="LTP305-set_image"><strong>set_image</strong></a>(self, image, offset_x=0, offset_y=0, wrap=False, bg=0)</dt><dd><span class="code">Set a PIL image to the display buffer.</span></dd></dl>
|
||||||
|
|
||||||
|
<dl><dt><a name="LTP305-set_pixel"><strong>set_pixel</strong></a>(self, x, y, c)</dt><dd><span class="code">Set a single pixel on the matrix.<br>
|
||||||
|
<br>
|
||||||
|
:param x: x position from 0 to 9 (0-4 on left matrix, 5-9 on right)<br>
|
||||||
|
:param y: y position<br>
|
||||||
|
:param c: state on/off</span></dd></dl>
|
||||||
|
|
||||||
|
<dl><dt><a name="LTP305-show"><strong>show</strong></a>(self)</dt><dd><span class="code">Update the LED matrixes from the buffer.</span></dd></dl>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
Data descriptors defined here:<br>
|
||||||
|
<dl><dt><strong>__dict__</strong></dt>
|
||||||
|
<dd><span class="code">dictionary for instance variables (if defined)</span></dd>
|
||||||
|
</dl>
|
||||||
|
<dl><dt><strong>__weakref__</strong></dt>
|
||||||
|
<dd><span class="code">list of weak references to the object (if defined)</span></dd>
|
||||||
|
</dl>
|
||||||
|
</td></tr></table></td></tr></table><p>
|
||||||
|
<table class="section">
|
||||||
|
<tr class="decor data-decor heading-text">
|
||||||
|
<td class="section-title" colspan=3> <br><strong class="bigsection">Data</strong></td></tr>
|
||||||
|
|
||||||
|
<tr><td class="decor data-decor"><span class="code"> </span></td><td> </td>
|
||||||
|
<td class="singlecolumn"><strong>CMD_BRIGHTNESS</strong> = 25<br>
|
||||||
|
<strong>CMD_MATRIX_L</strong> = 14<br>
|
||||||
|
<strong>CMD_MATRIX_R</strong> = 1<br>
|
||||||
|
<strong>CMD_MODE</strong> = 0<br>
|
||||||
|
<strong>CMD_OPTIONS</strong> = 13<br>
|
||||||
|
<strong>CMD_UPDATE</strong> = 12<br>
|
||||||
|
<strong>MODE</strong> = 24<br>
|
||||||
|
<strong>OPTS</strong> = 14<br>
|
||||||
|
<strong>font</strong> = {32: [0, 0, 0, 0, 0], 33: [0, 0, 95, 0, 0], 34: [0, 7, 0, 7, 0], 35: [20, 127, 20, 127, 20], 36: [36, 42, 127, 42, 18], 37: [35, 19, 8, 100, 98], 38: [54, 73, 85, 34, 80], 39: [0, 5, 3, 0, 0], 40: [0, 28, 34, 65, 0], 41: [0, 65, 34, 28, 0], ...}</td></tr></table>
|
||||||
|
</body></html>
|
||||||
43
deployed/home/dissimulo/Pimoroni/ltp305/examples/bargraph.py
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import time
|
||||||
|
import math
|
||||||
|
import random
|
||||||
|
from ltp305 import LTP305
|
||||||
|
|
||||||
|
|
||||||
|
print("""bargraph.py - A basic graph example
|
||||||
|
|
||||||
|
Displays an animated rising/falling bar on the left matrix,
|
||||||
|
and a scrolling bar graph on the right.
|
||||||
|
|
||||||
|
Press Ctrl+C to exit!
|
||||||
|
|
||||||
|
""")
|
||||||
|
|
||||||
|
display = LTP305()
|
||||||
|
width, height = display.get_shape()
|
||||||
|
|
||||||
|
values = [0, 0, 0, 0, 0]
|
||||||
|
|
||||||
|
|
||||||
|
while True:
|
||||||
|
# Add a new random value to our list and prune the list to visible values
|
||||||
|
values.insert(0, random.randint(0, height))
|
||||||
|
values = values[:width]
|
||||||
|
|
||||||
|
# Animate a value from 0 to height + 1
|
||||||
|
value = (math.sin(time.time() * math.pi) + 1) / 2.0
|
||||||
|
value *= height + 1
|
||||||
|
value = math.floor(value)
|
||||||
|
|
||||||
|
for y in range(height):
|
||||||
|
y = height - 1 - y
|
||||||
|
|
||||||
|
for x in range(width // 2):
|
||||||
|
# Left
|
||||||
|
display.set_pixel(x, y, value <= y)
|
||||||
|
|
||||||
|
# Right
|
||||||
|
display.set_pixel(x + (width // 2), y, values[x] <= y)
|
||||||
|
|
||||||
|
time.sleep(1.0 / height)
|
||||||
|
display.show()
|
||||||
50
deployed/home/dissimulo/Pimoroni/ltp305/examples/clock.py
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
import sys
|
||||||
|
import time
|
||||||
|
from ltp305 import LTP305
|
||||||
|
|
||||||
|
|
||||||
|
print("""clock.py - clock segment example.
|
||||||
|
|
||||||
|
Usage: python3 clock.py <hour/minute/second> <i2c-address>
|
||||||
|
|
||||||
|
This simple clock example will display the hour, minute or second on a single matrix display breakout.
|
||||||
|
|
||||||
|
Press Ctrl+C to exit.
|
||||||
|
|
||||||
|
""")
|
||||||
|
|
||||||
|
fmt = "%M"
|
||||||
|
address = 0x61
|
||||||
|
available = {
|
||||||
|
"hour": "%I",
|
||||||
|
"minute": "%M",
|
||||||
|
"second": "%S"
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(sys.argv) > 1:
|
||||||
|
try:
|
||||||
|
fmt = available[sys.argv[1]]
|
||||||
|
except KeyError:
|
||||||
|
raise ValueError("{} is not supported!".format(sys.argv[1]))
|
||||||
|
|
||||||
|
if len(sys.argv) > 2:
|
||||||
|
address = int(sys.argv[2], 16)
|
||||||
|
if address not in [0x61, 0x62, 0x63]:
|
||||||
|
raise ValueError("Invalid i2c address: 0x{:02x}. Run `i2c-detect -y 1` to discover breakouts.".format(address))
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
display = LTP305(address=address)
|
||||||
|
display.clear()
|
||||||
|
display.show()
|
||||||
|
except OSError:
|
||||||
|
raise OSError("Unable to find LTP305 on i2c address: 0x{:02x}. Run `i2c-detect -y 1` to discover breakouts".format(address))
|
||||||
|
|
||||||
|
|
||||||
|
while True:
|
||||||
|
minute = time.strftime(fmt)
|
||||||
|
left, right = minute
|
||||||
|
display.set_character(0, left)
|
||||||
|
display.set_character(5, right)
|
||||||
|
display.show()
|
||||||
|
time.sleep(1.0 / 60)
|
||||||
49
deployed/home/dissimulo/Pimoroni/ltp305/examples/eyes.py
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import math
|
||||||
|
import time
|
||||||
|
import random
|
||||||
|
|
||||||
|
|
||||||
|
print("""eyes.py - They're watching you!
|
||||||
|
|
||||||
|
Press Ctrl+C to exit!
|
||||||
|
|
||||||
|
""")
|
||||||
|
|
||||||
|
|
||||||
|
from ltp305 import LTP305
|
||||||
|
|
||||||
|
display = LTP305()
|
||||||
|
|
||||||
|
|
||||||
|
def eye(x, y):
|
||||||
|
display.set_pixel(x, y, True)
|
||||||
|
display.set_pixel(x, y + 1, True)
|
||||||
|
display.set_pixel(x + 1, y, True)
|
||||||
|
display.set_pixel(x + 1, y + 1, True)
|
||||||
|
|
||||||
|
|
||||||
|
while True:
|
||||||
|
t = time.time() * math.pi
|
||||||
|
|
||||||
|
# Get eye x and y positions in the range 0.0 to 1.0
|
||||||
|
# You can plug in your own 0.0 to 1.0 values here
|
||||||
|
x = (math.sin(t / 2) + 1) / 2
|
||||||
|
y = (math.sin(t / 4) + 1) / 2
|
||||||
|
|
||||||
|
# Multiply them up to display coords and convert to itn
|
||||||
|
x = math.floor(x * 4)
|
||||||
|
y = math.floor(y * 6)
|
||||||
|
|
||||||
|
# Clear the display
|
||||||
|
display.clear()
|
||||||
|
|
||||||
|
# Blink occasionally
|
||||||
|
if not random.randint(0, 20) == 2:
|
||||||
|
# Draw the eyes if not blinking
|
||||||
|
eye(x, y)
|
||||||
|
eye(x + 5, y)
|
||||||
|
|
||||||
|
# Update the display at 10FPS
|
||||||
|
# This gives our crude blink code time to not look like a random flicker
|
||||||
|
display.show()
|
||||||
|
time.sleep(1.0 / 10)
|
||||||
5
deployed/home/dissimulo/Pimoroni/ltp305/uninstall.sh
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
printf "It's recommended you run these steps manually.\n"
|
||||||
|
printf "If you want to run the full script, open it in\n"
|
||||||
|
printf "an editor and remove 'exit 1' from below.\n"
|
||||||
|
exit 1
|
||||||
|
rm -r /home/dissimulo/Pimoroni/ltp305
|
||||||
41
docs/inventory.md
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
# LTP-305G live-system inventory
|
||||||
|
|
||||||
|
Snapshot of every LTP-305G-related path on `matrixpi` at the time this repo
|
||||||
|
was created. Use this to spot drift if anything is changed on the live system
|
||||||
|
without being committed back here.
|
||||||
|
|
||||||
|
## Systemd units (root-owned)
|
||||||
|
|
||||||
|
- `/etc/systemd/system/matrix0x61.service` — runs `clock.py hour 0x61`
|
||||||
|
- `/etc/systemd/system/matrix0x63.service` — runs `clock.py minute 0x63`
|
||||||
|
|
||||||
|
Both have `WantedBy=multi-user.target` and are `enabled`.
|
||||||
|
|
||||||
|
## Deployed scripts (under `dissimulo`)
|
||||||
|
|
||||||
|
- `/home/dissimulo/Pimoroni/ltp305/`
|
||||||
|
- `docs.html`
|
||||||
|
- `uninstall.sh`
|
||||||
|
- `examples/bargraph.py`
|
||||||
|
- `examples/clock.py` — **locally modified**: `"hour": "%I"` (12-hour) instead of upstream `"%H"`
|
||||||
|
- `examples/eyes.py`
|
||||||
|
|
||||||
|
## Upstream library checkouts
|
||||||
|
|
||||||
|
- `/home/dissimulo/ltp305-python/` — clone of `pimoroni/ltp305-python`, used at install time
|
||||||
|
- `/home/dissimulo/breakout-garden/` — clone of `pimoroni/breakout-garden`, used for I2C autodetect
|
||||||
|
|
||||||
|
## Installed Python package
|
||||||
|
|
||||||
|
- `/usr/local/lib/python3.11/dist-packages/ltp305/__init__.py`
|
||||||
|
- `/usr/local/lib/python3.11/dist-packages/ltp305-0.0.1.dist-info/`
|
||||||
|
|
||||||
|
## Boot configuration
|
||||||
|
|
||||||
|
- `/boot/firmware/config.txt` contains `dtparam=i2c_arm=on` enabling the I2C bus the modules sit on.
|
||||||
|
|
||||||
|
## I2C bus state
|
||||||
|
|
||||||
|
`i2cdetect` is not currently installed on this system. Once `i2c-tools` is
|
||||||
|
installed, expect `0x61` and `0x63` to appear on bus 1 when both LTP-305G
|
||||||
|
breakouts are connected and powered.
|
||||||
39
install.sh
Executable file
@@ -0,0 +1,39 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Restore the LTP-305G matrix clock setup on a fresh Pi.
|
||||||
|
# Run as root (or with sudo). Idempotent.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
TARGET_USER="${TARGET_USER:-dissimulo}"
|
||||||
|
TARGET_HOME="$(getent passwd "$TARGET_USER" | cut -d: -f6)"
|
||||||
|
|
||||||
|
if [[ -z "$TARGET_HOME" ]]; then
|
||||||
|
echo "User '$TARGET_USER' not found. Create it first or set TARGET_USER." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $EUID -ne 0 ]]; then
|
||||||
|
echo "Run as root (sudo)." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "==> Installing ltp305 Python package from vendored source"
|
||||||
|
pip3 install --break-system-packages "$REPO_DIR/vendor/ltp305-python/library"
|
||||||
|
|
||||||
|
echo "==> Deploying script tree to $TARGET_HOME/Pimoroni/ltp305"
|
||||||
|
mkdir -p "$TARGET_HOME/Pimoroni"
|
||||||
|
cp -r "$REPO_DIR/deployed/home/dissimulo/Pimoroni/ltp305" "$TARGET_HOME/Pimoroni/"
|
||||||
|
chown -R "$TARGET_USER:$TARGET_USER" "$TARGET_HOME/Pimoroni"
|
||||||
|
|
||||||
|
echo "==> Installing systemd units"
|
||||||
|
install -m 0644 "$REPO_DIR/systemd/matrix0x61.service" /etc/systemd/system/matrix0x61.service
|
||||||
|
install -m 0644 "$REPO_DIR/systemd/matrix0x63.service" /etc/systemd/system/matrix0x63.service
|
||||||
|
|
||||||
|
echo "==> Reloading systemd and enabling units"
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl enable --now matrix0x61.service matrix0x63.service
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "Done. Status:"
|
||||||
|
systemctl --no-pager status matrix0x61.service matrix0x63.service || true
|
||||||
8
systemd/matrix0x61.service
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Matrix LED Clock 0x61
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
ExecStart=python3 /home/dissimulo/Pimoroni/ltp305/examples/clock.py hour 0x61
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
8
systemd/matrix0x63.service
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Matrix LED Clock 0x63
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
ExecStart=python3 /home/dissimulo/Pimoroni/ltp305/examples/clock.py minute 0x63
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
21
vendor/breakout-garden/LICENSE
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2018 Pimoroni Ltd.
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
29
vendor/breakout-garden/README.md
vendored
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# Breakout Garden
|
||||||
|
|
||||||
|
Software and examples for the Pimoroni [Breakout Garden](https://shop.pimoroni.com/products/breakout-garden-hat-i2c-spi),
|
||||||
|
a simple way to build projects using [breakouts](https://shop.pimoroni.com/collections/pimoroni-breakouts), with
|
||||||
|
absolutely no soldering required.
|
||||||
|
|
||||||
|
## Installing
|
||||||
|
|
||||||
|
Breakout Garden doesn't require any software of its own, but each breakout you use will need a Python library.
|
||||||
|
|
||||||
|
We've created a handy script to get you started:
|
||||||
|
|
||||||
|
1. Plug in all of your Breakout Garden-compatible breakouts
|
||||||
|
2. Run `wget https://github.com/pimoroni/breakout-garden/archive/master.zip` to download software.
|
||||||
|
3. Run `unzip master.zip` to unzip the software.
|
||||||
|
4. Run `cd breakout-garden-master` to enter code folder.
|
||||||
|
5. Run `sudo ./install.sh`
|
||||||
|
6. Step through the install process
|
||||||
|
7. Enjoy!
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
We've put together a few fun [examples](examples/) to show what's possible with Breakout Garden. Each example
|
||||||
|
has its own README telling you about it, which breakouts are required, and about how to use it.
|
||||||
|
|
||||||
|
## Bugs, Issues and Problems
|
||||||
|
|
||||||
|
If you have trouble with your Breakout Garden try our support forums: https://forums.pimoroni.com/
|
||||||
|
|
||||||
123
vendor/breakout-garden/autodetect.py
vendored
Executable file
@@ -0,0 +1,123 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
import smbus
|
||||||
|
import sys
|
||||||
|
|
||||||
|
I2C_BUS = 1
|
||||||
|
DEBUG = False
|
||||||
|
|
||||||
|
install_mode = False
|
||||||
|
|
||||||
|
if len(sys.argv) > 1:
|
||||||
|
if "--install" in sys.argv:
|
||||||
|
install_mode = True
|
||||||
|
|
||||||
|
try:
|
||||||
|
bus = smbus.SMBus(I2C_BUS)
|
||||||
|
except IOError:
|
||||||
|
print("Unable to access /dev/i2c-{}, please ensure i2c is enabled!".format(I2C_BUS))
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
|
||||||
|
def check_chip_id(i2c_addr, chip_ids):
|
||||||
|
if len(chip_ids) == 0:
|
||||||
|
return True
|
||||||
|
|
||||||
|
for register in chip_ids:
|
||||||
|
value, size, negate_match = chip_ids[register]
|
||||||
|
reg, reg_size = register
|
||||||
|
if reg_size == 1:
|
||||||
|
if size == 1:
|
||||||
|
read = bus.read_byte_data(i2c_addr, reg)
|
||||||
|
elif size == 2:
|
||||||
|
read = bus.read_word_data(i2c_addr, reg)
|
||||||
|
else:
|
||||||
|
raise RuntimeError("Unsupported Chip ID size: {} byte(s)".format(size))
|
||||||
|
|
||||||
|
if negate_match and value != read:
|
||||||
|
return True
|
||||||
|
elif value == read:
|
||||||
|
return True
|
||||||
|
|
||||||
|
else:
|
||||||
|
# TODO: Support 16-bit registers
|
||||||
|
raise RuntimeError("Unsupported register size: {} byte(s)".format(size))
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def get_device(line):
|
||||||
|
parts=[x.strip() for x in line.split(":")]
|
||||||
|
|
||||||
|
i2c_addr = int(parts[0][0:4], 16)
|
||||||
|
|
||||||
|
chip_ids = {}
|
||||||
|
|
||||||
|
if(len(parts[0]) > 4):
|
||||||
|
register_map = parts[0][5:-1].split(',')
|
||||||
|
for mapping in register_map:
|
||||||
|
if '!=' in mapping:
|
||||||
|
register, value = [(int(x, 16), (len(x) - 2) // 2) for x in mapping.split('!=')]
|
||||||
|
chip_ids[register] = value[0], value[1], True
|
||||||
|
else:
|
||||||
|
register, value = [(int(x, 16), (len(x) - 2) // 2) for x in mapping.split('=')]
|
||||||
|
chip_ids[register] = value[0], value[1], False
|
||||||
|
|
||||||
|
return i2c_addr, parts[1], parts[2], parts[3], chip_ids
|
||||||
|
|
||||||
|
devices = [get_device(line) for line in open("breakouts.config").read().strip().split("\n")]
|
||||||
|
|
||||||
|
addresses = set([device[0] for device in devices])
|
||||||
|
|
||||||
|
|
||||||
|
def identify(find_i2c_addr):
|
||||||
|
try:
|
||||||
|
bus.read_byte_data(find_i2c_addr, 0x00)
|
||||||
|
except IOError as e:
|
||||||
|
pass
|
||||||
|
|
||||||
|
for i2c_addr, library, module, name, chip_ids in devices:
|
||||||
|
if i2c_addr == find_i2c_addr and check_chip_id(i2c_addr, chip_ids):
|
||||||
|
installed = True
|
||||||
|
try:
|
||||||
|
__import__(module)
|
||||||
|
except ImportError:
|
||||||
|
installed = False
|
||||||
|
return installed, library, name
|
||||||
|
|
||||||
|
return None, None, None
|
||||||
|
|
||||||
|
|
||||||
|
found_addr = []
|
||||||
|
found_devices = {}
|
||||||
|
|
||||||
|
for i2c_addr in addresses:
|
||||||
|
try:
|
||||||
|
bus.read_byte_data(i2c_addr, 0x00)
|
||||||
|
if DEBUG: print("Found device on: {:02x}".format(i2c_addr))
|
||||||
|
found_addr.append(i2c_addr)
|
||||||
|
installed, library, name = identify(i2c_addr)
|
||||||
|
if installed is None:
|
||||||
|
continue
|
||||||
|
if name not in found_devices:
|
||||||
|
found_devices[name] = [installed, library, [i2c_addr]]
|
||||||
|
else:
|
||||||
|
found_devices[name][2].append(i2c_addr)
|
||||||
|
|
||||||
|
except IOError as e:
|
||||||
|
if DEBUG: print("IOError reading: {:02x}".format(i2c_addr))
|
||||||
|
continue
|
||||||
|
|
||||||
|
for name in found_devices:
|
||||||
|
installed, library, i2c_addresses = found_devices[name]
|
||||||
|
format_string = ""
|
||||||
|
if install_mode:
|
||||||
|
format_string = "{name}|{library}|{installed}"
|
||||||
|
else:
|
||||||
|
format_string = "{i2c_addresses}: {name} ({library} {installed})"
|
||||||
|
|
||||||
|
print(format_string.format(
|
||||||
|
i2c_addresses = ",".join(["0x{:02x}".format(i2c_addr) for i2c_addr in i2c_addresses]),
|
||||||
|
name = name,
|
||||||
|
library = library,
|
||||||
|
installed = "installed" if installed else "required"
|
||||||
|
))
|
||||||
31
vendor/breakout-garden/breakouts.config
vendored
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
0x0a: trackball-python: trackball: rgb illuminated mini trackball
|
||||||
|
0x0b: trackball-python: trackball: rgb illuminated mini trackball
|
||||||
|
0x3c: sh1106-python: luma.oled: 1.12" mono OLED
|
||||||
|
0x3d: sh1106-python: luma.oled: 1.12" mono OLED
|
||||||
|
0x1d: lsm303d-python: lsm303d: lsm303d 6dof motion sensor
|
||||||
|
0x1e: lsm303d-python: lsm303d: lsm303d 6dof motion sensor
|
||||||
|
0x23: ltr559-python: ltr559: ltr559 proximity sensor
|
||||||
|
0x38: bh1745-python: bh1745: bh1745 colour sensor
|
||||||
|
0x39: bh1745-python: bh1745: bh1745 colour sensor
|
||||||
|
0x29: vl53l1x-python: VL53L1X: vl53l1x time of flight
|
||||||
|
0x33: mlx90640-library: mlx90460: mlx90640 thermal camera
|
||||||
|
0x48: ads1015-python: ads1015: ads1015 ADC
|
||||||
|
0x49: as7262-python: as7262: as7262 spectrometer
|
||||||
|
0x57: max30105-python: max30105: max30105 heart rate, oximeter, smoke sensor
|
||||||
|
0x58: sgp30-python: sgp30: sgp30 TVOC and CO2 sensor
|
||||||
|
0x60: drv8830-python: drv8830: drv8830 H-bridge motor driver
|
||||||
|
0x66: mcp9600-python: mcp9600: mcp9600 thermocouple temperature sensor
|
||||||
|
0x67: mcp9600-python: mcp9600: mcp9600 thermocouple temperature sensor
|
||||||
|
0x68: icm20948-python: icm20948: icm20948 9dof motion sensor
|
||||||
|
0x69: icm20948-python: icm20948: icm20948 9dof motion sensor
|
||||||
|
0x74: rgbmatrix5x5-python: rgbmatrix5x5: rgb 5x5 led matrix
|
||||||
|
0x75: matrix11x7-python: matrix11x7: white 11x7 led matrix
|
||||||
|
0x76[0xd0=0x61]: bme680-python: bme680: bme680 weather sensor
|
||||||
|
0x76[0xd0=0x58]: bmp280-python: bmp280: bmp280 temperature and pressure sensor
|
||||||
|
0x77[0xd0=0x61]: bme680-python: bme680: bme680 weather sensor
|
||||||
|
0x77[0xd0=0x59]: bmp280-python: bmp280: bmp280 temperature and pressure sensor
|
||||||
|
0x5a: drv2605-python: drv2605: drv2605 haptic driver
|
||||||
|
0x26: msa301-python: msa301: msa3013-axis accelerometer
|
||||||
|
0x10[0x0c=0x0026]: veml6075-python: veml6075: veml6075 uva and uvb sensor
|
||||||
|
0x10[0x0c!=0x0026]: pa1010d-python: pa1010d: pa1010d GPS
|
||||||
|
0x52: rv3028-python: rv3028: rv3028 real-time-clock breakout
|
||||||
3
vendor/breakout-garden/documentation/bme680-temperature-pressure-humidity-gas-breakout.md
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# BME680 - Temperature, Pressure, Humidity And Gas Sensor
|
||||||
|
|
||||||
|
https://github.com/pimoroni/bme680
|
||||||
3
vendor/breakout-garden/documentation/ltr559-proximity-light-sensor.md
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# LTR559 - Proximity And Light Presence Sensor
|
||||||
|
|
||||||
|
https://github.com/pimoroni/ltr559-python
|
||||||
3
vendor/breakout-garden/documentation/mlx90640-thermal-camera-breakout.md
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# MLX90640 - 32x24 Pixel Thermal Camera
|
||||||
|
|
||||||
|
https://github.com/pimoroni/mlx90640-library
|
||||||
3
vendor/breakout-garden/documentation/sh1106-1.12inch-mono-oled-breakout.md
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# SH1106 - 1.12" Mono OLED
|
||||||
|
|
||||||
|
https://github.com/pimoroni/sh1106-python
|
||||||
3
vendor/breakout-garden/documentation/vl53l1x-time-of-flight-breakout.md
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# VL51L1X - Time Of Flight Distance Sensor
|
||||||
|
|
||||||
|
https://github.com/pimoroni/vl53l1x-python
|
||||||
6
vendor/breakout-garden/examples/README.md
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# Breakout Garden examples
|
||||||
|
|
||||||
|
These example programs demonstrate the sort of thing that's possible by combining
|
||||||
|
two or more Pimoroni breakouts on Breakout Garden.
|
||||||
|
|
||||||
|
Each example folder has its own README that you should check before running the example.
|
||||||
26
vendor/breakout-garden/examples/colour-control/README.md
vendored
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
# Colour control example
|
||||||
|
|
||||||
|
Use the trackball and switch to control the hue and brightness of the trackball's RGBW LEDs
|
||||||
|
and the 5x5 RGB matrix's LEDs.
|
||||||
|
|
||||||
|
|
||||||
|
Scroll up to increase brightness and left/right to change hue. Click to turn on/off.
|
||||||
|
|
||||||
|
## Pre-requisites
|
||||||
|
|
||||||
|
This example requires:
|
||||||
|
|
||||||
|
- A Pimoroni [Breakout Garden](https://shop.pimoroni.com/products/breakout-garden-hat-i2c-spi)
|
||||||
|
- A Pimoroni [Trackball Breakout](https://shop.pimoroni.com/products/trackball-breakout)
|
||||||
|
- A Pimoroni [5x5 RGB Matrix Breakout](https://shop.pimoroni.com/products/5x5-rgb-matrix-breakout)
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Pop the breakouts into your Breakout Garden, and then run the `install.sh`
|
||||||
|
script in the root of this repository with `sudo ./install.sh` to automagically
|
||||||
|
install the libraries to run the I2C breakouts.
|
||||||
|
|
||||||
|
## Running this example
|
||||||
|
|
||||||
|
To run this example, type `./colour-control.py` in the terminal.
|
||||||
|
|
||||||
80
vendor/breakout-garden/examples/colour-control/colour-control.py
vendored
Executable file
@@ -0,0 +1,80 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import time
|
||||||
|
import colorsys
|
||||||
|
import atexit
|
||||||
|
|
||||||
|
from trackball import TrackBall
|
||||||
|
from rgbmatrix5x5 import RGBMatrix5x5
|
||||||
|
|
||||||
|
print("""This Pimoroni Breakout Garden example requires an
|
||||||
|
Trackball Breakout and a 5x5 RGB Matrix Breakout.
|
||||||
|
|
||||||
|
Use the trackball and switch to control the hue and
|
||||||
|
brightness of the trackball's RGBW LEDs.
|
||||||
|
|
||||||
|
Scroll up to increase brightness and left/right
|
||||||
|
to change hue. Click to turn on/off.
|
||||||
|
|
||||||
|
Press Ctrl+C to exit.
|
||||||
|
""")
|
||||||
|
|
||||||
|
# Set up the trackball
|
||||||
|
trackball = TrackBall(interrupt_pin=4)
|
||||||
|
|
||||||
|
@atexit.register
|
||||||
|
def clear_trackball():
|
||||||
|
trackball.set_rgbw(0, 0, 0, 0)
|
||||||
|
|
||||||
|
# Set up the 5x5 RGB matrix
|
||||||
|
rgbmatrix5x5 = RGBMatrix5x5()
|
||||||
|
rgbmatrix5x5.set_clear_on_exit()
|
||||||
|
rgbmatrix5x5.set_brightness(0.8)
|
||||||
|
|
||||||
|
x = 0
|
||||||
|
y = 50.0
|
||||||
|
|
||||||
|
toggled = False
|
||||||
|
|
||||||
|
while True:
|
||||||
|
up, down, left, right, switch, state = trackball.read()
|
||||||
|
|
||||||
|
# Update x and y vals based on movement
|
||||||
|
y += up
|
||||||
|
y -= down
|
||||||
|
x += right / 10.0
|
||||||
|
x -= left / 10.0
|
||||||
|
|
||||||
|
# Clamp to min of 0 and max of 100
|
||||||
|
x %= 100
|
||||||
|
y = max(0, min(y, 100))
|
||||||
|
|
||||||
|
# Calculate hue and brightness
|
||||||
|
h = x / 100.0
|
||||||
|
v = y / 100.0
|
||||||
|
|
||||||
|
# Prevents button from retriggering
|
||||||
|
debounce = 0.5
|
||||||
|
|
||||||
|
# Change toggled state if switch is pressed
|
||||||
|
if state and not toggled:
|
||||||
|
toggled = True
|
||||||
|
time.sleep(debounce)
|
||||||
|
elif state and toggled:
|
||||||
|
toggled = False
|
||||||
|
time.sleep(debounce)
|
||||||
|
|
||||||
|
# Set brightness to zero if switch toggled
|
||||||
|
if toggled:
|
||||||
|
v = 0
|
||||||
|
|
||||||
|
# Calculate RGB vals
|
||||||
|
w = 0
|
||||||
|
r, g, b = [int(c * 255) for c in colorsys.hsv_to_rgb(h, 1.0, v)]
|
||||||
|
|
||||||
|
# Set LEDs
|
||||||
|
trackball.set_rgbw(r, g, b, w)
|
||||||
|
rgbmatrix5x5.set_all(r, g, b)
|
||||||
|
rgbmatrix5x5.show()
|
||||||
|
|
||||||
|
time.sleep(0.01)
|
||||||
44
vendor/breakout-garden/examples/distance/README.md
vendored
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
# Distance example
|
||||||
|
|
||||||
|
This example, the Park-O-Matic 6000, is a mockup of a car reversing
|
||||||
|
indicator.
|
||||||
|
|
||||||
|
## Pre-requisites
|
||||||
|
|
||||||
|
This example requires:
|
||||||
|
|
||||||
|
- A Pimoroni [Breakout Garden](https://shop.pimoroni.com/products/breakout-garden-hat-i2c-spi)
|
||||||
|
- A Pimoroni [VL53L1X Time of Flight Sensor Breakout](https://shop.pimoroni.com/products/vl53l1x-breakout)
|
||||||
|
- A Pimoroni [1.12" OLED Breakout (SPI)](https://shop.pimoroni.com/products/1-12-oled-breakout)
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Pop the breakouts into your Breakout Garden, and then run the `install.sh`
|
||||||
|
script in the root of this repository with `sudo ./install.sh` to automagically
|
||||||
|
install the libraries to run the I2C breakouts.
|
||||||
|
|
||||||
|
For this example you'll need to make sure some additional software is installed:
|
||||||
|
|
||||||
|
```
|
||||||
|
sudo apt install python3-pil
|
||||||
|
```
|
||||||
|
|
||||||
|
You'll need to clone and install the library for the 1.12" OLED Breakout (SPI)
|
||||||
|
as follows:
|
||||||
|
|
||||||
|
```
|
||||||
|
git clone https://github.com/pimoroni/sh1106-python
|
||||||
|
sudo ./install.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
This example assumes that you have the OLED plugged into the front slot on the
|
||||||
|
Breakout Garden HAT, which should also work with the Breakout Garden Mini HAT.
|
||||||
|
To change it to the back slot, change `device=1` to `device=0` on the line
|
||||||
|
where the OLED is set up.
|
||||||
|
|
||||||
|
## Running this example
|
||||||
|
|
||||||
|
To run this example, type `./distance.py` in the terminal.
|
||||||
|
|
||||||
|
You can change the `threshold` value (in cm) to change the threshold at which the warning
|
||||||
|
indicator starts flashing.
|
||||||
72
vendor/breakout-garden/examples/distance/distance.py
vendored
Executable file
@@ -0,0 +1,72 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
|
||||||
|
from PIL import Image
|
||||||
|
from PIL import ImageFont
|
||||||
|
from PIL import ImageDraw
|
||||||
|
|
||||||
|
import VL53L1X
|
||||||
|
|
||||||
|
from luma.core.interface.serial import spi
|
||||||
|
from luma.core.render import canvas
|
||||||
|
from luma.oled.device import sh1106
|
||||||
|
|
||||||
|
print("""This Pimoroni Breakout Garden example requires an
|
||||||
|
VL53L1X Time of Flight Sensor Breakout and a 1.12" OLED Breakout (SPI).
|
||||||
|
|
||||||
|
The Park-O-Matic 6000 is a car reversing indicator mockup!
|
||||||
|
|
||||||
|
Press Ctrl+C a couple times to exit.
|
||||||
|
""")
|
||||||
|
|
||||||
|
# Set up OLED
|
||||||
|
|
||||||
|
oled = sh1106(spi(port=0, device=1, gpio_DC=9), rotate=2, height=128, width=128)
|
||||||
|
|
||||||
|
# Set up VL53L1X Time of Flight sensor
|
||||||
|
|
||||||
|
tof = VL53L1X.VL53L1X(i2c_bus=1, i2c_address=0x29)
|
||||||
|
tof.open() # Initialise the I2C bus and configure the sensor
|
||||||
|
tof.start_ranging(3) # Start ranging, 1 = Short Range, 2 = Medium Range, 3 = Long Range
|
||||||
|
|
||||||
|
|
||||||
|
# Load fonts
|
||||||
|
|
||||||
|
rr_path = os.path.abspath(os.path.join(os.path.dirname(__file__), 'fonts', 'Roboto-Regular.ttf'))
|
||||||
|
rb_path = os.path.abspath(os.path.join(os.path.dirname(__file__), 'fonts', 'Roboto-Black.ttf'))
|
||||||
|
rr_24 = ImageFont.truetype(rr_path, 24)
|
||||||
|
rb_20 = ImageFont.truetype(rb_path, 20)
|
||||||
|
rr_12 = ImageFont.truetype(rr_path, 12)
|
||||||
|
|
||||||
|
|
||||||
|
i = 0
|
||||||
|
threshold = 20 # Threshold at which the warning indicator flashes, in cm
|
||||||
|
|
||||||
|
# Main loop
|
||||||
|
|
||||||
|
while True:
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
background = Image.open("images/distance.png").convert(oled.mode) # Load the artwork
|
||||||
|
|
||||||
|
draw = ImageDraw.ImageDraw(background)
|
||||||
|
|
||||||
|
# Measure the distance and write to the right place on the display
|
||||||
|
|
||||||
|
cm = tof.get_distance() // 10
|
||||||
|
cm += 1
|
||||||
|
pos = 80 - (rb_20.getsize(str(cm))[0] / 2)
|
||||||
|
draw.text((pos, 30), "{}".format(cm), fill="white", font=rb_20)
|
||||||
|
|
||||||
|
# Flash the warning indicator if the distance is below the threshold
|
||||||
|
|
||||||
|
if cm > threshold or i % cm == 1:
|
||||||
|
draw.rectangle([(76, 66), (115, 110)], fill="black")
|
||||||
|
|
||||||
|
# Display on the OLED
|
||||||
|
|
||||||
|
oled.display(background)
|
||||||
|
|
||||||
|
time.sleep(0.05)
|
||||||
BIN
vendor/breakout-garden/examples/distance/fonts/Roboto-Black.ttf
vendored
Normal file
BIN
vendor/breakout-garden/examples/distance/fonts/Roboto-Regular.ttf
vendored
Normal file
BIN
vendor/breakout-garden/examples/distance/images/distance.png
vendored
Normal file
|
After Width: | Height: | Size: 3.7 KiB |
22
vendor/breakout-garden/examples/haptic-trackball/README.md
vendored
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# Haptic trackball example
|
||||||
|
|
||||||
|
This example demonstrates how to generate haptic feedback
|
||||||
|
as the trackball is scrolled/pressed.
|
||||||
|
|
||||||
|
## Pre-requisites
|
||||||
|
|
||||||
|
This example requires:
|
||||||
|
|
||||||
|
- A Pimoroni [Breakout Garden](https://shop.pimoroni.com/products/breakout-garden-hat-i2c-spi)
|
||||||
|
- A Pimoroni [DRV2605L Haptic Breakout](https://shop.pimoroni.com/products/drv2605l-linear-actuator-haptic-breakout)
|
||||||
|
- A Pimoroni [Trackball Breakout](https://shop.pimoroni.com/products/trackball-breakout)
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Pop the breakouts into your Breakout Garden, and then run the `install.sh`
|
||||||
|
script in the root of this repository with `sudo ./install.sh` to automagically
|
||||||
|
install the libraries to run the I2C breakouts.
|
||||||
|
|
||||||
|
## Running this example
|
||||||
|
|
||||||
|
To run this example, type `./haptic-trackball.py` in the terminal.
|
||||||
77
vendor/breakout-garden/examples/haptic-trackball/haptic-trackball.py
vendored
Executable file
@@ -0,0 +1,77 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import time
|
||||||
|
from trackball import TrackBall
|
||||||
|
from drv2605 import DRV2605
|
||||||
|
|
||||||
|
|
||||||
|
print("""This Pimoroni Breakout Garden example requires a
|
||||||
|
DRV2605L Haptic Breakout and a Trackball Breakout.
|
||||||
|
|
||||||
|
This example demonstrates how to generate haptic feedback
|
||||||
|
as the trackball is scrolled/pressed.
|
||||||
|
|
||||||
|
Press Ctrl+C to exit.
|
||||||
|
""")
|
||||||
|
|
||||||
|
|
||||||
|
# Set up Trackball Breakout.
|
||||||
|
trackball = TrackBall(interrupt_pin=4)
|
||||||
|
drv2605 = DRV2605()
|
||||||
|
|
||||||
|
x = 0
|
||||||
|
y = 0
|
||||||
|
|
||||||
|
delta_x = 0
|
||||||
|
delta_y = 0
|
||||||
|
last_state = 0
|
||||||
|
|
||||||
|
# Set up Haptic Breakout.
|
||||||
|
drv2605.reset()
|
||||||
|
drv2605.set_realtime_data_format('Unsigned')
|
||||||
|
drv2605.set_feedback_mode('LRA')
|
||||||
|
drv2605.set_mode('Real-time Playback')
|
||||||
|
drv2605.go()
|
||||||
|
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
# Get positional values from trackball.
|
||||||
|
up, down, left, right, switch, state = trackball.read()
|
||||||
|
y += up
|
||||||
|
y -= down
|
||||||
|
x += right
|
||||||
|
x -= left
|
||||||
|
|
||||||
|
delta_x += right
|
||||||
|
delta_x -= left
|
||||||
|
|
||||||
|
delta_y += up
|
||||||
|
delta_y -= down
|
||||||
|
|
||||||
|
x = max(0, min(x, 255))
|
||||||
|
y = max(0, min(y, 255))
|
||||||
|
|
||||||
|
# Generate a longer click when trackball is pressed.
|
||||||
|
if state != last_state:
|
||||||
|
drv2605.set_realtime_input(255)
|
||||||
|
time.sleep(0.01)
|
||||||
|
drv2605.set_realtime_input(0)
|
||||||
|
last_state = state
|
||||||
|
|
||||||
|
# Generate shorter clicks when trackball is scrolled.
|
||||||
|
if abs(delta_x) > 2:
|
||||||
|
drv2605.set_realtime_input(255)
|
||||||
|
time.sleep(0.005)
|
||||||
|
drv2605.set_realtime_input(0)
|
||||||
|
delta_x = 0
|
||||||
|
|
||||||
|
elif abs(delta_y) > 2:
|
||||||
|
drv2605.set_realtime_input(255)
|
||||||
|
time.sleep(0.005)
|
||||||
|
drv2605.set_realtime_input(0)
|
||||||
|
delta_y = 0
|
||||||
|
|
||||||
|
time.sleep(0.001)
|
||||||
|
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
pass
|
||||||
57
vendor/breakout-garden/examples/heartbeat/README.md
vendored
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
# Heartbeat example
|
||||||
|
|
||||||
|
This example, the Pulse-O-Matic 6000, is a heartbeat plotter and BPM display.
|
||||||
|
|
||||||
|
## Important!
|
||||||
|
|
||||||
|
**This code should not be used for medical diagnosis, as the basis for a real smoke
|
||||||
|
or fire detector, or in life-critical situations. It's for fun/novelty use only,
|
||||||
|
so bear that in mind while using it.**
|
||||||
|
|
||||||
|
## Pre-requisites
|
||||||
|
|
||||||
|
This example requires:
|
||||||
|
|
||||||
|
- A Pimoroni [Breakout Garden HAT](https://shop.pimoroni.com/products/breakout-garden-hat-i2c-spi)
|
||||||
|
- A Pimoroni [MAX30105 Breakout - Heart Rate, Oximeter, Smoke Sensor](https://shop.pimoroni.com/products/max30105-breakout-heart-rate-oximeter-smoke-sensor)
|
||||||
|
- A Pimoroni [1.12" OLED Breakout (SPI)](https://shop.pimoroni.com/products/1-12-oled-breakout)
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Pop the breakouts into your Breakout Garden, and then run the `install.sh`
|
||||||
|
script in the root of this repository with `sudo ./install.sh` to automagically
|
||||||
|
install the libraries to run the I2C breakouts.
|
||||||
|
|
||||||
|
For this example you'll need to make sure some additional software is installed:
|
||||||
|
|
||||||
|
```
|
||||||
|
sudo apt install python3-pil
|
||||||
|
```
|
||||||
|
|
||||||
|
You'll need to clone and install the library for the 1.12" OLED Breakout (SPI)
|
||||||
|
as follows:
|
||||||
|
|
||||||
|
```
|
||||||
|
git clone https://github.com/pimoroni/sh1106-python
|
||||||
|
sudo ./install.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
This example assumes that you have the OLED plugged into the front slot on the
|
||||||
|
Breakout Garden HAT, which should also work with the Breakout Garden Mini HAT.
|
||||||
|
To change it to the back slot, change `device=1` to `device=0` on the line
|
||||||
|
where the OLED is set up.
|
||||||
|
|
||||||
|
## Running this example
|
||||||
|
|
||||||
|
To run this example, type `./heartbeat.py` in the terminal.
|
||||||
|
|
||||||
|
It's best to hold the sensor against your fingertip (the fleshy side)
|
||||||
|
using a piece of wire or a rubber band looped through the mounting
|
||||||
|
holes on the breakout, as the sensor is very sensitive to small
|
||||||
|
movements and it's hard to hold your finger against the sensor with
|
||||||
|
even pressure.
|
||||||
|
|
||||||
|
If you're using your MAX30105 Breakout with Breakout Garden, then
|
||||||
|
we'd recommend using one of our
|
||||||
|
[Breakout Garden Extender Kits](https://shop.pimoroni.com/products/breakout-garden-extender-kit)
|
||||||
|
with some [female to female jumper jerky](https://shop.pimoroni.com/products/jumper-jerky?variant=348491271).
|
||||||
BIN
vendor/breakout-garden/examples/heartbeat/fonts/LiberationSans-Bold.ttf
vendored
Normal file
BIN
vendor/breakout-garden/examples/heartbeat/fonts/LiberationSans-BoldItalic.ttf
vendored
Normal file
BIN
vendor/breakout-garden/examples/heartbeat/fonts/LiberationSans-Italic.ttf
vendored
Normal file
BIN
vendor/breakout-garden/examples/heartbeat/fonts/LiberationSans-Regular.ttf
vendored
Normal file
46
vendor/breakout-garden/examples/heartbeat/fonts/SIL Open Font License.txt
vendored
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
Digitized data copyright (c) 2010 Google Corporation
|
||||||
|
with Reserved Font Arimo, Tinos and Cousine.
|
||||||
|
Copyright (c) 2012 Red Hat, Inc.
|
||||||
|
with Reserved Font Name Liberation.
|
||||||
|
|
||||||
|
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||||
|
This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL
|
||||||
|
|
||||||
|
-----------------------------------------------------------
|
||||||
|
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||||
|
-----------------------------------------------------------
|
||||||
|
|
||||||
|
PREAMBLE
|
||||||
|
The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others.
|
||||||
|
|
||||||
|
The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives.
|
||||||
|
|
||||||
|
DEFINITIONS
|
||||||
|
"Font Software" refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation.
|
||||||
|
|
||||||
|
"Reserved Font Name" refers to any names specified as such after the copyright statement(s).
|
||||||
|
|
||||||
|
"Original Version" refers to the collection of Font Software components as distributed by the Copyright Holder(s).
|
||||||
|
|
||||||
|
"Modified Version" refers to any derivative made by adding to, deleting, or substituting -- in part or in whole -- any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment.
|
||||||
|
|
||||||
|
"Author" refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software.
|
||||||
|
|
||||||
|
PERMISSION & CONDITIONS
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions:
|
||||||
|
|
||||||
|
1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself.
|
||||||
|
|
||||||
|
2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user.
|
||||||
|
|
||||||
|
3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users.
|
||||||
|
|
||||||
|
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission.
|
||||||
|
|
||||||
|
5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software.
|
||||||
|
|
||||||
|
TERMINATION
|
||||||
|
This license becomes null and void if any of the above conditions are not met.
|
||||||
|
|
||||||
|
DISCLAIMER
|
||||||
|
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||||
133
vendor/breakout-garden/examples/heartbeat/heartbeat.py
vendored
Executable file
@@ -0,0 +1,133 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# NOTE! This code should not be used for medical diagnosis. It's
|
||||||
|
# for fun/novelty use only, so bear that in mind while using it.
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
from PIL import Image
|
||||||
|
from PIL import ImageFont
|
||||||
|
from PIL import ImageDraw
|
||||||
|
|
||||||
|
from threading import Thread
|
||||||
|
from luma.core.interface.serial import spi
|
||||||
|
from luma.oled.device import sh1106
|
||||||
|
|
||||||
|
from max30105 import MAX30105, HeartRate
|
||||||
|
|
||||||
|
print("""
|
||||||
|
NOTE! This code should not be used for medical diagnosis. It's
|
||||||
|
for fun/novelty use only, so bear that in mind while using it.
|
||||||
|
|
||||||
|
This Pimoroni Breakout Garden example requires a
|
||||||
|
MAX30105 Breakout and a 1.12" OLED Breakout (SPI).
|
||||||
|
|
||||||
|
The Pulse-O-Matic 6000 is a heartbeat plotter and BPM display.
|
||||||
|
|
||||||
|
Press Ctrl+C a couple times to exit.
|
||||||
|
""")
|
||||||
|
|
||||||
|
# Set up OLED
|
||||||
|
oled = sh1106(spi(port=0, device=1, gpio_DC=9), rotate=2, height=128, width=128)
|
||||||
|
|
||||||
|
# Load fonts
|
||||||
|
lsb_18 = ImageFont.truetype("fonts/LiberationSans-Bold.ttf", 18)
|
||||||
|
lsr_12 = ImageFont.truetype("fonts/LiberationSans-Regular.ttf", 12)
|
||||||
|
|
||||||
|
# Set up MAX30105 Breakout
|
||||||
|
max30105 = MAX30105()
|
||||||
|
max30105.setup(leds_enable=2)
|
||||||
|
|
||||||
|
max30105.set_led_pulse_amplitude(1, 0.2)
|
||||||
|
max30105.set_led_pulse_amplitude(2, 12.5)
|
||||||
|
max30105.set_led_pulse_amplitude(3, 0)
|
||||||
|
|
||||||
|
max30105.set_slot_mode(1, 'red')
|
||||||
|
max30105.set_slot_mode(2, 'ir')
|
||||||
|
max30105.set_slot_mode(3, 'off')
|
||||||
|
max30105.set_slot_mode(4, 'off')
|
||||||
|
|
||||||
|
hr = HeartRate(max30105)
|
||||||
|
data = []
|
||||||
|
running = True
|
||||||
|
|
||||||
|
bpm = 0
|
||||||
|
bpm_avg = 0
|
||||||
|
beat_detected = False
|
||||||
|
beat_status = False
|
||||||
|
|
||||||
|
|
||||||
|
def sample():
|
||||||
|
"""Function to thread heartbeat values separately to
|
||||||
|
OLED drawing"""
|
||||||
|
global bpm, bpm_avg, beat_detected, beat_status
|
||||||
|
|
||||||
|
average_over = 5
|
||||||
|
bpm_vals = [0 for x in range(average_over)]
|
||||||
|
last_beat = time.time()
|
||||||
|
|
||||||
|
while running:
|
||||||
|
t = time.time()
|
||||||
|
samples = max30105.get_samples()
|
||||||
|
if samples is not None:
|
||||||
|
for i in range(0, len(samples), 2):
|
||||||
|
ir = samples[i + 1]
|
||||||
|
beat_detected = hr.check_for_beat(ir)
|
||||||
|
if beat_detected:
|
||||||
|
beat_status = True
|
||||||
|
delta = t - last_beat
|
||||||
|
last_beat = t
|
||||||
|
bpm = 60 / delta
|
||||||
|
bpm_vals = bpm_vals[1:] + [bpm]
|
||||||
|
bpm_avg = sum(bpm_vals) / average_over
|
||||||
|
d = hr.low_pass_fir(ir & 0xff)
|
||||||
|
data.append(d)
|
||||||
|
if len(data) > 128:
|
||||||
|
data.pop(0)
|
||||||
|
|
||||||
|
time.sleep(0.05)
|
||||||
|
|
||||||
|
|
||||||
|
# The thread to measure acclerometer values
|
||||||
|
t = Thread(target=sample)
|
||||||
|
t.start()
|
||||||
|
|
||||||
|
# The main loop that draws values to the OLED
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
img = Image.open("images/heartbeat.png").convert(oled.mode)
|
||||||
|
draw = ImageDraw.Draw(img)
|
||||||
|
|
||||||
|
# Draw the heartbeat trace
|
||||||
|
vals = data
|
||||||
|
new_vals = [x / float((max(vals) - min(vals))) * 32 for x in vals]
|
||||||
|
|
||||||
|
for i in range(1, len(new_vals)):
|
||||||
|
draw.line([(i - 1, 80 - new_vals[i - 1]), (i, 80 - new_vals[i])],
|
||||||
|
fill="white")
|
||||||
|
|
||||||
|
# Draw the Pulse-O-Matic "branding"
|
||||||
|
draw.rectangle([(0, 0), (128, 20)], fill="black")
|
||||||
|
if bpm_avg > 40:
|
||||||
|
draw.text((0, 1), "BPM: {:.2f}".format(bpm_avg), fill="white",
|
||||||
|
font=lsb_18)
|
||||||
|
else:
|
||||||
|
draw.text((0, 1), "BPM: --.--", fill="white", font=lsb_18)
|
||||||
|
if beat_status:
|
||||||
|
draw.text((115, 1), u"\u2665", fill="white", font=lsb_18)
|
||||||
|
beat_status = False
|
||||||
|
draw.line([(0, 20), (128, 20)], fill="white")
|
||||||
|
|
||||||
|
draw.rectangle([(0, 108), (128, 128)], fill="black")
|
||||||
|
draw.text((0, 110), "Pulse-O-Matic 6000", fill="white", font=lsr_12)
|
||||||
|
draw.line([(0, 108), (128, 108)], fill="white")
|
||||||
|
|
||||||
|
# Display on the OLED
|
||||||
|
oled.display(img)
|
||||||
|
except ZeroDivisionError:
|
||||||
|
pass
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
running = False
|
||||||
|
sys.exit(0)
|
||||||
BIN
vendor/breakout-garden/examples/heartbeat/images/heartbeat.png
vendored
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
34
vendor/breakout-garden/examples/nightlight/README.md
vendored
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
# Nightlight example
|
||||||
|
|
||||||
|
A simple little example of how to make a nightlight with the LTR-559 and 5x5
|
||||||
|
RGB matrix breakouts. It can be toggled on or off by tapping the sensor, or
|
||||||
|
triggered automatically when it gets dark.
|
||||||
|
|
||||||
|
## Pre-requisites
|
||||||
|
|
||||||
|
This example requires:
|
||||||
|
|
||||||
|
- A Pimoroni [Breakout Garden](https://shop.pimoroni.com/products/breakout-garden-hat-i2c-spi)
|
||||||
|
- A Pimoroni [LTR-559 Light & Proximity Sensor Breakout](https://shop.pimoroni.com/products/ltr-559-light-proximity-sensor-breakout)
|
||||||
|
- A Pimoroni [5x5 RGB Matrix Breakout](https://shop.pimoroni.com/products/5x5-rgb-matrix-breakout)
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Pop the breakouts into your Breakout Garden, and then run the `install.sh`
|
||||||
|
script in the root of this repository with `sudo ./install.sh` to automagically
|
||||||
|
install all of the required libraries.
|
||||||
|
|
||||||
|
## Running this example
|
||||||
|
|
||||||
|
To run this example, type `./nightlight.py` in the terminal.
|
||||||
|
|
||||||
|
You can change the RGB values of the `colour` variable to change the colour
|
||||||
|
of the light to whatever you wish. If you want the light and proximity
|
||||||
|
thresholds to be more or less sensitive, then you can change the values of
|
||||||
|
the `light_threshold` and `prox_threshold` variables.
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
It's probably best to have the sensor and matrix breakouts on either side
|
||||||
|
of your Breakout Garden HAT, so that they're spaced apart and the LTR-559
|
||||||
|
won't be affected by the light from the matrix.
|
||||||
103
vendor/breakout-garden/examples/nightlight/nightlight.py
vendored
Executable file
@@ -0,0 +1,103 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import time
|
||||||
|
|
||||||
|
from ltr559 import LTR559
|
||||||
|
from rgbmatrix5x5 import RGBMatrix5x5
|
||||||
|
|
||||||
|
print("""This Pimoroni Breakout Garden example requires an
|
||||||
|
LTR-559 Light and Proximity Breakout and a 5x5 RGB Matrix Breakout.
|
||||||
|
This example creates a little nightlight that can be toggled on or
|
||||||
|
off by tapping the proximity sensor with your finger, or triggered
|
||||||
|
automatically when it's dark.
|
||||||
|
|
||||||
|
Press Ctrl+C to exit.
|
||||||
|
""")
|
||||||
|
|
||||||
|
# Set up the LTR-559 sensor
|
||||||
|
|
||||||
|
ltr559 = LTR559()
|
||||||
|
|
||||||
|
# Set up the 5x5 RGB matrix
|
||||||
|
rgbmatrix5x5 = RGBMatrix5x5()
|
||||||
|
rgbmatrix5x5.set_clear_on_exit()
|
||||||
|
rgbmatrix5x5.set_brightness(0.8)
|
||||||
|
|
||||||
|
# Initial variables to keep track of state of light
|
||||||
|
state = False
|
||||||
|
last_state = False
|
||||||
|
toggled = False
|
||||||
|
|
||||||
|
light_threshold = 100 # Low-light trigger level
|
||||||
|
prox_threshold = 1000 # Proximity trigger level
|
||||||
|
colour = (255, 165, 0) # Orange-ish
|
||||||
|
|
||||||
|
|
||||||
|
# Function to toggle the RGB matrix on or off depending on state
|
||||||
|
def toggle_matrix():
|
||||||
|
global state, last_state
|
||||||
|
|
||||||
|
if state is True and last_state is False:
|
||||||
|
rgbmatrix5x5.set_all(*colour)
|
||||||
|
rgbmatrix5x5.show()
|
||||||
|
elif state is False and last_state is True:
|
||||||
|
rgbmatrix5x5.clear()
|
||||||
|
rgbmatrix5x5.show()
|
||||||
|
|
||||||
|
last_state = state
|
||||||
|
|
||||||
|
|
||||||
|
# Read the sensor once, as the first values are always squiffy
|
||||||
|
ltr559.update_sensor()
|
||||||
|
lux = ltr559.get_lux()
|
||||||
|
prox =ltr559. get_proximity()
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
# Read the light and proximity sensor
|
||||||
|
ltr559.update_sensor()
|
||||||
|
lux = ltr559.get_lux()
|
||||||
|
prox = ltr559.get_proximity()
|
||||||
|
|
||||||
|
# If it's dark and the light isn't toggled on, turn on
|
||||||
|
if lux < light_threshold and not toggled:
|
||||||
|
state = True
|
||||||
|
if state != last_state:
|
||||||
|
print("It's dark! Turning light ON")
|
||||||
|
toggle_matrix()
|
||||||
|
|
||||||
|
# If it's light and the light isn't on, turn off
|
||||||
|
elif lux >= light_threshold and not toggled:
|
||||||
|
state = False
|
||||||
|
if state != last_state:
|
||||||
|
print("It's light! Turning light OFF")
|
||||||
|
toggle_matrix()
|
||||||
|
|
||||||
|
# If there's a tap on the sensor
|
||||||
|
if prox > prox_threshold:
|
||||||
|
# Toggle it off if it's currently on
|
||||||
|
if toggled:
|
||||||
|
state = False
|
||||||
|
toggled = False
|
||||||
|
if state != last_state:
|
||||||
|
print("Toggling light OFF")
|
||||||
|
toggle_matrix()
|
||||||
|
# Toggle it on if it's currently off
|
||||||
|
else:
|
||||||
|
state = True
|
||||||
|
toggled = True
|
||||||
|
if state != last_state:
|
||||||
|
print("Toggling light ON")
|
||||||
|
toggle_matrix()
|
||||||
|
# Wait a short while to prevent the on/off switch
|
||||||
|
# from immediately re-triggering
|
||||||
|
time.sleep(0.5)
|
||||||
|
|
||||||
|
elif prox < prox_threshold and lux >= light_threshold:
|
||||||
|
state = False
|
||||||
|
|
||||||
|
time.sleep(0.05)
|
||||||
|
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
pass
|
||||||
28
vendor/breakout-garden/examples/rainbow-compass/README.md
vendored
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
# Rainbow compass example
|
||||||
|
|
||||||
|
Calculates and displays compass heading as an
|
||||||
|
RGB colour around the hue wheel, with North being red, South cyan,
|
||||||
|
East green, and West purple, approximately.
|
||||||
|
|
||||||
|
## Pre-requisites
|
||||||
|
|
||||||
|
This example requires:
|
||||||
|
|
||||||
|
- A Pimoroni [Breakout Garden](https://shop.pimoroni.com/products/breakout-garden-hat-i2c-spi)
|
||||||
|
- A Pimoroni [LSM303D Motion Sensor Breakout](https://shop.pimoroni.com/products/lsm303d-6dof-motion-sensor-breakout)
|
||||||
|
- A Pimoroni [5x5 RGB Matrix Breakout](https://shop.pimoroni.com/products/5x5-rgb-matrix-breakout)
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Pop the breakouts into your Breakout Garden, and then run the `install.sh`
|
||||||
|
script in the root of this repository with `sudo ./install.sh` to automagically
|
||||||
|
install all of the required libraries.
|
||||||
|
|
||||||
|
## Running this example
|
||||||
|
|
||||||
|
To run this example, type `./rainbow-compass.py` in the terminal, and then
|
||||||
|
walk through the calibration steps.
|
||||||
|
|
||||||
|
Depending on the orientation of you LSM303D breakout, you can change the
|
||||||
|
line that says `Y = 2` in the `raw_heading` function to e.g. `Y = 1` if
|
||||||
|
you have the breakout flat rather than vertical.
|
||||||
100
vendor/breakout-garden/examples/rainbow-compass/rainbow-compass.py
vendored
Executable file
@@ -0,0 +1,100 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import time
|
||||||
|
import math
|
||||||
|
import colorsys
|
||||||
|
from lsm303d import LSM303D
|
||||||
|
from rgbmatrix5x5 import RGBMatrix5x5
|
||||||
|
|
||||||
|
print("""This Pimoroni Breakout Garden example requires an LSM303D
|
||||||
|
Motion Sensor Breakout and a 5x5 RGB Matrix Breakout.
|
||||||
|
|
||||||
|
The Rainbow Compass calculates and displays compass heading as an
|
||||||
|
RGB colour around the hue wheel, with North being red, South cyan,
|
||||||
|
East green, and West purple, appromximately.
|
||||||
|
|
||||||
|
Press Cyrl-+C to exit.
|
||||||
|
""")
|
||||||
|
|
||||||
|
|
||||||
|
def raw_heading(minimums, maximums, zero=0):
|
||||||
|
"""Return a raw compass heading calculated from the magnetometer data."""
|
||||||
|
|
||||||
|
X = 0
|
||||||
|
Y = 2 # Change to 1 if you have the breakout flat
|
||||||
|
|
||||||
|
# The range over which values will be calculated, i.e. -1 to +1
|
||||||
|
mag_range = 2
|
||||||
|
|
||||||
|
# Get the magnetometer's values
|
||||||
|
mag = list(lsm.magnetometer())
|
||||||
|
|
||||||
|
# Scale and shift values
|
||||||
|
for i in range(len(mag)):
|
||||||
|
mag[i] = ((mag_range / (maximums[i] - minimums[i])) * mag[i]) - \
|
||||||
|
(mag_range / 2.0)
|
||||||
|
|
||||||
|
# Calculate the heading from the vector
|
||||||
|
heading = math.atan2(mag[Y], mag[X])
|
||||||
|
|
||||||
|
if heading < 0:
|
||||||
|
heading += (2 * math.pi)
|
||||||
|
|
||||||
|
# Convert radian value to degrees
|
||||||
|
heading_degrees = (round(math.degrees(heading), 2) - zero) % 360
|
||||||
|
|
||||||
|
return heading_degrees
|
||||||
|
|
||||||
|
|
||||||
|
lsm = LSM303D(0x1d) # Change to 0x1e if you have soldered the address jumper
|
||||||
|
|
||||||
|
# Set up the 5x5 RGB matrix
|
||||||
|
rgbmatrix5x5 = RGBMatrix5x5()
|
||||||
|
rgbmatrix5x5.set_clear_on_exit()
|
||||||
|
rgbmatrix5x5.set_brightness(0.8)
|
||||||
|
|
||||||
|
# Python 2/3 compatibility
|
||||||
|
try:
|
||||||
|
input = raw_input
|
||||||
|
except NameError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
input("Lay your LSM303D in Breakout Garden flat (LSM303D vertical), \n\
|
||||||
|
press a key to start, then rotate it 360 degrees, keeping it flat...\n")
|
||||||
|
|
||||||
|
# Variables to govern calibration time
|
||||||
|
t_start = time.time()
|
||||||
|
t_elapsed = 0
|
||||||
|
calibration_time = 30
|
||||||
|
|
||||||
|
# Initial values for mins and maxs
|
||||||
|
minimums = list(lsm.magnetometer())
|
||||||
|
maximums = list(lsm.magnetometer())
|
||||||
|
|
||||||
|
# Run calibration until time limit is reached
|
||||||
|
while t_elapsed < calibration_time:
|
||||||
|
mag = lsm.magnetometer()
|
||||||
|
for i in range(len(mag)):
|
||||||
|
if mag[i] < minimums[i]: # Set new min
|
||||||
|
minimums[i] = mag[i]
|
||||||
|
if mag[i] > maximums[i]: # Set new max
|
||||||
|
maximums[i] = mag[i]
|
||||||
|
t_elapsed = time.time() - t_start
|
||||||
|
|
||||||
|
input("Calibration complete!\n\nIf you want to set a zero (North) point, \n\
|
||||||
|
then turn your breakout to that point and press a key...\n")
|
||||||
|
|
||||||
|
# Zero point for the compass
|
||||||
|
zero = raw_heading(minimums, maximums)
|
||||||
|
|
||||||
|
input("Press a key to begin readings!\n")
|
||||||
|
|
||||||
|
# Begin compass readings and display on RGB matrix
|
||||||
|
while True:
|
||||||
|
rh = raw_heading(minimums, maximums, zero=zero)
|
||||||
|
hue = rh / 360.0 # Hue is based on compass heading
|
||||||
|
r, g, b = [int(c * 255) for c in colorsys.hsv_to_rgb(hue, 1.0, 1.0)]
|
||||||
|
rgbmatrix5x5.set_all(r, g, b) # Set whole matrix to calculated RGB
|
||||||
|
rgbmatrix5x5.show()
|
||||||
|
print("compass heading: {:0.0f} degrees".format(rh))
|
||||||
|
time.sleep(0.1)
|
||||||
49
vendor/breakout-garden/examples/seismograph/README.md
vendored
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
# Seismograph example
|
||||||
|
|
||||||
|
The Dino-Detect v1.2 beta is a dino stomp detector. It's a
|
||||||
|
UNIX system, I know this.
|
||||||
|
|
||||||
|
## Pre-requisites
|
||||||
|
|
||||||
|
This example requires:
|
||||||
|
|
||||||
|
- A Pimoroni [Breakout Garden](https://shop.pimoroni.com/products/breakout-garden-hat-i2c-spi)
|
||||||
|
- A Pimoroni [LSM303D 6DoF Sensor Breakout](https://shop.pimoroni.com/products/lsm303d-6dof-motion-sensor-breakout)
|
||||||
|
- A Pimoroni [1.12" OLED Breakout (SPI)](https://shop.pimoroni.com/products/1-12-oled-breakout)
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Pop the breakouts into your Breakout Garden, and then run the `install.sh`
|
||||||
|
script in the root of this repository with `sudo ./install.sh` to automagically
|
||||||
|
install the libraries to run the I2C breakouts.
|
||||||
|
|
||||||
|
For this example you'll need to make sure some additional software is installed:
|
||||||
|
|
||||||
|
```
|
||||||
|
sudo apt install python3-pil
|
||||||
|
```
|
||||||
|
|
||||||
|
You'll need to clone and install the library for the 1.12" OLED Breakout (SPI)
|
||||||
|
as follows:
|
||||||
|
|
||||||
|
```
|
||||||
|
git clone https://github.com/pimoroni/sh1106-python
|
||||||
|
sudo ./install.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
This example assumes that you have the OLED plugged into the front slot on the
|
||||||
|
Breakout Garden HAT, which should also work with the Breakout Garden Mini HAT.
|
||||||
|
To change it to the back slot, change `device=1` to `device=0` on the line
|
||||||
|
where the OLED is set up.
|
||||||
|
|
||||||
|
## Running this example
|
||||||
|
|
||||||
|
To run this example, type `./seismograph.py` in the terminal.
|
||||||
|
|
||||||
|
Note that it takes a baseline reading initially to zero out the axes,
|
||||||
|
and then calculates subsequent readings against the baseline, so make
|
||||||
|
sure that your Breakout Garden is sitting still when you start the
|
||||||
|
program.
|
||||||
|
|
||||||
|
The `sensitivity` variable can be changed to make the seismograph more or
|
||||||
|
less sensitive to dino stomps.
|
||||||
BIN
vendor/breakout-garden/examples/seismograph/fonts/Roboto-Black.ttf
vendored
Normal file
BIN
vendor/breakout-garden/examples/seismograph/fonts/Roboto-Regular.ttf
vendored
Normal file
BIN
vendor/breakout-garden/examples/seismograph/images/seismograph.png
vendored
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
122
vendor/breakout-garden/examples/seismograph/seismograph.py
vendored
Executable file
@@ -0,0 +1,122 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
import sys
|
||||||
|
|
||||||
|
try:
|
||||||
|
from PIL import Image
|
||||||
|
from PIL import ImageFont
|
||||||
|
from PIL import ImageDraw
|
||||||
|
except ImportError:
|
||||||
|
print("""This example requires PIL.
|
||||||
|
Install with: sudo apt install python{v}-pil
|
||||||
|
""".format(v="" if sys.version_info.major == 2 else sys.version_info.major))
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
from lsm303d import LSM303D
|
||||||
|
from threading import Thread
|
||||||
|
from luma.core.interface.serial import spi
|
||||||
|
from luma.core.render import canvas
|
||||||
|
from luma.oled.device import sh1106
|
||||||
|
|
||||||
|
print("""This Pimoroni Breakout Garden example requires an
|
||||||
|
LSM303D 6DoF Breakout and a 1.12" OLED Breakout (SPI).
|
||||||
|
|
||||||
|
The Dino-Detect v1.2 beta is a dino stomp detector. It's a
|
||||||
|
UNIX system, I know this.
|
||||||
|
|
||||||
|
Press Ctrl+C to exit.
|
||||||
|
""")
|
||||||
|
|
||||||
|
# Set up OLED
|
||||||
|
|
||||||
|
oled = sh1106(spi(port=0, device=1, gpio_DC=9), rotate=2, height=128, width=128)
|
||||||
|
|
||||||
|
# Load fonts
|
||||||
|
|
||||||
|
rr_path = os.path.abspath(os.path.join(os.path.dirname(__file__), 'fonts', 'Roboto-Regular.ttf'))
|
||||||
|
print(rr_path)
|
||||||
|
rb_path = os.path.abspath(os.path.join(os.path.dirname(__file__), 'fonts', 'Roboto-Black.ttf'))
|
||||||
|
rr_24 = ImageFont.truetype(rr_path, 24)
|
||||||
|
rb_20 = ImageFont.truetype(rb_path, 20)
|
||||||
|
rr_12 = ImageFont.truetype(rr_path, 12)
|
||||||
|
|
||||||
|
# Set up LSM303D motion sensor
|
||||||
|
|
||||||
|
lsm = LSM303D(0x1d)
|
||||||
|
|
||||||
|
samples = []
|
||||||
|
points = []
|
||||||
|
|
||||||
|
sx, sy, sz = lsm.accelerometer() # Starting values to zero out accelerometer
|
||||||
|
|
||||||
|
sensitivity = 5 # Value from 1 to 10. Determines twitchiness of needle
|
||||||
|
|
||||||
|
# Function to thread accelerometer values separately to OLED drawing
|
||||||
|
|
||||||
|
def sample():
|
||||||
|
while True:
|
||||||
|
x, y, z = lsm.accelerometer()
|
||||||
|
|
||||||
|
x -= sx
|
||||||
|
y -= sy
|
||||||
|
z -= sz
|
||||||
|
|
||||||
|
v = y # Change this axis depending on orientation of breakout
|
||||||
|
|
||||||
|
# Scale up or down depending on sensitivity required
|
||||||
|
|
||||||
|
if v < 0:
|
||||||
|
v *= (100 * sensitivity)
|
||||||
|
else:
|
||||||
|
v *= (40 * sensitivity)
|
||||||
|
|
||||||
|
|
||||||
|
# Only keep 96 most recent values in list
|
||||||
|
|
||||||
|
points.append(v)
|
||||||
|
if len(points) > 96:
|
||||||
|
points.pop(0)
|
||||||
|
|
||||||
|
time.sleep(0.05)
|
||||||
|
|
||||||
|
# The thread to measure acclerometer values
|
||||||
|
|
||||||
|
t = Thread(target=sample)
|
||||||
|
t.daemon = True
|
||||||
|
t.start()
|
||||||
|
|
||||||
|
# Wait for at least one data oint
|
||||||
|
|
||||||
|
while len(points) == 0:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# The main loop that draws values to the OLED
|
||||||
|
|
||||||
|
while True:
|
||||||
|
background = Image.open("images/seismograph.png").convert(oled.mode)
|
||||||
|
draw = ImageDraw.ImageDraw(background)
|
||||||
|
|
||||||
|
draw.line([(128, 64), (96, 64 + points[-1])], fill="white")
|
||||||
|
draw.line([(128, 63), (96, 64 + points[-1])], fill="white")
|
||||||
|
draw.line([(128, 65), (96, 64 + points[-1])], fill="white")
|
||||||
|
|
||||||
|
# Draw the seismograph trace
|
||||||
|
|
||||||
|
for i in range(1, len(points)):
|
||||||
|
draw.line([(i - 1, 64 + points[i - 1]), (i, 64 + points[i])], fill="white")
|
||||||
|
|
||||||
|
# Draw the Dino-Detect "branding"
|
||||||
|
|
||||||
|
draw.rectangle([(0, 0), (128, 20)], fill="black")
|
||||||
|
draw.text((0, 1), "AUS (A UNIX System)", fill="white", font=rr_12)
|
||||||
|
draw.line([(0, 20), (128, 20)], fill="white")
|
||||||
|
|
||||||
|
draw.rectangle([(0, 108), (128, 128)], fill="black")
|
||||||
|
draw.text((0, 110), "Dino-Detect v1.2 BETA", fill="white", font=rr_12)
|
||||||
|
draw.line([(0, 108), (128, 108)], fill="white")
|
||||||
|
|
||||||
|
# Display on the OLED
|
||||||
|
|
||||||
|
oled.display(background)
|
||||||
46
vendor/breakout-garden/examples/spirit-level/README.md
vendored
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
# Spirit level example
|
||||||
|
|
||||||
|
This examples emulates a circular spirit level, using the LCD to draw
|
||||||
|
the spirit level and the 3DoF motion sensor to detect orientation.
|
||||||
|
|
||||||
|
## Pre-requisites
|
||||||
|
|
||||||
|
This example requires:
|
||||||
|
|
||||||
|
- A Pimoroni [Breakout Garden](https://shop.pimoroni.com/products/breakout-garden-hat-i2c-spi)
|
||||||
|
- A Pimoroni [MSA301 3DoF Motion Sensor Breakout](https://shop.pimoroni.com/products/msa301-3dof-motion-sensor-breakout)
|
||||||
|
- A Pimoroni [1.3" LCD Breakout](https://shop.pimoroni.com/products/1-3-spi-colour-lcd-240x240-breakout)
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Pop the breakouts into your Breakout Garden, and then run the `install.sh`
|
||||||
|
script in the root of this repository with `sudo ./install.sh` to automagically
|
||||||
|
install the libraries to run the I2C breakouts.
|
||||||
|
|
||||||
|
For this example you'll need to make sure some additional software is installed:
|
||||||
|
|
||||||
|
```
|
||||||
|
sudo apt install python3-pil
|
||||||
|
```
|
||||||
|
|
||||||
|
You'll need to clone and install the library for the 1.3" LCD Breakout
|
||||||
|
as follows:
|
||||||
|
|
||||||
|
```
|
||||||
|
git clone https://github.com/pimoroni/st7789-python
|
||||||
|
cd library
|
||||||
|
sudo python3 setup.py install
|
||||||
|
```
|
||||||
|
|
||||||
|
This example assumes that you have the LCD plugged into the front slot on the
|
||||||
|
Breakout Garden HAT, which should also work with the Breakout Garden Mini HAT.
|
||||||
|
To change it to the back slot, change `cs=ST7789.BG_SPI_CS_FRONT` to
|
||||||
|
`cs=ST7789.BG_SPI_CS_BACK` and `backlight=19` to backlight=18` on the line
|
||||||
|
where the LCD is set up.
|
||||||
|
|
||||||
|
## Running this example
|
||||||
|
|
||||||
|
To run this example, type `./spirit-level.py` in the terminal.
|
||||||
|
|
||||||
|
It's assumed that you have the MSA301 breakout and LCD breakout lying flat, so
|
||||||
|
your Raspberry Pi or Raspberry Pi Zero will be perpendicular to your flat surface.
|
||||||
BIN
vendor/breakout-garden/examples/spirit-level/images/spirit-level-bubble.png
vendored
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
BIN
vendor/breakout-garden/examples/spirit-level/images/spirit-level-crosshair-black.png
vendored
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
vendor/breakout-garden/examples/spirit-level/images/spirit-level-crosshair-red.png
vendored
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
vendor/breakout-garden/examples/spirit-level/images/spirit-level.png
vendored
Normal file
|
After Width: | Height: | Size: 22 KiB |
107
vendor/breakout-garden/examples/spirit-level/spirit-level.py
vendored
Executable file
@@ -0,0 +1,107 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
from PIL import Image, ImageDraw, ImageFont, ImageFilter
|
||||||
|
import math
|
||||||
|
import msa301
|
||||||
|
import ST7789 as ST7789
|
||||||
|
|
||||||
|
print("""This Pimoroni Breakout Garden example requires an
|
||||||
|
MSA301 3DoF Motion Sensor Breakout and a 1.3" LCD Breakout.
|
||||||
|
|
||||||
|
This examples emulates a circular spirit level, using the
|
||||||
|
LCD to draw the spirit level and the accelerometer to
|
||||||
|
detect orientation.
|
||||||
|
|
||||||
|
Press Ctrl+C to exit.
|
||||||
|
""")
|
||||||
|
|
||||||
|
# Set up LCD
|
||||||
|
disp = ST7789.ST7789(
|
||||||
|
port=0,
|
||||||
|
cs=ST7789.BG_SPI_CS_FRONT,
|
||||||
|
dc=9,
|
||||||
|
backlight=19,
|
||||||
|
spi_speed_hz=80 * 1000 * 1000
|
||||||
|
)
|
||||||
|
|
||||||
|
WIDTH = disp.width
|
||||||
|
HEIGHT = disp.height
|
||||||
|
|
||||||
|
disp.begin()
|
||||||
|
|
||||||
|
# Load the image assets
|
||||||
|
level = Image.open("images/spirit-level.png").convert("RGBA")
|
||||||
|
bubble = Image.open("images/spirit-level-bubble.png").convert("RGBA")
|
||||||
|
crosshair_black = Image.open("images/spirit-level-crosshair-black.png").convert("RGBA")
|
||||||
|
crosshair_red = Image.open("images/spirit-level-crosshair-red.png").convert("RGBA")
|
||||||
|
|
||||||
|
# Sizes/coordinates of things
|
||||||
|
bubble_dia = 64
|
||||||
|
bubble_rad = bubble_dia / 2
|
||||||
|
circle_dia = 190
|
||||||
|
circle_rad = circle_dia / 2
|
||||||
|
border = (WIDTH - circle_dia) / 2
|
||||||
|
|
||||||
|
centre_x = WIDTH / 2
|
||||||
|
centre_y = HEIGHT / 2
|
||||||
|
|
||||||
|
# Set up MSA301
|
||||||
|
accel = msa301.MSA301()
|
||||||
|
accel.set_power_mode('normal')
|
||||||
|
|
||||||
|
x_vals = []
|
||||||
|
y_vals = []
|
||||||
|
|
||||||
|
smooth = 3
|
||||||
|
|
||||||
|
while True:
|
||||||
|
# Read MSA301 values
|
||||||
|
x, y, z = accel.get_measurements()
|
||||||
|
|
||||||
|
# z = x-axis, y = y-axis
|
||||||
|
bubble_centre_x = (2 - (z + 1)) * (circle_dia / 2) + border
|
||||||
|
bubble_centre_y = (y + 1) * (circle_dia / 2) + border
|
||||||
|
|
||||||
|
# Work out vector length to check if outside circle
|
||||||
|
delta_x = bubble_centre_x - centre_x
|
||||||
|
delta_y = bubble_centre_y - centre_y
|
||||||
|
|
||||||
|
vector_length = math.sqrt(delta_x ** 2 + delta_y ** 2)
|
||||||
|
|
||||||
|
# If outside circle, scale position back down relatively
|
||||||
|
if vector_length > circle_rad - bubble_rad:
|
||||||
|
scale = (circle_rad - bubble_rad) / vector_length
|
||||||
|
bubble_centre_x = centre_x + delta_x * scale
|
||||||
|
bubble_centre_y = centre_y + delta_y * scale
|
||||||
|
|
||||||
|
|
||||||
|
# Average x and y values to smooth jitter
|
||||||
|
x_vals.append(bubble_centre_x)
|
||||||
|
|
||||||
|
if len(x_vals) > smooth:
|
||||||
|
x_vals = x_vals[1:]
|
||||||
|
|
||||||
|
bubble_centre_x = int(sum(x_vals) / len(x_vals))
|
||||||
|
|
||||||
|
y_vals.append(bubble_centre_y)
|
||||||
|
|
||||||
|
if len(y_vals) > smooth:
|
||||||
|
y_vals = y_vals[1:]
|
||||||
|
|
||||||
|
bubble_centre_y = int(sum(y_vals) / len(y_vals))
|
||||||
|
|
||||||
|
|
||||||
|
# Use red crosshair if bubble is close to centre
|
||||||
|
if (-0.05 < z < 0.05) and (-0.05 < y < 0.05):
|
||||||
|
crosshair = crosshair_red
|
||||||
|
else:
|
||||||
|
crosshair = crosshair_black
|
||||||
|
|
||||||
|
# Construct image
|
||||||
|
image = Image.new('RGBA', (WIDTH, HEIGHT), color=(0, 0, 0, 0))
|
||||||
|
image.paste(level, (0, 0))
|
||||||
|
image.paste(bubble, (int(bubble_centre_x - (bubble_dia / 2)), int(bubble_centre_y - (bubble_dia / 2))), mask=bubble)
|
||||||
|
image.paste(crosshair, (0, 0), mask=crosshair)
|
||||||
|
|
||||||
|
# Display on LCD
|
||||||
|
disp.display(image)
|
||||||
49
vendor/breakout-garden/examples/uv-warning/README.md
vendored
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
# UV warning example
|
||||||
|
|
||||||
|
This example uses a VEML6075 UVA/B sensor, a 5x5 RGB matrix, and a 1.2" OLED breakout
|
||||||
|
to display average UV index as a traffic light warning on the RGB matrix, with green
|
||||||
|
being a low UV index, yellow/orange being moderate, and red high or extreme. The numerical
|
||||||
|
values and a descriptive warning are displayed on the OLED.
|
||||||
|
|
||||||
|
## Pre-requisites
|
||||||
|
|
||||||
|
This example requires:
|
||||||
|
|
||||||
|
- A Pimoroni [Breakout Garden](https://shop.pimoroni.com/products/breakout-garden-hat-i2c-spi)
|
||||||
|
- A Pimoroni [VEML6075 UVA/B Sensor Breakout Breakout](https://shop.pimoroni.com/products/veml6075-uva-b-sensor-breakout)
|
||||||
|
- A Pimoroni [5x5 RGB Matrix Breakout](https://shop.pimoroni.com/products/5x5-rgb-matrix-breakout)
|
||||||
|
- A Pimoroni [1.12" OLED Breakout (SPI)](https://shop.pimoroni.com/products/1-12-oled-breakout)
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Pop the breakouts into your Breakout Garden, and then run the `install.sh`
|
||||||
|
script in the root of this repository with `sudo ./install.sh` to automagically
|
||||||
|
install the libraries to run the I2C breakouts.
|
||||||
|
|
||||||
|
For this example you'll need to make sure some additional software is installed:
|
||||||
|
|
||||||
|
```
|
||||||
|
sudo apt install python3-pil
|
||||||
|
```
|
||||||
|
|
||||||
|
You'll need to clone and install the library for the 1.12" OLED Breakout (SPI)
|
||||||
|
as follows:
|
||||||
|
|
||||||
|
```
|
||||||
|
git clone https://github.com/pimoroni/sh1106-python
|
||||||
|
sudo ./install.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
This example assumes that you have the OLED plugged into the front slot on the
|
||||||
|
Breakout Garden HAT, which should also work with the Breakout Garden Mini HAT.
|
||||||
|
To change it to the back slot, change `device=1` to `device=0` on the line
|
||||||
|
where the OLED is set up.
|
||||||
|
|
||||||
|
## Running this example
|
||||||
|
|
||||||
|
To run this example, type `./uv-warning.py` in the terminal
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
You might want rotate your Pi and Breakout Garden so that the UV sensor is facing
|
||||||
|
upwards at a better angle to detect UV light.
|
||||||
BIN
vendor/breakout-garden/examples/uv-warning/fonts/Roboto-Black.ttf
vendored
Normal file
BIN
vendor/breakout-garden/examples/uv-warning/fonts/Roboto-Regular.ttf
vendored
Normal file
BIN
vendor/breakout-garden/examples/uv-warning/images/uv-warning.png
vendored
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
99
vendor/breakout-garden/examples/uv-warning/uv-warning.py
vendored
Executable file
@@ -0,0 +1,99 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import veml6075
|
||||||
|
import smbus
|
||||||
|
import colorsys
|
||||||
|
from PIL import Image
|
||||||
|
from PIL import ImageFont
|
||||||
|
from PIL import ImageDraw
|
||||||
|
from luma.core.interface.serial import spi
|
||||||
|
from luma.core.render import canvas
|
||||||
|
from luma.oled.device import sh1106
|
||||||
|
from rgbmatrix5x5 import RGBMatrix5x5
|
||||||
|
|
||||||
|
print("""
|
||||||
|
This Pimoroni Breakout Garden example requires a VEML6075 UV
|
||||||
|
Breakout, a 5x5 RGB Matrix Breakout, and a 1.12" OLED Breakout (SPI).
|
||||||
|
|
||||||
|
The UV-O-Meter 3000 displays UV levels visually and in text.
|
||||||
|
|
||||||
|
Press Ctrl+C a couple times to exit.
|
||||||
|
""")
|
||||||
|
|
||||||
|
# Map UV index to a descriptive level
|
||||||
|
|
||||||
|
def uv_to_description(avg_uv_index):
|
||||||
|
if avg_uv_index < 3:
|
||||||
|
return "LOW"
|
||||||
|
elif 3 <= avg_uv_index < 6:
|
||||||
|
return "MEDIUM"
|
||||||
|
elif 6 <= avg_uv_index < 8:
|
||||||
|
return "HIGH"
|
||||||
|
elif 8 <= avg_uv_index < 11:
|
||||||
|
return "V. HIGH"
|
||||||
|
elif avg_uv_index > 11:
|
||||||
|
return "EXTREME"
|
||||||
|
else:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
bus = smbus.SMBus(1)
|
||||||
|
|
||||||
|
# Set up UV sensor
|
||||||
|
uv_sensor = veml6075.VEML6075(i2c_dev=bus)
|
||||||
|
uv_sensor.set_shutdown(False)
|
||||||
|
uv_sensor.set_high_dynamic_range(False)
|
||||||
|
uv_sensor.set_integration_time('100ms')
|
||||||
|
|
||||||
|
# Set up RGB matrix
|
||||||
|
rgbmatrix5x5 = RGBMatrix5x5()
|
||||||
|
rgbmatrix5x5.set_clear_on_exit()
|
||||||
|
rgbmatrix5x5.set_brightness(1.0)
|
||||||
|
|
||||||
|
# Set up OLED
|
||||||
|
oled = sh1106(spi(port=0, device=1, gpio_DC=9), rotate=2, height=128, width=128)
|
||||||
|
|
||||||
|
# Load fonts
|
||||||
|
rr_path = os.path.abspath(os.path.join(os.path.dirname(__file__), 'fonts', 'Roboto-Regular.ttf'))
|
||||||
|
rb_path = os.path.abspath(os.path.join(os.path.dirname(__file__), 'fonts', 'Roboto-Black.ttf'))
|
||||||
|
rb_15 = ImageFont.truetype(rb_path, 15)
|
||||||
|
rr_15 = ImageFont.truetype(rr_path, 15)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
img = Image.open("images/uv-warning.png").convert(oled.mode)
|
||||||
|
draw = ImageDraw.Draw(img)
|
||||||
|
|
||||||
|
draw.rectangle([(0, 0), (128, 128)], fill="black")
|
||||||
|
|
||||||
|
# Get UV data and calculate indices
|
||||||
|
uva, uvb = uv_sensor.get_measurements()
|
||||||
|
uv_comp1, uv_comp2 = uv_sensor.get_comparitor_readings()
|
||||||
|
uv_indices = uv_sensor.convert_to_index(uva, uvb, uv_comp1, uv_comp2)
|
||||||
|
uva_index, uvb_index, avg_uv_index = uv_indices
|
||||||
|
|
||||||
|
# Draw UV data to OLED
|
||||||
|
draw.text((0, 0), "UV-O-Meter 3000", fill="white", font=rb_15)
|
||||||
|
draw.text((0, 30), "UVA index: {:05.02f}".format(uva_index), fill="white", font=rr_15)
|
||||||
|
draw.text((0, 48), "UVB index: {:05.02f}".format(uvb_index), fill="white", font=rr_15)
|
||||||
|
draw.text((0, 66), "Avg. index: {:05.02f}".format(avg_uv_index), fill="white", font=rr_15)
|
||||||
|
draw.text((0, 100), "Level: {}".format(uv_to_description(avg_uv_index)), fill="white", font=rb_15)
|
||||||
|
|
||||||
|
# Map avg. UV index to colour, from green to red
|
||||||
|
hue = (10.001 - min(10, avg_uv_index)) / 30
|
||||||
|
sat = 1.0
|
||||||
|
val = 1.0
|
||||||
|
r, g, b = [int(255 * c) for c in colorsys.hsv_to_rgb(hue, sat, val)]
|
||||||
|
|
||||||
|
# Light RGB matrix with calculated colour
|
||||||
|
rgbmatrix5x5.set_all(r, g, b)
|
||||||
|
rgbmatrix5x5.show()
|
||||||
|
oled.display(img)
|
||||||
|
|
||||||
|
# Also print current UV index
|
||||||
|
print ('Avg. UV index: {}'.format(avg_uv_index))
|
||||||
|
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
sys.exit(0)
|
||||||
53
vendor/breakout-garden/examples/weather/README.md
vendored
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
# Weather example
|
||||||
|
|
||||||
|
This example turns your Breakout Garden into a mini weather display
|
||||||
|
combining indoor temperature and pressure data with a weather icon
|
||||||
|
indicating the current local weather conditions.
|
||||||
|
|
||||||
|
## Pre-requisites
|
||||||
|
|
||||||
|
This example requires:
|
||||||
|
|
||||||
|
- A Pimoroni [Breakout Garden](https://shop.pimoroni.com/products/breakout-garden-hat-i2c-spi)
|
||||||
|
- A Pimoroni [BME680 Breakout](https://shop.pimoroni.com/products/bme680-breakout)
|
||||||
|
- A Pimoroni [1.12" OLED Breakout (SPI)](https://shop.pimoroni.com/products/1-12-oled-breakout)
|
||||||
|
|
||||||
|
You'll need the requests (`sudo pip install requests`), geocoder (`sudo pip install geocoder`),
|
||||||
|
and BeautifulSoup4 (`sudo pip install beautifulsoup4`) libraries to query the Dark Sky weather page.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Pop the breakouts into your Breakout Garden, and then run the `install.sh`
|
||||||
|
script in the root of this repository with `sudo ./install.sh` to automagically
|
||||||
|
install the libraries to run the I2C breakouts.
|
||||||
|
|
||||||
|
For this example you'll need to make sure some additional software is installed:
|
||||||
|
|
||||||
|
```
|
||||||
|
sudo apt install python3-lxml python3-pil
|
||||||
|
sudo pip3 install requests geocoder beautifulsoup4
|
||||||
|
```
|
||||||
|
|
||||||
|
You'll need to clone and install the library for the 1.12" OLED Breakout (SPI)
|
||||||
|
as follows:
|
||||||
|
|
||||||
|
```
|
||||||
|
git clone https://github.com/pimoroni/sh1106-python
|
||||||
|
sudo ./install.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
This example assumes that you have the OLED plugged into the front slot on the
|
||||||
|
Breakout Garden HAT, which should also work with the Breakout Garden Mini HAT.
|
||||||
|
To change it to the back slot, change `device=1` to `device=0` on the line
|
||||||
|
where the OLED is set up.
|
||||||
|
|
||||||
|
|
||||||
|
## Running this example
|
||||||
|
|
||||||
|
To run this example, type `./weather.py` in the terminal
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
This example uses Sheffield as the default location, so you'll need to specify your city and
|
||||||
|
country code at the top of the file, changing the variables called `CITY` and `COUNTRYCODE`
|
||||||
|
to your current location.
|
||||||
BIN
vendor/breakout-garden/examples/weather/fonts/Roboto-Black.ttf
vendored
Normal file
BIN
vendor/breakout-garden/examples/weather/fonts/Roboto-Regular.ttf
vendored
Normal file
BIN
vendor/breakout-garden/examples/weather/icons/cloud.png
vendored
Executable file
|
After Width: | Height: | Size: 4.6 KiB |
BIN
vendor/breakout-garden/examples/weather/icons/rain.png
vendored
Executable file
|
After Width: | Height: | Size: 4.6 KiB |
BIN
vendor/breakout-garden/examples/weather/icons/snow.png
vendored
Executable file
|
After Width: | Height: | Size: 4.6 KiB |
BIN
vendor/breakout-garden/examples/weather/icons/storm.png
vendored
Executable file
|
After Width: | Height: | Size: 4.6 KiB |
BIN
vendor/breakout-garden/examples/weather/icons/sun.png
vendored
Executable file
|
After Width: | Height: | Size: 4.6 KiB |
BIN
vendor/breakout-garden/examples/weather/icons/wind.png
vendored
Executable file
|
After Width: | Height: | Size: 4.6 KiB |
BIN
vendor/breakout-garden/examples/weather/images/weather.png
vendored
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
218
vendor/breakout-garden/examples/weather/weather.py
vendored
Executable file
@@ -0,0 +1,218 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
import datetime
|
||||||
|
import glob
|
||||||
|
import logging
|
||||||
|
import sys
|
||||||
|
|
||||||
|
try:
|
||||||
|
import requests
|
||||||
|
import geocoder
|
||||||
|
import lxml
|
||||||
|
from bs4 import BeautifulSoup
|
||||||
|
from PIL import Image
|
||||||
|
from PIL import ImageFont
|
||||||
|
from PIL import ImageDraw
|
||||||
|
except ImportError:
|
||||||
|
print("""
|
||||||
|
This script requires several modules to run correctly.
|
||||||
|
Install with:
|
||||||
|
sudo pip install requests geocoder beautifulsoup4
|
||||||
|
sudo apt install python{v}-lxml python{v}-pil
|
||||||
|
""".format(v="" if sys.version_info.major == 2 else sys.version_info.major))
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
import bme680
|
||||||
|
from luma.core.interface.serial import spi
|
||||||
|
from luma.core.error import DeviceNotFoundError
|
||||||
|
from luma.oled.device import sh1106
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
TEMPERATURE_UPDATE_INTERVAL = 0.1 # in seconds
|
||||||
|
|
||||||
|
# Default to Sheffield-on-Sea for location
|
||||||
|
CITY = "Sheffield"
|
||||||
|
COUNTRYCODE = "GB"
|
||||||
|
|
||||||
|
# Used to calibrate the sensor
|
||||||
|
TEMP_OFFSET = 0.0
|
||||||
|
|
||||||
|
|
||||||
|
logging.basicConfig(level=os.environ.get("LOGLEVEL", "WARNING"))
|
||||||
|
|
||||||
|
|
||||||
|
print("""This Pimoroni Breakout Garden example requires a
|
||||||
|
BME680 Environmental Sensor Breakout and a 1.12" OLED Breakout.
|
||||||
|
|
||||||
|
This example turns your Breakout Garden into a mini weather display
|
||||||
|
combining indoor temperature and pressure data with a weather icon
|
||||||
|
indicating the current local weather conditions.
|
||||||
|
|
||||||
|
Press Ctrl+C a couple times to exit.
|
||||||
|
""")
|
||||||
|
|
||||||
|
|
||||||
|
# Convert a city name and country code to latitude and longitude
|
||||||
|
def get_coords(address):
|
||||||
|
g = geocoder.arcgis(address)
|
||||||
|
coords = g.latlng
|
||||||
|
logging.info("Location coordinates: %s", coords)
|
||||||
|
return coords
|
||||||
|
|
||||||
|
|
||||||
|
# Query Dark Sky (https://darksky.net/) to scrape current weather data
|
||||||
|
def get_weather(coords):
|
||||||
|
weather = {}
|
||||||
|
try:
|
||||||
|
res = requests.get("https://darksky.net/forecast/{}/uk212/en".format(","
|
||||||
|
.join([str(c) for c in coords])))
|
||||||
|
if res.status_code == 200:
|
||||||
|
soup = BeautifulSoup(res.content, "lxml")
|
||||||
|
curr = soup.find("span", "currently")
|
||||||
|
if curr:
|
||||||
|
img_name = curr.img["alt"].split()[0]
|
||||||
|
logging.info("Weather summary: %s", img_name)
|
||||||
|
weather["summary"] = img_name
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
logging.error("Could not get weather data from DarkSky: {}".format(e))
|
||||||
|
pass
|
||||||
|
|
||||||
|
return weather
|
||||||
|
|
||||||
|
|
||||||
|
# This maps the weather summary from Dark Sky
|
||||||
|
# to the appropriate weather icons
|
||||||
|
icon_map = {
|
||||||
|
"snow": ["snow", "sleet"],
|
||||||
|
"rain": ["rain"],
|
||||||
|
"cloud": ["fog", "cloudy", "partly-cloudy-day", "partly-cloudy-night"],
|
||||||
|
"sun": ["clear-day", "clear-night"],
|
||||||
|
"storm": [],
|
||||||
|
"wind": ["wind"]
|
||||||
|
}
|
||||||
|
|
||||||
|
# Pre-load icons into a dictionary with PIL
|
||||||
|
icons = {}
|
||||||
|
|
||||||
|
for icon in glob.glob("icons/*.png"):
|
||||||
|
icon_name = icon.split("/")[1].replace(".png", "")
|
||||||
|
icon_image = Image.open(icon)
|
||||||
|
icons[icon_name] = icon_image
|
||||||
|
|
||||||
|
|
||||||
|
location_string = "{city}, {countrycode}".format(city=CITY,
|
||||||
|
countrycode=COUNTRYCODE)
|
||||||
|
coords = get_coords(location_string)
|
||||||
|
|
||||||
|
|
||||||
|
def get_weather_icon(weather):
|
||||||
|
if weather:
|
||||||
|
summary = weather["summary"]
|
||||||
|
|
||||||
|
for icon in icon_map:
|
||||||
|
if summary in icon_map[icon]:
|
||||||
|
logging.info("Weather icon: %s", icon)
|
||||||
|
return icons[icon]
|
||||||
|
logging.error("Could not determine icon for weather")
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
logging.error("No weather information provided to get icon")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
# Get initial weather data for the given location
|
||||||
|
weather_icon = get_weather_icon(get_weather(coords))
|
||||||
|
|
||||||
|
# Set up OLED
|
||||||
|
oled = sh1106(spi(port=0, device=1, gpio_DC=9), rotate=2, height=128, width=128)
|
||||||
|
|
||||||
|
# Set up BME680 sensor
|
||||||
|
sensor = bme680.BME680()
|
||||||
|
|
||||||
|
sensor.set_humidity_oversample(bme680.OS_2X)
|
||||||
|
sensor.set_pressure_oversample(bme680.OS_4X)
|
||||||
|
sensor.set_temperature_oversample(bme680.OS_8X)
|
||||||
|
sensor.set_filter(bme680.FILTER_SIZE_3)
|
||||||
|
sensor.set_temp_offset(TEMP_OFFSET)
|
||||||
|
|
||||||
|
# Load fonts
|
||||||
|
rr_path = os.path.abspath(os.path.join(os.path.dirname(__file__), 'fonts',
|
||||||
|
'Roboto-Regular.ttf'))
|
||||||
|
rb_path = os.path.abspath(os.path.join(os.path.dirname(__file__), 'fonts',
|
||||||
|
'Roboto-Black.ttf'))
|
||||||
|
rr_24 = ImageFont.truetype(rr_path, 24)
|
||||||
|
rb_20 = ImageFont.truetype(rb_path, 20)
|
||||||
|
rr_12 = ImageFont.truetype(rr_path, 12)
|
||||||
|
|
||||||
|
# Fetch sensor dating first so that device settings take effect
|
||||||
|
sensor.get_sensor_data()
|
||||||
|
# Initial values
|
||||||
|
low_temp = sensor.data.temperature
|
||||||
|
high_temp = sensor.data.temperature
|
||||||
|
curr_date = datetime.date.today().day
|
||||||
|
|
||||||
|
last_checked = time.time()
|
||||||
|
|
||||||
|
# Main loop
|
||||||
|
while True:
|
||||||
|
# Limit calls to Dark Sky to 1 per minute
|
||||||
|
if time.time() - last_checked > 60:
|
||||||
|
weather_icon = get_weather_icon(get_weather(coords))
|
||||||
|
last_checked = time.time()
|
||||||
|
|
||||||
|
# Load in the background image
|
||||||
|
background = Image.open("images/weather.png").convert(oled.mode)
|
||||||
|
|
||||||
|
# Place the weather icon and draw the background
|
||||||
|
if weather_icon:
|
||||||
|
background.paste(weather_icon, (10, 46))
|
||||||
|
draw = ImageDraw.ImageDraw(background)
|
||||||
|
|
||||||
|
# Gets temp. and press. and keeps track of daily min and max temp
|
||||||
|
if sensor.get_sensor_data():
|
||||||
|
temp = sensor.data.temperature
|
||||||
|
press = sensor.data.pressure
|
||||||
|
if datetime.datetime.today().day == curr_date:
|
||||||
|
if temp < low_temp:
|
||||||
|
low_temp = temp
|
||||||
|
elif temp > high_temp:
|
||||||
|
high_temp = temp
|
||||||
|
else:
|
||||||
|
curr_date = datetime.datetime.today().day
|
||||||
|
low_temp = temp
|
||||||
|
high_temp = temp
|
||||||
|
|
||||||
|
# Write temp. and press. to image
|
||||||
|
draw.text((8, 22), "{0:4.0f}".format(press),
|
||||||
|
fill="white", font=rb_20)
|
||||||
|
draw.text((86, 12), u"{0:2.0f}°".format(temp),
|
||||||
|
fill="white", font=rb_20)
|
||||||
|
|
||||||
|
# Write min and max temp. to image
|
||||||
|
draw.text((80, 0), u"max: {0:2.0f}°".format(high_temp),
|
||||||
|
fill="white", font=rr_12)
|
||||||
|
draw.text((80, 110), u"min: {0:2.0f}°".format(low_temp),
|
||||||
|
fill="white", font=rr_12)
|
||||||
|
|
||||||
|
# Write the 24h time and blink the separator every second
|
||||||
|
if int(time.time()) % 2 == 0:
|
||||||
|
draw.text((4, 98), datetime.datetime.now().strftime("%H:%M"),
|
||||||
|
fill="white", font=rr_24)
|
||||||
|
else:
|
||||||
|
draw.text((4, 98), datetime.datetime.now().strftime("%H %M"),
|
||||||
|
fill="white", font=rr_24)
|
||||||
|
|
||||||
|
# These lines display the temp. on the thermometer image
|
||||||
|
draw.rectangle([(97, 43), (100, 86)], fill="black")
|
||||||
|
temp_offset = 86 - ((86 - 43) * ((temp - 20) / (32 - 20)))
|
||||||
|
draw.rectangle([(97, temp_offset), (100, 86)], fill="white")
|
||||||
|
|
||||||
|
# Display the completed image on the OLED
|
||||||
|
oled.display(background)
|
||||||
|
|
||||||
|
time.sleep(TEMPERATURE_UPDATE_INTERVAL)
|
||||||
57
vendor/breakout-garden/install-legacy.sh
vendored
Executable file
@@ -0,0 +1,57 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
printf "Breakout Garden: Auto Installer\n\n"
|
||||||
|
|
||||||
|
if [ $(id -u) -ne 0 ]; then
|
||||||
|
printf "Script must be run as root. Try 'sudo ./install.sh'\n"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf "Please plug the breakouts you would like to install into your Breakout Garden\n\n"
|
||||||
|
read -p "Press enter to continue..."
|
||||||
|
printf "\n"
|
||||||
|
|
||||||
|
WORKING_DIR=`pwd`
|
||||||
|
TMP_DIR="/tmp/breakout-garden"
|
||||||
|
|
||||||
|
results=`python autodetect.py`
|
||||||
|
found=`echo "$results" | wc -l`
|
||||||
|
|
||||||
|
if [[ "$found" -eq "0" ]] || [[ "$results" == "" ]]; then
|
||||||
|
printf "Sorry, I couldn't find any breakouts!\n"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf "Found $found breadkout(s):\n"
|
||||||
|
|
||||||
|
echo "$results" | while read line; do
|
||||||
|
printf "$line\n"
|
||||||
|
done
|
||||||
|
|
||||||
|
printf "\n"
|
||||||
|
|
||||||
|
read -p "Press enter to continue..."
|
||||||
|
|
||||||
|
if [[ ! -d "$TMP_DIR" ]]; then
|
||||||
|
mkdir "$TMP_DIR"
|
||||||
|
fi
|
||||||
|
|
||||||
|
python autodetect.py --install | while read line; do
|
||||||
|
printf "Installing $line\n"
|
||||||
|
git_dir="$line"
|
||||||
|
cd $TMP_DIR
|
||||||
|
if [[ ! -d "$git_dir/.git" ]]; then
|
||||||
|
rm -f $git_dir
|
||||||
|
git clone https://github.com/pimoroni/$line $git_dir
|
||||||
|
fi
|
||||||
|
cd $git_dir
|
||||||
|
git pull origin master
|
||||||
|
if [[ -f "install.sh" ]]; then
|
||||||
|
./install.sh
|
||||||
|
else
|
||||||
|
printf "Warning: No install.sh found for $line. Skipping!";
|
||||||
|
fi
|
||||||
|
cd $WORKING_DIR
|
||||||
|
done
|
||||||
|
|
||||||
|
printf "\nBreakout Garden setup complete! Enjoy your breakouts.\n"
|
||||||
313
vendor/breakout-garden/install.sh
vendored
Executable file
@@ -0,0 +1,313 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
WORKING_DIR=`pwd`
|
||||||
|
TMP_DIR="/tmp/breakout-garden"
|
||||||
|
LOG_FILE="$TMP_DIR/install.log"
|
||||||
|
ACTION="install"
|
||||||
|
VERBOSE=""
|
||||||
|
FORCE=""
|
||||||
|
|
||||||
|
DEVICES=()
|
||||||
|
STATUSES=()
|
||||||
|
REPOS=()
|
||||||
|
|
||||||
|
PADDING=5
|
||||||
|
|
||||||
|
success() {
|
||||||
|
printf "$(tput setaf 2)$1$(tput setaf 7)"
|
||||||
|
}
|
||||||
|
|
||||||
|
inform() {
|
||||||
|
printf "$(tput setaf 6)$1$(tput setaf 7)"
|
||||||
|
}
|
||||||
|
|
||||||
|
warning() {
|
||||||
|
printf "$(tput setaf 1)$1$(tput setaf 7)"
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ $(id -u) -ne 0 ]; then
|
||||||
|
printf "Breakout Garden: Installer\n\n"
|
||||||
|
inform "Script must be run as root. Try 'sudo ./install.sh'\n"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -c "/dev/i2c-1" ]; then
|
||||||
|
raspi-config nonint do_i2c 0
|
||||||
|
STATUS=$?
|
||||||
|
if [ $STATUS -eq 0 ]; then
|
||||||
|
inform "\nBreakout Garden requires I2C. We've enabled it for you.\n"
|
||||||
|
else
|
||||||
|
warning "\nWarning, Breakout Garden requires I2C but we couldn't enable it.\n"
|
||||||
|
printf "\nPlease try 'curl https://get.pimoroni.com/i2c | bash' to enable I2C first.\n"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
sleep 0.1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -d "$TMP_DIR" ]; then
|
||||||
|
mkdir $TMP_DIR
|
||||||
|
fi
|
||||||
|
|
||||||
|
while getopts "uvf" option; do
|
||||||
|
case $option in
|
||||||
|
u ) ACTION="uninstall";VERBOSE="true";;
|
||||||
|
v ) VERBOSE="true";;
|
||||||
|
f ) FORCE="true";;
|
||||||
|
\? ) printf "Invalid option: -$OPTARG\n"; exit 1;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
DETECTED=`python autodetect.py --install`
|
||||||
|
COUNT=`echo -e "$DETECTED" | wc -l`
|
||||||
|
|
||||||
|
if [[ "$COUNT" -eq "0" ]] || [[ "$DETECTED" == "" ]]; then
|
||||||
|
printf "Sorry, I couldn't find any breakouts!\n"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -f "/usr/bin/python3" ]]; then
|
||||||
|
DETECTED3=`python3 autodetect.py --install`
|
||||||
|
fi
|
||||||
|
|
||||||
|
array_index () {
|
||||||
|
local -n array=$1
|
||||||
|
string=$2
|
||||||
|
for i in "${!array[@]}"; do
|
||||||
|
if [[ "${array[$i]}" = "$string" ]]; then
|
||||||
|
return $i
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
check_status () {
|
||||||
|
index=0
|
||||||
|
|
||||||
|
# Iterate through Python3 detected packages
|
||||||
|
# and build out arrays of devices, statuses and Git repos
|
||||||
|
while read line; do
|
||||||
|
IFS='|' read -r -a package <<< "$line"
|
||||||
|
package_name=${package[0]}
|
||||||
|
package_library=${package[1]}
|
||||||
|
package_status=${package[2]}
|
||||||
|
DEVICES[$index]=$package_name
|
||||||
|
STATUSES[$index]=$package_status
|
||||||
|
REPOS[$index]=$package_library
|
||||||
|
index=$(($index+1))
|
||||||
|
done < <(echo -e "$DETECTED")
|
||||||
|
|
||||||
|
# Iterate through Python3 detected packages
|
||||||
|
# and update status to required if they are missing
|
||||||
|
if [[ ! "$DETECTED3" == "" ]]; then
|
||||||
|
while read line; do
|
||||||
|
IFS='|' read -r -a package <<< "$line"
|
||||||
|
package_name=${package[0]}
|
||||||
|
package_library=${package[1]}
|
||||||
|
package_status=${package[2]}
|
||||||
|
|
||||||
|
# Find the index of the package in our original array
|
||||||
|
# that we produced in the first loop above
|
||||||
|
array_index DEVICES "$package_name"
|
||||||
|
index=$?
|
||||||
|
|
||||||
|
if [[ ! "$index" == "-1" ]] && [[ "$package_status" == "required" ]]; then
|
||||||
|
STATUSES[$index]=$package_status
|
||||||
|
fi
|
||||||
|
done < <(echo -e "$DETECTED3")
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
do_uninstall () {
|
||||||
|
index=$1
|
||||||
|
package_name=${DEVICES[$index]}
|
||||||
|
package_library=${REPOS[$index]}
|
||||||
|
|
||||||
|
git_dir=$package_library
|
||||||
|
cd $TMP_DIR
|
||||||
|
if [[ ! -d "$git_dir/.git" ]]; then
|
||||||
|
if [[ ! "$VERBOSE" = "" ]]; then
|
||||||
|
rm -f $git_dir > $LOG_FILE
|
||||||
|
git clone https://github.com/pimoroni/$package_library $git_dir
|
||||||
|
else
|
||||||
|
rm -f $git_dir > $LOG_FILE 2>&1
|
||||||
|
git clone https://github.com/pimoroni/$package_library $git_dir > $LOG_FILE 2>&1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
cd $git_dir
|
||||||
|
git pull origin master > $LOG_FILE 2>&1
|
||||||
|
if [[ -f "uninstall.sh" ]]; then
|
||||||
|
if [[ ! "$VERBOSE" == "" ]]; then
|
||||||
|
./uninstall.sh
|
||||||
|
else
|
||||||
|
./uninstall.sh > $LOG_FILE 2>&1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if [[ ! "$VERBOSE" == "" ]]; then
|
||||||
|
echo "Warning: No uninstall.sh found for $package_name."
|
||||||
|
else
|
||||||
|
echo "Warning: No uninstall.sh found for $package_name." > $LOG_FILE 2>&1
|
||||||
|
fi
|
||||||
|
STATUSES[$index]="error"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
cd $TMP_DIR
|
||||||
|
rm -r $git_dir
|
||||||
|
cd $WORKING_DIR
|
||||||
|
STATUSES[$index]="uninstalled"
|
||||||
|
}
|
||||||
|
|
||||||
|
do_install () {
|
||||||
|
index=$1
|
||||||
|
package_name=${DEVICES[$index]}
|
||||||
|
package_library=${REPOS[$index]}
|
||||||
|
|
||||||
|
git_dir=$package_library
|
||||||
|
cd $TMP_DIR
|
||||||
|
if [[ ! -d "$git_dir/.git" ]]; then
|
||||||
|
if [[ ! "$VERBOSE" == "" ]]; then
|
||||||
|
rm -f $git_dir > $LOG_FILE
|
||||||
|
git clone https://github.com/pimoroni/$package_library $git_dir
|
||||||
|
else
|
||||||
|
rm -f $git_dir > $LOG_FILE 2>&1
|
||||||
|
git clone https://github.com/pimoroni/$package_library $git_dir > $LOG_FILE 2>&1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
cd $git_dir
|
||||||
|
git pull origin master > $LOG_FILE 2>&1
|
||||||
|
if [[ -f "install.sh" ]]; then
|
||||||
|
if [[ ! "$VERBOSE" == "" ]]; then
|
||||||
|
"./install.sh"
|
||||||
|
else
|
||||||
|
"./install.sh" > $LOG_FILE 2>&1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "Warning: No install.sh found for $package_name." > $LOG_FILE 2>&1
|
||||||
|
STATUSES[$index]="error"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
cd $WORKING_DIR
|
||||||
|
|
||||||
|
STATUSES[$index]="installed"
|
||||||
|
}
|
||||||
|
|
||||||
|
display () {
|
||||||
|
if [[ "$VERBOSE" == "" ]]; then
|
||||||
|
lines=$(tput lines)
|
||||||
|
lines=$(($lines-$COUNT-$PADDING))
|
||||||
|
tput cup $lines 0
|
||||||
|
fi
|
||||||
|
for ((i = 0; i < $COUNT; i++)); do
|
||||||
|
ITEM=${DEVICES[$i]}
|
||||||
|
STATUS=${STATUSES[$i]}
|
||||||
|
|
||||||
|
if [[ "$VERBOSE" == "" ]] || [[ "" == "$1" ]] || [[ "$i" == "$1" ]]; then
|
||||||
|
printf "%-30s %s" "$ITEM:" " "
|
||||||
|
case $STATUS in
|
||||||
|
"error"*)
|
||||||
|
warning "Error! ";;
|
||||||
|
"required"*)
|
||||||
|
warning "Required ";;
|
||||||
|
"installed"*)
|
||||||
|
success "Installed ";;
|
||||||
|
"uninstalled"*)
|
||||||
|
success "Uninstalled ";;
|
||||||
|
"uninstalling"*)
|
||||||
|
inform "Uninstalling...";;
|
||||||
|
"installing"*)
|
||||||
|
inform "Installing... ";;
|
||||||
|
"reinstalling"*)
|
||||||
|
inform "Reinstalling...";;
|
||||||
|
esac
|
||||||
|
printf "\n"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
check_status
|
||||||
|
|
||||||
|
installs_required=0
|
||||||
|
uninstalls_required=0
|
||||||
|
|
||||||
|
printf "\n"
|
||||||
|
|
||||||
|
printf "Breakout Garden: Installer. ($COUNT breakout(s) found) \n\n"
|
||||||
|
|
||||||
|
for ((y = 0; y < $COUNT; y++)); do
|
||||||
|
if [[ "$VERBOSE" == "" ]]; then
|
||||||
|
printf "\n"
|
||||||
|
fi
|
||||||
|
if [[ "${STATUSES[$y]}" == "required" ]] || [[ ! "$FORCE" == "" ]]; then
|
||||||
|
installs_required=$(($installs_required+1))
|
||||||
|
fi
|
||||||
|
if [[ "$ACTION" == "uninstall" ]] && [[ "${STATUSES[$y]}" == "installed" ]]; then
|
||||||
|
uninstalls_required=$(($uninstalls_required+1))
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ "$VERBOSE" == "" ]]; then
|
||||||
|
printf "\n\n\n\n"
|
||||||
|
fi
|
||||||
|
|
||||||
|
display
|
||||||
|
|
||||||
|
printf "\n"
|
||||||
|
|
||||||
|
if [[ "$ACTION" == "install" ]]; then
|
||||||
|
if [[ "$installs_required" == "0" ]]; then
|
||||||
|
read -p "Nothing to do! Press enter to quit..."
|
||||||
|
else
|
||||||
|
action_text="Installing"
|
||||||
|
if [[ ! "$FORCE" == "" ]]; then
|
||||||
|
forced_mode=" (forced)"
|
||||||
|
action_text="Reinstalling"
|
||||||
|
fi
|
||||||
|
read -p "$action_text $installs_required module(s)$forced_mode. Enter to continue (Ctrl+C to cancel)..."
|
||||||
|
|
||||||
|
for ((y = 0; y < $COUNT; y++)); do
|
||||||
|
STATUS=${STATUSES[$y]}
|
||||||
|
if [[ ! "$STATUS" == "installed" ]] || [[ ! "$FORCE" == "" ]]; then
|
||||||
|
if [[ ! "$FORCE" == "" ]]; then
|
||||||
|
STATUSES[$y]="reinstalling"
|
||||||
|
else
|
||||||
|
STATUSES[$y]="installing"
|
||||||
|
fi
|
||||||
|
|
||||||
|
display $y
|
||||||
|
|
||||||
|
do_install $y
|
||||||
|
display $y
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$ACTION" == "uninstall" ]]; then
|
||||||
|
if [[ "$uninstalls_required" == "0" ]]; then
|
||||||
|
read -p "Nothing to do! Press enter to quit..."
|
||||||
|
else
|
||||||
|
read -p "Removing $uninstalls_required module(s). Enter to continue (Ctrl+C to cancel)..."
|
||||||
|
|
||||||
|
for ((y = 0; y < $COUNT; y++)); do
|
||||||
|
STATUS=${STATUSES[$y]}
|
||||||
|
if [[ "$STATUS" == "installed" ]]; then
|
||||||
|
STATUSES[$y]="uninstalling"
|
||||||
|
display $y
|
||||||
|
|
||||||
|
do_uninstall $y
|
||||||
|
display $y
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf "\n\n"
|
||||||
|
|
||||||
|
for ((y = 0; y < $COUNT; y++)); do
|
||||||
|
STATUS=${STATUSES[$y]}
|
||||||
|
if [[ "$STATUS" == "error" ]]; then
|
||||||
|
warning "Errors occured during $ACTION. For more info see $LOG_FILE"
|
||||||
|
printf "\n"
|
||||||
|
break;
|
||||||
|
fi
|
||||||
|
done
|
||||||
37
vendor/ltp305-python/.github/workflows/test.yml
vendored
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
name: Python Tests
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
python: [2.7, 3.5, 3.7, 3.9]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Set up Python ${{ matrix.python }}
|
||||||
|
uses: actions/setup-python@v2
|
||||||
|
with:
|
||||||
|
python-version: ${{ matrix.python }}
|
||||||
|
- name: Install Dependencies
|
||||||
|
run: |
|
||||||
|
python -m pip install --upgrade setuptools tox
|
||||||
|
- name: Run Tests
|
||||||
|
working-directory: library
|
||||||
|
run: |
|
||||||
|
tox -e py
|
||||||
|
- name: Coverage
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
working-directory: library
|
||||||
|
run: |
|
||||||
|
python -m pip install coveralls
|
||||||
|
coveralls --service=github
|
||||||
|
if: ${{ matrix.python == '3.9' }}
|
||||||
|
|
||||||
20
vendor/ltp305-python/.gitignore
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
build/
|
||||||
|
_build/
|
||||||
|
*.o
|
||||||
|
*.so
|
||||||
|
*.a
|
||||||
|
*.py[cod]
|
||||||
|
*.egg-info
|
||||||
|
dist/
|
||||||
|
__pycache__
|
||||||
|
.DS_Store
|
||||||
|
*.deb
|
||||||
|
*.dsc
|
||||||
|
*.build
|
||||||
|
*.changes
|
||||||
|
*.orig.*
|
||||||
|
packaging/*tar.xz
|
||||||
|
library/debian/
|
||||||
|
.coverage
|
||||||
|
.pytest_cache
|
||||||
|
.tox
|
||||||
5
vendor/ltp305-python/.stickler.yml
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
linters:
|
||||||
|
flake8:
|
||||||
|
python: 3
|
||||||
|
max-line-length: 160
|
||||||
21
vendor/ltp305-python/LICENSE
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2018 Pimoroni Ltd.
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
70
vendor/ltp305-python/Makefile
vendored
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
LIBRARY_VERSION=$(shell grep version library/setup.cfg | awk -F" = " '{print $$2}')
|
||||||
|
LIBRARY_NAME=$(shell grep name library/setup.cfg | awk -F" = " '{print $$2}')
|
||||||
|
|
||||||
|
.PHONY: usage install uninstall
|
||||||
|
usage:
|
||||||
|
@echo "Library: ${LIBRARY_NAME}"
|
||||||
|
@echo "Version: ${LIBRARY_VERSION}\n"
|
||||||
|
@echo "Usage: make <target>, where target is one of:\n"
|
||||||
|
@echo "install: install the library locally from source"
|
||||||
|
@echo "uninstall: uninstall the local library"
|
||||||
|
@echo "check: peform basic integrity checks on the codebase"
|
||||||
|
@echo "python-readme: generate library/README.md from README.md + library/CHANGELOG.txt"
|
||||||
|
@echo "python-wheels: build python .whl files for distribution"
|
||||||
|
@echo "python-sdist: build python source distribution"
|
||||||
|
@echo "python-clean: clean python build and dist directories"
|
||||||
|
@echo "python-dist: build all python distribution files"
|
||||||
|
@echo "python-testdeploy: build all and deploy to test PyPi"
|
||||||
|
@echo "tag: tag the repository with the current version"
|
||||||
|
|
||||||
|
install:
|
||||||
|
./install.sh
|
||||||
|
|
||||||
|
uninstall:
|
||||||
|
./uninstall.sh
|
||||||
|
|
||||||
|
check:
|
||||||
|
@echo "Checking for trailing whitespace"
|
||||||
|
@! grep -IUrn --color "[[:blank:]]$$" --exclude-dir=sphinx --exclude-dir=.tox --exclude-dir=.git --exclude=PKG-INFO
|
||||||
|
@echo "Checking for DOS line-endings"
|
||||||
|
@! grep -IUrn --color "
|
||||||
|
" --exclude-dir=sphinx --exclude-dir=.tox --exclude-dir=.git --exclude=Makefile
|
||||||
|
@echo "Checking library/CHANGELOG.txt"
|
||||||
|
@cat library/CHANGELOG.txt | grep ^${LIBRARY_VERSION}
|
||||||
|
@echo "Checking library/${LIBRARY_NAME}/__init__.py"
|
||||||
|
@cat library/${LIBRARY_NAME}/__init__.py | grep "^__version__ = '${LIBRARY_VERSION}'"
|
||||||
|
|
||||||
|
tag:
|
||||||
|
git tag -a "v${LIBRARY_VERSION}" -m "Version ${LIBRARY_VERSION}"
|
||||||
|
|
||||||
|
python-readme: library/README.md
|
||||||
|
|
||||||
|
python-license: library/LICENSE.txt
|
||||||
|
|
||||||
|
library/README.md: README.md library/CHANGELOG.txt
|
||||||
|
cp README.md library/README.md
|
||||||
|
printf "\n# Changelog\n" >> library/README.md
|
||||||
|
cat library/CHANGELOG.txt >> library/README.md
|
||||||
|
|
||||||
|
library/LICENSE.txt: LICENSE
|
||||||
|
cp LICENSE library/LICENSE.txt
|
||||||
|
|
||||||
|
python-wheels: python-readme python-license
|
||||||
|
cd library; python3 setup.py bdist_wheel
|
||||||
|
cd library; python setup.py bdist_wheel
|
||||||
|
|
||||||
|
python-sdist: python-readme python-license
|
||||||
|
cd library; python setup.py sdist
|
||||||
|
|
||||||
|
python-clean:
|
||||||
|
-rm -r library/dist
|
||||||
|
-rm -r library/build
|
||||||
|
-rm -r library/*.egg-info
|
||||||
|
|
||||||
|
python-dist: python-clean python-wheels python-sdist
|
||||||
|
ls library/dist
|
||||||
|
|
||||||
|
python-testdeploy: python-dist
|
||||||
|
twine upload --repository-url https://test.pypi.org/legacy/ library/dist/*
|
||||||
|
|
||||||
|
python-deploy: check python-dist
|
||||||
27
vendor/ltp305-python/README.md
vendored
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# LTP305 - Breakout Garden dual 5x7 LED matrix driver
|
||||||
|
|
||||||
|
[](https://travis-ci.com/pimoroni/ltp305-python)
|
||||||
|
[](https://coveralls.io/github/pimoroni/ltp305-python?branch=master)
|
||||||
|
[](https://pypi.python.org/pypi/ltp305)
|
||||||
|
[](https://pypi.python.org/pypi/ltp305)
|
||||||
|
|
||||||
|
# Pre-requisites
|
||||||
|
|
||||||
|
You must enable i2c:
|
||||||
|
|
||||||
|
* i2c: `sudo raspi-config nonint do_i2c 0`
|
||||||
|
|
||||||
|
You can optionally run `sudo raspi-config` or the graphical Raspberry Pi Configuration UI to enable interfaces.
|
||||||
|
|
||||||
|
# Installing
|
||||||
|
|
||||||
|
Stable library from PyPi:
|
||||||
|
|
||||||
|
* Just run `sudo pip3 install ltp305`
|
||||||
|
|
||||||
|
Latest/development library from GitHub:
|
||||||
|
|
||||||
|
* `git clone https://github.com/pimoroni/ltp305-python`
|
||||||
|
* `cd ltp305-python`
|
||||||
|
* `sudo ./install.sh`
|
||||||
|
|
||||||
128
vendor/ltp305-python/REFERENCE.md
vendored
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
# Pimoroni LTP305 LED Matrices Breakout <!-- omit in toc -->
|
||||||
|
|
||||||
|
The LTP305 breakout includes two LTP305 displays and an i2c dual matrix driver to drive them.
|
||||||
|
|
||||||
|
- [Getting Started](#getting-started)
|
||||||
|
- [Pre-requisites](#pre-requisites)
|
||||||
|
- [Python 3 & pip](#python-3--pip)
|
||||||
|
- [Enabling i2c](#enabling-i2c)
|
||||||
|
- [Installing the library](#installing-the-library)
|
||||||
|
- [Reference](#reference)
|
||||||
|
- [set_pixel](#set_pixel)
|
||||||
|
- [set_decimal](#set_decimal)
|
||||||
|
- [set_character](#set_character)
|
||||||
|
- [set_image](#set_image)
|
||||||
|
- [get_shape](#get_shape)
|
||||||
|
- [clear](#clear)
|
||||||
|
- [show](#show)
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
You'll need to install the LTP305 software library and enable i2c on your Raspberry Pi.
|
||||||
|
|
||||||
|
### Pre-requisites
|
||||||
|
|
||||||
|
#### Python 3 & pip
|
||||||
|
|
||||||
|
You should use Python 3, which may need installing on your Pi:
|
||||||
|
|
||||||
|
```
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install python3 python3-pip
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Enabling i2c
|
||||||
|
|
||||||
|
You can use `sudo raspi-config` on the command line, the GUI Raspberry Pi Configuration app from the Pi desktop menu, or use the following command to enable i2c:
|
||||||
|
|
||||||
|
```
|
||||||
|
sudo raspi-config nonint do_i2c 0
|
||||||
|
```
|
||||||
|
|
||||||
|
### Installing the library
|
||||||
|
|
||||||
|
```python
|
||||||
|
python3 -m pip install ltp305
|
||||||
|
```
|
||||||
|
|
||||||
|
## Reference
|
||||||
|
|
||||||
|
In all cases you'll want to create an instance of the LTP305 class with the appropriate i2c address for your device.
|
||||||
|
|
||||||
|
The following addresses are available:
|
||||||
|
|
||||||
|
* `0x61` - Default address
|
||||||
|
* `0x62` - Solder bridge applied to Addr+1
|
||||||
|
* `0x63` - Cut Addr+2
|
||||||
|
|
||||||
|
```python
|
||||||
|
from ltp305 import LTP305
|
||||||
|
display = LTP305(address=0x61)
|
||||||
|
```
|
||||||
|
|
||||||
|
### set_pixel
|
||||||
|
|
||||||
|
Set a single pixel. Treats both matrices as a single display that's 10 pixels wide and 7 pixels high.
|
||||||
|
|
||||||
|
```python
|
||||||
|
display.set_pixel(0, 0, 1)
|
||||||
|
display.show()
|
||||||
|
```
|
||||||
|
|
||||||
|
### set_decimal
|
||||||
|
|
||||||
|
Set the decimal dot on one or both matrix displays:
|
||||||
|
|
||||||
|
```python
|
||||||
|
display.set_decimal(left=True, right=False) # Just the left
|
||||||
|
display.set_decimal(left=False, right=True) # Just the right
|
||||||
|
display.set_decimal(left=True, right=True) # Both
|
||||||
|
display.show()
|
||||||
|
```
|
||||||
|
|
||||||
|
### set_character
|
||||||
|
|
||||||
|
The library includes a font with a range of ASCII and Unicode characters, each matrix can display a single character.
|
||||||
|
|
||||||
|
```python
|
||||||
|
display.set_character(0, "a")
|
||||||
|
display.set_character(5, "b")
|
||||||
|
display.show()
|
||||||
|
```
|
||||||
|
|
||||||
|
### set_image
|
||||||
|
|
||||||
|
Set a 1-bit PIL image to the display.
|
||||||
|
|
||||||
|
```python
|
||||||
|
from PIL import Image, ImageDraw
|
||||||
|
image = Image.new("1", display.get_shape())
|
||||||
|
draw = ImageDraw.draw(image)
|
||||||
|
draw.rectangle((0, 0, 4, 6), 1)
|
||||||
|
display.set_image(image)
|
||||||
|
display.show()
|
||||||
|
```
|
||||||
|
|
||||||
|
### get_shape
|
||||||
|
|
||||||
|
Return the width and height of the display:
|
||||||
|
|
||||||
|
```python
|
||||||
|
width, height = display.get_shape()
|
||||||
|
```
|
||||||
|
|
||||||
|
### clear
|
||||||
|
|
||||||
|
Clear the display. Sets all pixels in the buffer to 0 (off). You must call `show()` to update the display.
|
||||||
|
|
||||||
|
```python
|
||||||
|
display.clear()
|
||||||
|
```
|
||||||
|
|
||||||
|
### show
|
||||||
|
|
||||||
|
Once you've finished setting pixels or drawing characters into the buffer, you must call `show` to update the matrices with the buffer:
|
||||||
|
|
||||||
|
```python
|
||||||
|
display.show()
|
||||||
|
```
|
||||||
43
vendor/ltp305-python/examples/bargraph.py
vendored
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import time
|
||||||
|
import math
|
||||||
|
import random
|
||||||
|
from ltp305 import LTP305
|
||||||
|
|
||||||
|
|
||||||
|
print("""bargraph.py - A basic graph example
|
||||||
|
|
||||||
|
Displays an animated rising/falling bar on the left matrix,
|
||||||
|
and a scrolling bar graph on the right.
|
||||||
|
|
||||||
|
Press Ctrl+C to exit!
|
||||||
|
|
||||||
|
""")
|
||||||
|
|
||||||
|
display = LTP305()
|
||||||
|
width, height = display.get_shape()
|
||||||
|
|
||||||
|
values = [0, 0, 0, 0, 0]
|
||||||
|
|
||||||
|
|
||||||
|
while True:
|
||||||
|
# Add a new random value to our list and prune the list to visible values
|
||||||
|
values.insert(0, random.randint(0, height))
|
||||||
|
values = values[:width]
|
||||||
|
|
||||||
|
# Animate a value from 0 to height + 1
|
||||||
|
value = (math.sin(time.time() * math.pi) + 1) / 2.0
|
||||||
|
value *= height + 1
|
||||||
|
value = math.floor(value)
|
||||||
|
|
||||||
|
for y in range(height):
|
||||||
|
y = height - 1 - y
|
||||||
|
|
||||||
|
for x in range(width // 2):
|
||||||
|
# Left
|
||||||
|
display.set_pixel(x, y, value <= y)
|
||||||
|
|
||||||
|
# Right
|
||||||
|
display.set_pixel(x + (width // 2), y, values[x] <= y)
|
||||||
|
|
||||||
|
time.sleep(1.0 / height)
|
||||||
|
display.show()
|
||||||
50
vendor/ltp305-python/examples/clock.py
vendored
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
import sys
|
||||||
|
import time
|
||||||
|
from ltp305 import LTP305
|
||||||
|
|
||||||
|
|
||||||
|
print("""clock.py - clock segment example.
|
||||||
|
|
||||||
|
Usage: python3 clock.py <hour/minute/second> <i2c-address>
|
||||||
|
|
||||||
|
This simple clock example will display the hour, minute or second on a single matrix display breakout.
|
||||||
|
|
||||||
|
Press Ctrl+C to exit.
|
||||||
|
|
||||||
|
""")
|
||||||
|
|
||||||
|
fmt = "%M"
|
||||||
|
address = 0x61
|
||||||
|
available = {
|
||||||
|
"hour": "%H",
|
||||||
|
"minute": "%M",
|
||||||
|
"second": "%S"
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(sys.argv) > 1:
|
||||||
|
try:
|
||||||
|
fmt = available[sys.argv[1]]
|
||||||
|
except KeyError:
|
||||||
|
raise ValueError("{} is not supported!".format(sys.argv[1]))
|
||||||
|
|
||||||
|
if len(sys.argv) > 2:
|
||||||
|
address = int(sys.argv[2], 16)
|
||||||
|
if address not in [0x61, 0x62, 0x63]:
|
||||||
|
raise ValueError("Invalid i2c address: 0x{:02x}. Run `i2c-detect -y 1` to discover breakouts.".format(address))
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
display = LTP305(address=address)
|
||||||
|
display.clear()
|
||||||
|
display.show()
|
||||||
|
except OSError:
|
||||||
|
raise OSError("Unable to find LTP305 on i2c address: 0x{:02x}. Run `i2c-detect -y 1` to discover breakouts".format(address))
|
||||||
|
|
||||||
|
|
||||||
|
while True:
|
||||||
|
minute = time.strftime(fmt)
|
||||||
|
left, right = minute
|
||||||
|
display.set_character(0, left)
|
||||||
|
display.set_character(5, right)
|
||||||
|
display.show()
|
||||||
|
time.sleep(1.0 / 60)
|
||||||
49
vendor/ltp305-python/examples/eyes.py
vendored
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import math
|
||||||
|
import time
|
||||||
|
import random
|
||||||
|
|
||||||
|
|
||||||
|
print("""eyes.py - They're watching you!
|
||||||
|
|
||||||
|
Press Ctrl+C to exit!
|
||||||
|
|
||||||
|
""")
|
||||||
|
|
||||||
|
|
||||||
|
from ltp305 import LTP305
|
||||||
|
|
||||||
|
display = LTP305()
|
||||||
|
|
||||||
|
|
||||||
|
def eye(x, y):
|
||||||
|
display.set_pixel(x, y, True)
|
||||||
|
display.set_pixel(x, y + 1, True)
|
||||||
|
display.set_pixel(x + 1, y, True)
|
||||||
|
display.set_pixel(x + 1, y + 1, True)
|
||||||
|
|
||||||
|
|
||||||
|
while True:
|
||||||
|
t = time.time() * math.pi
|
||||||
|
|
||||||
|
# Get eye x and y positions in the range 0.0 to 1.0
|
||||||
|
# You can plug in your own 0.0 to 1.0 values here
|
||||||
|
x = (math.sin(t / 2) + 1) / 2
|
||||||
|
y = (math.sin(t / 4) + 1) / 2
|
||||||
|
|
||||||
|
# Multiply them up to display coords and convert to itn
|
||||||
|
x = math.floor(x * 4)
|
||||||
|
y = math.floor(y * 6)
|
||||||
|
|
||||||
|
# Clear the display
|
||||||
|
display.clear()
|
||||||
|
|
||||||
|
# Blink occasionally
|
||||||
|
if not random.randint(0, 20) == 2:
|
||||||
|
# Draw the eyes if not blinking
|
||||||
|
eye(x, y)
|
||||||
|
eye(x + 5, y)
|
||||||
|
|
||||||
|
# Update the display at 10FPS
|
||||||
|
# This gives our crude blink code time to not look like a random flicker
|
||||||
|
display.show()
|
||||||
|
time.sleep(1.0 / 10)
|
||||||
254
vendor/ltp305-python/install-bullseye.sh
vendored
Executable file
@@ -0,0 +1,254 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
CONFIG=/boot/config.txt
|
||||||
|
DATESTAMP=`date "+%Y-%m-%d-%H-%M-%S"`
|
||||||
|
CONFIG_BACKUP=false
|
||||||
|
APT_HAS_UPDATED=false
|
||||||
|
USER_HOME=/home/$SUDO_USER
|
||||||
|
RESOURCES_TOP_DIR=$USER_HOME/Pimoroni
|
||||||
|
WD=`pwd`
|
||||||
|
USAGE="sudo ./install.sh (--unstable)"
|
||||||
|
POSITIONAL_ARGS=()
|
||||||
|
UNSTABLE=false
|
||||||
|
PYTHON="/usr/bin/python3"
|
||||||
|
CODENAME=`lsb_release -sc`
|
||||||
|
|
||||||
|
distro_check() {
|
||||||
|
if [[ $CODENAME != "bullseye" ]]; then
|
||||||
|
printf "This installer is for Raspberry Pi OS: Bullseye only, current distro: $CODENAME\n"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
user_check() {
|
||||||
|
if [ $(id -u) -ne 0 ]; then
|
||||||
|
printf "Script must be run as root. Try 'sudo ./install.sh'\n"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
confirm() {
|
||||||
|
if [ "$FORCE" == '-y' ]; then
|
||||||
|
true
|
||||||
|
else
|
||||||
|
read -r -p "$1 [y/N] " response < /dev/tty
|
||||||
|
if [[ $response =~ ^(yes|y|Y)$ ]]; then
|
||||||
|
true
|
||||||
|
else
|
||||||
|
false
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
prompt() {
|
||||||
|
read -r -p "$1 [y/N] " response < /dev/tty
|
||||||
|
if [[ $response =~ ^(yes|y|Y)$ ]]; then
|
||||||
|
true
|
||||||
|
else
|
||||||
|
false
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
success() {
|
||||||
|
echo -e "$(tput setaf 2)$1$(tput sgr0)"
|
||||||
|
}
|
||||||
|
|
||||||
|
inform() {
|
||||||
|
echo -e "$(tput setaf 6)$1$(tput sgr0)"
|
||||||
|
}
|
||||||
|
|
||||||
|
warning() {
|
||||||
|
echo -e "$(tput setaf 1)$1$(tput sgr0)"
|
||||||
|
}
|
||||||
|
|
||||||
|
function do_config_backup {
|
||||||
|
if [ ! $CONFIG_BACKUP == true ]; then
|
||||||
|
CONFIG_BACKUP=true
|
||||||
|
FILENAME="config.preinstall-$LIBRARY_NAME-$DATESTAMP.txt"
|
||||||
|
inform "Backing up $CONFIG to /boot/$FILENAME\n"
|
||||||
|
cp $CONFIG /boot/$FILENAME
|
||||||
|
mkdir -p $RESOURCES_TOP_DIR/config-backups/
|
||||||
|
cp $CONFIG $RESOURCES_TOP_DIR/config-backups/$FILENAME
|
||||||
|
if [ -f "$UNINSTALLER" ]; then
|
||||||
|
echo "cp $RESOURCES_TOP_DIR/config-backups/$FILENAME $CONFIG" >> $UNINSTALLER
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function apt_pkg_install {
|
||||||
|
PACKAGES=()
|
||||||
|
PACKAGES_IN=("$@")
|
||||||
|
for ((i = 0; i < ${#PACKAGES_IN[@]}; i++)); do
|
||||||
|
PACKAGE="${PACKAGES_IN[$i]}"
|
||||||
|
if [ "$PACKAGE" == "" ]; then continue; fi
|
||||||
|
printf "Checking for $PACKAGE\n"
|
||||||
|
dpkg -L $PACKAGE > /dev/null 2>&1
|
||||||
|
if [ "$?" == "1" ]; then
|
||||||
|
PACKAGES+=("$PACKAGE")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
PACKAGES="${PACKAGES[@]}"
|
||||||
|
if ! [ "$PACKAGES" == "" ]; then
|
||||||
|
echo "Installing missing packages: $PACKAGES"
|
||||||
|
if [ ! $APT_HAS_UPDATED ]; then
|
||||||
|
apt update
|
||||||
|
APT_HAS_UPDATED=true
|
||||||
|
fi
|
||||||
|
apt install -y $PACKAGES
|
||||||
|
if [ -f "$UNINSTALLER" ]; then
|
||||||
|
echo "apt uninstall -y $PACKAGES"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
K="$1"
|
||||||
|
case $K in
|
||||||
|
-u|--unstable)
|
||||||
|
UNSTABLE=true
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-p|--python)
|
||||||
|
PYTHON=$2
|
||||||
|
shift
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
if [[ $1 == -* ]]; then
|
||||||
|
printf "Unrecognised option: $1\n";
|
||||||
|
printf "Usage: $USAGE\n";
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
POSITIONAL_ARGS+=("$1")
|
||||||
|
shift
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
distro_check
|
||||||
|
user_check
|
||||||
|
|
||||||
|
if [ ! -f "$PYTHON" ]; then
|
||||||
|
printf "Python path $PYTHON not found!\n"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
PYTHON_VER=`$PYTHON --version`
|
||||||
|
|
||||||
|
inform "Installing. Please wait..."
|
||||||
|
|
||||||
|
$PYTHON -m pip install --upgrade configparser
|
||||||
|
|
||||||
|
CONFIG_VARS=`$PYTHON - <<EOF
|
||||||
|
from configparser import ConfigParser
|
||||||
|
c = ConfigParser()
|
||||||
|
c.read('library/setup.cfg')
|
||||||
|
p = dict(c['pimoroni'])
|
||||||
|
# Convert multi-line config entries into bash arrays
|
||||||
|
for k in p.keys():
|
||||||
|
fmt = '"{}"'
|
||||||
|
if '\n' in p[k]:
|
||||||
|
p[k] = "'\n\t'".join(p[k].split('\n')[1:])
|
||||||
|
fmt = "('{}')"
|
||||||
|
p[k] = fmt.format(p[k])
|
||||||
|
print("""
|
||||||
|
LIBRARY_NAME="{name}"
|
||||||
|
LIBRARY_VERSION="{version}"
|
||||||
|
""".format(**c['metadata']))
|
||||||
|
print("""
|
||||||
|
PY3_DEPS={py3deps}
|
||||||
|
PY2_DEPS={py2deps}
|
||||||
|
SETUP_CMDS={commands}
|
||||||
|
CONFIG_TXT={configtxt}
|
||||||
|
""".format(**p))
|
||||||
|
EOF`
|
||||||
|
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
warning "Error parsing configuration...\n"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
eval $CONFIG_VARS
|
||||||
|
|
||||||
|
RESOURCES_DIR=$RESOURCES_TOP_DIR/$LIBRARY_NAME
|
||||||
|
UNINSTALLER=$RESOURCES_DIR/uninstall.sh
|
||||||
|
|
||||||
|
mkdir -p $RESOURCES_DIR
|
||||||
|
|
||||||
|
cat << EOF > $UNINSTALLER
|
||||||
|
printf "It's recommended you run these steps manually.\n"
|
||||||
|
printf "If you want to run the full script, open it in\n"
|
||||||
|
printf "an editor and remove 'exit 1' from below.\n"
|
||||||
|
exit 1
|
||||||
|
EOF
|
||||||
|
|
||||||
|
printf "$LIBRARY_NAME $LIBRARY_VERSION Python Library: Installer\n\n"
|
||||||
|
|
||||||
|
if $UNSTABLE; then
|
||||||
|
warning "Installing unstable library from source.\n\n"
|
||||||
|
else
|
||||||
|
printf "Installing stable library from pypi.\n\n"
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd library
|
||||||
|
|
||||||
|
printf "Installing for $PYTHON_VER...\n"
|
||||||
|
apt_pkg_install "${PY3_DEPS[@]}"
|
||||||
|
if $UNSTABLE; then
|
||||||
|
$PYTHON setup.py install > /dev/null
|
||||||
|
else
|
||||||
|
$PYTHON -m pip install --upgrade $LIBRARY_NAME
|
||||||
|
fi
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
success "Done!\n"
|
||||||
|
echo "$PYTHON -m pip uninstall $LIBRARY_NAME" >> $UNINSTALLER
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd $WD
|
||||||
|
|
||||||
|
for ((i = 0; i < ${#SETUP_CMDS[@]}; i++)); do
|
||||||
|
CMD="${SETUP_CMDS[$i]}"
|
||||||
|
# Attempt to catch anything that touches /boot/config.txt and trigger a backup
|
||||||
|
if [[ "$CMD" == *"raspi-config"* ]] || [[ "$CMD" == *"$CONFIG"* ]] || [[ "$CMD" == *"\$CONFIG"* ]]; then
|
||||||
|
do_config_backup
|
||||||
|
fi
|
||||||
|
eval $CMD
|
||||||
|
done
|
||||||
|
|
||||||
|
for ((i = 0; i < ${#CONFIG_TXT[@]}; i++)); do
|
||||||
|
CONFIG_LINE="${CONFIG_TXT[$i]}"
|
||||||
|
if ! [ "$CONFIG_LINE" == "" ]; then
|
||||||
|
do_config_backup
|
||||||
|
inform "Adding $CONFIG_LINE to $CONFIG\n"
|
||||||
|
sed -i "s/^#$CONFIG_LINE/$CONFIG_LINE/" $CONFIG
|
||||||
|
if ! grep -q "^$CONFIG_LINE" $CONFIG; then
|
||||||
|
printf "$CONFIG_LINE\n" >> $CONFIG
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -d "examples" ]; then
|
||||||
|
if confirm "Would you like to copy examples to $RESOURCES_DIR?"; then
|
||||||
|
inform "Copying examples to $RESOURCES_DIR"
|
||||||
|
cp -r examples/ $RESOURCES_DIR
|
||||||
|
echo "rm -r $RESOURCES_DIR" >> $UNINSTALLER
|
||||||
|
success "Done!"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf "\n"
|
||||||
|
|
||||||
|
if [ -f "/usr/bin/pydoc" ]; then
|
||||||
|
printf "Generating documentation.\n"
|
||||||
|
pydoc -w $LIBRARY_NAME > /dev/null
|
||||||
|
if [ -f "$LIBRARY_NAME.html" ]; then
|
||||||
|
cp $LIBRARY_NAME.html $RESOURCES_DIR/docs.html
|
||||||
|
rm -f $LIBRARY_NAME.html
|
||||||
|
inform "Documentation saved to $RESOURCES_DIR/docs.html"
|
||||||
|
success "Done!"
|
||||||
|
else
|
||||||
|
warning "Error: Failed to generate documentation."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
success "\nAll done!"
|
||||||
|
inform "If this is your first time installing you should reboot for hardware changes to take effect.\n"
|
||||||
|
inform "Find uninstall steps in $UNINSTALLER\n"
|
||||||
251
vendor/ltp305-python/install.sh
vendored
Executable file
@@ -0,0 +1,251 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
CONFIG=/boot/config.txt
|
||||||
|
DATESTAMP=`date "+%Y-%m-%d-%H-%M-%S"`
|
||||||
|
CONFIG_BACKUP=false
|
||||||
|
APT_HAS_UPDATED=false
|
||||||
|
USER_HOME=/home/$SUDO_USER
|
||||||
|
RESOURCES_TOP_DIR=$USER_HOME/Pimoroni
|
||||||
|
WD=`pwd`
|
||||||
|
USAGE="sudo ./install.sh (--unstable)"
|
||||||
|
POSITIONAL_ARGS=()
|
||||||
|
UNSTABLE=false
|
||||||
|
CODENAME=`lsb_release -sc`
|
||||||
|
|
||||||
|
if [[ $CODENAME == "bullseye" ]]; then
|
||||||
|
bash ./install-bullseye.sh
|
||||||
|
exit $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
user_check() {
|
||||||
|
if [ $(id -u) -ne 0 ]; then
|
||||||
|
printf "Script must be run as root. Try 'sudo ./install.sh'\n"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
confirm() {
|
||||||
|
if [ "$FORCE" == '-y' ]; then
|
||||||
|
true
|
||||||
|
else
|
||||||
|
read -r -p "$1 [y/N] " response < /dev/tty
|
||||||
|
if [[ $response =~ ^(yes|y|Y)$ ]]; then
|
||||||
|
true
|
||||||
|
else
|
||||||
|
false
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
prompt() {
|
||||||
|
read -r -p "$1 [y/N] " response < /dev/tty
|
||||||
|
if [[ $response =~ ^(yes|y|Y)$ ]]; then
|
||||||
|
true
|
||||||
|
else
|
||||||
|
false
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
success() {
|
||||||
|
echo -e "$(tput setaf 2)$1$(tput sgr0)"
|
||||||
|
}
|
||||||
|
|
||||||
|
inform() {
|
||||||
|
echo -e "$(tput setaf 6)$1$(tput sgr0)"
|
||||||
|
}
|
||||||
|
|
||||||
|
warning() {
|
||||||
|
echo -e "$(tput setaf 1)$1$(tput sgr0)"
|
||||||
|
}
|
||||||
|
|
||||||
|
function do_config_backup {
|
||||||
|
if [ ! $CONFIG_BACKUP == true ]; then
|
||||||
|
CONFIG_BACKUP=true
|
||||||
|
FILENAME="config.preinstall-$LIBRARY_NAME-$DATESTAMP.txt"
|
||||||
|
inform "Backing up $CONFIG to /boot/$FILENAME\n"
|
||||||
|
cp $CONFIG /boot/$FILENAME
|
||||||
|
mkdir -p $RESOURCES_TOP_DIR/config-backups/
|
||||||
|
cp $CONFIG $RESOURCES_TOP_DIR/config-backups/$FILENAME
|
||||||
|
if [ -f "$UNINSTALLER" ]; then
|
||||||
|
echo "cp $RESOURCES_TOP_DIR/config-backups/$FILENAME $CONFIG" >> $UNINSTALLER
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function apt_pkg_install {
|
||||||
|
PACKAGES=()
|
||||||
|
PACKAGES_IN=("$@")
|
||||||
|
for ((i = 0; i < ${#PACKAGES_IN[@]}; i++)); do
|
||||||
|
PACKAGE="${PACKAGES_IN[$i]}"
|
||||||
|
if [ "$PACKAGE" == "" ]; then continue; fi
|
||||||
|
printf "Checking for $PACKAGE\n"
|
||||||
|
dpkg -L $PACKAGE > /dev/null 2>&1
|
||||||
|
if [ "$?" == "1" ]; then
|
||||||
|
PACKAGES+=("$PACKAGE")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
PACKAGES="${PACKAGES[@]}"
|
||||||
|
if ! [ "$PACKAGES" == "" ]; then
|
||||||
|
echo "Installing missing packages: $PACKAGES"
|
||||||
|
if [ ! $APT_HAS_UPDATED ]; then
|
||||||
|
apt update
|
||||||
|
APT_HAS_UPDATED=true
|
||||||
|
fi
|
||||||
|
apt install -y $PACKAGES
|
||||||
|
if [ -f "$UNINSTALLER" ]; then
|
||||||
|
echo "apt uninstall -y $PACKAGES"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
K="$1"
|
||||||
|
case $K in
|
||||||
|
-u|--unstable)
|
||||||
|
UNSTABLE=true
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
if [[ $1 == -* ]]; then
|
||||||
|
printf "Unrecognised option: $1\n";
|
||||||
|
printf "Usage: $USAGE\n";
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
POSITIONAL_ARGS+=("$1")
|
||||||
|
shift
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
user_check
|
||||||
|
|
||||||
|
apt_pkg_install python-configparser
|
||||||
|
|
||||||
|
CONFIG_VARS=`python - <<EOF
|
||||||
|
from configparser import ConfigParser
|
||||||
|
c = ConfigParser()
|
||||||
|
c.read('library/setup.cfg')
|
||||||
|
p = dict(c['pimoroni'])
|
||||||
|
# Convert multi-line config entries into bash arrays
|
||||||
|
for k in p.keys():
|
||||||
|
fmt = '"{}"'
|
||||||
|
if '\n' in p[k]:
|
||||||
|
p[k] = "'\n\t'".join(p[k].split('\n')[1:])
|
||||||
|
fmt = "('{}')"
|
||||||
|
p[k] = fmt.format(p[k])
|
||||||
|
print("""
|
||||||
|
LIBRARY_NAME="{name}"
|
||||||
|
LIBRARY_VERSION="{version}"
|
||||||
|
""".format(**c['metadata']))
|
||||||
|
print("""
|
||||||
|
PY3_DEPS={py3deps}
|
||||||
|
PY2_DEPS={py2deps}
|
||||||
|
SETUP_CMDS={commands}
|
||||||
|
CONFIG_TXT={configtxt}
|
||||||
|
""".format(**p))
|
||||||
|
EOF`
|
||||||
|
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
warning "Error parsing configuration...\n"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
eval $CONFIG_VARS
|
||||||
|
|
||||||
|
RESOURCES_DIR=$RESOURCES_TOP_DIR/$LIBRARY_NAME
|
||||||
|
UNINSTALLER=$RESOURCES_DIR/uninstall.sh
|
||||||
|
|
||||||
|
mkdir -p $RESOURCES_DIR
|
||||||
|
|
||||||
|
cat << EOF > $UNINSTALLER
|
||||||
|
printf "It's recommended you run these steps manually.\n"
|
||||||
|
printf "If you want to run the full script, open it in\n"
|
||||||
|
printf "an editor and remove 'exit 1' from below.\n"
|
||||||
|
exit 1
|
||||||
|
EOF
|
||||||
|
|
||||||
|
printf "$LIBRARY_NAME $LIBRARY_VERSION Python Library: Installer\n\n"
|
||||||
|
|
||||||
|
if $UNSTABLE; then
|
||||||
|
warning "Installing unstable library from source.\n\n"
|
||||||
|
else
|
||||||
|
printf "Installing stable library from pypi.\n\n"
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd library
|
||||||
|
|
||||||
|
printf "Installing for Python 2..\n"
|
||||||
|
apt_pkg_install "${PY2_DEPS[@]}"
|
||||||
|
if $UNSTABLE; then
|
||||||
|
python setup.py install > /dev/null
|
||||||
|
else
|
||||||
|
pip install --upgrade $LIBRARY_NAME
|
||||||
|
fi
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
success "Done!\n"
|
||||||
|
echo "pip uninstall $LIBRARY_NAME" >> $UNINSTALLER
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f "/usr/bin/python3" ]; then
|
||||||
|
printf "Installing for Python 3..\n"
|
||||||
|
apt_pkg_install "${PY3_DEPS[@]}"
|
||||||
|
if $UNSTABLE; then
|
||||||
|
python3 setup.py install > /dev/null
|
||||||
|
else
|
||||||
|
pip3 install --upgrade $LIBRARY_NAME
|
||||||
|
fi
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
success "Done!\n"
|
||||||
|
echo "pip3 uninstall $LIBRARY_NAME" >> $UNINSTALLER
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd $WD
|
||||||
|
|
||||||
|
for ((i = 0; i < ${#SETUP_CMDS[@]}; i++)); do
|
||||||
|
CMD="${SETUP_CMDS[$i]}"
|
||||||
|
# Attempt to catch anything that touches /boot/config.txt and trigger a backup
|
||||||
|
if [[ "$CMD" == *"raspi-config"* ]] || [[ "$CMD" == *"$CONFIG"* ]] || [[ "$CMD" == *"\$CONFIG"* ]]; then
|
||||||
|
do_config_backup
|
||||||
|
fi
|
||||||
|
eval $CMD
|
||||||
|
done
|
||||||
|
|
||||||
|
for ((i = 0; i < ${#CONFIG_TXT[@]}; i++)); do
|
||||||
|
CONFIG_LINE="${CONFIG_TXT[$i]}"
|
||||||
|
if ! [ "$CONFIG_LINE" == "" ]; then
|
||||||
|
do_config_backup
|
||||||
|
inform "Adding $CONFIG_LINE to $CONFIG\n"
|
||||||
|
sed -i "s/^#$CONFIG_LINE/$CONFIG_LINE/" $CONFIG
|
||||||
|
if ! grep -q "^$CONFIG_LINE" $CONFIG; then
|
||||||
|
printf "$CONFIG_LINE\n" >> $CONFIG
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -d "examples" ]; then
|
||||||
|
if confirm "Would you like to copy examples to $RESOURCES_DIR?"; then
|
||||||
|
inform "Copying examples to $RESOURCES_DIR"
|
||||||
|
cp -r examples/ $RESOURCES_DIR
|
||||||
|
echo "rm -r $RESOURCES_DIR" >> $UNINSTALLER
|
||||||
|
success "Done!"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf "\n"
|
||||||
|
|
||||||
|
if [ -f "/usr/bin/pydoc" ]; then
|
||||||
|
printf "Generating documentation.\n"
|
||||||
|
pydoc -w $LIBRARY_NAME > /dev/null
|
||||||
|
if [ -f "$LIBRARY_NAME.html" ]; then
|
||||||
|
cp $LIBRARY_NAME.html $RESOURCES_DIR/docs.html
|
||||||
|
rm -f $LIBRARY_NAME.html
|
||||||
|
inform "Documentation saved to $RESOURCES_DIR/docs.html"
|
||||||
|
success "Done!"
|
||||||
|
else
|
||||||
|
warning "Error: Failed to generate documentation."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
success "\nAll done!"
|
||||||
|
inform "If this is your first time installing you should reboot for hardware changes to take effect.\n"
|
||||||
|
inform "Find uninstall steps in $UNINSTALLER\n"
|
||||||
4
vendor/ltp305-python/library/.coveragerc
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
[run]
|
||||||
|
source = ltp305
|
||||||
|
omit =
|
||||||
|
.tox/*
|
||||||
4
vendor/ltp305-python/library/CHANGELOG.txt
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
0.0.1
|
||||||
|
-----
|
||||||
|
|
||||||
|
* Initial Release
|
||||||
21
vendor/ltp305-python/library/LICENSE.txt
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2018 Pimoroni Ltd.
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
5
vendor/ltp305-python/library/MANIFEST.in
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
include CHANGELOG.txt
|
||||||
|
include LICENSE.txt
|
||||||
|
include README.md
|
||||||
|
include setup.py
|
||||||
|
recursive-include ltp305 *.py
|
||||||
33
vendor/ltp305-python/library/README.md
vendored
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
# LTP305 - Breakout Garden dual 5x7 LED matrix driver
|
||||||
|
|
||||||
|
[](https://travis-ci.com/pimoroni/ltp305-python)
|
||||||
|
[](https://coveralls.io/github/pimoroni/ltp305-python?branch=master)
|
||||||
|
[](https://pypi.python.org/pypi/ltp305)
|
||||||
|
[](https://pypi.python.org/pypi/ltp305)
|
||||||
|
|
||||||
|
# Pre-requisites
|
||||||
|
|
||||||
|
You must enable i2c:
|
||||||
|
|
||||||
|
* i2c: `sudo raspi-config nonint do_i2c 0`
|
||||||
|
|
||||||
|
You can optionally run `sudo raspi-config` or the graphical Raspberry Pi Configuration UI to enable interfaces.
|
||||||
|
|
||||||
|
# Installing
|
||||||
|
|
||||||
|
Stable library from PyPi:
|
||||||
|
|
||||||
|
* Just run `sudo pip3 install ltp305`
|
||||||
|
|
||||||
|
Latest/development library from GitHub:
|
||||||
|
|
||||||
|
* `git clone https://github.com/pimoroni/ltp305-python`
|
||||||
|
* `cd ltp305-python`
|
||||||
|
* `sudo ./install.sh`
|
||||||
|
|
||||||
|
|
||||||
|
# Changelog
|
||||||
|
0.0.1
|
||||||
|
-----
|
||||||
|
|
||||||
|
* Initial Release
|
||||||
205
vendor/ltp305-python/library/ltp305/__init__.py
vendored
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
import smbus
|
||||||
|
from .font import font
|
||||||
|
|
||||||
|
|
||||||
|
__version__ = '0.0.1'
|
||||||
|
|
||||||
|
|
||||||
|
MODE = 0b00011000
|
||||||
|
OPTS = 0b00001110 # 1110 = 35mA, 0000 = 40mA
|
||||||
|
|
||||||
|
CMD_BRIGHTNESS = 0x19
|
||||||
|
CMD_MODE = 0x00
|
||||||
|
CMD_UPDATE = 0x0C
|
||||||
|
CMD_OPTIONS = 0x0D
|
||||||
|
|
||||||
|
CMD_MATRIX_L = 0x0E
|
||||||
|
CMD_MATRIX_R = 0x01
|
||||||
|
|
||||||
|
|
||||||
|
class LTP305:
|
||||||
|
"""
|
||||||
|
_buf_matrix_left = [
|
||||||
|
# Row 7654321
|
||||||
|
0b01111111, # col 1, bottom = msb
|
||||||
|
0b01111111, # col 2
|
||||||
|
0b01111111, # col 3
|
||||||
|
0b01111111, # col 4
|
||||||
|
0b01111111, # col 5
|
||||||
|
0b00000000,
|
||||||
|
0b00000000,
|
||||||
|
0b01000000 # bit 7 = decimal dot
|
||||||
|
]
|
||||||
|
|
||||||
|
_buf_matrix_right = [
|
||||||
|
# Col 12345
|
||||||
|
0b00011111, # row 1
|
||||||
|
0b00011111, # row 2
|
||||||
|
0b00011111, # row 3
|
||||||
|
0b00011111, # row 4
|
||||||
|
0b00011111, # row 5
|
||||||
|
0b00011111, # row 6
|
||||||
|
0b10011111, # row 7 + bit 8 = decimal dot
|
||||||
|
0b00000000
|
||||||
|
]
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, address=0x61, brightness=0.5):
|
||||||
|
"""LTP305 5x7 x 2 Matrix Driver
|
||||||
|
|
||||||
|
:param address: i2c address, one of 0x61, 0x62 or 0x63 (default 0x61)
|
||||||
|
:param brightness: LED brightness from 0.0 to 1.0 (default 0.5)
|
||||||
|
|
||||||
|
"""
|
||||||
|
self.bus = smbus.SMBus(1)
|
||||||
|
self.address = address
|
||||||
|
self.set_brightness(brightness)
|
||||||
|
self.clear()
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
"""Clear both LED matrices.
|
||||||
|
|
||||||
|
Must call .show() to display changes.
|
||||||
|
|
||||||
|
"""
|
||||||
|
self._buf_matrix_left = [0 for _ in range(8)]
|
||||||
|
self._buf_matrix_right = [0 for _ in range(8)]
|
||||||
|
|
||||||
|
def set_brightness(self, brightness, update=False):
|
||||||
|
"""Set brightness of both LED matrices.
|
||||||
|
|
||||||
|
:param brightnes: LED brightness from 0.0 to 1.0
|
||||||
|
:param update: Push change to display immediately (otherwise you must call .show())
|
||||||
|
|
||||||
|
"""
|
||||||
|
self._brightness = int(brightness * 127.0)
|
||||||
|
self._brightness = min(127, max(0, self._brightness))
|
||||||
|
if update:
|
||||||
|
self.bus.write_byte_data(self.address, CMD_BRIGHTNESS, self._brightness)
|
||||||
|
|
||||||
|
def set_decimal(self, left=None, right=None):
|
||||||
|
"""Set decimal of left and/or right matrix.
|
||||||
|
|
||||||
|
:param left: State of left decimal dot
|
||||||
|
:param right: State of right decimal dot
|
||||||
|
|
||||||
|
"""
|
||||||
|
if left is not None:
|
||||||
|
if left:
|
||||||
|
self._buf_matrix_left[7] |= 0b01000000
|
||||||
|
else:
|
||||||
|
self._buf_matrix_left[7] &= 0b10111111
|
||||||
|
if right is not None:
|
||||||
|
if right:
|
||||||
|
self._buf_matrix_right[6] |= 0b10000000
|
||||||
|
else:
|
||||||
|
self._buf_matrix_right[6] &= 0b01111111
|
||||||
|
|
||||||
|
def set_pixel(self, x, y, c):
|
||||||
|
"""Set a single pixel on the matrix.
|
||||||
|
|
||||||
|
:param x: x position from 0 to 9 (0-4 on left matrix, 5-9 on right)
|
||||||
|
:param y: y position
|
||||||
|
:param c: state on/off
|
||||||
|
|
||||||
|
"""
|
||||||
|
if x < 5: # Left Matrix
|
||||||
|
if c:
|
||||||
|
self._buf_matrix_left[x] |= (0b1 << y)
|
||||||
|
else:
|
||||||
|
self._buf_matrix_left[x] &= ~(0b1 << y)
|
||||||
|
else: # Right Matrix
|
||||||
|
x -= 5
|
||||||
|
if c:
|
||||||
|
self._buf_matrix_right[y] |= (0b1 << x)
|
||||||
|
else:
|
||||||
|
self._buf_matrix_right[y] &= ~(0b1 << x)
|
||||||
|
|
||||||
|
def set_character(self, x, char):
|
||||||
|
"""Set a single character.
|
||||||
|
|
||||||
|
:param x: x position, 0 for left, 5 for right, or in between if you fancy
|
||||||
|
:param char: string character or char ordinal
|
||||||
|
|
||||||
|
"""
|
||||||
|
if type(char) is not int:
|
||||||
|
char = ord(char)
|
||||||
|
char = font[char]
|
||||||
|
for cx in range(5):
|
||||||
|
for cy in range(8):
|
||||||
|
c = char[cx] & (0b1 << cy)
|
||||||
|
self.set_pixel(x + cx, cy, c)
|
||||||
|
|
||||||
|
def get_shape(self):
|
||||||
|
"""Set the width/height of the display."""
|
||||||
|
return 10, 7
|
||||||
|
|
||||||
|
def set_image(self, image, offset_x=0, offset_y=0, wrap=False, bg=0):
|
||||||
|
"""Set a PIL image to the display buffer."""
|
||||||
|
image_width, image_height = image.size
|
||||||
|
|
||||||
|
if image.mode != "1":
|
||||||
|
image = image.convert("1")
|
||||||
|
|
||||||
|
display_width, display_height = self.get_shape()
|
||||||
|
|
||||||
|
for y in range(display_height):
|
||||||
|
for x in range(display_width):
|
||||||
|
p = bg
|
||||||
|
i_x = x + offset_x
|
||||||
|
i_y = y + offset_y
|
||||||
|
if wrap:
|
||||||
|
while i_x >= image_width:
|
||||||
|
i_x -= image_width
|
||||||
|
while i_y >= image_height:
|
||||||
|
i_y -= image_height
|
||||||
|
if i_x < image_width and i_y < image_height:
|
||||||
|
p = image.getpixel((i_x, i_y))
|
||||||
|
self.set_pixel(x, y, p)
|
||||||
|
|
||||||
|
def show(self):
|
||||||
|
"""Update the LED matrixes from the buffer."""
|
||||||
|
self.bus.write_i2c_block_data(self.address, CMD_MATRIX_L, self._buf_matrix_left)
|
||||||
|
self.bus.write_i2c_block_data(self.address, CMD_MATRIX_R, self._buf_matrix_right)
|
||||||
|
self.bus.write_byte_data(self.address, CMD_MODE, MODE)
|
||||||
|
self.bus.write_byte_data(self.address, CMD_OPTIONS, OPTS)
|
||||||
|
self.bus.write_byte_data(self.address, CMD_BRIGHTNESS, self._brightness)
|
||||||
|
self.bus.write_byte_data(self.address, CMD_UPDATE, 0x01)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import time
|
||||||
|
matrix = LTP305(0x61)
|
||||||
|
delay = 1.0 / 72 * 4
|
||||||
|
c = True
|
||||||
|
while True:
|
||||||
|
for n in range(10):
|
||||||
|
matrix.set_character(0, str(n))
|
||||||
|
matrix.set_character(5, 'abcdefghij'[n])
|
||||||
|
matrix.show()
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
||||||
|
matrix.set_character(0, "=")
|
||||||
|
matrix.set_character(5, ")")
|
||||||
|
matrix.show()
|
||||||
|
time.sleep(1.0)
|
||||||
|
|
||||||
|
matrix.clear()
|
||||||
|
matrix.show()
|
||||||
|
time.sleep(0.5)
|
||||||
|
|
||||||
|
for _ in range(2):
|
||||||
|
for y in range(7):
|
||||||
|
for x in range(10):
|
||||||
|
matrix.set_pixel(x, y, c)
|
||||||
|
matrix.show()
|
||||||
|
time.sleep(delay)
|
||||||
|
|
||||||
|
matrix.set_decimal(left=c)
|
||||||
|
matrix.show()
|
||||||
|
time.sleep(delay)
|
||||||
|
|
||||||
|
matrix.set_decimal(right=c)
|
||||||
|
matrix.show()
|
||||||
|
time.sleep(delay)
|
||||||
|
c = not c
|
||||||
408
vendor/ltp305-python/library/ltp305/font.py
vendored
Normal file
@@ -0,0 +1,408 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
tinynumbers = [
|
||||||
|
[0b11111, 0b11111],
|
||||||
|
[0b11111],
|
||||||
|
[0b10111, 0b11101],
|
||||||
|
[0b10101, 0b11111],
|
||||||
|
[0b11110, 0b00011],
|
||||||
|
[0b11101, 0b10111],
|
||||||
|
[0b11111, 0b00111],
|
||||||
|
[0b10000, 0b11111],
|
||||||
|
[0b10101, 0b10101],
|
||||||
|
[0b11100, 0b11111]
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
# add japanese
|
||||||
|
font = {
|
||||||
|
32: [0x00, 0x00, 0x00, 0x00, 0x00], # (space)
|
||||||
|
33: [0x00, 0x00, 0x5f, 0x00, 0x00], # !
|
||||||
|
34: [0x00, 0x07, 0x00, 0x07, 0x00], # "
|
||||||
|
35: [0x14, 0x7f, 0x14, 0x7f, 0x14], # #
|
||||||
|
36: [0x24, 0x2a, 0x7f, 0x2a, 0x12], # $
|
||||||
|
37: [0x23, 0x13, 0x08, 0x64, 0x62], # %
|
||||||
|
38: [0x36, 0x49, 0x55, 0x22, 0x50], # &
|
||||||
|
39: [0x00, 0x05, 0x03, 0x00, 0x00], # '
|
||||||
|
40: [0x00, 0x1c, 0x22, 0x41, 0x00], # (
|
||||||
|
41: [0x00, 0x41, 0x22, 0x1c, 0x00], # )
|
||||||
|
42: [0x08, 0x2a, 0x1c, 0x2a, 0x08], # *
|
||||||
|
43: [0x08, 0x08, 0x3e, 0x08, 0x08], # +
|
||||||
|
44: [0x00, 0x50, 0x30, 0x00, 0x00], # ,
|
||||||
|
45: [0x08, 0x08, 0x08, 0x08, 0x08], # -
|
||||||
|
46: [0x00, 0x60, 0x60, 0x00, 0x00], # .
|
||||||
|
47: [0x20, 0x10, 0x08, 0x04, 0x02], # /
|
||||||
|
48: [0x3e, 0x51, 0x49, 0x45, 0x3e], # 0
|
||||||
|
49: [0x00, 0x42, 0x7f, 0x40, 0x00], # 1
|
||||||
|
50: [0x42, 0x61, 0x51, 0x49, 0x46], # 2
|
||||||
|
51: [0x21, 0x41, 0x45, 0x4b, 0x31], # 3
|
||||||
|
52: [0x18, 0x14, 0x12, 0x7f, 0x10], # 4
|
||||||
|
53: [0x27, 0x45, 0x45, 0x45, 0x39], # 5
|
||||||
|
54: [0x3c, 0x4a, 0x49, 0x49, 0x30], # 6
|
||||||
|
55: [0x01, 0x71, 0x09, 0x05, 0x03], # 7
|
||||||
|
56: [0x36, 0x49, 0x49, 0x49, 0x36], # 8
|
||||||
|
57: [0x06, 0x49, 0x49, 0x29, 0x1e], # 9
|
||||||
|
58: [0x00, 0x36, 0x36, 0x00, 0x00], # :
|
||||||
|
59: [0x00, 0x56, 0x36, 0x00, 0x00], # ;
|
||||||
|
60: [0x00, 0x08, 0x14, 0x22, 0x41], # <
|
||||||
|
61: [0x14, 0x14, 0x14, 0x14, 0x14], # =
|
||||||
|
62: [0x41, 0x22, 0x14, 0x08, 0x00], # >
|
||||||
|
63: [0x02, 0x01, 0x51, 0x09, 0x06], # ?
|
||||||
|
64: [0x32, 0x49, 0x79, 0x41, 0x3e], # @
|
||||||
|
65: [0x7e, 0x11, 0x11, 0x11, 0x7e], # A
|
||||||
|
66: [0x7f, 0x49, 0x49, 0x49, 0x36], # B
|
||||||
|
67: [0x3e, 0x41, 0x41, 0x41, 0x22], # C
|
||||||
|
68: [0x7f, 0x41, 0x41, 0x22, 0x1c], # D
|
||||||
|
69: [0x7f, 0x49, 0x49, 0x49, 0x41], # E
|
||||||
|
70: [0x7f, 0x09, 0x09, 0x01, 0x01], # F
|
||||||
|
71: [0x3e, 0x41, 0x41, 0x51, 0x32], # G
|
||||||
|
72: [0x7f, 0x08, 0x08, 0x08, 0x7f], # H
|
||||||
|
73: [0x00, 0x41, 0x7f, 0x41, 0x00], # I
|
||||||
|
74: [0x20, 0x40, 0x41, 0x3f, 0x01], # J
|
||||||
|
75: [0x7f, 0x08, 0x14, 0x22, 0x41], # K
|
||||||
|
76: [0x7f, 0x40, 0x40, 0x40, 0x40], # L
|
||||||
|
77: [0x7f, 0x02, 0x04, 0x02, 0x7f], # M
|
||||||
|
78: [0x7f, 0x04, 0x08, 0x10, 0x7f], # N
|
||||||
|
79: [0x3e, 0x41, 0x41, 0x41, 0x3e], # O
|
||||||
|
80: [0x7f, 0x09, 0x09, 0x09, 0x06], # P
|
||||||
|
81: [0x3e, 0x41, 0x51, 0x21, 0x5e], # Q
|
||||||
|
82: [0x7f, 0x09, 0x19, 0x29, 0x46], # R
|
||||||
|
83: [0x46, 0x49, 0x49, 0x49, 0x31], # S
|
||||||
|
84: [0x01, 0x01, 0x7f, 0x01, 0x01], # T
|
||||||
|
85: [0x3f, 0x40, 0x40, 0x40, 0x3f], # U
|
||||||
|
86: [0x1f, 0x20, 0x40, 0x20, 0x1f], # V
|
||||||
|
87: [0x7f, 0x20, 0x18, 0x20, 0x7f], # W
|
||||||
|
88: [0x63, 0x14, 0x08, 0x14, 0x63], # X
|
||||||
|
89: [0x03, 0x04, 0x78, 0x04, 0x03], # Y
|
||||||
|
90: [0x61, 0x51, 0x49, 0x45, 0x43], # Z
|
||||||
|
91: [0x00, 0x00, 0x7f, 0x41, 0x41], # [
|
||||||
|
92: [0x02, 0x04, 0x08, 0x10, 0x20], # \
|
||||||
|
93: [0x41, 0x41, 0x7f, 0x00, 0x00], # ]
|
||||||
|
94: [0x04, 0x02, 0x01, 0x02, 0x04], # ^
|
||||||
|
95: [0x40, 0x40, 0x40, 0x40, 0x40], # _
|
||||||
|
96: [0x00, 0x01, 0x02, 0x04, 0x00], # `
|
||||||
|
97: [0x20, 0x54, 0x54, 0x54, 0x78], # a
|
||||||
|
98: [0x7f, 0x48, 0x44, 0x44, 0x38], # b
|
||||||
|
99: [0x38, 0x44, 0x44, 0x44, 0x20], # c
|
||||||
|
100: [0x38, 0x44, 0x44, 0x48, 0x7f], # d
|
||||||
|
101: [0x38, 0x54, 0x54, 0x54, 0x18], # e
|
||||||
|
102: [0x08, 0x7e, 0x09, 0x01, 0x02], # f
|
||||||
|
103: [0x08, 0x14, 0x54, 0x54, 0x3c], # g
|
||||||
|
104: [0x7f, 0x08, 0x04, 0x04, 0x78], # h
|
||||||
|
105: [0x00, 0x44, 0x7d, 0x40, 0x00], # i
|
||||||
|
106: [0x20, 0x40, 0x44, 0x3d, 0x00], # j
|
||||||
|
107: [0x00, 0x7f, 0x10, 0x28, 0x44], # k
|
||||||
|
108: [0x00, 0x41, 0x7f, 0x40, 0x00], # l
|
||||||
|
109: [0x7c, 0x04, 0x18, 0x04, 0x78], # m
|
||||||
|
110: [0x7c, 0x08, 0x04, 0x04, 0x78], # n
|
||||||
|
111: [0x38, 0x44, 0x44, 0x44, 0x38], # o
|
||||||
|
112: [0x7c, 0x14, 0x14, 0x14, 0x08], # p
|
||||||
|
113: [0x08, 0x14, 0x14, 0x18, 0x7c], # q
|
||||||
|
114: [0x7c, 0x08, 0x04, 0x04, 0x08], # r
|
||||||
|
115: [0x48, 0x54, 0x54, 0x54, 0x20], # s
|
||||||
|
116: [0x04, 0x3f, 0x44, 0x40, 0x20], # t
|
||||||
|
117: [0x3c, 0x40, 0x40, 0x20, 0x7c], # u
|
||||||
|
118: [0x1c, 0x20, 0x40, 0x20, 0x1c], # v
|
||||||
|
119: [0x3c, 0x40, 0x30, 0x40, 0x3c], # w
|
||||||
|
120: [0x44, 0x28, 0x10, 0x28, 0x44], # x
|
||||||
|
121: [0x0c, 0x50, 0x50, 0x50, 0x3c], # y
|
||||||
|
122: [0x44, 0x64, 0x54, 0x4c, 0x44], # z
|
||||||
|
123: [0x00, 0x08, 0x36, 0x41, 0x00], # {
|
||||||
|
124: [0x00, 0x00, 0x7f, 0x00, 0x00], # |
|
||||||
|
125: [0x00, 0x41, 0x36, 0x08, 0x00], # }
|
||||||
|
126: [0x08, 0x08, 0x2a, 0x1c, 0x08], # ~
|
||||||
|
|
||||||
|
8221: [0x00, 0x07, 0x00, 0x07, 0x00], # ”
|
||||||
|
8592: [0x08, 0x1C, 0x2A, 0x08, 0x08], # ←
|
||||||
|
8593: [0x08, 0x04, 0x7E, 0x04, 0x08], # ↑
|
||||||
|
8594: [0x08, 0x08, 0x2A, 0x1C, 0x08], # →
|
||||||
|
8595: [0x08, 0x10, 0x3F, 0x10, 0x08], # ↓
|
||||||
|
9472: [0x08, 0x08, 0x08, 0x08, 0x08], # ─
|
||||||
|
9474: [0x00, 0x00, 0x7F, 0x00, 0x00], # │
|
||||||
|
9484: [0x00, 0x00, 0x78, 0x08, 0x08], # ┌
|
||||||
|
9488: [0x08, 0x08, 0x78, 0x00, 0x00], # ┐
|
||||||
|
9492: [0x00, 0x00, 0x0F, 0x08, 0x08], # └
|
||||||
|
9496: [0x08, 0x08, 0x0F, 0x00, 0x00], # ┘
|
||||||
|
9500: [0x00, 0x00, 0x7F, 0x08, 0x08], # ├
|
||||||
|
9508: [0x08, 0x08, 0x7F, 0x00, 0x00], # ┤
|
||||||
|
9516: [0x08, 0x08, 0x78, 0x08, 0x08], # ┬
|
||||||
|
9524: [0x08, 0x08, 0x0F, 0x08, 0x08], # ┴
|
||||||
|
9532: [0x08, 0x08, 0x7F, 0x08, 0x08], # ┼
|
||||||
|
9632: [0x7F, 0x7F, 0x7F, 0x7F, 0x7F], # ■
|
||||||
|
9633: [0x7F, 0x41, 0x41, 0x41, 0x7F], # □
|
||||||
|
9650: [0x10, 0x1C, 0x1E, 0x1C, 0x10], # ▲
|
||||||
|
9651: [0x10, 0x1C, 0x12, 0x1C, 0x10], # △
|
||||||
|
9660: [0x04, 0x1C, 0x3C, 0x1C, 0x04], # ▼
|
||||||
|
9661: [0x04, 0x1C, 0x24, 0x1C, 0x04], # ▽
|
||||||
|
9670: [0x08, 0x1C, 0x3E, 0x1C, 0x08], # ◆
|
||||||
|
9675: [0x1C, 0x22, 0x22, 0x22, 0x1C], # ○
|
||||||
|
9679: [0x1C, 0x3E, 0x3E, 0x3E, 0x1C], # ●
|
||||||
|
9733: [0x64, 0x3E, 0x1F, 0x3E, 0x64], # ★
|
||||||
|
12288: [0x00, 0x00, 0x00, 0x00, 0x00], #
|
||||||
|
12289: [0x10, 0x20, 0x40, 0x00, 0x00], # 、
|
||||||
|
12290: [0x70, 0x50, 0x70, 0x00, 0x00], # 。
|
||||||
|
12300: [0x00, 0x0F, 0x01, 0x01, 0x01], # 「
|
||||||
|
12301: [0x40, 0x40, 0x40, 0x78, 0x00], # 」
|
||||||
|
12316: [0x02, 0x01, 0x02, 0x04, 0x02], # 〜
|
||||||
|
12353: [0x28, 0x58, 0x3C, 0x68, 0x00], # ぁ
|
||||||
|
12354: [0x32, 0x7A, 0x2F, 0x1A, 0x72], # あ
|
||||||
|
12355: [0x38, 0x40, 0x08, 0x30, 0x00], # ぃ
|
||||||
|
12356: [0x3E, 0x40, 0x20, 0x00, 0x1C], # い
|
||||||
|
12357: [0x10, 0x0C, 0x4C, 0x30, 0x00], # ぅ
|
||||||
|
12358: [0x04, 0x05, 0x45, 0x25, 0x18], # う
|
||||||
|
12359: [0x48, 0x2C, 0x5C, 0x48, 0x00], # ぇ
|
||||||
|
12360: [0x44, 0x24, 0x35, 0x4D, 0x44], # え
|
||||||
|
12361: [0x68, 0x7C, 0x10, 0x64, 0x00], # ぉ
|
||||||
|
12362: [0x32, 0x7F, 0x0A, 0x48, 0x32], # お
|
||||||
|
12363: [0x34, 0x0F, 0x44, 0x38, 0x06], # か
|
||||||
|
12365: [0x22, 0x4A, 0x4F, 0x5A, 0x08], # き
|
||||||
|
12367: [0x00, 0x18, 0x24, 0x43, 0x00], # く
|
||||||
|
12369: [0x3E, 0x00, 0x44, 0x3F, 0x04], # け
|
||||||
|
12371: [0x22, 0x52, 0x42, 0x42, 0x44], # こ
|
||||||
|
12373: [0x24, 0x44, 0x47, 0x5C, 0x12], # さ
|
||||||
|
12375: [0x3F, 0x40, 0x40, 0x20, 0x10], # し
|
||||||
|
12377: [0x02, 0x0A, 0x56, 0x3F, 0x02], # す
|
||||||
|
12379: [0x04, 0x3E, 0x44, 0x5F, 0x44], # せ
|
||||||
|
12381: [0x08, 0x2D, 0x5B, 0x45, 0x44], # そ
|
||||||
|
12383: [0x72, 0x0F, 0x22, 0x4A, 0x48], # た
|
||||||
|
12385: [0x0A, 0x0E, 0x4B, 0x4A, 0x32], # ち
|
||||||
|
12387: [0x08, 0x48, 0x48, 0x30, 0x00], # っ
|
||||||
|
12388: [0x02, 0x02, 0x42, 0x22, 0x1C], # つ
|
||||||
|
12390: [0x02, 0x32, 0x4A, 0x46, 0x42], # て
|
||||||
|
12392: [0x30, 0x4B, 0x44, 0x44, 0x42], # と
|
||||||
|
12394: [0x0A, 0x27, 0x52, 0x38, 0x26], # な
|
||||||
|
12395: [0x3E, 0x00, 0x22, 0x42, 0x42], # に
|
||||||
|
12396: [0x38, 0x6F, 0x1C, 0x67, 0x78], # ぬ
|
||||||
|
12397: [0x12, 0x7F, 0x0A, 0x64, 0x78], # ね
|
||||||
|
12398: [0x3C, 0x62, 0x1E, 0x44, 0x38], # の
|
||||||
|
12399: [0x3E, 0x00, 0x32, 0x7F, 0x22], # は
|
||||||
|
12402: [0x04, 0x3E, 0x40, 0x47, 0x3C], # ひ
|
||||||
|
12405: [0x30, 0x05, 0x7A, 0x00, 0x38], # ふ
|
||||||
|
12411: [0x3E, 0x00, 0x35, 0x7F, 0x25], # ほ
|
||||||
|
12414: [0x2A, 0x5A, 0x7F, 0x2A, 0x4A], # ま
|
||||||
|
12415: [0x71, 0x3F, 0x08, 0x7E, 0x08], # み
|
||||||
|
12416: [0x1A, 0x3F, 0x42, 0x40, 0x26], # む
|
||||||
|
12417: [0x38, 0x4E, 0x34, 0x1F, 0x78], # め
|
||||||
|
12418: [0x14, 0x3F, 0x54, 0x40, 0x20], # も
|
||||||
|
12419: [0x1C, 0x68, 0x0C, 0x30, 0x00], # ゃ
|
||||||
|
12420: [0x04, 0x7F, 0x04, 0x2E, 0x18], # や
|
||||||
|
12421: [0x18, 0x50, 0x3C, 0x18, 0x00], # ゅ
|
||||||
|
12422: [0x1E, 0x44, 0x3F, 0x12, 0x0C], # ゆ
|
||||||
|
12423: [0x60, 0x7C, 0x28, 0x40, 0x00], # ょ
|
||||||
|
12424: [0x20, 0x50, 0x3F, 0x24, 0x44], # よ
|
||||||
|
12425: [0x10, 0x0D, 0x49, 0x4A, 0x30], # ら
|
||||||
|
12426: [0x0E, 0x00, 0x40, 0x21, 0x1E], # り
|
||||||
|
12427: [0x20, 0x55, 0x65, 0x4B, 0x30], # る
|
||||||
|
12428: [0x34, 0x7F, 0x04, 0x3E, 0x40], # れ
|
||||||
|
12429: [0x10, 0x49, 0x4D, 0x4B, 0x30], # ろ
|
||||||
|
12431: [0x24, 0x7F, 0x0A, 0x44, 0x38], # わ
|
||||||
|
12434: [0x22, 0x5F, 0x4A, 0x56, 0x42], # を
|
||||||
|
12435: [0x70, 0x0F, 0x30, 0x40, 0x20], # ん
|
||||||
|
12443: [0x02, 0x04, 0x01, 0x02, 0x00], # ゛
|
||||||
|
12444: [0x07, 0x05, 0x07, 0x00, 0x00], # ゜
|
||||||
|
12449: [0x44, 0x44, 0x3C, 0x14, 0x0C], # ァ
|
||||||
|
12450: [0x42, 0x42, 0x3A, 0x12, 0x0E], # ア
|
||||||
|
12451: [0x20, 0x10, 0x78, 0x04, 0x00], # ィ
|
||||||
|
12452: [0x10, 0x08, 0x04, 0x7E, 0x01], # イ
|
||||||
|
12453: [0x18, 0x08, 0x4C, 0x48, 0x38], # ゥ
|
||||||
|
12454: [0x0E, 0x42, 0x43, 0x22, 0x1E], # ウ
|
||||||
|
12455: [0x48, 0x48, 0x78, 0x48, 0x48], # ェ
|
||||||
|
12456: [0x42, 0x42, 0x7E, 0x42, 0x42], # エ
|
||||||
|
12457: [0x48, 0x38, 0x08, 0x7C, 0x08], # ォ
|
||||||
|
12458: [0x22, 0x12, 0x0A, 0x7F, 0x02], # オ
|
||||||
|
12459: [0x42, 0x3F, 0x02, 0x02, 0x7E], # カ
|
||||||
|
12461: [0x0A, 0x0A, 0x7F, 0x0A, 0x0A], # キ
|
||||||
|
12463: [0x08, 0x06, 0x42, 0x22, 0x1E], # ク
|
||||||
|
12465: [0x0F, 0x42, 0x42, 0x3E, 0x02], # ケ
|
||||||
|
12467: [0x42, 0x42, 0x42, 0x42, 0x7E], # コ
|
||||||
|
12469: [0x02, 0x4F, 0x42, 0x3F, 0x02], # サ
|
||||||
|
12471: [0x4A, 0x4A, 0x40, 0x20, 0x1C], # シ
|
||||||
|
12473: [0x42, 0x22, 0x12, 0x2A, 0x46], # ス
|
||||||
|
12475: [0x02, 0x3F, 0x42, 0x42, 0x4E], # セ
|
||||||
|
12477: [0x06, 0x48, 0x40, 0x30, 0x0E], # ソ
|
||||||
|
12479: [0x08, 0x4E, 0x52, 0x32, 0x0E], # タ
|
||||||
|
12481: [0x08, 0x4A, 0x4A, 0x3F, 0x09], # チ
|
||||||
|
12483: [0x18, 0x40, 0x58, 0x40, 0x30], # ッ
|
||||||
|
12484: [0x0E, 0x40, 0x4E, 0x20, 0x1E], # ツ
|
||||||
|
12486: [0x04, 0x45, 0x45, 0x3D, 0x04], # テ
|
||||||
|
12488: [0x00, 0x7F, 0x08, 0x10, 0x10], # ト
|
||||||
|
12490: [0x44, 0x44, 0x44, 0x3F, 0x04], # ナ
|
||||||
|
12491: [0x40, 0x42, 0x42, 0x42, 0x40], # ニ
|
||||||
|
12492: [0x42, 0x4A, 0x2A, 0x12, 0x2E], # ヌ
|
||||||
|
12493: [0x22, 0x12, 0x73, 0x0A, 0x16], # ネ
|
||||||
|
12494: [0x40, 0x40, 0x20, 0x10, 0x0E], # ノ
|
||||||
|
12495: [0x70, 0x0E, 0x00, 0x07, 0x78], # ハ
|
||||||
|
12498: [0x3F, 0x44, 0x44, 0x44, 0x44], # ヒ
|
||||||
|
12501: [0x02, 0x42, 0x42, 0x22, 0x1E], # フ
|
||||||
|
12504: [0x08, 0x04, 0x02, 0x0C, 0x30], # ヘ
|
||||||
|
12507: [0x32, 0x02, 0x7F, 0x02, 0x32], # ホ
|
||||||
|
12510: [0x02, 0x12, 0x22, 0x52, 0x0E], # マ
|
||||||
|
12511: [0x20, 0x2A, 0x2A, 0x2A, 0x40], # ミ
|
||||||
|
12512: [0x30, 0x2C, 0x22, 0x28, 0x70], # ム
|
||||||
|
12513: [0x40, 0x44, 0x28, 0x10, 0x2E], # メ
|
||||||
|
12514: [0x0A, 0x0A, 0x3E, 0x4A, 0x4A], # モ
|
||||||
|
12515: [0x08, 0x7C, 0x08, 0x28, 0x18], # ャ
|
||||||
|
12516: [0x04, 0x7F, 0x04, 0x14, 0x0C], # ヤ
|
||||||
|
12517: [0x48, 0x48, 0x48, 0x78, 0x40], # ュ
|
||||||
|
12518: [0x42, 0x42, 0x42, 0x7E, 0x40], # ユ
|
||||||
|
12519: [0x54, 0x54, 0x54, 0x7C, 0x00], # ョ
|
||||||
|
12520: [0x4A, 0x4A, 0x4A, 0x4A, 0x7E], # ヨ
|
||||||
|
12521: [0x04, 0x45, 0x45, 0x25, 0x1C], # ラ
|
||||||
|
12522: [0x0E, 0x40, 0x40, 0x20, 0x1E], # リ
|
||||||
|
12523: [0x7E, 0x00, 0x7E, 0x40, 0x38], # ル
|
||||||
|
12524: [0x7E, 0x40, 0x40, 0x20, 0x10], # レ
|
||||||
|
12525: [0x7E, 0x42, 0x42, 0x42, 0x7E], # ロ
|
||||||
|
12527: [0x0E, 0x42, 0x42, 0x22, 0x1E], # ワ
|
||||||
|
12530: [0x0A, 0x4A, 0x4A, 0x2A, 0x1E], # ヲ
|
||||||
|
12531: [0x42, 0x42, 0x40, 0x20, 0x1C], # ン
|
||||||
|
12539: [0x00, 0x00, 0x08, 0x00, 0x00], # ・
|
||||||
|
12540: [0x04, 0x08, 0x08, 0x08, 0x08], # ー
|
||||||
|
65281: [0x00, 0x00, 0x5F, 0x00, 0x00], # !
|
||||||
|
65283: [0x14, 0x7F, 0x14, 0x7F, 0x14], # #
|
||||||
|
65284: [0x24, 0x2A, 0x7F, 0x2A, 0x12], # $
|
||||||
|
65285: [0x23, 0x13, 0x08, 0x64, 0x62], # %
|
||||||
|
65286: [0x36, 0x49, 0x56, 0x20, 0x50], # &
|
||||||
|
65288: [0x00, 0x1C, 0x22, 0x41, 0x00], # (
|
||||||
|
65289: [0x00, 0x41, 0x22, 0x1C, 0x00], # )
|
||||||
|
65290: [0x2A, 0x1C, 0x3E, 0x1C, 0x2A], # *
|
||||||
|
65291: [0x08, 0x08, 0x3E, 0x08, 0x08], # +
|
||||||
|
65292: [0x00, 0x10, 0x30, 0x00, 0x00], # ,
|
||||||
|
65294: [0x00, 0x60, 0x60, 0x00, 0x00], # .
|
||||||
|
65295: [0x20, 0x10, 0x08, 0x04, 0x02], # /
|
||||||
|
65296: [0x3E, 0x51, 0x49, 0x45, 0x3E], # 0
|
||||||
|
65297: [0x42, 0x42, 0x7F, 0x40, 0x40], # 1
|
||||||
|
65298: [0x42, 0x61, 0x51, 0x49, 0x46], # 2
|
||||||
|
65299: [0x22, 0x41, 0x49, 0x49, 0x36], # 3
|
||||||
|
65300: [0x38, 0x24, 0x22, 0x7F, 0x20], # 4
|
||||||
|
65301: [0x2F, 0x45, 0x45, 0x45, 0x39], # 5
|
||||||
|
65302: [0x3C, 0x4A, 0x49, 0x49, 0x30], # 6
|
||||||
|
65303: [0x03, 0x01, 0x79, 0x05, 0x03], # 7
|
||||||
|
65304: [0x36, 0x49, 0x49, 0x49, 0x36], # 8
|
||||||
|
65305: [0x06, 0x49, 0x49, 0x49, 0x3E], # 9
|
||||||
|
65306: [0x00, 0x00, 0x12, 0x00, 0x00], # :
|
||||||
|
65307: [0x00, 0x40, 0x32, 0x00, 0x00], # ;
|
||||||
|
65308: [0x08, 0x14, 0x22, 0x41, 0x00], # <
|
||||||
|
65309: [0x14, 0x14, 0x14, 0x14, 0x14], # =
|
||||||
|
65310: [0x00, 0x41, 0x22, 0x14, 0x08], # >
|
||||||
|
65311: [0x02, 0x01, 0x51, 0x09, 0x06], # ?
|
||||||
|
65312: [0x1C, 0x22, 0x49, 0x55, 0x4E], # @
|
||||||
|
65313: [0x7C, 0x12, 0x11, 0x12, 0x7C], # A
|
||||||
|
65314: [0x7F, 0x49, 0x49, 0x49, 0x36], # B
|
||||||
|
65315: [0x3E, 0x41, 0x41, 0x41, 0x22], # C
|
||||||
|
65316: [0x7F, 0x41, 0x41, 0x22, 0x1C], # D
|
||||||
|
65317: [0x7F, 0x49, 0x49, 0x49, 0x41], # E
|
||||||
|
65318: [0x7F, 0x09, 0x09, 0x09, 0x01], # F
|
||||||
|
65319: [0x3E, 0x41, 0x49, 0x49, 0x79], # G
|
||||||
|
65320: [0x7F, 0x08, 0x08, 0x08, 0x7F], # H
|
||||||
|
65321: [0x00, 0x41, 0x7F, 0x41, 0x00], # I
|
||||||
|
65322: [0x20, 0x40, 0x41, 0x3F, 0x01], # J
|
||||||
|
65323: [0x7F, 0x08, 0x14, 0x22, 0x41], # K
|
||||||
|
65324: [0x7F, 0x40, 0x40, 0x40, 0x40], # L
|
||||||
|
65325: [0x7F, 0x02, 0x04, 0x02, 0x7F], # M
|
||||||
|
65326: [0x7F, 0x02, 0x04, 0x08, 0x7F], # N
|
||||||
|
65327: [0x3E, 0x41, 0x41, 0x41, 0x3E], # O
|
||||||
|
65328: [0x7F, 0x09, 0x09, 0x09, 0x06], # P
|
||||||
|
65329: [0x3E, 0x41, 0x51, 0x21, 0x5E], # Q
|
||||||
|
65330: [0x7F, 0x09, 0x19, 0x29, 0x46], # R
|
||||||
|
65331: [0x26, 0x49, 0x49, 0x49, 0x32], # S
|
||||||
|
65332: [0x01, 0x01, 0x7F, 0x01, 0x01], # T
|
||||||
|
65333: [0x3F, 0x40, 0x40, 0x40, 0x3F], # U
|
||||||
|
65334: [0x07, 0x18, 0x60, 0x18, 0x07], # V
|
||||||
|
65335: [0x7F, 0x20, 0x10, 0x20, 0x7F], # W
|
||||||
|
65336: [0x63, 0x14, 0x08, 0x14, 0x63], # X
|
||||||
|
65337: [0x03, 0x04, 0x78, 0x04, 0x03], # Y
|
||||||
|
65338: [0x61, 0x51, 0x49, 0x45, 0x43], # Z
|
||||||
|
65339: [0x00, 0x7F, 0x41, 0x41, 0x00], # [
|
||||||
|
65340: [0x02, 0x04, 0x08, 0x10, 0x20], # \
|
||||||
|
65341: [0x00, 0x41, 0x41, 0x7F, 0x00], # ]
|
||||||
|
65342: [0x04, 0x02, 0x01, 0x02, 0x04], # ^
|
||||||
|
65343: [0x40, 0x40, 0x40, 0x40, 0x40], # _
|
||||||
|
65344: [0x00, 0x01, 0x02, 0x00, 0x00], # `
|
||||||
|
65345: [0x24, 0x54, 0x54, 0x54, 0x78], # a
|
||||||
|
65346: [0x7F, 0x44, 0x44, 0x44, 0x38], # b
|
||||||
|
65347: [0x38, 0x44, 0x44, 0x44, 0x44], # c
|
||||||
|
65348: [0x38, 0x44, 0x44, 0x44, 0x7F], # d
|
||||||
|
65349: [0x38, 0x54, 0x54, 0x54, 0x18], # e
|
||||||
|
65350: [0x08, 0x08, 0x7E, 0x09, 0x09], # f
|
||||||
|
65351: [0x0C, 0x52, 0x52, 0x52, 0x3E], # g
|
||||||
|
65352: [0x7F, 0x08, 0x04, 0x04, 0x78], # h
|
||||||
|
65353: [0x00, 0x44, 0x7D, 0x40, 0x00], # i
|
||||||
|
65354: [0x20, 0x40, 0x40, 0x44, 0x3D], # j
|
||||||
|
65355: [0x7F, 0x20, 0x10, 0x28, 0x44], # k
|
||||||
|
65356: [0x00, 0x41, 0x7F, 0x40, 0x00], # l
|
||||||
|
65357: [0x7C, 0x04, 0x7C, 0x04, 0x78], # m
|
||||||
|
65358: [0x7C, 0x04, 0x04, 0x04, 0x78], # n
|
||||||
|
65359: [0x38, 0x44, 0x44, 0x44, 0x38], # o
|
||||||
|
65360: [0x7E, 0x12, 0x12, 0x12, 0x0C], # p
|
||||||
|
65361: [0x0C, 0x12, 0x12, 0x12, 0x7E], # q
|
||||||
|
65362: [0x7C, 0x08, 0x04, 0x04, 0x08], # r
|
||||||
|
65363: [0x48, 0x54, 0x54, 0x54, 0x24], # s
|
||||||
|
65364: [0x04, 0x04, 0x3F, 0x44, 0x44], # t
|
||||||
|
65365: [0x3C, 0x40, 0x40, 0x40, 0x7C], # u
|
||||||
|
65366: [0x1C, 0x20, 0x40, 0x20, 0x1C], # v
|
||||||
|
65367: [0x3C, 0x40, 0x38, 0x40, 0x3C], # w
|
||||||
|
65368: [0x44, 0x28, 0x10, 0x28, 0x44], # x
|
||||||
|
65369: [0x0E, 0x50, 0x50, 0x50, 0x3E], # y
|
||||||
|
65370: [0x44, 0x64, 0x54, 0x4C, 0x44], # z
|
||||||
|
65371: [0x00, 0x08, 0x36, 0x41, 0x41], # {
|
||||||
|
65372: [0x00, 0x00, 0x77, 0x00, 0x00], # |
|
||||||
|
65373: [0x41, 0x41, 0x36, 0x08, 0x00], # }
|
||||||
|
65374: [0x08, 0x04, 0x08, 0x10, 0x08], # ~
|
||||||
|
65383: [0x44, 0x44, 0x3C, 0x14, 0x0C], # ァ
|
||||||
|
65384: [0x42, 0x42, 0x3A, 0x12, 0x0E], # ィ
|
||||||
|
65385: [0x20, 0x10, 0x78, 0x04, 0x00], # ゥ
|
||||||
|
65386: [0x10, 0x08, 0x04, 0x7E, 0x01], # ェ
|
||||||
|
65387: [0x18, 0x08, 0x4C, 0x48, 0x38], # ォ
|
||||||
|
65388: [0x0E, 0x42, 0x43, 0x22, 0x1E], # ャ
|
||||||
|
65389: [0x48, 0x48, 0x78, 0x48, 0x48], # ュ
|
||||||
|
65390: [0x42, 0x42, 0x7E, 0x42, 0x42], # ョ
|
||||||
|
65391: [0x48, 0x38, 0x08, 0x7C, 0x08], # ッ
|
||||||
|
65392: [0x22, 0x12, 0x0A, 0x7F, 0x02], # ー
|
||||||
|
65393: [0x42, 0x3F, 0x02, 0x02, 0x7E], # ア
|
||||||
|
65394: [0x0A, 0x0A, 0x7F, 0x0A, 0x0A], # イ
|
||||||
|
65395: [0x08, 0x06, 0x42, 0x22, 0x1E], # ウ
|
||||||
|
65396: [0x0F, 0x42, 0x42, 0x3E, 0x02], # エ
|
||||||
|
65397: [0x42, 0x42, 0x42, 0x42, 0x7E], # オ
|
||||||
|
65398: [0x02, 0x4F, 0x42, 0x3F, 0x02], # カ
|
||||||
|
65399: [0x4A, 0x4A, 0x40, 0x20, 0x1C], # キ
|
||||||
|
65400: [0x42, 0x22, 0x12, 0x2A, 0x46], # ク
|
||||||
|
65401: [0x02, 0x3F, 0x42, 0x42, 0x4E], # ケ
|
||||||
|
65402: [0x06, 0x48, 0x40, 0x30, 0x0E], # コ
|
||||||
|
65403: [0x08, 0x4E, 0x52, 0x32, 0x0E], # サ
|
||||||
|
65404: [0x08, 0x4A, 0x4A, 0x3F, 0x09], # シ
|
||||||
|
65405: [0x18, 0x40, 0x58, 0x40, 0x30], # ス
|
||||||
|
65406: [0x0E, 0x40, 0x4E, 0x20, 0x1E], # セ
|
||||||
|
65407: [0x04, 0x45, 0x45, 0x3D, 0x04], # ソ
|
||||||
|
65408: [0x00, 0x7F, 0x08, 0x10, 0x10], # タ
|
||||||
|
65409: [0x44, 0x44, 0x44, 0x3F, 0x04], # チ
|
||||||
|
65410: [0x40, 0x42, 0x42, 0x42, 0x40], # ツ
|
||||||
|
65411: [0x42, 0x4A, 0x2A, 0x12, 0x2E], # テ
|
||||||
|
65412: [0x22, 0x12, 0x73, 0x0A, 0x16], # ト
|
||||||
|
65413: [0x40, 0x40, 0x20, 0x10, 0x0E], # ナ
|
||||||
|
65414: [0x70, 0x0E, 0x00, 0x07, 0x78], # ニ
|
||||||
|
65415: [0x3F, 0x44, 0x44, 0x44, 0x44], # ヌ
|
||||||
|
65416: [0x02, 0x42, 0x42, 0x22, 0x1E], # ネ
|
||||||
|
65417: [0x08, 0x04, 0x02, 0x0C, 0x30], # ノ
|
||||||
|
65418: [0x32, 0x02, 0x7F, 0x02, 0x32], # ハ
|
||||||
|
65419: [0x02, 0x12, 0x22, 0x52, 0x0E], # ヒ
|
||||||
|
65420: [0x20, 0x2A, 0x2A, 0x2A, 0x40], # フ
|
||||||
|
65421: [0x30, 0x2C, 0x22, 0x28, 0x70], # ヘ
|
||||||
|
65422: [0x40, 0x44, 0x28, 0x10, 0x2E], # ホ
|
||||||
|
65423: [0x0A, 0x0A, 0x3E, 0x4A, 0x4A], # マ
|
||||||
|
65424: [0x08, 0x7C, 0x08, 0x28, 0x18], # ミ
|
||||||
|
65425: [0x04, 0x7F, 0x04, 0x14, 0x0C], # ム
|
||||||
|
65426: [0x48, 0x48, 0x48, 0x78, 0x40], # メ
|
||||||
|
65427: [0x42, 0x42, 0x42, 0x7E, 0x40], # モ
|
||||||
|
65428: [0x54, 0x54, 0x54, 0x7C, 0x00], # ヤ
|
||||||
|
65429: [0x4A, 0x4A, 0x4A, 0x4A, 0x7E], # ユ
|
||||||
|
65430: [0x04, 0x45, 0x45, 0x25, 0x1C], # ヨ
|
||||||
|
65431: [0x0E, 0x40, 0x40, 0x20, 0x1E], # ラ
|
||||||
|
65432: [0x7E, 0x00, 0x7E, 0x40, 0x38], # リ
|
||||||
|
65433: [0x7E, 0x40, 0x40, 0x20, 0x10], # ル
|
||||||
|
65434: [0x7E, 0x42, 0x42, 0x42, 0x7E], # レ
|
||||||
|
65435: [0x0E, 0x42, 0x42, 0x22, 0x1E], # ロ
|
||||||
|
65436: [0x0A, 0x4A, 0x4A, 0x2A, 0x1E], # ワ
|
||||||
|
65437: [0x42, 0x42, 0x40, 0x20, 0x1C], # ン
|
||||||
|
65438: [0x02, 0x04, 0x01, 0x02, 0x00], # ゙
|
||||||
|
65439: [0x07, 0x05, 0x07, 0x00, 0x00], # ゚
|
||||||
|
}
|
||||||
3
vendor/ltp305-python/library/pyproject.toml
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
[build-system]
|
||||||
|
requires = ["setuptools>=40.8.0", "wheel"]
|
||||||
|
build-backend = "setuptools.build_meta"
|
||||||
49
vendor/ltp305-python/library/setup.cfg
vendored
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
[metadata]
|
||||||
|
name = ltp305
|
||||||
|
version = 0.0.1
|
||||||
|
author = Philip Howard
|
||||||
|
author_email = phil@pimoroni.com
|
||||||
|
description = Python library for dual LTP305 LED matrix breakout
|
||||||
|
long_description = file: README.md
|
||||||
|
long_description_content_type = text/markdown
|
||||||
|
keywords = Raspberry Pi
|
||||||
|
url = https://www.pimoroni.com
|
||||||
|
project_urls =
|
||||||
|
GitHub=https://www.github.com/pimoroni/ltp305-python
|
||||||
|
license = MIT
|
||||||
|
# This includes the license file(s) in the wheel.
|
||||||
|
# https://wheel.readthedocs.io/en/stable/user_guide.html#including-license-files-in-the-generated-wheel-file
|
||||||
|
license_files = LICENSE.txt
|
||||||
|
classifiers =
|
||||||
|
Development Status :: 4 - Beta
|
||||||
|
Operating System :: POSIX :: Linux
|
||||||
|
License :: OSI Approved :: MIT License
|
||||||
|
Intended Audience :: Developers
|
||||||
|
Programming Language :: Python :: 2.7
|
||||||
|
Programming Language :: Python :: 3
|
||||||
|
Topic :: Software Development
|
||||||
|
Topic :: Software Development :: Libraries
|
||||||
|
Topic :: System :: Hardware
|
||||||
|
|
||||||
|
[options]
|
||||||
|
python_requires = >= 2.7
|
||||||
|
packages = ltp305
|
||||||
|
install_requires = smbus2
|
||||||
|
|
||||||
|
[flake8]
|
||||||
|
exclude =
|
||||||
|
.tox,
|
||||||
|
.eggs,
|
||||||
|
.git,
|
||||||
|
__pycache__,
|
||||||
|
build,
|
||||||
|
dist
|
||||||
|
ignore =
|
||||||
|
E501
|
||||||
|
|
||||||
|
[pimoroni]
|
||||||
|
py2deps =
|
||||||
|
py3deps =
|
||||||
|
configtxt =
|
||||||
|
commands =
|
||||||
33
vendor/ltp305-python/library/setup.py
vendored
Executable file
@@ -0,0 +1,33 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Copyright (c) 2016 Pimoroni
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is furnished to do
|
||||||
|
so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from setuptools import setup, __version__
|
||||||
|
from pkg_resources import parse_version
|
||||||
|
|
||||||
|
minimum_version = parse_version('30.4.0')
|
||||||
|
|
||||||
|
if parse_version(__version__) < minimum_version:
|
||||||
|
raise RuntimeError("Package setuptools must be at least version {}".format(minimum_version))
|
||||||
|
|
||||||
|
setup()
|
||||||
22
vendor/ltp305-python/library/tests/conftest.py
vendored
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import sys
|
||||||
|
import mock
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope='function', autouse=True)
|
||||||
|
def cleanup():
|
||||||
|
"""Force module reimport between tests."""
|
||||||
|
yield None
|
||||||
|
try:
|
||||||
|
del sys.modules['ltp305']
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope='function', autouse=False)
|
||||||
|
def smbus():
|
||||||
|
"""Mock smbus module."""
|
||||||
|
smbus = mock.MagicMock()
|
||||||
|
sys.modules['smbus'] = smbus
|
||||||
|
yield smbus
|
||||||
|
del sys.modules['smbus']
|
||||||
51
vendor/ltp305-python/library/tests/test_features.py
vendored
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import mock
|
||||||
|
|
||||||
|
|
||||||
|
def test_set_pixel(smbus):
|
||||||
|
from ltp305 import LTP305, CMD_MATRIX_L, CMD_MATRIX_R
|
||||||
|
display = LTP305()
|
||||||
|
display.set_pixel(0, 0, 1)
|
||||||
|
display.set_pixel(5, 0, 1)
|
||||||
|
display.show()
|
||||||
|
smbus.SMBus(1).write_i2c_block_data.assert_has_calls((
|
||||||
|
mock.call(display.address, CMD_MATRIX_L, [1, 0, 0, 0, 0, 0, 0, 0]),
|
||||||
|
mock.call(display.address, CMD_MATRIX_R, [1, 0, 0, 0, 0, 0, 0, 0])
|
||||||
|
))
|
||||||
|
smbus.reset_mock()
|
||||||
|
display.set_pixel(0, 0, 0)
|
||||||
|
display.set_pixel(5, 0, 0)
|
||||||
|
display.show()
|
||||||
|
smbus.SMBus(1).write_i2c_block_data.assert_has_calls((
|
||||||
|
mock.call(display.address, CMD_MATRIX_L, [0, 0, 0, 0, 0, 0, 0, 0]),
|
||||||
|
mock.call(display.address, CMD_MATRIX_R, [0, 0, 0, 0, 0, 0, 0, 0])
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
def test_set_decimal(smbus):
|
||||||
|
from ltp305 import LTP305, CMD_MATRIX_L, CMD_MATRIX_R
|
||||||
|
display = LTP305()
|
||||||
|
display.set_decimal(left=True, right=True)
|
||||||
|
display.show()
|
||||||
|
smbus.SMBus(1).write_i2c_block_data.assert_has_calls((
|
||||||
|
mock.call(display.address, CMD_MATRIX_L, [0, 0, 0, 0, 0, 0, 0, 0b01000000]),
|
||||||
|
mock.call(display.address, CMD_MATRIX_R, [0, 0, 0, 0, 0, 0, 0b10000000, 0])
|
||||||
|
))
|
||||||
|
smbus.reset_mock()
|
||||||
|
display.set_decimal(left=False, right=False)
|
||||||
|
display.show()
|
||||||
|
smbus.SMBus(1).write_i2c_block_data.assert_has_calls((
|
||||||
|
mock.call(display.address, CMD_MATRIX_L, [0, 0, 0, 0, 0, 0, 0, 0]),
|
||||||
|
mock.call(display.address, CMD_MATRIX_R, [0, 0, 0, 0, 0, 0, 0, 0])
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
def test_set_character(smbus):
|
||||||
|
from ltp305 import LTP305, CMD_MATRIX_L, CMD_MATRIX_R
|
||||||
|
display = LTP305()
|
||||||
|
display.set_character(0, "A")
|
||||||
|
display.set_character(5, "B")
|
||||||
|
display.show()
|
||||||
|
smbus.SMBus(1).write_i2c_block_data.assert_has_calls((
|
||||||
|
mock.call(display.address, CMD_MATRIX_L, [126, 17, 17, 17, 126, 0, 0, 0]),
|
||||||
|
mock.call(display.address, CMD_MATRIX_R, [15, 17, 17, 15, 17, 17, 15, 0])
|
||||||
|
))
|
||||||
5
vendor/ltp305-python/library/tests/test_setup.py
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
def test_setup(smbus):
|
||||||
|
import ltp305
|
||||||
|
display = ltp305.LTP305()
|
||||||
|
smbus.SMBus.assert_called_with(1)
|
||||||
|
del display
|
||||||
25
vendor/ltp305-python/library/tox.ini
vendored
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
[tox]
|
||||||
|
envlist = py{27,35,37,39},qa
|
||||||
|
skip_missing_interpreters = True
|
||||||
|
|
||||||
|
[testenv]
|
||||||
|
commands =
|
||||||
|
python setup.py install
|
||||||
|
coverage run -m py.test -v -r wsx
|
||||||
|
coverage report -m
|
||||||
|
deps =
|
||||||
|
mock
|
||||||
|
pytest>=3.1
|
||||||
|
pytest-cov
|
||||||
|
|
||||||
|
[testenv:qa]
|
||||||
|
commands =
|
||||||
|
check-manifest --ignore tox.ini,tests/*,.coveragerc
|
||||||
|
python setup.py sdist bdist_wheel
|
||||||
|
twine check dist/*
|
||||||
|
flake8 --ignore E501
|
||||||
|
deps =
|
||||||
|
check-manifest
|
||||||
|
flake8
|
||||||
|
twine
|
||||||
|
|
||||||
25
vendor/ltp305-python/uninstall.sh
vendored
Executable file
@@ -0,0 +1,25 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
LIBRARY_VERSION=`cat library/setup.cfg | grep version | awk -F" = " '{print $2}'`
|
||||||
|
LIBRARY_NAME=`cat library/setup.cfg | grep name | awk -F" = " '{print $2}'`
|
||||||
|
|
||||||
|
printf "$LIBRARY_NAME $LIBRARY_VERSION Python Library: Uninstaller\n\n"
|
||||||
|
|
||||||
|
if [ $(id -u) -ne 0 ]; then
|
||||||
|
printf "Script must be run as root. Try 'sudo ./uninstall.sh'\n"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd library
|
||||||
|
|
||||||
|
printf "Unnstalling for Python 2..\n"
|
||||||
|
pip uninstall $LIBRARY_NAME
|
||||||
|
|
||||||
|
if [ -f "/usr/bin/pip3" ]; then
|
||||||
|
printf "Uninstalling for Python 3..\n"
|
||||||
|
pip3 uninstall $LIBRARY_NAME
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
printf "Done!\n"
|
||||||