0

I'm trying to operate an r/c robot with joystick and a raspberry pi running Debian and an nrf24l01 to communicate the joystick commands to the arduino on the robot. I only really need forward/back left and right, but eventually i want to put a bunch of stuff on this robot so it needs to be expandable. I'm using the nrf24 because i want to keep latency down, and it uses less power than a wifi chip. Eventually, I want to send all the axis values, some of the buttons, and the hat values to the arduino, but to test im only sending axis values.

Raspberry Pi code: (important parts are init statements on top, 128-134, and 177-180)

import RPi.GPIO as GPIO
from lib_nrf24 import NRF24
import spidev
import pygame
import time
import array

# Define some colors
BLACK    = (   0,   0,   0)
WHITE    = ( 255, 255, 255)

# This is a simple class that will help us print to the screen
# It has nothing to do with the joysticks, just outputting the
# information.
class TextPrint:
    def __init__(self):
        self.reset()
        self.font = pygame.font.Font(None, 20)

    def Print(self, screen, textString):
        textBitmap = self.font.render(textString, True, BLACK)
        screen.blit(textBitmap, [self.x, self.y])
        self.y += self.line_height

    def reset(self):
        self.x = 10
        self.y = 10
        self.line_height = 15

    def indent(self):
        self.x += 10

    def unindent(self):
        self.x -= 10


pygame.init()

# Initialize the GPIO interface
GPIO.setmode(GPIO.BCM)

# Set the width and height of the screen [width,height]
size = [500, 500]
screen = pygame.display.set_mode(size)

pygame.display.set_caption("joystick test app")

#Loop until the user clicks the close button.
done = False

# Used to manage how fast the screen updates
clock = pygame.time.Clock()

# Initialize the joysticks
pygame.joystick.init()

# Get ready to print
textPrint = TextPrint()

# Make an ouput array
output = list("test")

# Start the radio
pipes = [[0xE8, 0xE8, 0xF0, 0xF0, 0xE1], [0xF0, 0xF0, 0xF0, 0xF0, 0xE1]]

radio = NRF24(GPIO, spidev.SpiDev())
radio.begin(0, 17)

radio.setPayloadSize(32)
radio.setChannel(0x76)
radio.setDataRate(NRF24.BR_250KBPS)
radio.setPALevel(NRF24.PA_MAX)

radio.setAutoAck(True)
radio.enableDynamicPayloads()
radio.enableAckPayload()

radio.openWritingPipe(pipes[0])
radio.openReadingPipe(1, pipes[1])
radio.printDetails()

# -------- Main Program Loop -----------
while done==False:
    # create array for output
    # EVENT PROCESSING STEP
    for event in pygame.event.get(): # User did something
        if event.type == pygame.QUIT: # If user clicked close
            done=True # Flag that we are done so we exit this loop

        # Possible joystick actions: JOYAXISMOTION JOYBALLMOTION JOYBUTTONDOWN JOYBUTTONUP JOYHATMOTION
        if event.type == pygame.JOYBUTTONDOWN:
            print("Joystick button pressed.")
        if event.type == pygame.JOYBUTTONUP:
            print("Joystick button released.")


    # DRAWING STEP
    # First, clear the screen to white. Don't put other drawing commands
    # above this, or they will be erased with this command.
    screen.fill(WHITE)
    textPrint.reset()

    # Get count of joysticks
    joystick_count = pygame.joystick.get_count()

    textPrint.Print(screen, "Number of joysticks: {}".format(joystick_count) )
    textPrint.indent()

    # For each joystick:
    for i in range(joystick_count):
        joystick = pygame.joystick.Joystick(i)
        joystick.init()

        textPrint.Print(screen, "Joystick {}".format(i) )
        textPrint.indent()

        # Get the name from the OS for the controller/joystick
        name = joystick.get_name()
        textPrint.Print(screen, "Joystick name: {}".format(name) )

        # Usually axis run in pairs, up/down for one, and left/right for
        # the other.
        axes = joystick.get_numaxes()
        textPrint.Print(screen, "Number of axes: {}".format(axes) )
        textPrint.indent()
        axisvals = []

        for i in range( axes ):
            axis = joystick.get_axis( i )
            textPrint.Print(screen, "Axis {} value: {:>6.3f}".format(i, axis) )
            axis = (axis+1)/2
            axis = int(axis * 179)
            axisvals.append(axis)
            output.append(chr(axis))

        textPrint.unindent()

        buttons = joystick.get_numbuttons()
        textPrint.Print(screen, "Number of buttons: {}".format(buttons) )
        textPrint.indent()
        buttonvals = []

        for i in range( buttons ):
            button = joystick.get_button( i )
            textPrint.Print(screen, "Button {:>2} value: {}".format(i,button) )
            buttonvals.append(button)
            # output.append(chr(button)) for now, im only sending the axis data

        textPrint.unindent()

        # Hat switch. All or nothing for direction, not like joysticks.
        # Value comes back in an array.
        hats = joystick.get_numhats()
        textPrint.Print(screen, "Number of hats: {}".format(hats) )
        textPrint.indent()
        hatvals = []

        for i in range( hats ):
            hat = joystick.get_hat( i )
            textPrint.Print(screen, "Hat {} value: {}".format(i, str(hat)) )
            hat = str(hat)
            x,y=hat.split(",")  #hats is given as a strign (x, y) so we split it
            if x.find("0")>=0:  # only possible values are 1, 0, -1, so we search both halves
                hatvals.append(int(0))
            elif x.find("-")>=0:
                hatvals.append(int(-1))
            else:
                hatvals.append(int(1))
            if y.find("0")>=0:
                hatvals.append(int(0))
            elif y.find("-")>=0:
                hatvals.append(int(-1))
            else:
                hatvals.append(int(1))
        textPrint.unindent()

        radio.flush_tx()
        while (len(output) < 32):
            output.append(0)
        radio.write(output)    
        time.sleep(.005)
    # ALL CODE TO DRAW SHOULD GO ABOVE THIS COMMENT

    # Go ahead and update the screen with what we've drawn.
    pygame.display.flip()

    # Limit to 20 frames per second
    clock.tick(20)

# Close the window and quit.
# If you forget this line, the program will 'hang'
# on exit if running from IDLE.
pygame.quit ()

Arduino code:

#include<SPI.h>
#include<RF24.h>


// CE, CSN pins
RF24 radio(9, 10);

void setup(void){
  while(!Serial);
  Serial.begin(9600);

  radio.begin();
  radio.setPALevel(RF24_PA_MAX);
  radio.setChannel(0x76);
  radio.openWritingPipe(0xF0F0F0F0E1LL);
  const uint64_t pipe = (0xE8E8F0F0E1LL);
  radio.openReadingPipe(1, pipe);

  radio.enableDynamicPayloads();
  radio.powerUp();

}

void loop(void){
  radio.startListening();
  Serial.println("Starting loop. Radio on.");
  char receivedMessage[32] = {0};
  if(radio.available()){
    radio.read(receivedMessage, sizeof(receivedMessage));
    Serial.println(receivedMessage);
  } else {
    Serial.println("No data!");
  }
  delay(100);

}

I have the arduino and pi communicating already, he code from this guide (http://invent.module143.com/daskal_tutorial/rpi-3-tutorial-14-wireless-pi-to-arduino-communication-with-nrf24l01/) worked flawlessly, both tests he ran. I pretty much copied all the radio init statements into the joystick code i had, and it doesn't quite work. There are two problems: When i run it, the arduino only prints: 'YYYYYYYYY', regardless of the joystick changing. It also doesn't stop printing that message after i stop the script on the Pi. The second problem is a bit worse; after running the code for a minute or so, the terminal throws an error:

 Traceback (most recent call last):


File "joystick_test.py", line 180, in <module>
    radio.write(output)
  File "/home/pi/Desktop/NRF24L01/lib_nrf24.py", line 452, in write
    self.startWrite(buf)
  File "/home/pi/Desktop/NRF24L01/lib_nrf24.py", line 485, in startWrite
    self.write_payload(buf)
  File "/home/pi/Desktop/NRF24L01/lib_nrf24.py", line 268, in write_payload
    return self.spidev.xfer2(txbuffer)
OverflowError: Argument list size exceeds 4096 bytes.

despite the line 'radio.flush_tx()' on line 177, and the sleep afterwards.

I think the first problem has to do with the way variable work between cpp and python. Honestly, i have no idea about the difference between the two, but the axis value im sending is between 0-179. In the end, I'm only turning some servos on the other side, i really don't need to send something so complicated.

The other error, i think, actually stems from the first. The transmit buffer is probably overflowing because of something with the variable storage. But i don't see what wrong with my code.

0

The Python error message tells you exactly what is wrong.

File "joystick_test.py", line 180, in <module>
    radio.write(output)
  File "/home/pi/Desktop/NRF24L01/lib_nrf24.py", line 452, in write
    self.startWrite(buf)
  File "/home/pi/Desktop/NRF24L01/lib_nrf24.py", line 485, in startWrite
    self.write_payload(buf)
  File "/home/pi/Desktop/NRF24L01/lib_nrf24.py", line 268, in write_payload
    return self.spidev.xfer2(txbuffer)
OverflowError: Argument list size exceeds 4096 bytes.

The complaint is with radio.write(output)

You initialise output which you then append to repeatedly. So it grows and grows in size until the SPI driver complains it is too big when it gets to 4096 bytes.

I'm a bit surprised the NRF24 library didn't complain as the maximum NRF24 payload is 32 bytes.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.