Sep . 2025
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.
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); };
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); }
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 } }
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."); } } }
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
+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
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