一.替换原因:
本次项目名称为基于LoRa山区水库水质检测,由此可以看到检测对象为山区的水质检测,山区用ESP8266肯定就不合适的,因为山区没有局域网,所有这里我就用2G模块SIM900A来代替ESP8266传输数据到云端。
二.SIM900A通信方式:
1.模块介绍:
2.通信注意事项:
SIM900A跟ESP8266类似也是通过串口发送AT指令进行配置的,其中需要注意的是SIM900A有两组TX和RX,其中一组为5v的TX和RX,另外一组为3.3v的TX和RX,如果后续单片机串口接在5v的TX和RX上,需要注意的是此时通信的波特率为9600;如果单片机接在3.3v的TX和RX上,此时双方通信的波特率为:115200;最后就是SIM900A供电是用5v进行供电的。
三.SIM900A常用的AT指令:
AT+CPMS // 查询SIM卡内短消息使用状态
AT+CNMI // 新消息指示设置
AT+CMGF // 选择短消息格式
AT+CSCS // 编码设置
AT+CSCA // 查询、设置SMS服务中心号码
AT+CSMP // 设置短消息文本模式参数
AT+CMGS // 发送短消息
AT+CMGR // 读取短消息
AT+CMGD // 删除短消息
AT+CMGDA // 删除所有短消息
AT&F // 恢复出厂设置
AT+CSQ // 信号质量
更多的AT指令可以是找对应的开发文档,这里就不在一一展示了,咱们在开发这个模块的时候,可以首先用串口调试器进行测试一下这个模块,调试成功后才能更加方便程序设计。
四.SIM900A程序设计:
1.串口初始化程序
我这里单片机的串口2接在了SIM900A模块5v的TX和RX,所有这里的波特率就应该是9600.
//Usart2_Init(9600); //5TX---PA3 5RX---PA2
void Usart2_Init(unsigned int baud)
{
GPIO_InitTypeDef gpioInitStruct;
USART_InitTypeDef usartInitStruct;
NVIC_InitTypeDef nvicInitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
//PA2 TXD
gpioInitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
gpioInitStruct.GPIO_Pin = GPIO_Pin_2;
gpioInitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &gpioInitStruct);
//PA3 RXD
gpioInitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
gpioInitStruct.GPIO_Pin = GPIO_Pin_3;
gpioInitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &gpioInitStruct);
usartInitStruct.USART_BaudRate = baud;
usartInitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //无硬件流控
usartInitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //接收和发送
usartInitStruct.USART_Parity = USART_Parity_No; //无校验
usartInitStruct.USART_StopBits = USART_StopBits_1; //1位停止位
usartInitStruct.USART_WordLength = USART_WordLength_8b; //8位数据位
USART_Init(USART2, &usartInitStruct);
USART_Cmd(USART2, ENABLE); //使能串口
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); //使能接收中断
nvicInitStruct.NVIC_IRQChannel = USART2_IRQn;
nvicInitStruct.NVIC_IRQChannelCmd = ENABLE;
nvicInitStruct.NVIC_IRQChannelPreemptionPriority = 0;
nvicInitStruct.NVIC_IRQChannelSubPriority = 0;
NVIC_Init(&nvicInitStruct);
}
void USART2_IRQHandler(void)
{
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) //接收中断
{
if(esp8266_cnt >= sizeof(esp8266_buf)) esp8266_cnt = 0; //防止串口被刷爆
esp8266_buf[esp8266_cnt++] = USART2->DR;
USART_ClearFlag(USART2, USART_FLAG_RXNE);
}
}
2.SIM900A模块初始化程序
/*==================================================
函数 功能:GSM模块初始化检测
函数返回值:1表示模块检测失败,0表示成功
===================================================*/
u8 SIM800C_InitCheck(void)
{
UsartPrintf(USART_DEBUG, "01. AT\r\n");
while(ESP8266_SendCmd("AT\r\n", "OK"))
delay_ms(500);
UsartPrintf(USART_DEBUG, "02. ATE0\r\n");
while(ESP8266_SendCmd("ATE0\r\n", "OK"))
delay_ms(500);
UsartPrintf(USART_DEBUG, "03. AT+CGMI\r\n");
while(ESP8266_SendCmd("AT+CGMI\r\n", "OK"))
delay_ms(500);
UsartPrintf(USART_DEBUG, "04. AT+CGMM\r\n");
while(ESP8266_SendCmd("AT+CGMM\r\n", "OK"))
delay_ms(500);
return 0;
}
3.SIM900A模块与OneNET建立TCP连接
/*=======================================================
函数功能: 连接TCP服务器
函数参数:
ipaddr:ip地址
port:端口
返 回 值: 0表示成功,其他值表示失败
========================================================*/
u8 SIM800C_Connect_TCP_Server(char *ipaddr,char *port)
{
char cmd_buff[100];
ESP8266_SendCmd("AT+CIPSHUT\r\n","SHUT OK"); //关闭移动场景
sprintf(cmd_buff,"AT+CIPSTART=\"TCP\",\"%s\",\"%s\"\r\n",ipaddr,port);
while(ESP8266_SendCmd(cmd_buff,"OK"))
{
delay_ms(500);
}
printf("TCP 连接成功\r\n");
return 0;
}
4.SIM900A模块采用HTTP协议方式向OneNET服务器上报数据
我这里采用了count来计算SIM900A向OneNET云平台发送数据错误的次数,当错误小于等于5次时,就重新进行一次与OneNET建立TCP连接,这样效果就会很好,经过24小时的长时间测试,SIM900A会一直向OneNET发送数据而不会断。
int count=0;
void OneNet_HTTP_DataUpdate(float temp,float tds, float ph)
{
char OneNet_HTTP_CMD[400]={0};
char OneNet_HTTP_BUFF[200]={0};
snprintf(OneNet_HTTP_BUFF,sizeof(OneNet_HTTP_BUFF),
"{\"datastreams\":[{\"id\":\"Temp\",\"datapoints\":[{\"value\":%.1f}]},{\"id\":\"TDS\",\"datapoints\":[{\"value\":%.1f}]},{\"id\":\"PH\",\"datapoints\":[{\"value\":%.1f}]}]}",temp,tds,ph);
//拼接数据
snprintf(OneNet_HTTP_CMD,sizeof(OneNet_HTTP_CMD),
"POST /devices/%s/datapoints HTTP/1.1\r\n"\
"api-key:%s\r\n"\
"Host:api.heclouds.com\r\n"\
"Content-Length:%d\r\n"\
"\r\n"\
"%s",
ONENET_DEVICE_ID,ONENET_API_KEY,strlen(OneNet_HTTP_BUFF),OneNet_HTTP_BUFF);
if( SIIM800C_TCP_SendData( (u8*)OneNet_HTTP_CMD,strlen(OneNet_HTTP_CMD) ) == 0 )
{
printf("send data ok!\r\n");
}
else
{
printf("send data error!\r\n");
count++;
if(count >= 5)
{
count=0;
while(SIM800C_Connect_TCP_Server("183.230.40.33","80"))
{
delay_ms(500);
}
}
}
}
5.主程序
#include "system.h"
#include "SysTick.h"
#include "led.h"
#include "usart.h"
#include "usart3.h"
#include "key.h"
#include "stdio.h"
#include "string.h"
#include "lora.h"
#include "sim800c.h"
#include "time.h"
//Lora接收数据数组
extern char lora_rx_data[50];
extern char lora_rx_data2[50];
//接收数据变量
float Temp=0.0;
float TDS=0.0;
float PH=0.0;
int main()
{
/*=======================================系统初始化======================================*/
//时钟初始化
SysTick_Init(72);
//中断优先级分组 分2组
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
//串口初始化
Usart1_Init(115200);
Usart2_Init(9600); //5TX---PA3 5RX---PA2
Usart3_Init(115200);
//定时1ms
TIM4_Init ( 99, 719 );
/*=======================================传感器初始化======================================*/
//初始化SIM800C
while(SIM800C_InitCheck())
{
delay_ms(500);
}
while(SIM800C_Connect_TCP_Server("183.230.40.33","80"))
{
delay_ms(500);
}
//Lora初始化
LORA_Init();
/*=======================================while循环========================================*/
while(1)
{
//数据接收
Lora_Get_Data();
//发布数据
if(tim_v.count_SIM > 2000)
{
tim_v.count_SIM=0;
OneNet_HTTP_DataUpdate(Temp,TDS,PH);
}
//延时
delay_ms(10);
}
}