(Help needed) interacting with a Python script (WiFi)

User avatar
aspatru
Posts: 1
Joined: Sun Aug 13, 2023 1:01 am

(Help needed) interacting with a Python script (WiFi)

Postby aspatru » Sun Aug 13, 2023 1:36 am

Hi,

I'm building a toy car robot with an ESP-WROOM-32 MCU, using ultrasonic distance sensors and a DQN model. I've implemented a system where the ESP32 receives an "action" from a Python script, performs it, and then sends back the robot's "state" to the script. My problem lies in the TCP socket communication; it's too slow and often disconnects, hindering the training process.

Here's an outline of my setup:
  • ESP32: Handles WiFi communications on core 1, other tasks like sensor reading on core 0. It initializes a WiFi server and listens for incoming connections to receive actions and send states.
    Python Client: Sends commands to the robot and tries to receive data back. I've included code to handle reconnections, but it's not efficient and relies heavily on time delays.

For a more detailed look at the code, here is the WiFi task code:
  1. void task_remote_operation(void *pv_parameters) {
  2.   // Initialize variables for implementing a simple watchdog
  3.   uint64_t watchdog_time = millis();
  4.  
  5.   // Initialize TCP server
  6.   WiFiServer server(SRVR_PORT);
  7.   server.begin();
  8.  
  9.   // Infinite loop
  10.   while (true) {
  11.     // Listen for incoming connections
  12.     WiFiClient client = server.available();
  13.     if (client) {
  14.       // Reset watchdog
  15.       watchdog_time = millis();
  16.  
  17.       // Disable combining different messages in the same packet
  18.       client.setNoDelay(true);
  19.  
  20.       // Inhibit neural network from controling the robot
  21.       if (!STATE.teleop) {
  22.         if (xSemaphoreTake(MUTEX, portMAX_DELAY) == pdTRUE) {
  23.           STATE.teleop = true;
  24.           xSemaphoreGive(MUTEX);
  25.         }
  26.       }
  27.  
  28.       // Enter client/robot communication loop
  29.       while (client.connected()) {
  30.         if (client.available()) {
  31.           // Parse incoming action command
  32.           String action_str = client.readStringUntil('\n');
  33.           action_str.trim();
  34.           parse_action(&action_str);
  35.  
  36.           // Send acknowledgement to training process
  37.           client.print("ACK");
  38.  
  39.           // Send state string
  40.           String state_str = encode_state();
  41.           state_str.trim();
  42.           client.println(state_str);
  43.  
  44.           // Stop watchdog from reseting the motor outputs
  45.           watchdog_time = millis();
  46.         }
  47.  
  48.         // Stop the robot if training computer is disconnected
  49.         if (millis() - watchdog_time > 3000) {
  50.           if (xSemaphoreTake(MUTEX, portMAX_DELAY) == pdTRUE) {
  51.             reset_state();
  52.             xSemaphoreGive(MUTEX);
  53.           }
  54.  
  55.           watchdog_time = millis();
  56.         }
  57.  
  58.         // Wait a little
  59.         vTaskDelay(100 / portTICK_PERIOD_MS);
  60.       }
  61.  
  62.       // Connection with client stopped, reset state
  63.       if (xSemaphoreTake(MUTEX, portMAX_DELAY) == pdTRUE) {
  64.         reset_state();
  65.         xSemaphoreGive(MUTEX);
  66.       }
  67.  
  68.       // Disonnect from the client if required
  69.       client.stop();
  70.     }
  71.  
  72.     // Wait a little
  73.     else
  74.       vTaskDelay(200 / portTICK_PERIOD_MS);
  75.   }
  76. }

In my setup function, I make sure that the WiFi AP is initialized before creating any tasks, here is the code that I use:

  1. // Create Wi-Fi access point
  2. if (!WiFi.softAP(WIFI_SSID, WIFI_PASS)) {
  3.   while (true)
  4.     delay(1);
  5. }

In my Python project, I implemented a class that connects to the ESP32 and automatically re-opens the connection if it fails:

  1. import time
  2. import socket
  3. import numpy as np
  4.  
  5. class Client:
  6.     """
  7.    A client class to handle real-time interaction with a microcontroller via
  8.    TCP connection. The class sends actions and receives states, ensuring
  9.    efficient communication with the physical robot's actions during the
  10.    training phase.
  11.  
  12.    Attributes:
  13.        host (str): The IP address of the microcontroller.
  14.        port (int): The port number for the TCP connection.
  15.        client (socket): The socket object used to manage the TCP connection.
  16.    """
  17.  
  18.     def __init__(self, host='192.168.4.1', port=80):
  19.         """
  20.        Initializes the client with given host and port.
  21.  
  22.        Args:
  23.            host (str, optional): The IP address of the microcontroller.
  24.            port (int, optional): The port number for the TCP connection.
  25.        """
  26.  
  27.         self.host = host
  28.         self.port = port
  29.         self.client = self.connect()
  30.  
  31.     def connect(self):
  32.         """
  33.        Creates and returns a socket object to manage the TCP connection.
  34.  
  35.        Returns:
  36.            socket: The connected socket object.
  37.        """
  38.  
  39.         client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  40.         client.connect((self.host, self.port))
  41.         client.settimeout(5)
  42.         return client
  43.  
  44.     def close(self):
  45.         """Closes the TCP connection."""
  46.         self.client.close()
  47.  
  48.     def reconnect(self):
  49.         """Closes and reestablishes the TCP connection."""
  50.         self.client.close()
  51.         time.sleep(1)
  52.         self.client = self.connect()
  53.  
  54.     def write(self, command, silent):
  55.         """
  56.        Sends a command to the microcontroller, then waits for acknowledgment
  57.        and finally receives data.
  58.  
  59.        Args:
  60.            command (str): The command to send to the microcontroller.
  61.            silent (bool): If False, prints debug information.
  62.  
  63.        Returns:
  64.            str: The data received from the microcontroller.
  65.        """
  66.  
  67.         data = ''
  68.         retries = 5
  69.         while retries > 0 and data == '':
  70.             try:
  71.                 self.client.sendall(command.encode())
  72.                 acknowledgment = self.client.recv(32).decode().strip()
  73.                 if acknowledgment != 'ACK':
  74.                     raise ConnectionError('Invalid acknowledgment from ESP32')
  75.                
  76.                 raw = self.client.recv(1024).decode()
  77.                 data = raw.replace('\r', '').replace('\n', '')
  78.                 break
  79.  
  80.             except (socket.timeout,
  81.                     BrokenPipeError,
  82.                     ConnectionResetError,
  83.                     ConnectionError):
  84.                 retries -= 1
  85.                 self.reconnect()
  86.  
  87.         return data
  88.  
  89.     def send_action(self, action, silent=True):
  90.         """
  91.        Transmits an action to the microcontroller and receives the
  92.        corresponding state from the robot.
  93.  
  94.        Args:
  95.            action (np.array): The action to transmit.
  96.            silent (bool, optional): If False, prints debug information.
  97.  
  98.        Returns:
  99.            np.array: The state received from the microcontroller.
  100.        """
  101.  
  102.         frame = ','.join(map(str, action.flatten())) + '\n'
  103.         response = self.write(frame, silent)
  104.  
  105.         if response == '':
  106.             return self.send_action(action, silent)
  107.  
  108.         if not silent:
  109.             print('[TX] ', end='')
  110.             print(action)
  111.  
  112.         state = np.array(list(map(float, response.split(','))))
  113.  
  114.         if not silent:
  115.             print('[RX] ', end='')
  116.             print(state)
  117.             print('')
  118.  
  119.         return state
The code works briefly but from time to time it disconnects or is slow to send/receive data. How can I improve the stability and speed of this connection? Any hints or recommendations are highly welcome.

Thanks,
Alex Spataru

Who is online

Users browsing this forum: No registered users and 59 guests