This is an old revision of the document!
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 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 ((millis() - lastTime) > timerDelay) { // 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 }