最近完成了一个物联网数据采集终端的开发项目,使用STM32L151RCT6作为主控芯片,整合了温湿度传感器、GPS定位和模拟量采集功能,并通过NB-IoT模块BC20将数据上传至OneNET物联网平台。这个项目特别适合需要远程环境监测的应用场景,比如农业大棚监测、车辆定位跟踪等。
整个系统的核心功能包括:
选择STM32L151RCT6主要基于以下几点考虑:
提示:在实际PCB布局时,需要注意STM32L1系列的VDDA和VSSA引脚必须连接适当的去耦电容(通常为1μF+100nF),否则ADC精度会受到影响。
BC20是一款支持NB-IoT和GNSS的通信模组,其优势在于:
实际使用中发现,BC20的GPS天线必须放置在室外才能获得良好的定位效果。在室内测试时,定位数据往往不可靠。
完整的初始化序列如下:
c复制int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
delay_init();
Usart1_Init(115200); // 调试串口
Usart2_Init(115200); // BC20通信串口
DHT11_Init();
OLED_Init();
Led_Init();
BC26_Init();
BC26_PDPACT();
BC20_INITGNSS();
BC26_RegONENETIOT();
TIM2_Init(1000,3200); // 1秒定时
OLED_Clear();
ADC_DMA_Init();
while(1) {
// 主循环处理
}
}
DHT11采用单总线协议,时序要求严格。实际测试中发现,在读取数据前需要至少18ms的起始信号,且数据线需要上拉4.7kΩ电阻。
c复制void DHT11_Read_Data(uint8_t *temp, uint8_t *humi)
{
uint8_t buf[5];
DHT11_Start();
if(DHT11_Check()) {
for(int i=0; i<5; i++) {
buf[i] = DHT11_Read_Byte();
}
if(buf[0] + buf[1] + buf[2] + buf[3] == buf[4]) {
*humi = buf[0];
*temp = buf[2];
}
}
}
BC20的GNSS模块输出NMEA格式数据,本项目主要解析RMC语句中的经纬度信息。实际应用中发现,直接从模块获取的经纬度需要转换为十进制格式:
c复制void Getdata_Change(char status)
{
if(status) {
float GPS_Latitude = (Latitudess[0]-0x30)*10 + (Latitudess[1]-0x30) +
((Latitudedd[0]-0x30)*10 + (Latitudedd[1]-0x30) +
(float)(Latitudedd[3]-0x30)/10 + ... )/60.0;
// 经度转换同理
sprintf(latStrAF, "%f", GPS_Latitude);
sprintf(lonStrAF, "%f", GPS_Longitude);
}
}
使用DMA方式连续采集ADC数据,在主循环中读取转换结果:
c复制void ADC_DMA_Init(void)
{
ADC_InitTypeDef ADC_InitStructure;
DMA_InitTypeDef DMA_InitStructure;
// DMA配置
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&ADC_Read;
// ...其他DMA参数
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
// ADC配置
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
// ...其他ADC参数
ADC_Init(ADC1, &ADC_InitStructure);
ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE);
ADC_DMACmd(ADC1, ENABLE);
ADC_Cmd(ADC1, ENABLE);
}
BC20通过MQTT协议连接OneNET平台,关键步骤包括:
数据按照OneNET物模型规范上报,JSON格式如下:
json复制{
"id": "123",
"version": "1.0",
"params": {
"temp": {"value": 25},
"humi": {"value": 50},
"ADC": {"value": 75},
"map": {
"value": {
"lon": "116.331398",
"lat": "39.897445"
}
}
}
}
对应的AT指令构造:
c复制sprintf(SendArray, "AT+QMTPUB=0,0,0,0,\"$sys/%s/%s/thing/property/post\","
"{\"id\":\"123\",\"version\":\"1.0\",\"params\":{"
"\"temp\":{\"value\":%d},"
"\"humi\":{\"value\":%d},"
"\"ADC\":{\"value\":%d},"
"\"map\":{\"value\":{\"lon\":\"%s\",\"lat\":\"%s\"}}"
"}}\"\r\n",
PRODUCEID, DEVICENAME, Sersor.Temp, Sersor.Humi,
adc_value, gpsDatalon, gpsDatalat);
c复制void IWDG_Init(uint8_t Prc, uint16_t Reload)
{
IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);
IWDG_SetPrescaler(Prc);
IWDG_SetReload(Reload);
IWDG_ReloadCounter();
IWDG_Enable();
}
实际部署中发现,定时器中断处理中如果执行耗时操作会影响系统稳定性。建议将数据处理和通信任务放在主循环中,中断仅做标志位设置:
c复制void TIM2_IRQHandler(void)
{
if(TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) {
Timer2_Count++;
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
}
对于需要更高精度的应用,可以考虑以下改进:
这个项目完整展示了从传感器数据采集到云平台数据传输的物联网系统开发流程,其中的硬件选型、低功耗设计和通信协议实现经验可以复用到大多数物联网终端设备开发中。