STM32+GPRS物联网通信:Air208S模块实战(4/10)

作者:宋一平
分类:嵌入式 / 物联网 / 通信技术
预计阅读:15分钟


一、GPRS模块选型

1.1 为什么选GPRS?

在物联网项目中,设备联网有多种选择:

方案 优点 缺点 适用场景
WiFi 速度快、成本低 需要路由器、功耗高 室内有WiFi环境
4G/Cat1 速度快、覆盖广 成本高、功耗大 需要高速传输
GPRS 覆盖广、成本低、功耗低 速度慢、2G退网风险 低频小数据传输 ✅
NB-IoT 功耗极低、覆盖深 模组贵、移动性差 静态设备
LoRa 远距离、自组网 需要网关、无运营商 私有网络

本项目选择GPRS的理由

  • 数据量小(加注记录上报,几十字节)
  • 频率低(每次加注后上报一次)
  • 成本敏感(设备要批量部署)
  • 覆盖要求(设备可能在地下室)

1.2 常见GPRS模块对比

模块 厂商 特点 价格
SIM800C SIMCom 资料多、生态成熟 ¥15-20
SIM900A SIMCom 老款、性价比高 ¥12-15
Air208S 中移物联 自带协议栈、开发简单 ¥20-25
Air202 中移物联 Air208S简化版 ¥15-18
EC20 移远 4G模块,兼容2G ¥50-60

1.3 为什么选Air208S?

Air208S的核心优势

┌─────────────────────────────────────────────────────┐
│                   Air208S 模块                       │
│                                                     │
│  ✅ 内置TCP/IP协议栈,无需自己实现                    │
│  ✅ 支持MQTT/HTTP等应用协议                          │
│  ✅ AT指令简单,开发效率高                            │
│  ✅ 低功耗模式,适合电池供电                          │
│  ✅ 全国2G覆盖,移动/联通双网                         │
│                                                     │
└─────────────────────────────────────────────────────┘

与传统GPRS模块对比

传统方案(SIM800C):
MCU → AT指令 → GPRS模块 → 自己实现TCP/IP → 服务器

Air208S方案:
MCU → AT指令 → Air208S → 内置TCP/IP → 服务器
                     ↑
              简化开发难度

二、硬件连接

2.1 Air208S引脚说明

引脚 功能 说明
VCC 电源 3.4V-4.2V,峰值电流2A
GND 与MCU共地
TXD 发送 接MCU的RX
RXD 接收 接MCU的TX
PWR_KEY 开机 拉低1秒开机
RESET 复位 拉低复位
SIM_DET SIM卡检测 插卡检测

2.2 接线图

┌─────────────────────────────────────────────────────┐
│                                                     │
│   STM32F103                                        │
│   ┌──────────┐         ┌──────────────┐            │
│   │          │         │              │            │
│   │   PA2    │◄────────│ TXD          │            │
│   │  (RXD)   │         │              │            │
│   │          │         │   Air208S    │            │
│   │   PA3    │────────►│ RXD          │            │
│   │  (TXD)   │         │              │            │
│   │          │         │              │            │
│   │   PB0    │────────►│ PWR_KEY      │            │
│   │  (GPIO)  │         │              │            │
│   │          │         │              │            │
│   │   3.3V   │         │    VCC       │◄── 4.0V   │
│   │   GND    │◄───────►│    GND       │◄── GND    │
│   │          │         │              │            │
│   └──────────┘         │    SIM卡槽   │            │
│                        │              │            │
│                        └──────────────┘            │
│                                                     │
└─────────────────────────────────────────────────────┘

2.3 电源设计要点

GPRS模块发射时电流可达2A,电源设计至关重要:

┌─────────────────────────────────────────────────────┐
│                    电源方案                          │
│                                                     │
│   12V输入 → 降压芯片 → 4.0V输出 → Air208S          │
│              (如MP1584)                             │
│                                                     │
│   ┌─────────┐      ┌─────────┐      ┌─────────┐   │
│   │  12V    │      │ MP1584  │      │  4.0V   │   │
│   │ 输入    │────► │ 降压    │────► │ 输出    │   │
│   └─────────┘      └─────────┘      └─────────┘   │
│                          │                         │
│                    大电容(470μF)                    │
│                          │                         │
│                        GND                         │
│                                                     │
└─────────────────────────────────────────────────────┘

关键设计

  • 输出电容:≥470μF,应对发射脉冲电流
  • 走线宽度:电源线≥2mm,减小压降
  • 滤波电容:靠近模块VCC引脚放置

三、AT指令入门

3.1 常用AT指令表

指令 功能 响应
AT 测试通信 OK
ATI 查询模块信息 模块型号
AT+CPIN? 查询SIM卡状态 +CPIN: READY
AT+CSQ 查询信号强度 +CSQ: 20,0
AT+CIPSTART 建立TCP连接 OK / ERROR
AT+CIPSEND 发送数据 > (等待输入)
AT+CIPCLOSE 关闭连接 OK

3.2 连接流程

Step 1: 测试通信
─────────────────
发送: AT
响应: OK

Step 2: 检查SIM卡
─────────────────
发送: AT+CPIN?
响应: +CPIN: READY

Step 3: 检查信号强度
─────────────────
发送: AT+CSQ
响应: +CSQ: 20,0
      ↑
    信号强度(0-31,建议>10)

Step 4: 建立TCP连接
─────────────────
发送: AT+CIPSTART="TCP","server.com",8080
响应: OK
      CONNECT OK

Step 5: 发送数据
─────────────────
发送: AT+CIPSEND=10
响应: >
发送: 1234567890    (10字节数据)
响应: SEND OK

Step 6: 接收数据
─────────────────
服务器下发时自动收到:
+RECEIVE,10:
1234567890

Step 7: 关闭连接
─────────────────
发送: AT+CIPCLOSE
响应: CLOSE OK

3.3 心跳机制

保持连接需要定时发送心跳包:

┌─────────────────────────────────────────────────────┐
│                    心跳流程                          │
│                                                     │
│   MCU                           服务器              │
│    │                              │                │
│    │──── 心跳包(每60秒) ─────────►│                │
│    │◄─── 心跳响应 ───────────────│                │
│    │                              │                │
│    │     超时未响应?               │                │
│    │        │                     │                │
│    │    重连3次                    │                │
│    │        │                     │                │
│    │    仍失败?                    │                │
│    │        │                     │                │
│    │    重启模块                   │                │
│    │                              │                │
└─────────────────────────────────────────────────────┘

四、代码实现

4.1 模块初始化

/**
 * @brief GPRS模块初始化
 * @return 0:成功, <0:失败
 */
s8 GPRS_Init(void)
{
    u8 retry = 0;
    u8 rx_buf[100];
    int rx_len = 0;
    
    // 初始化串口(UART2)
    UART_INIT(U_2, BOUND_9600, PRI_NONE);
    
    // 开机控制
    GPRS_PWR_KEY = 0;
    vTaskDelay(1000);  // 拉低1秒
    GPRS_PWR_KEY = 1;
    vTaskDelay(3000);  // 等待模块启动
    
    // 测试通信
    for (retry = 0; retry < 5; retry++)
    {
        UARTSend(U_2, "AT\r\n", 4);
        vTaskDelay(500);
        
        if (UARTRead(U_2, rx_buf, &rx_len))
        {
            if (strstr((char *)rx_buf, "OK") != NULL)
            {
                break;  // 通信成功
            }
        }
    }
    
    if (retry >= 5) return -1;  // 通信失败
    
    // 检查SIM卡
    UARTSend(U_2, "AT+CPIN?\r\n", 10);
    vTaskDelay(500);
    if (UARTRead(U_2, rx_buf, &rx_len))
    {
        if (strstr((char *)rx_buf, "READY") == NULL)
        {
            return -2;  // SIM卡错误
        }
    }
    
    // 检查信号强度
    UARTSend(U_2, "AT+CSQ\r\n", 8);
    vTaskDelay(500);
    if (UARTRead(U_2, rx_buf, &rx_len))
    {
        // 解析信号强度
        // +CSQ: 20,0
        u8 csq = ParseCSQ(rx_buf);
        if (csq < 10) return -3;  // 信号太弱
    }
    
    return 0;  // 初始化成功
}

4.2 TCP连接

/**
 * @brief 建立TCP连接
 * @param server 服务器地址
 * @param port 端口号
 * @return 0:成功, <0:失败
 */
s8 GPRS_Connect(char *server, u16 port)
{
    u8 cmd[100];
    u8 rx_buf[100];
    int rx_len = 0;
    
    // 构造AT指令
    sprintf((char *)cmd, "AT+CIPSTART=\"TCP\",\"%s\",%d\r\n", server, port);
    
    // 发送连接命令
    UARTSend(U_2, cmd, strlen((char *)cmd));
    
    // 等待响应(最多10秒)
    for (u8 i = 0; i < 20; i++)
    {
        vTaskDelay(500);
        if (UARTRead(U_2, rx_buf, &rx_len))
        {
            if (strstr((char *)rx_buf, "CONNECT OK") != NULL)
            {
                return 0;  // 连接成功
            }
            if (strstr((char *)rx_buf, "ERROR") != NULL)
            {
                return -1;  // 连接失败
            }
        }
    }
    
    return -2;  // 超时
}

4.3 发送数据

/**
 * @brief 发送数据
 * @param data 数据缓冲区
 * @param len 数据长度
 * @return 0:成功, <0:失败
 */
s8 GPRS_Send(u8 *data, u16 len)
{
    u8 cmd[32];
    u8 rx_buf[100];
    int rx_len = 0;
    
    // 发送数据长度
    sprintf((char *)cmd, "AT+CIPSEND=%d\r\n", len);
    UARTSend(U_2, cmd, strlen((char *)cmd));
    
    // 等待 ">" 提示符
    vTaskDelay(100);
    if (UARTRead(U_2, rx_buf, &rx_len))
    {
        if (strstr((char *)rx_buf, ">") == NULL)
        {
            return -1;  // 未收到提示符
        }
    }
    
    // 发送数据
    UARTSend(U_2, data, len);
    
    // 等待发送完成
    vTaskDelay(500);
    if (UARTRead(U_2, rx_buf, &rx_len))
    {
        if (strstr((char *)rx_buf, "SEND OK") != NULL)
        {
            return 0;  // 发送成功
        }
    }
    
    return -2;  // 发送失败
}

4.4 接收数据

/**
 * @brief 接收数据(非阻塞)
 * @param buf 接收缓冲区
 * @param max_len 最大长度
 * @return 接收到的数据长度,0表示无数据
 */
u16 GPRS_Receive(u8 *buf, u16 max_len)
{
    u8 rx_buf[200];
    int rx_len = 0;
    
    if (UARTRead(U_2, rx_buf, &rx_len))
    {
        // 解析接收格式: +RECEIVE,10:1234567890
        if (strstr((char *)rx_buf, "+RECEIVE") != NULL)
        {
            // 提取数据长度
            char *p = strchr((char *)rx_buf, ':');
            if (p != NULL)
            {
                p++;  // 跳过冒号
                u16 data_len = strlen(p);
                if (data_len > max_len) data_len = max_len;
                memcpy(buf, p, data_len);
                return data_len;
            }
        }
    }
    
    return 0;
}

4.5 完整通信流程

/**
 * @brief 上传加注记录
 * @param record 记录数据
 * @return 0:成功, <0:失败
 */
s8 Upload_Record(char *record)
{
    s8 ret;
    
    // 1. 建立连接
    ret = GPRS_Connect("api.example.com", 8080);
    if (ret != 0)
    {
        return -1;  // 连接失败
    }
    
    // 2. 发送数据
    ret = GPRS_Send((u8 *)record, strlen(record));
    if (ret != 0)
    {
        GPRS_Close();
        return -2;  // 发送失败
    }
    
    // 3. 等待响应
    vTaskDelay(1000);
    u8 rx_buf[100];
    if (GPRS_Receive(rx_buf, 100) > 0)
    {
        // 解析服务器响应
        if (strstr((char *)rx_buf, "OK") != NULL)
        {
            GPRS_Close();
            return 0;  // 上传成功
        }
    }
    
    GPRS_Close();
    return -3;  // 响应异常
}

/**
 * @brief 关闭连接
 */
void GPRS_Close(void)
{
    UARTSend(U_2, "AT+CIPCLOSE\r\n", 13);
    vTaskDelay(500);
}

五、云端对接

5.1 协议设计

本项目采用自定义文本协议,简单易懂:

上传记录格式:
id=00000001|total=20|money=200|price=10|GPSX=118.123456|GPSY=32.123456|

字段说明:
- id: 设备ID
- total: 加注量(单位:100mL)
- money: 金额(单位:分)
- price: 单价(单位:分/100mL)
- GPSX: 经度
- GPSY: 纬度

5.2 服务器响应

成功: OK
失败: ERROR:原因

示例:
OK              → 上传成功
ERROR:DB        → 数据库错误
ERROR:AUTH      → 认证失败

5.3 云平台选择

平台 特点 适用场景
阿里云IoT 功能全面、生态成熟 企业级项目
腾讯云IoT 性价比高、文档友好 中小项目
OneNET 移动出品、与Air208S兼容好 GPRS项目 ✅
自建服务器 完全可控、成本可控 特殊需求

5.4 本项目架构

┌─────────────────────────────────────────────────────┐
│                    系统架构                          │
│                                                     │
│  ┌─────────┐      GPRS       ┌─────────┐           │
│  │ 加注机  │ ◄────────────► │ 云服务器 │           │
│  │ (STM32) │                 │ (API)   │           │
│  └─────────┘                 └────┬────┘           │
│                                   │                │
│                              ┌────┴────┐           │
│                              │         │           │
│                         ┌────┴──┐ ┌────┴──┐       │
│                         │ 管理后台│ │ 微信   │       │
│                         │ (Web) │ │ 小程序 │       │
│                         └───────┘ └───────┘       │
│                                                     │
└─────────────────────────────────────────────────────┘

六、常见问题

Q1:模块不上线?

排查步骤

  1. 检查电源:VCC是否在3.4V-4.2V之间?
  2. 检查SIM卡:SIM卡是否插好?是否欠费?
  3. 检查信号:AT+CSQ查询,信号是否>10?
  4. 检查网络:是否支持2G网络?(移动/联通)
// 信号强度判断
u8 csq = ParseCSQ(rx_buf);
if (csq == 99) {
    // 99表示未知,可能是天线问题
}
else if (csq < 10) {
    // 信号太弱,建议更换天线或位置
}

Q2:连接超时?

可能原因

  1. 服务器地址错误:确认域名/端口正确
  2. 防火墙拦截:检查服务器端口是否开放
  3. 网络拥塞:GPRS网络质量不稳定,建议重试
// 重试机制
for (u8 retry = 0; retry < 3; retry++)
{
    if (GPRS_Connect(server, port) == 0)
    {
        break;  // 连接成功
    }
    vTaskDelay(5000);  // 等待5秒重试
}

Q3:数据丢失?

原因分析

  1. 发送未完成:未等待"SEND OK"
  2. 缓冲区溢出:接收缓冲区太小
  3. 并发冲突:多任务同时访问串口

解决方案

// 使用信号量保护
xSemaphoreTake(gprs_sem, 1000);
GPRS_Send(data, len);
xSemaphoreGive(gprs_sem);

Q4:功耗过高?

GPRS模块功耗分析:

状态 电流 说明
待机 3mA 空闲状态
数据传输 200-500mA 平均电流
发射峰值 2A 最大电流

省电建议

  1. 不用时关闭连接
  2. 使用PSM低功耗模式
  3. 减少上报频率

七、进阶优化

7.1 断线重连机制

/**
 * @brief 自动重连任务
 */
void GPRS_Task(void *pvParameters)
{
    while (1)
    {
        // 检查连接状态
        if (gprs_connected == 0)
        {
            // 尝试重连
            if (GPRS_Connect(SERVER, PORT) == 0)
            {
                gprs_connected = 1;
            }
            else
            {
                // 重连失败,重启模块
                GPRS_Restart();
            }
        }
        
        // 发送心跳
        if (gprs_connected)
        {
            GPRS_Send((u8 *)"PING", 4);
        }
        
        vTaskDelay(60000);  // 60秒检查一次
    }
}

7.2 数据缓存与补传

// 掉电保护:未上传记录存EEPROM
typedef struct {
    u8  count;          // 未上传记录数
    u8  records[10][64]; // 最多缓存10条
} RecordCache;

// 上传成功后清除
void Clear_Record(u8 index)
{
    memset(cache.records[index], 0, 64);
    cache.count--;
}

// 启动时检查并补传
void Check_Cache(void)
{
    for (u8 i = 0; i < cache.count; i++)
    {
        if (Upload_Record(cache.records[i]) == 0)
        {
            Clear_Record(i);
        }
    }
}

7.3 MQTT协议(可选)

如需更专业的物联网协议,可使用MQTT:

// Air208S支持MQTT指令
AT+MQTTCONNECT="broker.com",1883,"client_id","user","pass",1
AT+MQTTSUB="topic",0
AT+MQTTPUB="topic",0,0,"message"

八、总结

8.1 Air208S使用要点

要点 说明
电源设计 4.0V输出,≥470μF电容
通信测试 先用串口助手验证AT指令
错误处理 超时重试+断线重连
数据保护 EEPROM缓存未上传记录
心跳保活 定时发送心跳包

8.2 GPRS vs 其他方案

场景 推荐方案
低频小数据 GPRS ✅
高频大数据 4G/Cat1
室内有WiFi WiFi
静态低功耗 NB-IoT

8.3 本项目通信特点

  • 低频上报:每次加注后上传一次
  • 数据量小:单条记录约60字节
  • 可靠性高:重试+缓存+补传机制
  • 成本低:GPRS模块约¥20

九、源码获取

本文完整源码已收录至「STM32物联网项目资料包」

资料包包含

  • ✅ GPRS通信完整代码(初始化/连接/收发)
  • ✅ 玻璃水加注机完整工程
  • ✅ 外设驱动代码模板
  • ✅ 调试技巧文档
  • ✅ 常见问题FAQ

获取方式

  1. 点赞 + 收藏本文
  2. 私信我,备注「资料」免费领取

作者:宋一平 | 嵌入式/物联网技术分享
转载请注明出处

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐