Files
mixly3/boards/default/micropython_robot/build/lib/bot51.py
2024-07-19 10:16:00 +08:00

204 lines
10 KiB
Python

"""
BOT51
Micropython library for the BOT51(ADC*7, Motor*2*2, Matrix12x12, on/off)
=======================================================
#Preliminary composition 20230812
@dahanzimin From the Mixly Team
"""
import uframebuf
from micropython import const
_BOT51_ADDRESS = const(0x26)
_BOT5_ID = const(0x00)
_BOT51_VBAT = const(0x01)
_BOT51_PS = const(0x03)
_BOT51_ALS = const(0x07)
_BOT51_FLAG = const(0x0B)
_BOT51_MOTOR = const(0x0C)
_BOT51_LEDS = const(0x10)
class BOT51(uframebuf.FrameBuffer_Uincode):
def __init__(self, i2c_bus, address=_BOT51_ADDRESS, brightness=0.8, font_address=0x3A0000, width=12, height=12):
self._i2c= i2c_bus
self._addr = address
if self._rreg(_BOT5_ID) != 0x26:
raise AttributeError("Cannot find a BOT51")
self._buffer = bytearray((width + 7) // 8 * height)
super().__init__(self._buffer, width, height, uframebuf.MONO_HMSB)
self.reset()
self.font(font_address)
self.set_brightness(brightness)
def _wreg(self, reg, val):
'''Write memory address'''
self._i2c.writeto_mem(self._addr, reg, val.to_bytes(1, 'little'))
def _rreg(self, reg, nbytes=1):
'''Read memory address'''
self._i2c.writeto(self._addr, reg.to_bytes(1, 'little'))
return self._i2c.readfrom(self._addr, nbytes)[0] if nbytes<=1 else self._i2c.readfrom(self._addr, nbytes)[0:nbytes]
def reset(self):
"""Reset all registers to default state"""
for reg in range(_BOT51_MOTOR, _BOT51_MOTOR + 28):
self._wreg(reg, 0x00)
def shutdown(self, flag=True):
"""This function is only available on battery power"""
if flag:
self._wreg(_BOT51_FLAG, (self._rreg(_BOT51_FLAG)) & 0XBF)
else:
self._wreg(_BOT51_FLAG, (self._rreg(_BOT51_FLAG)) | 0X40)
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(_BOT51_FLAG, round(15 * brightness) | (self._rreg(_BOT51_FLAG)) & 0xF0)
def direction(self, angle=0):
'''set display direction '''
if not 0 <= angle <= 3:
raise ValueError("Direction must be a number in the range: 0~3")
self._wreg(_BOT51_FLAG, angle << 4 | (self._rreg(_BOT51_FLAG)) & 0xCF)
def show(self):
"""Refresh the display and show the changes."""
self._i2c.writeto_mem(self._addr, _BOT51_LEDS, self._buffer)
def pwm(self, index, duty=None):
"""Motor*2*2 PWM duty cycle data register"""
if not 0 <= index <= 3:
raise ValueError("Motor port must be a number in the range: 0~3")
if duty is None:
return self._rreg(_BOT51_MOTOR + index)
else:
if not 0 <= duty <= 255:
raise ValueError("Duty must be a number in the range: 0~255")
self._wreg(_BOT51_MOTOR + index, duty)
def motor(self, index, action, speed=0, atten=0.4):
speed = round(max(min(speed, 100), -100) * atten)
if action=="N":
self.pwm(index * 2, 0)
self.pwm(index * 2 + 1, 0)
elif action=="P":
self.pwm(index * 2, 255)
self.pwm(index * 2 + 1, 255)
elif action=="CW":
if speed >= 0:
self.pwm(index * 2, 0)
self.pwm(index * 2 + 1, speed * 255 // 100)
else:
self.pwm(index * 2, - speed * 255 // 100)
self.pwm(index * 2 + 1, 0)
elif action=="CCW":
if speed >= 0:
self.pwm(index * 2, speed * 255 // 100)
self.pwm(index * 2 + 1, 0)
else:
self.pwm(index * 2, 0)
self.pwm(index * 2 + 1, - speed * 255 // 100)
elif action=="NC":
return round(self.pwm(index * 2) * 100 / 255 / atten), round(self.pwm(index * 2 + 1) * 100 / 255 / atten)
else:
raise ValueError('Invalid input, valid are "N","P","CW","CCW"')
def move(self, action, speed=100, atten=0.4):
if action=="N":
self.motor(0, "N")
self.motor(1, "N")
elif action=="P":
self.motor(0, "P")
self.motor(1, "P")
elif action=="F":
self.motor(0, "CCW", speed, atten)
self.motor(1, "CW", speed, atten)
elif action=="B":
self.motor(0, "CW", speed, atten)
self.motor(1, "CCW", speed, atten)
elif action=="L":
self.motor(0, "CW", speed, atten)
self.motor(1, "CW", speed, atten)
elif action=="R":
self.motor(0, "CCW", speed, atten)
self.motor(1, "CCW", speed, atten)
else:
raise ValueError('Invalid input, valid are "N","P","F","B","L","R"')
def read_bat(self, ratio=5/1023):
'''Read battery power'''
vbat= self._rreg(_BOT51_VBAT) << 2 | self._rreg(_BOT51_VBAT + 1) >> 6
return round(vbat * ratio, 2)
def read_ps(self, index, ratio=100/1023):
'''Read proximity sensor values'''
if not 0 <= index <= 1:
raise ValueError("Proximity sensor port must be a number in the range: 0,1")
vps= self._rreg(_BOT51_PS + (1 - index) * 2) << 2 | self._rreg(_BOT51_PS + (1 - index) * 2 + 1) >> 6
return round(vps * ratio, 2)
def read_als(self, index, ratio=100/255):
'''Read light sensor values'''
if not 0 <= index <= 3:
raise ValueError("Light sensor port must be a number in the range: 0~3")
vals= self._rreg(_BOT51_ALS + index)
return round(vals * ratio)
"""Graph module"""
HEART_SMALL=b'\x00\x00\x00\x00\x88\x00\xdc\x01\xfe\x03\xfe\x03\xfc\x01\xf8\x00p\x00 \x00\x00\x00\x00\x00'
HEART=b'\x00\x00\x88\x00\xdc\x01\xfe\x03\xff\x07\xff\x07\xfe\x03\xfc\x01\xf8\x00p\x00 \x00\x00\x00'
HAPPY=b'\x00\x00\x00\x00\x01\x08\x01\x08\x01\x08\x00\x00\x00\x00\x04\x02\x08\x01\x90\x00`\x00\x00\x00'
SAD=b'\x00\x00\x00\x00\x07\x0e\x00\x00\x00\x00\x00\x00\x00\x00`\x00\x90\x00\x08\x01\x04\x02\x00\x00'
SMILE=b'\x00\x00\x00\x00\x01\x08\x03\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x03\x0c\x0e\x07\xf8\x01\x00\x00'
SILLY=b'\x00\x00\x00\x00\x01\x08\x02\x04\x01\x08\x00\x00\x00\x00\xf8\x01\x08\x01\x90\x00`\x00\x00\x00'
FABULOUS=b'\x00\x00\x01\x08\x02\x04\x03\x0c\x00\x00\x00\x00\xfe\x07\x02\x04\x02\x04\x04\x02\xf8\x01\x00\x00'
SURPRISED=b'`\x00`\x00`\x00`\x00`\x00`\x00`\x00`\x00\x00\x00`\x00`\x00\x00\x00'
ASLEEP=b'\x00\x00\x00\x00\x00\x00\x03\x0c\x00\x00\x00\x00\x00\x00\x00\x00\xf8\x01\x04\x02\xf8\x01\x00\x00'
ANGRY=b'\x00\x00\x08\x01\x04\x02\x02\x04\x01\x08\x00\x00\x00\x00`\x00\x90\x00\x08\x01\x04\x02\x02\x04'
CONFUSED=b'\xf0\x00\x98\x01\x0c\x03\x0c\x03\x00\x03\x80\x01\xc0\x00`\x00`\x00\x00\x00`\x00`\x00'
NO=b'\x00\x00\x00\x00\x04\x02\x08\x01\x90\x00`\x00`\x00\x90\x00\x08\x01\x04\x02\x00\x00\x00\x00'
YES=b'\x00\x00\x00\x00\x00\x0c\x00\x06\x00\x03\x80\x01\xc3\x00f\x00<\x00\x18\x00\x00\x00\x00\x00'
LEFT_ARROW=b'\x00\x00\x00\x00\x10\x00\x08\x00\x04\x00\xfa\x07\x04\x00\x08\x00\x10\x00\x00\x00\x00\x00\x00\x00'
RIGHT_ARROW=b'\x00\x00\x00\x00\x80\x00\x00\x01\x00\x02\xfe\x05\x00\x02\x00\x01\x80\x00\x00\x00\x00\x00\x00\x00'
DRESS=b'\x00\x00\x00\x00\x88\x00\xfc\x01\xd8\x00\x70\x00\xa8\x00\x54\x01\xaa\x02\xfc\x01\x00\x00\x00\x00'
TRANSFORMERS=b'\x00\x00\x40\x00\x40\x00\xb0\x01\x50\x01\x50\x01\x50\x01\xa0\x00\xa0\x00\x10\x01\x08\x02\x00\x00'
SCISSORS=b'\x00\x00\x01\x04\x03\x06\x06\x03\x8c\x01\xd8\x00\x70\x00\x20\x00\xdc\x01\x52\x02\x52\x02\x8c\x01'
EXIT=b'\x00\x00\xc0\x00\x40\x00\x70\x00\xe8\x00\x24\x03\x20\x00\x54\x00\x90\x00\x10\x01\x00\x02\x00\x00'
TREE=b'\x00\x00\x20\x00\x70\x00\xf8\x00\xfc\x01\xfe\x03\xff\x07\x20\x00\x20\x00\x20\x00\x20\x00\x00\x00'
PACMAN=b'\x00\x00\xfc\x01\x06\x02\x23\x01\x83\x00\x41\x00\x83\x00\x03\x01\x06\x02\xfc\x01\x00\x00\x00\x00'
TARGET=b'\x00\x00\x00\x00\xf0\x01\x08\x02\x08\x02\x48\x02\x08\x02\x08\x02\xf0\x01\x00\x00\x00\x00\x00\x00'
TSHIRT=b'\x10\x01\xe8\x02\x04\x04\x02\x08\x0c\x06\x08\x02\x08\x02\x08\x02\x08\x02\x08\x02\xf8\x03\x00\x00'
ROLLERSKATE=b'\x00\x00\x3e\x00\x22\x00\x22\x00\xe2\x03\x02\x04\x02\x04\xfe\x07\x05\x0a\x07\x0e\x00\x00\x00\x00'
DUCK=b'\x00\x00\x00\x00\x38\x00\x24\x00\x42\x00\x4f\x00\xc8\x0f\x08\x04\x08\x02\xf8\x01\x00\x00\x00\x00'
HOUSE=b'\x00\x00\xfc\x03\x06\x06\x03\x0c\xf4\x02\x94\x02\x94\x02\xb4\x02\x94\x02\x94\x02\x00\x00\x00\x00'
TORTOISE=b'\x00\x00\x60\x00\x68\x01\xf8\x01\xf8\x01\xf8\x01\xf8\x01\xf8\x01\xf8\x01\xf0\x00\x08\x01\x00\x00'
BUTTERFLY=b'\x00\x00\x06\x03\x89\x04\x51\x04\xfe\x03\x70\x00\xa8\x00\x74\x01\x8a\x02\x06\x03\x00\x00\x00\x00'
STICKFIGURE=b'\x20\x00\x50\x00\x50\x00\x20\x00\x20\x00\x70\x00\xa8\x00\x24\x01\x50\x00\x88\x00\x04\x01\x00\x00'
GHOST=b'\x00\x00\xf8\x00\xac\x01\xac\x01\xac\x01\xfc\x01\xac\x01\xac\x01\x54\x01\xfc\x03\xfc\x07\x00\x00'
PITCHFORK=b'\x00\x00\x00\x00\x00\x0f\x80\x00\x80\x00\xff\x0f\x80\x00\x80\x00\x00\x0f\x00\x00\x00\x00\x00\x00'
MUSIC_QUAVERS=b'\x00\x00\x00\x00\x00\x00\x00\x00\x8c\x01\xde\x03\x79\x0f\x30\x06\x00\x00\x00\x00\x00\x00\x00\x00'
MUSIC_QUAVER=b'\xc0\x00\x40\x01\x40\x01\x40\x01\x40\x01\x40\x00\x40\x00\x58\x00\x7c\x00\x7e\x00\x3e\x00\x1c\x00'
MUSIC_CROTCHET=b'\x40\x00\x40\x00\x40\x00\x40\x00\x40\x00\x40\x00\x40\x00\x58\x00\x7c\x00\x7e\x00\x3e\x00\x1c\x00'
COW=b'\x00\x00\x00\x00\x08\x00\x1e\x00\xfa\x03\xfe\x07\xfe\x0b\x18\x0b\x18\x03\x18\x03\x18\x03\x00\x00'
RABBIT=b'\x50\x00\xa8\x00\xa8\x00\xa8\x00\xa8\x00\x8c\x01\x04\x01\x54\x01\x04\x01\x24\x01\x74\x01\xf8\x00'
SQUARE_SMALL=b'\x00\x00\x00\x00\x00\x00\x00\x00\xf0\x00\x90\x00\x90\x00\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00'
SQUARE=b'\xff\x0f\x01\x08\x01\x08\x01\x08\x01\x08\x01\x08\x01\x08\x01\x08\x01\x08\x01\x08\x01\x08\xff\x0f'
DIAMOND_SMALL=b'\x00\x00\x00\x00\x00\x00\x00\x00\x70\x00\xa8\x00\x50\x00\x20\x00\x00\x00\x00\x00\x00\x00\x00\x00'
DIAMOND=b'\x00\x00\x00\x00\xfc\x01\x56\x03\xfb\x06\x76\x03\xac\x01\xd8\x00\x70\x00\x20\x00\x00\x00\x00\x00'
CHESSBOARD=b'\x00\x00\x00\x00\xff\x07\x55\x05\xff\x07\x55\x05\xff\x07\x55\x05\xff\x07\x00\x00\x00\x00\x00\x00'
TRIANGLE_LEFT=b'\x00\x00\x00\x00\x80\x00\xc0\x00\xe0\x00\xf0\x00\xe0\x00\xc0\x00\x80\x00\x00\x00\x00\x00\x00\x00'
TRIANGLE=b'\x00\x00\x00\x00\x00\x00\x60\x00\xf0\x00\xf8\x01\xfc\x03\xfe\x07\xff\x0f\x00\x00\x00\x00\x00\x00'
SNAKE=b'\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x0a\x00\x0e\xf0\x03\xf8\x03\x1c\x00\x0e\x00\x00\x00\x00\x00'
UMBRELLA=b'\x00\x00\x00\x00\x20\x00\xf8\x00\xfc\x01\xfe\x03\x20\x00\x20\x00\x20\x00\xa0\x00\x40\x00\x00\x00'
SKULL=b'\x00\x00\x00\x00\xf8\x00\x24\x01\x22\x02\xde\x03\xfc\x01\x88\x00\x88\x00\xa8\x00\x70\x00\x00\x00'
GIRAFFE=b'\xc0\x00\xc0\x01\x40\x00\x40\x00\x40\x00\x40\x00\x40\x00\x40\x00\x78\x00\x48\x00\x48\x00\x48\x00'
SWORD=b'\x00\x00\x00\x00\x00\x00\x04\x00\x0c\x00\xff\x07\xff\x07\x0c\x00\x04\x00\x00\x00\x00\x00\x00\x00'