玩轉(zhuǎn) ESP32 + Arduino (二十二) SIM800L上傳數(shù)據(jù)到阿里IOT(溫濕度和LBS)(NTP對(duì)時(shí))

思路同上一篇: 改為連接阿里云平臺(tái), 增加了平臺(tái)對(duì)時(shí)功能和定位功能
思路同上一篇:
本例程實(shí)現(xiàn)了以下功能:

  1. 通過(guò)SIM800L連接網(wǎng)絡(luò)
  2. 連接阿里IOT平臺(tái)()
  3. 調(diào)用SHT20的庫(kù)采集溫濕度
  4. 將溫濕度信息以及LBS定位信息上傳至IOT
  5. 與阿里云IOT平臺(tái)進(jìn)行對(duì)時(shí)
  6. 每5分鐘采集一次, 采集完成就休眠
#include <Arduino.h>
#include "PubSubClient.h"
#include "aliyun_mqtt.h"
#include "ArduinoJson.h"
#include "uFire_SHT20.h"

/*-------------------------------SIM800L 硬件定義----------------------------------*/
#define MODEM_RST 5       //SIM800L復(fù)位引腳接在GPIO5
#define MODEM_PWRKEY 4    //SIM800L開(kāi)關(guān)機(jī)引腳接在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 //調(diào)試串口為UART0
#define SerialAT Serial1 //AT串口為UART1

/*-------------------------------公共變量,參數(shù)定義-------------------------------------*/
float currentTemp, currentHumi; //溫濕度
bool tempAndHumi_Ready = false; //溫濕度采集成功標(biāo)志位
bool timeNTPdone = false;

//以下參數(shù)需要休眠記憶
RTC_DATA_ATTR time_t lastNTP_timestamp;       //上次對(duì)時(shí)的時(shí)間戳
RTC_DATA_ATTR int postMsgId = 0;          //記錄已經(jīng)post了多少條
RTC_DATA_ATTR float locationE, locationN; //地理位置,經(jīng)度緯度
RTC_DATA_ATTR tm *timeNow;                //當(dāng)前時(shí)間

/*-------------------------------Modem相關(guān)定義-------------------------------------*/
#define TINY_GSM_MODEM_SIM800 // Modem is SIM800
//引入TinyGSM庫(kù). 在引入之前要定義好TINY_GSM_MODEM_SIM800,讓它知道我們用的模塊型號(hào)
#include <TinyGsmClient.h>
// 創(chuàng)建一個(gè)關(guān)聯(lián)到SerialAT的SIM800L模型
TinyGsm modem(SerialAT);
// 創(chuàng)建一個(gè)GSM型的網(wǎng)絡(luò)客戶(hù)端
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

/*-------------------------------云平臺(tái)相關(guān)定義-------------------------------------*/
#define PRODUCT_KEY "a1AYa96sZMJ"                        //產(chǎn)品ID
#define DEVICE_NAME "EspTempAndHumi_D001"                //設(shè)備名
#define DEVICE_SECRET "a23249cb179feee41ca2f8f38525113d" //設(shè)備key
//鑒權(quán)信息
#define mqtt_password "version=2018-10-31&res=products%2F370098%2Fdevices%2Fesp_device001&et=4092512761&method=md5&sign=MUV%2BKFLzv81a4Bw6BDrChQ%3D%3D"

//設(shè)備下發(fā)命令的set主題
#define ALINK_TOPIC_PROP_SET "/sys/" PRODUCT_KEY "/" DEVICE_NAME "/thing/service/property/set"
//設(shè)備上傳數(shù)據(jù)的post主題
#define ALINK_TOPIC_PROP_POST "/sys/" PRODUCT_KEY "/" DEVICE_NAME "/thing/event/property/post"
//設(shè)備post上傳數(shù)據(jù)要用到一個(gè)json字符串, 這個(gè)是拼接postJson用到的一個(gè)字符串
#define ALINK_METHOD_PROP_POST "thing.event.property.post"
//這是post上傳數(shù)據(jù)使用的模板
#define ALINK_BODY_FORMAT "{\"id\":\"%u\",\"version\":\"1.0\",\"method\":\"%s\",\"params\":%s}"
//服務(wù)器時(shí)間同步主題
#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);   //開(kāi)關(guān)機(jī)鍵引腳

  // 先打開(kāi)SIM800L的電源
  digitalWrite(MODEM_POWER_ON, HIGH);
  //根據(jù)手冊(cè)要求拉下PWRKEY 1秒鐘以上 可以開(kāi)機(jī)
  digitalWrite(MODEM_PWRKEY, HIGH);
  delay(100);
  digitalWrite(MODEM_PWRKEY, LOW);
  delay(1000);
  digitalWrite(MODEM_PWRKEY, HIGH);
  SerialMon.println("Initializing modem...");
  modem.init(); //開(kāi)機(jī)后modem初始化一下
}
/*-------------------------------SIM800L連接GPRS-------------------------------------*/
void modemToGPRS()
{
  //連接網(wǎng)絡(luò)
  SerialMon.print("Waiting for network...");
  while (!modem.waitForNetwork(240000L))
  {
    SerialMon.print(".");
    delay(500);
  }
  SerialMon.println(" OK");
  //連接GPRS接入點(diǎn)
  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("此處寫(xiě)溫濕度采集失敗的處理函數(shù)");
  }
}
/*-------------------------------獲取位置信息-------------------------------------*/
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\"}";
    //再?gòu)膍qtt客戶(hù)端中發(fā)布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!");
    }
  }
}

/*-------------------------------向云平臺(tái)斷線(xiàn)重連-------------------------------------*/
void clientReconnect()
{
  while (!mqttClient.connected()) //再重連客戶(hù)端
  {
    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);
    }
  }
}
/*-------------------------------向云平臺(tái)發(fā)送溫濕度數(shù)據(jù)-------------------------------*/
void sendTempAndHumi()
{
  if (mqttClient.connected())
  {
    if (!tempAndHumi_Ready)
      return; //如果沒(méi)有測(cè)好溫濕度, 就不用上傳了
    //先拼接出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); //我們把要上傳的數(shù)據(jù)寫(xiě)在param里
    postMsgId += 1;
    sprintf(jsonBuf, ALINK_BODY_FORMAT, postMsgId, ALINK_METHOD_PROP_POST, param);
    //再?gòu)膍qtt客戶(hù)端中發(fā)布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;
  }
}

/*-------------------------------云平臺(tái)回調(diào)-------------------------------*/
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添加一個(gè)結(jié)束附,防止Serial.println()讀過(guò)了
    Serial.println((char *)payload);
    //接下來(lái)是收到的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();
    //接下來(lái)就可以解析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添加一個(gè)結(jié)束附,防止Serial.println()讀過(guò)了
    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"];
    //這個(gè)時(shí)間字符串是以毫秒為單位的, 用toInt方法可能會(huì)溢出,所以需要裁減
    str = str.substring(0, 10);
    long stamp = str.toInt() + 8 * 60 * 60; //我們?cè)跂|八區(qū),所以給時(shí)間戳加了八個(gè)小時(shí)的偏移
    time_t timesmap = stamp;
    timeNow = localtime(&timesmap);
    Serial.printf("timeNow: %d-%d-%d %d:%d",
                  timeNow->tm_year + 1900, //由于格林威治時(shí)間從1900年開(kāi)始,所以加1900
                  timeNow->tm_mon + 1,     //格林威治時(shí)間月份是0~11,所以+1
                  timeNow->tm_mday,        //mday的意思是本月第幾天
                  timeNow->tm_hour,
                  timeNow->tm_min);
    timeNTPdone = true;
  }
}

void setup()
{
  Wire.begin();
  sht20.begin();
  SerialMon.begin(115200);                                //初始化調(diào)試串口
  SerialAT.begin(115200, SERIAL_8N1, MODEM_RX, MODEM_TX); //初始化AT串口

  setupModem();  //SIM800L物理開(kāi)機(jī)
  modemToGPRS(); //modem連接GPRS
  Serial.println("get LBS location:");
  getLBSLocation(); //采集位置信息
  //連接OneNet并上傳數(shù)據(jù)
  Serial.println("connecting to ALI IOT...");
  if (connectAliyunMQTT(mqttClient, PRODUCT_KEY, DEVICE_NAME, DEVICE_SECRET))
  {
    Serial.println("MQTT connected!");
  };
  mqttClient.setCallback(callback); //綁定收到set主題時(shí)的回調(diào)(命令下發(fā)回調(diào))
  getTempAndHumi();                 //采集溫濕度數(shù)據(jù)
  sendTempAndHumi();                //發(fā)布一次溫濕度位置消息
  Serial.println("now NTPing");
  timeNTPdone = false;
  mqttPublish_ntpTimeRequest();
  Serial.println("now looping");
  while (!timeNTPdone)
  {
    mqttClient.loop();
  }
  delay(500);
  //進(jìn)入睡眠
  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);
}

經(jīng)測(cè)試, 上傳正常

這是阿里云studio的畫(huà)面

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末融痛,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子迎卤,更是在濱河造成了極大的恐慌管搪,老刑警劉巖酸舍,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瓣戚,死亡現(xiàn)場(chǎng)離奇詭異沛慢,居然都是意外死亡肾砂,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)扣溺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)骇窍,“玉大人,你說(shuō)我怎么就攤上這事锥余「鼓桑” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵哈恰,是天一觀的道長(zhǎng)只估。 經(jīng)常有香客問(wèn)我志群,道長(zhǎng)着绷,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任锌云,我火速辦了婚禮荠医,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘桑涎。我一直安慰自己彬向,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布攻冷。 她就那樣靜靜地躺著娃胆,像睡著了一般。 火紅的嫁衣襯著肌膚如雪等曼。 梳的紋絲不亂的頭發(fā)上里烦,一...
    開(kāi)封第一講書(shū)人閱讀 49,144評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音禁谦,去河邊找鬼胁黑。 笑死,一個(gè)胖子當(dāng)著我的面吹牛州泊,可吹牛的內(nèi)容都是我干的丧蘸。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼遥皂,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼力喷!你這毒婦竟也來(lái)了刽漂?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤弟孟,失蹤者是張志新(化名)和其女友劉穎爽冕,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體披蕉,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡颈畸,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了没讲。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片眯娱。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖爬凑,靈堂內(nèi)的尸體忽然破棺而出徙缴,到底是詐尸還是另有隱情,我是刑警寧澤嘁信,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布于样,位于F島的核電站,受9級(jí)特大地震影響潘靖,放射性物質(zhì)發(fā)生泄漏穿剖。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一卦溢、第九天 我趴在偏房一處隱蔽的房頂上張望糊余。 院中可真熱鬧,春花似錦单寂、人聲如沸贬芥。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)蘸劈。三九已至,卻和暖如春尊沸,著一層夾襖步出監(jiān)牢的瞬間威沫,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工椒丧, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留壹甥,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓壶熏,卻偏偏與公主長(zhǎng)得像句柠,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345