Sep . 2025
To control the DMR858M efficiently and reliably, an object-oriented approach is recommended, creating a driver class to encapsulate all interactions with the module. This architecture is similar to libraries designed for other AT command modules (such as GSM or Wi-Fi modules) and offers good modularity and reusability.
We will design a C++ class named DMR858M_Controller. This class will be responsible for managing UART communication, building and parsing data frames, handling commands and responses, and managing the module's state.
// 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);
// ... other function prototypes
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 is the core of all write operations. It is responsible for assembling the complete binary packet, calculating the checksum, and sending it 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; // Head
frame[1] = cmd; // CMD
frame[2] = rw; // R/W
frame[3] = 0x01; // S/R (Request)
frame[4] = 0x00; // CKSUM_HI (temporary)
frame[5] = 0x00; // CKSUM_LO (temporary)
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; // Tail
// Calculate checksum from CMD to the end of 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;
size_t current_len = len;
while (current_len > 1) {
sum += (uint16_t)((*current_buf << 8) | *(current_buf + 1));
current_buf += 2;
current_len -= 2;
}
if (current_len > 0) {
sum += (uint16_t)(*current_buf << 8);
}
while (sum >> 16) {
sum = (sum & 0xFFFF) + (sum >> 16);
}
return (uint16_t)(sum ^ 0xFFFF);
}
In embedded systems, blocking waits are a programming pattern to be avoided. A simple waitForResponse function using a loop like while(!_serial.available()){} will freeze the entire main loop, preventing the MCU from performing other tasks such as updating a display or responding to button presses, leading to an unresponsive system.
A more robust design should be non-blocking. In the main loop, the program should continuously check the serial port for data and use a state machine to process the incoming data frame. This approach ensures that the system can still handle other real-time events while waiting for a response from the module. For a platform like the ESP32 that supports FreeRTOS, a better solution is to create a dedicated RTOS task to handle communication with the DMR module. This task can block when there is no data without affecting the execution of other tasks.
Here is a simplified example of non-blocking read logic suitable for an Arduino loop() function:
// Simplified non-blocking response handling logic
void loop() {
// ... other tasks...
if (_serial.available()) {
// Read byte and place it into a buffer
// Use a state machine to parse the data frame (look for header 0x68, read specified length, verify checksum and tail 0x10)
// Once successfully parsed, process the response data
}
}
The following is a complete Arduino/PlatformIO example that demonstrates how to initialize the module, control PTT with a button, and send an SMS via the serial monitor.
#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("DMR858M Firmware: " + fwVersion);
} else {
Serial.println("Failed to communicate with DMR858M module.");
}
// Example: Set frequency for channel 1 to 433.500 MHz
dmr.setFrequency(433500000, 433500000);
}
void loop() {
// PTT control logic
if (digitalRead(PTT_BUTTON_PIN) == LOW) {
dmr.setPTT(true);
digitalWrite(LED_PIN, HIGH); // Transmit indicator
} else {
dmr.setPTT(false);
digitalWrite(LED_PIN, LOW);
}
// ... non-blocking serial response handling logic can be added here...
// Example: Send SMS via serial monitor
if (Serial.available()) {
String cmd = Serial.readStringUntil('\n');
if (cmd.startsWith("sms")) {
// Parse SMS content and target ID
// Call dmr.sendSMS(...)
Serial.println("SMS command received.");
}
}
}
Part 1: In-depth Analysis of the DMR858M Module
Part 2: Hardware Integration and Reference Design
Part 3: Deconstructing the Serial Control Protocol
Part 4: Firmware Development and Driver Design
Part 5: Exploring Advanced Features and Conclusion
+86-755-23080616
sales@nicerf.com
Website: https://www.nicerf.com/
Address: 309-315, 3/F, Bldg A, Hongdu business building, Zone 43, Baoan Dist, Shenzhen, China
Privacy Policy
· Privacy Policy
There is currently no content available
Email:sales@nicerf.com
Tel:+86-755-23080616