Rotary Switch via I2C

This rotary switch peripheral operates via I2C, on the MCU of your choice  (2.7V to 5.5V operating voltage). There is one IO Expander chip that uses one address out of 8 possible per I2C bus. There are LEDs to show the position of the switch, and the use of these LEDs is hard wired. A MicroPython class driver has been written below, for this peripheral and so it would make a good UI addition to a student project. Note, that it is fairly straight forward to divide this board design into two boards connected by a ribbon cable; UI (rotary switch) and I2C chip boards; see remote control as example. This board has interrupt outputs and so a suitable student project might be to write a MicroPython driver for this hardware involving interrupts.

# MIT License (MIT)
# Copyright (c) 2024 Microtron Ltd NZ - Stephen Eichler
# https://opensource.org/licenses/MIT
# Written 14/10/2025

from machine import Pin, I2C
import time
import rotaryswitch12poll


i2c = I2C(id=0, scl=Pin(1), sda=Pin(0))
rspoll=rotaryswitch12poll.ROTARYSWITCH12POLL(i2c) 

def i2c_scan(i2c):
    try:
        devices = i2c.scan()
        if len(devices) == 0:
            print("No I2C devices found.")
        else:
            print("Found the following I2C devices:")
            for device in devices:
                print(f"Device address: 0x{device:02X}")
    except OSError as e:
        print(f"An error occurred during the I2C scan: {e}")
        
i2c_scan(i2c)

lastswitch = None
while True:
    switchno=rspoll.poll()
    
    if switchno is not None and lastswitch != switchno:
        print ("Switch position: ", switchno)
        lastswitch = switchno
            
    time.sleep_ms(50)
=====================================================================
# rotaryswitch12poll.py
# MIT License (MIT)
# Copyright (c) 2024 Microtron Ltd NZ - Stephen Eichler
# https://opensource.org/licenses/MIT
# Written 14/10/2025
#import rotaryswitch12poll
#i2c = I2C(id=0, scl=Pin(1), sda=Pin(0))
#rspoll=rotaryswitch12poll.ROTARYSWITCH12POLL(i2c)
#switchno=rspoll.poll()

import mcp23017

class ROTARYSWITCH12POLL:

    def __init__(self, i2c, i2caddress=0x20):
        #self.d0 = Channel(self, 0)
        self.mcp = mcp23017.MCP23017(i2c, i2caddress)
        self.mcp.pin(8, mode=1, pullup=True)
        self.mcp.pin(9, mode=1, pullup=True)
        self.mcp.pin(10, mode=1, pullup=True)
        self.mcp.pin(11, mode=1, pullup=True)
        self.mcp.pin(12, mode=1, pullup=True)
        self.mcp.pin(13, mode=1, pullup=True)
        self.mcp.pin(14, mode=1, pullup=True)
        self.mcp.pin(15, mode=1, pullup=True)
        self.mcp.pin(0, mode=1, pullup=True)
        self.mcp.pin(1, mode=1, pullup=True)
        self.mcp.pin(2, mode=1, pullup=True)
        self.mcp.pin(3, mode=1, pullup=True)
        self.bitarray = [8,9,10,11,12,13,14,15,0,1,2,3]
        
    def poll(self):
        values = self.mcp.gpio
        switchno = None
        for counter in range(12):
            bit = self.bitarray[counter]
            mask = (1 << bit) ^ 0xffff
            bitres = (values | mask) & (1 << bit)
            if bitres == 0:
                switchno = counter + 1
                break
        return switchno
======================================================================
>>> %Run -c $EDITOR_CONTENT

MPY: soft reboot
Found the following I2C devices:
Device address: 0x20
Device address: 0x3C
Switch position:  1
Switch position:  2
Switch position:  3
Switch position:  4
Switch position:  5
Switch position:  6
Switch position:  7
Switch position:  8
Switch position:  9
Switch position:  10
Switch position:  11
Switch position:  12
Switch position:  11
Switch position:  10
Switch position:  9
Switch position:  8
Switch position:  7
Switch position:  6
Switch position:  5
Switch position:  4
Switch position:  3
Switch position:  2
Switch position:  1