Skip to content

Commit

Permalink
update to v0.8
Browse files Browse the repository at this point in the history
  • Loading branch information
echo-lalia committed Mar 7, 2024
1 parent da92a9a commit 60131cc
Show file tree
Hide file tree
Showing 8 changed files with 912 additions and 258 deletions.
298 changes: 103 additions & 195 deletions ports/esp32/boards/MICROHYDRA/launcher/launcher.py

Large diffs are not rendered by default.

194 changes: 194 additions & 0 deletions ports/esp32/boards/MICROHYDRA/lib/M5Sound.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
"""
/*
* ----------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42 modified):
* <[email protected]> wrote this file. As long as you retain this notice and
* my credit somewhere you can do whatever you want with this stuff. If we
* meet some day, and you think this stuff is worth it, you can buy me a beer
* in return.
* ----------------------------------------------------------------------------
*/
"""

from machine import Pin, I2S
import time

_PERIODS = ( # c0 thru a0 - how much to advance a sample pointer per frame for each note
b'\x01\x00\x00\x00\x00\x00\x00\x00',
b'\x01\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00',
b'\x01\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00',
b'\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00',
b'\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00',
b'\x01\x00\x00\x00\x00\x00',
b'\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00',
b'\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00',
b'\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00',
b'\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00',
b'\x01\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00',
b'\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00'
)

_MIN_VOLUME = const(0)
_MAX_VOLUME = const(15)
@micropython.native
def _volume(volume):
return 15 - (0 if volume < 0 else 15 if volume > 15 else volume)

@micropython.viper
def _vipmod(a:uint, b:uint) -> uint:
while a >= b:
a -= b
return a

class Register:
def __init__(self, buf_start=0, sample=None, sample_len=0, pointer=0, note=0, period=1, period_mult=4, loop=False, volume=0):
self.buf_start = buf_start
self.sample = sample
self.pointer = pointer
self.period = period
self.note = note
self.period_mult = period_mult
self.sample_len = sample_len
self.loop = loop
self.volume = volume

def copy(self):
registers = Register()
registers.buf_start = self.buf_start
registers.sample = self.sample
registers.pointer = self.pointer
registers.period = self.period
registers.note = self.note
registers.period_mult = self.period_mult
registers.sample_len = self.sample_len
registers.loop = self.loop
registers.volume = self.volume
return registers

def __str__(self):
return f"{self.buf_start}: {self.sample} v:{self.volume} n:{self.note}"

class M5Sound:
def __init__(self, buf_size=2048, rate=11025, channels=4, sck=41, ws=43, sd=42):
self._output = I2S(
1,
sck=Pin(sck),
ws=Pin(ws),
sd=Pin(sd),
mode=I2S.TX,
bits=16,
format=I2S.STEREO,
rate=rate,
ibuf=buf_size
)

self._rate = rate
self._buf_size:int = buf_size
self._buffer = bytearray(buf_size*2) # twice for stereo
self.channels = channels
self._registers = [Register() for _ in range(channels)]
self._queues = [[] for _ in range(channels)]
self._last_tick = 0
self._output.irq(self._process_buffer)
self._process_buffer(None)

def __del__(self):
self._output.deinit()

# @micropython.native
def _gen_buf_start(self):
return int((time.ticks_diff(time.ticks_us(), self._last_tick) // (1000000 / self._rate)) * 2) # stereo

# @micropython.native
def play(self, sample, note=0, octave=4, volume=15, channel=0, loop=False):
registers = Register(
buf_start = self._gen_buf_start(),
sample = sample,
sample_len = len(sample) // 2,
loop = loop,
note = note % 12,
period_mult = 2 ** (octave + (note // 12)),
volume = volume
)
self._queues[channel].append(registers)

# @micropython.native
def stop(self, channel=0):
registers = Register() # default has empty sample
registers.buf_start = self._gen_buf_start()
self._queues[channel].append(registers)

# @micropython.native
def setvolume(self, volume, channel=0):
if len(self._queues[channel]) > 0:
registers = self._queues[channel][-1].copy()
else:
registers = self._registers[channel].copy()
registers.buf_start = self._gen_buf_start()
registers.volume = volume
self._queues[channel].append(registers)

@micropython.viper
def _clear_buffer(self):
buf = ptr16(self._buffer)
for i in range(0, int(self._buf_size)):
buf[i] = 0

@micropython.viper
def _fill_buffer(self, registers, end:int) -> bool:
buf = ptr16(self._buffer)
start = 1 + int(registers.buf_start)
smp = ptr16(registers.sample)
slen = uint(registers.sample_len)
ptr = uint(registers.pointer)
per = ptr8(_PERIODS[registers.note])
perlen = uint(len(_PERIODS[registers.note]))
perptr = uint(registers.period)
permult = int(registers.period_mult)
vol = uint(_volume(registers.volume))
loop = bool(registers.loop)
for i in range(start, end, 2): # odd word: right channel only
if ptr >= slen: # sample ended
if not loop: # stop playing
return False
ptr = uint(_vipmod(ptr, slen)) # or loop
bsmp = smp[ptr]
# ladies and gentlemen, the two's complement
bsmp = (bsmp & 0b1000000000000000) | ((bsmp & 0b0111111111111111) >> vol)
if (bsmp & 0b1000000000000000) != 0:
bsmp |= (0b111111111111111 << 15-vol)
buf[i] += bsmp
for _ in range(permult): # add together frame periods for different octaves
ptr += per[perptr]
perptr += uint(1)
if perptr >= perlen:
perptr = uint(0)
registers.buf_start = 0
registers.pointer = ptr
registers.period = perptr
return True

# @micropython.native
def _process_buffer(self, arg):
self._last_tick = time.ticks_us()
self._output.write(self._buffer)
self._clear_buffer()

for ch in range(0, int(self.channels)):
playing = True
while playing:
registers = self._registers[ch]

end = self._buf_size
if len(self._queues[ch]) > 0:
if self._queues[ch][0].buf_start >= self._buf_size:
self._queues[ch][0].buf_start -= self._buf_size
else:
end = self._queues[ch][0].buf_start
self._registers[ch] = self._queues[ch].pop(0)
else:
playing = False

if registers.sample:
if not self._fill_buffer(registers, end):
registers.sample = None
80 changes: 80 additions & 0 deletions ports/esp32/boards/MICROHYDRA/lib/battlevel.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import machine



# CONSTANTS:
# vbat has a voltage divider of 1/2
_MIN_VALUE = const(1575000) # 3.15v
_MAX_VALUE = const(2100000) # 4.2v

_LOW_THRESH = const(_MIN_VALUE + ((_MAX_VALUE - _MIN_VALUE) // 3))
_HIGH_THRESH = const(_LOW_THRESH + ((_MAX_VALUE - _MIN_VALUE) // 3))



# CLASS Battery:
class Battery:
def __init__(self):
#init the ADC for the battery
self.adc = machine.ADC(10)
self.adc.atten(machine.ADC.ATTN_11DB) # needed to get apropriate range

def read_pct(self):
"""
Return an approximate battery level as a percentage
"""
raw_value = self.adc.read_uv()

if raw_value <= _MIN_VALUE:
return 0
elif raw_value >= _MAX_VALUE:
return 100

delta_value = raw_value - _MIN_VALUE # shift range down
delta_max = _MAX_VALUE - _MIN_VALUE # shift range down
pct_value = int((delta_value / delta_max) * 100)
return (pct_value)

def read_level(self):
"""
Read approx battery level on the adc and return as int range 0 (low) to 3 (high)
This is reccomended, as the readings are not very accurate,
and a percentage could therefore be misleading.
"""
raw_value = self.adc.read_uv()
if raw_value < _MIN_VALUE:
return 0
if raw_value < _LOW_THRESH:
return 1
if raw_value < _HIGH_THRESH:
return 2
return 3

if __name__ == "__main__":
from lib import st7789fbuf, keyboard
from lib import microhydra as mh
from launcher.icons import battery
import time
from font import vga2_16x32 as font
from machine import SPI, Pin, PWM, reset, ADC

tft = st7789fbuf.ST7789(
SPI(1, baudrate=40000000, sck=Pin(36), mosi=Pin(35), miso=None),
135,
240,
reset=Pin(33, Pin.OUT),
cs=Pin(37, Pin.OUT),
dc=Pin(34, Pin.OUT),
backlight=Pin(38, Pin.OUT),
rotation=1,
color_order=st7789fbuf.BGR
)
batt = Battery()

while True:
time.sleep(1)
tft.fill(0)
tft.bitmap_text(font, f"Batt level: {batt.read_level()}", 10,10, 65535)
tft.bitmap_text(font, f"pct: {batt.read_pct()}%", 10,50, 65535)
tft.show()

34 changes: 16 additions & 18 deletions ports/esp32/boards/MICROHYDRA/lib/beeper.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
from machine import I2S, Pin
import math

fast_sin_len = const(320)
fast_sin_hz = const(25)
SCK_PIN = const(41)
WS_PIN = const(43)
SD_PIN = const(42)
I2S_ID = const(1)
BUFFER_LENGTH_IN_BYTES = const(2100)
SAMPLE_SIZE_IN_BITS = const(16)
FORMAT = I2S.STEREO
SAMPLE_RATE_IN_HZ = const(16000)
_SCK_PIN = const(41)
_WS_PIN = const(43)
_SD_PIN = const(42)
_I2S_ID = const(1)
_BUFFER_LENGTH_IN_BYTES = const(2048)
_SAMPLE_SIZE_IN_BITS = const(16)
_FORMAT = I2S.STEREO
_SAMPLE_RATE_IN_HZ = const(16000)

volume_map = {0:1,1:4,2:10,3:16,4:20,5:28,6:36,7:50,8:60,9:80,10:127}
tone_map = {
Expand Down Expand Up @@ -56,15 +54,15 @@ class Beeper:
def __init__(self, buf_size=4000):

self._output = I2S(
I2S_ID,
sck=Pin(SCK_PIN),
ws=Pin(WS_PIN),
sd=Pin(SD_PIN),
_I2S_ID,
sck=Pin(_SCK_PIN),
ws=Pin(_WS_PIN),
sd=Pin(_SD_PIN),
mode=I2S.TX,
bits=SAMPLE_SIZE_IN_BITS,
format=FORMAT,
rate=SAMPLE_RATE_IN_HZ,
ibuf=BUFFER_LENGTH_IN_BYTES)
bits=_SAMPLE_SIZE_IN_BITS,
format=_FORMAT,
rate=_SAMPLE_RATE_IN_HZ,
ibuf=_BUFFER_LENGTH_IN_BYTES)

self._current_notes = []

Expand Down
Loading

0 comments on commit 60131cc

Please sign in to comment.