Skip to content

Instantly share code, notes, and snippets.

@BjoernSchilberg
Last active May 30, 2022 21:52
Show Gist options
  • Save BjoernSchilberg/533b555eaf9e6c5b699c4e2b77467acf to your computer and use it in GitHub Desktop.
Save BjoernSchilberg/533b555eaf9e6c5b699c4e2b77467acf to your computer and use it in GitHub Desktop.
M5 Atom

M5 Atom Matrix

MicroPython

Flash MicroPython.

https://micropython.org/download/M5STACK_ATOM/

Use esptool from https://github.com/espressif/esptool/releases.

esptool --chip esp32 --port /dev/ttyUSB0 erase_flash
esptool --chip esp32 --port /dev/ttyUSB0 --baud 115200 write_flash -z 0x1000 /tmp/M5STACK_ATOM-20220117-v1.18.bin

MicroPython Examples

Put MicroPython files on device via https://github.com/scientifichackers/ampy.

ampy --port /dev/ttyUSB0 --delay 3 put boot.py
ampy --port /dev/ttyUSB0 --delay 3 put mpu6886.py

MicroPython in Docker for testing

WASM

.NET nanoFramework

Links

from mpu6886 import MPU6886
from neopixel import NeoPixel
from machine import Pin, SoftI2C
from time import sleep
from math import *
# Definitions for the ATOM Matrix
LED_GPIO = const(27)
MPU6886_SCL = const(21)
MPU6886_SDA = const(25)
matrix_size_x = const(5)
matrix_size_y = const(5)
is_atom_matrix = True
# Definitions for the M5StickC + NeoFlash hat
#LED_GPIO = const(26)
#MPU6886_SCL = const(22)
#MPU6886_SDA = const(21)
#matrix_size_x = const(18)
#matrix_size_y = const(7)
#is_atom_matrix = False
threshold = const(5) # minimum angle that triggers movement
color = (0, 0, 20) # Initial color: Blue
x = int(matrix_size_x / 2) # Get matrix
y = int(matrix_size_y / 2) # center
np = NeoPixel(Pin(LED_GPIO), matrix_size_x * matrix_size_y)
def computeAngles(ax,ay,az):
# https://roboticsclubiitk.github.io/2017/12/21/Beginners-Guide-to-IMU.html
pitch = 180 * atan (ax/sqrt(ay**2 + az**2))/ pi
roll = 180 * atan (ay/sqrt(ax**2 + az**2))/ pi
yaw = 180 * atan (az/sqrt(ax**2 + ay**2))/ pi
return pitch, roll, yaw
def updateDot(p, angle, size, threshold, color1, color2):
global color
if angle > threshold: # Test if angle is positive
if p < size - 1: # If it is not at the matrix
p = p + 1 # border, move the dot
else:
color = color1 # change color if reached the border
elif angle < - threshold: # Test if angle is negative
if p > 0: # If it is not at the matrix
p = p - 1 # border, move the dot
else:
color = color2 # change color if reached the border
return p
# I2C bus init for MPU6886
i2c = SoftI2C(scl=Pin(MPU6886_SCL), sda=Pin(MPU6886_SDA))
# Values you can use to initialize the accelerometer. AFS_16G, means +-8G sensitivity, and so on
# Larger scale means less precision
AFS_2G = const(0x00)
AFS_4G = const(0x01)
AFS_8G = const(0x02)
AFS_16G = const(0x03)
# Values you can use to initialize the gyroscope. GFS_2000DPS means 2000 degrees per second sensitivity, and so on
# Larger scale means less precision
GFS_250DPS = const(0x00)
GFS_500DPS = const(0x01)
GFS_1000DPS = const(0x02)
GFS_2000DPS = const(0x03)
# by default, if you initialize MPU6886 with imu = MPU6886(i2c), GFS_2000DPS and AFS_8G are used
# if you want to initialize with other values you have too use :
# imu = MPU6886(i2c,mpu6886.GFS_250DPS,mpu6886.AFS_4G )
# imu = MPU6886(i2c) #=> use default 8G / 2000DPS
imu = MPU6886(i2c, GFS_500DPS, AFS_4G)
# in order to calibrate Gyroscope you have to put the device on a flat surface
# preferably level with the floor and not touch it during the procedure. (1s for 20 cycles)
#calibrateGyro(20)
while True:
ax,ay,az = imu.getAccelData()
# gx,gy,gz = imu.getGyroData()
pitch, roll, yaw = computeAngles(ax,ay,az)
# print(ax,ay,az)
# print(pitch, roll, yaw)
np[ y * matrix_size_x + x ] = (0,0,0) # Turn off the LED on the current dot possition
if is_atom_matrix:
x = updateDot(x, pitch, matrix_size_x, threshold, (20, 0, 0), (20, 20, 0))
y = updateDot(y, roll, matrix_size_y, threshold, (20, 0, 20), (0, 20, 20))
else: # Compensate for the different orientation of the sensor on the M5StickC
x = updateDot(x, -roll, matrix_size_x, threshold, (20, 0, 0), (20, 20, 0))
y = updateDot(y, pitch, matrix_size_y, threshold, (20, 0, 20), (0, 20, 20))
np[ y * matrix_size_x + x ] = color # Turn on the LED on the new dot possition
np.write()
sleep(0.2)
# This file is executed on every boot (including wake-boot from deepsleep)
#import esp
#esp.osdebug(None)
#import webrepl
#webrepl.start()
# C.LEBOCQ 02/2020
# MicroPython library for the MPU6886 imu ( M5StickC / ATOM Matrix )
# Based on https://github.com/m5stack/M5StickC/blob/master/src/utility/MPU6886.cpp
from machine import I2C
from time import sleep
MPU6886_ADDRESS = const(0x68)
MPU6886_WHOAMI = const(0x75)
MPU6886_ACCEL_INTEL_CTRL = const(0x69)
MPU6886_SMPLRT_DIV = const(0x19)
MPU6886_INT_PIN_CFG = const(0x37)
MPU6886_INT_ENABLE = const(0x38)
MPU6886_ACCEL_XOUT_H = const(0x3B)
MPU6886_ACCEL_XOUT_L = const(0x3C)
MPU6886_ACCEL_YOUT_H = const(0x3D)
MPU6886_ACCEL_YOUT_L = const(0x3E)
MPU6886_ACCEL_ZOUT_H = const(0x3F)
MPU6886_ACCEL_ZOUT_L = const(0x40)
MPU6886_TEMP_OUT_H = const(0x41)
MPU6886_TEMP_OUT_L = const(0x42)
MPU6886_GYRO_XOUT_H = const(0x43)
MPU6886_GYRO_XOUT_L = const(0x44)
MPU6886_GYRO_YOUT_H = const(0x45)
MPU6886_GYRO_YOUT_L = const(0x46)
MPU6886_GYRO_ZOUT_H = const(0x47)
MPU6886_GYRO_ZOUT_L = const(0x48)
MPU6886_USER_CTRL = const(0x6A)
MPU6886_PWR_MGMT_1 = const(0x6B)
MPU6886_PWR_MGMT_2 = const(0x6C)
MPU6886_CONFIG = const(0x1A)
MPU6886_GYRO_CONFIG = const(0x1B)
MPU6886_ACCEL_CONFIG = const(0x1C)
MPU6886_ACCEL_CONFIG2 = const(0x1D)
MPU6886_FIFO_EN = const(0x23)
#consts for Acceleration & Resolution scale
AFS_2G = const(0x00)
AFS_4G = const(0x01)
AFS_8G = const(0x02)
AFS_16G = const(0x03)
GFS_250DPS = const(0x00)
GFS_500DPS = const(0x01)
GFS_1000DPS = const(0x02)
GFS_2000DPS = const(0x03)
class MPU6886():
def __init__(self, i2c, Gscale = GFS_2000DPS, Ascale = AFS_8G):
self.i2c = i2c
self.Gscale = Gscale
self.Ascale = Ascale
if self.init():
self.setAccelFsr(Ascale)
self.setGyroFsr(Gscale)
# sleep in ms
def sleepms(self,n):
sleep(n / 1000)
# set I2C reg (1 byte)
def setReg(self, reg, dat):
self.i2c.writeto(MPU6886_ADDRESS, bytearray([reg, dat]))
# get I2C reg (1 byte)
def getReg(self, reg):
self.i2c.writeto(MPU6886_ADDRESS, bytearray([reg]))
t = self.i2c.readfrom(MPU6886_ADDRESS, 1)
return t[0]
# get n reg
def getnReg(self, reg, n):
self.i2c.writeto(MPU6886_ADDRESS, bytearray([reg]))
t = self.i2c.readfrom(MPU6886_ADDRESS, n)
return t
def init(self):
tempdata = self.getReg(MPU6886_WHOAMI)
if tempdata != 0x19:
return False
self.sleepms(1)
regdata = 0x00
self.setReg(MPU6886_PWR_MGMT_1, regdata)
self.sleepms(10)
regdata = (0x01<<7)
self.setReg(MPU6886_PWR_MGMT_1, regdata)
self.sleepms(10)
regdata = (0x01<<0)
self.setReg(MPU6886_PWR_MGMT_1, regdata)
self.sleepms(10)
regdata = 0x10
self.setReg(MPU6886_ACCEL_CONFIG, regdata)
self.sleepms(1)
regdata = 0x18
self.setReg(MPU6886_GYRO_CONFIG, regdata)
self.sleepms(1)
regdata = 0x01
self.setReg(MPU6886_CONFIG, regdata)
self.sleepms(1)
regdata = 0x05
self.setReg(MPU6886_SMPLRT_DIV, regdata)
self.sleepms(1)
regdata = 0x00
self.setReg(MPU6886_INT_ENABLE, regdata)
self.sleepms(1)
regdata = 0x00
self.setReg(MPU6886_ACCEL_CONFIG2, regdata)
self.sleepms(1)
regdata = 0x00
self.setReg(MPU6886_USER_CTRL, regdata)
self.sleepms(1)
regdata = 0x00
self.setReg(MPU6886_FIFO_EN, regdata)
self.sleepms(1)
regdata = 0x22
self.setReg(MPU6886_INT_PIN_CFG, regdata)
self.sleepms(1)
regdata = 0x01
self.setReg(MPU6886_INT_ENABLE, regdata)
self.sleepms(100)
self.getGres()
self.getAres()
return True
def getGres(self):
if self.Gscale == GFS_250DPS:
self.gRes = 250.0 / 32768.0
elif self.Gscale == GFS_500DPS:
self.gRes = 500.0/32768.0
elif self.Gscale == GFS_1000DPS:
self.gRes = 1000.0/32768.0
elif self.Gscale == GFS_2000DPS:
self.gRes = 2000.0/32768.0
else:
self.gRes = 250.0/32768.0
def getAres(self):
if self.Ascale == AFS_2G:
self.aRes = 2.0/32768.0
elif self.Ascale == AFS_4G:
self.aRes = 4.0/32768.0
elif self.Ascale == AFS_8G:
self.aRes = 8.0/32768.0
elif self.Ascale == AFS_16G:
self.aRes = 16.0/32768.0
else:
self.aRes = 2.0/32768.0
def getAccelAdc(self):
buf = self.getnReg(MPU6886_ACCEL_XOUT_H,6)
ax = (buf[0]<<8) | buf[1]
ay = (buf[2]<<8) | buf[3]
az = (buf[4]<<8) | buf[5]
return ax,ay,az
def getAccelData(self):
ax,ay,az = self.getAccelAdc()
if ax > 32768:
ax -= 65536
if ay > 32768:
ay -= 65536
if az > 32768:
az -= 65536
ax *= self.aRes
ay *= self.aRes
az *= self.aRes
return ax,ay,az
def getGyroAdc(self):
buf = self.getnReg(MPU6886_GYRO_XOUT_H,6)
gx = (buf[0]<<8) | buf[1]
gy = (buf[2]<<8) | buf[3]
gz = (buf[4]<<8) | buf[5]
return gx,gy,gz
def getGyroData(self):
gx,gy,gz = self.getGyroAdc()
if gx > 32768:
gx -= 65536
if gy > 32768:
gy -= 65536
if gz > 32768:
gz -= 65536
gx *= self.gRes
gy *= self.gRes
gz *= self.gRes
return gx, gy, gz
def getTempAdc(self):
buf = self.getnReg(MPU6886_TEMP_OUT_H,2)
return (buf[0]<<8) | buf[1]
def getTempData(self):
return self.getTempAdc() / 326.8 + 25.0
def setGyroFsr(self,scale):
regdata = (scale<<3)
self.setReg(MPU6886_GYRO_CONFIG, regdata)
self.sleepms(10)
self.Gscale = scale
self.getGres()
def setAccelFsr(self,scale):
regdata = (scale<<3)
self.setReg(MPU6886_ACCEL_CONFIG, regdata)
self.sleepms(10)
self.Ascale = scale
self.getAres()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment