fbpixel
Los distintos protocolos de comunicación

Los distintos protocolos de comunicación

Sea cual sea tu proyecto de electrónica, programación o domótica, seguro que utilizas un protocolo de comunicación. Ya sea para programar el microcontrolador o para comunicarse con un sensor. Este artículo presenta varios protocolos de comunicación comúnmente utilizados en Arduino, Raspberry Pi y ESP8266/ESP32.

Bus de comunicación serie

Puerto serie

USB

USB (Universal Serial Bus) es un estándar de bus de comunicación utilizado para intercambiar datos entre periféricos informáticos. La particularidad de este estándar es que permite conectar dispositivos mientras están en funcionamiento y posibilita el reconocimiento automático del periférico).

UART

UART (Universal Asynchronous Receiver Transmitter) es el estándar que especifica cómo se envían los datos a través del puerto serie.

RS-232

El protocolo RS-232 es un protocolo de comunicación que define la conectividad y permite la comunicación asíncrona y dúplex entre dos equipos. La particularidad de este protocolo es que utiliza tensiones de 3 a 25 V para transmitir datos, lo que lo convierte en un bus menos sensible a las interferencias y al ruido.

(RS232/RS422/RS485)

I2C

El bus de comunicación I2C es un protocolo que permite conectar varios dispositivos “Maestros” a varios dispositivos “Esclavos”, posibilitando la comunicación de hasta 128 dispositivos. Permite conexiones asíncronas entre varios componentes para compartir información a través de un “bus común”. Este protocolo se utiliza generalmente para intercambios de placa a placa, pero puede utilizarse en distancias más largas.

SPI

SPI (Serial Peripheral Interface) es un bus de datos serie que funciona en modo full-duplex, lo que significa que puede transmitir y recibir datos al mismo tiempo. Utiliza una arquitectura maestro-esclavo y el esclavo se selecciona mediante una línea dedicada.

CAN

El bus CAN (Controller Area Network) es un bus de comunicación en serie muy utilizado en la industria del automóvil. Permite multiplexar distintos dispositivos para que se comuniquen a través del mismo bus. Esto reduce la cantidad y la complejidad del cableado.

Ethernet

Ethernet es un protocolo de comunicaciones por cable que intercambia datos en paquetes a alta velocidad.

(I2S)

MIDI

MIDI (Musical Instrument Digital Interface) es un protocolo de comunicación entre instrumentos electrónicos, controladores y software musical. Consiste en enviar una serie de bytes para especificar el tipo de mensaje y la información asociada (nota, duración de la nota, instrumento, etc.).

Protocolos de comunicación inalámbrica

Bluetooth

BLE

La red BLE (Bluetooth Low Energy) es una red Bluetooth de bajo consumo energético.

Wifi

ESP-NOW

Protocolo de comunicación de bajo consumo desarrollado por Espressif que utiliza ondas de 2,4 GHz.

RF 433 MHz

RF 2,4 GHz

(Zigbee)

LoRaWAN

La red Lora (Long Range Wide-area network) es una red de radio que permite a los dispositivos de bajo consumo comunicarse a baja velocidad. Esto hace posible disponer de objetos conectados con una importante autonomía.

Ahora tiene una visión completa de los protocolos de comunicación por cable e inalámbricos más utilizados en electrónica e informática.

Comunicación Bluetooth con ESP32

Comunicación Bluetooth con ESP32

En este tutorial aprenderemos a activar, gestionar y probar Bluetooth en un ESP32 utilizando el lenguaje de programación Arduino. Bluetooth es una tecnología inalámbrica ampliamente utilizada para la comunicación entre dispositivos electrónicos. Puedes convertir rápidamente tu sistema en un objeto conectado.

Equipamiento

  • Un módulo ESP32 (Bluetooth+Wifi a bordo)
  • Un ordenador con Python instalado o un smartphone
  • Cable USB para conexión ESP32-ordenador

Configuración del entorno y del IDE

Para programar tu ESP32 con el IDE de Arduino, puedes seguir este tutorial anterior.

Recuperar dirección MAC

Esta información no es necesariamente necesaria, pero siempre es una buena idea saber cómo recuperar la dirección MAC del ESP32.

#include "esp_bt_main.h"
#include "esp_bt_device.h"
#include "BluetoothSerial.h"

BluetoothSerial SerialBT;

void printDeviceAddress() {
  const uint8_t* point = esp_bt_dev_get_address();
  for (int i = 0; i < 6; i++) {
    char str[3];
    sprintf(str, "%02X", (int)point[i]);
    Serial.print(str);
    if (i < 5){
      Serial.print(":");
    }
  }
}
void setup() {
  Serial.begin(115200);
  SerialBT.begin("ESP32BT");
  Serial.print("MaxAddr : ");
  printDeviceAddress();

}

void loop() {}

Salida

14:42:43.448 -> MaxAddr : 3C:61:05:31:5F:12

Comunicación serie por Bluetooth

La comunicación Bluetooth se activa del mismo modo que la comunicación serie. El método es similar para el módulo HC-06

#include "BluetoothSerial.h"

#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED)
#error Bluetooth is not enabled! Please run `make menuconfig` to and enable it
#endif

BluetoothSerial SerialBT;

void setup() {
  Serial.begin(115200);
  SerialBT.begin("ESP32BT"); //Bluetooth device name
  Serial.println("The device started, now you can pair it with bluetooth!");
}

void loop() {
  if (Serial.available()) {
    SerialBT.write(Serial.read());
  }
  if (SerialBT.available()) {
    Serial.write(SerialBT.read());
  }
  delay(20);
}

N.B.: Parece que hay una forma de añadir un código PIN, pero no consigo que funcione.

SerialBT.setPin(pin);SerialBT.begin("ESP32BT ", true);

Emparejamiento

Una vez configurado el módulo como desee, puede emparejar el ESP32 con el sistema de su elección como cualquier otro dispositivo Bluetooth. Seleccione el nombre de la lista de dispositivos detectados (nombre ESP32BT)

Comprobación de la comunicación Bluetooth mediante un terminal Bluetooth serie

Vamos a probar la comunicación Bluetooth utilizando la aplicación Serial Bluetooth Terminal.

El mensaje se intercambia entre el teléfono y el ESP32 a través de Bluetooth

Código para recuperar el pedido completo

En el código anterior, copiamos el mensaje byte a byte para enviarlo al monitor. Aquí vamos a registrar el comando completo en un String msg. Esto nos permitirá analizar el comando y definir la acción correspondiente (por ejemplo, encender

#include "BluetoothSerial.h"

#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED)
#error Bluetooth is not enabled! Please run `make menuconfig` to and enable it
#endif
String msg;

BluetoothSerial SerialBT;
const char *pin = "1234";

void setup() {
  Serial.begin(115200);
  SerialBT.begin("ESP32BT"); //Bluetooth device name
  Serial.println("ESP32BT device started, now you can pair it!");
}

void loop(){
  readSerialPort();
  
  // Send answer to master
  if(msg!=""){
    Serial.print("Master sent : " );
    Serial.println(msg);
    SerialBT.print("received : "+msg);
    msg=""; 
  }
}

void readSerialPort(){
 while (SerialBT.available()) {
   delay(10); 
   if (SerialBT.available() >0) {
     char c = SerialBT.read();  //gets one byte from serial buffer
     msg += c; //makes the string readString
   }
 }
 SerialBT.flush();
}

Comunicación entre ESP32 y Python a través de Bluetooth

Puedes gestionar la comunicación Bluetooth desde tu PC.

Para ello, instale el paquete PyBluez

python -m pip install pybluez

Detectar dispositivos bluetooth

import bluetooth

target_name = "ESP32BT"
target_address = None

nearby_devices = bluetooth.discover_devices(lookup_names=True,lookup_class=True)
print(nearby_devices)
for btaddr, btname, btclass in nearby_devices:
	if target_name == btname:
		target_address = btaddr
		break

if target_address is not None:
	print("found target {} bluetooth device with address {} ".format(target_name,target_address))
else:
	print("could not find target bluetooth device nearby")

Salida

[('88:C6:26:91:30:84', 'UE\xa0BOOM\xa02', 2360344), ('88:C6:26:7E:F2:7A', 'UE\xa0BOOM\xa02', 2360344), ('4C:EA:AE:D6:92:08', 'OPPO A94 5G', 5898764), ('41:42:DD:1F:45:69', 'MX_light', 2360344), ('3C:61:05:31:5F:12', 'ESP32BT', 7936)]
found target ESP32BT bluetooth device with address 3C:61:05:31:5F:12

Conexión y comunicación con el ESP32

He aquí un script en Python para conectarse automáticamente al dispositivo Bluetooth ESP32 desde un PC

import bluetooth
import socket

target_name = "ESP32BT"
target_address = None

nearby_devices = bluetooth.discover_devices(lookup_names=True,lookup_class=True)
print(nearby_devices)
for btaddr, btname, btclass in nearby_devices:
	if target_name == btname:
		target_address = btaddr
		break

if target_address is not None:
	print("found target {} bluetooth device with address {} ".format(target_name,target_address))

	""" # With PyBluez NOT WORKING
	serverMACAddress = target_address
	port = 1
	s = bluetooth.BluetoothSocket(bluetooth.RFCOMM)
	s.connect((serverMACAddress, port))
	while 1:
		text = raw_input() # Note change to the old (Python 2) raw_input
		if text == "quit":
			break
		s.send(text)
		data = s.recv(1024)
		if data:
			print(data)
	sock.close()"""
	
	
	
	serverMACAddress = target_address
	port = 1
	s = socket.socket(socket.AF_BLUETOOTH, socket.SOCK_STREAM, socket.BTPROTO_RFCOMM)
	s.connect((serverMACAddress,port))
	print("connected to {}".format(target_name))
	while 1:
		text = input()
		if text == "quit":
			break
		s.send(bytes(text, 'UTF-8'))
		data = s.recv(1024)
		if data:
			print(data)
	s.close()

else:
	print("could not find target bluetooth device nearby")



N.B.: Sólo la librería socket funciona para la comunicación Bluetooth. Parece que hay un problema de mantenimiento con la biblioteca PyBluez.

Aplicaciones

Fuentes

Configuración de una Raspberry Pi como punto de acceso WiFi

Configuración de una Raspberry Pi como punto de acceso WiFi

La Raspberry Pi se puede configurar como un punto de acceso Wi-Fi. Esta función es útil cuando la Raspberry Pi no tiene acceso a una red WiFi y desea conectarse a ella localmente. En este tutorial, veremos cómo configurar la Raspberry Pi para generar su propia red WiFi.

Hardware

  • Raspberry Pi (3B+, 4)
  • Tarjeta SD de 32 GB
  • Alimentación 5V 3A
  • Pantalla HDMI+teclado+ratón o acceso remoto (SSH, VNC, NoMachine)

Instalación de paquetes para la configuración de puntos de acceso

sudo apt update && sudo apt upgrade

Para configurar el punto de acceso, es necesario instalar los siguientes paquetes

sudo apt install hostapd dnsmasq

A continuación, puede desenmascarar y activar el servicio hostapd

sudo systemctl unmask hostapd
sudo systemctl enable hostapd

Para el resto de este tutorial, tenemos que elegir una dirección IP fija para la Raspberry Pi. Aquí elegimos 10.142.2.1

Configuración de la Raspberry Pi como punto de acceso (Headless)

  • Configurar wpa_supplicant
sudo nano /etc/wpa_supplicant/wpa_supplicant.conf
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
country=FR

network={
	ssid="NETWORK-NAME"
	psk="NETWORK-PWD"
}
  • Configurar dhcpcd
sudo nano /etc/dhcpcd.conf
# RaspAP default configuration
hostname
clientid
persistent
option rapid_commit
option domain_name_servers, domain_name, domain_search, host_name
option classless_static_routes
option ntp_servers
require dhcp_server_identifier
slaac private

# RaspAP wlan0 configuration
interface wlan0
    static ip_address=10.142.2.1/24
    nohook wpa_supplicant
  • configurar hostapd
sudo nano /etc/hostapd/hostapd.conf
interface=wlan0
ssid=NETWORK-NAME
wpa_passphrase=NETWORK-PWD
country_code=FR
wpa=2
wpa_key_mgmt=WPA-PSK
wpa_pairwise=TKIP CCMP
wpa_pairwise=CCMP
driver=nl80211
hw_mode=g
channel=7
wmm_enabled=0
macaddr_acl=0
auth_algs=1
ignore_broadcast_ssid=0
## RaspAP wireless client AP mode
#interface=uap0

## RaspAP bridge AP mode (disabled by default)
#bridge=br0

Para activar el servicio hostapd, debe especificar la ruta al archivo de configuración

sudo nano /etc/default/hostapd

Descomente la línea DAEMON-CONF y añada la ruta al archivo de configuración

DAEMON_CONF="/etc/hostapd/hostapd.conf"
  • configurar dnsmasq

En el archivo dnsmasq.conf, especificaremos un conjunto de direcciones IP disponibles

sudo nano /etc/dnsmasq.conf
interface=wlan0
dhcp-rang=10.142.2.10,10.142.2.200,255.255.255.0,24h
domain=wlan
address=/gw.wlan/10.142.2.1

A continuación, puede reiniciar su Raspberry para activar el punto de acceso. El icono con las flechas opuestas debería aparecer en el escritorio de la Raspberry.

Y el punto de acceso debe estar disponible en los dispositivos dentro del alcance de la red

Resúmenes de pedidos

sudo apt update && sudo apt upgrade
sudo apt install hostapd dnsmasq
sudo nano /etc/wpa_supplicant/wpa_supplicant.conf
sudo nano /etc/dhcpcd.conf
sudo nano /etc/hostapd/hostapd.conf
sudo nano /etc/default/hostapd

Desactivar el modo AP

Para desactivar el modo AP y conectarse a WiFi, basta con comentar los cambios en el archivo

Bonus: Elige un alias local para tu dirección IP

Una vez creado nuestro Punto de Acceso, puedes darle un alias a tu dirección IP. Esto le permite escribir una URL (domain.com) en lugar de la dirección IP para acceder a la Raspberry Pi

Para crear un alias local, definiremos el alias en dos archivos diferentes y lo vincularemos a la dirección IP

  • En /etc/dnsmasq.conf
sudo nano /etc/dnsmasq.conf

Al final del archivo, añada la siguiente línea con su dirección IP y el nombre de dominio deseado

cname=midominio.com, 10.142.2.1
  • En el archivo /etc/hosts
sudo nano /etc/hosts

Al final del archivo, copie la siguiente línea con su dirección IP y el nombre de dominio deseado

10.142.2.1 midominio.com

Después de reiniciar la Raspberry Pi, puede comprobar la configuración conectándose a través de SSH (no olvide conectarse al punto de acceso de la Raspberry Pi).

ssh pi@mydomain.com

Este procedimiento es muy útil cuando se desea crear un Servidor Web local en su Raspberry Pi. Esto puede permitirte acceder a una página web, alojada en la Raspberry Pi y servida por Flask, escribiendo una URL en tu navegador web cuando estés conectado a la misma red.

Fuentes

Comunicación Bluetooth con ESP32

Comunicación BLE con ESP32

En este tutorial, vamos a aprender a activar y gestionar Bluetooth Low Energy (BLE) en un ESP32 utilizando el lenguaje de programación Arduino.

Bluetooth Low Energy es una versión de baja energía de Bluetooth que permite enviar pequeños paquetes de datos a intervalos regulares.

Equipamiento

  • Un módulo ESP32 (Bluetooth+Wifi a bordo)
  • Un ordenador con Python instalado o un smartphone
  • Cable USB para conexión ESP32-ordenador

Configuración del entorno y del IDE

Para programar tu ESP32 con el IDE de Arduino, puedes seguir este tutorial anterior.

Communication Série via BLE

La comunicación BLE debe configurarse con un cierto número de direcciones (UIIDs) que son como registros de memoria en los que podemos leer y escribir.

//https://github.com/espressif/arduino-esp32/tree/master/libraries/BLE

#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>

// See the following for generating UUIDs:
// https://www.uuidgenerator.net/

#define SERVICE_UUID        "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"


class MyCallbacks: public BLECharacteristicCallbacks {
    void onWrite(BLECharacteristic *pCharacteristic) {
      std::string value = pCharacteristic->getValue();

      if (value.length() > 0) {
        Serial.println("*********");
        Serial.print("New value: ");
        for (int i = 0; i < value.length(); i++)
          Serial.print(value[i]);

        Serial.println();
        Serial.println("*********");
      }
    }
};

void setup() {
  Serial.begin(115200);

  Serial.println("1- Download and install an BLE Terminal FREE");
  Serial.println("2- Scan for BLE devices in the app");
  Serial.println("3- Connect to ESP32BLE");
  Serial.println("4- Go to CUSTOM CHARACTERISTIC in CUSTOM SERVICE and write something");

  BLEDevice::init("ESP32BLE");
  BLEServer *pServer = BLEDevice::createServer();

  BLEService *pService = pServer->createService(SERVICE_UUID);

  BLECharacteristic *pCharacteristic = pService->createCharacteristic(
                                         CHARACTERISTIC_UUID,
                                         BLECharacteristic::PROPERTY_READ |
                                         BLECharacteristic::PROPERTY_WRITE
                                       );

  pCharacteristic->setCallbacks(new MyCallbacks());

  pCharacteristic->setValue("Hello World");
  pService->start();

  BLEAdvertising *pAdvertising = pServer->getAdvertising();
  pAdvertising->start();

  Serial.print("Server address:");
  Serial.println(BLEDevice::getAddress().toString().c_str());
}

void loop() {
  // put your main code here, to run repeatedly:
  delay(2000);
}

N.B.: es posible recuperar la dirección MAC del ESP32 utilizando la función BLEDevice::getAddress().toString().c_str().

Emparejamiento

Una vez configurado el módulo como desee, puede emparejar el ESP32 con el sistema de su elección como cualquier otro dispositivo Bluetooth. Seleccione el nombre de la lista de dispositivos detectados (nombre ESP32BLE)

Prueba de la comunicación BLE con el terminal BLE

Vamos a probar la comunicación BLE utilizando la aplicación BLE Terminal.

El mensaje se intercambia entre el teléfono y el ESP32 a través de Bluetooth LE

Comunicación bidireccional entre el dispositivo y el ESP32BLE

Este es el código utilizado para modificar el valor de la característica utilizando el monitor serie. El dispositivo conectado y el monitor serie modifican el valor de la característica.

#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>

// See the following for generating UUIDs:
// https://www.uuidgenerator.net/

#define SERVICE_UUID        "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"

BLECharacteristic *pCharacteristic = NULL;

std::string msg;

class MyCallbacks: public BLECharacteristicCallbacks {
    void onWrite(BLECharacteristic *pCharacteristic) {
      std::string value = pCharacteristic->getValue();

      if (value.length() > 0) {
        Serial.println("*********");
        Serial.print("New value: ");
        for (int i = 0; i < value.length(); i++)
          Serial.print(value[i]);

        Serial.println();
        Serial.println("*********");
      }
    }
};

void setup() {
  Serial.begin(115200);

  Serial.println("1- Download and install an BLE Terminal Free");
  Serial.println("2- Scan for BLE devices in the app");
  Serial.println("3- Connect to ESP32BLE");
  Serial.println("4- Go to CUSTOM CHARACTERISTIC in CUSTOM SERVICE and write something");

  BLEDevice::init("ESP32BLE");
  BLEServer *pServer = BLEDevice::createServer();

  BLEService *pService = pServer->createService(SERVICE_UUID);

  pCharacteristic = pService->createCharacteristic(
                                         CHARACTERISTIC_UUID,
                                         BLECharacteristic::PROPERTY_READ |
                                         BLECharacteristic::PROPERTY_WRITE
                                       );

  pCharacteristic->setCallbacks(new MyCallbacks());

  pCharacteristic->setValue("Hello World");
  pService->start();

  BLEAdvertising *pAdvertising = pServer->getAdvertising();
  pAdvertising->addServiceUUID(SERVICE_UUID);
  pAdvertising->setScanResponse(true);
  pAdvertising->setMinPreferred(0x06);  // functions that help with iPhone connections issue
  pAdvertising->setMinPreferred(0x12);
  pAdvertising->start();

  Serial.print("Server address:");
  Serial.println(BLEDevice::getAddress().toString().c_str());
}

void loop() {
  readSerialPort();

  //Send data to slave
  if(msg!=""){
    pCharacteristic->setValue(msg);
    msg="";
  }
  
  delay(2000);
}

void readSerialPort(){
 while (Serial.available()) {
   delay(10);  
   if (Serial.available() >0) {
     char c = Serial.read();  //gets one byte from serial buffer
     msg += c; //add to String
   }
 }
 Serial.flush(); //clean buffer
}

En la práctica, será preferible utilizar un servicio para escribir y otro para leer.

Comunicación entre ESP32 y Python a través de BLE

Puedes gestionar la comunicación Bluetooth Low Energy desde tu PC.

Para ello, instale el paquete Bleak

python -m pip install bleak

Detectar dispositivos bluetooth

import asyncio
from bleak import BleakScanner

async def main():
	target_name = "ESP32BLE"
	target_address = None

	devices = await BleakScanner.discover()
	for d in devices:
		print(d)
		if target_name == d.name:
			target_address = d.address
			print("found target {} bluetooth device with address {} ".format(target_name,target_address))
			break
asyncio.run(main())

Salida

C1:2E:C6:8E:47:E8: None
3C:61:05:31:5F:12: ESP32BLE
found target ESP32BLE bluetooth device with address 3C:61:05:31:5F:12

Conexión y comunicación con el ESP32

Aquí tienes un script en Python para conectarte automáticamente al dispositivo ESP32 BLE desde un PC. Para comunicarse con el dispositivo BLE, es importante conocer el UUID de los diferentes servicios. Definimos los UUID como los definidos en el código del ESP32

import asyncio
from bleak import BleakScanner
from bleak import BleakClient

async def main():
	target_name = "ESP32BLE"
	target_address = None

	SERVICE_UUID=        "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
	CHARACTERISTIC_UUID= "beb5483e-36e1-4688-b7f5-ea07361b26a8"

	devices = await BleakScanner.discover()
	for d in devices:
		print(d)
		if target_name == d.name:
			target_address = d.address
			print("found target {} bluetooth device with address {} ".format(target_name,target_address))
			break

	if target_address is not None:		
		async with BleakClient(target_address) as client:
			print(f"Connected: {client.is_connected}")
				
			while 1:
				text = input()
				if text == "quit":
					break

				await client.write_gatt_char(CHARACTERISTIC_UUID, bytes(text, 'UTF-8'), response=True)
				
				try:
					data = await client.read_gatt_char(CHARACTERISTIC_UUID)
					data = data.decode('utf-8') #convert byte to str
					print("data: {}".format(data))
				except Exception:
					pass
				
			
	else:
		print("could not find target bluetooth device nearby")


asyncio.run(main())

Aplicación

Fuentes

Desarrollo de un monitor UDP con Python

Desarrollo de un monitor UDP con Python

En este proyecto, vamos a crear un monitor de comunicación de red UDP usando Python (PyQt). Cuando desarrolles un proyecto con Arduino, Raspberry Pi o cualquier otro microcontrolador, seguramente necesitarás crear una interfaz gráfica para gestionar el sistema (depurar, observar medidas, lanzar acciones, etc.). Hay muchas herramientas disponibles para crear interfaces gráficas. En este proyecto, vamos a crear un monitor de comunicación de red utilizando PyQt(PySide2).

Objetivo

Para este proyecto, queremos crear una interfaz gráfica en Python para Windows que se comporte como un monitor de comunicaciones UDP. Para ello, necesitaremos realizar las siguientes funciones

  • Casilla de introducción de la dirección IP
  • Zona portuaria de entrada
  • Botón de conexión
  • Zona de escritura por encargo
  • Botón Enviar
  • Consola que muestra los datos recibidos

Recuperar la dirección IP de la máquina

En la máquina receptora, recupere la dirección IPv4 (en nuestro caso 192.168.1.67)

ipconfig #sur la machine windows

o

ip addr #sur machine linux

Aplicación de monitorización de comunicaciones UDP

En primer lugar, vamos a crear la ventana de la aplicación, que llamaremos AcApp y que será la base de nuestra aplicación.

#!/usr/bin/python3
# -*-coding:Utf-8 -*

import sys,os
#from PyQt5.QtWidgets import *
#from PyQt5.QtCore import *
#from PyQt5.QtGui import *
from PySide2.QtWidgets import *
from PySide2.QtCore import *
from PySide2.QtGui import *
pyqtSignal=Signal #translate pyqt to Pyside

class AcApp(QMainWindow):

    def __init__(self,title='AcApp',mainFrame=QFrame):
        super().__init__()
        self.title=title
        self.mainFrame=mainFrame()
        self.initUI()

    def initUI(self):        
        self.setCentralWidget(self.mainFrame)
       
        #connect signals
        self.mainFrame.debugSignal.connect(self.debugMsg)
       
        #General configuration
        #self.resize(self.width, self.height)
        self.setWindowTitle(self.title)
        self.setGeometry(300, 300, 850, 450)
        #self.setWindowIcon(QIcon(__icon__))
       
        #Debug bar
        self.statusBar()
        self.statusBar().showMessage('Display debug messages')
       
        self.show()

    def debugMsg(self,val):
        self.statusBar().showMessage(val)

def main():
    app = QApplication(sys.argv)
    app.setQuitOnLastWindowClosed(True)
    ex = AcApp(__title__)
    #ex = AcApp(__title__,EthernetInterface)
    app.quit()
    sys.exit(app.exec_())

       
if __name__ == '__main__':
    main()

Creación de widgets de gestión de comunicaciones UDP

Crearemos entonces una clase que contenga todos los Widgets que necesitamos para crear y gestionar el monitor de comunicación UDP (QLineEdit,QTextEdit,QButton, etc.). Este Widget se inicializará con la clase que gestionará la comunicación

class EthernetInterface(QFrame):
    debugSignal=pyqtSignal(str) #define debug signal   

    def __init__(self,parent=None):
        super(EthernetInterface,self).__init__(parent)
        self.grid=QGridLayot()
        self.setLayot(self.grid)
        self.defineWidgets()
        self.model=None 
        if self.model is not None: self.model.debugSignal.connect(self.read)
   
    def defineWidgets(self):
        #self.setStyleSheet("""QGropBox{backgrond-color:white;border: 1px solid green;border-radius: 4px;}
        #QGropBox::title {padding:1 5px;}""")
               
        #groopbox widget container
        self.grp=QGropBox(self)
        self.grp.setTitle("Connection Configuration")
       
        self.fields=QGridLayot()
        self.grp.setLayot(self.fields)
        self.grid.addWidget(self.grp,0,0)
       
        #Define widget UI
        #validator
        ipRange = "(?:[0-1]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])"   # Part of the regular expression
        # Regulare expression
        ipRegex = QRegExp("^" + ipRange + "\\." + ipRange + "\\." + ipRange + "\\." + ipRange + "$")
        ipValidator = QRegExpValidator(ipRegex, self)  
       
        #label
        self.selectlbl = QLabel("IP Address:")
        self.typeBox = QLineEdit(HOST)
        #self.typeBox.setInputMask("0.0.0.0");
        self.typeBox.setValidator(ipValidator);
       
        self.baudlbl = QLabel("Port:")
        self.baudBox = QLineEdit("{}".format(PORT))
       
        #btn
        self.button = QPushButton("Connect")
        self.button.clicked.connect(self.clicked)
        self.button.clicked.connect(self.connec)
       
        sendBtn = QPushButton("send")
        sendBtn.clicked.connect(self.clicked)
        sendBtn.clicked.connect(self.send)
       
        titlelbl=  QLabel("Enter")
        self.edit = QLineEdit("")
        sentlbl=QLabel("Sent")
        self.sent = QTextEdit("")
        desclbl=QLabel("Console")
        self.desc = QTextEdit("")
           
        #row, column[, rowSpan=1[, columnSpan=1[
        self.fields.addWidget(self.selectlbl,0,0,1,1)
        self.fields.addWidget(self.typeBox,0,1,1,1)
        self.fields.addWidget(self.baudlbl,0,2,1,1)
        self.fields.addWidget(self.baudBox,0,3,1,1)
       
        self.fields.addWidget(self.button,0,4,1,1)
       
        self.fields.addWidget(titlelbl,1,0,1,1)
        self.fields.addWidget(self.edit,1,1,1,3)
        self.fields.addWidget(sendBtn,1,4,1,1)
       
        self.fields.addWidget(sentlbl,2,0,1,1,Qt.AlignTop)#Qt.AlignmentFlag.AlignTop)
        self.fields.addWidget(self.sent,2,1,1,3)  
        self.fields.addWidget(desclbl,3,0,1,1,Qt.AlignTop)#Qt.AlignmentFlag.AlignTop)
        self.fields.addWidget(self.desc,3,1,1,3)      

    def debug(self,msg):
        sender = self.sender()
        self.debugSignal.emit(sender.__class__.__name__+" : "+msg)
       
    def clicked(self):
        sender = self.sender()
        if sender.__class__.__name__=="QPushButton":
            self.debugSignal.emit(sender.text()+ " clicked")
        if sender.__class__.__name__=="QComboBox":
            self.debugSignal.emit(sender.currentText()+ " selected")

    def connec(self):
        #self.desc.setText("")
        self.desc.clear()
        if self.model is not None:
            if self.button.text() == "Connect":
                self.desc.setText("&gt;&gt; trying to connect to address {} on port {} ...".format(self.typeBox.text(),self.baudBox.text()))

                print("Started")
                self.button.setText("Stop")
                #self.model = EthModel()
                self.model.connec(self.typeBox.text(),int(self.baudBox.text()))
                self.model.start()
            else:
                self.model.quit_flag = True
                print("Stop sent")
                self.model.close()#self.model.wait()
                print("Stopped")
                self.button.setText("Connect")
                self.desc.setText("&gt;&gt; deconnect address {} on port {} ...".format(self.typeBox.text(),self.baudBox.text()))
                
   
    def read(self,msg):
        self.desc.setText(self.desc.toPlainText()+msg+"\n")
        self.desc.verticalScrollBar().setValue(self.desc.verticalScrollBar().maximum());
       
                   
    def send(self):
        if self.edit.text() != "":
            self.sent.setText(self.sent.toPlainText()+self.edit.text()+"\n")
           
            if self.model is not None:
                self.model.write(self.edit.text())

Sustituya ex = AcApp(__title__) por ex = AcApp(__title__,EthernetInterface) en la función main()

Una vez colocados nuestros widgets, vamos a empezar a utilizarlos.

Creación de un QThread para gestionar la comunicación UDP

Para no bloquear la interfaz gráfica cuando se reciben o envían paquetes, vamos a crear un QThread para gestionar la comunicación UDP.

#Ethernet connection
class EthModel(QThread):
    """Handle Ethernet connexion with remote device and connect to interface EthInterface"""
    debugSignal=pyqtSignal(str) #define debug signal   
    def __init__(self):
        super(EthModel, self).__init__()
        self.quit_flag = False
        self.msgToEmit=""

    def run(self):
        while True:
            if not self.quit_flag:
                self.read()
                #time.sleep(1)
            else:
                self.close()
                break

        self.quit()
        #self.wait()
                
    def connec(self,addr='192.168.1.10',port=7):
        # Create a UDP/IP socket
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) #worls with esp8266 udp client
        self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
        self.sock.bind((addr,port))
        # Listen for incoming connections
        #self.sock.listen(1) #stream
        
        print('starting up on {} port {}'.format(addr,port))
        self.debugSignal.emit('starting up on {} port {}'.format(addr,port))
        self.quit_flag = False
        self._isRunning=True
	
    def read(self):
        while self._isRunning:
            #data = self.sock.recv(1024)
            try:
                data, addr = self.sock.recvfrom(1024)
                print(data)
            except:
                print("socket closed")
                data=False

            if not data:
                print("no data &gt; break loop")
                break
            #self.sock.sendto("received OK".encode('utf-8'),addr)
            if self.msgToEmit!="":
                self.sock.sendto(self.msgToEmit.encode('utf-8'),addr)
                self.msgToEmit="" #clear message
            self.debugSignal.emit(str(data))


    def write(self,msg):
        self.msgToEmit=msg

    def close(self):
        self._isRunning=False
        self.sock.close()

Cliente UDP Código Python

Para probar su interfaz, puede ejecutar un código en el mismo ordenador utilizando la dirección 127.0.0.1 (dirección local). El siguiente código creará un socket y enviará el valor de un contador.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import socket
import time
HOST = "127.0.0.1"
PORT = 8888
bufferSize = 1024



conter=0
while True:
	# Create a UDP socket at client side
	with socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM) as client:
		# Send to server using created UDP socket
		client.sendto(str.encode(str(conter)), (HOST,PORT))

		data,addr= client.recvfrom(bufferSize)
		msg = "Message from Server {}".format(data)

		print(msg)
	conter+=1
	time.sleep(0.1)

Código ESP8266 Cliente UDP

El código del ESP8266 para la comunicación UDP es bastante sencillo. Definimos la comunicación de red utilizando el protocolo UDP y devolvemos el valor dado por la función millis().

#include <ESP8266WiFi.h>
#include <WiFiUdp.h>

WiFiUDP udp;
char packetBuffer[256];
unsigned int localPort = 9999;
const char *ssid = "******";
const char *password = "******";

// Set yor Static IP address
IPAddress local_IP(192, 168, 1, 80);
IPAddress gateway(192, 168, 1, 1);
IPAddress subnet(255, 255, 0, 0);

// Set destination IP address
const char *destaddr = "192.168.1.67";
unsigned int destPort = 8888;

void setup() {
  Serial.begin(115200);
  WiFi.config(local_IP, gateway, subnet);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  udp.begin(localPort);
  Serial.print(F("UDP Client : ")); Serial.println(WiFi.localIP());
}

void loop() {
  int packetSize = udp.parsePacket();
  Serial.print(" Received packet from : "); Serial.println(udp.remoteIP());
  Serial.print(" Size : "); Serial.println(packetSize);
  Serial.print(" Data : ");
  packetBuffer[0] = '0'; //reset buffer
  if (packetSize) {
    int len = udp.read(packetBuffer, 256);
    Serial.print(packetBuffer);
    
    udp.flush();
  }
  Serial.println("\n");
  delay(500);
  Serial.print("[Client Connected] ");
  Serial.println(WiFi.localIP());
  udp.beginPacket(destaddr, destPort);
  udp.write("Send millis: ");
  char buf[20];
  unsigned long testID = millis();
  sprintf(buf, "%lu", testID);
  Serial.print(" Sent : "); Serial.println(buf);
  udp.write(buf);
  udp.write("\r\n");
  udp.endPacket();
}

N.B.: Para este proyecto, estamos utilizando un esp8266, pero puedes adaptar el código a cualquier dispositivo que utilice el protocolo UDP.

Resultados

Para utilizar el código de cliente Python, introduzca la dirección IP 127.0.0.1

Para probar la comunicación con el ESP8266, utilice la dirección IP de su ordenador (aquí, 192.168.1.67).

Hemos creado un monitor de comunicación UDP usando Python que puede interactuar con dispositivos remotos como ESP8266, ESP32, Raspberry Pi u otros ordenadores. Ahora puedes mejorar la interfaz para adaptarla a tus necesidades.

Código completo

#!/usr/bin/python3
# -*-coding:Utf-8 -*
"""
Created on Thu Nov 17 16:59:13 2022

@author: X.Wiedmer

AC windows
Define the application window
"""
import sys,os
#from PyQt5.QtWidgets import *
#from PyQt5.QtCore import *
#from PyQt5.QtGui import *
from PySide2.QtWidgets import *
from PySide2.QtCore import *
from PySide2.QtGui import *
pyqtSignal=Signal #translate pyqt to Pyside

import socket
import time

"""
App configuration
"""
__title__="ACTerminal"
__version__="v0.1"

HOST = '192.168.1.67'
PORT = 8888

#Ethernet connection
class EthModel(QThread):
    """Handle Ethernet connexion with remote device and connect to interface EthInterface"""
    debugSignal=pyqtSignal(str) #define debug signal   
    def __init__(self):
        super(EthModel, self).__init__()
        self.quit_flag = False
        self.msgToEmit=""

    def run(self):
        while True:
            if not self.quit_flag:
                self.read()
                #time.sleep(1)
            else:
                self.close()
                break

        self.quit()
        self.exit()
        #self.wait()
                
    def connec(self,addr='192.168.1.10',port=7):
        # Create a UDP/IP socket
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) #worls with esp8266 udp client
        self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
        self.sock.bind((addr,port))
        # Listen for incoming connections
        #self.sock.listen(1) #stream
        
        print('starting up on {} port {}'.format(addr,port))
        self.debugSignal.emit('starting up on {} port {}'.format(addr,port))
        self.quit_flag = False
        self._isRunning=True
	
    def read(self):
        while self._isRunning:
            #data = self.sock.recv(1024)
            try:
                data, addr = self.sock.recvfrom(1024)
                print(data)
            except:
                print("socket closed")
                data=False

            if not data:
                print("no data &gt; break loop")
                break
            #self.sock.sendto("received OK".encode('utf-8'),addr)
            if self.msgToEmit!="":
                self.sock.sendto(self.msgToEmit.encode('utf-8'),addr)
                self.msgToEmit="" #clear message
            self.debugSignal.emit(str(data))


    def write(self,msg):
        self.msgToEmit=msg

    def close(self):
        self._isRunning=False
        self.sock.close()

#define GUI    
class EthernetInterface(QFrame):
    debugSignal=pyqtSignal(str) #define debug signal   

    def __init__(self,parent=None):
        super(EthernetInterface,self).__init__(parent)
        self.grid=QGridLayot()
        self.setLayot(self.grid)
        self.defineWidgets()
        self.model=EthModel()#self.model=None
        self.model.debugSignal.connect(self.read)
   
    def defineWidgets(self):
        #self.setStyleSheet("""QGropBox{backgrond-color:white;border: 1px solid green;border-radius: 4px;}
        #QGropBox::title {padding:1 5px;}""")
               
        #groopbox widget container
        self.grp=QGropBox(self)
        self.grp.setTitle("Connection Configuration")
       
        self.fields=QGridLayot()
        self.grp.setLayot(self.fields)
        self.grid.addWidget(self.grp,0,0)
       
        #Define widget UI
        #validator
        ipRange = "(?:[0-1]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])"   # Part of the regular expression
        # Regulare expression
        ipRegex = QRegExp("^" + ipRange + "\\." + ipRange + "\\." + ipRange + "\\." + ipRange + "$")
        ipValidator = QRegExpValidator(ipRegex, self)  
       
        #label
        self.selectlbl = QLabel("IP Address:")
        self.typeBox = QLineEdit(HOST)
        #self.typeBox.setInputMask("0.0.0.0");
        self.typeBox.setValidator(ipValidator);
       
        self.baudlbl = QLabel("Port:")
        self.baudBox = QLineEdit("{}".format(PORT))
       
        #btn
        self.button = QPushButton("Connect")
        self.button.clicked.connect(self.clicked)
        self.button.clicked.connect(self.connec)
       
        sendBtn = QPushButton("send")
        sendBtn.clicked.connect(self.clicked)
        sendBtn.clicked.connect(self.send)
       
        titlelbl=  QLabel("Enter")
        self.edit = QLineEdit("")
        sentlbl=QLabel("Sent")
        self.sent = QTextEdit("")
        desclbl=QLabel("Console")
        self.desc = QTextEdit("")
           
        #row, column[, rowSpan=1[, columnSpan=1[
        self.fields.addWidget(self.selectlbl,0,0,1,1)
        self.fields.addWidget(self.typeBox,0,1,1,1)
        self.fields.addWidget(self.baudlbl,0,2,1,1)
        self.fields.addWidget(self.baudBox,0,3,1,1)
       
        self.fields.addWidget(self.button,0,4,1,1)
       
        self.fields.addWidget(titlelbl,1,0,1,1)
        self.fields.addWidget(self.edit,1,1,1,3)
        self.fields.addWidget(sendBtn,1,4,1,1)
       
        self.fields.addWidget(sentlbl,2,0,1,1,Qt.AlignTop)#Qt.AlignmentFlag.AlignTop)
        self.fields.addWidget(self.sent,2,1,1,3)  
        self.fields.addWidget(desclbl,3,0,1,1,Qt.AlignTop)#Qt.AlignmentFlag.AlignTop)
        self.fields.addWidget(self.desc,3,1,1,3)      

    def debug(self,msg):
        sender = self.sender()
        self.debugSignal.emit(sender.__class__.__name__+" : "+msg)
       
    def clicked(self):
        sender = self.sender()
        if sender.__class__.__name__=="QPushButton":
            self.debugSignal.emit(sender.text()+ " clicked")
        if sender.__class__.__name__=="QComboBox":
            self.debugSignal.emit(sender.currentText()+ " selected")

    def connec(self):
        #self.desc.setText("")
        self.desc.clear()
        if self.model is not None:
            if self.button.text() == "Connect":
                self.desc.setText("&gt;&gt; trying to connect to address {} on port {} ...\n".format(self.typeBox.text(),self.baudBox.text()))

                print("Started")
                self.button.setText("Stop")
                self.model.connec(self.typeBox.text(),int(self.baudBox.text()))
                self.model.start()
            else:
                self.model.quit_flag = True
                print("Stop sent")
                self.model.close()#self.model.wait()
                print("Stopped")
                self.button.setText("Connect")
                self.desc.setText("&gt;&gt; deconnect address {} on port {} ...".format(self.typeBox.text(),self.baudBox.text()))
                
   
    def read(self,msg):
        self.desc.setText(self.desc.toPlainText()+msg+"\n")
        self.desc.verticalScrollBar().setValue(self.desc.verticalScrollBar().maximum());
       
                   
    def send(self):
        if self.edit.text() != "":
            self.sent.setText(self.sent.toPlainText()+self.edit.text()+"\n")
           
            if self.model is not None:
                self.model.write(self.edit.text())

# Generic app container     
class AcApp(QMainWindow):

    def __init__(self,title='AcApp',mainFrame=QFrame):
        super().__init__()
        self.title=title
        self.mainFrame=mainFrame()
        self.initUI()

    def initUI(self):        
        self.setCentralWidget(self.mainFrame)
       
        #connect signals
        self.mainFrame.debugSignal.connect(self.debugMsg)
       
        #General configuration
        #self.resize(self.width, self.height)
        self.setWindowTitle(self.title)
        self.setGeometry(300, 300, 850, 450)
        #self.setWindowIcon(QIcon(__icon__))
       
        #Debug bar
        self.statusBar()
        self.statusBar().showMessage('Display debug messages')
       
        self.show()

    def debugMsg(self,val):
        self.statusBar().showMessage(val)
   


       
def main():
    app = QApplication(sys.argv)
    app.setQuitOnLastWindowClosed(True)
    #app.setStyleSheet(open("style.txt").read()); #set style according to file 
    #ex = AcApp(__title__)
    ex = AcApp(__title__,EthernetInterface)
    app.quit()
    sys.exit(app.exec_())

       
if __name__ == '__main__':
    main()

Fuentes