Advices in socket comunication

 def on_new_com_data(self, *args, **kwargs):
        """
        To handle mental command data emitted from Cortex
        
        Returns
        -------
        data: dictionary
             the format such as {'action': 'neutral', 'power': 0.0, 'time': 1590736942.8479}
        """
        data = kwargs.get('data')
        #print(data['mc data: {}'.format(data)])
        if data:
           # self.guardar_datos_en_csv(data, 'C:/Users/James/Desktop/cortex-v2-example-master/datos.txt')

            # Agregar el comando actual al historial
            command = data.get('action', '').lower()  # 
            self.command_history.append(command)

            # Check if it has been at least 8 segueing since the last impression
            current_time = time.time()
            if current_time - self.last_print_time >= 8:
                
                counts = {word: self.command_history.count(word) for word in self.target_words}
                # Check if most of the commands are any of the target words
                majority_word = max(counts, key=counts.get)
                majority_count = counts[majority_word]

                if majority_count >= 20:  # If 20 or more of the last 40 samples are of any target word
                    self.guardar_datos_en_csv(majority_word, 'C:/Users/James/Desktop/cortex-v2-example-master/datos.txt')
                    print(f"Mayority of command are: {majority_word}")
                    comando = majority_word
                    comando_encoded = comando.encode('utf-8')
                    self.envioporsocket(comando_encoded)
                    self.last_print_time = current_time  # Update the record of the last moment of printing



 def envioporsocket(self,comando): #here we will be the client, the server will be the VM
        host = '192.168.1.103'
        port = 5000
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
            s.connect((host, port))
            s.sendall(comando)

Hi guys!, im working on a bci interface to drive a drone with mental commands. this is part of my code(Client Side), im having some troubles… if i send the commands to the virtual machine using static commands like in the next code, it works good and the drone keeps flying waiting for the commands

import socket
import time

# Configura el servidor y el puerto al que te estás conectando
server_ip = '192.168.1.103'  # Reemplaza con la dirección IP del servidor
server_port = 5000

# Crea un socket
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# Conéctate al servidor
client_socket.connect((server_ip, server_port))

# Define los comandos que deseas enviar al servidor
comandos = ["neutral", "right", "left","right", "land"]

for comando in comandos:
            # Envía el comando al servidor
    client_socket.send(comando.encode('utf-8'))
    print(f"Comando enviado: {comando}")
    time.sleep(15)  # Espera 8 segundos antes de enviar el siguiente comando
    if comando == 'land':
        break

But… if i send them using the client side(def on _new_com) drone doesnt keeps in the air and lands by itself and takeoff to execute the command and lands again, and i expected a big delay

here is the server side code(Drone API):

def manejar_comandos():
    # Configura el servidor socket
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.bind(('0.0.0.0', 5000))
    server_socket.listen(1)
    print("Servidor socket activo. Esperando comandos...")

    # Inicializa el Crazyflie
    with SyncCrazyflie(URI, cf=Crazyflie(rw_cache='./cache')) as scf:
        mc = MotionCommander(scf)

        while True:
            client_socket, client_address = server_socket.accept()
            print("Cliente conectado desde:", client_address)
            mc.take_off()
            print("Despegando")
            
            while True:
                command_bytes = client_socket.recv(1024)
                if not command_bytes:
                    break  # Si no se reciben datos, salimos del bucle interno

                command = command_bytes.decode('utf-8')
                print(f"Comando recibido: {command}")

                    # Procesa y ejecuta el comando
                if command == "neutral":
                    print("NNo hago nada")
                    time.sleep(3)
                elif command == "right":
                    mc.right(0.3)
                    print("Girando a la derecha")
                    time.sleep(3)
                        
                elif command == "left":
                    mc.left(0.3)
                    print("Girando a la izquierda")
                    time.sleep(3)
                        
                elif command == "up":
                    #mc.up(0.2)
                    print("Movimiento hacia arriba")
                    time.sleep(1)
                    mc.stop()
                elif command == "down":
                    #mc.down(0.2)
                    print("Movimiento hacia abajo")
                    time.sleep(1)
                    mc.stop()
                else:
                    mc.stop()
                    mc.land()
                    print("Aterrizando por comando desconocido")
                    break
                        

            print("Cliente desconectado.")
            mc.stop()
            mc.land()
            print("Aterrizando el Crazyflie por romper el try")
                
        
if __name__ == '__main__':
    cflib.crtp.init_drivers()

    # Inicia un hilo para manejar los comandos
    thread = threading.Thread(target=manejar_comandos)
    thread.start()
    thread.join()

Im open to change things to optimize my code, im not very skilled programming but i try :wink:

Your socket server is entirely sequential, and keeps blocking. It waits for a signal, and after responding to a message, waits a specific length of time before responding. If you like, this means your program doesn’t know how to walk and chew gum at the same time.

What you’ll want to do here is either use two threads (one to respond to commands and one to keep moving the drone around), or switch to non-blocking I/O. Personally, I would recommend the second option, using the asyncio module in Python; it’s going to be a bit of work to learn how to do things differently, but it’s so worthwhile.

1 Like

hi!, thanks for your answer! :raised_hands: . when you say switch to non blocking I/O what do you mean exactly? use Queue ? buffering?

the drone would move only an X distance and stay hovering waiting for the next command.

No, it’s a specific thing called non-blocking I/O, you can see how it works in the socket module. The best way to do it would be asyncio. Look up a tutorial on socket programming in asyncio and see how it goes for you.

1 Like

Cool project!

To see why there is a difference between your client and your static commands, note where your socket connects and disconnects are.

In your static commands snippet, it goes like this (pseudocode):

create socket
connect socket

for command in commands
    send command

close socket

In your client, it goes like this:

when a command is detected:
    call function to send command

in the function:
    create socket
    connect socket
    send command 
    close socket

In other words, in the static snippet multiple commands are sent during one connection to the server: connect -> command -> command -> command -> disconnect

In the client, a new connection is made for each command: connect -> command -> disconnect, connect -> command -> disconnect, connect -> command -> disconnect

The server loops are structured so that the drone takes off when the client connects, executes commands, then lands when the client disconnects (or sends the command to land).

loop 1:
    accept connection
    take off

    loop 2:
        try to receive command
        if no data received:
            break loop 2
        do command

    stop
    land

All of loop 1 will execute each time the client connects, sends a single command, then disconnects. That is my theory, based on you seeing your drone take off then land when you try to use the client.


What @Rosuav says about the server is also true, and using asyncio will make it more reliable and responsive. But for a first version what you have is pretty good, and seems very close to doing what you want, as long as you don’t need the client and server to be very fast at responding.

1 Like

hi guys, thank you so much for the advices:

about using 2 threads: 1 for the connection with the server that keeps “alive” my socket connection and
1 to execute the command execution based on what i recieve in my socket (is a word like “forward, right,left”) meanwhile the connection is alive, right?

about using asyncio, i found it very interesting because i saw that i can make this::

here is a pseudocode

run script 
	
	execute the connection(socket connection)
	"stop"  
	syncronizes the dron
	"stop"
        take off
        "stop"
	execute function that move drone based on the word and break when there is nothing (a loop)
	"stop"
	lands
	"stops"
	Ends the connection

Correct! Each thread has a loop and each is responsible for one thing, like you said one for handling the socket connection and another for executing drone commands. The tricky bit is communicating between the threads, because the socket thread needs to be able to tell the drone control thread when it has received a new command, and you need to be able to tell your threads when to stop their loops (otherwise they will loop forever, waiting.)

In Python the easiest way to do this is probably threading.Queue. You build the queue and pass it to each thread, and the socket thread only adds commands to the queue and the drone control thread only removes them.

async/await lets you write code that looks very linear but can actually manage multiple I/O tasks at once. Behind the scenes a loop is running that can change tasks depending on whether they are waiting for slow I/O like network or files. With asyncio you would probably keep all your server code together instead of splitting it into two loops like with the threads. In many situations async code will be smaller and less complicated than doing the same task with threads because the event loop is doing a lot of work for you.

But also, @Rosuav knows a lot more about asyncio than I do and they may have a better idea how to organize your server code if you try asyncio and have questions.

1 Like

cool,today im going to do some test with this code i made helping myself using Youtube Tutorials and IA too.

import asyncio
import socket
from cflib.crazyflie import Crazyflie
from cflib.positioning.motion_commander import MotionCommander
from cflib.crazyflie.syncCrazyflie import SyncCrazyflie

async def manejar_comandos(client_socket, addr):
    print(f"Cliente conectado desde: {addr}")
    
    # Inicializa el Crazyflie
    async with SyncCrazyflie(URI, cf=Crazyflie(rw_cache='./cache')) as scf:
        mc = MotionCommander(scf)

        try:
            while True:
                command_bytes = await loop.sock_recv(client_socket, 1024)
                if not command_bytes:
                    break  # Si no se reciben datos, salimos del bucle

                command = command_bytes.decode('utf-8')
                print(f"Comando recibido de {addr}: {command}")

                # Procesa y ejecuta el comando
                if command == "neutral":
                    print("No hago nada")
                    await asyncio.sleep(3)
                elif command == "right":
                    mc.right(0.3)
                    print("Girando a la derecha")
                    await asyncio.sleep(3)
                elif command == "left":
                    mc.left(0.3)
                    print("Girando a la izquierda")
                    await asyncio.sleep(3)
                elif command == "up":
                    #mc.up(0.2)
                    print("Movimiento hacia arriba")
                    await asyncio.sleep(1)
                    mc.stop()
                elif command == "down":
                    #mc.down(0.2)
                    print("Movimiento hacia abajo")
                    await asyncio.sleep(1)
                    mc.stop()
                else:
                    mc.stop()
                    mc.land()
                    print("Aterrizando por comando desconocido")
                    break
        except Exception as e:
            print(f"Error al manejar comandos para {addr}: {e}")

    print(f"Cliente {addr} desconectado.")
    mc.stop()
    mc.land()
    print("Aterrizando el Crazyflie")

async def main():
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.bind(('0.0.0.0', 5000))
    server_socket.listen(1)  # Espera una única conexión
    print("Servidor socket activo. Esperando comandos...")

    client_socket, addr = await loop.sock_accept(server_socket)
    await manejar_comandos(client_socket, addr)

if __name__ == "__main__":
    URI = "radio://0/80/2M/E7E7E7E7E7"
    
    # Configura el bucle de eventos asyncio
    loop = asyncio.get_event_loop()
    server_coroutine = main()

    try:
        loop.run_until_complete(server_coroutine)
    finally:
        loop.close()

I gotta still check some details on the code but, i will test it today and see if i dont broke the dron :sweat_smile: :joy: that try i will quit it, and i will keep in touch with you guys. any more advices im here :raised_hands: