Compare commits

...

10 Commits

Author SHA1 Message Date
30697ec832 Badger 2040, tests Ferreol et Capucine 2022-06-22 23:32:57 +02:00
39f5e18e3d Update setup.md 2021-03-15 16:33:28 +00:00
a563aed66e Update phat-beat.md 2021-01-05 22:25:25 +01:00
f76f6aeeec Update setup.md 2020-12-31 15:01:55 +01:00
ca775f3773 docs 2020-12-31 14:24:33 +01:00
2f44cc9ff4 + docs
- installation phat beat / pirate radio
2020-12-06 17:14:44 +01:00
e24f749ce4 ajout docs 2020-12-05 13:03:22 +01:00
21dc360c96 Update pihole.md 2020-11-03 22:00:17 +01:00
d1bf15eb87 ajout doc pihole 2020-11-01 21:22:58 +01:00
320333cc16 ajout commentaires 2020-05-26 22:14:02 +02:00
50 changed files with 4359 additions and 38 deletions

View File

@ -1,3 +1,16 @@
# Raspberry pi
scripts et tuto raspberry pi.
scripts et tuto raspberry pi
## Docs
- [setup](/doc/setup.md)
- [pi-hole (bloqueur de pub)](/doc/pihole.md)
- [phat-beat](/doc/phat-beat.md)
## Commandes courantes
```bash
# eteindre
sudo shutdown -h now
# redémarrer
sudo shutdown -r now
```

BIN
badger2040/badge-image.bin Normal file

Binary file not shown.

6
badger2040/badge.txt Normal file
View File

@ -0,0 +1,6 @@
Fringe Division
Diane Ducastel
role
Special agent
universe
Blue

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,8 @@
Images must be 296x128 pixel with 1bit colour depth.
You can use examples/badger2040/image_converter/convert.py to convert them:
python3 convert.py --binary --resize image_file_1.png image_file_2.png image_file_3.png
Create a new "images" directory via Thonny, and upload the .bin files there.

1
badger2040/launcher.json Normal file
View File

@ -0,0 +1 @@
{"font_size": 1, "running": "badge", "page": 1, "inverted": false}

View File

@ -0,0 +1,136 @@
#!/usr/bin/env python3
"""
Converts images into a format suitable for display on Badger 2040.
Optionally resizes images to 296x128 to fit the display.
Crunches images down to dithered, 1bit colour depth.
Outputs either in raw binary format or as a .py file for embedding into MicroPython.
Output to py functionality is borrwed from data_to_py.py, Copyright (c) 2016 Peter Hinch
"""
import io
import argparse
from PIL import Image, ImageEnhance
from pathlib import Path
PY_HEADER = """# Code generated by convert.py.
"""
PY_FOOTER = """_mvdata = memoryview(_data)
def data():
return _mvdata
"""
parser = argparse.ArgumentParser(description='Converts images into the format used by Badger2040.')
parser.add_argument('file', nargs="+", help='input files to convert')
parser.add_argument('--out_dir', type=Path, default=None, help='output directory')
parser.add_argument('--binary', action="store_true", help='output binary file for MicroPython')
parser.add_argument('--py', action="store_true", help='output .py file for MicroPython embedding')
parser.add_argument('--resize', action="store_true", help='force images to 296x128 pixels')
options = parser.parse_args()
class ByteWriter(object):
bytes_per_line = 16
def __init__(self, stream, varname):
self.stream = stream
self.stream.write('{} =\\\n'.format(varname))
self.bytecount = 0 # For line breaks
def _eol(self):
self.stream.write("'\\\n")
def _eot(self):
self.stream.write("'\n")
def _bol(self):
self.stream.write("b'")
# Output a single byte
def obyte(self, data):
if not self.bytecount:
self._bol()
self.stream.write('\\x{:02x}'.format(data))
self.bytecount += 1
self.bytecount %= self.bytes_per_line
if not self.bytecount:
self._eol()
# Output from a sequence
def odata(self, bytelist):
for byt in bytelist:
self.obyte(byt)
# ensure a correct final line
def eot(self): # User force EOL if one hasn't occurred
if self.bytecount:
self._eot()
self.stream.write('\n')
def convert_image(img):
if options.resize:
img = img.resize((296, 128)) # resize
try:
enhancer = ImageEnhance.Contrast(img)
img = enhancer.enhance(2.0)
except ValueError:
pass
img = img.convert("1") # convert to black and white
return img
def write_stream(header, footer, ip_stream, op_stream):
op_stream.write(header)
op_stream.write('\n')
data = ip_stream.read()
bw_data = ByteWriter(op_stream, '_data')
bw_data.odata(data)
bw_data.eot()
op_stream.write(footer)
# create map of images based on input filenames
for input_filename in options.file:
with Image.open(input_filename) as img:
img = convert_image(img)
image_name = Path(input_filename).stem
w, h = img.size
output_data = [~b & 0xff for b in list(img.tobytes())]
if options.binary:
if options.out_dir is not None:
output_filename = (options.out_dir / image_name).with_suffix(".bin")
else:
output_filename = Path(input_filename).with_suffix(".bin")
print(f"Saving to {output_filename}, {w}x{h}")
with open(output_filename, "wb") as out:
out.write(bytearray(output_data))
elif options.py:
if options.out_dir is not None:
output_filename = (options.out_dir / image_name).with_suffix(".py")
else:
output_filename = Path(input_filename).with_suffix(".py")
print(f"Saving to {output_filename}, {w}x{h}")
with open(output_filename, "w") as out:
write_stream(PY_HEADER, PY_FOOTER, io.BytesIO(bytes(output_data)), out)
else:
image_code = '''\
static const uint8_t {image_name}[{count}] = {{
{byte_data}
}};
'''.format(image_name=image_name, count=len(output_data), byte_data=", ".join(str(b) for b in output_data))
print(image_code)

View File

@ -0,0 +1,154 @@
#! /usr/bin/python3
# -*- coding: utf-8 -*-
# The MIT License (MIT)
#
# Copyright (c) 2016 Peter Hinch
#
# 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.
import argparse
import sys
import os
# UTILITIES FOR WRITING PYTHON SOURCECODE TO A FILE
# ByteWriter takes as input a variable name and data values and writes
# Python source to an output stream of the form
# my_variable = b'\x01\x02\x03\x04\x05\x06\x07\x08'\
# Lines are broken with \ for readability.
class ByteWriter(object):
bytes_per_line = 16
def __init__(self, stream, varname):
self.stream = stream
self.stream.write('{} =\\\n'.format(varname))
self.bytecount = 0 # For line breaks
def _eol(self):
self.stream.write("'\\\n")
def _eot(self):
self.stream.write("'\n")
def _bol(self):
self.stream.write("b'")
# Output a single byte
def obyte(self, data):
if not self.bytecount:
self._bol()
self.stream.write('\\x{:02x}'.format(data))
self.bytecount += 1
self.bytecount %= self.bytes_per_line
if not self.bytecount:
self._eol()
# Output from a sequence
def odata(self, bytelist):
for byt in bytelist:
self.obyte(byt)
# ensure a correct final line
def eot(self): # User force EOL if one hasn't occurred
if self.bytecount:
self._eot()
self.stream.write('\n')
# PYTHON FILE WRITING
STR01 = """# Code generated by data_to_py.py.
version = '0.1'
"""
STR02 = """_mvdata = memoryview(_data)
def data():
return _mvdata
"""
def write_func(stream, name, arg):
stream.write('def {}():\n return {}\n\n'.format(name, arg))
def write_data(op_path, ip_path):
try:
with open(ip_path, 'rb') as ip_stream:
try:
with open(op_path, 'w') as op_stream:
write_stream(ip_stream, op_stream)
except OSError:
print("Can't open", op_path, 'for writing')
return False
except OSError:
print("Can't open", ip_path)
return False
return True
def write_stream(ip_stream, op_stream):
op_stream.write(STR01)
op_stream.write('\n')
data = ip_stream.read()
bw_data = ByteWriter(op_stream, '_data')
bw_data.odata(data)
bw_data.eot()
op_stream.write(STR02)
# PARSE COMMAND LINE ARGUMENTS
def quit(msg):
print(msg)
sys.exit(1)
DESC = """data_to_py.py
Utility to convert an arbitrary binary file to Python source.
Sample usage:
data_to_py.py image.jpg image.py
"""
if __name__ == "__main__":
parser = argparse.ArgumentParser(__file__, description=DESC,
formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument('infile', type=str, help='Input file path')
parser.add_argument('outfile', type=str,
help='Path and name of output file. Must have .py extension.')
args = parser.parse_args()
if not os.path.isfile(args.infile):
quit("Data filename does not exist")
if not os.path.splitext(args.outfile)[1].upper() == '.PY':
quit('Output filename must have a .py extension.')
print('Writing Python file.')
if not write_data(args.outfile, args.infile):
sys.exit(1)
print(args.outfile, 'written successfully.')

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

View File

@ -0,0 +1,92 @@
# Badger 2040 Examples <!-- omit in toc -->
- [Function Examples](#function-examples)
- [Battery](#battery)
- [Button Test](#button-test)
- [LED](#led)
- [Pin interrupt](#pin-interrupt)
- [Application Examples](#application-examples)
- [Badge](#badge)
- [Checklist](#checklist)
- [Clock](#clock)
- [E-Book](#e-book)
- [Fonts](#fonts)
- [Image](#image)
- [QR gen](#qr-gen)
- [Launcher](#launcher)
## Function Examples
### Battery
[battery.py](battery.py)
An example of how to read the battery voltage and display a battery level indicator.
### Button Test
[button_test.py](button_test.py)
An example of how to read Badger2040's buttons and display a unique message for each.
### LED
[led.py](led.py)
Blinks Badger's LED on and off.
### Pin interrupt
[pin_interrupt.py](pin_interrupt.py)
An example of drawing text and graphics and using the buttons.
## Application Examples
### Badge
[badge.py](badge.py)
Create your own name badge! This application looks for two files on your MicroPython drive:
* `badge.txt` - A text file containing 6 lines, corresponding to the 6 different pieces of text on the badge
* `badge-image.bin` - A 108x128px 1-bit colour depth image to display alongside the text. You can use `examples/badger2040/image_converter/convert.py` to convert them:
```shell
python3 convert.py --binary --resize image_file_1.png image_file_2.png image_file_3.png
```
### Checklist
[list.py](list.py)
A checklist application, letting you navigate through items and tick each of them off.
### Clock
[clock.py](clock.py)
A simple clock showing the time and date, that uses the E Ink's fast speed to update every second
### E-Book
[ebook.py](ebook.py)
A mini text file e-reader. Comes pre-loaded with an excerpt of The Wind In the Willows.
### Fonts
[fonts.py](fonts.py)
A demonstration of the various fonts that can be used in your programs.
### Image
[image.py](image.py)
An image gallery. Displays and lets you cycle through any images stored within the MicroPython device's `/images` directory. Images must be 296x128 pixels with 1-bit colour depth. You can use `examples/badger2040/image_converter/convert.py` to convert them:
```shell
python3 convert.py --binary --resize image_file_1.png image_file_2.png image_file_3.png
```
### QR gen
[qrgen.py](qrgen.py)
This application looks for a file on your MicroPython drive:
- `qrcode.txt` - A text file containing 9 lines. The first line should be a URL which will be converted into and displayed as a QR code. Up to 8 more lines of information can be added, which will be shown as plain text to the right of the QR code.
### Launcher
[launcher.py](launcher.py)
A launcher-style application, that provide a menu of other applications that can be loaded, as well as information such as battery level.

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

View File

@ -0,0 +1,6 @@
try:
open("main.py", "r")
except OSError:
with open("main.py", "w") as f:
f.write("import _launcher")
f.flush()

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

View File

@ -0,0 +1,267 @@
import badger2040
import machine
import time
# Global Constants
WIDTH = badger2040.WIDTH
HEIGHT = badger2040.HEIGHT
IMAGE_WIDTH = 104
COMPANY_HEIGHT = 30
DETAILS_HEIGHT = 20
NAME_HEIGHT = HEIGHT - COMPANY_HEIGHT - (DETAILS_HEIGHT * 2) - 2
TEXT_WIDTH = WIDTH - IMAGE_WIDTH - 1
COMPANY_TEXT_SIZE = 0.6
DETAILS_TEXT_SIZE = 0.5
LEFT_PADDING = 5
NAME_PADDING = 20
DETAIL_SPACING = 10
OVERLAY_BORDER = 40
OVERLAY_SPACING = 20
OVERLAY_TEXT_SIZE = 0.6
DEFAULT_TEXT = """mustelid inc
H. Badger
RP2040
2MB Flash
E ink
296x128px"""
BADGE_IMAGE = bytearray(int(IMAGE_WIDTH * HEIGHT / 8))
try:
open("badge-image.bin", "rb").readinto(BADGE_IMAGE)
except OSError:
try:
import badge_image
BADGE_IMAGE = bytearray(badge_image.data())
del badge_image
except ImportError:
pass
# ------------------------------
# Utility functions
# ------------------------------
# Reduce the size of a string until it fits within a given width
def truncatestring(text, text_size, width):
while True:
length = display.measure_text(text, text_size)
if length > 0 and length > width:
text = text[:-1]
else:
text += ""
return text
# ------------------------------
# Drawing functions
# ------------------------------
# Draw an overlay box with a given message within it
def draw_overlay(message, width, height, line_spacing, text_size):
# Draw a light grey background
display.pen(12)
display.rectangle((WIDTH - width) // 2, (HEIGHT - height) // 2, width, height)
# Take the provided message and split it up into
# lines that fit within the specified width
words = message.split(" ")
lines = []
line = ""
appended_line = ""
for word in words:
if len(word) > 0:
appended_line += " "
appended_line += word
if display.measure_text(appended_line, text_size) >= width:
lines.append(line)
appended_line = word
else:
line = appended_line
if len(line) != 0:
lines.append(line)
display.pen(0)
display.thickness(2)
# Display each line of text from the message, centre-aligned
num_lines = len(lines)
for i in range(num_lines):
length = display.measure_text(lines[i], text_size)
current_line = (i * line_spacing) - ((num_lines - 1) * line_spacing) // 2
display.text(lines[i], (WIDTH - length) // 2, (HEIGHT // 2) + current_line, text_size)
# Draw the badge, including user text
def draw_badge():
display.pen(0)
display.clear()
# Draw badge image
display.image(BADGE_IMAGE, IMAGE_WIDTH, HEIGHT, WIDTH - IMAGE_WIDTH, 0)
# Draw a border around the image
display.pen(0)
display.thickness(1)
display.line(WIDTH - IMAGE_WIDTH, 0, WIDTH - 1, 0)
display.line(WIDTH - IMAGE_WIDTH, 0, WIDTH - IMAGE_WIDTH, HEIGHT - 1)
display.line(WIDTH - IMAGE_WIDTH, HEIGHT - 1, WIDTH - 1, HEIGHT - 1)
display.line(WIDTH - 1, 0, WIDTH - 1, HEIGHT - 1)
# Uncomment this if a white background is wanted behind the company
# display.pen(15)
# display.rectangle(1, 1, TEXT_WIDTH, COMPANY_HEIGHT - 1)
# Draw the company
display.pen(15) # Change this to 0 if a white background is used
display.font("serif")
display.thickness(3)
display.text(company, LEFT_PADDING, (COMPANY_HEIGHT // 2) + 1, COMPANY_TEXT_SIZE)
# Draw a white background behind the name
display.pen(15)
display.thickness(1)
display.rectangle(1, COMPANY_HEIGHT + 1, TEXT_WIDTH, NAME_HEIGHT)
# Draw the name, scaling it based on the available width
display.pen(0)
display.font("sans")
display.thickness(4)
name_size = 2.0 # A sensible starting scale
while True:
name_length = display.measure_text(name, name_size)
if name_length >= (TEXT_WIDTH - NAME_PADDING) and name_size >= 0.1:
name_size -= 0.01
else:
display.text(name, (TEXT_WIDTH - name_length) // 2, (NAME_HEIGHT // 2) + COMPANY_HEIGHT + 1, name_size)
break
# Draw a white backgrounds behind the details
display.pen(15)
display.thickness(1)
display.rectangle(1, HEIGHT - DETAILS_HEIGHT * 2, TEXT_WIDTH, DETAILS_HEIGHT - 1)
display.rectangle(1, HEIGHT - DETAILS_HEIGHT, TEXT_WIDTH, DETAILS_HEIGHT - 1)
# Draw the first detail's title and text
display.pen(0)
display.font("sans")
display.thickness(3)
name_length = display.measure_text(detail1_title, DETAILS_TEXT_SIZE)
display.text(detail1_title, LEFT_PADDING, HEIGHT - ((DETAILS_HEIGHT * 3) // 2), DETAILS_TEXT_SIZE)
display.thickness(2)
display.text(detail1_text, 5 + name_length + DETAIL_SPACING, HEIGHT - ((DETAILS_HEIGHT * 3) // 2), DETAILS_TEXT_SIZE)
# Draw the second detail's title and text
display.thickness(3)
name_length = display.measure_text(detail2_title, DETAILS_TEXT_SIZE)
display.text(detail2_title, LEFT_PADDING, HEIGHT - (DETAILS_HEIGHT // 2), DETAILS_TEXT_SIZE)
display.thickness(2)
display.text(detail2_text, LEFT_PADDING + name_length + DETAIL_SPACING, HEIGHT - (DETAILS_HEIGHT // 2), DETAILS_TEXT_SIZE)
# ------------------------------
# Program setup
# ------------------------------
# Global variables
show_overlay = False
# Create a new Badger and set it to update NORMAL
display = badger2040.Badger2040()
display.update_speed(badger2040.UPDATE_NORMAL)
# Open the badge file
try:
badge = open("badge.txt", "r")
except OSError:
badge = open("badge.txt", "w")
badge.write(DEFAULT_TEXT)
badge.flush()
badge.seek(0)
# Read in the next 6 lines
company = badge.readline() # "mustelid inc"
name = badge.readline() # "H. Badger"
detail1_title = badge.readline() # "RP2040"
detail1_text = badge.readline() # "2MB Flash"
detail2_title = badge.readline() # "E ink"
detail2_text = badge.readline() # "296x128px"
# Truncate all of the text (except for the name as that is scaled)
company = truncatestring(company, COMPANY_TEXT_SIZE, TEXT_WIDTH)
detail1_title = truncatestring(detail1_title, DETAILS_TEXT_SIZE, TEXT_WIDTH)
detail1_text = truncatestring(detail1_text, DETAILS_TEXT_SIZE,
TEXT_WIDTH - DETAIL_SPACING - display.measure_text(detail1_title, DETAILS_TEXT_SIZE))
detail2_title = truncatestring(detail2_title, DETAILS_TEXT_SIZE, TEXT_WIDTH)
detail2_text = truncatestring(detail2_text, DETAILS_TEXT_SIZE,
TEXT_WIDTH - DETAIL_SPACING - display.measure_text(detail2_title, DETAILS_TEXT_SIZE))
# Set up the buttons
button_a = machine.Pin(badger2040.BUTTON_A, machine.Pin.IN, machine.Pin.PULL_DOWN)
button_b = machine.Pin(badger2040.BUTTON_B, machine.Pin.IN, machine.Pin.PULL_DOWN)
button_c = machine.Pin(badger2040.BUTTON_C, machine.Pin.IN, machine.Pin.PULL_DOWN)
button_up = machine.Pin(badger2040.BUTTON_UP, machine.Pin.IN, machine.Pin.PULL_DOWN)
button_down = machine.Pin(badger2040.BUTTON_DOWN, machine.Pin.IN, machine.Pin.PULL_DOWN)
# Button handling function
def button(pin):
global show_overlay
if pin == button_a:
show_overlay = True
return
if pin == button_b:
show_overlay = True
return
if pin == button_c:
show_overlay = True
return
if pin == button_up:
show_overlay = True
return
if pin == button_down:
show_overlay = True
return
# Register the button handling function with the buttons
button_a.irq(trigger=machine.Pin.IRQ_RISING, handler=button)
button_b.irq(trigger=machine.Pin.IRQ_RISING, handler=button)
button_c.irq(trigger=machine.Pin.IRQ_RISING, handler=button)
button_up.irq(trigger=machine.Pin.IRQ_RISING, handler=button)
button_down.irq(trigger=machine.Pin.IRQ_RISING, handler=button)
# ------------------------------
# Main program loop
# ------------------------------
draw_badge()
display.update()
while True:
if show_overlay:
draw_overlay("To change the text, connect Badger2040 to a PC, load up Thonny, and modify badge.txt",
WIDTH - OVERLAY_BORDER, HEIGHT - OVERLAY_BORDER, OVERLAY_SPACING, OVERLAY_TEXT_SIZE)
display.update()
time.sleep(4)
draw_badge()
display.update()
show_overlay = False
time.sleep(0.1)

View File

@ -0,0 +1,163 @@
import badger2040
from machine import Pin, ADC
import time
# Global Constants
# for e.g. 2xAAA batteries, try max 3.4 min 3.0
MAX_BATTERY_VOLTAGE = 4.0
MIN_BATTERY_VOLTAGE = 3.2
WIDTH = badger2040.WIDTH
HEIGHT = badger2040.HEIGHT
BATT_WIDTH = 200
BATT_HEIGHT = 100
BATT_BORDER = 10
BATT_TERM_WIDTH = 20
BATT_TERM_HEIGHT = 50
BATT_BAR_PADDING = 10
BATT_BAR_HEIGHT = BATT_HEIGHT - (BATT_BORDER * 2) - (BATT_BAR_PADDING * 2)
BATT_BAR_START = ((WIDTH - BATT_WIDTH) // 2) + BATT_BORDER + BATT_BAR_PADDING
BATT_BAR_END = ((WIDTH + BATT_WIDTH) // 2) - BATT_BORDER - BATT_BAR_PADDING
NUM_BATT_BARS = 4
# ------------------------------
# Utility functions
# ------------------------------
def map_value(input, in_min, in_max, out_min, out_max):
return (((input - in_min) * (out_max - out_min)) / (in_max - in_min)) + out_min
# ------------------------------
# Drawing functions
# ------------------------------
# Draw the frame of the reader
def draw_battery(level, resolution):
display.pen(15)
display.clear()
display.thickness(1)
# Draw the battery outline
display.pen(0)
display.rectangle(
(WIDTH - BATT_WIDTH) // 2, (HEIGHT - BATT_HEIGHT) // 2, BATT_WIDTH, BATT_HEIGHT
)
display.rectangle(
(WIDTH + BATT_WIDTH) // 2,
(HEIGHT - BATT_TERM_HEIGHT) // 2,
BATT_TERM_WIDTH,
BATT_TERM_HEIGHT,
)
display.pen(15)
display.rectangle(
(WIDTH - BATT_WIDTH) // 2 + BATT_BORDER,
(HEIGHT - BATT_HEIGHT) // 2 + BATT_BORDER,
BATT_WIDTH - BATT_BORDER * 2,
BATT_HEIGHT - BATT_BORDER * 2,
)
# Add a special check for no battery
if level < 1:
X = WIDTH // 2
Y = HEIGHT // 2
display.pen(0)
display.thickness(1)
thickness = (BATT_BORDER * 3) // 2
start_extra = thickness // 3
end_extra = (thickness * 2) // 3
for i in range(0, thickness):
excess = i // 2
display.line(
X - (BATT_HEIGHT // 2) + i - excess - start_extra,
Y - (BATT_HEIGHT // 2) - excess - start_extra,
X + (BATT_HEIGHT // 2) + i - excess + end_extra,
Y + (BATT_HEIGHT // 2) - excess + end_extra,
)
display.pen(15)
for i in range(0 - thickness, 0):
display.line(
X - (BATT_HEIGHT // 2) + i,
Y - (BATT_HEIGHT // 2),
X + (BATT_HEIGHT // 2) + i,
Y + (BATT_HEIGHT // 2),
)
else:
# Draw the battery bars
display.pen(0)
length = (
BATT_BAR_END - BATT_BAR_START - ((NUM_BATT_BARS - 1) * BATT_BAR_PADDING)
) // NUM_BATT_BARS
current_level = 0.0
normalised_level = level / resolution
for i in range(NUM_BATT_BARS):
current_level = (1.0 * i) / NUM_BATT_BARS
if normalised_level > current_level:
pos = i * (length + BATT_BAR_PADDING)
display.rectangle(
BATT_BAR_START + pos,
(HEIGHT - BATT_BAR_HEIGHT) // 2,
length,
BATT_BAR_HEIGHT,
)
display.update()
# ------------------------------
# Program setup
# ------------------------------
# Create a new Badger and set it to update FAST
display = badger2040.Badger2040()
display.update_speed(badger2040.UPDATE_FAST)
# Set up the ADCs for measuring battery voltage
vbat_adc = ADC(badger2040.PIN_BATTERY)
vref_adc = ADC(badger2040.PIN_1V2_REF)
vref_en = Pin(badger2040.PIN_VREF_POWER)
vref_en.init(Pin.OUT)
vref_en.value(0)
# ------------------------------
# Main program loop
# ------------------------------
last_level = -1
while True:
# Enable the onboard voltage reference
vref_en.value(1)
# Calculate the logic supply voltage, as will be lower that the usual 3.3V when running off low batteries
vdd = 1.24 * (65535 / vref_adc.read_u16())
vbat = (
(vbat_adc.read_u16() / 65535) * 3 * vdd
) # 3 in this is a gain, not rounding of 3.3V
# Disable the onboard voltage reference
vref_en.value(0)
# Print out the voltage
print("Battery Voltage = ", vbat, "V", sep="")
# Convert the voltage to a level to display onscreen
level = int(
map_value(vbat, MIN_BATTERY_VOLTAGE, MAX_BATTERY_VOLTAGE, 0, NUM_BATT_BARS)
)
# Only draw if the battery level has changed significantly
if level != last_level:
draw_battery(level, NUM_BATT_BARS)
last_level = level
time.sleep(1)

View File

@ -0,0 +1,67 @@
import badger2040
import machine
import time
display = badger2040.Badger2040()
display.update_speed(badger2040.UPDATE_TURBO)
display.pen(15)
display.clear()
display.update()
button_a = machine.Pin(badger2040.BUTTON_A, machine.Pin.IN, machine.Pin.PULL_DOWN)
button_b = machine.Pin(badger2040.BUTTON_B, machine.Pin.IN, machine.Pin.PULL_DOWN)
button_c = machine.Pin(badger2040.BUTTON_C, machine.Pin.IN, machine.Pin.PULL_DOWN)
button_up = machine.Pin(badger2040.BUTTON_UP, machine.Pin.IN, machine.Pin.PULL_DOWN)
button_down = machine.Pin(badger2040.BUTTON_DOWN, machine.Pin.IN, machine.Pin.PULL_DOWN)
# the User button (boot/usr on back of board) is inverted from the others
button_user = machine.Pin(badger2040.BUTTON_USER, machine.Pin.IN, machine.Pin.PULL_UP)
message = None
message_y = 60
def button(pin):
global message
if message is not None:
return
if pin == button_a:
message = "Button a"
return
if pin == button_b:
message = "Button b"
return
if pin == button_c:
message = "Button c"
return
if pin == button_up:
message = "Button Up"
return
if pin == button_down:
message = "Button Down"
return
if pin == button_user:
message = "Button Usr"
return
button_a.irq(trigger=machine.Pin.IRQ_RISING, handler=button)
button_b.irq(trigger=machine.Pin.IRQ_RISING, handler=button)
button_c.irq(trigger=machine.Pin.IRQ_RISING, handler=button)
button_up.irq(trigger=machine.Pin.IRQ_RISING, handler=button)
button_down.irq(trigger=machine.Pin.IRQ_RISING, handler=button)
button_user.irq(trigger=machine.Pin.IRQ_RISING, handler=button)
while True:
if message is not None:
display.pen(15)
display.clear()
display.pen(0)
display.thickness(4)
display.text(message, 6, message_y, 1.4)
for _ in range(2):
display.update()
message = None
time.sleep(0.1)

View File

@ -0,0 +1,148 @@
import time
import machine
import badger2040
rtc = machine.RTC()
screen = badger2040.Badger2040()
screen.update_speed(badger2040.UPDATE_TURBO)
screen.font("gothic")
cursors = ["year", "month", "day", "hour", "minute"]
set_clock = False
cursor = 0
last = 0
# Set up the buttons
button_down = machine.Pin(badger2040.BUTTON_DOWN, machine.Pin.IN, machine.Pin.PULL_DOWN)
button_up = machine.Pin(badger2040.BUTTON_UP, machine.Pin.IN, machine.Pin.PULL_DOWN)
button_a = machine.Pin(badger2040.BUTTON_A, machine.Pin.IN, machine.Pin.PULL_DOWN)
button_b = machine.Pin(badger2040.BUTTON_B, machine.Pin.IN, machine.Pin.PULL_DOWN)
button_c = machine.Pin(badger2040.BUTTON_C, machine.Pin.IN, machine.Pin.PULL_DOWN)
def days_in_month(month, year):
if month == 2 and ((year % 4 == 0 and year % 100 != 0) or year % 400 == 0):
return 29
return (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)[month - 1]
# Button handling function
def button(pin):
global last, set_clock, cursor, year, month, day, hour, minute
time.sleep(0.05)
if not pin.value():
return
adjust = 0
changed = False
if pin == button_b:
set_clock = not set_clock
changed = True
if not set_clock:
rtc.datetime((year, month, day, 0, hour, minute, second, 0))
if set_clock:
if pin == button_c:
cursor += 1
cursor %= len(cursors)
if pin == button_a:
cursor -= 1
cursor %= len(cursors)
if pin == button_up:
adjust = 1
if pin == button_down:
adjust = -1
if cursors[cursor] == "year":
year += adjust
year = max(year, 2022)
day = min(day, days_in_month(month, year))
if cursors[cursor] == "month":
month += adjust
month = min(max(month, 1), 12)
day = min(day, days_in_month(month, year))
if cursors[cursor] == "day":
day += adjust
day = min(max(day, 1), days_in_month(month, year))
if cursors[cursor] == "hour":
hour += adjust
hour %= 24
if cursors[cursor] == "minute":
minute += adjust
minute %= 60
if set_clock or changed:
draw_clock()
# Register the button handling function with the buttons
button_down.irq(trigger=machine.Pin.IRQ_RISING, handler=button)
button_up.irq(trigger=machine.Pin.IRQ_RISING, handler=button)
button_a.irq(trigger=machine.Pin.IRQ_RISING, handler=button)
button_b.irq(trigger=machine.Pin.IRQ_RISING, handler=button)
button_c.irq(trigger=machine.Pin.IRQ_RISING, handler=button)
def draw_clock():
hms = "{:02}:{:02}:{:02}".format(hour, minute, second)
ymd = "{:04}/{:02}/{:02}".format(year, month, day)
hms_width = screen.measure_text(hms, 1.8)
hms_offset = int((badger2040.WIDTH / 2) - (hms_width / 2))
h_width = screen.measure_text(hms[0:2], 1.8)
mi_width = screen.measure_text(hms[3:5], 1.8)
mi_offset = screen.measure_text(hms[0:3], 1.8)
ymd_width = screen.measure_text(ymd, 1.0)
ymd_offset = int((badger2040.WIDTH / 2) - (ymd_width / 2))
y_width = screen.measure_text(ymd[0:4], 1.0)
m_width = screen.measure_text(ymd[5:7], 1.0)
m_offset = screen.measure_text(ymd[0:5], 1.0)
d_width = screen.measure_text(ymd[8:10], 1.0)
d_offset = screen.measure_text(ymd[0:8], 1.0)
screen.pen(15)
screen.clear()
screen.pen(0)
screen.thickness(5)
screen.text(hms, hms_offset, 40, 1.8)
screen.thickness(3)
screen.text(ymd, ymd_offset, 100, 1.0)
if set_clock:
if cursors[cursor] == "year":
screen.line(ymd_offset, 120, ymd_offset + y_width, 120)
if cursors[cursor] == "month":
screen.line(ymd_offset + m_offset, 120, ymd_offset + m_offset + m_width, 120)
if cursors[cursor] == "day":
screen.line(ymd_offset + d_offset, 120, ymd_offset + d_offset + d_width, 120)
if cursors[cursor] == "hour":
screen.line(hms_offset, 70, hms_offset + h_width, 70)
if cursors[cursor] == "minute":
screen.line(hms_offset + mi_offset, 70, hms_offset + mi_offset + mi_width, 70)
screen.update()
year, month, day, wd, hour, minute, second, _ = rtc.datetime()
if (year, month, day) == (2021, 1, 1):
rtc.datetime((2022, 2, 28, 0, 12, 0, 0, 0))
last_second = second
while True:
if not set_clock:
year, month, day, wd, hour, minute, second, _ = rtc.datetime()
if second != last_second:
draw_clock()
last_second = second
time.sleep(0.1)

View File

@ -0,0 +1,211 @@
import math
import time
from random import random
import machine
import badger2040
# ------------------------------
# Program setup
# ------------------------------
# Global constants
CELL_SIZE = 6 # Size of cell in pixels
INITIAL_DENSITY = 0.3 # Density of cells at start
# Create a new Badger and set it to update TURBO
screen = badger2040.Badger2040()
screen.update_speed(badger2040.UPDATE_TURBO)
restart = False # should sim be restarted
# ------------------------------
# Button functions
# ------------------------------
# Button handling function
def button(pin):
global restart
# if 'a' button is pressed, restart the sim
if pin == button_a:
restart = True
# Set up button
button_a = machine.Pin(badger2040.BUTTON_A, machine.Pin.IN, machine.Pin.PULL_DOWN)
button_a.irq(trigger=machine.Pin.IRQ_RISING, handler=button)
# ------------------------------
# Screen functions
# ------------------------------
# Remove everything from the screen
def init_screen():
screen.update_speed(badger2040.UPDATE_NORMAL)
screen.pen(15)
screen.clear()
screen.update()
screen.update_speed(badger2040.UPDATE_TURBO)
# ------------------------------
# Classes
# ------------------------------
# Define a 'cell'
class Cell:
def __init__(self):
self._alive = False
def make_alive(self):
self._alive = True
def make_dead(self):
self._alive = False
def is_alive(self):
return self._alive
# Define the whole board
class Board:
def __init__(self):
self._rows = math.floor(badger2040.WIDTH / CELL_SIZE)
self._columns = math.floor(badger2040.HEIGHT / CELL_SIZE)
self._grid = [[Cell() for _ in range(self._columns)] for _ in range(self._rows)]
self._initialise_board()
# Draw the board to the screen
def draw_board(self):
row_idx = 0
column_idx = 0
for row in self._grid:
column_idx = 0
for cell in row:
if cell.is_alive():
screen.pen(0)
else:
screen.pen(15)
screen.rectangle(
row_idx * CELL_SIZE, column_idx * CELL_SIZE, CELL_SIZE, CELL_SIZE
)
column_idx += 1
row_idx += 1
screen.update()
# Generate the first iteration of the board
def _initialise_board(self):
for row in self._grid:
for cell in row:
if random() <= INITIAL_DENSITY:
cell.make_alive()
# Get the neighbour cells for a given cell
def get_neighbours(self, current_row, current_column):
# Cells either side of current cell
neighbour_min = -1
neighbour_max = 2
neighbours = []
for row in range(neighbour_min, neighbour_max):
for column in range(neighbour_min, neighbour_max):
neighbour_row = current_row + row
neighbour_column = current_column + column
# Don't count the current cell
if not (
neighbour_row == current_row and neighbour_column == current_column
):
# It's a toroidal world so go all the way round if necessary
if (neighbour_row) < 0:
neighbour_row = self._rows - 1
elif (neighbour_row) >= self._rows:
neighbour_row = 0
if (neighbour_column) < 0:
neighbour_column = self._columns - 1
elif (neighbour_column) >= self._columns:
neighbour_column = 0
neighbours.append(self._grid[neighbour_row][neighbour_column])
return neighbours
# Calculate the next generation
def create_next_generation(self):
to_alive = []
to_dead = []
changed = False
for row in range(len(self._grid)):
for column in range(len(self._grid[row])):
# Get all the neighours that are alive
alive_neighbours = []
for neighbour_cell in self.get_neighbours(row, column):
if neighbour_cell.is_alive():
alive_neighbours.append(neighbour_cell)
current_cell = self._grid[row][column]
# Apply the Conway GoL rules (B3/S23)
if current_cell.is_alive():
if len(alive_neighbours) < 2 or len(alive_neighbours) > 3:
to_dead.append(current_cell)
if len(alive_neighbours) == 3 or len(alive_neighbours) == 2:
to_alive.append(current_cell)
else:
if len(alive_neighbours) == 3:
to_alive.append(current_cell)
for cell in to_alive:
if not cell.is_alive():
# The board has changed since the previous generation
changed = True
cell.make_alive()
for cell in to_dead:
if cell.is_alive():
# The board has changed since the previous generation
changed = True
cell.make_dead()
return changed
# ------------------------------
# Main program loop
# ------------------------------
def main():
global restart
init_screen()
board = Board()
board.draw_board()
time.sleep(0.5)
while True:
# The 'a' button has been pressed so restart sim
if restart:
init_screen()
restart = False
board = Board()
board.draw_board()
time.sleep(0.5)
# The board didn't update since the previous generation
if not board.create_next_generation():
screen.update_speed(badger2040.UPDATE_NORMAL)
board.draw_board()
screen.update_speed(badger2040.UPDATE_TURBO)
time.sleep(5)
restart = True
# Draw the next generation
else:
board.draw_board()
main()

View File

@ -0,0 +1,276 @@
import badger2040
import machine
import time
import gc
# **** Put the name of your text file here *****
text_file = "book.txt" # File must be on the MicroPython device
try:
open(text_file, "r")
except OSError:
try:
# If the specified file doesn't exist,
# pre-populate with Wind In The Willows
import witw
open(text_file, "wb").write(witw.data())
del witw
except ImportError:
pass
gc.collect()
# Global Constants
WIDTH = badger2040.WIDTH
HEIGHT = badger2040.HEIGHT
ARROW_THICKNESS = 3
ARROW_WIDTH = 18
ARROW_HEIGHT = 14
ARROW_PADDING = 2
TEXT_PADDING = 4
TEXT_SIZE = 0.5
TEXT_SPACING = int(34 * TEXT_SIZE)
TEXT_WIDTH = WIDTH - TEXT_PADDING - TEXT_PADDING - ARROW_WIDTH
FONTS = ["sans", "gothic", "cursive", "serif"]
FONT_THICKNESSES = [2, 1, 1, 2]
# ------------------------------
# Drawing functions
# ------------------------------
# Draw a upward arrow
def draw_up(x, y, width, height, thickness, padding):
border = (thickness // 4) + padding
display.line(x + border, y + height - border,
x + (width // 2), y + border)
display.line(x + (width // 2), y + border,
x + width - border, y + height - border)
# Draw a downward arrow
def draw_down(x, y, width, height, thickness, padding):
border = (thickness // 2) + padding
display.line(x + border, y + border,
x + (width // 2), y + height - border)
display.line(x + (width // 2), y + height - border,
x + width - border, y + border)
# Draw the frame of the reader
def draw_frame():
display.pen(15)
display.clear()
display.pen(12)
display.rectangle(WIDTH - ARROW_WIDTH, 0, ARROW_WIDTH, HEIGHT)
display.pen(0)
display.thickness(ARROW_THICKNESS)
if current_page > 1:
draw_up(WIDTH - ARROW_WIDTH, (HEIGHT // 4) - (ARROW_HEIGHT // 2),
ARROW_WIDTH, ARROW_HEIGHT, ARROW_THICKNESS, ARROW_PADDING)
draw_down(WIDTH - ARROW_WIDTH, ((HEIGHT * 3) // 4) - (ARROW_HEIGHT // 2),
ARROW_WIDTH, ARROW_HEIGHT, ARROW_THICKNESS, ARROW_PADDING)
# ------------------------------
# Program setup
# ------------------------------
# Global variables
next_page = True
prev_page = False
change_font_size = False
change_font = False
last_offset = 0
current_page = 0
# Create a new Badger and set it to update FAST
display = badger2040.Badger2040()
display.update_speed(badger2040.UPDATE_FAST)
# Set up the buttons
button_down = machine.Pin(badger2040.BUTTON_DOWN, machine.Pin.IN, machine.Pin.PULL_DOWN)
button_up = machine.Pin(badger2040.BUTTON_UP, machine.Pin.IN, machine.Pin.PULL_DOWN)
button_a = machine.Pin(badger2040.BUTTON_A, machine.Pin.IN, machine.Pin.PULL_DOWN)
button_b = machine.Pin(badger2040.BUTTON_B, machine.Pin.IN, machine.Pin.PULL_DOWN)
# Set up the activity LED
led = machine.Pin(badger2040.PIN_LED, machine.Pin.OUT)
offsets = []
# Button handling function
def button(pin):
global next_page, prev_page, change_font_size, change_font
if pin == button_down:
next_page = True
if pin == button_up:
prev_page = True
if pin == button_a:
change_font_size = True
if pin == button_b:
change_font = True
# Register the button handling function with the buttons
button_down.irq(trigger=machine.Pin.IRQ_RISING, handler=button)
button_up.irq(trigger=machine.Pin.IRQ_RISING, handler=button)
button_a.irq(trigger=machine.Pin.IRQ_RISING, handler=button)
button_b.irq(trigger=machine.Pin.IRQ_RISING, handler=button)
# ------------------------------
# Render page
# ------------------------------
def render_page():
row = 0
line = ""
pos = ebook.tell()
next_pos = pos
add_newline = False
display.font(FONTS[0])
while True:
# Read a full line and split it into words
words = ebook.readline().split(" ")
# Take the length of the first word and advance our position
next_word = words[0]
if len(words) > 1:
next_pos += len(next_word) + 1
else:
next_pos += len(next_word) # This is the last word on the line
# Advance our position further if the word contains special characters
if '\u201c' in next_word:
next_word = next_word.replace('\u201c', '\"')
next_pos += 2
if '\u201d' in next_word:
next_word = next_word.replace('\u201d', '\"')
next_pos += 2
if '\u2019' in next_word:
next_word = next_word.replace('\u2019', '\'')
next_pos += 2
# Rewind the file back from the line end to the start of the next word
ebook.seek(next_pos)
# Strip out any new line characters from the word
next_word = next_word.strip()
# If an empty word is encountered assume that means there was a blank line
if len(next_word) == 0:
add_newline = True
# Append the word to the current line and measure its length
appended_line = line
if len(line) > 0 and len(next_word) > 0:
appended_line += " "
appended_line += next_word
appended_length = display.measure_text(appended_line, TEXT_SIZE)
# Would this appended line be longer than the text display area, or was a blank line spotted?
if appended_length >= TEXT_WIDTH or add_newline:
# Yes, so write out the line prior to the append
print(line)
display.pen(0)
display.thickness(FONT_THICKNESSES[0])
display.text(line, TEXT_PADDING, (row * TEXT_SPACING) + (TEXT_SPACING // 2) + TEXT_PADDING, TEXT_SIZE)
# Clear the line and move on to the next row
line = ""
row += 1
# Have we reached the end of the page?
if (row * TEXT_SPACING) + TEXT_SPACING >= HEIGHT:
print("+++++")
display.update()
# Reset the position to the start of the word that made this line too long
ebook.seek(pos)
return
else:
# Set the line to the word and advance the current position
line = next_word
pos = next_pos
# A new line was spotted, so advance a row
if add_newline:
print("")
row += 1
if (row * TEXT_SPACING) + TEXT_SPACING >= HEIGHT:
print("+++++")
display.update()
return
add_newline = False
else:
# The appended line was not too long, so set it as the line and advance the current position
line = appended_line
pos = next_pos
# ------------------------------
# Main program loop
# ------------------------------
# Open the book file
ebook = open(text_file, "r")
while True:
# Was the next page button pressed?
if next_page:
current_page += 1
# Is the next page one we've not displayed before?
if current_page > len(offsets):
offsets.append(ebook.tell()) # Add its start position to the offsets list
draw_frame()
render_page()
next_page = False # Clear the next page button flag
# Was the previous page button pressed?
if prev_page:
if current_page > 1:
current_page -= 1
ebook.seek(offsets[current_page - 1]) # Retrieve the start position of the last page
draw_frame()
render_page()
prev_page = False # Clear the prev page button flag
if change_font_size:
TEXT_SIZE += 0.1
if TEXT_SIZE > 0.8:
TEXT_SIZE = 0.5
TEXT_SPACING = int(34 * TEXT_SIZE)
offsets = [0]
ebook.seek(0)
current_page = 1
draw_frame()
render_page()
change_font_size = False
if change_font:
FONTS.append(FONTS.pop(0))
FONT_THICKNESSES.append(FONT_THICKNESSES.pop(0))
offsets = [0]
ebook.seek(0)
current_page = 1
draw_frame()
render_page()
change_font = False
time.sleep(0.1)

View File

@ -0,0 +1,141 @@
import badger2040
import machine
import time
# Global Constants
FONT_NAMES = ("sans", "gothic", "cursive", "serif", "serif_italic")
WIDTH = badger2040.WIDTH
HEIGHT = badger2040.HEIGHT
MENU_TEXT_SIZE = 0.5
MENU_SPACING = 20
MENU_WIDTH = 84
MENU_PADDING = 2
TEXT_SIZE = 0.8
TEXT_INDENT = MENU_WIDTH + 10
ARROW_THICKNESS = 3
ARROW_WIDTH = 18
ARROW_HEIGHT = 14
ARROW_PADDING = 2
# ------------------------------
# Drawing functions
# ------------------------------
# Draw a upward arrow
def draw_up(x, y, width, height, thickness, padding):
border = (thickness // 4) + padding
display.line(x + border, y + height - border,
x + (width // 2), y + border)
display.line(x + (width // 2), y + border,
x + width - border, y + height - border)
# Draw a downward arrow
def draw_down(x, y, width, height, thickness, padding):
border = (thickness // 2) + padding
display.line(x + border, y + border,
x + (width // 2), y + height - border)
display.line(x + (width // 2), y + height - border,
x + width - border, y + border)
# Draw the frame of the reader
def draw_frame():
display.pen(15)
display.clear()
display.pen(12)
display.rectangle(WIDTH - ARROW_WIDTH, 0, ARROW_WIDTH, HEIGHT)
display.pen(0)
display.thickness(ARROW_THICKNESS)
draw_up(WIDTH - ARROW_WIDTH, (HEIGHT // 4) - (ARROW_HEIGHT // 2),
ARROW_WIDTH, ARROW_HEIGHT, ARROW_THICKNESS, ARROW_PADDING)
draw_down(WIDTH - ARROW_WIDTH, ((HEIGHT * 3) // 4) - (ARROW_HEIGHT // 2),
ARROW_WIDTH, ARROW_HEIGHT, ARROW_THICKNESS, ARROW_PADDING)
# Draw the fonts and menu
def draw_fonts():
display.font("sans")
display.thickness(1)
for i in range(len(FONT_NAMES)):
name = FONT_NAMES[i]
display.pen(0)
if i == selected_font:
display.rectangle(0, i * MENU_SPACING, MENU_WIDTH, MENU_SPACING)
display.pen(15)
display.text(name, MENU_PADDING, (i * MENU_SPACING) + (MENU_SPACING // 2), MENU_TEXT_SIZE)
display.font(FONT_NAMES[selected_font])
display.thickness(2)
display.pen(0)
display.text("The quick", TEXT_INDENT, 10, TEXT_SIZE)
display.text("brown fox", TEXT_INDENT, 32, TEXT_SIZE)
display.text("jumped over", TEXT_INDENT, 54, TEXT_SIZE)
display.text("the lazy dog.", TEXT_INDENT, 76, TEXT_SIZE)
display.text("0123456789", TEXT_INDENT, 98, TEXT_SIZE)
display.text("!\"£$%^&*()", TEXT_INDENT, 120, TEXT_SIZE)
display.thickness(1)
display.update()
# ------------------------------
# Program setup
# ------------------------------
# Global variables
selected_font = 0
pressed = False
# Create a new Badger and set it to update FAST
display = badger2040.Badger2040()
display.update_speed(badger2040.UPDATE_FAST)
# Set up the buttons
button_up = machine.Pin(badger2040.BUTTON_UP, machine.Pin.IN, machine.Pin.PULL_DOWN)
button_down = machine.Pin(badger2040.BUTTON_DOWN, machine.Pin.IN, machine.Pin.PULL_DOWN)
# Button handling function
def button(pin):
global pressed
global selected_font
if not pressed:
if pin == button_up:
selected_font -= 1
if selected_font < 0:
selected_font = len(FONT_NAMES) - 1
pressed = True
return
if pin == button_down:
selected_font += 1
if selected_font >= len(FONT_NAMES):
selected_font = 0
pressed = True
return
# Register the button handling function with the buttons
button_up.irq(trigger=machine.Pin.IRQ_RISING, handler=button)
button_down.irq(trigger=machine.Pin.IRQ_RISING, handler=button)
# ------------------------------
# Main program loop
# ------------------------------
while True:
draw_frame()
draw_fonts()
pressed = False
while not pressed:
time.sleep(0.1)

View File

@ -0,0 +1,40 @@
import badger2040
import time
from badger2040 import WIDTH
TEXT_SIZE = 0.45
LINE_HEIGHT = 16
display = badger2040.Badger2040()
display.pen(0)
display.rectangle(0, 0, WIDTH, 16)
display.thickness(1)
display.pen(15)
display.text("badgerOS", 3, 8, 0.4)
display.pen(0)
y = 16 + int(LINE_HEIGHT / 2)
display.thickness(2)
display.text("Normal:", 0, y, TEXT_SIZE)
display.thickness(1)
y += LINE_HEIGHT
display.text("Up / Down - Change launcher page", 0, y, TEXT_SIZE)
y += LINE_HEIGHT
display.text("a, b or c - Launch app", 0, y, TEXT_SIZE)
y += LINE_HEIGHT
y += LINE_HEIGHT
display.thickness(2)
display.text("Holding USER button:", 0, y, TEXT_SIZE)
display.thickness(1)
y += LINE_HEIGHT
display.text("Up / Down - Change font size", 0, y, TEXT_SIZE)
y += LINE_HEIGHT
display.text("a - Toggle invert", 0, y, TEXT_SIZE)
y += LINE_HEIGHT
display.update()
while True:
time.sleep(1)

View File

@ -0,0 +1,154 @@
import os
import sys
import time
import machine
import badger2040
from badger2040 import WIDTH, HEIGHT
REAMDE = """
Images must be 296x128 pixel with 1bit colour depth.
You can use examples/badger2040/image_converter/convert.py to convert them:
python3 convert.py --binary --resize image_file_1.png image_file_2.png image_file_3.png
Create a new "images" directory via Thonny, and upload the .bin files there.
"""
OVERLAY_BORDER = 40
OVERLAY_SPACING = 20
OVERLAY_TEXT_SIZE = 0.5
TOTAL_IMAGES = 0
# Try to preload BadgerPunk image
try:
os.mkdir("images")
except OSError:
pass
try:
import badgerpunk
with open("images/badgerpunk.bin", "wb") as f:
f.write(badgerpunk.data())
f.flush()
with open("images/readme.txt", "w") as f:
f.write(REAMDE)
f.flush()
del badgerpunk
except (OSError, ImportError):
pass
try:
IMAGES = [f for f in os.listdir("/images") if f.endswith(".bin")]
TOTAL_IMAGES = len(IMAGES)
except OSError:
pass
display = badger2040.Badger2040()
button_a = machine.Pin(badger2040.BUTTON_A, machine.Pin.IN, machine.Pin.PULL_DOWN)
button_b = machine.Pin(badger2040.BUTTON_B, machine.Pin.IN, machine.Pin.PULL_DOWN)
button_c = machine.Pin(badger2040.BUTTON_C, machine.Pin.IN, machine.Pin.PULL_DOWN)
button_up = machine.Pin(badger2040.BUTTON_UP, machine.Pin.IN, machine.Pin.PULL_DOWN)
button_down = machine.Pin(badger2040.BUTTON_DOWN, machine.Pin.IN, machine.Pin.PULL_DOWN)
image = bytearray(int(296 * 128 / 8))
current_image = 0
show_info = True
# Draw an overlay box with a given message within it
def draw_overlay(message, width, height, line_spacing, text_size):
# Draw a light grey background
display.pen(12)
display.rectangle((WIDTH - width) // 2, (HEIGHT - height) // 2, width, height)
# Take the provided message and split it up into
# lines that fit within the specified width
words = message.split(" ")
lines = []
current_line = ""
for word in words:
if display.measure_text(current_line + word + " ", text_size) < width:
current_line += word + " "
else:
lines.append(current_line.strip())
current_line = word + " "
lines.append(current_line.strip())
display.pen(0)
display.thickness(2)
# Display each line of text from the message, centre-aligned
num_lines = len(lines)
for i in range(num_lines):
length = display.measure_text(lines[i], text_size)
current_line = (i * line_spacing) - ((num_lines - 1) * line_spacing) // 2
display.text(lines[i], (WIDTH - length) // 2, (HEIGHT // 2) + current_line, text_size)
def show_image(n):
file = IMAGES[n]
name = file.split(".")[0]
open("images/{}".format(file), "r").readinto(image)
display.image(image)
if show_info:
name_length = display.measure_text(name, 0.5)
display.pen(0)
display.rectangle(0, HEIGHT - 21, name_length + 11, 21)
display.pen(15)
display.rectangle(0, HEIGHT - 20, name_length + 10, 20)
display.pen(0)
display.text(name, 5, HEIGHT - 10, 0.5)
for i in range(TOTAL_IMAGES):
x = 286
y = int((128 / 2) - (TOTAL_IMAGES * 10 / 2) + (i * 10))
display.pen(0)
display.rectangle(x, y, 8, 8)
if current_image != i:
display.pen(15)
display.rectangle(x + 1, y + 1, 6, 6)
display.update()
if TOTAL_IMAGES == 0:
display.pen(15)
display.clear()
draw_overlay("To run this demo, create an /images directory on your device and upload some 1bit 296x128 pixel images.", WIDTH - OVERLAY_BORDER, HEIGHT - OVERLAY_BORDER, OVERLAY_SPACING, OVERLAY_TEXT_SIZE)
display.update()
sys.exit()
show_image(current_image)
while True:
if button_up.value():
if current_image > 0:
current_image -= 1
show_image(current_image)
if button_down.value():
if current_image < TOTAL_IMAGES - 1:
current_image += 1
show_image(current_image)
if button_a.value():
show_info = not show_info
show_image(current_image)
if button_b.value() or button_c.value():
display.pen(15)
display.clear()
draw_overlay("To add images connect Badger2040 to a PC, load up Thonny, and see readme.txt in images/", WIDTH - OVERLAY_BORDER, HEIGHT - OVERLAY_BORDER, OVERLAY_SPACING, 0.5)
display.update()
time.sleep(4)
show_image(current_image)
time.sleep(0.01)

View File

@ -0,0 +1,38 @@
import badger2040
import time
from badger2040 import WIDTH
TEXT_SIZE = 0.45
LINE_HEIGHT = 16
display = badger2040.Badger2040()
display.pen(0)
display.rectangle(0, 0, WIDTH, 16)
display.thickness(1)
display.pen(15)
display.text("badgerOS", 3, 8, 0.4)
display.pen(0)
y = 16 + int(LINE_HEIGHT / 2)
display.text("Made by Pimoroni, powered by MicroPython", 0, y, TEXT_SIZE)
y += LINE_HEIGHT
display.text("Dual-core RP2040, 133MHz, 264KB RAM", 0, y, TEXT_SIZE)
y += LINE_HEIGHT
display.text("2MB Flash (1MB OS, 1MB Storage)", 0, y, TEXT_SIZE)
y += LINE_HEIGHT
display.text("296x128 pixel Black/White e-Ink", 0, y, TEXT_SIZE)
y += LINE_HEIGHT
y += LINE_HEIGHT
display.thickness(2)
display.text("For more info:", 0, y, TEXT_SIZE)
display.thickness(1)
y += LINE_HEIGHT
display.text("https://pimoroni.com/badger2040", 0, y, TEXT_SIZE)
display.update()
while True:
time.sleep(1)

View File

@ -0,0 +1,260 @@
import gc
import os
import time
import math
import machine
import badger2040
from badger2040 import WIDTH
import launchericons
# for e.g. 2xAAA batteries, try max 3.4 min 3.0
MAX_BATTERY_VOLTAGE = 4.0
MIN_BATTERY_VOLTAGE = 3.2
page = 0
font_size = 1
inverted = False
icons = bytearray(launchericons.data())
icons_width = 576
examples = [
("_clock", 0),
("_fonts", 1),
("_ebook", 2),
("_image", 3),
("_list", 4),
("_badge", 5),
("_qrgen", 8),
("_info", 6),
("_help", 7),
]
font_sizes = (0.5, 0.7, 0.9)
# Approximate center lines for buttons A, B and C
centers = (41, 147, 253)
MAX_PAGE = math.ceil(len(examples) / 3)
button_a = machine.Pin(badger2040.BUTTON_A, machine.Pin.IN, machine.Pin.PULL_DOWN)
button_b = machine.Pin(badger2040.BUTTON_B, machine.Pin.IN, machine.Pin.PULL_DOWN)
button_c = machine.Pin(badger2040.BUTTON_C, machine.Pin.IN, machine.Pin.PULL_DOWN)
button_up = machine.Pin(badger2040.BUTTON_UP, machine.Pin.IN, machine.Pin.PULL_DOWN)
button_down = machine.Pin(badger2040.BUTTON_DOWN, machine.Pin.IN, machine.Pin.PULL_DOWN)
# Inverted. For reasons.
button_user = machine.Pin(badger2040.BUTTON_USER, machine.Pin.IN, machine.Pin.PULL_UP)
# Battery measurement
vbat_adc = machine.ADC(badger2040.PIN_BATTERY)
vref_adc = machine.ADC(badger2040.PIN_1V2_REF)
vref_en = machine.Pin(badger2040.PIN_VREF_POWER)
vref_en.init(machine.Pin.OUT)
vref_en.value(0)
display = badger2040.Badger2040()
def map_value(input, in_min, in_max, out_min, out_max):
return (((input - in_min) * (out_max - out_min)) / (in_max - in_min)) + out_min
def get_battery_level():
# Enable the onboard voltage reference
vref_en.value(1)
# Calculate the logic supply voltage, as will be lower that the usual 3.3V when running off low batteries
vdd = 1.24 * (65535 / vref_adc.read_u16())
vbat = (
(vbat_adc.read_u16() / 65535) * 3 * vdd
) # 3 in this is a gain, not rounding of 3.3V
# Disable the onboard voltage reference
vref_en.value(0)
# Convert the voltage to a level to display onscreen
return int(map_value(vbat, MIN_BATTERY_VOLTAGE, MAX_BATTERY_VOLTAGE, 0, 4))
def draw_battery(level, x, y):
# Outline
display.thickness(1)
display.pen(15)
display.rectangle(x, y, 19, 10)
# Terminal
display.rectangle(x + 19, y + 3, 2, 4)
display.pen(0)
display.rectangle(x + 1, y + 1, 17, 8)
if level < 1:
display.pen(0)
display.line(x + 3, y, x + 3 + 10, y + 10)
display.line(x + 3 + 1, y, x + 3 + 11, y + 10)
display.pen(15)
display.line(x + 2 + 2, y - 1, x + 4 + 12, y + 11)
display.line(x + 2 + 3, y - 1, x + 4 + 13, y + 11)
return
# Battery Bars
display.pen(15)
for i in range(4):
if level / 4 > (1.0 * i) / 4:
display.rectangle(i * 4 + x + 2, y + 2, 3, 6)
def draw_disk_usage(x):
# f_bfree and f_bavail should be the same?
# f_files, f_ffree, f_favail and f_flag are unsupported.
f_bsize, f_frsize, f_blocks, f_bfree, _, _, _, _, _, f_namemax = os.statvfs(
"/")
f_total_size = f_frsize * f_blocks
f_total_free = f_bsize * f_bfree
f_total_used = f_total_size - f_total_free
f_used = 100 / f_total_size * f_total_used
# f_free = 100 / f_total_size * f_total_free
display.image(
bytearray(
(
0b00000000,
0b00111100,
0b00111100,
0b00111100,
0b00111000,
0b00000000,
0b00000000,
0b00000001,
)
),
8,
8,
x,
4,
)
display.pen(15)
display.rectangle(x + 10, 3, 80, 10)
display.pen(0)
display.rectangle(x + 11, 4, 78, 8)
display.pen(15)
display.rectangle(x + 12, 5, int(76 / 100.0 * f_used), 6)
display.text("{:.2f}%".format(f_used), x + 91, 8, 0.4)
def render():
display.pen(15)
display.clear()
display.pen(0)
display.thickness(2)
max_icons = min(3, len(examples[(page * 3):]))
for i in range(max_icons):
x = centers[i]
label, icon = examples[i + (page * 3)]
label = label[1:].replace("_", " ")
display.pen(0)
display.icon(icons, icon, icons_width, 64, x - 32, 24)
w = display.measure_text(label, font_sizes[font_size])
display.text(label, x - int(w / 2), 16 + 80, font_sizes[font_size])
for i in range(MAX_PAGE):
x = 286
y = int((128 / 2) - (MAX_PAGE * 10 / 2) + (i * 10))
display.pen(0)
display.rectangle(x, y, 8, 8)
if page != i:
display.pen(15)
display.rectangle(x + 1, y + 1, 6, 6)
display.pen(0)
display.rectangle(0, 0, WIDTH, 16)
display.thickness(1)
draw_disk_usage(90)
draw_battery(get_battery_level(), WIDTH - 22 - 3, 3)
display.pen(15)
display.text("badgerOS", 3, 8, 0.4)
display.update()
def launch(file):
for k in locals().keys():
if k not in ("gc", "file", "machine"):
del locals()[k]
gc.collect()
try:
__import__(file[1:]) # Try to import _[file] (drop underscore prefix)
except ImportError:
__import__(file) # Failover to importing [_file]
machine.reset() # Exit back to launcher
def launch_example(index):
try:
launch(examples[(page * 3) + index][0])
return True
except IndexError:
return False
def button(pin):
global page, font_size, inverted
if button_user.value(): # User button is NOT held down
if pin == button_a:
launch_example(0)
if pin == button_b:
launch_example(1)
if pin == button_c:
launch_example(2)
if pin == button_up:
if page > 0:
page -= 1
render()
if pin == button_down:
if page < MAX_PAGE - 1:
page += 1
render()
else: # User button IS held down
if pin == button_up:
font_size += 1
if font_size == len(font_sizes):
font_size = 0
render()
if pin == button_down:
font_size -= 1
if font_size < 0:
font_size = 0
render()
if pin == button_a:
inverted = not inverted
display.invert(inverted)
render()
display.update_speed(badger2040.UPDATE_MEDIUM)
render()
display.update_speed(badger2040.UPDATE_FAST)
# Wait for wakeup button to be released
while button_a.value() or button_b.value() or button_c.value() or button_up.value() or button_down.value():
pass
while True:
if button_a.value():
button(button_a)
if button_b.value():
button(button_b)
if button_c.value():
button(button_c)
if button_up.value():
button(button_up)
if button_down.value():
button(button_down)
time.sleep(0.01)

View File

@ -0,0 +1,12 @@
# Blinky badger fun!
import badger2040
import time
badger = badger2040.Badger2040()
while True:
badger.led(255)
time.sleep(1)
badger.led(0)
time.sleep(1)

View File

@ -0,0 +1,311 @@
import badger2040
import machine
import time
# **** Put your list title and contents here *****
list_title = "Checklist"
list_content = ["Badger", "Badger", "Badger", "Badger", "Badger", "Mushroom", "Mushroom", "Snake"]
list_states = [False] * len(list_content)
list_file = "checklist.txt"
# Global Constants
WIDTH = badger2040.WIDTH
HEIGHT = badger2040.HEIGHT
ARROW_THICKNESS = 3
ARROW_WIDTH = 18
ARROW_HEIGHT = 14
ARROW_PADDING = 2
MAX_ITEM_CHARS = 26
TITLE_TEXT_SIZE = 0.7
ITEM_TEXT_SIZE = 0.6
ITEM_SPACING = 20
LIST_START = 40
LIST_PADDING = 2
LIST_WIDTH = WIDTH - LIST_PADDING - LIST_PADDING - ARROW_WIDTH
LIST_HEIGHT = HEIGHT - LIST_START - LIST_PADDING - ARROW_HEIGHT
def save_list():
with open(list_file, "w") as f:
f.write(list_title + "\n")
for i in range(len(list_content)):
list_item = list_content[i]
if list_states[i]:
list_item += " X"
f.write(list_item + "\n")
# ------------------------------
# Drawing functions
# ------------------------------
# Draw the list of items
def draw_list(items, item_states, start_item, highlighted_item, x, y, width, height, item_height, columns):
item_x = 0
item_y = 0
current_col = 0
for i in range(start_item, len(items)):
if i == highlighted_item:
display.pen(12)
display.rectangle(item_x, item_y + y - (item_height // 2), width // columns, item_height)
display.pen(0)
display.text(items[i], item_x + x + item_height, item_y + y, ITEM_TEXT_SIZE)
draw_checkbox(item_x, item_y + y - (item_height // 2), item_height, 15, 0, 2, item_states[i], 2)
item_y += item_height
if item_y >= height - (item_height // 2):
item_x += width // columns
item_y = 0
current_col += 1
if current_col >= columns:
return
# Draw a upward arrow
def draw_up(x, y, width, height, thickness, padding):
border = (thickness // 4) + padding
display.line(x + border, y + height - border,
x + (width // 2), y + border)
display.line(x + (width // 2), y + border,
x + width - border, y + height - border)
# Draw a downward arrow
def draw_down(x, y, width, height, thickness, padding):
border = (thickness // 2) + padding
display.line(x + border, y + border,
x + (width // 2), y + height - border)
display.line(x + (width // 2), y + height - border,
x + width - border, y + border)
# Draw a left arrow
def draw_left(x, y, width, height, thickness, padding):
border = (thickness // 2) + padding
display.line(x + width - border, y + border,
x + border, y + (height // 2))
display.line(x + border, y + (height // 2),
x + width - border, y + height - border)
# Draw a right arrow
def draw_right(x, y, width, height, thickness, padding):
border = (thickness // 2) + padding
display.line(x + border, y + border,
x + width - border, y + (height // 2))
display.line(x + width - border, y + (height // 2),
x + border, y + height - border)
# Draw a tick
def draw_tick(x, y, width, height, thickness, padding):
border = (thickness // 2) + padding
display.line(x + border, y + ((height * 2) // 3),
x + (width // 2), y + height - border)
display.line(x + (width // 2), y + height - border,
x + width - border, y + border)
# Draw a cross
def draw_cross(x, y, width, height, thickness, padding):
border = (thickness // 2) + padding
display.line(x + border, y + border, x + width - border, y + height - border)
display.line(x + width - border, y + border, x + border, y + height - border)
# Draw a checkbox with or without a tick
def draw_checkbox(x, y, size, background, foreground, thickness, tick, padding):
border = (thickness // 2) + padding
display.pen(background)
display.rectangle(x + border, y + border, size - (border * 2), size - (border * 2))
display.pen(foreground)
display.thickness(thickness)
display.line(x + border, y + border, x + size - border, y + border)
display.line(x + border, y + border, x + border, y + size - border)
display.line(x + size - border, y + border, x + size - border, y + size - border)
display.line(x + border, y + size - border, x + size - border, y + size - border)
if tick:
draw_tick(x, y, size, size, thickness, 2 + border)
# ------------------------------
# Program setup
# ------------------------------
# Global variables
update = True
needs_save = False
current_item = 0
items_per_page = 0
# Create a new Badger and set it to update FAST
display = badger2040.Badger2040()
display.update_speed(badger2040.UPDATE_FAST)
# Set up the buttons
button_a = machine.Pin(badger2040.BUTTON_A, machine.Pin.IN, machine.Pin.PULL_DOWN)
button_b = machine.Pin(badger2040.BUTTON_B, machine.Pin.IN, machine.Pin.PULL_DOWN)
button_c = machine.Pin(badger2040.BUTTON_C, machine.Pin.IN, machine.Pin.PULL_DOWN)
button_up = machine.Pin(badger2040.BUTTON_UP, machine.Pin.IN, machine.Pin.PULL_DOWN)
button_down = machine.Pin(badger2040.BUTTON_DOWN, machine.Pin.IN, machine.Pin.PULL_DOWN)
# Find out what the longest item is
longest_item = 0
for i in range(len(list_content)):
while True:
item = list_content[i]
item_length = display.measure_text(item, ITEM_TEXT_SIZE)
if item_length > 0 and item_length > LIST_WIDTH - ITEM_SPACING:
list_content[i] = item[:-1]
else:
break
longest_item = max(longest_item, display.measure_text(list_content[i], ITEM_TEXT_SIZE))
# And use that to calculate the number of columns we can fit onscreen and how many items that would give
list_columns = 1
while longest_item + ITEM_SPACING < (LIST_WIDTH // (list_columns + 1)):
list_columns += 1
items_per_page = ((LIST_HEIGHT // ITEM_SPACING) + 1) * list_columns
# Button handling function
def button(pin):
global update, current_item, needs_save
if len(list_content) > 0 and not update:
if pin == button_a:
if current_item > 0:
current_item = max(current_item - (items_per_page) // list_columns, 0)
update = True
return
if pin == button_b:
list_states[current_item] = not list_states[current_item]
needs_save = True
update = True
return
if pin == button_c:
if current_item < len(list_content) - 1:
current_item = min(current_item + (items_per_page) // list_columns, len(list_content) - 1)
update = True
return
if pin == button_up:
if current_item > 0:
current_item -= 1
update = True
return
if pin == button_down:
if current_item < len(list_content) - 1:
current_item += 1
update = True
return
# Register the button handling function with the buttons
button_a.irq(trigger=machine.Pin.IRQ_RISING, handler=button)
button_b.irq(trigger=machine.Pin.IRQ_RISING, handler=button)
button_c.irq(trigger=machine.Pin.IRQ_RISING, handler=button)
button_up.irq(trigger=machine.Pin.IRQ_RISING, handler=button)
button_down.irq(trigger=machine.Pin.IRQ_RISING, handler=button)
# ------------------------------
# Main program loop
# ------------------------------
try:
with open(list_file, "r") as f:
list_content = f.read().strip().split("\n")
list_title = list_content.pop(0)
list_states = [False] * len(list_content)
for i in range(len(list_content)):
list_content[i] = list_content[i].strip()
if list_content[i].endswith(" X"):
list_states[i] = True
list_content[i] = list_content[i][:-2]
except OSError:
save_list()
while True:
if needs_save:
save_list()
needs_save = False
if update:
display.pen(15)
display.clear()
display.pen(12)
display.rectangle(WIDTH - ARROW_WIDTH, 0, ARROW_WIDTH, HEIGHT)
display.rectangle(0, HEIGHT - ARROW_HEIGHT, WIDTH, ARROW_HEIGHT)
y = LIST_PADDING + 12
display.pen(0)
display.thickness(3)
display.text(list_title, LIST_PADDING, y, TITLE_TEXT_SIZE)
y += 12
display.pen(0)
display.thickness(2)
display.line(LIST_PADDING, y, WIDTH - LIST_PADDING - ARROW_WIDTH, y)
if len(list_content) > 0:
page_item = 0
if items_per_page > 0:
page_item = (current_item // items_per_page) * items_per_page
# Draw the list
display.pen(0)
display.thickness(2)
draw_list(list_content, list_states, page_item, current_item, LIST_PADDING, LIST_START,
LIST_WIDTH, LIST_HEIGHT, ITEM_SPACING, list_columns)
# Draw the interaction button icons
display.pen(0)
display.thickness(ARROW_THICKNESS)
# Previous item
if current_item > 0:
draw_up(WIDTH - ARROW_WIDTH, (HEIGHT // 4) - (ARROW_HEIGHT // 2),
ARROW_WIDTH, ARROW_HEIGHT, ARROW_THICKNESS, ARROW_PADDING)
# Next item
if current_item < (len(list_content) - 1):
draw_down(WIDTH - ARROW_WIDTH, ((HEIGHT * 3) // 4) - (ARROW_HEIGHT // 2),
ARROW_WIDTH, ARROW_HEIGHT, ARROW_THICKNESS, ARROW_PADDING)
# Previous column
if current_item > 0:
draw_left((WIDTH // 7) - (ARROW_WIDTH // 2), HEIGHT - ARROW_HEIGHT,
ARROW_WIDTH, ARROW_HEIGHT, ARROW_THICKNESS, ARROW_PADDING)
# Next column
if current_item < (len(list_content) - 1):
draw_right(((WIDTH * 6) // 7) - (ARROW_WIDTH // 2), HEIGHT - ARROW_HEIGHT,
ARROW_WIDTH, ARROW_HEIGHT, ARROW_THICKNESS, ARROW_PADDING)
if list_states[current_item]:
# Tick off item
draw_cross((WIDTH // 2) - (ARROW_WIDTH // 2), HEIGHT - ARROW_HEIGHT,
ARROW_HEIGHT, ARROW_HEIGHT, ARROW_THICKNESS, ARROW_PADDING)
else:
# Untick item
draw_tick((WIDTH // 2) - (ARROW_WIDTH // 2), HEIGHT - ARROW_HEIGHT,
ARROW_HEIGHT, ARROW_HEIGHT, ARROW_THICKNESS, ARROW_PADDING)
else:
# Say that the list is empty
empty_text = "Nothing Here"
text_length = display.measure_text(empty_text, ITEM_TEXT_SIZE)
display.text(empty_text, ((LIST_PADDING + LIST_WIDTH) - text_length) // 2, (LIST_HEIGHT // 2) + LIST_START - (ITEM_SPACING // 4), ITEM_TEXT_SIZE)
display.update()
display.update_speed(badger2040.UPDATE_TURBO)
update = False
time.sleep(0.1)

View File

@ -0,0 +1,54 @@
function (convert_image TARGET IMAGE)
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/../modules/${IMAGE}.py
COMMAND
cd ${CMAKE_CURRENT_LIST_DIR}/assets && python3 ../../../../examples/badger2040/image_converter/convert.py --out_dir ${CMAKE_CURRENT_BINARY_DIR}/../modules --py ${IMAGE}.png
DEPENDS ${CMAKE_CURRENT_LIST_DIR}/assets/${IMAGE}.png
)
target_sources(${TARGET} INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/../modules/${IMAGE}.py)
endfunction()
function (convert_raw TARGET SRC DST)
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/../modules/${DST}.py
COMMAND
cd ${CMAKE_CURRENT_LIST_DIR}/assets && python3 ../../../../examples/badger2040/image_converter/data_to_py.py ${CMAKE_CURRENT_LIST_DIR}/assets/${SRC} ${CMAKE_CURRENT_BINARY_DIR}/../modules/${DST}.py
DEPENDS ${CMAKE_CURRENT_LIST_DIR}/assets/${SRC}
)
target_sources(${TARGET} INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/../modules/${DST}.py)
endfunction()
function (copy_module TARGET SRC DST)
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/../modules/${DST}.py
COMMAND
cp ${SRC} ${CMAKE_CURRENT_BINARY_DIR}/../modules/${DST}.py
DEPENDS ${src}
)
target_sources(${TARGET} INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/../modules/${DST}.py)
endfunction()
convert_image(usermod_badger2040 badge_image)
convert_image(usermod_badger2040 badgerpunk)
convert_image(usermod_badger2040 launchericons)
convert_raw(usermod_badger2040 289-0-wind-in-the-willows-abridged.txt witw)
copy_module(usermod_badger2040 ${CMAKE_CURRENT_LIST_DIR}/assets/boot.py boot)
copy_module(usermod_badger2040 ${CMAKE_CURRENT_LIST_DIR}/launcher.py _launcher)
copy_module(usermod_badger2040 ${CMAKE_CURRENT_LIST_DIR}/clock.py _clock)
copy_module(usermod_badger2040 ${CMAKE_CURRENT_LIST_DIR}/fonts.py _fonts)
copy_module(usermod_badger2040 ${CMAKE_CURRENT_LIST_DIR}/ebook.py _ebook)
copy_module(usermod_badger2040 ${CMAKE_CURRENT_LIST_DIR}/image.py _image)
copy_module(usermod_badger2040 ${CMAKE_CURRENT_LIST_DIR}/list.py _list)
copy_module(usermod_badger2040 ${CMAKE_CURRENT_LIST_DIR}/badge.py _badge)
copy_module(usermod_badger2040 ${CMAKE_CURRENT_LIST_DIR}/help.py _help)
copy_module(usermod_badger2040 ${CMAKE_CURRENT_LIST_DIR}/info.py _info)
copy_module(usermod_badger2040 ${CMAKE_CURRENT_LIST_DIR}/qrgen.py _qrgen)

View File

@ -0,0 +1,65 @@
import badger2040
import machine
display = badger2040.Badger2040()
button_a = machine.Pin(badger2040.BUTTON_A, machine.Pin.IN, machine.Pin.PULL_DOWN)
button_b = machine.Pin(badger2040.BUTTON_B, machine.Pin.IN, machine.Pin.PULL_DOWN)
display.thickness(10)
display.pen(0)
display.line(0, 5, 295, 5)
display.line(0, 123, 295, 123)
display.thickness(1)
for x in range(14):
display.line(x * 20, 10, x * 20, 118)
display.line(0, 0, 295, 127)
display.line(0, 127, 295, 0)
display.font("sans")
display.thickness(5)
display.text("Hello World", 10, 30, 1.0)
display.pen(7)
display.text("Hello World", 10, 60, 1.0)
display.pen(11)
display.text("Hello World", 10, 90, 1.0)
display.update()
dirty = False
pressed = None
def button(pin):
global dirty, pressed
if pin == button_a:
pressed = "Button A"
dirty = True
return
if pin == button_b:
pressed = "Button B"
dirty = True
return
button_a.irq(trigger=machine.Pin.IRQ_RISING, handler=button)
button_b.irq(trigger=machine.Pin.IRQ_RISING, handler=button)
# This breaks Thonny, since it's no longer possible to Stop the code
# need to press the reset button on the board...
# It will also crash your USB bus, probably, your whole bus...
# @micropython.asm_thumb
# def lightsleep():
# wfi()
while True:
if dirty:
display.pen(15)
display.clear()
display.pen(0)
display.text(pressed, 10, 60, 2.0)
display.update()
dirty = False
# machine.lightsleep() # Currently imposible to wake from this on IRQ

View File

@ -0,0 +1,70 @@
import badger2040
import qrcode
import time
# Open the qrcode file
try:
text = open("qrcode.txt", "r")
except OSError:
text = open("qrcode.txt", "w")
text.write("""https://pimoroni.com/badger2040
Badger 2040
* 296x128 1-bit e-ink
* six user buttons
* user LED
* 2MB QSPI flash
Scan this code to learn
more about Badger 2040.
""")
text.flush()
text.seek(0)
lines = text.read().strip().split("\n")
code_text = lines.pop(0)
title_text = lines.pop(0)
detail_text = lines
display = badger2040.Badger2040()
code = qrcode.QRCode()
def measure_qr_code(size, code):
w, h = code.get_size()
module_size = int(size / w)
return module_size * w, module_size
def draw_qr_code(ox, oy, size, code):
size, module_size = measure_qr_code(size, code)
display.pen(15)
display.rectangle(ox, oy, size, size)
display.pen(0)
for x in range(size):
for y in range(size):
if code.get_module(x, y):
display.rectangle(ox + x * module_size, oy + y * module_size, module_size, module_size)
code.set_text(code_text)
size, _ = measure_qr_code(128, code)
left = top = int((badger2040.HEIGHT / 2) - (size / 2))
draw_qr_code(left, top, 128, code)
left = 128 + 5
display.thickness(2)
display.text(title_text, left, 20, 0.5)
display.thickness(1)
top = 40
for line in detail_text:
display.text(line, left, top, 0.4)
top += 10
display.update()
while True:
time.sleep(1.0)

58
badger2040/poetry.lock generated Normal file
View File

@ -0,0 +1,58 @@
[[package]]
name = "pillow"
version = "9.1.1"
description = "Python Imaging Library (Fork)"
category = "main"
optional = false
python-versions = ">=3.7"
[package.extras]
docs = ["olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-issues (>=3.0.1)", "sphinx-removed-in", "sphinx-rtd-theme (>=1.0)", "sphinxext-opengraph"]
tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"]
[metadata]
lock-version = "1.1"
python-versions = "^3.8"
content-hash = "8a3b1f7b4c4af4b58e7bcb3333c009c0da1516d59db4332afeca1ce6a360c1ad"
[metadata.files]
pillow = [
{file = "Pillow-9.1.1-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:42dfefbef90eb67c10c45a73a9bc1599d4dac920f7dfcbf4ec6b80cb620757fe"},
{file = "Pillow-9.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ffde4c6fabb52891d81606411cbfaf77756e3b561b566efd270b3ed3791fde4e"},
{file = "Pillow-9.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c857532c719fb30fafabd2371ce9b7031812ff3889d75273827633bca0c4602"},
{file = "Pillow-9.1.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:59789a7d06c742e9d13b883d5e3569188c16acb02eeed2510fd3bfdbc1bd1530"},
{file = "Pillow-9.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4d45dbe4b21a9679c3e8b3f7f4f42a45a7d3ddff8a4a16109dff0e1da30a35b2"},
{file = "Pillow-9.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e9ed59d1b6ee837f4515b9584f3d26cf0388b742a11ecdae0d9237a94505d03a"},
{file = "Pillow-9.1.1-cp310-cp310-win32.whl", hash = "sha256:b3fe2ff1e1715d4475d7e2c3e8dabd7c025f4410f79513b4ff2de3d51ce0fa9c"},
{file = "Pillow-9.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:5b650dbbc0969a4e226d98a0b440c2f07a850896aed9266b6fedc0f7e7834108"},
{file = "Pillow-9.1.1-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:0b4d5ad2cd3a1f0d1df882d926b37dbb2ab6c823ae21d041b46910c8f8cd844b"},
{file = "Pillow-9.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9370d6744d379f2de5d7fa95cdbd3a4d92f0b0ef29609b4b1687f16bc197063d"},
{file = "Pillow-9.1.1-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b761727ed7d593e49671d1827044b942dd2f4caae6e51bab144d4accf8244a84"},
{file = "Pillow-9.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a66fe50386162df2da701b3722781cbe90ce043e7d53c1fd6bd801bca6b48d4"},
{file = "Pillow-9.1.1-cp37-cp37m-win32.whl", hash = "sha256:2b291cab8a888658d72b575a03e340509b6b050b62db1f5539dd5cd18fd50578"},
{file = "Pillow-9.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:1d4331aeb12f6b3791911a6da82de72257a99ad99726ed6b63f481c0184b6fb9"},
{file = "Pillow-9.1.1-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:8844217cdf66eabe39567118f229e275f0727e9195635a15e0e4b9227458daaf"},
{file = "Pillow-9.1.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b6617221ff08fbd3b7a811950b5c3f9367f6e941b86259843eab77c8e3d2b56b"},
{file = "Pillow-9.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20d514c989fa28e73a5adbddd7a171afa5824710d0ab06d4e1234195d2a2e546"},
{file = "Pillow-9.1.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:088df396b047477dd1bbc7de6e22f58400dae2f21310d9e2ec2933b2ef7dfa4f"},
{file = "Pillow-9.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53c27bd452e0f1bc4bfed07ceb235663a1df7c74df08e37fd6b03eb89454946a"},
{file = "Pillow-9.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:3f6c1716c473ebd1649663bf3b42702d0d53e27af8b64642be0dd3598c761fb1"},
{file = "Pillow-9.1.1-cp38-cp38-win32.whl", hash = "sha256:c67db410508b9de9c4694c57ed754b65a460e4812126e87f5052ecf23a011a54"},
{file = "Pillow-9.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:f054b020c4d7e9786ae0404278ea318768eb123403b18453e28e47cdb7a0a4bf"},
{file = "Pillow-9.1.1-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:c17770a62a71718a74b7548098a74cd6880be16bcfff5f937f900ead90ca8e92"},
{file = "Pillow-9.1.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f3f6a6034140e9e17e9abc175fc7a266a6e63652028e157750bd98e804a8ed9a"},
{file = "Pillow-9.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f372d0f08eff1475ef426344efe42493f71f377ec52237bf153c5713de987251"},
{file = "Pillow-9.1.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09e67ef6e430f90caa093528bd758b0616f8165e57ed8d8ce014ae32df6a831d"},
{file = "Pillow-9.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66daa16952d5bf0c9d5389c5e9df562922a59bd16d77e2a276e575d32e38afd1"},
{file = "Pillow-9.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d78ca526a559fb84faaaf84da2dd4addef5edb109db8b81677c0bb1aad342601"},
{file = "Pillow-9.1.1-cp39-cp39-win32.whl", hash = "sha256:55e74faf8359ddda43fee01bffbc5bd99d96ea508d8a08c527099e84eb708f45"},
{file = "Pillow-9.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:7c150dbbb4a94ea4825d1e5f2c5501af7141ea95825fadd7829f9b11c97aaf6c"},
{file = "Pillow-9.1.1-pp37-pypy37_pp73-macosx_10_10_x86_64.whl", hash = "sha256:769a7f131a2f43752455cc72f9f7a093c3ff3856bf976c5fb53a59d0ccc704f6"},
{file = "Pillow-9.1.1-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:488f3383cf5159907d48d32957ac6f9ea85ccdcc296c14eca1a4e396ecc32098"},
{file = "Pillow-9.1.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b525a356680022b0af53385944026d3486fc8c013638cf9900eb87c866afb4c"},
{file = "Pillow-9.1.1-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:6e760cf01259a1c0a50f3c845f9cad1af30577fd8b670339b1659c6d0e7a41dd"},
{file = "Pillow-9.1.1-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a4165205a13b16a29e1ac57efeee6be2dfd5b5408122d59ef2145bc3239fa340"},
{file = "Pillow-9.1.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:937a54e5694684f74dcbf6e24cc453bfc5b33940216ddd8f4cd8f0f79167f765"},
{file = "Pillow-9.1.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:baf3be0b9446a4083cc0c5bb9f9c964034be5374b5bc09757be89f5d2fa247b8"},
{file = "Pillow-9.1.1.tar.gz", hash = "sha256:7502539939b53d7565f3d11d87c78e7ec900d3c72945d4ee0e2f250d598309a0"},
]

15
badger2040/pyproject.toml Normal file
View File

@ -0,0 +1,15 @@
[tool.poetry]
name = "badger2040"
version = "0.1.0"
description = "scripts et libs pour badger2040"
authors = ["Your Name <you@example.com>"]
[tool.poetry.dependencies]
python = "^3.8"
Pillow = "^9.1.1"
[tool.poetry.dev-dependencies]
[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"

23
doc/phat-beat.md Normal file
View File

@ -0,0 +1,23 @@
# Phat Beat / Pirate Radio
Hat audio par Piromoni
- [getting started](https://learn.pimoroni.com/tutorial/sandyj/getting-started-with-phat-beat)
## Internet Radio via VLC
Le [tuto Piromoni](https://learn.pimoroni.com/tutorial/sandyj/internet-radio-on-your-pirate-radio) ne fonctionne pas. On obtient le message :
> --- Warning ---
>
> The VLC Radio installer
> does not work on this version of Raspbian.
Il faut cloner [le dépot git](https://github.com/pimoroni/phat-beat) et lancer le script via `sudo bash setup.sh` depuis le dépot cloné.
La playlist se trouve à /etc/vlcd/default.m3u
Démarrer / stopper le service : `sudo service vlcd stop`
## à voir
- [juke box python + vlc + tornado](https://github.com/lionaneesh/RasPod)

151
doc/pihole.md Normal file
View File

@ -0,0 +1,151 @@
# Pi-Hole pour filtrer le réseau
## commandes d'administration
[doc des commandes](https://docs.pi-hole.net/core/pihole-command/)
```bash
pihole -a -p # Changer le mot de passe WebUI
pihole status # afficher le statut
pihole -up # mise à jour de pi-hole
pihole enable # Activer PiHole
pihole disable # Désactiver PiHole en permanence
pihole disable 10m # Désactiver PiHole pendant 10 minutes
pihole disable 60s # Désactiver PiHole pendant 1 min
```
## Forcer l'usage de piHole
https://www.geekzone.fr/2020/05/13/pi-hole-5-un-trou-noir-qui-protege-votre-logement-des-pubs/
https://discourse.pi-hole.net/t/how-do-i-use-pi-holes-built-in-dhcp-server-and-why-would-i-want-to/3026
> Some routers (and even some Internet Service Providers) do not let you change these settings. In this case, using Pi-holes DHCP server is very advantageous because you can still get automatic, network-wide ad ad blocking even if you cant make the necessary changes on existing hardware.
> Bonjour messieurs, j'infirme ce qui a été dit avant, on peut changer les DNS de la livebox mais il faut désactiver le serveur DHCP de la box.
>
> Par exemple, j'utilise Pi-hole sur un ubuntu (rasperry pi fera l'affaire), sur ce pi-hole j'ai activé le serveur dhcp pi-hole , qui distribue les adresses DNS, et fait office de DNS avec débordements sur celles d'Orange mais j'aurai déborder vers google aussi.
>
> En méthodologie, il faut activer d'abord le dhcp de la livebox et ainsi fixer l'adresse IP de la livebox (pour moi 10.0.0.1), j'affecte ensuite une ip fixe sur le rasperri pi-hole , puis je désactive le dhcp livebox et enfin j'active celui du rasperry pi-hole avec passerelle sur le 10.0.0.1. J'ajoute un DNS secondary dans les fichiers config pihole et je mets la livebox , au cas où le rasperry ait un souci de ralentissement important.
https://mediacenterz.com/tutoriel-complete-pi-hole-bloqueur-dannonces-pour-toute-la-maison/ :
> Pour bloquer les annonces au niveau du réseau, ce qui signifie que tous les appareils connectés à votre réseau domestique ne verront aucune annonce, vous devrez modifier manuellement les adresses IP de votre serveur DNS sur votre routeur. Alors que de nombreux routeurs autorisent la configuration manuelle des serveurs DNS, certains ne rendent pas cette option avancée disponible. Les micrologiciels de routeur personnalisés, tels que DD-WRT, OpenWRT et Tomato, permettent douvrir cette option parmi dautres. Par conséquent, si vous ne voyez pas doption permettant de modifier les serveurs de noms DNS, envisagez de passer à lun des microprogrammes de routeur de remplacement gratuit pris en charge.
>
>Lavantage ici est quil ny a quun seul endroit où vous aurez besoin de mettre à jour ladresse IP de votre serveur DNS au lieu de chaque appareil. Cependant, il y a quelques inconvénients:
>
>Le suivi par hôte sera indisponible. Toutes les demandes adressées à PiHole apparaîtront comme si elles venaient de votre routeur. Mon opinion personnelle est que ce nest pas un gros problème pour un utilisateur domestique typique. Je ne lutilise pas. Mais si vous en avez absolument besoin
Vous ne pourrez pas vous connecter aux périphériques avec leurs noms dhôte car PiHole ne peut pas résoudre les noms dhôte. Encore une fois, pas un gros problème pour un utilisateur à domicile typique à mon avis.
Si les deux inconvénients ci-dessus sont des pactes pour vous, vous pouvez les surmonter partiellement en utilisant le fichier PiHole Hosts ou en publiant complètement ladresse IP de PiHole via Dnsmasq dans un routeur (si cette fonction est prise en charge).
>
>Notez que si vous choisissez cette méthode, vous devrez renouveler les baux DHCP fournis par le routeur. Pour ce faire, le plus simple consiste à redémarrer le routeur.
## faire tourner d'autres sites avec Apache
https://discourse.pi-hole.net/t/migrating-pi-hole-from-lighttpd-to-apache/152/14 :
> I just installed pihole on ubuntu server and because I already use Apache & PHP I uninstalled lighttpd.
I have several virtual sites on Apache so I added also pihole; all work without problems.
> Here it is my config if anybody need to replicate for his use:
edit: /etc/apache2/sites-available/000-default.conf
```
#=== pihole WEBSITE ===
<VirtualHost *:80>
ServerAdmin webmaster@localhost
ServerName pihole
ServerAlias pi.hole
DocumentRoot /var/www/html/dns
<Directory /var/www/html/dns/>
Options FollowSymLinks MultiViews
AllowOverride all
Order deny,allow
allow from all
</Directory>
ErrorLog ${APACHE_LOG_DIR}/pihole_error.log
LogLevel warn
CustomLog ${APACHE_LOG_DIR}/pihole_access.log combined
</VirtualHost>
#=== site1 WEBSITE ===
<VirtualHost *:80>
ServerAdmin webmaster@localhost
ServerName site1
DocumentRoot /var/www/html/site1
<Directory /var/www/html/site1/>
Options FollowSymLinks MultiViews
AllowOverride all
Order deny,allow
Deny from all
allow from 127.0.0.1
allow from 10.22.22.0/24
</Directory>
ErrorLog ${APACHE_LOG_DIR}/site1_error.log
LogLevel warn
CustomLog ${APACHE_LOG_DIR}/site1_access.log combined
</VirtualHost>
#==== site2 WEBSITE ====
<VirtualHost *:80>
ServerAdmin webmaster@localhost
ServerName webtv
DocumentRoot /var/www/html/site2
<Directory /var/www/html/site2/>
Options FollowSymLinks MultiViews
AllowOverride all
Order deny,allow
Deny from all
allow from 10.22.22.0/24
allow from 127.0.0.1
</Directory>
ErrorLog ${APACHE_LOG_DIR}/site2_error.log
LogLevel warn
CustomLog ${APACHE_LOG_DIR}/site2_access.log combined
</VirtualHost>
```
> edit your server hosts file so you can access any website you need from server,
( if you host your custom files for blocking):
```
# /etc/hosts
127.0.0.1 localhost
127.0.1.1 ubuntu1
127.0.1.2 pihole
127.0.1.3 site1
127.0.1.4 site2
10.22.22.16 site1
10.22.22.16 site2
10.22.22.16 pihole
10.22.22.16 ubuntu1
```
> copy all web files for pihole ( move it from /var/www/html/ ) and also content from pihole in dns:
it will look like this:
/var/www/html/dns/admin/...
/var/www/html/dns/pihole/...
/var/www/html/dns/blockingpage.css
/var/www/html/dns/index.js
/var/www/html/dns/index.php
/var/www/html/site1/...
/var/www/html/site2/...
> If I did not forgot something this is all you need to change to have pihole working in Apache, so now reload/restart apache.

37
doc/setup.md Normal file
View File

@ -0,0 +1,37 @@
# SETUP nouveau raspberry pi
## Tutos
- [Installer un pi sans écran ni clavier](https://learn.pimoroni.com/tutorial/sandyj/setting-up-a-headless-pi)
- [installer log2ram pour prolonger la vie de la carte SD](https://github.com/azlux/log2ram)
### Créer un utilisateur sudoer
Adapté depuis [ce tuto](https://www.raspberrypi.org/forums/viewtopic.php?t=169079) :
```shell
NEW_USER="jducastel"
# créer l'utilisateur + dossier home
sudo adduser $NEW_USER
# l'ajouter aux mêmes groupes que l'utilisateur "pi"
sudo usermod $NEW_USER -a -G pi,adm,dialout,cdrom,sudo,audio,video,plugdev,games,users,input,netdev,spi,i2c,gpio
# pour ne pas avoir à taper le mdp en tant que sudo,
# on clone le fichier concernant l'utilisateur "pi" dans le dossier inclus
sudo cp /etc/sudoers.d/010_pi-nopasswd /etc/sudoers.d/$NEW_USER
# remplacer "pi" par le nom de l'utilisateur
sudo nano /etc/sudoers.d/$NEW_USER
```
### changer le hostname
```shell
sudo hostnamectl set-hostname NEW_HOSTNAME
# remplacer "raspberry" par le nouveau nom pour 127.0.1.1
sudo nano /etc/hosts
```
### passer de bash à zshell
```shell
sudo apt-get update && sudo apt-get install zsh
# Edit your passwd configuration file to tell which shell to use for user pi :
# change /bin/bash for /bin/zsh for your user
sudo nano /etc/passwd
```

View File

@ -6,30 +6,25 @@ sur raspberry pi + sense hat
principe : saisie d'un message en morse via le joystick de la sense hat
la grille LED permet d'avoir un retour / analyse de ce qui est saisi
une fois le message construit, transmission via la matrice LED
utilisation du joystick de la sense hat
gauche = saisir un point
droit = saisir un trait
haut = valider (selon le contexte : lettre, mot, message), ajouter un espace
bas = corriger / annuler la dernière entrée
haut = saisir un point
bas = saisir un trait
droite = valider (selon le contexte : lettre, mot, message), ajouter un espace
gauche = corriger / annuler la dernière entrée
utilise la lib morse-talk pour le decodage / encodage
utilise la lib morse-talk pour le decodage / encodage. pour l'installation :
$ pip install morse-talk
utilisation :
$ python sense_morse.py
"""
from sense_hat import SenseHat, ACTION_PRESSED, ACTION_HELD, ACTION_RELEASED
from signal import pause
import time, morse_talk
DOT = '.'
DASH = '-'
PAUSE = ' '
END_CHAR = ' '
END_WORD = ' '
class MorseSenseHatKeyer:
green = [0, 255, 0]
@ -38,16 +33,19 @@ class MorseSenseHatKeyer:
def __init__(self):
self.sense = SenseHat()
# association des evenements générés par le joystick de la SenseHat
# aux méthodes de traitement internes
self.sense.stick.direction_up = self.pushed_up
self.sense.stick.direction_down = self.pushed_down
self.sense.stick.direction_left = self.pushed_left
self.sense.stick.direction_right = self.pushed_right
self.sense.stick.direction_any = self.refresh
self.message_ascii = ''
self.current_morse_char = ''
# initalisation des variables de traitement
self.message_ascii = '' # message construit sous forme ASCII
self.current_morse_char = '' # caractère morse en cours de construction
def pushed_left(self, event):
""" correction """
""" correction (réinitialise caractère courant) """
# correction
if event.action != ACTION_RELEASED:
self.current_morse_char = ''
@ -62,48 +60,49 @@ class MorseSenseHatKeyer:
self.validate_char()
def pushed_down(self, event):
""" ajout trait """
if event.action == ACTION_HELD:
self.show_dash()
if event.action == ACTION_RELEASED:
self.add_dash()
""" ajout d'un trait """
if event.action == ACTION_HELD: # tant que le bouton est pressé
self.show_dash() # affichage d'un trait
if event.action == ACTION_RELEASED: # au relachement
self.add_dash() # ajout du trait si possible
def pushed_up(self, event):
""" ajout point """
if event.action == ACTION_HELD:
self.show_dot()
if event.action == ACTION_HELD: # tant que le bouton est pressé
self.show_dot() # affichage d'un point
if event.action == ACTION_RELEASED:
self.add_dot()
self.add_dot() # ajout du point si possible
def show_dot(self, rgb=None):
# affichage d'un point sur la matrice LED
rgb = rgb or [150, 150, 150]
self.sense.clear()
for x in range(3, 5):
for y in range(3, 5):
self.sense.set_pixel(x, y, rgb)
# time.sleep(duration)
def show_dash(self, rgb=None):
# affichage d'un trait sur la matrice LED
rgb = rgb or [150, 150, 150]
self.sense.clear()
for x in range(1, 7):
for y in range(3, 5):
self.sense.set_pixel(x, y, rgb)
# time.sleep(duration)
def refresh(self, event):
pass
# self.sense.clear()
def show_char(self, char, rgb=None):
""" affiche un caractère """
""" affiche un caractère dans la couleur définie (gris par defaut)"""
rgb = rgb or [150, 150, 150]
self.sense.clear()
self.sense.show_letter(char, rgb)
def add_dot(self):
""" ajout d'un point au caractère courant """
""" ajout d'un point au caractère courant
tant que le caractère courant reste interpretable
sinon, affichage d'une erreur et reinitialise le caractère courant """
try:
self.current_morse_char += '.'
char = morse_talk.decode(self.current_morse_char)
@ -113,7 +112,9 @@ class MorseSenseHatKeyer:
self.current_morse_char = ''
def add_dash(self):
""" ajout d'un trait au caractère courant """
""" ajout d'un trait au caractère courant
tant que le caractère courant reste interpretable
sinon, affichage d'une erreur et reinitialise le caractère courant """
self.current_morse_char += '-'
try:
char = morse_talk.decode(self.current_morse_char)
@ -122,13 +123,15 @@ class MorseSenseHatKeyer:
self.show_char('X', self.red)
self.current_morse_char = ''
def show_error(self):
pass
def reset_char(self):
""" annule le caractère courant """
pass
self.current_morse_char = ''
def validate_char(self):
""" valide la lettre courante """
""" valide la lettre courante, et donne un retour visuel """
char = morse_talk.decode(self.current_morse_char)
self.show_char(char, self.green)
self.message_ascii += char
@ -138,13 +141,13 @@ class MorseSenseHatKeyer:
""" affiche le message courant """
if __name__ == "__main__":
if __name__ == "__main__": # lors de l'execution du script en standalone
try:
keyer = MorseSenseHatKeyer()
keyer.sense.show_message("morse://", 0.02)
# waiting for jostick events and directing them to keyer
# waiting for joystick events and directing them to keyer
pause()
except (KeyboardInterrupt, SystemExit) as err:
except (KeyboardInterrupt, SystemExit) as err: # lors d'un CTRL+C pour interruption
sense = SenseHat()
sense.show_message('out', 0.02)
sense.show_message('bye', 0.02)
sense.clear()