Author: Stan Ionut Razvan
Email: ionut_razvan.stan@stud.acs.upb.ro
Master: SRIC
This project uses an ESP32 that reads inputs from sensors for a racing video game. It reads up, down, left and right inputs and sends them forward through HTTP to the game. The game has an HTTP endpoint accepting POST requests with inputs from the card. It tries to keep the game input state as close in sync as possible to the board.
The ESP32 board in this figure :
* connects to the local WIFI adddress
* reads the inputs from the obstacle sensors that are meant to represent the left, right movement. The board reads their analog values and if the value is below a threshold it considers that input (left or right) to be pressed
* reads the inputs from the buttons that represent the forward / backward inputs
* sends every X milliseconds a POST request to a specified endpoint sending a combination of the following strings separated by space: “LEFT”, “RIGHT”, “FORWARD”, “BACKWARD”. If a request misses a specific direction it is considered that the input is not being pressed
The frontend game side:
* reads the POST request from the ESP32 board through a HTTP server running on a separate thread, parses the request and modifies its local variables given the inputs.
* displays the game and adapts to the inputs given in a concurrently safe way
The ESP 32 Dev Module is plugged into a breadboard, with the ground pin connected to the power rail, as well as the 5V pin. There are two IR FC-51 Sensors on a smaller breadboard, with the ground pins connected to the ground rail, the VCC pins connected to the 5V rail, and the output of the “left” sensor to the 34th GPIO pin, “right” being pin 35. In addition, two buttons, connected on another breadboard for “forward” and “back”. They get ground from the main ground rail, and their other connectors are plugged into pins 27 and 26.
The Hardware is composed of :
* ESP32 board
* 2x Infrared sensor modules for obstacles
* Button modules
* Breadboard HQ
* 2x Mini Breadboard
* Laptop that can run the video game
The ESP32 code uses an HTTP library to send post requests gotten through platformio:
#define DISTANCE_THRESHHOLD 3000
void loop() {
if 1)
// Check WiFi connection status if (WiFi.status() == WL_CONNECTED) { HTTPClient http;
String serverPath = serverName; // eventually add + "something?="
http.begin(serverPath.c_str());
float distanceLeft = analogRead(irSensorPin1); float distanceRight = analogRead(irSensorPin2); bool forwardButton = digitalRead(forwardButtonPin) == LOW; bool backwardButton = digitalRead(backwardButtonPin) == LOW;
String postData = "";
// if (distanceLeft < 3000 && distanceRight < 3000) //{ // Serial.println("FORWARD"); // postData = "FORWARD"; // } if (distanceLeft < DISTANCE_THRESHHOLD) { // Serial.println("LEFT"); postData += " LEFT"; } if (distanceRight < DISTANCE_THRESHHOLD) { // Serial.println("RIGHT"); postData += " RIGHT"; } if (forwardButton) { // Serial.println("FORWARD"); postData += " FORWARD"; }
if (backwardButton) { postData += " BACKWARD"; }
Serial.println(postData);
int httpResponseCode = http.POST(postData);
if (httpResponseCode > 0) { Serial.print("HTTP Response code: "); Serial.println(httpResponseCode); String payload = http.getString(); Serial.println(payload); } else { Serial.print("Error code: "); Serial.println(httpResponseCode); } // Free resources http.end(); } else { Serial.println("WiFi Disconnected"); } lastTime = millis(); }}
The game frontend uses the standard HTTP library from Golang to listen to HTTP requests at a certain point. It also uses Raylib (through a community made binding module) to render 3D Graphics.
func handler(w http.ResponseWriter, r *http.Request) {// fmt.Printf("Received a %s request at %s\n", r.Method, r.URL.Path) // Prints for any request
body, _ := io.ReadAll(r.Body) fmt.Printf("Body: %s \n", body) bodyStr := string(body) // bodyStr = strings.ReplaceAll(bodyStr, " ", "") dirMutex.Lock() defer dirMutex.Unlock()
directions := strings.Split(bodyStr, " ") isLeftPressed = false isRightPressed = false isUpPressed = false isDownPressed = false for _, direction := range directions { if direction == DIRECTION_LEFT_STR { isLeftPressed = true } if direction == DIRECTION_RIGHT_STR { isRightPressed = true } if direction == DIRECTION_UP_STR { isUpPressed = true } if direction == DIRECTION_DOWN_STR { isDownPressed = true } } fmt.Fprintf(w, "Request body: %s", bodyStr)} func listeningServer() {
http.HandleFunc("/post", handler) // Handles any path after "/" fmt.Println("Server listening on :8080") http.ListenAndServe("192.168.1.129:8080", nil)} var trackVerticalTexture rl.Texture2D var trackHorizontalTexture rl.Texture2D var trackCornerRDTexture rl.Texture2D var trackCornerDRTexture rl.Texture2D const CHRONOMETER_MAX_TIME = 120.0 func main() {
screenWidth := int32(1600) screenHeight := int32(900)other code } ''