gerrikoio
Posts: 2
Joined: Thu May 06, 2021 6:17 pm

I2C read problems with a pair of Wii Nunchucks

Thu May 06, 2021 6:44 pm

I'm way more familiar with Arduino than I am with MicroPython.

So, I'm not sure if I've missed something in my MicroPython code as I have my Arduino code working fine, with the Pico, but I have been unable to replicate using MicroPython (or the Pico C SDK, for that matter).

My code is running without error but the program is unable to read data from the Nunchucks, while with my Arduino code it can. How is my output from my code... so it gives me hex values FF-FF-FF-FF-FF-FF for the ID and it will not read any of the data, like the Z & C button presses or the Joystick X-Y values. If I press a button there is no update, for example.

Some of you may notice that I have a (hacky?) repeat read (for loop) in my code for the "self.i2c.readfrom_into(self.addr, readBuff)" function as I found that the program intermittently returned an exception (failed to read). It appears that the 2nd attempt to read works. Anyway, I'm not sure if this is normal I2C behaviour, or indicative of some other issue. I cannot replicate this in my Arduino code (either they hide it, or it's not detected in the code, or it's not there).

The main thing is that I cannot get data, even though the program is reading something.

Any feedback/advice is welcomed.
>>> Running nunchucks.py

Nunchuck 1 I2C Address : 0X52
Nunchuck 1 I2C Configuration: I2C(0, freq=100000, scl=21, sda=20)
Nunchuck 2 I2C Address : 0X52
Nunchuck 2 I2C Configuration: I2C(1, freq=100000, scl=21, sda=20)
Nunchuck 1 ID: ffffffffffff
Nunchuck 2 ID: ffffffffffff
Nun_1: BtnZ 0 BtnC 0 | JoyX 0 JoyY 0
Nun_2: BtnZ 0 BtnC 0 | JoyX 0 JoyY 0
Nun_1: BtnZ 0 BtnC 0 | JoyX 0 JoyY 0
Nun_2: BtnZ 0 BtnC 0 | JoyX 0 JoyY 0

Code: Select all

from machine import Pin, I2C
from micropython import const
from utime import sleep_ms
import array as arr

NUNCHUK_ADDRESS = const(0x52)

pin = Pin(25, Pin.OUT)

class nunchuck_i2c():
    def __init__(self, i2c, addr=0x52):
        self.i2c = i2c
        self.addr = addr
        self.cmdBuff = bytearray(2)
        self.datBuff = bytearray(1)
    
    analogXY = bytearray(2)
    accelXYZ = arr.array('I', [0, 0, 0])
    btnsZC = bytearray(2)
    IDbuff = bytearray(6)

    def begin(self):
        self.cmdBuff[0] = 0x55
        self.cmdBuff[1] = 0xF0
        try:
            self.i2c.writeto(self.addr, self.cmdBuff)
        except Exception:
            print("I2C Failed to Write 0x55 0xFA")

        sleep_ms(7)

        self.cmdBuff[0] = 0x00
        self.cmdBuff[1] = 0xFB
        try:
            self.i2c.writeto(self.addr, self.cmdBuff)
        except Exception:
            print("I2C Failed to Write 0x00 0xFB")

    def getID(self):
        readBuff = bytearray(6)
        self.datBuff[0] = 0xFA
        try:
            self.i2c.writeto(self.addr, self.datBuff)
        except Exception:
            print("I2C Failed to Write 0xFA")

        sleep_ms(7)

        try:
            self.i2c.readfrom_into(self.addr, readBuff)
            for i in range(0, 6):
                self.IDbuff[i] = readBuff[i]

        except Exception:
            print("I2C Failed to Read from Memory 0xFA")


    def update(self):

        self.datBuff[0] = 0x00
        readBuff = bytearray(6)
        try:
            self.i2c.writeto(self.addr, self.datBuff)
        except Exception:
            print("I2C Failed to Write 0x00")

        sleep_ms(7)

        for i in range (0,1):
            readOK = True
            try:
                self.i2c.readfrom_into(self.addr, readBuff)

                self.analogXY[0] = readBuff[0]
                self.analogXY[1] = readBuff[1]
                self.accelXYZ[0] = (readBuff[2] << 2) | ((readBuff[5] >> 2) & 3)
                self.accelXYZ[1] = (readBuff[3] << 2) | ((readBuff[5] >> 4) & 3)
                self.accelXYZ[2] = (readBuff[4] << 2) | ((readBuff[5] >> 6) & 3)
                self.btnsZC[0] = ((readBuff[5] >> 0) & 1)
                self.btnsZC[1] = ((readBuff[5] >> 1) & 1)
                #store the inverse
                if (self.btnsZC[0] == 1): 
                    self.btnsZC[0] = 0
                else:
                    self.btnsZC[0] = 1
                if (self.btnsZC[1] == 1): 
                    self.btnsZC[1] = 0
                else:
                    self.btnsZC[1] = 1

            except Exception:
                print("I2C Failed to Read from Memory 0x00")
                readOK = False
            
            if readOK == True:
                break
        

# ========================== end of nunchuck_i2c class =============================

sleep_ms(1000)               # just a little wait for peripherals 

i2c0 = I2C(0, scl=Pin(21), sda=Pin(20), freq=50000)        # Init I2C using pins GP8 & GP9 (alt I2C0 default pins)

i2c1 = I2C(1, scl=Pin(7), sda=Pin(6), freq=50000)          # Init I2C using pins GP6 & GP7

print("Nunchuck 1 I2C Address      : "+hex(i2c0.scan()[0]).upper()) # Display device address
print("Nunchuck 1 I2C Configuration: "+str(i2c0))                   # Display I2C config

print("Nunchuck 2 I2C Address      : "+hex(i2c1.scan()[0]).upper()) # Display device address
print("Nunchuck 2 I2C Configuration: "+str(i2c1))                   # Display I2C config

NC1 = nunchuck_i2c(i2c0, 0x52)

NC2 = nunchuck_i2c(i2c1, 0x52)

NC1.begin()

NC2.begin()

sleep_ms(100)

NC1.getID()
print("Nunchuck 1 ID: {0:x}{0:x}{0:x}{0:x}{0:x}{0:x}".
    format(NC1.IDbuff[0],NC1.IDbuff[1],NC1.IDbuff[2],NC1.IDbuff[3],NC1.IDbuff[4],NC1.IDbuff[5]))

NC2.getID()
print("Nunchuck 2 ID: {0:x}{0:x}{0:x}{0:x}{0:x}{0:x}".
    format(NC2.IDbuff[0],NC2.IDbuff[1],NC2.IDbuff[2],NC2.IDbuff[3],NC2.IDbuff[4],NC2.IDbuff[5]))

sleep_ms(10)

while True:
    pin.toggle()
    NC1.update()
    print("Nun_1: BtnZ {0:d} BtnC {0:d} | JoyX {0:d} JoyY {0:d}".format(NC1.btnsZC[0], NC1.btnsZC[1], NC1.analogXY[0], NC1.analogXY[1]))
    NC2.update()
    print("Nun_2: BtnZ {0:d} BtnC {0:d} | JoyX {0:d} JoyY {0:d}".format(NC2.btnsZC[0], NC2.btnsZC[1], NC2.analogXY[0], NC2.analogXY[1]))
    sleep_ms(500)


sonnybalut
Posts: 21
Joined: Fri Apr 17, 2015 2:57 am

Re: I2C read problems with a pair of Wii Nunchucks

Fri May 07, 2021 1:22 am

I have a wii nunchuck youtube demo (source in the description).
youtube link ----> https://www.youtube.com/watch?v=A-CVNTwYs5U

Thanks,
Sonny

gerrikoio
Posts: 2
Joined: Thu May 06, 2021 6:17 pm

Re: I2C read problems with a pair of Wii Nunchucks

Fri May 07, 2021 10:20 am

Sonny,

Thanks for posting your reply. That's a great demo.

Hmmm, for some reason I had my initial write values in reverse order. Doh!

Problem solved and my code works.

EDIT: Just to add that I noticed now that my print statements were not printing out data correctly. It appears that whilst the use of the "format" method does not cause a complaint from the compiler, it simply wasn't printing out values correctly. When I changed to

Code: Select all

    print("Nun_1: BtnZ %d BtnC %d | JoyX %d JoyY %d"%(NC1.btnsZC[0], NC1.btnsZC[1], NC1.analogXY[0], NC1.analogXY[1]))
it works perfectly.

User avatar
OneMadGypsy
Posts: 265
Joined: Wed Apr 28, 2021 1:57 am
Location: New Orleans, Louisiana
Contact: Website

Re: I2C read problems with a pair of Wii Nunchucks

Sun May 09, 2021 10:53 pm

@gerrikoio

Code: Select all

NC2.getID()
print("Nunchuck 2 ID: {0:x}{0:x}{0:x}{0:x}{0:x}{0:x}".
        format(NC2.IDbuff[0],NC2.IDbuff[1],NC2.IDbuff[2],NC2.IDbuff[3],NC2.IDbuff[4],NC2.IDbuff[5]))

`format` works perfectly. The problem is that you told it to print the first argument in every spot. :D


This is all that is necessary to print your entire buffer

Code: Select all

print("Nunchuck 2 ID: {}".format(NC2.IDbuff))

Or if you really need it in hex

Code: Select all

print("Nunchuck 2 ID: {:x}{:x}{:x}{:x}{:x}{:x}".format(*NC2.IDbuff))

Or if you really need it in hex and want to be overly specific

Code: Select all

print("Nunchuck 2 ID: {0:x}{1:x}{2:x}{3:x}{4:x}{5:x}".format(*NC2.IDbuff))


If you prepend a `list` or `tuple` with * it unpacks the data in place. You don't have to specify each index individually. You just have to make sure that there is room for the data to go. For instance if we got rid of one {:x} in the above print statement problems would ensue. However using * is only necessary if you are passing an iterable. If the iterable is on the right side of an = then the * is not necessary, but you still need to make sure there are enough values to unpack it to.

example:

Code: Select all

values = (1, 2, 3)

def thing(*args):
    a, b, c = args

thing(*values)
"Focus is a matter of deciding what things you're not going to do." ~ John Carmack

Return to “MicroPython”