An absolute magnetic angle sensor can report where a magnet points without knowing how many turns it made. Connect an exact AS5600 module to a Pico, rotate a magnet slowly across the zero boundary, then deliberately remove it or create an observation gap. The useful result is a conditional path with a visible validity boundary, rather than a turn counter that silently reconnects unrelated measurements.

Angle and accumulated rotation are different information

The AS5600 observes a magnetic field and returns a twelve-bit angle around one circle. Twelve bits give 4096 possible codes. One code corresponds nominally to 360/4096 = 0.087890625 degrees. This is resolution, the digital spacing between codes, not a promise of that angular accuracy. Magnet alignment, field strength, mounting and device errors still matter.

An absolute angle is a position within one turn. Accumulated rotation adds changes over time, and therefore depends on observations between positions. If a magnet returns to code 100 after one complete revolution, the two endpoint codes are indistinguishable from a stationary magnet. No subtraction rule can recover a turn that was never observed. A program needs a physical motion bound and an uninterrupted observation history before interpreting its sum as travelled angle.

This lesson uses an original Raspberry Pi Pico RP2040 with MicroPython 1.26.0 and Adafruit AS5600 module product 6357. The module is an identified board with a regulator and bus support parts, rather than an unspecified magnetic-encoder board. The manufacturer document linked by the current board supplier is ams AS5600 version 1.06, June 20, 2018. The older direct ams document endpoint was unavailable during review; the supplier-hosted primary datasheet is identified explicitly instead of claiming a newly verified silicon revision.

Exact pins, supply and magnet

The AS5600 is an eight-lead small-outline integrated circuit (SOIC). Its functions are pin 1 VDD5V, 2 VDD3V3, 3 analog or pulse-width-modulated OUT, 4 ground, 5 program-option PGO, 6 serial data SDA, 7 serial clock SCL and 8 direction DIR. The bare device has distinct documented supply configurations: 4.5–5.5 V in five-volt mode, or 3.0–3.6 V in three-volt mode. Absolute ratings are stress limits, not alternative supply instructions.

We use the finished module's labelled VIN, ground, SDA and SCL connections. Its power circuitry provides the selected chip supply configuration. Keep all factory regulator, bypass and level-shifting parts in place. The board has 10 kΩ pull-ups on both bus lines and a DIR jumper tied to ground by default, selecting its documented clockwise direction. Leave OUT and PGO unconnected. Do not cut DIR or operate any programming pads in this experiment.

Use a diametrically magnetised magnet: north and south must lie across its diameter in the rotating plane. A coin magnet whose poles are on its flat faces is a different field arrangement. Hold the intended rotating axis above the middle of the sensor in a nonmagnetic support. The datasheet illustrates a typical small air gap, but a millimetre setting alone does not qualify the magnetic field. Check the actual status bits while aligning. No motor, high-speed shaft or moving machinery is part of this fixture.

A four-wire read-only connection

Original Pico 3V3 OUT pin 36 feeds Adafruit AS5600 module 6357 VIN, ground pin 38 joins GND, GP0 pin 1 joins SDA and GP1 pin 2 joins SCL. The default DIR ground jumper remains fitted; OUT, PGO and regulated 3Vo output are unused.
Original read-only module circuit. Factory bus pull-ups, regulator and bypass components remain in place; no permanent programming connection is used. Open full-size diagram.
Original PicoAdafruit 6357
3V3 OUT, physical 36VIN
Ground, physical 38GND
GP0, physical 1SDA
GP1, physical 2SCL

General-purpose input/output (GPIO) names are different from physical header positions: the code uses GP0 and GP1. The Inter-Integrated Circuit (I²C) bus address is 0x36, expressed as a seven-bit address. Set the bus to 100 kHz, use short leads and power the Pico only through its Universal Serial Bus (USB) connector. Leave the module's 3Vo regulated output open; it is not a second required input. Do not add another pull-up pair for this one-module bus.

Build with USB disconnected. Check the module labels rather than assuming the drawing represents header placement. After power-up, confirm supply at the board and inspect its status with the magnet supported rather than hand-held against the package. The electronics lessons explain common ground and pull-up paths; here those paths support an experiment about angle interpretation.

A status check is useful, but not an angle certificate

The read-only program reads STATUS register 0x0B, the two RAW ANGLE bytes at 0x0C and 0x0D, then STATUS again. In the status word, MD indicates magnet detection, ML indicates a field below the required strength, and MH indicates excessive field strength. Accept only MD set with ML and MH clear at both checks. A missing magnet must not become code zero or a stationary valid angle.

Bracketing the data with status checks can catch a field problem present at either endpoint. It does not make three bus transfers atomic. A brief interruption between them could escape detection. The interface has no sample sequence number or cyclic redundancy check (CRC) in these register reads. A plausible value and an acknowledged address do not authenticate the device, prove angular accuracy or establish a new physical conversion for every poll.

RAW ANGLE avoids applying the user-programmed output start and stop range. It does not erase unknown history or remove all internal filtering. This lesson never writes configuration or one-time-programmable (OTP) storage. Burning OTP can permanently change device settings and is unnecessary for reading angles. Preserve the board's existing programming and record its physical identity.

Subtract around the circle, then impose a physical limit

A constructed angle circle marks the zero boundary and the short 4090 to 6 path, a positive 12 count change. A separate complete dashed circle returns to the same endpoint, showing why identical codes cannot distinguish no movement from an unseen full revolution.
Original circular geometry and exact modular arithmetic; blue arc enlarged for visibility, not to angular scale or measured magnet motion. The assumed motion bound is external information; the sensor provides no missed-turn record. Open full-size diagram.

For two codes, first compute d = (current−previous) modulo 4096. If d exceeds 2048, subtract 4096 to choose the shorter signed path. Thus 4090→6 gives +12 counts, while 6→4090 gives −12. A naive subtraction would falsely produce a change near a whole revolution. Exactly 2048 counts is half a turn, so both directions are equally short; the code rejects it instead of arbitrarily choosing a sign.

The application further refuses any accepted step above 256 counts, or 22.5 degrees. It polls approximately every 50 milliseconds and rejects an elapsed acquisition gap above 150 milliseconds, a backwards clock or a nonadvancing later observation clock. These are chosen experiment limits. They cannot prove that the magnet obeyed the assumed speed between samples: a fast full turn can alias into a small apparent step. Keep rotation slow and under direct observation.

The first good sample establishes a reference and reports zero relative counts. Later values are labelled CONDITIONAL_PATH, with physical_turn_count_verified explicitly false. Any invalid status, transfer failure, ambiguous half turn, large step or timing fault invalidates the path and latches a fault. A new tracker starts a new relative experiment; it does not reconnect to the lost history. Record separate runs rather than hiding an interruption in the same total.

Complete program

Save this complete code as main.py. The read–evaluate–print loop (REPL), MicroPython's interactive console, prints each diagnostic record. The code contains the actual register reader, modular subtraction, validity state and bounded acquisition loop. It needs no external driver library and sends no programming command to the sensor.

"""Read-only AS5600 angle lab; original Pico, MicroPython 1.26.0.
Unwrapping is conditional on a physical motion bound, not a revolution sensor.
"""

def delta_counts(previous,current):
    if any(type(x)is not int or not 0<=x<4096 for x in (previous,current)):
        raise ValueError('Need two 12-bit integer angles')
    d=(current-previous)%4096
    if d==2048:raise ValueError('Exactly half a turn is direction-ambiguous')
    return d-4096 if d>2048 else d


class AS5600:
    def __init__(self,bus):self.bus=bus
    def read(self):
        before=self.bus.readfrom_mem(0x36,0x0b,1)
        raw=self.bus.readfrom_mem(0x36,0x0c,2)
        after=self.bus.readfrom_mem(0x36,0x0b,1)
        if len(before)!=1 or len(after)!=1 or len(raw)!=2:
            raise OSError('Short status or angle read')
        if any((s&0x38)!=0x20 for s in (before[0],after[0])):
            raise ValueError('Magnet absent, too weak or too strong')
        if raw[0]&0xf0:raise ValueError('Unexpected reserved angle bits')
        return (raw[0]<<8)|raw[1]


class Tracker:
    def __init__(self):self.last=None;self.total=0;self.failed=False
    def invalidate(self):self.failed=True;self.last=None;self.total=None
    def feed(self,raw,elapsed_ms):
        if self.failed:raise ValueError('Tracking faulted; explicitly start a new run')
        try:
            if type(raw)is not int or not 0<=raw<4096:raise ValueError('Invalid raw angle')
            if (type(elapsed_ms)is not int or not 0<=elapsed_ms<=150
                    or (self.last is not None and elapsed_ms==0)):
                raise ValueError('Clock order or acquisition gap')
            if self.last is None:
                self.last=raw
                return {'state':'REFERENCE_ONLY','angle_deg':raw*360/4096,
                        'relative_counts':0,'physical_turn_count_verified':False}
            d=delta_counts(self.last,raw)
            if abs(d)>256:raise ValueError('Step exceeds chosen 22.5 degree bound')
            self.total+=d;self.last=raw
            return {'state':'CONDITIONAL_PATH','angle_deg':raw*360/4096,
                    'delta_counts':d,'relative_counts':self.total,
                    'physical_turn_count_verified':False}
        except BaseException:
            self.invalidate();raise


def acquire(sensor,tracker,ticks_ms,ticks_diff,sleep_ms,emit=print,count=200):
    if type(count)is not int or not 1<=count<=1000:raise ValueError('Invalid bounded run length')
    previous=ticks_ms()
    for _ in range(count):
        try:
            raw=sensor.read();now=ticks_ms();row=tracker.feed(raw,ticks_diff(now,previous))
        except BaseException:
            tracker.invalidate();raise
        emit(row);previous=now;sleep_ms(50)
    return tracker.total


def main():
    from machine import Pin,I2C
    from time import ticks_ms,ticks_diff,sleep_ms
    bus=I2C(0,sda=Pin(0),scl=Pin(1),freq=100000)
    try:acquire(AS5600(bus),Tracker(),ticks_ms,ticks_diff,sleep_ms)
    except (OSError,ValueError)as error:
        print({'state':'SESSION_FAULT','relative_counts':None,'error':str(error)})


if __name__=='__main__':main()

Compare observation continuity with real motion

  1. Record module identity, firmware, magnet construction, mounting and starting gap. Inspect wiring without power, then let the fixture remain still after power-up.
  2. Start a run with a detected, qualified field. Rotate slowly across the zero boundary in both directions. Compare code changes with the signed count differences and note that one code is about 0.088 degrees nominally.
  3. Return to the initial orientation without assuming an exact physical angle reference. Compare relative counts; a display returning near zero is an observation, not an accuracy calibration.
  4. Lift the magnet away during a run. The program must stop and clear the path. Reposition it and explicitly start a separate run; do not add the two partial totals together.
  5. As a software exercise, compare identical endpoint codes with the possibility of one unseen full turn. This is an information limit, not a defect repaired by a different modulo expression.

The blank worksheet keeps expected action, raw angle, diagnostic state, controller interval and independently observed motion separate. Do not copy the synthetic illustration into measured columns. If apparent direction is unexpected, inspect magnet orientation, the existing DIR jumper and viewing direction before altering formulas. If status alternates between weak and strong, fix the mechanical fixture and gap; increasing an acceptance threshold cannot repair field geometry.

Challenge: codes 100 and 110 arrive on time with good magnet status. Can the program prove a ten-count movement? Answer: only under the stated motion bound. The same endpoints could follow one full turn plus ten counts. Good endpoint status does not observe the missing path. Likewise, the 150-millisecond limit is a rejection rule, not a guarantee that slower-looking data are true.

Twenty actual-code host checks cover circular arithmetic, every adjacent code including wrap, field-status faults, exact bus read order, short reads, ambiguous and excessive steps, timing faults, acquisition errors and interruption invalidation. They use injected bus and clock objects on desktop Python. They are not a MicroPython target run, magnet-strength measurement, verified angle calibration or physical assembly. The bench log is empty until real observations exist.

Primary references

No separate applicable erratum was located in the documentation examined. The unavailable former manufacturer endpoint prevents claiming a newly established latest device revision. Recheck current primary documentation and the actual board before physical construction or deployment.