Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for large font. #226

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
8aaf6f0
Add support for large font.
francoisgeorgy Feb 15, 2023
58b0110
Source reformatted with black.
francoisgeorgy Feb 16, 2023
d001469
add config save and load methods to EuroPiScript
mjaskula Aug 4, 2022
a604120
add ability to save values to config file
mjaskula Sep 23, 2022
d59a3c5
config improvements and clean up
mjaskula Nov 17, 2022
e880966
make europi config points available to all scripts
mjaskula Nov 17, 2022
e5f999b
a more pythonic config api
mjaskula Jan 12, 2023
1027c45
use configuration in europi.py
mjaskula Feb 6, 2023
0b2095d
add script to generate default config files
mjaskula Jan 17, 2023
40616cd
add execution instructions to generate_default_configs.py
mjaskula Feb 13, 2023
c254eb1
use namedtuple for validation
mjaskula Feb 13, 2023
f8ddba5
don't use config lookup outside of __init__
mjaskula Feb 13, 2023
758144c
Merge pull request #220 from mjaskula/mjj/config_script
mjaskula Feb 17, 2023
57c4dce
Add logic script (#225)
chrisib Feb 17, 2023
0381ff9
Update software/firmware/experimental/largefont_writer.py
francoisgeorgy Feb 21, 2023
ea2c56f
Update software/firmware/europi.py
francoisgeorgy Feb 21, 2023
3581395
New method text_len to get the pixel length of a string. Especially u…
francoisgeorgy Feb 23, 2023
ef3cf87
Add support for large font.
francoisgeorgy Feb 15, 2023
e1a53d4
Source reformatted with black.
francoisgeorgy Feb 16, 2023
6c0b545
Update software/firmware/experimental/largefont_writer.py
francoisgeorgy Feb 21, 2023
f8d4d78
Update software/firmware/europi.py
francoisgeorgy Feb 21, 2023
1dd5512
New method text_len to get the pixel length of a string. Especially u…
francoisgeorgy Feb 23, 2023
27b4c36
Merge remote-tracking branch 'origin/large_font_support' into large_f…
francoisgeorgy Feb 23, 2023
b849256
Update software/firmware/europi.py
francoisgeorgy Feb 23, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -27,3 +27,4 @@ venv
.idea
dist/
*.egg-info/
config
4 changes: 4 additions & 0 deletions Makefile
Expand Up @@ -13,3 +13,7 @@ clean:
deploy_firmware: clean
# requires rshell https://github.com/dhylands/rshell
rshell -f scripts/deploy_firmware.rshell

deploy_configs:
# requires rshell https://github.com/dhylands/rshell
rshell -f scripts/deploy_configs.rshell
1 change: 1 addition & 0 deletions docs/api.rst
Expand Up @@ -5,6 +5,7 @@ API
:toctree: generated

europi
config
europi_script
ui
experimental
Expand Down
2 changes: 2 additions & 0 deletions scripts/deploy_configs.rshell
@@ -0,0 +1,2 @@
mkdir /pyboard/config
cp ./config/*.json /pyboard/config
90 changes: 90 additions & 0 deletions scripts/generate_default_configs.py
@@ -0,0 +1,90 @@
#!/usr/bin/env python3
"""
This script can be used to generate default configuration files for all EuroPiScripts that use the
configuration feature. Simply execute this script from the root of the project directory.

$ python3 scripts/generate_default_configs.py

The config files will be generated in the a `config` directory. The files can be edited and then
loaded onto the pico in a `config` directory in the root of the pico's file system.
"""
import os
import sys
import importlib
from types import ModuleType


def find_europi_scripts():
modules = sorted(
set(
f.partition(".")[0]
for f in os.listdir(importlib.import_module("contrib").__path__[0])
if f.endswith((".py", ".pyc", ".pyo")) and not f.startswith("__init__.py")
)
)

pkg = __import__("contrib", fromlist=modules)
visited = set() # avoid processing scripts twice if they get imported twice (eg by the menu)

# most of this code was taken from https://stackoverflow.com/a/3507271
for m in modules:
module = getattr(pkg, m)
if type(module) == ModuleType:
for c in dir(module):
klass = getattr(module, c)
if (
isinstance(klass, type)
and klass is not EuroPiScript
and issubclass(klass, EuroPiScript)
):
if klass not in visited:
visited.add(klass)
yield klass


def generate_default_config(europi_script):
spec = ConfigSpec(europi_script.config_points())

if spec: # don't bother generating empty config files
print(f"Generating: {ConfigFile.config_filename(europi_script)}")
ConfigFile.save_config(europi_script, spec.default_config())


def mock_time_functions():
# a file in the mock package doesn't work for the `time` package, so we will have to monkey
# patch the missing functions to make the imports in contrib scripts succeed

import time

def noop():
pass

time.sleep_ms = noop
time.ticks_ms = noop
time.ticks_add = noop
time.ticks_diff = noop


if __name__ == "__main__":
sys.path.insert(0, os.path.abspath("software/firmware"))
sys.path.insert(0, os.path.abspath("software"))
sys.path.insert(0, os.path.abspath("software/tests/mocks"))

mock_time_functions()

ConfigSpec = importlib.import_module("configuration").ConfigSpec
ConfigFile = importlib.import_module("configuration").ConfigFile
EuroPiScript = importlib.import_module("europi_script").EuroPiScript
EuroPiConfig = importlib.import_module("europi_config").EuroPiConfig

print(
"""
Generating default config files for any contrib scripts that have config points defined.
Edit and upload to /config on the pico to change a script's configuration.
"""
)

generate_default_config(EuroPiConfig)

for script in find_europi_scripts():
generate_default_config(script)
7 changes: 7 additions & 0 deletions software/contrib/README.md
Expand Up @@ -48,6 +48,13 @@ The division of the master clock that each LFO runs at, as well as each of their
<i>Author: [roryjamesallen](https://github.com/roryjamesallen)</i>
<br><i>Labels: LFO</i>

### Logic \[ [documentation](/software/contrib/logic.md) | [script](/software/contrib/logic.py) \]

Treats both inputs as digital on/off signals and outputs the results of binary AND, OR, XOR, NAND, NOR, and XNOR operations on outputs 1-6.

<i>Author: [chrisib](https://github.com/chrisib)</i>
<br><i>Labels: logic, gates, binary operators</i>

### Noddy Holder \[ [documentation](/software/contrib/noddy_holder.md) | [script](/software/contrib/noddy_holder.py) \]
Two channels of sample/track and hold based on a single trigger and CV source

Expand Down
43 changes: 29 additions & 14 deletions software/contrib/diagnostic.py
@@ -1,8 +1,25 @@
from machine import ADC
from time import sleep

from europi import OLED_HEIGHT, OLED_WIDTH, ain, b1, b2, cv1, cv2, cv3, cv4, cv5, cv6, din, k1, k2, oled
from europi import (
OLED_HEIGHT,
OLED_WIDTH,
ain,
b1,
b2,
cv1,
cv2,
cv3,
cv4,
cv5,
cv6,
din,
k1,
k2,
oled,
)
from europi_script import EuroPiScript
import configuration

"""
A diagnostic utility intended to help prove out a new EuroPi build and calibration. Each aspect of the EuroPi's hardware
Expand All @@ -19,8 +36,8 @@

TEMP_CONV_FACTOR = 3.3 / 65535

class Diagnostic(EuroPiScript):

class Diagnostic(EuroPiScript):
def __init__(self):
super().__init__()
self.temp_sensor = ADC(4)
Expand All @@ -32,32 +49,31 @@ def __init__(self):
5,
10, # max
]
self.temp_units = self.config["temp_units"]
self.use_fahrenheit = self.temp_units == "F"

@classmethod
def config_points(cls):
return [configuration.choice(name="temp_units", choices=["C", "F"], default="C")]

def calc_temp(self):
# see the pico's datasheet for the details of this calculation
return 27 - ((self.temp_sensor.read_u16() * TEMP_CONV_FACTOR) - 0.706) / 0.001721

@staticmethod
def convert_fahrenheit(temp_c):
return (temp_c * 1.8) + 32

t = 27 - ((self.temp_sensor.read_u16() * TEMP_CONV_FACTOR) - 0.706) / 0.001721
if self.use_fahrenheit:
t = (t * 1.8) + 32
return t

def rotate_r(self):
self.voltages = self.voltages[-1:] + self.voltages[:-1]


def rotate_l(self):
self.voltages = self.voltages[1:] + self.voltages[:1]


def main(self):

b1.handler(self.rotate_l)
b2.handler(self.rotate_r)

while True:

# Set the outputs to useful values
cv1.voltage(self.voltages[0])
cv2.voltage(self.voltages[1])
Expand All @@ -69,9 +85,8 @@ def main(self):
oled.fill(0)

# calc and format temp
use_fahrenheit = b1.value() or b2.value()
t = self.calc_temp()
formatted_temp = f"{int(self.convert_fahrenheit(t) if use_fahrenheit else t)}{'F' if use_fahrenheit else 'C'}"
formatted_temp = f"{int(t)}{self.temp_units}"

# display the input values
oled.text(f"ain: {ain.read_voltage():5.2f}v {formatted_temp}", 2, 3, 1)
Expand Down
58 changes: 58 additions & 0 deletions software/contrib/largefont_demo.py
@@ -0,0 +1,58 @@
"""
Large font demo
author: François Georgy (https://github.com/francoisgeorgy)
date: 2023-02-15

Use this script to test and demo the support for large fonts.

See the largefont_writer module for information about how to import your own fonts.
"""
import time

from europi_script import EuroPiScript
from europi import oled, OLED_WIDTH, OLED_HEIGHT
import freesans14
import freesans17
import freesans20
import freesans24


class LargeFontDemo(EuroPiScript):
@classmethod
def display_name(cls):
return "Large font demo"

def __init__(self):
super().__init__()
self.demo = 4
self.boxed = True # alternate one round boxed, one round unboxed.

def update_demo(self):
self.demo = (self.demo + 1) % 5
if self.demo == 0:
oled.centre_text("Default\nmonospaced\n8x8 font")
self.boxed = not self.boxed
elif self.demo == 1:
oled.centre_text("14 ABC gpq\n0123456 gp", font=freesans14)
elif self.demo == 2:
oled.centre_text("17 ABC gp", font=freesans17)
elif self.demo == 3:
oled.centre_text("20 ABC gp", font=freesans20)
elif self.demo == 4:
oled.centre_text("24 ABC gp", font=freesans24)
if self.boxed:
oled.rect(0, 0, OLED_WIDTH, OLED_HEIGHT, 1)
oled.show()

def main(self):
t = 0
while True:
if (time.time() - t) >= 1:
self.update_demo()
t = time.time()
time.sleep(0.1)


if __name__ == "__main__":
oled.contrast(0) # dim the display
LargeFontDemo().main()
41 changes: 41 additions & 0 deletions software/contrib/logic.md
@@ -0,0 +1,41 @@
# Logic

This program implements six digital logic operations, with each
operation's result sent to a different output jack.

- `ain`: the first boolean input; any signal above 0.8V is treated
as ON, anything below that is OFF. (Note: this is the same voltage
threshold used on the digital input.)
- `din`: the second boolean input
- `cv1`: `ain` AND `din`
- `cv2`: `ain` OR `din`
- `cv3`: `ain` XOR `din`
- `cv4`: `ain` NAND `din`
- `cv5`: `ain` NOR `din`
- `cv6`: `ain` XNOR `din`

The outputs are as follows:

| Output No. | Operation |
|------------|-----------|
| Out 1 | AND |
| Out 2 | OR |
| Out 3 | XOR |
| Out 4 | NAND |
| Out 5 | NOR |
| Out 6 | XNOR |

The following table shows the value for each operation for every
input combination:

| Din | Ain | AND | OR | XOR | NAND | NOR | XNOR |
|-----|-----|-----|----|-----|------|-----|------|
| 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 |
| 0 | 1 | 0 | 1 | 1 | 1 | 0 | 0 |
| 1 | 0 | 0 | 1 | 1 | 1 | 0 | 0 |
| 1 | 1 | 1 | 1 | 0 | 0 | 0 | 1 |

The screen will go to sleep after 20 minutes of inactivity. Pressing
either button will wake the screen up.

The knobs are not used in this program.