Close

Calibration

A project log for TMD-3: Turing Machine Demonstrator Mark 3

They say that the third time's a charm. Let's find out.

michael-gardiMichael Gardi 09/10/2023 at 18:280 Comments

I finished the tedious but not too arduous task of calibrating the SS49E Linear Hall Effect Sensors. What does this mean. Well I run a small test program that I wrote that detects the presence of a tile and emits the location of the sensor and the value returned by reading that sensor. Here is the Python code.

import time
import busio
import digitalio
import board
import adafruit_mcp3xxx.mcp3008 as MCP
from adafruit_mcp3xxx.analog_in import AnalogIn
import pigpio

# Access the gpio pins.
GPIO = pigpio.pi()

# Select pins for the CD4067BE.
S0 = 23
S1 = 27  
S2 = 17
S3 = 18

# Selct pins are all OUTPUT.
GPIO.set_mode(S0, pigpio.OUTPUT)
GPIO.set_mode(S1, pigpio.OUTPUT)
GPIO.set_mode(S2, pigpio.OUTPUT)
GPIO.set_mode(S3, pigpio.OUTPUT)

# Select the C8 pin.
GPIO.write(S0, 0)
GPIO.write(S1, 0)
GPIO.write(S2, 0)
GPIO.write(S3, 0)

# Create the spi bus.
spi = busio.SPI(clock=board.SCK, MISO=board.MISO, MOSI=board.MOSI)

# Create the cs (chip select).
cs = digitalio.DigitalInOut(board.D22)

# Create the mcp object.
mcp = MCP.MCP3008(spi, cs)

# Create analog input channels.
chan0 = AnalogIn(mcp, MCP.P0)
chan1 = AnalogIn(mcp, MCP.P1)
chan2 = AnalogIn(mcp, MCP.P2)
chan3 = AnalogIn(mcp, MCP.P3)
chan4 = AnalogIn(mcp, MCP.P4)
chan5 = AnalogIn(mcp, MCP.P5)

def selectPin(pin):
    if pin & 0b00000001:
        GPIO.write(S0, 1)
    else:
        GPIO.write(S0, 0)
    if pin & 0b00000010:
        GPIO.write(S1, 1)
    else:
        GPIO.write(S1, 0)
    if pin & 0b00000100:
        GPIO.write(S2, 1)
    else:
        GPIO.write(S2, 0)
    if pin & 0b00001000:
        GPIO.write(S3, 1)
    else:
        GPIO.write(S3, 0)

# The hall effect sensor will read in the middle of the range
# someplace. Get the middle values for all 16 sensors.
mids0 = []
mids1 = []
mids2 = []
mids3 = []
mids4 = []
mids5 = []
for i in range(0,16):
    selectPin(i)
    time.sleep(0.01)
    mids0.append(chan0.value)
    mids1.append(chan1.value)
    mids2.append(chan2.value)
    mids3.append(chan3.value)
    mids4.append(chan4.value)
    mids5.append(chan5.value)
print(mids0)
print(mids1)
print(mids2)
print(mids3)
print(mids4)
print(mids5)

while True:

    for i in range(0,16):
        selectPin(i)
        time.sleep(0.01)
        val = chan0.value-mids0[i]
        if abs(val) > 200:
            print("C",i,"=",round(val/10))
        val = chan1.value-mids1[i]
        if abs(val) > 200:
            print("B",i,"=",round(val/10))
        val = chan2.value-mids2[i]
        if abs(val) > 200:
            print("A",i,"=",round(val/10))
        val = chan3.value-mids3[i]
        if abs(val) > 200:
            print("D",i,"=",round(val/10))
        val = chan4.value-mids4[i]
        if abs(val) > 200:
            print("E",i,"=",round(val/10))
        val = chan5.value-mids5[i]
        if abs(val) > 200:
            print("F",i,"=",round(val/10))
            
    time.sleep(0.5)

So for each of the 96 sensors I get the values for only those tiles that are valid for that position. So for instance the MOVE row only accepts L and R tiles. By only accepting valid row tiles there is some error checking, but because there are only 8 different magnet configurations, some tiles are interchangeable like the 2 and E tiles for instance.  I add those values to a table specific to the state being checked. Here is the table for the C state.

    # Add the valid tiles to each sensor along with the expected sensor value.
    # C
    
    # READ Row
    sensors[0][8]["tiles"] = [(-627,'b'), (282,'4')]  

    # WRITE Row
    sensors[0][4]["tiles"] = [(-435,'0'), (-301,'1'), (691,'2'), (429,'3'), (301,'4')]
    sensors[0][5]["tiles"] = [(-410,'0'), (-282,'1'), (640,'2'), (397,'3'), (275,'4')]
    sensors[0][6]["tiles"] = [(-384,'0'), (-262,'1'), (589,'2'), (365,'3'), (262,'4')]
    sensors[0][7]["tiles"] = [(-371,'0'), (-256,'1'), (576,'2'), (358,'3'), (256,'4')]
    sensors[0][9]["tiles"] = [(-358,'0'), (-250,'1'), (563,'2'), (352,'3'), (250,'4')]

    # MOVE Row
    sensors[0][0]["tiles"] = [(-205,'L'), (205,'R')]  
    sensors[0][1]["tiles"] = [(-192,'L'), (192,'R')]
    sensors[0][2]["tiles"] = [(-179,'L'), (179,'R')]
    sensors[0][3]["tiles"] = [(-179,'L'), (179,'R')]
    sensors[0][10]["tiles"] = [(-173,'L'), (173,'R')]

    # GOTO Row
    sensors[0][12]["tiles"] = [(-749,'A'), (-474,'B'), (-307,'C'), (-218,'D'), (723,'E'), (467,'F'), (320,'H')]
    sensors[0][13]["tiles"] = [(-653,'A'), (-422,'B'), (-275,'C'), (-198,'D'), (627,'E'), (410,'F'), (282,'H')]
    sensors[0][14]["tiles"] = [(-608,'A'), (-390,'B'), (-262,'C'), (-186,'D'), (595,'E'), (384,'F'), (269,'H')]
    sensors[0][15]["tiles"] = [(-602,'A'), (-384,'B'), (-250,'C'), (-179,'D'), (570,'E'), (384,'F'), (269,'H')]
    sensors[0][11]["tiles"] = [(-602,'A'), (-384,'B'), (-256,'C'), (-186,'D'), (557,'E'), (378,'F'), (256,'H')]

There will be one of these tables for each of the six states.

You might ask why is calibration necessary for all six states?  Well for sure you have to calibrate at least one state in the table to get the initial sensor readings for the tiles.  Once I had the values for my first state I tried using those reading for all of the other states as well. You know they worked pretty good but not perfectly. Despite my best efforts to be as consistent as possible, there is enough variance between different sensor readings that a some tiles could not be recognized or were incorrect. Why the variance? Well the sensors themselves while carefully soldered to the PCB would not be perfectly aligned.  The printed state panels when joined probably have some slight differences in height from the sensors. You get the idea.

With all six state panels calibrated the recognition fidelity is great. 

Discussions