Matik.Org

Tricking out motors with rotary encoders - AS5600 and micropython

I want to build a 2-axis antenna tracker. I probably want to use a motor other than a servo, such as a stepper motor. I would want to know where my antenna is pointing at, even after a power outage. Absolute position encoders are perfect for that usecase. Less useful for a stepper motor perhaps, especially combined with end switches, but it’s still nice to have one. For general robotics applications, they are quite necessary.

These absolute position encoders used to be entirely optical with gray codes and such painted on a rotating disk. They did not have a great resolution, so it’s nice to see sub-$10 magnetic encoders popping up these days. There’s the presumably older, 12 bit AS5600, and the more newfangled 14-bit AS5048 (A or B variant, depending on whether you need SPI or I2C). They work with an array of hall sensors in the AS5600 chip - you place it directly above the center of a diametrically magnetized disk magnet (that is presumably mounted on the axle of a stepper motor) and by interpreting the changes in the magnetic field and some magic, the sensor figures the correct angle.

I just have the 12-bit AS5600, which gives an approximately 0.1 degree resolution, plenty for what I have in mind.

The AS5600 (and the AS5048 as well) have two ways reading the sensor:

  1. a PWM output

  2. a digital output, either i2c or SPI.

I tried the I2C output on the AS5600. I also recently got a m5stack MATRIX ESP32 module, so I tried this out at the same time. There are ways to temporarily or permanently programming the AS5600 chip, which I ignored for my tests - it seemed to work OK in the default configuration.

Interim update

I did not manage to upload my regular, self-compiled ESP32 micropython onto the m5stack MATRIX. I believe there are some electrical issues with the reset pin (I should add a capacitor between gnd and reset, I suppose, but I didn’t know that). After flailing around a while, I tried to install the loboris version and even that didn’t work until I reduced flashing speed to 115200 baud. In retrospect, this probably would have worked for the other micropython firmware versions too.

Because of the non-standard micropython version, some libraries were unfamiliar, e.g. the Neopixel library.

However, like the chump I am, I hacked at various samples and made the simple script below. It measures the angle evyry second, and it seems to work.

I noted that I couldn’t find AS5600 or AS5048 drivers - if there is interest, leave a comment - I could check in a driver to github, it seems rather straightforward.

rotary encoder in operation with M5Stack Matrix detail of LEGO mount detail of LEGO mount

# testing AS5600 rotary encoder
# using loboris micropython implementation at https://github.com/loboris/MicroPython_ESP32_psRAM_LoBo
# I had initial trouble flashing other micropython firmware.
# This means that e.g. the Neopixel implementation is not the standard micropython one, keep this in mind.
# datasheet:
# https://ams.com/documents/20143/36005/AS5600_DS000365_5-00.pdf

from machine import Neopixel, Pin, I2C
from time import sleep

# Definitions for the ATOM Matrix
LED_GPIO = const(27)
ATOM_PIXELS = const(25)

# AS5600 specific
AS5600_SCL = const(19)
AS5600_SDA = const(22)
AS5600_ADDRESS = const(0x36)   # AS5600 has a fixed address (so can only use one per I2C bus?)
ANGLE_H	= const(0x0E)          # Angle register (high byte)
ANGLE_L	= const(0x0F)          # Angle register (low byte)

def getnReg(reg, n):
    i2c.writeto(AS5600_ADDRESS, bytearray([reg]))
    t =	i2c.readfrom(AS5600_ADDRESS, n)
    return t    


def getAngle():
    buf = getnReg(ANGLE_H, 2)
    return ((buf[0]<<8) | buf[1])/ 4096.0*360


np = Neopixel(Pin(LED_GPIO), 25)

# I2C bus init for ATOM Matrix MPU6886
i2c = I2C(scl=Pin(AS5600_SCL), sda=Pin(AS5600_SDA))

np.brightness(10)
i=0
while True:
    print(i, getAngle())
    
    # Blinkenlights
    # NB: this should cycle through the whole matrix and light each pixel green in turn
    # But it seems to adress only the second half of the pixels, and in the wrong color
    np.set(i, 0) 
    i=(i+1) % ATOM_PIXELS
    np.set(i, Neopixel.GREEN)
    
    sleep(1)

Notes:

  1. The pins on the AS5600 module I got are godawful close together, and I’m a terrible solder-man. Even with AWG30 wires, I spent 30 minutes cursing, getting high on flux. The PWM solder pads are much bigger, so if that’s an option and you have no PhD in soldering, you should seriously consider PWM.

  2. You need diametrically magnetized magnets. Most disc magnets you buy are axially magnetized, so the two ends are north and south pole, respectively. You want the version where each end piece has both a north and south side. I bought mine at K&J magnetics.

  3. The M5Stack system seems really nice. I get that they make some money on the specific boards, but the modules with an ESP32 are dirt cheap (like $9 for a 5x5 neopixel ESP32), and the form factors make working with them fun. Flashing firmware though, especially on MacOS, could me improved.

  4. LEGO is your friend. My magnet just fit into a rotating LEGO piece ‘just so’, and I could build a snug nest for the sensor to try it out. BTW, M5Stack (no relation/affiliation) even has LEGO compatible servos etc.

  5. I would prefer the AS5048 over the AS5600. It’s a bit more expensive, but one can daisychain multiple AS5048s, whereas there can be only one AS5600 per I2C bus (they all have a hardcoded address, 0x36).

Written on August 31, 2020