思路同上一篇: 改为连接阿里云平台, 增加了平台对时功能和定位功能
思路同上一篇:
本例程实现了以下功能:
- 通过SIM800L连接网络
- 连接阿里IOT平台()
- 调用SHT20的库采集温湿度
- 将温湿度信息以及LBS定位信息上传至IOT
- 与阿里云IOT平台进行对时
- 每5分钟采集一次, 采集完成就休眠
#include <Arduino.h>
#include "PubSubClient.h"
#include "aliyun_mqtt.h"
#include "ArduinoJson.h"
#include "uFire_SHT20.h"
/*-------------------------------SIM800L 硬件定义----------------------------------*/
#define MODEM_RST 5 //SIM800L复位引脚接在GPIO5
#define MODEM_PWRKEY 4 //SIM800L开关机引脚接在GPIO4
#define MODEM_POWER_ON 23 //SIM800L电源引脚接在GPIO23
#define MODEM_TX 27 //SIM800L串口TX引脚接在GPIO27
#define MODEM_RX 26 //SIM800L串口RX引脚接在GPIO26
/*-------------------------------其他硬件定义-------------------------------------*/
uFire_SHT20 sht20;
#define SerialMon Serial //调试串口为UART0
#define SerialAT Serial1 //AT串口为UART1
/*-------------------------------公共变量,参数定义-------------------------------------*/
float currentTemp, currentHumi; //温湿度
bool tempAndHumi_Ready = false; //温湿度采集成功标志位
bool timeNTPdone = false;
//以下参数需要休眠记忆
RTC_DATA_ATTR time_t lastNTP_timestamp; //上次对时的时间戳
RTC_DATA_ATTR int postMsgId = 0; //记录已经post了多少条
RTC_DATA_ATTR float locationE, locationN; //地理位置,经度纬度
RTC_DATA_ATTR tm *timeNow; //当前时间
/*-------------------------------Modem相关定义-------------------------------------*/
#define TINY_GSM_MODEM_SIM800 // Modem is SIM800
//引入TinyGSM库. 在引入之前要定义好TINY_GSM_MODEM_SIM800,让它知道我们用的模块型号
#include <TinyGsmClient.h>
// 创建一个关联到SerialAT的SIM800L模型
TinyGsm modem(SerialAT);
// 创建一个GSM型的网络客户端
TinyGsmClient gsmclient(modem);
PubSubClient mqttClient(gsmclient);
// Your GPRS credentials (leave empty, if missing)
const char apn[] = "CMNET"; // Your APN
const char gprsUser[] = ""; // User
const char gprsPass[] = ""; // Password
const char simPIN[] = ""; // SIM card PIN code, if any
/*-------------------------------云平台相关定义-------------------------------------*/
#define PRODUCT_KEY "a1AYa96sZMJ" //产品ID
#define DEVICE_NAME "EspTempAndHumi_D001" //设备名
#define DEVICE_SECRET "a23249cb179feee41ca2f8f38525113d" //设备key
//鉴权信息
#define mqtt_password "version=2018-10-31&res=products%2F370098%2Fdevices%2Fesp_device001&et=4092512761&method=md5&sign=MUV%2BKFLzv81a4Bw6BDrChQ%3D%3D"
//设备下发命令的set主题
#define ALINK_TOPIC_PROP_SET "/sys/" PRODUCT_KEY "/" DEVICE_NAME "/thing/service/property/set"
//设备上传数据的post主题
#define ALINK_TOPIC_PROP_POST "/sys/" PRODUCT_KEY "/" DEVICE_NAME "/thing/event/property/post"
//设备post上传数据要用到一个json字符串, 这个是拼接postJson用到的一个字符串
#define ALINK_METHOD_PROP_POST "thing.event.property.post"
//这是post上传数据使用的模板
#define ALINK_BODY_FORMAT "{\"id\":\"%u\",\"version\":\"1.0\",\"method\":\"%s\",\"params\":%s}"
//服务器时间同步主题
#define ALINK_TOPIC_NTP_REQ "/ext/ntp/" PRODUCT_KEY "/" DEVICE_NAME "/request"
#define ALINK_TOPIC_NTP_RSP "/ext/ntp/" PRODUCT_KEY "/" DEVICE_NAME "/response"
/*-------------------------------初始化SIM800L-------------------------------------*/
void setupModem()
{
pinMode(MODEM_POWER_ON, OUTPUT); //电源引脚
pinMode(MODEM_PWRKEY, OUTPUT); //开关机键引脚
// 先打开SIM800L的电源
digitalWrite(MODEM_POWER_ON, HIGH);
//根据手册要求拉下PWRKEY 1秒钟以上 可以开机
digitalWrite(MODEM_PWRKEY, HIGH);
delay(100);
digitalWrite(MODEM_PWRKEY, LOW);
delay(1000);
digitalWrite(MODEM_PWRKEY, HIGH);
SerialMon.println("Initializing modem...");
modem.init(); //开机后modem初始化一下
}
/*-------------------------------SIM800L连接GPRS-------------------------------------*/
void modemToGPRS()
{
//连接网络
SerialMon.print("Waiting for network...");
while (!modem.waitForNetwork(240000L))
{
SerialMon.print(".");
delay(500);
}
SerialMon.println(" OK");
//连接GPRS接入点
SerialMon.print(F("Connecting to APN: "));
SerialMon.print(apn);
while (!modem.gprsConnect(apn, gprsUser, gprsPass))
{
SerialMon.print(".");
delay(10000);
}
SerialMon.println(" OK");
}
/*-------------------------------获取温湿度-------------------------------------*/
void getTempAndHumi()
{
float _currentTemp = sht20.temperature();
float _currentHumi = sht20.humidity();
if (_currentTemp > -50 && _currentTemp < 80)
{
currentTemp = _currentTemp;
currentHumi = _currentHumi;
tempAndHumi_Ready = true;
/* code */
}
else
{
Serial.println("此处写温湿度采集失败的处理函数");
}
}
/*-------------------------------获取位置信息-------------------------------------*/
void getLBSLocation()
{
String locationStr, locationStrX, locationStrY;
locationStr = modem.getGsmLocation();
if (locationStr.length() > 15)
{
int finddou;
finddou = locationStr.indexOf(',');
locationStrX = locationStr.substring(0, finddou);
locationStrY = locationStr.substring(finddou + 1, locationStr.length());
Serial.println(locationStr);
Serial.println(finddou);
Serial.println(locationStrX);
Serial.println(locationStrY);
if (locationStrX.toFloat() > 1)
{
locationE = locationStrX.toFloat();
locationN = locationStrY.toFloat();
}
}
}
/*-------------------------------获取NTP信息-------------------------------------*/
void mqttPublish_ntpTimeRequest()
{
if (mqttClient.connected())
{
//先拼接出json字符串
char jsonBuf[178] = "{\"deviceSendTime\":\"1571724098000\"}";
//再从mqtt客户端中发布post消息
if (mqttClient.publish(ALINK_TOPIC_NTP_REQ, jsonBuf))
{
Serial.print("NTP message to cloud: ");
Serial.println(jsonBuf);
}
else
{
Serial.println("Publish NTP message to cloud failed!");
}
}
}
/*-------------------------------向云平台断线重连-------------------------------------*/
void clientReconnect()
{
while (!mqttClient.connected()) //再重连客户端
{
Serial.print("net connected :");
Serial.println(modem.isGprsConnected());
Serial.println("reconnect MQTT...");
if (connectAliyunMQTT(mqttClient, PRODUCT_KEY, DEVICE_NAME, DEVICE_SECRET))
{
Serial.println("connected");
}
else
{
Serial.println("failed");
Serial.println(mqttClient.state());
Serial.println("try again in 5 sec");
delay(5000);
}
}
}
/*-------------------------------向云平台发送温湿度数据-------------------------------*/
void sendTempAndHumi()
{
if (mqttClient.connected())
{
if (!tempAndHumi_Ready)
return; //如果没有测好温湿度, 就不用上传了
//先拼接出json字符串
char param[182];
char jsonBuf[278];
sprintf(param, "{\"CurrentHumidity\":%.2f,\"CurrentTemperature\":%.2f,\"GeoLocation\":{\"value\":{\"Longitude\":%.2f,\"Latitude\":%.2f,\"Altitude\":150,\"CoordinateSystem\":2}}}",
currentHumi,
currentTemp,
locationE,
locationN); //我们把要上传的数据写在param里
postMsgId += 1;
sprintf(jsonBuf, ALINK_BODY_FORMAT, postMsgId, ALINK_METHOD_PROP_POST, param);
//再从mqtt客户端中发布post消息
if (mqttClient.publish(ALINK_TOPIC_PROP_POST, jsonBuf))
{
Serial.print("Post message to cloud: ");
Serial.println(jsonBuf);
}
else
{
Serial.println("Publish message to cloud failed!");
}
tempAndHumi_Ready = false;
}
}
/*-------------------------------云平台回调-------------------------------*/
void callback(char *topic, byte *payload, unsigned int length)
{
//如果收到的主题里包含字符串ALINK_TOPIC_PROP_SET(也就是收到"/sys/" PRODUCT_KEY "/" DEVICE_NAME "/thing/service/property/set"主题)
if (strstr(topic, ALINK_TOPIC_PROP_SET))
{
Serial.println("rev a topic:");
Serial.println(topic);
Serial.println("the payload is:");
payload[length] = '\0'; //为payload添加一个结束附,防止Serial.println()读过了
Serial.println((char *)payload);
//接下来是收到的json字符串的解析
DynamicJsonDocument doc(100);
DeserializationError error = deserializeJson(doc, payload);
if (error)
{
Serial.println("parse json failed");
return;
}
JsonObject setAlinkMsgObj = doc.as<JsonObject>();
serializeJsonPretty(setAlinkMsgObj, Serial);
Serial.println();
//接下来就可以解析set里的命令了
}
//如果收到的主题里包含字符串ALINK_TOPIC_NTP_RSP(也就是收到"/ext/ntp/" PRODUCT_KEY "/" DEVICE_NAME "/response")
if (strstr(topic, ALINK_TOPIC_NTP_RSP))
{
Serial.println("rev a topic:");
Serial.println(topic);
Serial.println("the payload is:");
payload[length] = '\0'; //为payload添加一个结束附,防止Serial.println()读过了
Serial.println((char *)payload);
DynamicJsonDocument timDoc(100);
DeserializationError error = deserializeJson(timDoc, payload);
if (error)
{
Serial.println("parse timDoc json failed");
return;
}
JsonObject setAlinkMsgObj = timDoc.as<JsonObject>();
serializeJsonPretty(setAlinkMsgObj, Serial);
String str = setAlinkMsgObj["serverRecvTime"];
//这个时间字符串是以毫秒为单位的, 用toInt方法可能会溢出,所以需要裁减
str = str.substring(0, 10);
long stamp = str.toInt() + 8 * 60 * 60; //我们在东八区,所以给时间戳加了八个小时的偏移
time_t timesmap = stamp;
timeNow = localtime(×map);
Serial.printf("timeNow: %d-%d-%d %d:%d",
timeNow->tm_year + 1900, //由于格林威治时间从1900年开始,所以加1900
timeNow->tm_mon + 1, //格林威治时间月份是0~11,所以+1
timeNow->tm_mday, //mday的意思是本月第几天
timeNow->tm_hour,
timeNow->tm_min);
timeNTPdone = true;
}
}
void setup()
{
Wire.begin();
sht20.begin();
SerialMon.begin(115200); //初始化调试串口
SerialAT.begin(115200, SERIAL_8N1, MODEM_RX, MODEM_TX); //初始化AT串口
setupModem(); //SIM800L物理开机
modemToGPRS(); //modem连接GPRS
Serial.println("get LBS location:");
getLBSLocation(); //采集位置信息
//连接OneNet并上传数据
Serial.println("connecting to ALI IOT...");
if (connectAliyunMQTT(mqttClient, PRODUCT_KEY, DEVICE_NAME, DEVICE_SECRET))
{
Serial.println("MQTT connected!");
};
mqttClient.setCallback(callback); //绑定收到set主题时的回调(命令下发回调)
getTempAndHumi(); //采集温湿度数据
sendTempAndHumi(); //发布一次温湿度位置消息
Serial.println("now NTPing");
timeNTPdone = false;
mqttPublish_ntpTimeRequest();
Serial.println("now looping");
while (!timeNTPdone)
{
mqttClient.loop();
}
delay(500);
//进入睡眠
esp_sleep_enable_timer_wakeup(300000000);
Serial.println("now sleep");
esp_deep_sleep_start();
}
void loop()
{
}
/*-------------------------------获取定位信息LSB-------------------------------------*/
void getLSB()
{
String locationStr, locationStrX, locationStrY;
int finddou;
locationStr = modem.getGsmLocation();
finddou = locationStr.indexOf(',');
locationStrX = locationStr.substring(0, finddou);
locationStrY = locationStr.substring(finddou+1, locationStr.length());
Serial.println(locationStr);
Serial.println(finddou);
Serial.println(locationStrX);
Serial.println(locationStrY);
}
经测试, 上传正常
这是阿里云studio的画面