Xbox controller -> raspberry pi -> serial -> arduino problems. Eg buttons sometimes don't register in arduino code

Python code

from evdev import list_devices, InputDevice, categorize, ecodes, KeyEvent,event_factory
from pySerialTransfer import pySerialTransfer as txfer
import time

gamepad = InputDevice('/dev/input/event0')

analogValue = [0,0,0,0,0,0]
buttonValue = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
stickIndexes = [0,1,3,4]
buttonIndex = [304,305,307,308,310,311,314,315,316,317,318]

def map2(input,inmin,inmax,outmin,outmax):#Scales variable to range
	return int((input-inmin)*(outmax-outmin)/(inmax-inmin)+outmin)

def checkKeys():#If key is active it is then 1 on buttonValue[index]
	for x in gamepad.active_keys():
				for y in range(len(buttonIndex)):
					if x == buttonIndex[y]:
						buttonValue[y] = 1
	#print("checked keys")

stickDeadZone = 1000; #The raw value range is -32767/32767 so you will have a lot of space to correct

def checkAnalog():
	#allocates gamepad absinfo to correct analogValue[index] and scales with deadzone
	for x in range(4):
		if gamepad.absinfo(stickIndexes[x]).value > stickDeadZone or gamepad.absinfo(stickIndexes[x]).value < -stickDeadZone:
			analogValue[x] = map2(gamepad.absinfo(stickIndexes[x]).value,-32767,32767,-1000,1000)
		else:
			analogValue[x] = 0

		#trigger values as they are already 1023
		analogValue[4] = gamepad.absinfo(2).value
		analogValue[5] = gamepad.absinfo(5).value
	#D-pad values
	if gamepad.absinfo(16).value != 0:
		if gamepad.absinfo(16).value == -1:
			buttonValue[11] = 1
		elif gamepad.absinfo(16).value == 1:
			buttonValue[12] = 1
	else:
		buttonValue[11] = 0
		buttonValue[12] = 0

	if gamepad.absinfo(17).value != 0:
		if gamepad.absinfo(17).value == -1:
			buttonValue[13] = 1
		elif gamepad.absinfo(17).value == 1:
			buttonValue[14] = 1
	else:
		buttonValue[13] = 0
		buttonValue[14] = 0
	#print("checked analog")



if __name__ == '__main__':
	try:
		link = txfer.SerialTransfer('/dev/serial0')
		print("1");
		link.open()
		time.sleep(2) # allow some time for the Arduino to completely reset
		
		for event in gamepad.read_loop():
			print("2");
			keyevent = categorize(event)
			if event.type == ecodes.EV_KEY and keyevent.keystate == KeyEvent.key_down:
				checkKeys()
				print("3");
			else:
				buttonValue = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
				print("4");

			#if event.type != 0:
			print("5")
			#checkAnalog()
			send_size = 0
			list_ = analogValue+buttonValue
			print(list_)
			list_size = link.tx_obj(list_)
			send_size += list_size
			link.send(send_size)

			#while not link.available():
			if False:
				print("6");
				if link.status < 0:
					if link.status == txfer.CRC_ERROR:
						print('ERROR: CRC_ERROR')
					elif link.status == txfer.PAYLOAD_ERROR:
						print('ERROR: PAYLOAD_ERROR')
					elif link.status == txfer.STOP_BYTE_ERROR:
						print('ERROR: STOP_BYTE_ERROR')
					else:
						print('ERROR: {}'.format(link.status))
			

	except KeyboardInterrupt:
		try:
			link.close()
		except:
			pass
	
	except:
		import traceback
		traceback.print_exc()
		
		try:
			link.close()
		except:
			pass

Arduino code

variables

const int xboxInputAmount = 21; //How many xbox values there will be
int32_t inputArray[xboxInputAmount]; //This will contain every xbox controller outputs

const int xbLSTICKAX = 0;//Left stick X axis
const int xbLSTICKAY = 1;//Left stick Y axis
const int xbLBA = 4;//Left trigger axis
const int xbRSTICKAX = 2;//Right stick X axis
const int xbRSTICKAY = 3; //Right stick Y axis
const int xbRBA = 5;//Right trigger axis
const int xbA = 6;//Button A
const int xbB = 7;//Button B
const int xbX = 8;//Button X
const int xbY = 9;//Button Y
const int xbLB = 10;//Button Left Bumber
const int xbRB = 11;//Button Right Bumber
const int xbSHARE = 12;//Button Share
const int xbSETTINGS = 13;//Button Settings
const int xbEMBLEM = 14;//Button Emblem
const int xbLSTICKD = 15;//Left stick down
const int xbRSTICKD = 16;//Right stick down
const int xbDL = 17;//Dpad left
const int xbDR = 18;//Dpad Right
const int xbDU = 19;//Dpad up
const int xbDD = 20;//Dpad down

//[Number of positions] [Number of axises]
//                     [lenght,turn,tilt]
int maxPositions = 10;
long positions[10][3];
long speedpositions[10]; //Speed value array
byte positionCount = 2;
byte indexValue = 0;

Main

#include <Arduino.h>
#include "variables.h" //Contains most of the variables
#include "Wire.h"
#include "SPI.h"
#include "SerialTransfer.h"
#include <LiquidCrystal_I2C.h>
#include <LiquidMenu.h>
#include <AccelStepper.h>
#include <MultiStepper.h>

AccelStepper lenghtStepper = AccelStepper(1, LENGHT_STEP_PIN, LENGHT_DIR_PIN);
AccelStepper turnStepper = AccelStepper(1, TURN_STEP_PIN, TURN_DIR_PIN);
AccelStepper tiltStepper = AccelStepper(1, TILT_STEP_PIN, TILT_DIR_PIN);

MultiStepper steppers; // Manages multiple AccelSteppers


LiquidCrystal_I2C lcd(lcd_address, lcd_width, lcd_height);

///////////////////////////////////////////////////////////////////
//Main menu
//LINES
LiquidLine welcome_line1(6,0,"Main Menu");
LiquidLine welcome_line2(0,1,"Drive menu");

//SCREENS
// You can add 4 line items here but the rest would then be required to be put into setup
LiquidScreen welcome_screen1(welcome_line1,welcome_line2);

//MENU
LiquidMenu welcome_menu(lcd,welcome_screen1);

///////////////////////////////////////////////////////////////////
//Drive menu

//MENU1 where you set how many positions you want
	//LINES
	LiquidLine drive_line1(2,0,"Set index amount");
	LiquidLine drive_line2(4,1,"Index : ",positionCount);
	LiquidLine drive_line3(7,2,"Accept");

	//SCREEN
	LiquidScreen drive_screen1(drive_line1,drive_line2,drive_line3);

	//MENU
	
//MENU2 where you drive stepper motors into positions
	//LINES
		LiquidLine drive2_line1(0,0,"Index: ",indexValue);
		LiquidLine drive2_line2(0,1,indexValue," / ", positionCount);
		LiquidLine drive2_line3(0,2,"empty");

	//SCREEN
	LiquidScreen drive_screen2(drive2_line1,drive2_line2,drive2_line3);

	//MENU
	//LiquidMenu drive_menu2(lcd,drive_screen2);

LiquidMenu drive_menu(lcd,drive_screen1,drive_screen2);

///////////////////////////////////////////////////////////////////
//Menu system

LiquidSystem menu_system(welcome_menu,drive_menu);

///////////////////////////////////////////////////////////////////

void gotodrive(){
	menu_system.change_menu(drive_menu);
}


//Increases indexValue variable and checks if it is under max positions
void increaseIndex(){
	positionCount++;
	if(positionCount > positionCount){
		positionCount = maxPositions;
	}
	Serial.printf("In inc ind %d",positionCount);
	menu_system.update();
}
//Decreases indexValue variable and checks if it is over the min value
void decreaseIndex(){
	positionCount--;
	if(positionCount < 2){
		positionCount = 2;
	}
	Serial.printf("In dec ind %d",positionCount);
	menu_system.update();
}

SerialTransfer myTransfer; // remember to use myTransfer.tick(); to get callback

void drivemotors(){
	menu_system.next_screen();
		Serial.println("drive");
	for(indexValue = 0; indexValue < positionCount; indexValue++)
	{
		menu_system.update();
		Serial.printf("for i %d posc %d \n",indexValue,positionCount);
		while (true)
		{
			myTransfer.tick();
			Serial.printf(" while true i %d posc %d \n",indexValue,positionCount);
			lenghtStepper.setSpeed(inputArray[xbLSTICKAX]);
			turnStepper.setSpeed(inputArray[xbRSTICKAX]);
			tiltStepper.setSpeed(inputArray[xbRSTICKAY]);

			lenghtStepper.runSpeed();
			turnStepper.runSpeed();
			tiltStepper.runSpeed();

			if(inputArray[xbX]){
				positions[indexValue][0] = lenghtStepper.currentPosition();
				positions[indexValue][1] = turnStepper.currentPosition();
				positions[indexValue][2] = tiltStepper.currentPosition();
				delay(100);
				break;
			}
		}
	}
	for(int i = 0; i < 10; i++){
		Serial.printf("%d %d %d",positions[i][0],positions[i][1],positions[i][2]);
		Serial.println();
	}
}

void buttonsCheckMenuNavigation()//Menu navigation code
{
	if (inputArray[xbB])
	{
		menu_system.call_function(1);//Select
	}

	if (inputArray[xbRB])
	{
		menu_system.call_function(2);//Increase
	}

	if (inputArray[xbLB])
	{
		menu_system.call_function(3);//Increase
	}

	if (inputArray[xbDL])
	{

	}

	if (inputArray[xbDR])
	{

	}

	if (inputArray[xbY])
	{
		menu_system.switch_focus(false);//Up in menu
	}
	if (inputArray[xbA])
	{
		menu_system.switch_focus(true);//Down in menu
	}
}


void callbackController()
{
		Serial.println("callback");
		uint16_t recSize = 0;
		recSize = myTransfer.rxObj(inputArray, recSize); 
		buttonsCheckMenuNavigation();

		//Increase and decrease speed value
		if (inputArray[xbLBA] < inputArray[xbRBA])
		{
			//Right trigger code
			speedmodifier = map(inputArray[xbRBA], 0, 1000, 0, 100);
			speed = speed + speedmodifier;
			//Checks if speed is over max
			if(speed > MaxTotalSpeed){
				speed = MaxTotalSpeed;
			}
		}
		else
		{
			//Left trigger code
			speedmodifier = map(inputArray[xbLBA], 0, 1000, 0, 100);
			speed = speed - speedmodifier;
			//If speed is negative
			if(speed < 0){
				speed = 0;
			}
		}
		//Serial.println(speed);

		//Maps stick analog values to the speed
		for (int i = 0; i < 4; i++)
		{
			inputArray[i] = map(inputArray[i], 0, 1000, 0, speed);
		}

		//Print inputArray
		/*
		for(int i = 0; i < xboxInputAmount; i++){
			Serial.print(inputArray[i]);
			Serial.print("|");
		}
		Serial.println();
		*/
		
}




// supplied as a reference - persistent allocation required
const functionPtr callbackArr[] = { callbackController };


void setup()
{
	Serial1.begin(115200);
	Serial.begin(115200);
	Serial.println("Start");

	lcd.init();
	lcd.backlight();

	welcome_menu.init();
	drive_menu.init();

	welcome_line2.attach_function(1,gotodrive);

	drive_line2.attach_function(2,increaseIndex);
	drive_line2.attach_function(3,decreaseIndex);
	drive_line3.attach_function(1,drivemotors);
	menu_system.update();

	///////////////////////////////////////////////////////////////////
	configST myConfig;
	myConfig.debug        = true;
	myConfig.callbacks    = callbackArr;
	myConfig.callbacksLen = sizeof(callbackArr) / sizeof(functionPtr);
	///////////////////////////////////////////////////////////////////

	myTransfer.begin(Serial1, myConfig);

	lenghtStepper.setMaxSpeed(LENGHTMAXSPEED);
	turnStepper.setMaxSpeed(TURNMAXSPEED);
	tiltStepper.setMaxSpeed(TILTMAXSPEED);

	steppers.addStepper(lenghtStepper);
	steppers.addStepper(turnStepper);
	steppers.addStepper(tiltStepper);

	//Make sure that the position and speedposition arrays are zero
	for(int i = 0; i < maxPositions; i++){
		for(int j = 0; j < 3; j++){
			positions[i][j] = 0;
			speedpositions[i] = 0;
		}
	}

}


void loop()
{
	myTransfer.tick();
	//buttonsCheckMenuNavigation();
}

Hello!
I'm having problems with my arduino/python code.
The way I detect button presses sometimes does not register them.
The way I send button values over from raspberry to arduino is a bit finnicky and I get crc errors.

I can get the analog values pretty easily but the button values are the problem here.

I use
if(array from raspberry pi[xboxbuttons code]){
//do cool stuff here
}
as the button check. How should I do it instead?

I am using serial transfer library to send a list over to arduino. The list format is analog values and then button values[1/0]eg [analogval1][analogval2][button1][button2]. Should I be doing this or is there a better way of sending xbox values over to arduino?

Should I send the serial packet every python code loop or once the action(event) happens in python?

If I send serial for every event in game controller shouldn't I also send a blank button array to not cause stuck buttons or to zero the button array in arduino code?

I have fought with the python code for some days now...

Thank you for your help and I hope I have included enough info for you to try and help me.

What is your timing window for checking data from the controller?

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.