204 lines
10 KiB
Python
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'
|
|
|