本案例是使用51单片机和SIM800L模块实现上传温度数据的功能,同时可以发短信点灯。SIM800L模块可以收发短信和拨打电话,价格也挺便宜的,是户外上云的一个选择吧。虽说是使用51单片机,但是其他单片机的实现原理也一样,可以借鉴一下的。
1、在OneNET平台下创建使用HTTP协议的产品和设备
2、准备SIM卡,SIM800L支持移动卡和联通卡,最好是移动卡
3、在进行单片机测试前,可以使用USB转串口的模块单独测试
在将SIM800L模块接入单片机前,使用USB转串口对模块进行AT指令的测试,可以帮助了解上传数据的整个过程。
指令说明
串口助手显示:
连接OneNET服务器指令:AT+CIPSTART=“TCP”,“183.230.40.33”,80
每条指令都应该带回车换行,其中1A需要按16进制发送
指令发送:
可以按照如下顺序发送AT指令来上传数据到OneNET云平台。
要改的是发送数据中设备ID和Master-APIkey,其中699063374是设备ID,=08b33=G=KP8qxRoCYYrurN41yk=是Master-APIkey,18是数据(,;Temp,20;Humi,30;)长度
//查看SIM卡的状态)回车,返回:+CPIN:READY OK(正常)
AT+CPIN?
//信号质量查询
AT+CSQ
//检查是否注册到网络,返回:+CREG: 0,1(正常)
AT+CREG?
//检查 GPRS 附着状态,返回:+CGATT: 1(正常)
AT+CGATT?
//设备连接OneNET服务器,返回:CONNECT OK(正常)
AT+CIPSTART="TCP","183.230.40.33",80
//准备发送数据,返回:>(正常)
AT+CIPSEND
//发送数据
POST /devices/699063374/datapoints?type=5 HTTP/1.1
api-key:=08b33=G=KP8qxRoCYYrurN41yk=
Host:api.heclouds.com
Content-Length:18
,;Temp,30;Humi,60;
//发送结束符
1A(按16进制发送)
SIM800L模块:TX—P30 RX—P31 VCC—3.5-4.2V
DS18B20: IO—P37
由于单片机上没有3.5~4.2V的电压,所以我接了5V。
串口助手输出界面:
注:为了实现上传多个数据,伪造了一个湿度数据。
main.c文件
#include "reg52.h" //此文件中定义了单片机的一些特殊功能寄存器
#include
#include
#include "usart.h"
#include "delay.h"
#include "18b20.h"
//需要修改的是下面2行
#define devkey "=08b33=G=KP8qxRoCYYrurN41yk=" //onenet平台产品apikey
#define devid "699063374" //onenet平台设备id
float Temp = 30.5; //温度
float Humi = 66.5; //湿度
unsigned int connectErr = 0; //连接状态,0正常连接,1断开连接
unsigned int OneNet_FillBuf(char *buf); //将上传的数据打包
void send_msg(char *phone,char *msg); //发送短信
void call_phone(char *phone); //拨打电话
#if 1
void main()
{
char xdata buf[64];
unsigned int bufLen = 0;
set52_baudrate(11.0592, 115200);//串口初始化
printf("AT\r\n");
DelayMs(2000);
printf("AT+CPIN?\r\n"); //查看SIM卡的状态)回车,返回:+CPIN:READY OK(正常)
DelayMs(2000);
printf("AT+CSQ\r\n"); //信号质量查询
DelayMs(2000);
printf("AT+CREG?\r\n"); //检查是否注册到网络,返回:+CREG: 0,1(正常)
DelayMs(2000);
printf("AT+CGATT?\r\n"); //检查 GPRS 附着状态,返回:+CGATT: 1(正常)
DelayMs(2000);
printf("AT+CIPSTART=\"TCP\",\"183.230.40.33\",80\r\n"); //设备连接OneNET服务器,返回:CONNECT OK(正常)
//printf("AT+CIPSTART=\"TCP\",\"101.200.212.234\",1234\r\n"); //连接个人服务器
DelayMs(5000);
bufLen = OneNet_FillBuf(buf);
printf("AT+CIPSEND\r\n"); //准备发送数据,返回:>(正常)
DelayMs(1000);
//发送数据
printf("POST /devices/%s/datapoints?type=5 HTTP/1.1\r\napi-key:%s\r\nHost:api.heclouds.com\r\nContent-Length:%d\r\n\r\n%s",devid,devkey,bufLen,buf);
DelayMs(1000);
UARTSendByte(0X1A);
DelayMs(1000);
while(1)
{
Temp=(float)ReadTemperature()*0.0625; //读取温度并转换
bufLen = OneNet_FillBuf(buf);
printf("AT+CIPSEND\r\n"); //准备发送数据,返回:>(正常)
DelayMs(1000);
//发送数据
printf("POST /devices/%s/datapoints?type=5 HTTP/1.1\r\napi-key:%s\r\nHost:api.heclouds.com\r\nContent-Length:%d\r\n\r\n%s",devid,devkey,bufLen,buf);
DelayMs(1000);
UARTSendByte(0X1A);
DelayMs(1000);
//意外断开连接时重新连接服务器
if(connectErr==1)
{
connectErr = 0;
printf("AT+CIPSTART=\"TCP\",\"183.230.40.33\",80\r\n"); //设备连接服务器
DelayMs(3000);
}
}
}
#endif
unsigned int OneNet_FillBuf(char *buf)
{
char xdata text[32];
memset(text, 0, sizeof(text));
strcpy(buf, ",;");
memset(text, 0, sizeof(text));
sprintf(text, "Temp,%.1f;", Temp);
strcat(buf, text);
memset(text, 0, sizeof(text));
sprintf(text, "Humi,%.1f;",Humi);
strcat(buf, text);
return strlen(buf);
}
void send_msg(char *phone,char *msg)
{
printf("AT+CMGF=1\r\n");
DelayMs(1000);
printf("AT+CMGS=\"%s\"\r\n",phone);
DelayMs(1000);
printf("%s",msg);
DelayMs(1000);
//SendASC(0x1a);
UARTSendByte(0x1a);
DelayMs(1000);
}
void call_phone(char *phone)
{
printf("ATD%s;\r\n",phone);
DelayMs(3000);
}
串口中断服务程序
这部分代码是上传数据成功后p20处LED闪烁、短信点灯、断开重连
char uart_buf[32] = {0}; //用于保存串口数据
unsigned char uart_cnt = 0; //用于定位串口数据的位置
extern unsigned int connectErr;
void UART_SER (void) interrupt 4 //串行中断服务程序
{
if(RI==1) //判断是接收中断产生
{
ES = 0; //关闭串行中断
RI=0; //清除串口接收标志位
uart_buf[uart_cnt++] =SBUF; //接收串口数据
//收到succ后表示数据发送成功 succ
if(uart_buf[uart_cnt-4]=='s'&&uart_buf[uart_cnt-3]=='u'&&uart_buf[uart_cnt-2]=='c'&&uart_buf[uart_cnt-1] == 'c')
{
uart_cnt = 0;
led1 = ~led1;
}
//判断是否意外断开了连接 CLOSED
if(uart_buf[uart_cnt-4]=='O'&&uart_buf[uart_cnt-3]=='S'&&uart_buf[uart_cnt-2]=='E'&&uart_buf[uart_cnt-1] == 'D')
{
uart_cnt = 0;
connectErr = 1;
led2 = ~led2;
}
//如果模块接收到短信,会提示如:+CMTI:"SM",2
if(uart_buf[uart_cnt-4]=='C'&&uart_buf[uart_cnt-3]=='M'&&uart_buf[uart_cnt-2]=='T'&&uart_buf[uart_cnt-1]=='I')
{
uart_cnt = 0;
led3 = ~led3;
}
if(uart_cnt>28) //防止数组越界
{
uart_cnt = 0;
}
ES = 1; //开启串行中断
}
if(TI==1) //如果是发送标志位,清零
{
TI=0;
}
}
1、devkey 是Master-APIkey,devid 是设备ID
2、这个函数是将需要上传的数据打包成一定的格式,并返回数据包的长度。
如果想要上传更多的数据,只需要拷贝memset,spintf,strcat这三条代码,将仿照温湿度数据上传的格式来写。此外需要定义一个足够大的buf数组。
这部分的代码实现在串口接收中断里面,由于时间原因,我没有对短信的具体内容进行解读,只是收到任意短信后,在P2_2处的灯状态取反而已。收到短信后串口会接收到字符串+CMTI:“SM”,2 其中2代表是接收到的短信在第几条。
//如果模块接收到短信,会提示如:+CMTI:"SM",2
if(uart_buf[uart_cnt-4]=='C'&&uart_buf[uart_cnt-3]=='M'&&uart_buf[uart_cnt-2]=='T'&&uart_buf[uart_cnt-1]=='I')
{
uart_cnt = 0;
led3 = ~led3;
}
关于接收短信几点总结
查询短信条数 AT+CPMS?
返回“SIM”,3,50,“SIM”,3,50,“SIM”,3,50 说明短信最多能有50条,现在有3条。
删除短信 AT+CMGD=N //N是第几条短信
删除第1条短信后,后面的第2条短信并不会成为第1条短信,所以要删除第2条还得发AT+CMGD=2指令。
SIM800L在接收到短信后,会输出字符串+CMTI:“SM”,2
在读取短信前应该把短信设置为文本格式,不然读取出来的是一连串看不懂的数字
短信设置为文本格式:AT+CMGF=1
读取短信: AT+CMRD=N //N是第几条短信
SIM800L最基本的功能还是用来收发短信和拨打电话,代码中也有写
void send_msg(char *phone,char *msg); //发送短信
void call_phone(char *phone); //拨打电话
使用SIM800L将温湿度数据上传到OneNET里面,重点是如何将数据打包好,如果发送的数据格式不对,就会与OneNET服务器断开连接。代码还有需要改进的地方,有时候程序会突然卡死,不知道什么原因。