HOGARSOBRE NOSOTROSINFORMACIóN DE LA INDUSTRIA GUíA PRáCTICA PARA WALKIE-TALKIES DIGITALES ESP32 ...

Guía práctica para walkie-talkies digitales ESP32 personalizados (Parte 4): Desarrollo de firmware y diseño de controladores

11

Sep . 2025

Por sdga:

Para controlar el DMR858M de forma eficiente y fiable, se recomienda un enfoque orientado a objetos, creando una clase de controlador que encapsule todas las interacciones con el módulo. Esta arquitectura es similar a las bibliotecas diseñadas para otros módulos de comando AT (como los módulos GSM o Wi-Fi) y ofrece buena modularidad y reutilización.

Método de arquitectura: Clase DMR858M_Controller

Diseñaremos una clase C++ llamada DMR858M_Controller. Esta clase se encargará de gestionar la comunicación UART, construir y analizar tramas de datos, gestionar comandos y respuestas, y gestionar el estado del módulo.

// DMR858M_Controller.h #include <Arduino.h> clase DMR858M_Controller { público:     DMR858M_Controller(HardwareSerial& serial, int pttPin, int csPin);     vacío begin(long speed);     bool setFrequency(uint32_t txFreq, uint32_t rxFreq);     bool setPowerLevel(bool highPower);     bool getFirmwareVersion(String& version);     vacío setPTT(bool active);     // ... otros prototipos de funciones privado:     HardwareSerial& _serial;     int _pttPin;     int _csPin;     vacío sendCommand(uint8_t cmd, uint8_t rw, const uint8_t* data, uint16_t len);     bool waitForResponse(uint8_t* búfer, uint16_t& len, uint32_t tiempo de espera = 1000);     uint16_t calculateChecksum(const uint8_t* datos, tamaño_t len); };






















Detalles de implementación del núcleo (ejemplo de código)

Construcción y transmisión de paquetes

sendCommand es el núcleo de todas las operaciones de escritura. Se encarga de ensamblar el paquete binario completo, calcular la suma de comprobación y enviarlo mediante UART.

// DMR858M_Controller.cpp void DMR858M_Controller::sendCommand(uint8_t cmd, uint8_t rw, const uint8_t* data, uint16_t len) {     const uint16_t totalFrameLen = 9 + len;     uint8_t frame[totalFrameLen];     frame[0] = 0x68; // Encabezado     frame[1] = cmd; // CMD     frame[2] = rw; // L/E     frame[3] = 0x01; // S/R (Solicitud)     frame[4] = 0x00; // CKSUM_HI (temporal)     frame[5] = 0x00; // CKSUM_LO (temporal)     frame[6] = (len >> 8) & 0xFF; // LEN_HI     frame[7] = len & 0xFF; // LEN_LO     si (datos y len > 0) {         memcpy(&frame[8], datos, len);     }     frame[8 + len] = 0x10; // Cola     // Calcular la suma de comprobación desde CMD hasta el final de DATOS     uint16_t checksum = calculateChecksum(&frame[1], 7 + len);     frame[4] = (suma de comprobación >> 8) y 0xFF; // CKSUM_HI     frame[5] = suma de comprobación y 0xFF; // CKSUM_LO     _serial.write(frame, totalFrameLen); } uint16_t DMR858M_Controller::calculateChecksum(const uint8_t* buf, size_t len) {     uint32_t suma = 0;     const uint8_t* current_buf = buf;     size_t current_len = len;     mientras (longitud_actual > 1) {         suma += (uint16_t)((*buf_actual << 8) | *(buf_actual + 1));         buf_actual += 2;         longitud_actual -= 2;     }     si (longitud_actual > 0) {         suma += (uint16_t)(*buf_actual << 8);     }     mientras (suma >> 16) {         suma = (suma & 0xFFFF) + (suma >> 16);     }     devolver (uint16_t)(suma ^ 0xFFFF); }








   


   


   



   

   


   


   






















Importancia del manejo de respuestas y operaciones asincrónicas

En sistemas embebidos, las esperas bloqueantes son un patrón de programación que debe evitarse. Una simple función waitForResponse que utilice un bucle como while(!_serial.available()){} congelará todo el bucle principal, impidiendo que el MCU realice otras tareas, como actualizar una pantalla o responder a las pulsaciones de botones, lo que provocará que el sistema no responda.

Un diseño más robusto debería ser no bloqueante . En el bucle principal, el programa debe verificar continuamente la presencia de datos en el puerto serie y usar una máquina de estados para procesar la trama de datos entrante. Este enfoque garantiza que el sistema pueda gestionar otros eventos en tiempo real mientras espera la respuesta del módulo. Para una plataforma como el ESP32 compatible con FreeRTOS, una mejor solución es crear una tarea RTOS dedicada a gestionar la comunicación con el módulo DMR. Esta tarea puede bloquearse cuando no hay datos sin afectar la ejecución de otras tareas.

A continuación se muestra un ejemplo simplificado de lógica de lectura sin bloqueo adecuada para una función loop() de Arduino:

// Lógica simplificada de manejo de respuesta sin bloqueo void loop() {     // ... otras tareas...     if (_serial.available()) {         // Leer el byte y colocarlo en un búfer         // Usar una máquina de estados para analizar el marco de datos (buscar el encabezado 0x68, leer la longitud especificada, verificar la suma de comprobación y la cola 0x10)         // Una vez analizado exitosamente, procesar los datos de respuesta     } }










Ejemplo completo: un programa de prueba de concepto

El siguiente es un ejemplo completo de Arduino/PlatformIO que demuestra cómo inicializar el módulo, controlar PTT con un botón y enviar un SMS a través del monitor serial.

#include <Arduino.h> #include "DMR858M_Controller.h" #define PTT_BUTTON_PIN 25 #define PTT_MODULE_PIN 26 #define LED_PIN 2 HardwareSerial SerialTwo(2); DMR858M_Controller dmr(SerialTwo, PTT_MODULE_PIN, -1); void setup() {     Serial.begin(115200);     pinMode(PTT_BUTTON_PIN, INPUT_PULLUP);     pinMode(LED_PIN, OUTPUT);     dmr.begin(57600);     delay(500);     String fwVersion;     if (dmr.getFirmwareVersion(fwVersion)) {         Serial.println("Firmware DMR858M: " + fwVersion);     } else {         Serial.println("Error al comunicarse con el módulo DMR858M.");     }     // Ejemplo: Establecer la frecuencia para el canal 1 a 433,500 MHz     dmr.setFrequency(433500000, 433500000); } void loop() {     // Lógica de control PTT     if (digitalRead(PTT_BUTTON_PIN) == LOW) {         dmr.setPTT(true);         digitalWrite(LED_PIN, HIGH); // Indicador de transmisión     } else {         dmr.setPTT(false);         digitalWrite(LED_PIN, LOW);     }     // ... se puede agregar aquí una lógica de manejo de respuesta serial no bloqueante...     // Ejemplo: Enviar SMS a través del monitor serial     if (Serial.available()) {         String cmd = Serial.readStringUntil('\n');         if (cmd.startsWith("sms")) {             // Analizar el contenido del SMS y el ID del destino             // Llamar a dmr.sendSMS(...)             Serial.println("Comando SMS recibido.");         }     } }



















































Guía práctica para la serie de walkie-talkies digitales ESP32 personalizados


Parte 1: Análisis en profundidad del módulo DMR858M

Parte 2: Integración de hardware y diseño de referencia

Parte 3: Desconstrucción del protocolo de control en serie

Parte 4: Desarrollo de firmware y diseño de controladores

Parte 5: Exploración de funciones avanzadas y conclusión



    Contáctenos

     +86-755-23080616

     ventas@nicerf.com

    Sitio web: https://www.nicerf.com/

    Dirección: 309-314, 3/F, Bldg A, edificio comercial Hongdu, Zona 43, Baoan Dist, Shenzhen, China

    Contáctenos
    política de privacidad

    política de privacidad

    · Política de privacidad

    Actualmente no hay contenido disponible


               

    Correo electrónico:sales@nicerf.com

    Teléfono:+86-755-23080616

    Dirección: 309-314, 3/F, Bldg A, edificio comercial Hongdu, Zona 43, Baoan Dist, Shenzhen, China


    ×