环境配置: 参考Arduino IDE上手教程完成 IDE 安装,并根据实际使用的开发板安装对应的板管理,与需要的驱动库。
使用到的驱动库:


G5 (TX)、G6 (RX),RS485 IO 为 G7 (TX),G8 (RX)(下方例程中定义后未实际使用)。TinyGSM 库中 TinyGsmClientSIM7028.h 文件 line 32 的 MODE_NB_IOT 的宏定义注释去掉;如果需要 debug ,同理可将 TINY_GSM_DEBUG 及下方代码中的 DUMP_AT_COMMANDS 宏定义取消注释。#include "M5Unified.h"
#define TINY_GSM_MODEM_SIM7028 // Specify to use SIM7028 module; TinyGsm library adapts instruction set based on this macro, must be placed here
#include "PubSubClient.h"
#include "TinyGsmClient.h"
#include "StreamDebugger.h"
#include "M5IOE1.h"
#define SerialMon Serial // MCU -> PC for log output
#define MONITOR_BAUDRATE 115200
#define SerialAT Serial1 // MCU <-> SIM7028 module communication
#define SIM7028_BAUDRATE 115200
#define SIM7028_RESET -1
#define MCU_TX 5
#define MCU_RX 6
#define RS485_TX 7
#define RS485_RX 8
#define IO_SCL 39
#define IO_SDA 38
#define MQTT_BROKER "mqtt.m5stack.com" // MQTT broker address
#define MQTT_PORT 1883 // MQTT service port
#define mqtt_devid "XXXXXXXX" // MQTT client ID
#define mqtt_pubid "XXXXXXXX" // MQTT username
#define mqtt_password "XXXXXXXX" // MQTT authentication key
#define UPLOAD_INTERVAL 10000 // Data upload interval, 10s
// M5IOE1 device instance
M5IOE1 ioe1;
// M5IOE1 I2C address (default 0x6F)
#define I2C_ADDR M5IOE1_DEFAULT_ADDR
// Pin definitions for M5IOE1 GPIOs
#define SIM_PWR_EN M5IOE1_PIN_9
#define SIM_RST M5IOE1_PIN_11
#define VIN_ADC M5IOE1_PIN_2
// If you need to see log detail, you can open the following macro definition
// #define DUMP_AT_COMMANDS
// If you need to debug, you can open the following macro definition in TinyGsmClientSIM7028.h line 13
// #define TINY_GSM_DEBUG Serial
#ifdef DUMP_AT_COMMANDS
StreamDebugger debugger(SerialAT, SerialMon);
TinyGsm modem(debugger);
#else
TinyGsm modem(SerialAT);
#endif
TinyGsmClient tcpClient(modem); // Create TCP client based on TinyGsm (MQTT relies on TCP connection)
PubSubClient mqttClient(MQTT_BROKER, MQTT_PORT, tcpClient); // Create MQTT client instance (specify broker, port and TCP client)
void mqttCallback(char *topic, byte *payload, unsigned int len); // Callback function for receiving MQTT messages
bool mqttConnect(void); // Function to establish MQTT connection
void nbConnect(void); // Function to initialize SIM7028 and connect to NB-IoT network
const char* topicSub = "Atom DTU NBIoT v1.1 Receive"; // MQTT topic for receiving messages (replace with your actual subscribe topic)
const char* topicPub = "Atom DTU NBIoT v1.1 Send"; // MQTT topic for sending messages (replace with your actual publish topic)
int num = 0; // Counter for published messages
uint32_t lastReconnectAttempt = 0; // Timestamp for last MQTT reconnection attempt (avoid frequent retries)
bool lastMqttState = false; // Track MQTT connection status
const char apn[] = "cmnbiot";
void log(String info)
{
SerialMon.println(info);
}
void SIM7028_EN()
{
if (ioe1.begin(&Wire, I2C_ADDR, IO_SDA, IO_SCL, M5IOE1_I2C_FREQ_100K, M5IOE1_INT_MODE_POLLING) == M5IOE1_OK){
ioe1.pinMode(SIM_PWR_EN, OUTPUT);
ioe1.digitalWrite(SIM_PWR_EN, HIGH); // Enable SIM power
delay(100);
ioe1.pinMode(SIM_RST, OUTPUT);
ioe1.digitalWrite(SIM_RST, LOW); // SIM reset
delay(100);
ioe1.digitalWrite(SIM_RST, HIGH);
log("SIM power enabled");
}
else{
log("Failed to enable SIM power");
}
}
void setup()
{
M5.begin();
SerialMon.begin(115200);
SerialMon.println(">>SIM7028 MQTT TEST");
SerialAT.begin(SIM7028_BAUDRATE, SERIAL_8N1, MCU_RX, MCU_TX);
M5.Display.setFont(&fonts::FreeMonoBold9pt7b);
M5.Display.setTextDatum(middle_center);
M5.Display.drawString("NBIoT MQTT", M5.Display.width() / 2, 10);
M5.Display.drawString("Init...", M5.Display.width() / 2, 60);
delay(500);
SIM7028_EN();
nbConnect(); // Initialize SIM7028 module and connect to network
mqttClient.setServer(MQTT_BROKER, MQTT_PORT); // Set MQTT broker address and port
mqttClient.setKeepAlive(120); // MQTT keep-alive interval (120 seconds)
mqttClient.setSocketTimeout(15000); // TCP connection timeout (15 seconds)
mqttClient.setCallback(mqttCallback); // Register MQTT message receive callback
}
void loop()
{
static unsigned long timer = 0;
M5.update();
// Check for MQTT connection status change (connected <-> disconnected)
if (mqttClient.connected() != lastMqttState) {
if (mqttClient.connected()) {
log("=== MQTT CONNECTED ===");
M5.Display.fillRect(0, 20, 128, 20, TFT_BLACK);
M5.Display.setTextColor(TFT_GREEN);
M5.Display.drawString("CONNECTED", M5.Display.width() / 2, 30);
} else {
log("=== MQTT DISCONNECTED ===");
M5.Display.fillRect(0, 20, 128, 20, TFT_BLACK);
M5.Display.setTextColor(TFT_RED);
M5.Display.drawString("DISCONNECTED", M5.Display.width() / 2, 30);
}
lastMqttState = mqttClient.connected();
}
// MQTT reconnection logic (retry every 3 seconds if disconnected)
if (!mqttClient.connected()) {
log(">> MQTT NOT CONNECTED");
log("MQTT state code: " + String(mqttClient.state()));
M5.Display.fillRect(0, 20, 128, 20, TFT_BLACK);
M5.Display.setTextColor(TFT_RED);
M5.Display.drawString("DISCONNECTED", M5.Display.width() / 2, 30);
uint32_t t = millis();
if (t - lastReconnectAttempt > 3000L) {
lastReconnectAttempt = t;
if (mqttConnect()) {
lastReconnectAttempt = 0;
}
}
delay(100);
}
// Periodic data publishing (triggered when timer expires)
if (millis() >= timer) {
timer = millis() + UPLOAD_INTERVAL;
if (mqttClient.connected()) {
char jsonBuf[256];
snprintf(jsonBuf, sizeof(jsonBuf), "SIM7028 MQTT #%d", num);
log(">> [MQTT SEND] Topic: " + String(topicPub));
log("Info: " + String(jsonBuf));
mqttClient.publish(topicPub, jsonBuf);
M5.Display.fillRect(0, 40, 128, 40, TFT_BLACK);
M5.Display.setTextColor(TFT_WHITE);
M5.Display.drawString("Send:", M5.Display.width() / 2, 50);
M5.Display.drawString("#" + String(num++), M5.Display.width() / 2, 70);
}
}
mqttClient.loop();// Process MQTT incoming messages, maintain connection and handle keep-alive
delay(10);
}
void mqttCallback(char *topic, byte *payload, unsigned int len)
{
String payloadStr;
for (unsigned int i = 0; i < len; i++) {
payloadStr += (char)payload[i];
}
log(">> [MQTT RECEIVE] Topic: " + String(topic) + "\nInfo: " + payloadStr);
M5.Display.fillRect(0, 90, 128, 38, TFT_BLACK);
M5.Display.setTextColor(TFT_WHITE);
M5.Display.drawString("Rece:", M5.Display.width() / 2, 90);
M5.Display.drawString(String(payloadStr), M5.Display.width() / 2, 110);
}
bool mqttConnect(void)
{
log(">> Connecting to MQTT broker: " + String(MQTT_BROKER));
bool status = mqttClient.connect(mqtt_devid, mqtt_pubid, mqtt_password);
if (!status) {
log("!! MQTT Connection FAILED, code: " + String(mqttClient.state()));
return false;
}
log(">> MQTT CONNECTED, subscribing: " + String(topicSub));
mqttClient.subscribe(topicSub);
return true;
}
void nbConnect(void)
{
unsigned long start = millis();
log("Initializing modem...");
while (!modem.init()) {
log("waiting...." + String((millis() - start) / 1000) + "s");
};
start = millis();
log("Waiting for network...");
while (!modem.waitForNetwork()) {
log("waiting...." + String((millis() - start) / 1000) + "s");
}
log("success");
log("Waiting for GPRS connect...");
if (!modem.gprsConnect(apn)) {
log("waiting...." + String((millis() - start) / 1000) + "s");
}
log("success");
// Get CCID for IoT SIM card
SerialAT.println("AT+CICCID");
delay(100); // Short delay for response
String resp = "";
while (SerialAT.available()) {
resp += (char)SerialAT.read();
}
int pos = resp.indexOf("+CICCID:");
if (pos != -1) {
String ccid = resp.substring(pos + 9, resp.indexOf("\r", pos));
ccid.trim();
Serial.println("CCID: " + ccid);
} else {
Serial.println("CCID: Not found");
}
// Acquire signal strength
int csq = modem.getSignalQuality();
Serial.println("Signal quality: " + String(csq));
// Example Query the IP address of a device
String ip = modem.getLocalIP();
log("Device IP address: " + ip);
log("success");
}#include "M5Unified.h"
#define TINY_GSM_MODEM_SIM7028 // Specify to use SIM7028 module; TinyGsm library adapts instruction set based on this macro, must be placed here
#include "ArduinoHttpClient.h"
#include "TinyGsmClient.h"
#include "StreamDebugger.h"
#include "M5IOE1.h"
#define SerialMon Serial // MCU -> PC for log output
#define MONITOR_BAUDRATE 115200
#define SerialAT Serial1 // MCU <-> SIM7028 module communication
#define SIM7028_BAUDRATE 115200
#define SIM7028_RESET -1
#define MCU_TX 5
#define MCU_RX 6
#define RS485_TX 7
#define RS485_RX 8
#define IO_SCL 39
#define IO_SDA 38
// M5IOE1 device instance
M5IOE1 ioe1;
// M5IOE1 I2C address (default 0x6F)
#define I2C_ADDR M5IOE1_DEFAULT_ADDR
// Pin definitions for M5IOE1 GPIOs
#define SIM_PWR_EN M5IOE1_PIN_9
#define SIM_RST M5IOE1_PIN_11
#define VIN_ADC M5IOE1_PIN_2
// If you need to see log detail, you can open the following macro definition
// #define DUMP_AT_COMMANDS
// If you need to debug, you can open the following macro definition in TinyGsmClientSIM7028.h line 13
// #define TINY_GSM_DEBUG Serial
#ifdef DUMP_AT_COMMANDS
StreamDebugger debugger(SerialAT, SerialMon);
TinyGsm modem(debugger);
#else
TinyGsm modem(SerialAT);
#endif
// Server details
const char server[] = "httpbin.org";
const char resource[] = ""; // API endpoint path
const int port = 80; // HTTP port
TinyGsmClient client(modem); // Create TCP client based on TinyGsm (HTTP relies on TCP connection)
HttpClient http(client, server, port); // Create HTTP client instance (specify TCP client, server and port)
void modemConnect(void);// Initialize ML307 module and establish network connection
const char apn[] = "cmnbiot";
void log(String info)
{
SerialMon.println(info);
}
void SIM7028_EN()
{
if (ioe1.begin(&Wire, I2C_ADDR, IO_SDA, IO_SCL, M5IOE1_I2C_FREQ_100K, M5IOE1_INT_MODE_POLLING) == M5IOE1_OK){
ioe1.pinMode(SIM_PWR_EN, OUTPUT);
ioe1.digitalWrite(SIM_PWR_EN, HIGH); // Enable SIM power
delay(100);
ioe1.pinMode(SIM_RST, OUTPUT);
ioe1.digitalWrite(SIM_RST, LOW); // SIM reset
delay(100);
ioe1.digitalWrite(SIM_RST, HIGH);
log("SIM power enabled");
}
else{
log("Failed to enable SIM power");
}
}
void setup()
{
M5.begin();
SerialMon.begin(115200);
SerialMon.println(">>SIM7028 HTTP TEST");
SerialAT.begin(SIM7028_BAUDRATE, SERIAL_8N1, MCU_RX, MCU_TX);
M5.Display.setFont(&fonts::FreeMonoBold9pt7b);
M5.Display.setTextDatum(middle_center);
M5.Display.drawString("NBIoT HTTP", M5.Display.width() / 2, 10);
M5.Display.drawString("Init...", M5.Display.width() / 2, 60);
delay(500);
SIM7028_EN();
modemConnect();
}
void loop()
{
// --- GET request ---
M5.Display.fillRect(0, 30, 128, 98, TFT_BLACK);
M5.Display.drawString("GET", M5.Display.width() / 2, 40);
Serial.println("making GET request");
http.get("/get");
// read the status code and body of the response
int statusCode = http.responseStatusCode();
String response = http.responseBody();
Serial.print("Status code: ");
Serial.println(statusCode);
Serial.print("Response: ");
Serial.println(response);
Serial.println("Wait five seconds");
M5.Display.drawString("STATUS:", M5.Display.width() / 2, 70);
M5.Display.drawString(String(statusCode), M5.Display.width() / 2, 100);
delay(5000);
// --- POST request ---
M5.Display.fillRect(0, 30, 128, 98, TFT_BLACK);
M5.Display.drawString("POST", M5.Display.width() / 2, 40);
Serial.println("making POST request");
String contentType = "application/x-www-form-urlencoded";
String postData = "name=Alice&age=12";
http.post("/post", contentType, postData);
// read the status code and body of the response
statusCode = http.responseStatusCode();
response = http.responseBody();
Serial.print("Status code: ");
Serial.println(statusCode);
Serial.print("Response: ");
Serial.println(response);
Serial.println("Wait five seconds");
M5.Display.drawString("STATUS:", M5.Display.width() / 2, 70);
M5.Display.drawString(String(statusCode), M5.Display.width() / 2, 100);
delay(5000);
}
void modemConnect(void)
{
unsigned long start = millis();
log("Initializing modem...");
while (!modem.init()) {
log("waiting...." + String((millis() - start) / 1000) + "s");
};
start = millis();
log("Waiting for network...");
while (!modem.waitForNetwork()) {
log("waiting...." + String((millis() - start) / 1000) + "s");
}
log("success");
log("Waiting for GPRS connect...");
if (!modem.gprsConnect(apn)) {
log("waiting...." + String((millis() - start) / 1000) + "s");
}
log("success");
// Get CCID for SIM card
SerialAT.println("AT+CICCID");
delay(100); // Short delay for response
String resp = "";
while (SerialAT.available()) {
resp += (char)SerialAT.read();
}
int pos = resp.indexOf("+CICCID:");
if (pos != -1) {
String ccid = resp.substring(pos + 9, resp.indexOf("\r", pos));
ccid.trim();
Serial.println("CCID: " + ccid);
} else {
Serial.println("CCID: Not found");
}
// Acquire signal strength
int csq = modem.getSignalQuality();
log("Signal quality: " + String(csq));
// Example Query the IP address of a device
String ip = modem.getLocalIP();
log("Device IP address: " + ip);
log("success");
}
设备上电后,串口监视器会输出通信模块及 MQTT 服务器相关信息,连接成功后每隔 10 秒会向服务器发布一条消息,同时订阅的主题有消息时会打印接收到的消息内容。
串口监视器反馈如下所示:
>>SIM7028 MQTT TEST
Initializing modem...
Waiting for network...
success
Waiting for GPRS connect...
success
CCID: 898607B01022C0006068
Signal quality: 18
Device IP address: 100.50.14.152
OK
success
>> MQTT NOT CONNECTED
MQTT state code: -1
>> Connecting to MQTT broker: mqtt.m5stack.com
>> MQTT CONNECTED, subscribing: Atom DTU NBIoT v1.1 Receive
>> [MQTT SEND] Topic: Atom DTU NBIoT v1.1 Send
Info: SIM7028 MQTT! #0
=== MQTT CONNECTED ===
>> [MQTT RECEIVE] Topic: Atom DTU NBIoT v1.1 Receive
Info: Hello NBIoT 设备上电后,串口监视器会输出通信模块相关信息,连接成功后会向指定服务器循环发送 GET 和 POST 请求,主控屏幕会显示请求状态,串口可查看详细信息。


串口监视器反馈如下所示:
>>SIM7028 HTTP TEST
[M5IOE1] Initializing M5IOE1 with 100KHz (device default)
[M5IOE1] Device UID: 0xDF0E, FW Version: 41
[M5IOE1] Current I2C speed matches user request (100K)
[M5IOE1] I2C config set and verified: sleep=0, speed=100K, wake=falling, pull=on
[M5IOE1] M5IOE1 initialized at address 0x6F (I2C: 100000 Hz)
[M5IOE1] Interrupt mode -> POLLING (pause if I2C sleep on; resume when off)
[M5IOE1] Pin 1 interrupt detached and verified
[M5IOE1] Pin 8 mode set and verified: 0x03
[M5IOE1] Pin 10 mode set and verified: 0x03
SIM power enabled
Initializing modem...
Waiting for network...
success
Waiting for GPRS connect...
success
CCID: 898607B01022C0006068
Signal quality: 16
Device IP address: 100.65.16.242
OK
success
making GET request
Status code: 200
Response: {
"args": {},
"headers": {
"Host": "httpbin.org",
"User-Agent": "Arduino/2.2.0",
"X-Amzn-Trace-Id": "Root=1-6961fd12-1549e2280f15840b34db2c4e"
},
"origin": "111.55.204.81",
"url": "http://httpbin.org/get"
}
Wait five seconds
making POST request
Status code: 200
Response: {
"args": {},
"data": "",
"files": {},
"form": {
"age": "12",
"name": "Alice"
},
"headers": {
"Content-Length": "17",
"Content-Type": "application/x-www-form-urlencoded",
"Host": "httpbin.org",
"User-Agent": "Arduino/2.2.0",
"X-Amzn-Trace-Id": "Root=1-6961fd1a-4d5c07665fbd6ae37525cd8d"
},
"json": null,
"origin": "111.55.204.81",
"url": "http://httpbin.org/post"
}
Wait five seconds