fbpixel
Os diferentes protocolos de comunicação

Os diferentes protocolos de comunicação

Qualquer que seja o seu projeto de eletrónica, programação ou domótica, irá certamente utilizar um protocolo de comunicação. Seja para programar o microcontrolador ou para comunicar com um sensor. Este artigo apresenta vários protocolos de comunicação normalmente utilizados no Arduino, Raspberry Pi e ESP8266/ESP32.

Barramento de comunicação série

Porta série

USB

USB (Universal Serial Bus) é uma norma de barramento de comunicação utilizada para trocar dados entre periféricos de computador. A caraterística especial desta norma é o facto de permitir a ligação de dispositivos enquanto estes estão em funcionamento e de permitir o reconhecimento automático do periférico).

UART

UART (Universal Asynchronous Receiver Transmitter) é a norma que especifica a forma como os dados são enviados através da porta série.

RS-232

O protocolo RS-232 é um protocolo de comunicação que define a conetividade e permite a comunicação assíncrona e duplex entre dois equipamentos. A caraterística especial deste protocolo é o facto de utilizar tensões de 3 a 25 V para transmitir dados, tornando-o um barramento menos sensível a interferências e ruídos.

(RS232/RS422/RS485)

I2C

O barramento de comunicação I2C é um protocolo que permite a ligação de vários dispositivos “Master” a vários dispositivos “Slave”, permitindo a comunicação de até 128 dispositivos. Permite ligações assíncronas entre vários componentes para partilhar informações através de um “barramento comum”. Este protocolo é geralmente utilizado para trocas placa a placa, mas pode ser utilizado em distâncias mais longas.

SPI

SPI (Serial Peripheral Interface) é um bus de dados em série que funciona em modo full-duplex, o que significa que pode transmitir e receber dados ao mesmo tempo. Utiliza uma arquitetura mestre-escravo e o escravo é selecionado através de uma linha dedicada.

CAN

O barramento CAN (Controller Area Network) é um barramento de comunicação em série amplamente utilizado na indústria automóvel. Permite a multiplexagem de diferentes dispositivos, permitindo-lhes comunicar utilizando o mesmo bus. Isto reduz a quantidade e a complexidade da cablagem.

Ethernet

A Ethernet é um protocolo de comunicações com fios que troca dados em pacotes a alta velocidade.

(I2S)

MIDI

MIDI (Musical Instrument Digital Interface) é um protocolo de comunicação entre instrumentos electrónicos, controladores e software de música. Envolve o envio de uma série de bytes para especificar o tipo de mensagem e as informações associadas (nota, duração da nota, instrumento, etc.).

Protocolos de comunicação sem fios

Bluetooth

BLE

A rede BLE (Bluetooth Low Energy) é uma rede Bluetooth com baixo consumo de energia.

Wifi

ESP-NOW

Um protocolo de comunicação de baixo consumo desenvolvido pela Espressif que utiliza ondas de 2,4 GHz.

RF 433MHz

RF 2.4GHz

(Zigbee)

LoRaWAN

A rede Lora (Long Range Wide-area network) é uma rede de rádio que permite que dispositivos de baixo consumo comuniquem a baixa velocidade. Isto torna possível ter objectos ligados com uma autonomia significativa.

Tem agora uma visão completa dos protocolos de comunicação com e sem fios mais utilizados em eletrónica e informática.

Comunicação Bluetooth com o ESP32

Comunicação Bluetooth com o ESP32

Neste tutorial, vamos aprender a ativar, gerir e testar o Bluetooth num ESP32 utilizando a linguagem de programação Arduino. O Bluetooth é uma tecnologia sem fios muito utilizada para a comunicação entre dispositivos electrónicos. Pode transformar rapidamente o seu sistema num objeto ligado.

Equipamento

  • Um módulo ESP32 (Bluetooth+Wifi integrados)
  • Um computador com Python instalado ou um smartphone
  • Cabo USB para ligação ao computador ESP32

Configuração do ambiente e do IDE

Para programar o seu ESP32 com o IDE Arduino, pode seguir este tutorial anterior.

Recuperar o endereço MAC

Esta informação não é necessariamente necessária, mas é sempre uma boa ideia saber como obter o endereço MAC do 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() {}

Saída

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

Comunicação em série via Bluetooth

A comunicação Bluetooth é activada da mesma forma que a comunicação em série. O método é semelhante para o 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 haver uma forma de adicionar um código PIN, mas não consigo pô-lo a funcionar.

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

Emparelhamento

Uma vez configurado o módulo, pode emparelhar o ESP32 com o sistema da sua escolha, como qualquer outro dispositivo Bluetooth. Seleccione o nome na lista de dispositivos detectados (nome ESP32BT)

Teste da comunicação Bluetooth utilizando o terminal Bluetooth de série

Vamos testar a comunicação Bluetooth utilizando a aplicação Serial Terminal Bluetooth.

A mensagem é trocada entre o telemóvel e o ESP32 através de Bluetooth

Código para obter a encomenda completa

No código anterior, copiámos a mensagem byte a byte para a enviar de volta para o monitor. Aqui vamos registar o comando completo numa String msg. Isto permitir-lhe-á analisar o comando e definir a ação correspondente (por exemplo, ligar

#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();
}

Comunicação entre o ESP32 e o Python via Bluetooth

Pode gerir a comunicação Bluetooth a partir do seu PC.

Para fazer isso, instale o pacote PyBluez

python -m pip install pybluez

Detetar 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")

Saída

[('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

Ligação e comunicação com o ESP32

Aqui está um script Python para se ligar automaticamente ao dispositivo Bluetooth ESP32 a partir de um 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.: Apenas a biblioteca socket funciona para a comunicação Bluetooth. Parece haver um problema de manutenção com a biblioteca PyBluez.

Aplicações

Fontes

Comunicação Bluetooth com o ESP32

Comunicação BLE com o ESP32

Neste tutorial, vamos aprender a ativar e gerir o Bluetooth Low Energy (BLE) num ESP32 utilizando a linguagem de programação Arduino.

O Bluetooth Low Energy é uma versão de baixo consumo de energia do Bluetooth que permite o envio de pequenos pacotes de dados a intervalos regulares.

Equipamento

  • Um módulo ESP32 (Bluetooth+Wifi integrados)
  • Um computador com Python instalado ou um smartphone
  • Cabo USB para ligação ao computador ESP32

Configuração do ambiente e do IDE

Para programar o seu ESP32 com o IDE Arduino, pode seguir este tutorial anterior.

Communication Série via BLE

A comunicação BLE deve ser configurada com um determinado número de endereços (UIID), que são como registos de memória nos quais podemos ler e escrever.

//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);
}

Nota: é possível obter o endereço MAC do ESP32 utilizando a função BLEDevice::getAddress().toString().c_str().

Emparelhamento

Uma vez configurado o módulo, pode emparelhar o ESP32 com o sistema da sua escolha, como qualquer outro dispositivo Bluetooth. Seleccione o nome na lista dos dispositivos detectados (nome ESP32BLE)

Teste da comunicação BLE utilizando o terminal BLE

Vamos testar a comunicação BLE utilizando a aplicação Terminal BLE.

A mensagem é trocada entre o telemóvel e o ESP32 através de Bluetooth LE

Comunicação bidirecional entre o dispositivo e o ESP32BLE

Este é o código utilizado para modificar o valor da caraterística através do monitor serial. O dispositivo conectado e o monitor serial modificam o valor da caraterí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
}

Na prática, será certamente preferível utilizar um serviço para a escrita e outro para a leitura.

Comunicação entre o ESP32 e o Python via BLE

Pode gerir a comunicação Bluetooth Low Energy a partir do seu PC.

Para isso, instale o pacote Bleak

python -m pip install bleak

Detetar 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())

Saída

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

Ligação e comunicação com o ESP32

Eis um script Python para ligar automaticamente o dispositivo ESP32 BLE a partir de um PC. Para comunicar com o dispositivo BLE, é importante conhecer o uiid dos diferentes serviços. Definimos os UUIDs como os definidos no código do 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())

Aplicação

Fontes

Visão geral do microcontrolador NodeMCU ESP32

Visão geral do microcontrolador NodeMCU ESP32

O NodeMCU ESP32 é um microcontrolador com módulos WiFi e Bluetooth integrados. É muito fácil de utilizar, leve e tem mais memória e capacidade de cálculo do que um Arduino. É, por isso, uma placa ideal para aprender a programar e desenvolver objectos ou servidores conectados.

Características do microcontrolador

O microcontrolador NodeMCU ESP32 utiliza o microprocessador ESP-WROOM-32 (Tensilica Xtensa LX6). Este processador funciona a uma frequência de relógio de 240 MHz e dispõe de 520 kB de RAM, 448 kB de EEPROM e 4000 kB de memória Flash (para programação e registo de dados).

  • CPU ESP-WROOM-32 (Tensilica Xtensa LX6)
  • Voltage : 3.3V
  • Flash : 4000 kB
  • RAM : 520 kB
  • EEPROM : 448 kB
  • Clock speed : 240MHz
  • WiFi : Yes
  • Bluetooth : Yes
  • SD : No

O microcontrolador tem um chip WiFi que lhe permite ligar-se à rede local, criar um servidor ou criar a sua própria rede para que outros dispositivos possam ligar-se a ela. O microcontrolador tem um chip Bluetooth que lhe permite interagir com outros dispositivos.

Alimentação eléctrica

O microcontrolador NodeMCU ESP32 funciona numa gama de tensões de 7-12V graças ao seu regulador de tensão integrado, enquanto o microprocessador funciona a uma tensão de 3,3V. Em funcionamento normal, o microcontrolador consome até 45mA (se não houver alimentação) e pode aceitar uma corrente máxima de 40mA em cada um dos seus pinos IO.

Pinagem

  • Analog I/O : 15 (2, 4, 12, 13, 14, 15, 25, 26, 27, 32, 33, 34, 35, 36, 39)
  • Digital I/O : 4 (all except 6 to 11)
  • Pinos PWM: 16 (2, 4, 5, 6, 12, 13, 14, 15, 18, 19, 21, 25, 26, 27, 32, 33)
  • Comunicação Série: 9 (1, 2, 3, 4, 5, 12, 13, 14, 15)
  • Comunicação I2C: 1 ((’21’, ’22’))
  • Comunicação SPI: 1 ((‘5′, ’18’, ’19’, ’23’))
  • Comunicação I2S: 1 ((’26’, ’25’, ’33’))
  • Toque: 9 (2, 4, 12, 13, 14, 15, 27, 32, 33)
  • Interrupção: 26 (0, 1, 2, 4, 5, 7, 12, 13, 14, 15, 16, 17, 18, 19, 21, 22, 23, 25, 26, 27, 32, 33, 34, 35, 36, 39)

Código básico e identificação de pinos

const int analogPin=34; // 2, 4, 12, 13, 14, 15, 25, 26, 27, 32, 33, 34, 35, 36, 39
const int digitalInPin=2; // all except 6 to 11
const int digitalOutPin=4; // all except 6 to 11 and 34, 35, 36 and 39 inputs only
const int pwmPin=3; // broches 2, 4, 5, 6, 12, 13, 14, 15, 18, 19, 21, 25, 26, 27, 32, 33

int analogVal=0;
int digitalState=LOW;
int pwmVal=250;

// setting PWM properties
const int freq = 5000;
const int ledChannel = 0;
const int resolution = 8;

void setup() {
  Serial.begin(115200); 
  
  pinMode(analogPin,INPUT); // broches 0-13 et 14-19, Argument OUTPUT, INPUT
  pinMode(digitalInPin,INPUT);
  pinMode(digitalOutPin,OUTPUT);

  ledcSetup(ledChannel, freq, resolution);
  ledcAttachPin(pwmPin, ledChannel);
}

void loop() {
 analogVal=analogRead(analogPin); //  return int
 digitalState=digitalRead(digitalInPin); //  return boolean
 digitalWrite(digitalOutPin,HIGH); // valeur LOW(0) ou HIGH(1)
 ledcWrite(ledChannel, pwmVal);// valeur 0-255
}

Pour plus d’information sur l’usage des broches, vous pouvez visiter le site ESP32 Pinagem Reference

Resumo das características

Microcontrôleur
Nom: ESP32
Marque: Espressif
Caractéristiques
CPU: ESP-WROOM-32 (Tensilica Xtensa LX6)
Tension d’alimentation : 7-12V
Tension logic: 3.3V
E/S digitales: 14
Entrées analogiques: 6
Flash: 4000kB
SRAM: 520kB
EEPROM: 448kB
Fréquence d’horloge: 240 MHz
Wifi: Yes
Bluetooth: Yes
SD card: No
Touch: Yes
UART/SPI/I2C/I2S: Yes/Yes/Yes/Yes

Como começar

Desenvolvendo um monitor UDP com Python

Desenvolvendo um monitor UDP com Python

Neste projeto, vamos criar um monitor de comunicação de rede UDP usando Python (PyQt). Quando se desenvolve um projeto com Arduino, Raspberry Pi ou qualquer outro microcontrolador, é certamente necessário criar uma interface gráfica para gerir o sistema (depurar, observar medições, lançar acções, etc.). Existem muitas ferramentas disponíveis para criar interfaces gráficas. Neste projeto, vamos criar um monitor de comunicação de rede usando PyQt (PySide2).

Objetivo

Para este projeto, queremos criar uma interface gráfica em Python para Windows que se comporte como um monitor de comunicação UDP. Para fazer isso, precisaremos executar as seguintes funções

  • Caixa de introdução do endereço IP
  • Zona de entrada no porto
  • Botão de ligação
  • Área de escrita para encomenda
  • Botão Enviar
  • Consola que apresenta os dados recebidos

Recuperar o endereço IP da máquina

Na máquina recetora, obtenha o endereço IPv4 (no nosso caso, 192.168.1.67)

ipconfig #sur la machine windows

ou

ip addr #sur machine linux

Aplicação de monitorização da comunicação UDP

Primeiro, vamos criar a janela da aplicação, a que chamaremos AcApp e que será a base da nossa aplicação.

#!/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()

Criação de widgets de gestão de comunicações UDP

Em seguida, criamos uma classe que contém todos os Widgets necessários para criar e gerir o monitor de comunicação UDP (QLineEdit, QTextEdit, QButton, etc.). Este Widget será inicializado com a classe que vai gerir a comunicação

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

    def __init__(self,parent=None):
        super(EthernetInterface,self).__init__(parent)
        self.grid=QGridLayout()
        self.setLayout(self.grid)
        self.defineWidgets()
        self.model=None 
        if self.model is not None: self.model.debugSignal.connect(self.read)
   
    def defineWidgets(self):
        #self.setStyleSheet("""QGroupBox{background-color:white;border: 1px solid green;border-radius: 4px;}
        #QGroupBox::title {padding:1 5px;}""")
               
        #grooupbox widget container
        self.grp=QGroupBox(self)
        self.grp.setTitle("Connection Configuration")
       
        self.fields=QGridLayout()
        self.grp.setLayout(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())

Substituir ex = AcApp(__title__) por ex = AcApp(__title__,EthernetInterface) na função main()

Assim que os nossos widgets estiverem no sítio, podemos começar a utilizá-los.

Criar uma QThread para gerir a comunicação UDP

Para não bloquear a GUI quando os pacotes são recebidos ou enviados, vamos criar uma QThread que irá gerir a comunicação 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()

Código Python do cliente UDP

Para testar a sua interface, pode executar um código no mesmo computador utilizando o endereço 127.0.0.1 (endereço local). O código a seguir criará um socket e enviará o valor de um contador.

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



counter=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(counter)), (HOST,PORT))

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

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

Código Cliente UDP ESP8266

O código do ESP8266 para a comunicação UDP é bastante simples. Definimos a comunicação em rede utilizando o protocolo UDP e devolvemos o valor dado pela função millis().

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

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

// Set your 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 projeto, utilizamos um esp8266, mas pode adaptar o código a qualquer dispositivo que utilize o protocolo UDP.

Resultados

Para utilizar o código de cliente Python, introduza o endereço IP 127.0.0.1

Para testar a comunicação com o ESP8266, utilize o endereço IP do seu computador (aqui, 192.168.1.67)

Criámos um monitor de comunicação UDP utilizando Python que pode interagir com dispositivos remotos como o ESP8266, ESP32, Raspberry Pi ou outros computadores. Pode agora melhorar a interface de acordo com as suas necessidades

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=QGridLayout()
        self.setLayout(self.grid)
        self.defineWidgets()
        self.model=EthModel()#self.model=None
        self.model.debugSignal.connect(self.read)
   
    def defineWidgets(self):
        #self.setStyleSheet("""QGroupBox{background-color:white;border: 1px solid green;border-radius: 4px;}
        #QGroupBox::title {padding:1 5px;}""")
               
        #grooupbox widget container
        self.grp=QGroupBox(self)
        self.grp.setTitle("Connection Configuration")
       
        self.fields=QGridLayout()
        self.grp.setLayout(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()

Fontes