pythonpygamearduinojoystick

joystick from arduino to pygame


Right now, I'm just trying to move the circle object based on the joystick coordinates given from the Arduino data.
I'm just testing the x axis to move the circle right when the value of the x axis reaches 1023.

I have two main issues:

  1. When I run main.py it only prints the Arduino joystick coordinates a couple times then it stops unless I press control
  2. The ball does not move right even when the x axis value of the joystick is 1023.

So my 2 questions are:

  1. why doesn't the ball move right when the joystick x coordinate reaches 1023(It does print the ballpos[0] when control is tapped (which strangely continues the event loop for a moment) and Arduino joystick data for the x axis is 1023)?
  2. How can I make the Arduino Joystick data be read continuously in game event loop?

I'm getting following Arduino data from the COM 3 port into python:

JSPy.ino

//input pins

//analog
int JSx = A0;
int JSy = A1;

//digital
int JSpin = 9;

//read JS position values
int JSxVal;
int JSyVal;
int JSVal;

// delay(ms)
int dt = 500;

void setup() {
    // put your setup code here, to run once:

    pinMode(JSx,INPUT);
    pinMode(JSy,INPUT);
    digitalWrite(JSpin,HIGH);
    Serial.begin(9600);
    
}
void loop() {
    // put your main code here, to run repeatedly:

    JSxVal = analogRead(JSx);
    JSyVal = analogRead(JSy);
    JSVal = digitalRead(JSpin);
    
    delay(dt);

    //JSx coor
    Serial.print(JSxVal);
    Serial.print(" ");

    //JSy coor
    Serial.println(JSyVal); 
}

This is the py file:

main.py

import serial
import time 
import pygame as pg

# default port is 9600 baud rate so didnt have to set
arduinoData = serial.Serial("com3")
time.sleep(1)


# pygame settings

#screen
bgSize =screenW,screenH = 500,600
screen = pg.display.set_mode((bgSize))


ballx,bally = 250,300
ballPos=(ballx,bally)

GRAY = (112,112,112)
BLUE = (0,0,255)



run =True

while run:

    for event in pg.event.get():
        while(arduinoData.inWaiting()==0):
            pass
        dataPacket = arduinoData.readline()
        # convert from binary to string
        dataPacket = dataPacket.decode(encoding='utf-8')
        
            # make list of string numbers of coordinates
        dataPacket = dataPacket.split()
        # turn coordinates into int in the list
        dataPacket = [eval(i) for i in dataPacket]

        
        if event.type == pg.QUIT:
            run=False
        if event.type == pg.KEYDOWN:
            if event.key == pg.K_ESCAPE:
                run= False
            if dataPacket[0] == 1023:
                ballx+=1
                print(f"x pos = {ballPos[0]}")

        screen.fill(BLUE)
        print(f" JS - x - {dataPacket[0]}")
        pg.draw.circle(screen, GRAY,ballPos,15,4)
        pg.display.update()

Solution

  • There's a couple of issues:

    dataPacket = dataPacket.split()
    # turn coordinates into int in the list
    dataPacket = [eval(i) for i in dataPacket]
    

    Firstly don't use eval().

    So the Arduino has written two textual numbers into a string, separated by a space, and ended with a newline. That's pretty easy to convert into integers.

    positions = dataPacket.split()  # creates a list of string-numbers
    try:
        joy_x = int( positions[0], 10 )
        joy_y = int( poisitons[1], 10 )
        had_error = False
    except:
        print( "Bad data packet: " + dataPacket )
        had_error = True
    

    The code is drawing the ball at position specified by the tuple ballPos. The code reads like ballPos refers to ballx and bally - it doesn't, they're copied into the tuple. So you need to update ballPos too:

    if ( not had_error ):
        ballx = joy_x
        bally = joy_y
        ballPos = ( ballx, bally )   # update circle locations
    

    Note: the code above assumes the received position is an absolute pixel coordinate. Maybe that's not how your Arduino joystick is working, but in any event this change will get something happening.