Files
mixly3/boards/default/micropython_esp32c2/build/lib/mini_bot.py

425 lines
15 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
MINI_WCH
Micropython library for the MINI_WCH(TOUCH*2, MIC*1, Buzzer*1, PWM*2, Matrix8x12, HID)
=======================================================
@dahanzimin From the Mixly Team
"""
import time, math
from esp import flash_read
from micropython import const
from framebuf import FrameBuffer, MONO_VLSB
_BOT035_ADDRESS = const(0x13)
_BOT5_TOUCH = const(0x01)
_BOT035_MIC = const(0x05)
_BOT035_SPK = const(0x07)
_BOT035_PWM = const(0x0B)
_BOT035_FLAG = const(0x0F)
_BOT035_LEDS = const(0x10)
_BOT035_KB = const(0x1C)
_BOT035_MS = const(0x20)
_BOT035_STR = const(0x24)
_BOT035_STA = const(0x25)
_BOT035_PCM = const(0x26)
_FONT_W = const(5)
_FONT_H = const(8)
_LEDS_W = const(12)
_LEDS_H = const(8)
_FONT5x8_CODE = const(b'\x05\x08\x00\x00\x00\x00\x00\x00\x00_\x00\x00\x00\x07\x00\x07\x00\x14\x7f\x14\x7f\x14$*\x7f*\x12#\x13\x08db6IV P\x00\x08\x07\x03\x00\x00\x1c"A\x00\x00A"\x1c\x00*\x1c\x7f\x1c*\x08\x08>\x08\x08\x00\x80p0\x00\x08\x08\x08\x08\x08\x00\x00``\x00 \x10\x08\x04\x02>QIE>\x00B\x7f@\x00rIIIF!AIM3\x18\x14\x12\x7f\x10\'EEE9<JII1A!\x11\t\x076III6FII)\x1e\x00\x00\x14\x00\x00\x00@4\x00\x00\x00\x08\x14"A\x14\x14\x14\x14\x14\x00A"\x14\x08\x02\x01Y\t\x06>A]YN|\x12\x11\x12|\x7fIII6>AAA"\x7fAAA>\x7fIIIA\x7f\t\t\t\x01>AAQs\x7f\x08\x08\x08\x7f\x00A\x7fA\x00 @A?\x01\x7f\x08\x14"A\x7f@@@@\x7f\x02\x1c\x02\x7f\x7f\x04\x08\x10\x7f>AAA>\x7f\t\t\t\x06>AQ!^\x7f\t\x19)F&III2\x03\x01\x7f\x01\x03?@@@?\x1f @ \x1f?@8@?c\x14\x08\x14c\x03\x04x\x04\x03aYIMC\x00\x7fAAA\x02\x04\x08\x10 \x00AAA\x7f\x04\x02\x01\x02\x04@@@@@\x00\x03\x07\x08\x00 TTx@\x7f(DD88DDD(8DD(\x7f8TTT\x18\x00\x08~\t\x02\x18\xa4\xa4\x9cx\x7f\x08\x04\x04x\x00D}@\x00 @@=\x00\x7f\x10(D\x00\x00A\x7f@\x00|\x04x\x04x|\x08\x04\x04x8DDD8\xfc\x18$$\x18\x18$$\x18\xfc|\x08\x04\x04\x08HTTT$\x04\x04?D$<@@ |\x1c @ \x1c<@0@<D(\x10(DL\x90\x90\x90|DdTLD\x00\x086A\x00\x00\x00w\x00\x00\x00A6\x08\x00\x02\x01\x02\x04\x02<&#&<')
_Uincode_ADDR = const(0x3A0000)
class BOT035(FrameBuffer):
def __init__(self, i2c_bus, brightness=0.8):
self._i2c = i2c_bus
self._way = 2
self._speed = 100
self._buffer = bytearray(12)
self._brightness = brightness
self._touchs = [self.touch(0), self.touch(1)]
super().__init__(self._buffer, _LEDS_W, _LEDS_H, MONO_VLSB)
self.version()
self.reset()
self.show()
def _chardata(self, ch):
'''Ascall code font reading data'''
if 0x20 <= ord(ch) <= 0x7f:
char_index = 2 + (ord(ch)-32) * _FONT_W
return _FONT5x8_CODE[char_index : char_index + _FONT_W]
else:
raise ValueError("Cannot display characters other than ASCLL code")
def _uincode(self, ch):
'''uincode code font reading data'''
uni = ord(ch)
if 0x20 <= uni <= 0x2642 :
_address = 0x28 + (uni - 0x20) * 4
elif 0x3001 <= uni <= 0x9fa0 :
_address = 0x98b4 + (uni - 0x3001) * 4
elif 0xff01 <= uni <= 0xffe5 :
_address = 0x25734 + (uni - 0xff01) * 4
else:
raise ValueError("Cannot display characters other than GB2312 code")
buffer = bytearray(4)
flash_read(_Uincode_ADDR + _address, buffer)
font_info = buffer[3] << 24 | buffer[2] << 16 | buffer[1] << 8 | buffer[0]
font_address = font_info & 0x3FFFFFF
font_width = font_info >> 26
buffer = bytearray(12 * (font_width // 8 + 1))
flash_read(_Uincode_ADDR + font_address, buffer)
return buffer, font_width, 12
def shift(self, x, y, rotate=False):
"""Shift pixels by x and y"""
if x > 0: # Shift Right
for _ in range(x):
for row in range(0, _LEDS_H):
last_pixel = self.pixel(_LEDS_W - 1, row) if rotate else 0
for col in range(_LEDS_W - 1, 0, -1):
self.pixel(col, row, self.pixel(col - 1, row))
self.pixel(0, row, last_pixel)
elif x < 0: # Shift Left
for _ in range(-x):
for row in range(0, _LEDS_H):
last_pixel = self.pixel(0, row) if rotate else 0
for col in range(0, _LEDS_W - 1):
self.pixel(col, row, self.pixel(col + 1, row))
self.pixel(_LEDS_W - 1, row, last_pixel)
if y > 0: # Shift Up
for _ in range(y):
for col in range(0, _LEDS_W):
last_pixel = self.pixel(col, _LEDS_H - 1) if rotate else 0
for row in range(_LEDS_H - 1, 0, -1):
self.pixel(col, row, self.pixel(col, row - 1))
self.pixel(col, 0, last_pixel)
elif y < 0: # Shift Down
for _ in range(-y):
for col in range(0, _LEDS_W):
last_pixel = self.pixel(col, 0) if rotate else 0
for row in range(0, _LEDS_H - 1):
self.pixel(col, row, self.pixel(col, row + 1))
self.pixel(col, _LEDS_H - 1, last_pixel)
self.show()
def shift_right(self, num, rotate=False):
"""Shift all pixels right"""
self.shift(num, 0, rotate)
def shift_left(self, num, rotate=False):
"""Shift all pixels left"""
self.shift(-num, 0, rotate)
def shift_up(self, num, rotate=False):
"""Shift all pixels up"""
self.shift(0, -num, rotate)
def shift_down(self, num, rotate=False):
"""Shift all pixels down"""
self.shift(0, num, rotate)
def map_invert(self, own):
"""Graph invert operation"""
result = bytearray()
for i in range(len(own)):
result.append(~ own[i])
return result
def map_add(self, own, other):
"""Graph union operation"""
result = bytearray()
for i in range(min(len(own), len(other))):
result.append(own[i] | other[i])
return result
def map_sub(self, own, other):
"""Graphic subtraction operation"""
result = bytearray()
for i in range(min(len(own), len(other))):
result.append((own[i] ^ other[i]) & own[i])
return result
def set_buffer(self, buffer):
for i in range(len(buffer)):
self._buffer[i] = self._buffer[i] | buffer[i]
def _ascall_bitmap(self, data, x=0 ,space=0):
for char in data:
buf = self._chardata(char)
if -_FONT_W <= x <= _LEDS_W:
for _x in range(_FONT_W):
for _y in range(_FONT_H):
if (buf[_x] >> _y) & 0x1:
self.pixel(x + _x, _y, 1)
x = _FONT_W + x + space
self.show()
def _uincode_scroll(self, buffer, space):
_len = 0
for buf in buffer:
_len = _len + space + (buf[2] if self._way <= 1 else buf[1])
for i in range(_len - space + (_LEDS_W if self._way <= 1 else _LEDS_H)):
_step = (_LEDS_W - 1 if self._way <= 1 else _LEDS_H) - i
self.fill(0)
for buf in buffer:
_buf, width, high = buf
_xx = (_LEDS_W - width) // 2
if -high < _step < _LEDS_W:
for _y in range(12):
for _x in range(width):
if _buf[_y * ((width + 7) // 8) + _x // 8] & (0x80 >> (_x & 7)):
if self._way == 0:
self.pixel( _LEDS_W - (_xx + _x) - 2, _LEDS_H - (_step + _y), 1)
elif self._way == 1:
self.pixel(_xx + _x + 1, _step + _y, 1)
elif self._way == 2:
self.pixel(_y, _LEDS_H - (_step + _x), 1)
elif self._way == 3:
self.pixel(_LEDS_W - _y - 1, (_step + _x), 1)
_step = _step + space + (high if self._way <= 1 else width)
self.show()
time.sleep_ms(self._speed)
def _gb2312_scroll(self, data, space):
for char in data:
if ord(char) >= 0xFF:
font_buffer = []
for c in data:
_buffer = self._uincode(c)
font_buffer.append(_buffer)
self._uincode_scroll(font_buffer, space)
return True
def scroll_way(self, way=1, speed=None):
"""0,1竖2,3横"""
self._way = way % 4
if speed is not None:
self._speed = speed
def shows(self, data, space=1, center=True):
"""Display character"""
if data is not None:
self.fill(0)
if type(data) in [bytes, bytearray]:
self.set_buffer(data)
self.show()
else:
data = str(data)
if not self._gb2312_scroll(data, space):
x = (_LEDS_W - len(data) * (_FONT_W + space) + space) // 2 if center else 0
self._ascall_bitmap(data, x)
def frame(self, data, delay=500):
"""Display one frame per character"""
if data is not None:
data = str(data)
for char in data:
self.fill(0)
if ord(char) >= 0xFF:
_way = self._way
self._way = 1
self._uincode_scroll([self._uincode(char)], 0)
self._way = _way
else:
self._ascall_bitmap([char], (_LEDS_W - _FONT_W) // 2)
time.sleep_ms(delay)
def scroll(self, data, space=0, speed=None):
"""Scrolling characters"""
if speed is not None:
self._speed = speed
if data is not None:
data = str(data)
if not self._gb2312_scroll(data, space):
str_len = len(data) * (_FONT_W + space) - space
for i in range(str_len + _LEDS_W + 1):
x = _LEDS_W - i
self.fill(0)
self._ascall_bitmap(data, x)
time.sleep_ms(self._speed)
def pointern(self, x=_LEDS_W // 2, y=_LEDS_H // 2, l=_LEDS_H // 2, angle=0):
radian = math.radians(angle)
self.fill(0)
self.line(x, y, round(x + l * math.sin(radian)), round(y - l * math.cos(radian)), 1)
self.show()
def _wreg(self, reg, val):
'''Write memory address'''
self._i2c.writeto_mem(_BOT035_ADDRESS, reg, val.to_bytes(1, 'little'))
def _rreg(self, reg, nbytes=1):
'''Read memory address'''
self._i2c.writeto(_BOT035_ADDRESS, reg.to_bytes(1, 'little'))
return self._i2c.readfrom(_BOT035_ADDRESS, nbytes)[0] if nbytes <= 1 else self._i2c.readfrom(_BOT035_ADDRESS, nbytes)
def version(self):
_ver = self._rreg(0x00)
if _ver == 0x26:
self._version = 0
return "v1.7", "Only support CDC serial port"
elif _ver == 0x27:
self._version = 1
return "v2.5", "Composite devices (CDC, Keyboard and Mouse)"
elif _ver == 0x28:
self._version = 2
return "v2.9", "Composite devices (CDC, HID, WEBUSB, Keyboard and Mouse)"
elif _ver == 0x29:
self._version = 3
return "v3.0", "Composite devices (CDC, HID, WEBUSB, Keyboard and Mouse), Support PCM collection for MIC"
else:
return "vx.x", "Unknown, awaiting update"
def reset(self):
"""Reset SPK, PWM, HID registers to default state"""
self._i2c.writeto_mem(_BOT035_ADDRESS, _BOT035_SPK, b'\x0A\x00\x00\x00\x20\x4E\x64\x64\x28')
if self._version: self._i2c.writeto_mem(_BOT035_ADDRESS, _BOT035_KB, bytes(10))
def get_brightness(self):
return self._brightness
def set_brightness(self, brightness):
if not 0.0 <= brightness <= 1.0:
raise ValueError("Brightness must be a decimal number in the range: 0.0-1.0")
self._brightness = brightness
self._wreg(_BOT035_FLAG, (self._rreg(_BOT035_FLAG) & 0x0F) | round(10 * brightness))
def show(self):
self._i2c.writeto_mem(_BOT035_ADDRESS, _BOT035_LEDS, self._buffer)
def buzzer(self, duty=None, freq=None):
if duty is not None:
duty = max(min(duty, 100), 0)
self._wreg(_BOT035_SPK + 2, int(duty))
if freq is not None:
freq = max(min(freq, 65535), 10)
self._wreg(_BOT035_SPK, freq & 0xFF)
self._wreg(_BOT035_SPK + 1, freq >> 8)
if freq is None and duty is None:
return self._rreg(_BOT035_SPK + 2), self._rreg(_BOT035_SPK) | self._rreg(_BOT035_SPK + 1) << 8
def usben(self, index=1, duty=None, freq=None):
index = max(min(index, 2), 1) - 1
if duty is not None:
duty = max(min(duty, 100), 0)
self._wreg(_BOT035_PWM + index + 2, int(duty))
if freq is not None:
freq = max(min(freq, 65535), 10)
self._wreg(_BOT035_PWM, freq & 0xFF)
self._wreg(_BOT035_PWM + 1, freq >> 8)
if freq is None and duty is None:
return self._rreg(_BOT035_PWM + index + 2), self._rreg(_BOT035_PWM) | self._rreg(_BOT035_PWM + 1) << 8
def touch(self, index, value=None):
index = max(min(index, 1), 0)
touch = 4095 - (self._rreg(_BOT5_TOUCH + index * 2) | self._rreg(_BOT5_TOUCH + index * 2 + 1) << 8)
return touch > value if value else touch
def touched(self, index, value=600):
return self.touch(index, value)
def touch_slide(self):
values = []
for i in range(30):
values.append((self.touch(1) - self._touchs[1]) - (self.touch(0) - self._touchs[0]))
return round(sorted(values)[15] / 10)
def soundlevel(self):
values = []
for i in range(50):
values.append(self._rreg(_BOT035_MIC) | self._rreg(_BOT035_MIC + 1) << 8)
values = sorted(values)
return values[-10] - values[10]
def hid_keyboard(self, special=0, general=0, release=True):
if self._version:
self._buf = bytearray(4)
self._buf[0] = special
if type(general) in (tuple, list):
for i in range(len(general)):
if i > 2: break
self._buf[i + 1] = general[i]
else:
self._buf[1] = general
self._i2c.writeto_mem(_BOT035_ADDRESS, _BOT035_KB, self._buf)
if release:
time.sleep_ms(10)
self._i2c.writeto_mem(_BOT035_ADDRESS, _BOT035_KB, bytes(4))
else:
print("Warning: Please upgrade the coprocessor firmware to use this feature")
def hid_keyboard_str(self, string, delay=0):
if self._version:
for char in str(string):
self._wreg(_BOT035_STR, ord(char) & 0xFF)
time.sleep_ms(20 + delay)
else:
print("Warning: Please upgrade the coprocessor firmware to use this feature")
def hid_keyboard_state(self):
state = self._rreg(_BOT035_STA)
return bool(state & 0x10), bool(state & 0x20), bool(state & 0x40)
def hid_mouse(self, keys=0, move=(0, 0), wheel=0, release=True):
if self._version:
self._i2c.writeto_mem(_BOT035_ADDRESS, _BOT035_MS, bytes([keys & 0x0F, move[0] & 0xFF, move[1] & 0xFF, wheel & 0xFF]))
if release:
time.sleep_ms(10)
self._i2c.writeto_mem(_BOT035_ADDRESS, _BOT035_MS, bytes(4))
else:
print("Warning: Please upgrade the coprocessor firmware to use this feature")
def mic_pga(self, value=None):
'''0:4x, 1:8x, 2:16x, 3:32x, 4:OFF'''
if value is None:
return self._rreg(_BOT035_FLAG) >> 4
else:
self._wreg(_BOT035_FLAG, (self._rreg(_BOT035_FLAG) & 0x0F) | (value & 0x03) << 4)
def pcm_en(self, power=None):
if self._version >= 3:
if power is None:
return bool(self._rreg(_BOT035_STA) & 0x08)
else:
self._wreg(_BOT035_STA, (self._rreg(_BOT035_STA) & 0xF7) | (power & 0x01) << 3)
else:
print("Warning: Please upgrade the coprocessor firmware to use this feature")
def pcm_any(self):
return bool(self._rreg(_BOT035_STA) & 0x80)
def pcm_read(self, ibuf=1600):
return self._rreg(_BOT035_PCM, ibuf)
def pcm_record(self, path='mixly.wav', seconds=5, ibuf=1600, timeout=2000):
if self._version >= 3:
self.pcm_en(True)
_star = time.ticks_ms()
_size = int(ibuf * seconds * 10)
_header = b'RIFF' + (36 + _size).to_bytes(4, 'little') + b'WAVEfmt \x10\x00\x00\x00\x01\x00\x01\x00' + (ibuf * 5).to_bytes(4, 'little') + (ibuf * 10).to_bytes(4, 'little') + b'\x02\x00\x10\x00data' + _size.to_bytes(4, 'little')
with open(path, 'wb') as f:
f.write(_header)
while _size > 0:
if self.pcm_any():
f.write(self.pcm_read(ibuf))
_size -= ibuf
_star = time.ticks_ms()
if time.ticks_diff(time.ticks_ms(), _star) > timeout:
raise OSError("Timeout write error")
self.pcm_en(False)
else:
print("Warning: Please upgrade the coprocessor firmware to use this feature")
"""Graph module"""
HEART =b'\x00\x0c\x1e?~\xfc~?\x1e\x0c\x00\x00'
HEART_SMALL =b'\x00\x00\x0c\x1e<x<\x1e\x0c\x00\x00\x00'
HAPPY =b'\x00\x06\x06\x10 @@ \x10\x06\x06\x00'
SAD =b'\x04\x02\x02B \x10\x10 B\x02\x02\x04'
SMILE =b'\x04\x02\x02$@\x80\x80@$\x02\x02\x04'
ANGRY =b'\x01\x02\x84B!\x10\x10!B\x84\x02\x01'
NO =b'\x00\x00\x00B$\x18\x18$B\x00\x00\x00'
YES =b'\x00\x00\x10 @@ \x10\x08\x04\x02\x00'
DOOR_OPEN =b'\x00\x00\xfe\xfd\x01\x01\x01\x01\x01\xfe\x00\x00'
DOOR_OPENING =b'\x00\x00\xfe\x03\x03\x15\xf9\x01\x01\xfe\x00\x00'
DOOR_CLOSE =b'\x00\x00\xfe\x03\x03\x03\x13\x13\xff\xfe\x00\x00'