Reading Wii Nunchuck data in Python

From XPUB & Lens-Based wiki
Revision as of 18:15, 20 November 2011 by Jasper van Loenen (talk | contribs) (Created page with "=== Not finished yet, I'll add some more info + images later === Since I've installed Linux I use the terminal a lot. I really like how you can use something text based and real...")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Not finished yet, I'll add some more info + images later

Since I've installed Linux I use the terminal a lot. I really like how you can use something text based and really basic to perform all kinds of complex tasks.

After messing with SVG for a bit I thought it might be interesting to use the text based terminal as the interface to a SVG generating script. Also, I wanted to see how I could use the terminal together with an Arduino so I used one to connect my Python script too a Wii controller, which is used as input for the script.

[Disclaimer] Since I wrote the code from scratch and added 'features' as I went along I bet it's far from optimized. But it seems to do the job. I think I should add a screenshot later for the people without a Nunchuck or Arduino (though changing the controls to keyboard input should be trivial)

I used the WiiChuck class [1] to read the Wii Nunchuck data in Arduino.

Python code

#=========================================================
# Import libs and set basic vars
#=========================================================
import serial, os, math
width, height = 50, 20
xpos, ypos = 0, 0
sx, sy = -1, -1
zbut, prevZbut, cbut = 0, 0, 0
cursorChar, prevCursorChar = "o", ""
svgScale = 10.0
svgHeightCorrection = 2

theScreen = [] #will hold all of the 'pixels'
fullRow = "" # the line of #s for use at the top and bottom
for x in range(0, width+2):
    fullRow += "#"

#initiate the screen arrays
for y in range(0, height):
    xlist = []
    for x in range(0, width):
        xlist.append(" ")
    theScreen.append(xlist)

svgOut = ""
#=========================================================
# function to draw the arrays to the screen
#=========================================================
def display():
    global theScreen
    global cursorChar
    global xpos
    global ypos

    prevCursorChar = theScreen[ypos][xpos] 
    theScreen[ypos][xpos] = cursorChar
    os.system("clear")
    print fullRow
    for y in theScreen:
        printLine = "#"
        for x in y:
            printLine += str(x)
        print printLine+"#"
    print fullRow
    theScreen[ypos][xpos] = prevCursorChar

#=========================================================
# function to draw a line
#=========================================================
def drawLine(x1, y1, x2, y2):
    global theScreen
    global svgOut
    global svgScale
    global svgHeightCorrection
    bx1, by1, bx2, by2 = int(x1), int(y1), int(x2), int(y2)

    x1 = max(0, min(x1, width))
    x2 = max(0, min(x2, width))
    y1 = max(0, min(y1, height))
    y2 = max(0, min(y2, height))
        
    d = math.atan2(y2-y1, x2 - x1)*180/math.pi #d = direction
    d2 = d
    if d2 < 0:
        d2 = (360-abs(d2))        
    dist = math.floor(math.sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2)))
    lineChar = "*"
    if (d2 >= 360-22.53 or d2 < 22.5) or (d2>=157.5 and d2<202.5):
       lineChar = "~"
    elif (d2 >= 22.5 and d2 < 67.5) or (d2>=202.5 and d2<247.5):
       lineChar = "\\"
    elif (d2>=67.5 and d2<112.5) or (d2>=247.5 and d2<292.5):
       lineChar = "|"
    elif (d2>=112.5 and d2<157.5) or (d2>=292.5 and d2<337.5):
       lineChar = "/"
    
    step = 0
    while(step < dist):
        x1 += math.cos(d*math.pi/180)
        y1 += math.sin(d*math.pi/180)
        if x1>width:
            x1 = width-1
            x2 = width-1
        elif x1<0:
            x1 = 0
            x2 = 0
        if y1>height:
            y1 = height-1
            y2 = height-1
        elif y2<0:
            y1 = 0
            y2 = 0
        drawY = int(math.floor(y1))
        drawX = int(math.floor(x1))
        if drawY < len(theScreen):
            if drawX < len(theScreen[drawY]):
                theScreen[drawY][drawX] = lineChar
        step = step + 1
    theScreen[by1][bx1], theScreen[by2][bx2] = "*", "*"

    svgOut = svgOut + """<line x1="{0}" y1="{1}" x2="{2}" y2="{3}"
    style="stroke:rgb(0,0,0);stroke-width:{4}"/>""".format(bx1*svgScale, by1*(svgScale*svgHeightCorrection), bx2*svgScale, by2*(svgScale*svgHeightCorrection), svgScale)

#=========================================================
# Save image, open and quit
#=========================================================
def saveOpenAndQuit():
    global svgOut
    global width
    global height
    global svgScale
    svgOut = """<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="{0}" height="{1}">""".format(svgScale*width, svgScale*(height*svgHeightCorrection)) + svgOut
    svgOut = svgOut + "</svg>"

    fo = open("image.svg", "wb")
    fo.write( svgOut);
    fo.close()
    os.system("google-chrome image.svg &")

#=========================================================
# Main loop
#=========================================================
loop = 0
ser = serial.Serial('/dev/ttyACM0', 19200)
while 1:
    loop = loop + 1
    serialData = ser.readline()
    serialData = serialData.replace("\r\n", "")
    dataList = serialData.split(',')

    if len(dataList)==6:
        prevZbut = zbut
        accx = float(dataList[0])
        accy = float(dataList[1])
        zbut = float(dataList[2])
        cbut = float(dataList[3])
        joyx = float(dataList[4])
        joyy = float(dataList[5])
        
        #move cursor around
        if joyx>150:
            xpos = xpos + 1
        elif joyx<100:
            xpos = xpos - 1

        if joyy>150:
            ypos = ypos - 1
        elif joyy<100:
            ypos = ypos + 1

        xpos = max(0, min(xpos, width-1))
        ypos = max(0, min(ypos, height-1))

        #remember previous char on cursor location
        prevCursorChar = theScreen[ypos][xpos]
        
        if cbut==1:
            saveOpenAndQuit()

        cursorChar = "o"
        if zbut==1:#button down
            cursorChar = "x"
        elif zbut==0 and prevZbut==1: #button released
            if sx==-1:#first time releasing button
                 sx, sy = xpos, ypos
                 theScreen[sy][sx] = "*"
            else:#button released, draw line
                drawLine(sx, sy, xpos, ypos)
                sx, sx = -1, -1
    
    display()

#Reset the character at the cursor position (prevent trailing)
#theScreen[ypos][xpos] = prevCursorChar


Arduino code

#include <Wire.h>
#include "nunchuck_funcs.h"

int loop_cnt=0;

byte accx,accy,zbut,cbut,joyx,joyy;
int ledPin = 13;


void setup()
{
  Serial.begin(19200);
  nunchuck_setpowerpins();
  nunchuck_init(); // send the initilization handshake
}

void loop()
{
  if( loop_cnt > 100 ) { // every 100 msecs get new data
    loop_cnt = 0;

    nunchuck_get_data();
    //nunchuck_print_data();

    accx  = nunchuck_accelx(); // ranges from approx 70 - 182
    accy  = nunchuck_accely(); // ranges from approx 65 - 173
    zbut = nunchuck_zbutton();
    cbut = nunchuck_cbutton(); 
    joyx = nunchuck_joyx();
    joyy = nunchuck_joyy(); 
    
    Serial.print((byte)accx,DEC);
    Serial.print(",");
    Serial.print((byte)accy,DEC);
    Serial.print(",");
    Serial.print((byte)zbut,DEC);
    Serial.print(",");
    Serial.print((byte)cbut,DEC);
    Serial.print(",");
    Serial.print((byte)joyx,DEC);
    Serial.print(",");
    Serial.print((byte)joyy,DEC);
    Serial.println();
  }
  loop_cnt++;
  delay(1);
}