I try to use Pimoroni’s 6-axis LSM303D with Pi pico (Thonny micropython).
The module comes with drivers for RP 2,3,3+,zero etc. but not micropython driver.
Works well with arduino nano as well.
I tried to rewrite the i2cdevice lib to match machine.I2C but it does not seem that easy.
Would appreciate any help.
Thanks! :)
It looks like there is Circuit Python support, if that helps?
LSM303D (Accelerometer & Compass) · Issue #17 · pimoroni/BreakoutGarden (github.com)
I use “pimoroni-pico-v0.2.2-micropython-v1.15.uf2” because my project utilizes both Pimoroni display and Pimoroni LSM303D. I assume the suggested circuit python by adafruit requires another uf2 file that would not fit the display.
if I misunderstand something about this env, you are welcome to shed some light
Yes, if you use the Adafruit Circuit Python uf2, you will have to find Circuit Python libraries for your other hardware. It’s what I had to do with my Pico RGB Keypad Base. The Adafruit Circuit Python has USB HID support but Micro Python doesn’t.
You could also try our MicroPython version with the Blinka compatibility layer baked in, that may let you use the LSM303D CircuitPython drivers from within MicroPython?
Well, I put some effort into it but obviously I’m missing something.
Using Thonny, I uploaded PlatformDetect and Blinka onto my pico, clearing some unused directories from blinka. then got some error about missing adafruit_pioasm.
I have adafruit_pioasm.mpy file, but apparently this env requires .py and not .mpy
There looks to be a .py version of adafruit_pioasm in the .py driver bundle at Libraries ?
It’s possible that not all breakouts will work using the Blinka route though, I think it depends on how the hardware is addressed.
I managed to control the accelarator without a library driver.
My original goal was to use Pimoroni display with LSM303D together.
I was trying to combine micropython and circuit python altogether with blinka and adafruit lib and pimoroni uf2, and everything got mixed up.
Eventually I took the direct approach and opened the LSM303D datasheet. Enabling the accelerator requires only one I2C command.
It works.
The below example is like a levelling app. you can stick the accelerator behind Pimoroni display, connect LSM303D I2C to pico pins 1-2. (not sure I use I2C most efficiently)
you get a leveler bubble moving on display as you tilt it.
from machine import Pin, I2C
import time
import picodisplay as display
# Initialise display with a bytearray display buffer
xx = display.get_width()
yy = display.get_height()
buf = bytearray(xx * (yy+10) * 2) ## add 10 lines to avoid bottom line corruption
display.init(buf)
display.set_backlight(0.5)
## SET I2C bus - using pico pins 1-2
sda=machine.Pin(0)
scl=machine.Pin(1)
i2c=machine.I2C(0,sda=sda, scl=scl, freq=100000)
## Enable LSM303D accelerator at I2C addr 0x1D
config=bytearray(1)
config[0]=39+8
## Reg Control1: (0x20) 50Hz+enable (0x57) + block update
i2c.writeto_mem(29, 32, config)
def get_axis(reg): ## 40 - X , 42 - Y , 44 - Z
high=bytearray(1)
low=bytearray(1)
i2c.readfrom_mem_into(29, reg, low)
i2c.readfrom_mem_into(29, reg+1, high)
res = high[0] * 256 + low[0]
if (res<16384):
result = res/16384.0
elif (res>=16384 and res<49152):
result = (32768-res)/16384.0
else:
result = (res-65536)/16384.0
return result
while True:
display.set_pen(160, 160, 25)
display.clear()
x = ( get_axis(40) + 1) * xx / 2.0
y = (-get_axis(42) + 1) * yy / 2.0
display.set_pen(60,60,65)
display.circle(int(x), int(y), 12)
display.set_pen(180,180,200)
display.circle(int(x), int(y), 9)
display.update()
time.sleep(0.05)
@Yos this is great - I’ve got it working (most of the time - had to put a delay in)
Can you explain this?
if (res<16384):
result = res/16384.0
elif (res>=16384 and res<49152):
result = (32768-res)/16384.0
else:
result = (res-65536)/16384.0
Im having trouble reproducing the solution in processing.org , as well trying to access the magnetometer data(to figure out direction/angle) but im keep getting None as a result does anyone has an idea?
from machine import UART, Pin, I2C
import time
import sys
TOF_length = 16
TOF_header=(87,0,255)
TOF_system_time = 0
TOF_distance = 0
TOF_status = 0
TOF_signal = 0
TOF_check = 0
uart0 = UART(0, baudrate=921600 , tx=Pin(16), rx=Pin(17)) # 921600 speed if errors appear
## SET I2C bus - using pico pins 1-2 | 1, scl=machine.Pin(3), sda=machine.Pin(2)
sda=machine.Pin(2)
scl=machine.Pin(3)
i2c=machine.I2C(1,sda=sda, scl=scl, freq=100000)
## Enable LSM303D accelerator at I2C addr 0x1D
config=bytearray(1)
config[0]=39+8
## Reg Control1: (0x20) 50Hz+enable (0x57) + block update
i2c.writeto_mem(29, 32, config)
config=bytearray(1)
config[0]=7+8
# LSM303DLHC Mag address, 0x1E(30)
# Select MR register, 0x02(02)
# 0x00(00) Continous conversion mode
i2c.writeto_mem(29, 34, config)
def get_axis(reg): ## 40 - X , 42 - Y , 44 - Z
high=bytearray(1)
low=bytearray(1)
i2c.readfrom_mem_into(29, reg, low)
i2c.readfrom_mem_into(29, reg+1, high)
res = high[0] * 256 + low[0]
if (res<16384):
result = res/16384.0
elif (res>=16384 & res<49152):
result = (32768-res)/16384.0
else:
result = (res-65536)/16384.0
return result
def verifyCheckSum(data, len):
#print(data)
TOF_check = 0
for k in range(0,len-1):
TOF_check += data[k]
TOF_check=TOF_check%256
if(TOF_check == data[len-1]):
##print("TOF data is ok!")
return 1
else:
##print("TOF data is error!")
return 0
def getMag(reg):
high=bytearray(1)
low=bytearray(1)
# LSM303DLHC Mag address, 0x1E(30)
# Read data back from 0x03(03), 2 bytes
# X-Axis Mag MSB, X-Axis Mag LSB
##data0 = bus.read_byte_data(0x1E, 0x03)
##data1 = bus.read_byte_data(0x1E, 0x04)
i2c.readfrom_mem_into(29, reg, low)
i2c.readfrom_mem_into(29, reg+1, high)
# Convert the data
xMag = high[0] * 256 + low[0]
#xMag = data0 * 256 + data1
if xMag > 32767 :
xMag -= 65536
# LSM303DLHC Mag address, 0x1E(30)
# Read data back from 0x05(05), 2 bytes
# Y-Axis Mag MSB, Y-Axis Mag LSB
##data0 = bus.read_byte_data(0x1E, 0x07)
##data1 = bus.read_byte_data(0x1E, 0x08)
i2c.readfrom_mem_into(29, reg, low)
i2c.readfrom_mem_into(29, reg+1, high)
# Convert the data
yMag = high[0] * 256 + low[0]
if yMag > 32767 :
yMag -= 65536
# LSM303DLHC Mag address, 0x1E(30)
# Read data back from 0x07(07), 2 bytes
# Z-Axis Mag MSB, Z-Axis Mag LSB
##data0 = bus.read_byte_data(0x1E, 0x05)
##data1 = bus.read_byte_data(0x1E, 0x06)
i2c.readfrom_mem_into(29, reg, low)
i2c.readfrom_mem_into(29, reg+1, high)
# Convert the data
#zMag = data0 * 256 + data1
zMag = high[0] * 256 + low[0]
if zMag > 32767 :
zMag -= 65536
while True:
time.sleep(0.1)
#magx = getMag(10)
#print(magx)
x = ( get_axis(40) + 1) #xx is displayscreen * xx / 2.0
y = (-get_axis(42) + 1) #yy is displayscreen * yy / 2.0
z = (-get_axis(44) + 1)
##print("accel:",f'{x:.3f}',",",f'{y:.3f}',",",f'{z:.3f}')
TOF_data=()
if(uart0.any()>=32):
try:
for i in range(0,16):
TOF_data=TOF_data+(ord(uart0.read(1)),ord(uart0.read(1)))
#print(TOF_data)
for j in range(0,16):
if((TOF_data[j]==TOF_header[0] and TOF_data[j+1]==TOF_header[1] and TOF_data[j+2]==TOF_header[2]) and (verifyCheckSum(TOF_data[j:TOF_length],TOF_length))):
if(((TOF_data[j+12]) | (TOF_data[j+13]<<8) )==0):
print("Out of range!")
else:
#print("TOF id is: "+ str(TOF_data[j+3]))
TOF_system_time = TOF_data[j+4] | TOF_data[j+5]<<8 | TOF_data[j+6]<<16 | TOF_data[j+7]<<24;
#print("TOF system time is: "+str(TOF_system_time)+'ms')
TOF_distance = (TOF_data[j+8]) | (TOF_data[j+9]<<8) | (TOF_data[j+10]<<16);
##print("TOF distance is: "+str(TOF_distance)+'mm')
TOF_status = TOF_data[j+11];
#print("TOF status is: "+str(TOF_status))
TOF_signal = TOF_data[j+12] | TOF_data[j+13]<<8;
#print("TOF signal is: "+str(TOF_signal))
break
print("(",str(TOF_distance),",",f'{x:.3f}',",",f'{y:.3f}',",",f'{z:.3f}',")")
except:
print("TOF fail")
##output is (distanceinMM,x,y,z) xyz accel
Took me a while to figure it out posting it here for others
edited: here is a clean version https://github.com/staberas/LSM303D-Pico-Micropython
you only need the i2c initialisation (under # LSM303D Mag address) and the functions getMagX , getMagY , getMagZ , and the heading variable calculation at the end of the script
from machine import UART, Pin, I2C
import time
import sys
from math import atan2, pi, asin, cos, sin
TOF_length = 16
TOF_header=(87,0,255)
TOF_system_time = 0
TOF_distance = 0
TOF_status = 0
TOF_signal = 0
TOF_check = 0
xMag = 0
yMag = 0
zMag = 0
heading=0
uart0 = UART(0, baudrate=921600 , tx=Pin(16), rx=Pin(17)) # 921600 speed if errors appear
## SET I2C bus - using pico pins 1-2 | 1, scl=machine.Pin(3), sda=machine.Pin(2)
sda=machine.Pin(2)
scl=machine.Pin(3)
i2c=machine.I2C(1,sda=sda, scl=scl, freq=100000)
## Enable LSM303D accelerator at I2C addr 0x1D => 0x29
config=bytearray(1)
config[0]=39+8
## Reg Control1: (0x20) 50Hz+enable (0x57) + block update
i2c.writeto_mem(29, 32, config)
config=bytearray(1)
config[0]=0
# LSM303D Mag address,
# 0x00(00) Continous conversion mode
config[0]=0
i2c.writeto_mem(29, 34, config)
config[0]=0
i2c.writeto_mem(29, 35, config)
config[0]=100
i2c.writeto_mem(29, 36, config)
config[0]=32
i2c.writeto_mem(29, 37, config)
config[0]=0
i2c.writeto_mem(29, 38, config)
def get_axis(reg): ## 40 - X , 42 - Y , 44 - Z
high=bytearray(1)
low=bytearray(1)
i2c.readfrom_mem_into(29, reg, low)
i2c.readfrom_mem_into(29, reg+1, high)
res = high[0] * 256 + low[0]
if (res<16384):
result = res/16384.0
elif (res>=16384 & res<49152):
result = (32768-res)/16384.0
else:
result = (res-65536)/16384.0
return result
def verifyCheckSum(data, len):
#print(data)
TOF_check = 0
for k in range(0,len-1):
TOF_check += data[k]
TOF_check=TOF_check%256
if(TOF_check == data[len-1]):
##print("TOF data is ok!")
return 1
else:
##print("TOF data is error!")
return 0
def getMagX():
high=bytearray(1)
low=bytearray(1)
# LSM303DLHC Mag address, 0x1E(30)
# Read data back from 0x03(03), 2 bytes
# X-Axis Mag MSB, X-Axis Mag LSB
i2c.readfrom_mem_into(29, 8, low)
i2c.readfrom_mem_into(29, 9, high)
# Convert the data
xMag = high[0] * 256 + low[0]
#xMag = data0 * 256 + data1
if xMag > 32767 :
xMag -= 65536
return xMag
def getMagY():
high=bytearray(1)
low=bytearray(1)
# LSM303DLHC Mag address, 0x1E(30)
# Read data back from 0x05(05), 2 bytes
# Y-Axis Mag MSB, Y-Axis Mag LSB
i2c.readfrom_mem_into(29, 10, low)
i2c.readfrom_mem_into(29, 11, high)
# Convert the data
yMag = high[0] * 256 + low[0]
if yMag > 32767 :
yMag -= 65536
return yMag
def getMagZ():
high=bytearray(1)
low=bytearray(1)
# LSM303DLHC Mag address, 0x1E(30)
# Read data back from 0x07(07), 2 bytes
# Z-Axis Mag MSB, Z-Axis Mag LSB
i2c.readfrom_mem_into(29, 12, low)
i2c.readfrom_mem_into(29, 13, high)
# Convert the data
#zMag = data0 * 256 + data1
zMag = high[0] * 256 + low[0]
if zMag > 32767 :
zMag -= 65536
return zMag
while True:
time.sleep(0.1)
xMag = getMagX()
yMag = getMagY()
zMag = getMagZ()
x = ( get_axis(40) + 1) #xx is displayscreen * xx / 2.0
y = (-get_axis(42) + 1) #yy is displayscreen * yy / 2.0
z = (-get_axis(44) + 1)
TOF_data=()
if(uart0.any()>=32):
try:
for i in range(0,16):
TOF_data=TOF_data+(ord(uart0.read(1)),ord(uart0.read(1)))
#print(TOF_data)
for j in range(0,16):
if((TOF_data[j]==TOF_header[0] and TOF_data[j+1]==TOF_header[1] and TOF_data[j+2]==TOF_header[2]) and (verifyCheckSum(TOF_data[j:TOF_length],TOF_length))):
if(((TOF_data[j+12]) | (TOF_data[j+13]<<8) )==0):
print("Out of range!")
else:
#print("TOF id is: "+ str(TOF_data[j+3]))
TOF_system_time = TOF_data[j+4] | TOF_data[j+5]<<8 | TOF_data[j+6]<<16 | TOF_data[j+7]<<24;
#print("TOF system time is: "+str(TOF_system_time)+'ms')
TOF_distance = (TOF_data[j+8]) | (TOF_data[j+9]<<8) | (TOF_data[j+10]<<16);
##print("TOF distance is: "+str(TOF_distance)+'mm')
TOF_status = TOF_data[j+11];
#print("TOF status is: "+str(TOF_status))
TOF_signal = TOF_data[j+12] | TOF_data[j+13]<<8;
#print("TOF signal is: "+str(TOF_signal))
break
#heading
heading = 180*atan2(yMag,xMag)/pi #assume pitch, roll are 0
if(heading < 0):
heading += 360
#head Tilt calculates a tilt-compensated heading.
#A float between 0 and 360 degrees is returned. You need
#to pass this function both a magneto and acceleration array.
#float pitch = asin(-accelValue[X]);
#float roll = asin(accelValue[Y] / cos(pitch));
print(" ",str(TOF_distance),",",f'{x:.3f}',",",f'{y:.3f}',",",f'{z:.3f}',",",xMag,",",yMag,",",zMag,",",heading," ")
except:
print("TOF fail")