LARSOBRE NóSINFORMAçõES DA INDúSTRIA UM GUIA PRáTICO PARA WALKIE-TALKIES DIGITAIS ESP32...

Um guia prático para walkie-talkies digitais ESP32 personalizados (parte 4): desenvolvimento de firmware e design de driver

11

Sep . 2025

Por SDGA:

Para controlar o DMR858M de forma eficiente e confiável, recomenda-se uma abordagem orientada a objetos, criando uma classe de driver para encapsular todas as interações com o módulo. Essa arquitetura é semelhante às bibliotecas projetadas para outros módulos de comando AT (como módulos GSM ou Wi-Fi) e oferece boa modularidade e reutilização.

Método de arquitetura: Classe DMR858M_Controller

Projetaremos uma classe C++ chamada DMR858M_Controller. Esta classe será responsável por gerenciar a comunicação UART, construir e analisar quadros de dados, manipular comandos e respostas e gerenciar o estado do módulo.

// DMR858M_Controller.h #include <Arduino.h> class DMR858M_Controller { public:     DMR858M_Controller(HardwareSerial& serial, int pttPin, int csPin);     void begin(long speed);     bool setFrequency(uint32_t txFreq, uint32_t rxFreq);     bool setPowerLevel(bool highPower);     bool getFirmwareVersion(String& version);     void setPTT(bool active);     // ... outros protótipos de função private:     HardwareSerial& _serial;     int _pttPin;     int _csPin;     void sendCommand(uint8_t cmd, uint8_t rw, const uint8_t* data, uint16_t len);     bool waitForResponse(uint8_t* buffer, uint16_t& len, uint32_t timeout = 1000);     uint16_t calculateChecksum(const uint8_t* data, size_t len); };






















Detalhes da implementação principal (exemplo de código)

Construção e transmissão de pacotes

sendCommand é o núcleo de todas as operações de gravação. Ele é responsável por montar o pacote binário completo, calcular a soma de verificação e enviá-lo via 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; // Cabeça     frame[1] = cmd; // CMD     frame[2] = rw; // R/W     frame[3] = 0x01; // S/R (Solicitação)     frame[4] = 0x00; // CKSUM_HI (temporário)     frame[5] = 0x00; // CKSUM_LO (temporário)     frame[6] = (len >> 8) & 0xFF; // LEN_HI     frame[7] = len & 0xFF; // LEN_LO     if (data && len > 0) {         memcpy(&frame[8], data, len);     }     frame[8 + len] = 0x10; // Cauda     // Calcula a soma de verificação do CMD até o final de DATA     uint16_t checksum = calculateChecksum(&frame[1], 7 + len);     frame[4] = (checksum >> 8) & 0xFF; // CKSUM_HI     frame[5] = checksum & 0xFF; // CKSUM_LO     _serial.write(frame, totalFrameLen); } uint16_t DMR858M_Controller::calculateChecksum(const uint8_t* buf, size_t len) {     uint32_t sum = 0;     const uint8_t* current_buf = buf;     tamanho_t comprimento_atual = comprimento;     enquanto (comprimento_atual > 1) {         soma += (uint16_t)((*buf_atual << 8) | *(buf_atual + 1));         comprimento_atual += 2;         comprimento_atual -= 2;     }     se (comprimento_atual > 0) {         soma += (uint16_t)(*buf_atual << 8);     }     enquanto (soma >> 16) {         soma = (soma & 0xFFFF) + (soma >> 16);     }     retornar (uint16_t)(soma ^ 0xFFFF); }








   


   


   



   

   


   


   






















Importância do tratamento de respostas e operações assíncronas

Em sistemas embarcados, esperas de bloqueio são um padrão de programação a ser evitado. Uma simples função waitForResponse usando um loop como while(!_serial.available()){} congelará todo o loop principal, impedindo que o MCU execute outras tarefas, como atualizar uma tela ou responder a pressionamentos de botões, resultando em um sistema sem resposta.

Um design mais robusto deve ser não bloqueante . No loop principal, o programa deve verificar continuamente a porta serial em busca de dados e usar uma máquina de estados para processar o quadro de dados de entrada. Essa abordagem garante que o sistema ainda possa lidar com outros eventos em tempo real enquanto aguarda uma resposta do módulo. Para uma plataforma como o ESP32, que suporta FreeRTOS, uma solução melhor é criar uma tarefa RTOS dedicada para lidar com a comunicação com o módulo DMR. Essa tarefa pode ser bloqueada quando não há dados sem afetar a execução de outras tarefas.

Aqui está um exemplo simplificado de lógica de leitura não bloqueante adequada para uma função loop() do Arduino:

// Lógica simplificada de tratamento de resposta não bloqueante void loop() {     // ... outras tarefas...     if (_serial.available()) {         // Leia o byte e coloque-o em um buffer         // Use uma máquina de estado para analisar o quadro de dados (procure o cabeçalho 0x68, leia o comprimento especificado, verifique a soma de verificação e a cauda 0x10)         // Após a análise bem-sucedida, processe os dados de resposta     } }










Exemplo abrangente: um programa de prova de conceito

A seguir está um exemplo completo do Arduino/PlatformIO que demonstra como inicializar o módulo, controlar o PTT com um botão e enviar um SMS via 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("Falha na comunicação com o módulo DMR858M.");     }     // Exemplo: Definir frequência para o canal 1 como 433,500 MHz     dmr.setFrequency(433500000, 433500000); } void loop() {     // Lógica de controle PTT     if (digitalRead(PTT_BUTTON_PIN) == LOW) {         dmr.setPTT(true);         digitalWrite(LED_PIN, HIGH); // Indicador de transmissão     } else {         dmr.setPTT(false);         digitalWrite(LED_PIN, LOW);     }     // ... lógica de tratamento de resposta serial sem bloqueio pode ser adicionada aqui...     // Exemplo: Enviar SMS via monitor serial     if (Serial.available()) {         String cmd = Serial.readStringUntil('\n');         if (cmd.startsWith("sms")) {             // Analisar o conteúdo do SMS e o ID do alvo             // Chamar dmr.sendSMS(...)             Serial.println("Comando SMS recebido.");         }     } }



















































Um guia prático para a série de walkie-talkies digitais ESP32 personalizados


Parte 1: Análise aprofundada do módulo DMR858M

Parte 2: Integração de Hardware e Design de Referência

Parte 3: Desconstruindo o Protocolo de Controle Serial

Parte 4: Desenvolvimento de firmware e design de driver

Parte 5: Explorando recursos avançados e conclusão



    Contate-nos

     +86-755-23080616

     vendas@nicerf.com

    Site: https://www.nicerf.com/

    Endereço: 309-314, 3/F, Bldg A, edifício comercial Hongdu, Zona 43, Baoan Dist, Shenzhen, China

    Contate-nos
    política de Privacidade

    política de Privacidade

    · Política de Privacidade

    No momento não há conteúdo disponível


               

    E-mail: sales@nicerf.com

    Tel:+86-755-23080616

    Endereço:309-314, 3/F, Bldg A, edifício comercial Hongdu, Zona 43, Baoan Dist, Shenzhen, China


    ×