#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); }
ESP32的DAC函数可以实现真正的模拟输出。
ESP32 没有Arduino输出 PWM 的 analogWrite(pin, value) 方法,取而代之的 ESP32 有一个 LEDC 来实现PWM功能。
本文学习如何使用ESP32开发板来进行多线程的开发。
ESP8266有三种工作模式,分别为:AP,STA,AP混合STA
ESP32有四个SPI外设,分别为SPI0、SPI1、HSPI和VSPI。