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 I2C Realtime Clocks #400

Draft
wants to merge 46 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
70c4437
Add initial support for I2C realtime clocks. Allow for future expansi…
chrisib Jan 6, 2025
eb0ceb6
Remove the UTC offsets from the documentation; these aren't implemented
chrisib Jan 6, 2025
766a90e
Linting, ignore formatting in some blocks where the formatting improv…
chrisib Jan 6, 2025
dbf352e
Linting
chrisib Jan 6, 2025
80da883
Ignore more blocks GitHub's linter is whinging about (but my local li…
chrisib Jan 6, 2025
52f6a51
Remove an unnecessary else
chrisib Jan 6, 2025
7c8a31d
Fix some inconsistencies with the datetime tuples when setting vs get…
chrisib Jan 6, 2025
debda2a
Formatting fixes
chrisib Jan 6, 2025
a79adcd
Missing newline
chrisib Jan 6, 2025
951b89c
Expand the comment block to provide instructions on how to set the clock
chrisib Jan 6, 2025
5a5fde8
Minor formatting changes
chrisib Jan 6, 2025
d376631
Typo
chrisib Jan 6, 2025
5c4f8c5
Typo
chrisib Jan 6, 2025
1239ce0
Refactor the RTC module to move all of the weekday/month names into c…
chrisib Jan 6, 2025
36b99df
Start working on a basic RTC-based random sequencer
chrisib Jan 7, 2025
f61d7ed
Add a weekday-depending bitmask as a 12-bit baseline for the seed and…
chrisib Jan 7, 2025
10b97b5
Show the clock on daily_random, add a Sequence class to help keep thi…
chrisib Jan 8, 2025
afa376e
Move the clock installation instructions to a new file
chrisib Jan 8, 2025
ffa04ba
Insert images of the I2C header and standoff mount
chrisib Jan 8, 2025
17be47e
Add DateTime class, add support for local timezone offset via experim…
chrisib Jan 8, 2025
bd73b24
Formatting
chrisib Jan 8, 2025
3360d19
Add pictures of vertical mount, add note about insulating the clock
chrisib Jan 9, 2025
c802933
Fix the math for local timezone conversions
chrisib Jan 9, 2025
0ce976d
Check that the weekday isn't None before re-incrementing it
chrisib Jan 9, 2025
72a7b78
Merge branch 'main' into i2c-rtc
chrisib Jan 10, 2025
56e5715
Initial commit of pet rock script
chrisib Jan 11, 2025
a6a3483
Add pet rock to the menu & readme
chrisib Jan 11, 2025
0c75c80
Allow internally clocking the sequences, using the knobs as speed con…
chrisib Jan 11, 2025
73b4993
Add placeholders for the other algorithms (to be added as a config-on…
chrisib Jan 11, 2025
ab7fec3
Implement the 4 deprecated moods, add a config point to enable them
chrisib Jan 11, 2025
b84be0b
Document the new config option
chrisib Jan 11, 2025
67decb9
Add license information, link to the original source code
chrisib Jan 11, 2025
bd60f60
Fix param documentation for continuity variable
chrisib Jan 11, 2025
892675a
Swap the shields & clubs icons around, add a note to the readme about…
chrisib Jan 11, 2025
d095d23
Make Algo.map return an int to avoid floating-point conversion issues
chrisib Jan 11, 2025
963dd8b
Fix a bug when appending the block contents in AlgoBlock
chrisib Jan 11, 2025
276e681
Update Algo docstrings to briefly describe each algorithm. Tidy up Al…
chrisib Jan 11, 2025
404713f
Refactor the DateTime class to add properties for the number of days …
chrisib Jan 12, 2025
3b40e47
Render the fake time when running the tests
chrisib Jan 12, 2025
c2c9acd
Only use localtime for displaying the clock, checking the last displa…
chrisib Jan 12, 2025
f5afe17
Add logging for debugging, ensure that the sequence is never full nor…
chrisib Jan 12, 2025
8060650
Change the length of Cultures based on the moon phase to reduce insta…
chrisib Jan 12, 2025
ecacf37
Choose different culture rhythms for channels A and B, so even if the…
chrisib Jan 12, 2025
8773ce1
Choose the same rhythm, but backfill the second half (+/- extra steps…
chrisib Jan 12, 2025
3b64f9c
Fix an out-of-bounds error with daily_random
chrisib Jan 12, 2025
8414bcd
Add some additional test logging to make sure we have distinct patter…
chrisib Jan 12, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions software/CONFIGURATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,24 @@ shows the default configuration:
```json
{
"VOLTS_PER_OCTAVE": 1.0,
"RTC_IMPLEMENTATION": "",
"UTC_OFFSET_HOURS": 0,
"UTC_OFFSET_MINUTES": 0,
}
```

Quantization options:
- `VOLTS_PER_OCTAVE` must be one of `1.0` (Eurorack standard) or `1.2` (Buchla standard). Default: `1.0`

RTC options:
- `RTC_IMPLEMENTATION` is one of the following, representing the realtime clock enabled on your module:
- `""`: there is no RTC present. (default)
- `"ds3231"`: use a DS3231 module connected to the external I2C interface
- `"ds1307"`: use a DS1307 module connected to the external I2C interface (THIS IS UNTESTED! USE AT YOUR OWN RISK)

Timezone options:
- `UTC_OFFSET_HOURS`: The number of hours ahead/behind UTC the local timezone is (-24 to +24)
- `UTC_OFFSET_MINUTES`: The number of minutes ahead/behind UTC the local timezone is (-59 to +59)

# Accessing config members in Python code

Expand Down
15 changes: 15 additions & 0 deletions software/contrib/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,14 @@ Recording of CV can be primed so that you can record a movement without missing
<i>Author: [anselln](https://github.com/anselln)</i>
<br><i>Labels: sequencer, CV, performance</i>

### Daily Random \[ [documentation](/software/contrib/daily_random.md) | [script](/software/contrib/daily_random.md) \]
A pseudo-random gate and CV sequencer that uses a realtime clock to generate patterns.

Requires installing and configuring a realtime clock module, connected to EuroPi's external I2C interface for best results.

<i>Author: [chrisib](https://github.com/chrisib)</i>
<br><i>Labels: sequencer, gate, cv, random, realtime clock</i>

### DCSN-2 \[ [documentation](/software/contrib/dscn2.md) | [script](/software/contrib/dcsn2.md) \]
A loopable random gate sequencer based on a binary tree. Inspired by the Robaux DCSN3

Expand Down Expand Up @@ -165,6 +173,13 @@ While not technically random, the effects of changing the particle's initial con
<i>Author: [chrisib](https://github.com/chrisib)</i>
<br><i>Labels: gate, lfo, sequencer, random, trigger</i>

### Pet Rock \[ [documentation](/software/contrib/pet_rock.md) | [script](/software/contrib/pet_rock.py) \]
A pseudo-random gate generator that uses the realtime clock to track the phase of the moon as a seed. Based on [Pet Rock by Jonah Senzel](https://petrock.site)

Requires installing and configuring a realtime clock module, connected to EuroPi's external I2C interface for best results.

<i>Author: [chrisib](https://github.com/chrisib)</i>
<br><i>Labels: sequencer, gate, random, realtime clock</i>

### Poly Square \[ [documentation](/software/contrib/poly_square.md) | [script](/software/contrib/poly_square.py) \]
Six independent oscillators which output on CVs 1-6.
Expand Down
27 changes: 27 additions & 0 deletions software/contrib/daily_random.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Daily Random

Generates pseudo-random gate and CV patterns based on the current date and time.

## I/O Assignments

- `ain`: not used
- `din`: external clock input
- `b1`: not used
- `b2`: not used
- `k1`: not used
- `k2`: not used
- `cv1`: daily gate sequence (updates at midnight local time)
- `cv2`: hourly gate sequence (updates at the top of every hour)
- `cv3`: minute gate sequence (updates at the top of every minute)
- `cv4`: daily CV sequence (updates at midnight local time)
- `cv5`: hourly CV sequence (updates at the top of every hour)
- `cv6`: minute CV sequence (updates at the top of every minute)

## Required Hardware

This script _can_ be used on a normal EuroPi, but will result in highly predictable
patterns. For best result, connect a Realtime Clock (RTC) to EuroPi's secondary I2C
header pins, located on the underside of the board.

See [Realtime Clock Installation](/software/realtime_clock.md) for instructions on
installing and configuring the realtime clock.
144 changes: 144 additions & 0 deletions software/contrib/daily_random.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
from europi import *
from europi_script import EuroPiScript

from experimental.rtc import clock

import random


class Sequence:
"""
A single gate or CV sequence
"""
BASE_SEQUENCE_LENGTH = 16

def __init__(self, seed):
self.index = 0
self.regenerate(seed)

def regenerate(self, seed):
random.seed(seed)

# randomize the length so the majority are 16, but we get some longer or shorter
length = self.BASE_SEQUENCE_LENGTH
r = random.random()
if r < 0.1:
length -= 2
elif r < 0.25:
length -= 1
elif r > 0.8:
length += 2
elif r > 0.75:
length += 1

pattern = []
for i in range(length):
pattern.append(random.random())

self.pattern = pattern

def next(self):
self.index = (self.index + 1) % len(self.pattern)

@property
def gate_volts(self):
return (int(self.pattern[self.index] * 2) % 2) * europi_config.GATE_VOLTAGE

@property
def cv_volts(self):
return self.pattern[self.index] * europi_config.MAX_OUTPUT_VOLTAGE


class DailyRandom(EuroPiScript):
"""
Generates a set of pseudo-random gate and CV sequences every day

This script requires a realtime clock. Please refer to
experimental.clocks for supported clocks.

If no RTC is installed & configured, the default clock will be used,
but this will generate the same pattern every time the module is
restarted.
"""

SEQUENCE_LENGTH = 16

BITMASKS = [
0b101010101010,
0b001100110011,
0b000111000111,
0b111111000000,
0b111000111000,
0b110011001100,
0b010101010101,
]

def __init__(self):
super().__init__()

self.sequences = [
Sequence(0) for cv in cvs
]
self.regenerate_sequences()

self.trigger_recvd = False

@din.handler
def advance_sequence():
for s in self.sequences:
s.next()
self.trigger_recvd = True

@din.handler_falling
def gates_off():
for i in range(len(cvs) // 2):
cvs[i].off()

def regenerate_sequences(self):
datetime = clock.localnow()
year = datetime.year
month = datetime.month
day = datetime.day
hour = datetime.hour
minute = datetime.minute
second = datetime.second if datetime.second is not None else 0
weekday = datetime.weekday if datetime.weekday is not None else 1

# bit-shift the fields around to reduce collisions
# mask: 12 bits
# year: 11 bits
# month: 4 bits
# day: 5 bits
# hour: 6 bits
# minute: 6 bits
seeds = [
self.BITMASKS[weekday - 1] ^ year ^ (month << 7) ^ day,
self.BITMASKS[weekday - 1] ^ year ^ (month << 6) ^ day ^ ~hour,
self.BITMASKS[weekday - 1] ^ year ^ (month << 7) ^ day ^ (hour << 6) ^ minute,
]

for i in range(len(self.sequences)):
self.sequences[i].regenerate(seeds[i % len(seeds)])

def main(self):
last_draw_at = clock.localnow()
oled.centre_text(str(last_draw_at).replace(" ", "\n"))

while True:
now = clock.localnow()
if now != last_draw_at:
self.regenerate_sequences()
oled.centre_text(str(now).replace(" ", "\n"))
last_draw_at = now

if self.trigger_recvd:
self.trigger_recvd = False
for i in range(len(self.sequences)):
if i < len(cvs) // 2:
cvs[i].voltage(self.sequences[i].gate_volts)
else:
cvs[i].voltage(self.sequences[i].cv_volts)


if __name__ == "__main__":
DailyRandom().main()
2 changes: 2 additions & 0 deletions software/contrib/menu.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
["Consequencer", "contrib.consequencer.Consequencer"],
["Conway", "contrib.conway.Conway"],
["CVecorder", "contrib.cvecorder.CVecorder"],
["Daily Random", "contrib.daily_random.DailyRandom"],
["DCSN-2", "contrib.dscn2.Dcsn2"],
["Diagnostic", "contrib.diagnostic.Diagnostic"],
["EgressusMelodiam", "contrib.egressus_melodiam.EgressusMelodiam"],
Expand All @@ -48,6 +49,7 @@
["NoddyHolder", "contrib.noddy_holder.NoddyHolder"],
["Pam's Workout", "contrib.pams.PamsWorkout"],
["Particle Phys.", "contrib.particle_physics.ParticlePhysics"],
["Pet Rock", "contrib.pet_rock.PetRock"],
["Piconacci", "contrib.piconacci.Piconacci"],
["PolyrhythmSeq", "contrib.polyrhythmic_sequencer.PolyrhythmSeq"],
["PolySquare", "contrib.poly_square.PolySquare"],
Expand Down
Binary file added software/contrib/pet_rock-docs/cups.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added software/contrib/pet_rock-docs/diamond.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added software/contrib/pet_rock-docs/first_quarter.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added software/contrib/pet_rock-docs/full_moon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added software/contrib/pet_rock-docs/heart.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added software/contrib/pet_rock-docs/new_moon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added software/contrib/pet_rock-docs/pentacle.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added software/contrib/pet_rock-docs/shield.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added software/contrib/pet_rock-docs/spade.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added software/contrib/pet_rock-docs/swords.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added software/contrib/pet_rock-docs/third_quarter.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added software/contrib/pet_rock-docs/wands.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added software/contrib/pet_rock-docs/waning_gibbous.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added software/contrib/pet_rock-docs/waxing_gibbous.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
82 changes: 82 additions & 0 deletions software/contrib/pet_rock.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Pet Rock

A EuroPi clone of [Johan Senzel's Pet Rock](https://petrock.site/).

This script generates pseudo-random gate sequences based on the phase of the moon.

## I/O Assignments

- `din`: external clock input for sequence A
- `ain`: external clock input for sequence B
- `b1`: manually advance sequence A
- `b2`: manually advance sequence B
- `k1`: speed control for sequence A
- `k2`: speed control for sequence B
- `cv1`: primary gate output for sequence A
- `cv2`: inverted gate output for sequence A
- `cv3`: end of sequence trigger for sequence A
- `cv4`: primary gate output for sequence B
- `cv5`: inverted gate output for sequence B
- `cv6`: end of sequence trigger for sequence B

Both sequence A and sequence B can be internally clocked by setting the speed using `K1` and `K2`. Turning
these knobs fully anticlockwise will stop the internal clocks.

## Configuration

Pet Rock can be configured to use different pseudo-random rhythm-generating algorithms. To choose, edit
`config/PetRock.json` to set the `MOODS` key:
```json
{
"MOODS": "classic"
}
```

- `MOODS` can be one of `classic` (default), `alternate`, or `all`

Depending on the `MOODS` configured, the following algorithms are used, cycling every new moon:

**Classic**
- ![swords](./pet_rock-docs/swords.png) Plain
- ![cups](./pet_rock-docs/cups.png) Reich
- ![shields](./pet_rock-docs/shield.png) Sparse
- ![pentacles](./pet_rock-docs/pentacle.png) Vari

**Alternate**

These algorithms were implemented in the original Pet Rock firmware, but ultimately not used in the final
release.
- ![hearts](./pet_rock-docs/heart.png) Blocks
- ![spades](./pet_rock-docs/spade.png) Culture
- ![diamonds](./pet_rock-docs/diamond.png) Over
- ![clubs](./pet_rock-docs/wands.png) Wonk

**All**

When `"all"` moods are selected, the order is the 4 classic algorithms, followed by the 4 alternate algorithms,
in the order listed above.


### Note on suits

Yes, I'm aware that "shields" isn't a normal Tarot suit. Originally I used "clubs" (an alternative to the
traditional "wands" suit in most tarot decks). But it seemed weird having 3/4 English card suits used for
the alternate moods, and then have "shields" tossed-in to fill it out; "shields" is a suit more associated
with Swiss playing cards.

Since shields and swords are a natural pairing, I swapped things around to have, in my mind, more logical
groupings. I'm sorry if this decision causes anyone distress.

Additionally, I realize the "pentacle" symbol has some negative associations for some. No offense is meant;
this is a traditional suit in tarot cards, and felt appropriate for a moon-phase-tracking program. I did
consider swapping it for its "coins" alternative, but was concerned that a circular or elliptical coin
motif might be too visually similar to the full & gibbous moon icons.


## Required Hardware

This script requires a Realtime Clock (RTC) to EuroPi's secondary I2C header pins,
located on the underside of the board.

See [Realtime Clock Installation](/software/realtime_clock.md) for instructions on
installing and configuring the realtime clock.
Loading
Loading