Hi there!
Recently I'm trying to build my mini FRC robot project. (FRC stands for First Robotics Competition) I have a Logitech F310 controller, and I want to use that controller to control my robot. (drive it with the logitech joystick)
I realized the hardest part is to establish communication wirelessly, like how to send joystick values over to the robot microcontroller.
I have 2 working ESP32 boards. (a DOIT devkit v1 and a esp32CAM) I also have a raspberry pi pico, which supports bluetooth but not wifi.
My plan is to plug the joystick into my laptop, and run a program on the laptop to get the joystick values. (Pygame will work) But I'm not sure how to send this data over to the robot microcontroller. Device networking is a completely new topic to me and I need some help. I did some research, and I feel like bluetooth Serial, socket library, or wifi access point might work. But I'm not sure. I watched yt channel techiesms' series on esp32 micropython.
Which method is the easiest to implement? And what do you recommend? Any help will be appreciated!
what distance communication do you require - WiFi is typically 10 to 20 metres
ESP-now is probably the simplest wireless communication
Thank you! I'll be driving the robot indoors, and it doesn't need a long range. 5m is enough for me.
Do you mean Bluetooth? I'd expect a bit more from WiFi.
I mainly want low latency -- I don't want the robot to start moving one second after I push the joystick.
If that is the requirement, then you want NO errors and a continuous communication link. So plan for error checking and recovery for your commands. Also plan on continuously sending "No OP" type commands to maintain the link, then insert your actual functional command into the stream of NOP commands. NO wait commands or While loops in your code.
Thank you! I don't quite understand your terms and I'll look into them. I previously only know to send strings from laptop and to read strings on the microcontroller. What is error checking? And
what is recovery for commands? Do you mind explaining them a little bit here?
Stay with strings for now. HOW does the receiver know the string that was received does not contain an error? It doesn't. That means a command could be interpreted differently from the what the original intent was. How will the receiver react to a string in error? That is what you need to think about. First is the receiver must be able to actually know there is an error in the string, so research how to ensure error-free data.
Then if your code does find an error, what to do about it? Does the receiver just ignore the string or do you implement a way to tell the sender about the error and ask the sender to try sending the command string again?
In order to ensure the remote control network is fully functional when you actually send a string, the network must be running continuously and that is done with strings that are not commands, but a string specifically designed to keep the network busy. When a command string is ready to be sent, then send that string instead of the busy work string.
After a lot of trying and researching I have it working today. I'm very happy. However, the esp32 is running the loop very slowly, about 4 times per second. I set the esp32 as a wifi access point and use it to host a socket. And on my laptop I run the other piece of code. It's a python program that gets joystick inputs, and send it over to the esp32 socket through a client socket. Currently I have to put time.sleep(0.25) in my laptop code, otherwise the string received by esp32 will sometimes be doubled (it should get '0,0,0' but gets'0,0,00,0,0' instead). I'll attach both pieces of code and a setup diagram. Is there any way to make the latency less? Any help is appreciated.
import network
from machine import Pin
import time
import socket
led = Pin(2, Pin.OUT)
wifi = network.WLAN(network.AP_IF)
wifi.active(True)
wifi.config(essid='Crescendo', authmode=network.AUTH_WPA_WPA2_PSK, password='password')
print(wifi.ifconfig())
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # AF_INET - Internet Socket, SOCK_STREAM - TCP protocol
Host = '' # Empty means, it will allow all IP addresses to connect
Port = 80 # HTTP port
s.bind((Host, Port)) # Host, Port
s.listen(5)
s.setblocking(False) # Set socket to non-blocking mode
while True:
try:
# Accept a connection from a client
client_socket, addr = s.accept()
print("Connection from {}".format(addr))
while True:
# Receive data from the client
data = client_socket.recv(1024).decode()
if not data:
break # No more data from client
# print(data)
if len(data.split(',')) == 3:
x, y, lightOn = data.split(',')
print("lightOn:", lightOn)
led.value(int(lightOn))
except OSError:
# Handle non-blocking socket timeout
pass
You have not measure the latency, so how can you know if changing something makes a difference.
# Code for laptop
import socket
import time
import keyboard
import pygame
# Controller stuff
pygame.init()
pygame.joystick.init()
joystick = pygame.joystick.Joystick(0)
joystick.init()
print(str(joystick.get_button(1)))
# Create a socket object
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Get the hostname of the ESP32
host = '192.168.4.1' # Replace with the hostname or IP address of your ESP32
port = 80 # Same port number used in the server script
# Connect to the server
client_socket.connect((host, port))
recvs = 0
while True:
pygame.event.get()
isShooting = (joystick.get_button(2) or joystick.get_button(3))
outputstr = str(joystick.get_axis(0))[:5]+","+str(-joystick.get_axis(1))[:5] +","+str(joystick.get_button(1))
client_socket.send(outputstr.encode())
time.sleep(0.25)
# print(recvs)
# recvs += 1
# Close the socket
client_socket.close()
pygame.quit()
I have to put time.sleep(0.25) in the laptop code. Any less the received string will start to pile together.
So the slowness is in the processing, not in the message delivery.
That makes sense. I'll look more into improving efficiency. I'll also try arduino c++ because I heard it's a lot faster than micropython.