国产秋霞理论久久久电影-婷婷色九月综合激情丁香-欧美在线观看乱妇视频-精品国avA久久久久久久-国产乱码精品一区二区三区亚洲人-欧美熟妇一区二区三区蜜桃视频

前端程序員是怎么做物聯(lián)網(wǎng)開發(fā)的

共 46585字,需瀏覽 94分鐘

 ·

2023-10-16 20:50

大廠技術  高級前端  Node進階

點擊上方 程序員成長指北,關注公眾號

回復1,加入高級Node交流群



關于文本,作者:德萊厄斯 

鏈接:https://juejin.cn/post/7203180003471081531


image-20230104162825029

上圖是我歷時一周做的在線的溫濕度可視化項目,可以查看截至目前往前一天的溫度、濕度變化趨勢,并且實時更新當前溫濕度

概述和基礎講解

該項目用到的技術有:

  • 前端:jq、less、echarts、mqtt.js
  • 后端:eggjs、egg-emqtt
  • 數(shù)據(jù)庫:mysql
  • 服務器:emqx(mqtt broker)
  • 硬件:
    • 板子:wemos D1 R2(設計基于 Arduino Uno R3 , 搭載esp8266 wifi模塊)
  • 調(diào)試工具:mqttx、Arduino IDE v2.0.3 使用Arduino C開發(fā)

必備知識:

  • nodejs(eggjs框架)能面向業(yè)務即可
  • mysql 能寫基本插入查詢語句即可
  • C語言的基本語法了解即可
  • 知道m(xù)qtt協(xié)議的運作方式即可
  • arduino 開發(fā)板或任何其他電路板的初步了解即可

簡單介紹一下上面幾個的知識點:

  1. 從來沒有后端學習經(jīng)驗的同學,推薦一個全棧項目供你參考:**vue-xmw-admin-pro** ,該項目用到了 前端VUE、后端eggjs、mysql、redis,對全棧學習很有幫助。

  2. mysql 只需要知道最簡單的插入和查詢語句即可,在本項目中,其實使用mongodb是更合適的,但是我為了方便,直接用了現(xiàn)成的mysql

  3. 即使你不知道C語言的基本語法,也可以在一小時內(nèi)快速了解一下,知道簡單的定義變量、函數(shù)、返回值即可

  4. MQTT(消息隊列遙測傳輸)是一種網(wǎng)絡協(xié)議(長連接,意思就是除了客戶端可以主動向服務器通信外,服務器也可以主動向客戶端發(fā)起),也是基于TCP/IP的,適用于算力低下的硬件設備使用,基于發(fā)布\訂閱范式的消息協(xié)議,具體示例如下:

    image-20230104170333941

    當某客戶端想發(fā)布消息時,圖大概長這樣:

    image-20230104171235368

    由上圖可知,當客戶端通過驗證上線后,還需要訂閱主題,當某客戶端向某主題發(fā)布消息時,只有訂閱了該主題的客戶端會收到broker的轉發(fā)。

    舉一個簡單的例子:你和我,還有他,我們把自己的名字、學號報告給門衛(wèi)大爺(broker),門衛(wèi)大爺就同意我們在警衛(wèi)室玩一會,警衛(wèi)室有無數(shù)塊黑板(topic),我們每個人都可以向門衛(wèi)請求:如果某黑板上被人寫了字,請轉告給我。門衛(wèi)會記住每個人的要求,比如當你向一塊黑板寫了字(你向某topic發(fā)送了消息),所有要求門衛(wèi)告訴的人都會被門衛(wèi)告知你寫了什么(如果你也要求被告知,那么也包括你自己)。

  5. 開發(fā)板可以被寫入程序,程序可以使用簡單的代碼控制某個針腳的高低電平,或者讀取某針腳的數(shù)據(jù)。

開始

  1. 購買 wemos d1開發(fā)板、DHT11溫濕度傳感器,共計19.3元。
  2. 使用arduino ide(以下簡稱ide) 對wemos d1編程需要下載esp8266依賴 參見:Arduino IDE安裝esp8266 SDK
  3. 在ide的菜單欄選擇:文件>首選項>其他開發(fā)板管理器地址填入:arduino.esp8266.com/stable/pack…,可以順便改個中文
  4. 安裝ch340驅(qū)動參見:win10 安裝 CH340驅(qū)動 實測win11同樣可用
  5. 使用 micro-usb 線,連接電腦和開發(fā)板,在ide菜單中選擇:工具>開發(fā)板>esp8266>LOLIN(WEMOS) D1 R2 & mini
  6. 選擇端口,按win+x,打開設備管理器,查看你的ch340在哪個端口,在ide中選擇對應的端口
  7. 當ide右下角顯示LOLIN(WEMOS) D1 R2 & mini 在comXX上時,連接就成功了
  8. 打開ide菜單欄 :文件>示例>esp8266>blink,此時ide會打開新窗口,在新窗口點擊左上角的上傳按鈕,等待上傳完成,當板子上的燈一閃一閃,就表明:環(huán)境、設置、板子都沒問題,可以開始編程了,如果報錯,那么一定是哪一步出問題了,我相信你能夠根據(jù)錯誤提示找出到底是什么問題,如果實在找不出問題,那么可能買到了壞的板子(故障率還是蠻高的)

wemos d1 針腳中有一個 3.3v電源輸出,三個或更多的GND接地口,當安裝DHT11傳感器元件時,需要將正極插入3.3v口,負極插入GND口,中間的數(shù)據(jù)線插入隨便的數(shù)字輸入口,比如D5口(D5口的PIN值是14,后面會用到)。

使用DHT11傳感器,需要安裝庫:DHT sensor library by Adafruit , 在ide的左側欄中的庫管理中直接搜索安裝即可

下面是一個獲取DHT11數(shù)據(jù)的簡單示例,如果正常的話,在串口監(jiān)視器中,會每秒輸出溫濕度數(shù)據(jù)

arduino復制代碼#include "DHT.h"  //這是依賴或者叫庫,或者叫驅(qū)動也行
#include "string.h"
#define DHTPIN 14 // DHT11數(shù)據(jù)引腳連接到D5引腳 D5引腳的PIN值是14
#define DHTTYPE DHT11 // 定義DHT11傳感器
DHT dht(DHTPIN, DHTTYPE); //初始化傳感器

void setup() {
Serial.begin(115200);
//wemos d1 的波特率是 115200
pinMode(BUILTIN_LED, OUTPUT); //設置一個輸出的LED
dht.begin(); //啟動傳感器
}

char* getDHT11Data() {
float h = dht.readHumidity(); //獲取濕度值
float t = dht.readTemperature(); //獲取溫度值
static char data[100];
if (isnan(h) || isnan(t)) {
Serial.println("Failed to read from DHT sensor!");
sprintf(data, "Temperature: %.1f, Humidity: %.1f", 0.0, 0.0); //如果任何一個值沒有值,直接返回兩個0.0,這樣我們就知道傳感器可能出問題了
return data;
}
sprintf(data, "Temperature: %.1f, Humidity: %.1f", t, h); //正常就取到值,我這里拼成了一句話
return data;
}

void loop() {
char* data = getDHT11Data(); //此處去取傳感器值
Serial.println("got: " + String(data)); // 打印主題內(nèi)容
delay(1000); //每次循環(huán)延遲一秒
}

繼續(xù)

到了這一步,如果你用的是普通的arduino uno r3板子,就可以結束了。

取到數(shù)據(jù)之后,你就可以根據(jù)數(shù)據(jù)做一些其他的事情了,比如打開接在d6引腳上的繼電器,而這個繼電器控制著一個加濕器。

如果你跟我一樣,使用了帶wifi網(wǎng)絡的板子,就可以繼續(xù)跟我做。

我們繼續(xù)分步操作:

設備端:

  1. 引入esp8266庫(上面已經(jīng)提到安裝過程)

    1. arduino
      復制代碼#include "ESP8266WiFi.h"
  2. 安裝mqtt客戶端庫 ,直接在庫商店搜索 PubSubClient ,下載 PubSubClient by Nick O'Leary 那一項,下載完成后:

    1. arduino
      復制代碼#include "PubSubClient.h"
  3. 至此,庫文件已全部安裝引入完畢

  4. 設置 wifi ssid(即名字) 和 密碼,如:

    1. ini復制代碼char* ssid = "2104";
      char* passwd = "13912428897";
  5. 嘗試連接 wifi

    1. scss復制代碼WiFiClient espClient;
      int isConnect = 0;
      void connectWIFI() {
      isConnect = 0;
      WiFi.mode(WIFI_STA); //不知道什么意思,照著寫就完了
      WiFi.begin(ssid, passwd); //嘗試連接
      int timeCount = 0; //嘗試次數(shù)
      while (WiFi.status() != WL_CONNECTED) { //如果沒有連上,繼續(xù)循環(huán)
      for (int i = 200; i <= 255; i++) {
      analogWrite(BUILTIN_LED, i);
      delay(2);
      }
      for (int i = 255; i >= 200; i--) {
      analogWrite(BUILTIN_LED, i);
      delay(2);
      }
      // 上兩個循環(huán)共計200ms左右,在控制LED閃爍而已,你也可以不寫
      Serial.println("wifi connecting......" + String(timeCount));
      timeCount++;
      isConnect = 1; //每次都需要把連接狀態(tài)碼設置一下,只有連不上時設置為0
      // digitalWrite(BUILTIN_LED, LOW);
      if (timeCount >= 200) {
      // 當40000毫秒時還沒連上,就不連了
      isConnect = 0; //設置狀態(tài)碼為 0
      break;
      }
      }
      if (isConnect == 1) {
      Serial.println("Connect to wifi successfully!" + String("SSID is ") + WiFi.SSID());
      Serial.println(String("mac address is ") + WiFi.macAddress());
      // digitalWrite(BUILTIN_LED, LOW);
      analogWrite(BUILTIN_LED, 250); //設置LED常亮,250的亮度對我來說已經(jīng)很合適了
      settMqttConfig(); //嘗試連接mqtt服務器,在下一步有詳細代碼
      } else {
      analogWrite(BUILTIN_LED, 255); //設置LED常滅,不要問我為什么255是常滅,因為我的燈是高電平熄滅的
      //連接wifi失敗,等待一分鐘重連
      delay(60000);
      }
      }
  6. 嘗試連接 mqtt

    1. arduino復制代碼const char* mqtt_server = "larryblog.top";  //這里是我的服務器,當你看到這篇文章的時候,很可能已經(jīng)沒了,因為我的服務器還剩11天到期
      const char* TOPIC = "testtopic"; // 設置信息主題
      const char* client_id = "mqttx_3b2687d2"; //client_id不可重復,可以隨便取,相當于你的網(wǎng)名
      PubSubClient client(espClient);
      void settMqttConfig() {
      client.setServer(mqtt_server, 1883); //設定MQTT服務器與使用的端口,1883是默認的MQTT端口
      client.setCallback(onMessage); //設置收信函數(shù),當訂閱的主題有消息進來時,會進這個函數(shù)
      Serial.println("try connect mqtt broker");
      client.connect(client_id, "wemos", "aa995231030"); //后兩個參數(shù)是用戶名密碼
      client.subscribe(TOPIC); //訂閱主題
      Serial.println("mqtt connected"); //一切正常的話,就連上了
      }
      //收信函數(shù)
      void onMessage(char* topic, byte* payload, unsigned int length) {
      Serial.print("Message arrived [");
      Serial.print(topic); // 打印主題信息
      Serial.print("]:");
      char* payloadStr = (char*)malloc(length + 1);
      memcpy(payloadStr, payload, length);
      payloadStr[length] = '\0';
      Serial.println(payloadStr); // 打印主題內(nèi)容
      if (strcmp(payloadStr, (char*)"getDHTData") == 0) {
      char* data = getDHT11Data();
      Serial.println("got: " + String(data)); // 打印主題內(nèi)容
      client.publish("wemos/dht11", data);
      }
      free(payloadStr); // 釋放內(nèi)存
      }
  7. 發(fā)送消息

    1. arduino復制代碼client.publish("home/status/", "{device:client_id,'status':'on'}");
      //注意,這里向另外一個主題發(fā)送的消息,消息內(nèi)容就是設備在線,當有其他的客戶端(比如web端)訂閱了此主題,便能收到此消息

至此,板子上的代碼基本上就寫完了,完整代碼如下:

scss復制代碼#include "ESP8266WiFi.h"
#include "PubSubClient.h"
#include "DHT.h"
#include "string.h"
#define DHTPIN 14 // DHT11數(shù)據(jù)引腳連接到D5引腳
#define DHTTYPE DHT11 // DHT11傳感器
DHT dht(DHTPIN, DHTTYPE);

char* ssid = "2104";
char* passwd = "13912428897";
const char* mqtt_server = "larryblog.top";
const char* TOPIC = "testtopic"; // 訂閱信息主題
const char* client_id = "mqttx_3b2687d2";
int isConnect = 0;
WiFiClient espClient;
PubSubClient client(espClient);
long lastMsg = 0;
void setup() {
Serial.begin(115200);
// Set WiFi to station mode
connectWIFI();
pinMode(BUILTIN_LED, OUTPUT);
dht.begin();
}
char* getDHT11Data() {
float h = dht.readHumidity();
float t = dht.readTemperature();
static char data[100];
if (isnan(h) || isnan(t)) {
Serial.println("Failed to read from DHT sensor!");
sprintf(data, "Temperature: %.1f, Humidity: %.1f", 0.0, 0.0);
return data;
}
sprintf(data, "Temperature: %.1f, Humidity: %.1f", t, h);
return data;
}
void connectWIFI() {
isConnect = 0;
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, passwd);
int timeCount = 0;
while (WiFi.status() != WL_CONNECTED) {
for (int i = 200; i <= 255; i++) {
analogWrite(BUILTIN_LED, i);
delay(2);
}
for (int i = 255; i >= 200; i--) {
analogWrite(BUILTIN_LED, i);
delay(2);
}
// 上兩個循環(huán)共計200ms左右
Serial.println("wifi connecting......" + String(timeCount));
timeCount++;
isConnect = 1;
// digitalWrite(BUILTIN_LED, LOW);
if (timeCount >= 200) {
// 當40000毫秒時還沒連上,就不連了
isConnect = 0;
break;
}
}
if (isConnect == 1) {
Serial.println("Connect to wifi successfully!" + String("SSID is ") + WiFi.SSID());
Serial.println(String("mac address is ") + WiFi.macAddress());
// digitalWrite(BUILTIN_LED, LOW);
analogWrite(BUILTIN_LED, 250);
settMqttConfig();
} else {
analogWrite(BUILTIN_LED, 255);
//連接wifi失敗,等待一分鐘重連
delay(60000);
}
}
void settMqttConfig() {
client.setServer(mqtt_server, 1883); //設定MQTT服務器與使用的端口,1883是默認的MQTT端口
client.setCallback(onMessage);
Serial.println("try connect mqtt broker");
client.connect(client_id, "wemos", "aa995231030");
client.subscribe(TOPIC);
Serial.println("mqtt connected");
}
void onMessage(char* topic, byte* payload, unsigned int length) {
Serial.print("Message arrived [");
Serial.print(topic); // 打印主題信息
Serial.print("]:");
char* payloadStr = (char*)malloc(length + 1);
memcpy(payloadStr, payload, length);
payloadStr[length] = '\0';
Serial.println(payloadStr); // 打印主題內(nèi)容
if (strcmp(payloadStr, (char*)"getDHTData") == 0) {
char* data = getDHT11Data();
Serial.println("got: " + String(data)); // 打印主題內(nèi)容
client.publish("wemos/dht11", data);
}
free(payloadStr); // 釋放內(nèi)存
}
void publishDhtData() {
char* data = getDHT11Data();
Serial.println("got: " + String(data)); // 打印主題內(nèi)容
client.publish("wemos/dht11", data);
delay(2000);
}
void reconnect() {
Serial.print("Attempting MQTT connection...");
// Attempt to connect
if (client.connect(client_id, "wemos", "aa995231030")) {
Serial.println("reconnected successfully");
// 連接成功時訂閱主題
client.subscribe(TOPIC);
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
// Wait 5 seconds before retrying
delay(5000);
}
}
void loop() {
if (!client.connected() && isConnect == 1) {
reconnect();
}
if (WiFi.status() != WL_CONNECTED) {
connectWIFI();
}
client.loop();
publishDhtData();
long now = millis();
if (now - lastMsg > 2000) {
lastMsg = now;
client.publish("home/status/", "{device:client_id,'status':'on'}");
}
// Wait a bit before scanning again
delay(1000);
}

服務器

剛才的一同操作很可能讓人一頭霧水,相信大家對上面mqtt的操作還是一知半解的,不過沒有關系,通過對服務端的設置,你會對mqtt的機制了解的更加透徹

我們需要在服務端部署 mqtt broker,也就是mqtt的消息中心服務器

在網(wǎng)絡上搜索 emqx , 點擊 EMQX: 大規(guī)模分布式物聯(lián)網(wǎng) MQTT 消息服務器 ,這是一個帶有可視化界面的軟件,而且畫面特別精美,操作特別絲滑,功能相當強大,使用起來基本上沒有心智負擔。點擊立即下載,并選擇適合你的服務器系統(tǒng)的版本:

image-20230223102450653

這里拿 ubuntu和windows說明舉例,相信其他系統(tǒng)也都大差不差

在ubuntu上,推薦使用apt下載,按上圖步驟操作即可,如中途遇到其他問題,請自行解決

  1. sudo ufw status 查看開放端口,一般情況下,你只會看到幾個你手動開放過的端口,或者只有80、443端口
  2. udo ufw allow 18083 此端口是 emqx dashboard 使用的端口,開啟此端口后,可以在外網(wǎng)訪問 emqx看板控制臺

當你看到如圖所示的畫面,說明已經(jīng)開啟成功了

windows下直接下載安裝包,上傳到服務器,雙擊安裝即可

  1. 打開 “高級安全Windows Defender 防火墻”,點擊入站規(guī)則>新建規(guī)則
  2. 點擊端口 > 下一步
  3. 點擊TCP、特定本地端口 、輸入18083,點擊下一步
  4. 一直下一步到最后一步,輸入名稱,推薦輸入 emqx 即可
image-20230223103810837

當你看到如圖所示畫面,說明你已經(jīng)配置成功了。

完成服務端程序安裝和防火墻端口配置后,我們需要配置服務器后臺的安全策略,這里拿阿里云舉例:

如果你是 ECS 云主機,點擊實例>點擊你的服務器名>安全組>配置規(guī)則>手動添加

添加這么一條即可:

image-20230223104139442

如果你是輕量服務器,點擊安全>防火墻>添加規(guī)則 即可,跟ECS設置大差不差。

完成后,可以在本地瀏覽器嘗試訪問你的emqx控制臺

image-20230223104408482

直接輸入域名:18083即可,初始用戶名為admin,初始密碼為public,登錄完成后,你便會看到如下畫面

image-20230223104559151

接下來需要配置 客戶端登錄名和密碼,比如剛剛在設備中寫的用戶名密碼,就是在這個系統(tǒng)中設置的

點擊 訪問控制>認證 > 創(chuàng)建,然后無腦下一步即可,完成后你會看到如下畫面

image-20230223104906488

點擊用戶管理,添加用戶即可,用戶名和密碼都是自定義的,這些用戶名密碼可以分配給設備端、客戶端、服務端、測試端使用,可以參考我的配置

image-20230223105013597

userClient是準備給前端頁面用的 ,server是給后端用的,995231030是我個人自留的超級用戶,wemos是設備用的,即上面設備連接時輸入的用戶名密碼。

至此,emqx 控制臺配置完成。

下載 mqttx,作為測試端嘗試連接一下

image-20230223105505838

點擊連接,你會發(fā)現(xiàn),根本連接不上......

因為,1883(mqtt默認端口)也是沒有開啟的,當然,和開啟18083的方法一樣。

同時,還建議你開啟:

  • 1803 websocket 默認端口
  • 1804 websockets 默認端口
  • 3306 mysql默認端口

后面這四個端口都會用到。

當你開啟完成后,再次嘗試使用mqttx連接broker,會發(fā)現(xiàn)可以連接了

image-20230223105957929

這個頁面的功能也是很易懂的,我們在左側添加訂閱,右側的聊天框里會出現(xiàn)該topic的消息

image-20230223110105586

你是否還記得,在上面的設備代碼中,我們在loop中每一秒向 home/status/ 發(fā)送一條設備在線的提示,我們現(xiàn)在在這里就收到了。

當你看到這些消息的時候,就說明,你的設備、服務器、emqx控制臺已經(jīng)跑通了。

前后端以及數(shù)據(jù)庫

前端

前端不必多說,我們使用echarts承載展示數(shù)據(jù),由于體量較小,我們不使用任何框架,直接使用jq和echarts實現(xiàn),這里主要講前端怎么連接mqtt

首先引入mqtt庫

xml
復制代碼<script src="https://cdn.bootcdn.net/ajax/libs/mqtt/4.1.0/mqtt.min.js"></script>

然后設置連接參數(shù)

javascript復制代碼  const options = {
    cleantrue// true: 清除會話, false: 保留會話
    connectTimeout4000// 超時時間
    clientId'userClient_' + generateRandomString(),
    //前端客戶端很可能比較多,所以這里我們生成一個隨機的6位字母加數(shù)字作為clientId,以保證不會重復
    username'userClient',
    password'aa995231030',
  }
   function generateRandomString() {
    let result = '';
    let characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    let charactersLength = characters.length;
    for (let i = 0; i < 6; i++) {
      result += characters.charAt(Math.floor(Math.random() * charactersLength));
    }
    return result;
  }
 

連接

ini復制代碼  // const connectUrl = 'mqtt://larryblog.top/mqtt' 當然你可以使用mqtt協(xié)議,但是有可能會遇到 ssl 跨域的問題,如果你不使用 https 可以忽略這一項,直接使用mqtt即可
const connectUrl = 'wss://larryblog.top/mqtt' //注意,這里使用了nginx進行轉發(fā),后面會講
const client = mqtt.connect(connectUrl, options)

因為前端代碼不多,我這里直接貼了

html:

index.html

xml復制代碼<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="stylesheet/less" href="./style.less">
  <link rel="stylesheet" href="http://at.alicdn.com/t/c/font_3712319_bzaequy11dn.css">
  <script src="https://cdn.bootcdn.net/ajax/libs/less.js/4.1.3/less.js"></script>
  <title>wemos d1 test</title>
</head>

<body>
  <div class="app" id="app">
    <div id="deviceStatus">
      <span class="statusLight"></span>
      <span id="statusText">Loading device status</span>
      <!-- <span class="iconfont icon-xinxi"></span> -->
    </div>
    <div class="container">
      <div class="Temperature">
        <div id="echartsViewTemperature"></div>
        <span>Current temperature:</span>
        <span id="Temperature">loading...</span>
      </div>
      <div class="Humidity">
        <div id="echartsViewHumidity"></div>
        <span>Current humidity:</span>
        <span id="Humidity">loading...</span>
      </div>
    </div>
  </div>
</body>
<script src="./showTip.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.4/moment.min.js"></script>
<script src="https://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/mqtt/4.1.0/mqtt.min.js"></script>
<script src="https://cdn.staticfile.org/echarts/4.7.0/echarts.js"></script>
<script src="./echarts.js?v=1.0.0"></script>
<script src="./mqttController.js"></script>

</html>

mqttController.js

javascript復制代碼// const mqtt = require('mqtt')
$(document).ready(() => {
  // Welcome to request my open interface. When the device is not online, the latest 2000 pieces of data will be returned
  $.post("https://larryblog.top/api", {
    topic"getWemosDhtData",
    skip0
  },
    (data, textStatus, jqXHR) => {
      setData(data.res)
      // console.log("line:77 data==> ", data)
    },
  );
  // for (let i = 0; i <= 10; i++) {
  //   toast.showToast(1, "test")
  // }
  const options = {
    cleantrue// true: 清除會話, false: 保留會話
    connectTimeout4000// 超時時間
    // Authentication information
    clientId'userClient_' + generateRandomString(),
    username'userClient',
    password'aa995231030',
    // You are welcome to use my open mqtt broker(My server is weak but come on you). When connecting, remember to give yourself a personalized clientId to prevent being squeezed out
    // Topic rule:
    // baseName/deviceId/events
  }
  // 連接字符串, 通過協(xié)議指定使用的連接方式
  // ws 未加密 WebSocket 連接
  // wss 加密 WebSocket 連接
  // mqtt 未加密 TCP 連接
  // mqtts 加密 TCP 連接
  // wxs 微信小程序連接
  // alis 支付寶小程序連接
  let timer;
  let isShowTip = 1
  const connectUrl = 'wss://larryblog.top/mqtt'
  const client = mqtt.connect(connectUrl, options)
  client.on('connect', (error) => {
    console.log('已連接:', error)
    toast.showToast("Broker Connected")
    timer = setTimeout(onTimeout, 3500);
    // 訂閱主題
    client.subscribe('wemos/dht11'function (err{
      if (!err) {
        // 發(fā)布消息
        client.publish('testtopic''getDHTData')
      }
    })
    client.subscribe('home/status/')
    client.publish('testtopic''Hello mqtt')

  })
  client.on('reconnect', (error) => {
    console.log('正在重連:', error)
    toast.showToast(3"reconnecting...")
  })

  client.on('error', (error) => {
    console.log('連接失敗:', error)
    toast.showToast(2"connection failed")
  })
  client.on('message', (topic, message) => {
    // console.log('收到消息:', topic, message.toString())
    switch (topic) {
      case "wemos/dht11":
        const str = message.toString()
        const arr = str.split(", "); // 分割字符串
        const obj = Object.fromEntries(arr.map(s => s.split(": "))); // 轉化為對象

        document.getElementById("Temperature").innerHTML = obj.Temperature + " ℃"
        optionTemperature.xAxis.data.push(moment().format("MM-DD/HH:mm:ss"))
        optionTemperature.xAxis.data.length >= 100 && optionTemperature.xAxis.data.shift()
        optionTemperature.series[0].data.length >= 100 && optionTemperature.series[0].data.shift()
        optionTemperature.series[0].data.push(parseFloat(obj.Temperature))
        ChartTemperature.setOption(optionTemperature, true);

        document.getElementById("Humidity").innerHTML = obj.Humidity + " %RH"
        optionHumidity.xAxis.data.push(moment().format("MM-DD/HH:mm:ss"))
        optionHumidity.xAxis.data.length >= 100 && optionHumidity.xAxis.data.shift()
        optionHumidity.series[0].data.length >= 100 && optionHumidity.series[0].data.shift()
        optionHumidity.series[0].data.push(parseFloat(obj.Humidity))
        ChartHumidity.setOption(optionHumidity, true);
        break
      case "home/status/":
        $("#statusText").text("device online")
        deviceOnline()
        $(".statusLight").removeClass("off")
        $(".statusLight").addClass("on")
        clearTimeout(timer);
        timer = setTimeout(onTimeout, 3500);
        break

    }

  })

  function deviceOnline() {
    if (isShowTip) {
      toast.showToast(1"device online")
    }
    isShowTip = 0
  }

  function setData(data{
    // console.log("line:136 data==> ", data)
    for (let i = data.length - 1; i >= 0; i--) {
      let item = data[i]
      // console.log("line:138 item==> ", item)
      optionTemperature.series[0].data.push(item.temperature)
      optionHumidity.series[0].data.push(item.humidity)
      optionHumidity.xAxis.data.push(moment(item.updateDatetime).format("MM-DD/HH:mm:ss"))
      optionTemperature.xAxis.data.push(moment(item.updateDatetime).format("MM-DD/HH:mm:ss"))
    }
    ChartTemperature.setOption(optionTemperature);
    ChartHumidity.setOption(optionHumidity);
  }

  function onTimeout() {
    $("#statusText").text("device offline")
    toast.showToast(3"device offline")
    isShowTip = 1
    document.getElementById("Temperature").innerHTML = "No data"
    document.getElementById("Humidity").innerHTML = "No data"
    $(".statusLight").removeClass("on")
    $(".statusLight").addClass("off")
  }

  function generateRandomString() {
    let result = '';
    let characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    let charactersLength = characters.length;
    for (let i = 0; i < 6; i++) {
      result += characters.charAt(Math.floor(Math.random() * charactersLength));
    }
    return result;
  }
});

showTip.js 是我發(fā)布在npm上的一個包,如果有需要可以自行npm下載

style.less

css復制代碼* {
  padding0;
  margin0;
  color#fff;
}

.app {
  background#1b2028;
  width100vw;
  height100vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;

  #deviceStatus {
    display: flex;
    align-items: center;
    gap10px;
    padding20px;

    .statusLight {
      display: block;
      height10px;
      width10px;
      border-radius100px;
      background#b8b8b8;

      &.on {
        background#00a890;
      }

      &.off {
        background#b8b8b8;
      }
    }
  }

  .container {
    width100%;
    height0;
    flex1;
    display: flex;

    @media screen and (max-width768px) {
      flex-direction: column;
    }

    >div {
      flex1;
      height100%;
      text-align: center;

      #echartsViewTemperature,
      #echartsViewHumidity {
        width80%;
        height50%;
        margin10px auto;
        // background#eee;
      }
    }
  }
}

echarts.js 這個文件是我自己寫的,別學我這種命名方式,這是反例

yaml復制代碼let optionTemperature = null
let ChartTemperature = null
$(document).ready(() => {
  setTimeout(() => {
    // waiting
    ChartTemperature = echarts.init(document.getElementById('echartsViewTemperature'));
    ChartHumidity = echarts.init(document.getElementById('echartsViewHumidity'));
    // 指定圖表的配置項和數(shù)據(jù)
    optionTemperature = {
      textStyle: {
        color: '#fff'
      },
      tooltip: {
        trigger: 'axis',
        // transitionDuration: 0,
        backgroundColor: '#fff',
        textStyle: {
          color: "#333",
          align: "left"
        },
      },
      xAxis: {
        min: 0,
        data: [],
        boundaryGap: false,
        splitLine: {
          show: false
        },
        axisLine: {
          lineStyle: {
            color: '#fff'
          }
        }
      },
      yAxis: {
        splitLine: {
          show: false
        },
        axisTick: {
          show: false // 隱藏 y 軸的刻度線
        },
        axisLine: {
          show: false,
          lineStyle: {
            color: '#fff'
          }
        }
      },
      grid: {
        // 為了讓標尺和提示框在圖表外面,需要將圖表向外擴展一點
        left: '10%',
        right: '5%',
        bottom: '5%',
        top: '5%',
        containLabel: true,
      },
      series: [{
        // clipOverflow: false,
        name: '溫度',
        type: 'line',
        smooth: true,
        symbol: 'none',
        data: [],
        itemStyle: {
          color: '#00a890'
        },
        areaStyle: {
          color: {
            type: 'linear',
            x: 0,
            y: 0,
            x2: 0,
            y2: 1,
            colorStops: [{
              offset: 0,
              color: '#00a89066' // 0% 處的顏色
            }, {
              offset: 1,
              color: '#00a89000' // 100% 處的顏色
            }],
            global: false // 缺省為 false
          }
        },
        hoverAnimation: true,
        label: {
          show: false,
        },
        markLine: {
          symbol: ['none', 'none'],
          data: [
            {
              type: 'average',
              name: '平均值',
            },
          ],
        },
      }]
    };
    optionHumidity = {
      textStyle: {
        color: '#fff'
      },
      tooltip: {
        trigger: 'axis',
        backgroundColor: '#fff',
        textStyle: {
          color: "#333",
          align: "left"
        },
      },
      xAxis: {
        min: 0,
        data: [],
        boundaryGap: false,
        splitLine: {
          show: false
        },
        axisTick: {
          //x軸刻度相關設置
          alignWithLabel: true,
        },
        axisLine: {
          lineStyle: {
            color: '#fff'
          }
        }
      },
      yAxis: {
        splitLine: {
          show: false
        },
        axisTick: {
          show: false // 隱藏 y 軸的刻度線
        },
        axisLine: {
          show: false,
          lineStyle: {
            color: '#fff'
          }
        }
      },
      grid: {
        // 為了讓標尺和提示框在圖表外面,需要將圖表向外擴展一點
        left: '5%',
        right: '5%',
        bottom: '5%',
        top: '5%',
        containLabel: true,
      },
      // toolbox: {
      //   feature: {
      //     dataZoom: {},
      //     brush: {
      //       type: ['lineX', 'clear'],
      //     },
      //   },
      // },
      series: [{
        clipOverflow: false,
        name: '濕度',
        type: 'line',
        smooth: true,
        symbol: 'none',
        data: [],
        itemStyle: {
          color: '#ffa74b'
        },
        areaStyle: {
          color: {
            type: 'linear',
            x: 0,
            y: 0,
            x2: 0,
            y2: 1,
            colorStops: [{
              offset: 0,
              color: '#ffa74b66' // 0% 處的顏色
            }, {
              offset: 1,
              color: '#ffa74b00' // 100% 處的顏色
            }],
            global: false // 缺省為 false
          }
        },
        hoverAnimation: true,
        label: {
          show: false,
        },
        markLine: {
          symbol: ['none', 'none'],
          data: [
            {
              type: 'average',
              name: '平均值',
            },
          ],
        },
      }]
    };

    // 使用剛指定的配置項和數(shù)據(jù)顯示圖表。
    ChartTemperature.setOption(optionTemperature);
    ChartHumidity.setOption(optionHumidity);
  }, 100)
});

當你看到這里,你應該可以在你的前端頁面上展示你的板子發(fā)來的每一條消息了,但是還遠遠做不到首圖上那種密密麻麻的數(shù)據(jù),我并不是把頁面開了一天,而是使用了后端和數(shù)據(jù)庫存儲了一部分數(shù)據(jù)。

后端

后端我們分為了兩個部分,一個是nodejs的后端程序,一個是nginx代理,這里先講代理,因為上一步前端的連接需要走這個代理

nginx

如果你沒有使用https連接,那么可以不看本節(jié),直接使用未加密的mqtt協(xié)議,如果你有自己的域名,且申請了ssl證書,那么可以參考我的nginx配置,配置如下

ini復制代碼http {
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
include /etc/nginx/mime.types;
default_type application/octet-stream;

##
# SSL Settings
##
server {
listen 80;
server_name jshub.cn;
#將請求轉成https
rewrite ^(.*)$ https://$host$1 permanent;
}
server {
listen 443 ssl;
server_name jshub.cn;
location / {
root /larryzhu/web/release/toolbox;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
location /mqtt {
proxy_pass http://localhost:8083;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
# SSL 協(xié)議版本
ssl_protocols TLSv1.2;
# 證書
ssl_certificate /larryzhu/web/keys/9263126_jshub.cn.pem;
# 私鑰
ssl_certificate_key /larryzhu/web/keys/9263126_jshub.cn.key;
# ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
# ssl_ciphers AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256;

# 與False Start沒關系,默認此項開啟,此處減少抓包的干擾而關閉
# ssl_session_tickets off;

# return 200 "https ok \n";
}

注意這只是部分配置,切不可全部覆蓋你的配置。

如果你不會使用nginx,說明你無需配置 ssl ,直接使用 mqtt協(xié)議即可。

后端程序部分

這里以egg.js框架為例

首先需要下載egg.js的插件 egg-emqtt ,直接使用npm下載即可,詳細配置和啟用方法 參見 MQTT系列實踐二 在EGG中使用mqtt

上面教程的方法并不全面,可以下載我的示例,仿照著寫一下,因為內(nèi)容相對復雜,地址:gitee.com/zhu_yongbo/…

其中還包含了 mysql 數(shù)據(jù)庫的連接方法,內(nèi)有我服務器的地址、mysql開放端口,用戶名以及密碼,我服務器還剩不到十天到期,有緣人看到我的文章可以對我的服務器為所欲為,沒有什么重要數(shù)據(jù)。

mysql

mysql方面,只需要一個庫,一個表即可完成全部工作

image-20230223114608671

如圖所示,不復雜,仿照我的建庫即可

有一點,比較重要,因為mysql本身不適用于存儲量級太大的數(shù)據(jù),我們的數(shù)據(jù)重復的又比較多,可以考慮一下壓縮算法,或者添加一個事件(每次插入時檢查數(shù)據(jù)量是否超過一定值)。像我的板子大概正常累計運行了幾天的時間(每兩秒一條數(shù)據(jù)),到目前可以看到已經(jīng)累計了七十萬條數(shù)據(jù)了,如果不是因為我設置了插入事件,這個數(shù)據(jù)量已經(jīng)可以明顯影響查詢速度了。

可以仿照我的事件,語句如下:

sql復制代碼DELIMITER $$
CREATE TRIGGER delete_oldest_data
AFTER INSERT ON wemosd1_dht11
FOR EACH ROW
BEGIN
    -- 如果數(shù)據(jù)量超過43200(每兩秒插入一條,這是一天的量)條,調(diào)用存儲過程刪除最早的一條數(shù)據(jù)
    IF (SELECT COUNT(*) FROM wemosd1_dht11) > 43200 THEN
        CALL delete_oldest();
    END IF;
END$$
DELIMITER ;

-- 創(chuàng)建存儲過程
CREATE PROCEDURE delete_oldest()
BEGIN
    -- 刪除最早的一條數(shù)據(jù)
    delete from wemosd1_dht11 order by id asc limit 1
    
END$$
DELIMITER ;

BTW:這是chatGPT教我的,我只進行了一點小小的修改。

這樣做會刪除id比較小的數(shù)據(jù),然后就會導致,id會增長的越來越大,好處是可以看到一共累計了多少條數(shù)據(jù)。但是如果你不想讓id累計,那么可以選擇重建id,具體做法,建議你咨詢一下chatGPT

結語

至此,我們已經(jīng)完成了前端、后端、設備端三端連通。

我們整體梳理一下數(shù)據(jù)是怎么一步一步來到我們眼前的:

首先wemos d1開發(fā)板會在DHT11溫濕度傳感器上讀取溫濕度值,然后開發(fā)板把數(shù)據(jù)通過mqtt廣播給某topic,我們的前后端都訂閱了此topic,后端收到后,把處理過的數(shù)據(jù)存入mysql,前端直接使用echarts進行展示,當前端啟動時,還可以向后端程序查詢歷史數(shù)據(jù),比如前8000條數(shù)據(jù),之后的變化由在線的開發(fā)板提供,我們就得到了一個實時的,并且能看到歷史數(shù)據(jù)的溫濕度在線大屏。

如果你覺得牛逼,就給我點個贊吧。

作者:德萊厄斯 

鏈接:https://juejin.cn/post/7203180003471081531

Node 社群

    
    


我組建了一個氛圍特別好的 Node.js 社群,里面有很多 Node.js小伙伴,如果你對Node.js學習感興趣的話(后續(xù)有計劃也可以),我們可以一起進行Node.js相關的交流、學習、共建。下方加 考拉 好友回復「Node」即可。

   “分享、點贊、在看” 支持一下

瀏覽 2977
點贊
評論
收藏
分享

手機掃一掃分享

分享
舉報
評論
圖片
表情
推薦
點贊
評論
收藏
分享

手機掃一掃分享

分享
舉報

感谢您访问我们的网站,您可能还对以下资源感兴趣:

国产秋霞理论久久久电影-婷婷色九月综合激情丁香-欧美在线观看乱妇视频-精品国avA久久久久久久-国产乱码精品一区二区三区亚洲人-欧美熟妇一区二区三区蜜桃视频 强伦轩一区二区三区四区播放方式| 久久久偷拍视频| 69久久久久久久久久| 亚洲成人高清无码| 男女啪啪动态图| 国产嫩苞又嫩又紧AV在线| 男人天堂视频在线| 亚洲无码在线资源| 久久久少妇| 黄色大片网站| 99在线免费观看| 亚州精品人妻一二三区| 黄色视频网站在线免费观看| 丁香五月婷婷久久| 桃花岛tⅴ+亚洲品质| 日韩午夜无码| 亚洲色图一区二区| 色婷婷激情五月天| 亚洲天堂在线播放| 日本一级婬片A片AAA毛多多| 国产成人精品三级麻豆| 欧美八区| 熊猫成人网| 色欲影音| 色色色色综合| 亚洲无码中文视频| 免费无码国产在线| 国产乱码一区二区三区| 在线观看免费高清无码| 日本不卡一区二区三区四区| 久色性爱视频| 91人妻无码| 日韩中文字幕AV| 国产成人AV在线播放| 黄色网页在线免费观看| 日韩欧美手机在线| 综合偷拍| 少妇高潮在线| 日韩在线观看av| 免费日批网站| 91无码人妻东京热精品一区| 97色色视频| 国内无码精品| 夜夜嗨av无码一区二区三区| 按摩忍不住BD中文字幕| 国产天天操| 操逼视频大全| 一级a片免费看| www.狠狠撸| 成人特级毛片| 无码狠狠躁久久久久久久91| 国产A片免费观看| 日韩黄色三级| 老婆中文字幕乱码中文乱码| 激情五月天在线观看| 免费无码在线播放| 日韩欧美成人在线视频| 91香蕉网| 91大神在线看| 日本三级片网址| 水蜜桃成人网| 亚洲乱伦图| 国产大屌| 91插插插插| 色欲AV秘无码一区二区三区| www.俺去啦| 国产精品嫩草久久久久yw193 | 辽宁模特张雪馨视频最新| 免费看无码网站成人A片| 中文字幕毛片| 国产黄色自拍视频| 国产无码免费视频| 一本道无码在线观看| 毛片一区二区| 久精品视频| www.日韩精品| 国产色情性黄片Av网站| 99视频免费观看| www.婷婷| 亚洲久久久| 亚洲日韩精品中文字幕| 国精产品乱码一区一区三区四区| 波多野结衣性爱视频| 亚洲秘无码一区二区三区观看| 亚洲精品秘一区二区三区蜜桃久| 少妇一级| 免费一级黄色片| 2018天天操| 国产日逼视频| 亚洲成人福利在线| 亚洲精品成人片在线观看精品字幕 | 无码视频免费| 99精品在线观看| 空姐白洁| 1024黄| www.五月天婷婷| 亚洲免费观看高清视频| 五月天丁香成人| 中文字幕69| 天堂视频中文在线| 亚洲香蕉视频网站| 亚洲天堂三级片| 男女福利视频| 伊人婷婷大香蕉| 我爱大香蕉| 99九九热| 欧美亚洲动漫| 一级片在线视频| 91禁樱桃在线| 伊人91| 人人看人人射| 午夜精品秘一区二区三区| 精品人妻少妇| 99视频在线| 春色Av| 欧美色一级| 99A片| 粉嫩99国产精品久久久久久人妻| 亚洲色图15| 伊人大香蕉视频在线观看| 精品国产毛片| 亚洲爆乳无码一区二区三区| 日韩家庭乱伦| 欧美AAA视频| 最近最好的2019中文| 97无码人妻| 久色视频| 熟女人妻一区二区三区| www.黄色在线| 久久五月天综合| 天天日天天| 少妇搡BBBB搡BBB搡AA| 久久国产精品一区二区三区| 国产啊啊啊啊| av免费播放| 中文字字幕在线中文乱码| 97福利| 春色Av| 91九色麻豆| jizz在线视频| 香蕉视频日韩| 丝袜一区二区三区| 成人亚洲A片V一区二区三区蜜月| 成人色色网| 国产黄色自拍视频| 99精品六月婷婷综合在线| 伊人网在线免费视频| 亚洲电影免费观看| 丁香六月婷婷综合激情欧美| 亚洲天堂网在线视频| 国产成人精品123区免费视频| AV在线免费观看网站| 毛片在线视频| 强波多野结衣黑人| 51午夜福利| 亚洲阿v天堂| 又黄又爽的视频| aa免费视频| 一本一道无码| 伊人大香蕉网| 久久久婷婷五月亚洲国产精品| 黑人在线播放| 3d啪啪动漫| 岛国av免费看| 成人做爰黄A片免费看直播室动漫 中文字幕一区二区三区四虎在线 欧美熟妇精品一级A片视色 | 黑人粗大无码| 日本黄色直播| 日韩大片在线观看| 老鸭窝在线观看视频| 影音先锋成人网| 亚洲视频日韩在线观看| 色色在线观看| 夜色88V精品国产亚洲| 国产伊人影院| 91超碰在线免费观看| 三级片自拍| 日韩一及| AV资源在线免费观看| 在线视频一区二区三区四区| 人人爽人人爱| 无码免费播放| 亚洲视频456| 91区视频| 河南熟妇搡BBBB搡BBBB| 一区二区三区中文字幕| 亚洲第一视频| 亚洲一二区| 日韩亚洲在线视频| 高清无码视频18| 福利久久| 在线观看免费黄片| 欧美在线看片| 黄色无码视频在线观看| 97免费| 欧美后门菊门交| 欧美成人一区免费视频| Www.黄色| 毛片网站免费| 欧美一区视频| 天天射天天干| 日本天堂网在线观看| AV偷拍| 狼友精品| 中文字幕精品无码| 激情婷婷在线| 影音先锋男人你懂的| 浪潮在线观看完整版| 亚洲91黄色片| 久久久久亚洲精品| AAA无码| 日逼免费网站| 日韩精品一区二区三区四区蜜桃视频 | 成人AV免费在线观看| 91搞一搞| 美女被操面费网站| 中文在线字幕免费观| 亚洲欧美在线观看视频| 国产精品免费av在线| 在线免费看A| 国产精品国产三级国产专区53| 91人人人| 久久黄色视频| 大香蕉久久精品| 91久热| 伊人成色| 亚洲三区视频| 久久久黄色电影| 欧美丰满美乳XXⅩ高潮www| 影音先锋男人站| 亚洲色色视频| 综合精品7799| 久久久亚洲熟妇熟女| 黄网站在线观看| 精品国产一二三区| 亚洲国产精品二二三三区| 操逼网站免费看| 日本成人午夜福利| 国产AⅤ无码一区二区| 大黑人荫蒂BBBBBBBBB| 亚洲三级无码| 性爱A级视频| 伦理被部长侵犯HD中字| 18禁网站在线| 嫖中国站街老熟女HD| 黄色操逼大片| 久色网站| 人妻精品一区二区在线| 国产精品每日更新| 北条麻妃人妻中文无码| 北条麻妃在线观看香蕉| 亚洲日韩精品无码| 久操免费在线视频| 中文爱爱视频| 中文字幕无码影院| 丁香六月综合| 日本三级片免费观看| 好逼天天有| 抽插视频免费| 中文字幕系列| 日韩色逼| 91做爱| 特级毛片AAAAAA蜜桃| 日韩欧美中文字幕公布| 操欧美逼| 一本色道久久88综合无码| 欧美视频基地| 一起操在线观看| 黄色a片在线观看| 无码福利导航| 色中文字幕| 安微妇搡BBBB搡BBBB日| 69国产精品成人无码视频色| 色77777| 香蕉国产在线| 日韩中文字码无砖| 白浆四溢av| 欧美黄色免费在线观看| 三级成人无码| 中文字幕亚洲在线观看| 秋霞久久日| 国产日韩二区| 极品一线天小嫩嫩真紧| 国产中文字幕视频| 欧美一级电影| 色婷婷中文| 亚洲无码综合| 日日夜夜草| 蜜臀久久久| 婷婷五月花| 国产精品白浆| 探花视频在线观看| 日韩A片一级无码免费蜜桃| henhengan| 国产精品在线观看视频| 亚洲福利| 久久精品苍井空免费一区| 久久99久久99久久99人受| 欧美成人视频。| 国产欧美日韩在线播放| 亚洲成人日韩| 日韩操逼图| 五月天成人小说| 91嫖妓站街按摩店老熟女| 日韩AV成人无码久久电影| 久久一| 亚洲精品成人无码| 高清无码在线免费视频| 老熟女-ThePorn| 国产毛片欧美毛片高潮| 日本久热| 欧美亚洲视频| 91人妻人人澡人人爽人人DVD| 超碰A片| 91爱爱| 中文无码在线观看中文字幕av中文| 69黄色视频| 色五月视频在线| 日韩黄网| 99视频精品| 一区二区三区国产精品| 国产女人18毛片水18精| 亚洲在线高清视频| 精品国产久久久久久| 暖暖爱视频免费| 亚洲中文AV在线| 美女天天肏| 欧美最猛黑A片黑人猛交蜜桃视频| 四虎影成人精品A片| 欧美日本中文字幕| 欧美艹逼视频| 日欧无码| 五月天黄色视频| 亚洲ww国产a大作| 青青草在线播放| 日韩看片| 青青草视频免费在线观看| 久久久91| 日韩午夜精品| 午夜福利大片| 很很撸| 五月天色婷婷丁香| 桃色五月天| 成人无码小电影| 蜜臀AV一区二区| 无码免费在线视频| 色欲AV秘无码一区二区三区| jiujiuav| 黄色视频在线观看| 婷婷在线观看视频| 91在线视频免费播放| 天天视频色| 久久免费成人电影| 中文字幕无码Av在线看| 国产精品人妻无码一区牛牛影视| 岛国av在线播放| 国产一级做a爱免费视频| 国产一区二区三区无码| 国产AV综合网| 国产激情网| 国产夫妻露脸| 中文字幕第98页| henhengan| 在线天堂视频| 91大奶熟女| 少妇无码视频| 夜夜骚av.一区二区三区四区 | 最新中文字幕一区| 老婆被黑人杂交呻吟视频| 国产精品18进进出出17c| 免费的av网站| 日韩无码久| 日韩无| 人人人人人妻| 老司机av| 日韩中文无码电影| 伊人网在线视频观看| 91综合视频在线播放| 自拍偷拍第一页| 日产精品久久久一区二区| 色婷婷视频| 一二三四区视频| 先锋影音一区二区三区| 五月天激情性爱| 亚洲欧洲久久| 成人视频一区二区三区| 波多野结衣AV在线观看| 亚洲无码高清在线观看| 亚洲无码久久精品| 欧美日韩男女淫乱一区二区| 亚洲精品一区二区三区新线路| 亚洲AV色香蕉一区二区三区| 精品国产AⅤ麻豆| 免费一级A| 婷婷色婷婷| 欧美久久久久久久| 无码欧美| 久久久久无码精品国产91福利| 一道本无码免费视频| 欧美久草| 草草影院第一页YYCCC| 日韩视频第一页| www.91AV| 免费一级婬片AAA片毛片A级| 91丨PORNY丨丰满人妻网站 | 国产精品性爱| 中文字幕乱码无码人妻系列蜜桃| 色AV高清| Chinese搡老女人| 精品国产免费无码久久噜噜噜AV| 免费V片| 国产小精品| 亚洲在线视频| 国产精品911| av在线观看网站| 午夜国产在线观看| 国产精品美女毛片j酒店| 男人AV网| 天天骑夜夜操| 欧美一二三| 操逼A片| A片免费观看视频| 激情中文网| 欧美精品网站| 男人视频网| 强波多野结衣黑人| 特级黄色视频| 懂色av蜜臀av粉嫩av分| 在线观看免费黄片| 91精品电影| 日韩AV成人无码久久电影| 成人黄片免费看| 欧美性猛交XXXX乱大交HD| 亚洲91黄色片| 欧美亚洲天堂网| 色哟哟无码精品一区二区三区| 欧美亚洲综合手机在线| 精品成人A片久久久久久不卡三区 免费看成人A片无码照片88hⅤ | 日韩无码视频二区| 中文字幕日韩人妻在线| 日韩免费精品视频| 熟女人妻一区二区三区| 中文字幕第6页| 99久热在线精品| 日本高清视频网站网wwwwww| 性满足BBWBBWBBW| 99精品视频免费在线观看| 久色视频福利| 欧美日韩在线视频播放| 欧美日韩网| 欧美色成人免费在线视频| 亚洲av男人天堂| 波多野结衣无码一区| 国产无码AV| 国产精品欧美精品| A级免费视频| 午夜视频在线| 玩弄人妻少妇500系列视频| 影音先锋在线视频观看| 黄色片基地| 国产性生活| 国产伦精品一区二区三区视频女| 吴梦梦一区二区在线观看| 色噜噜av| 无码中文字幕在线视频| 91综合在线观看| 亚洲三级无码| 干屄网| 成人黄色录像| 成人女人18女人毛片| 午夜精品18视频国产17c| 大香蕉大香蕉网| 欧美精品一区二区三区蜜臀| 麻豆乱码国产一区二区三区| 国产女主播在线| 国产成人三级片在线观看| 亚洲激情小说| 大香蕉伊人在线网| 手机在线操B视频| 欧美美女日逼视频| 亚洲A片在线观看| 欧美黑人大吊| 免费无码国产在线怀| 亚洲成人av| 日韩AV免费在线| 五月天婷婷成人| 精品精品视频| 三级片无码| 天堂在线无码| 自拍啪啪| 青草香蕉视频| 亚洲欧美成人在线| 国产无码免费在线观看| 91成人久久| 成人水蜜桃| 精品无码一区二区三区四区| 亚洲熟女视频| 色天天干| 无码九九九| 99精品在线| 日本久久久| 日韩中文字幕免费在线观看| 人人操人人爱人人妻| 不卡无码中文字幕一区| 图片区视频区小说区| 天堂俺去俺来也www久久婷婷 | 欧一美一婬一伦一区二区三区| 搡BBBB推BBBB推BBBB| 国产综合AV| 一级a免一级a做免费线看内裤的注意事项 | 7799综合| 欧美va亚洲va| 男女啪网| 亚洲免费在线观看视频| 欧美成人午夜福利| 国产成人无码一区二区在线观看 | 国产精品美女毛片真酒店| 成人无码电影在线观看| 日本黄色电影在线播放| 午夜色色影院| 国产强伦轩免费视频在线| 91人妻网| 亚洲黄在线观看| 无码色网| 亚洲精品成人片在线观看精品字幕 | 五月丁香激情视频| 亚洲av毛片| 国产欧美日韩在线播放| 久久午夜成人电影| 俺来也操逼| 九色PORNY自拍视频| 中文字幕亚洲高清| 国产成人自拍偷拍视频| 亚洲成人动漫在线| 狠狠干高清成人二区三区| A片在线免费看| 九色PORNY蝌蚪自拍视频 | AV资源在线免费观看| 黄色成人视频网站| 午夜性爱福利| 无码福利电影| 91黄色在线观看| 成人无码精品亚洲| 成人性爱视频免费在线观看| 在线免费观看国产| 国产成人在线免费观看| 亭亭五月丁香| 一级片av| 99久久久无码国产精品性波多| 国产成人久久精品麻豆二区 | 99在线观看免费| 久久久久久99| 日本操逼视频| 亚洲精品在线视频| 日本精品在线视频| 裸体美女视频欧美18| 99er在线观看| av在线小说| 亚洲午夜无码精品专区| 亚洲天天干| 国产AV一级| 99精品无码| 国产熟女| 成人福利视频在线观看| 国产成人片在线观看| 九九色色| 国产91白浆四溢| 国产a级毛片| 五月开心婷婷| 大香蕉欧美视频| 中国无码视频| 老妇槡BBBB槡BBBB槡| 国产精品嫩草久久久久yw193 | 国产精品久久久久无码AV| 色天使av| 日韩不卡一区二区三区| 亚洲中文无码在线观看| 久久99精品国产.久久久久| 高清无码免费在线观看| 青青草原AV| 中文字幕人妻丰满熟妇| 99热在线观看免费| 51AV在线| 探花极品无套大学生| 婷婷五月伊人| 成人午夜小电影| 人人干人人操人人爽| 黄色视频免费在线观看| 91Av视频| 蜜桃Av噜噜一区二区| 五月婷中文字幕| 欧美日韩精品在线视频| 国产在线观看91| 囯产精品一区二区三区AV做线| 国产福利在线导航| 日韩极品视频在线| 精产国品一区二区| 最近中文字幕高清2019中文字幕| 免费黄色av| 操逼的网站| 在线观看你懂得| 国产在线高潮| 中文字幕在线网| 亚洲成人视频免费在线观看| 国产又爽又黄免费| 一插菊花网| 高清无码在线免费视频| 91精品少妇高潮一区二区三区不卡| 在线观看亚州| 开心激情播播网| 亚洲色在线播放| 国产精品天天AVJ精麻传媒| 18国产免费视频| 超碰AV在线| 亚洲无码AV电影| 日韩有码第一页| 色五婷婷| 免费做爱网站| 亚洲图片在线观看| 久久久夜夜夜| 亚洲无码www| 成人视频你懂的| 波多野结衣一二三区| 中文字幕在线乱| 爱爱免费不卡视频| 男女91视频| 一级片AV| 高H网站| 色婷婷7777| 日韩v亚洲| 起碰在线视频| 欧美激情伊人| 国产黄色片网站| 日韩大屌操| 五月丁香花婷婷| 国产三级国产三级国产普通话| 精品一区二区三区蜜桃臀www| 欧美亚洲综合手机在线| 五月天婷婷乱伦| 日韩中文字幕在线| 欧美乱伦视频| 91福利在线观看| 尤物在线播放| www黄片| 九九九国产| 亚洲视屏| 国产麻豆剧传媒精品国产AV| 亚洲精品电影| 亚洲一级黄色电影| 无码性爱视频| 91一区| 精品日韩| 国产精品在线观看| 99热精品在线| 亚洲无码三级片| 一区二区三区免费| 中文字幕精品在线免费视频观看视频| 91麻豆大奶巨乳一区白虎| 最近中文字幕高清2019中文字幕| 伊人婷婷色香综合| 无码一区二区北条| 狠狠爱av| 韩日不卡视频| 人妻熟女视频| 巨爆乳肉感一区二区三区视频| 国产成人精品亚洲男人的天堂| 亚洲无码午夜| 日韩激情视频在线观看| 性性性性性XXXXX| 日本高清免费视频| 成人网站大香蕉| 国产午夜精品一区二区三区嫩A| 日本免费色视频| 最近最经典中文MV字幕| 日韩色情在线| 成人A√| 日韩乱伦av| 操逼视频免费网站| jizz国产精品| 秋霞中文字幕| 国产高清一区二区| 91久久综合亚洲鲁鲁五月天| 91大片| 黄片网站免费| 五月天黄色视频| 无码人妻日韩精品一区二区三| 影音先锋中文字幕资源| 日韩一级性爱| 丰满的人妻一区二区10| 一级aa视频| 亚洲国产一| 91久久婷婷亚洲精品成人| 日韩码波多野结衣| 老司机视频在线视频18| 青榴社区| 婷婷色色婷婷| 国产—a毛—a毛A免费| 无码人妻一区二区三区| 一区免费视频| 一区二区三区四区在线| 日逼99| 日韩中文字幕成人| 中文资源在线√8| 日本黄色电影在线播放| 日韩色逼| 成人AV片导航| 亚洲人妻一区二区| 亚洲精品成a人在线观看| 欧美日韩国产高清| 日本无码嫩草一区二区| 日韩综合网| 亚洲精品国产精品乱码视99| 日屄在线观看| 欧洲AV片| 51妺嘿嘿午夜福利视频| 免费无码一级A片大黄在线观看 | 91农村站街老熟女露脸| 激情国产| 青青操B| 韩国无码精品| 亚洲中文字幕在线观看| 亚洲国产成人va| 揄拍成人国产精品视频| chinese高潮老女人| 日韩中文字幕网| 亚洲日韩欧美在线观看| 五月天久久婷婷| 国产免费成人视频| 久久久成人网| 亚洲三级网站在线观看| 色色网站视频| 国产精品V日韩精品V在线观看| 天天躁天干天干| 九九视频免费观看| 四虎高清无码| 激情五月天成人| 无码国产精品一区二区| 日韩精品久| 国产一区二区不卡视频| 欧美色色视频| 日本国产在线| 黄色AV免费看| 一级片A片| 中文字幕乱伦日本| 亚洲AV片一区二区三区| 成人无码网站在线观看| 欧美黄色片网站| 色婷婷在线无码精品秘人口传媒| 五月婷婷视频| 男女日皮视频| 久久艹艹| 99免费热视频| 久久综合操| 国产视频福利在线| 国产精品TV| 欧美日日| 精品乱子伦一区二区三区下载| 国产av网| 精品乱子伦一区二区三区在线播放 | 51亚洲精品| 中文字幕免费av| 日韩爆乳一区二区三区| 亚洲二级片| 天堂在线免费视频| 麻豆天美传媒AV果冻传媒| 亚洲成人情趣大香蕉| 亚洲色图自拍| 北条麻妃在线视频聊天| 操少妇| 国产777| 苍井空中文字幕在线观看| 黄色片成人| 一级a一级a爰片免费| 国精品伦一区一区三区有限公司 | 无码免费看| 三级理论片| 日韩在线国产| 麻豆激情视频| 水蜜桃视频免费观看| 日韩操逼网| 亚洲人成免费网站| 欧美AAAAAAAAAA特级| 日韩黄| 日韩精品一二三区| 欧美自拍视频| 欧美激情伊人| 成人无码91| 欧美肉大捧一进一出小说| 国内自拍视频在线观看| 约操少妇| 极品久久久久| 上海熟妇搡BBBB搡BBBB| 亚洲精品无码久久久| 人人摸人人搞| 国产AV一卡| 最近最经典中文MV字幕| 91成人在线免费视频| 免费无码成人片在线观看在线 | 中文字幕国产一区| 欧美三级美国一级| 亚洲91在线| 国产丝袜AV| 久久久久亚洲AV无码专区| 日韩高清无码网站| 黑人亚洲娇小videos∞| 男女啪啪免费视频| 精品久久久久久久久久| 欧美性爱香蕉视频| 9一区二区三区| 国产乱码一区二区三区的区别| 香蕉久久网| 天天色天天色| 91成人三级| 淫色综合网| 亚洲AⅤ无码一区二区波多野按摩 69国产成人综合久久精品欧美 | 亚洲XXXXX| 人人操人人干人人爽| 六月婷婷五月丁香| 92自拍视频| 各种BBwBBwBBwBBw| 骚逼综合| 强伦轩人妻一区二区三区最新版本更新内容 | 国产欧美在线免费观看| 在线看片av| 人妻精品一区二区| 91福利网| 亚洲综合片| 中文字幕高清无码在线观看| 国产人妻| 婷婷色情网| 自拍啪啪| 干欧美| 另类老妇性BBBWBBW| 久久久久久久9999| 毛片在线观看视频| 国产成人无码A片V99| 午夜AV福利影院| 国产亲子乱XXXXimim/| 亚洲综合一区二区| 一级做a爰片毛片A片| 四虎综合网| 午夜福利10000| 91福利在线观看| 先锋AV资源站| 又黄又爽无遮挡| 高清一区二区三区| 国产成人精品一区二区三区四区| 日本成人电影在线观看| 人妻AV在线| 在线国产黄色| 91porn在线观看| 日韩欧美操逼| 88海外华人免费一区| 91国在线| 黄色视频网站在线观看免费| 超碰自拍私拍二区三区区| 日韩免费片| 色老板最新网址| 免费中文视频| 性爱视频99| 亚洲无码电影网站| 亚洲成人少妇老妇a视频在线| 91社区成人影院| 天堂在线无码| 专业操美女视频网站| 久久久久久大香蕉| 在线少妇| 男女精品一区| 一区视频免费观看| 国产毛片基地| 国家一级A片| 国产乱伦一区| 欧美日韩国产高清| 特级毛片av| 国产内射精品| 暖暖爱视频免费| 操B视频在线| 2025av在线| 黄色一级网站| 99热99re6国产线播放| 国产一卡二卡在线观看| 91视频在线观看网| 91视频第一页| 97国产精品视频| 91av天堂| 91精品国产一区二区| 东京热这里只有精品| 成人视频黄片| 欧美性性生交XXXXX无码| 日韩99| 91无码一区二区三区| 熟女视频网站| 国产精品久久久久国产A级| 97色色婷婷| 无码人妻一区二区三区| 操逼视频91| 欧美色操| 午夜免费性爱视频| 久久天堂AV综合合色蜜桃网| 日韩A级毛片| 91精品91久久久中77777|