使用Wio Terminal通過HTTP請求獲取並展示傳感器實時數據

使用Wio Terminal讀取傳感器的實時數據是在用Seeeduino V4.2開發板接收數據並把PC端作爲服務端的基礎上進行的,Wio Terminal作爲客戶端,在同一局域網下就可以訪問傳感器通過串口傳送到服務器的數據。

下面是我在用Wio Terminal讀取傳感器實時數據的思路:

  1. 將Seeed-BME680獲取的數據用WEB前端展示
  2. 用正則表達式整理串口傳回的數據
  3. 使用Wio Terminal做HTTP請求並展示數據

在這裏插入圖片描述

1. 將Seeed-BME680獲取的數據用WEB前端展示

在前一篇文章裏,我講解了基於Django實現的網站:
使用Django搭建簡易數據中臺(基於Grove - Temperature Humidity Pressure Gas Sensor)

在這裏用Django可能會顯得有點heavy,我的想法是在後期做一個數據管理端,做些數據分析什麼的,所以這個Django項目我會繼續完善。

我之前做的前端是以字符串的形式展示的:
在這裏插入圖片描述
這樣其實不太好,如果這樣寫的話,Wio Terminal就要在做HTTP請求時把數據提取出來,導致刷新的時間過長

所以這裏要做一些改進,把數據改成Json串:

import json

def index(request):
    datas = getDatas()
    content = {
        'temperature':datas[0],
        'pressure':datas[1],
        'humidity':datas[2],
        'gas':datas[3],
        'updataTime':datas[4],
    }
    jsonData = json.dumps(content)
    return HttpResponse(jsonData)

在這裏插入圖片描述
這時前端的頁面如下所示:
在這裏插入圖片描述
這裏雖然是前端頁面,但其實不是給用戶去看的,這個頁面是供Wio Terminal做請求數據用的

2. 用正則表達式整理串口傳回的數據

在前面我們留了一個getDatas()的方法沒有講到,原來的getDatas()方法只是單純地獲取數據而已,並沒有對數據進行處理:

def getDatas():
    serialPort = "COM10"  # 串口
    baudRate = 9600  # 波特率
    ser = serial.Serial(serialPort, baudRate, timeout=0.5)
    # print("參數設置:串口=%s ,波特率=%d" % (serialPort, baudRate))
    while True:
        str = ser.readline().decode('utf-8')
        if str.strip()!='':
            print(str)
            updata_time = datetime.datetime.now().strftime('%F %T')
            print(updata_time)
            return str,updata_time

這裏的改進是通過正則表達式把數據提取出來:

import re
import serial
import datetime

def getDatas():
    serialPort = "COM10"  # 串口
    baudRate = 9600  # 波特率
    ser = serial.Serial(serialPort, baudRate, timeout=0.5)
    # print("參數設置:串口=%s ,波特率=%d" % (serialPort, baudRate))
    while True:
        lists = []
        strs = ser.readline().decode('utf-8')
        if strs.strip()!='':
            print(strs)
            str = re.finditer(r"(\d+).(\d+)",strs)
            for item in str:
                # print(item.group())
                lists.append(float(item.group()))

            updata_time = datetime.datetime.now().strftime('%F %T')
            # print(updata_time)
            lists.append(updata_time)
            print(lists)
            return lists

其實就是提取數據,把字符串裏的有效信息提取出來,先存到一個列表裏:
在這裏插入圖片描述
簡單講講我這裏用到的正則表達式:

re.finditer(r"(\d+).(\d+)",strs)

可以看到,想要提取的有效數據是帶小數點後兩位的數, \d 表示匹配任意數字,等價於0-9,但是小數點不是數字 ,所以不會被匹配,因此正則表達式可以寫成: “(\d+).(\d+)”

3. 使用Wio Terminal做HTTP請求並展示數據

前兩個部分是這一部分的基礎,這一部分主要是Arduino的知識

我在做的時候,主要考慮3點:

  1. 連接WIFI並做HTTP請求
  2. 獲取數據並在LCD屏上顯示
  3. 通過待機來省電

連接WIFI並做HTTP請求

我拿到的Wio Terminal是樣品,在使用無線連接前要在Wio Terminal上更新Wireless Core Realtek RTL8720的最新固件,大家拿到手裏的應該都是已經刷好了的。如果在上傳程序後,串口出現如下輸出:
在這裏插入圖片描述
這時可嘗試重新flash,具體方法請參考官方文檔:
https://wiki.seeedstudio.com/Wio-Terminal-Network-Overview/

這一步解決以後,連接WIFI應該不成問題,看官方文檔就能輕鬆解決,我這裏講一些比較特殊的部分。官方文檔給了一個HTTPS的請求demo:
在這裏插入圖片描述
因爲我現在只是在局域網內共享數據,因此用HTTP請求就行了。這裏需要用到一些網絡協議的知識,可以把HTTPS協議理解爲HTTP協議的升級,就是在HTTP的基礎上增加了數據加密,這個S即SSL。

有了這一基礎就好辦了,我們只需要在官方給的示例程序上稍作修改即可,先看看HTTPS請求:

#include <WiFiClientSecure.h>
 
const char* ssid     = "yourNetworkName";     // your network SSID
const char* password = "yourNetworkPassword"; // your network password
 
const char*  server = "www.example.com";  // Server URL
const char* test_root_ca = \
                            "-----BEGIN CERTIFICATE-----\n"
                            "MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh\n"
                            "MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\n"
                            "d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD\n"
                            "QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT\n"
                            "MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j\n"
                            "b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG\n"
                            "9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB\n"
                            "CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97\n"
                            "nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt\n"
                            "43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P\n"
                            "T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4\n"
                            "gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO\n"
                            "BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR\n"
                            "TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw\n"
                            "DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr\n"
                            "hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg\n"
                            "06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF\n"
                            "PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls\n"
                            "YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk\n"
                            "CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=\n"
                            "-----END CERTIFICATE-----\n";
 
// You can use x.509 client certificates if you want
//const char* test_client_key = "";   //to verify the client
//const char* test_client_cert = "";  //to verify the client
 
WiFiClientSecure client;
 
void setup() {
    //Initialize serial and wait for port to open:
    Serial.begin(115200);
    while(!Serial); // Wait for Serial to be ready
    delay(1000);
 
    Serial.print("Attempting to connect to SSID: ");
    Serial.println(ssid);
    WiFi.begin(ssid, password);
 
    // attempt to connect to Wifi network:
    while (WiFi.status() != WL_CONNECTED) {
        Serial.print(".");
        // wait 1 second for re-trying
        delay(1000);
    }
    Serial.print("Connected to ");
    Serial.println(ssid);
 
    client.setCACert(test_root_ca);
    //client.setCertificate(test_client_key); // for client verification
    //client.setPrivateKey(test_client_cert); // for client verification
 
    Serial.println("\nStarting connection to server...");
    if (!client.connect(server, 443)) {
        Serial.println("Connection failed!");
    } else {
        Serial.println("Connected to server!");
        // Make a HTTPS request:
        client.println("GET https://www.example.com HTTP/1.0");
        client.println("Host: www.example.com");
        client.println("Connection: close");
        client.println();
 
        while (client.connected()) {
            String line = client.readStringUntil('\n');
            if (line == "\r") {
                Serial.println("headers received");
                break;
            }
        }
        // if there are incoming bytes available
        // from the server, read them and print them:
        while (client.available()) {
            char c = client.read();
            if (c == '\n') {
                Serial.write('\r');
            }
            Serial.write(c);
        }
        client.stop();
    }
}
 
void loop() {
    // do nothing
}

WiFiClientSecure這個類是用來做HTTPS請求的,用setCACert(test_root_ca)進行認證

HTTP請求時改成WiFiClient即可,並且不需要認證,這時我們就可以把很多代碼都刪掉了:

#include <WiFiClientSecure.h>
 
const char* ssid     = "yourNetworkName";     // your network SSID
const char* password = "yourNetworkPassword"; // your network password
const char*  server = "Server URL";  // Server URL

 
WiFiClient client;
 
void setup() {
    //Initialize serial and wait for port to open:
    Serial.begin(115200);
    while(!Serial); // Wait for Serial to be ready
    delay(1000);
 
    Serial.print("Attempting to connect to SSID: ");
    Serial.println(ssid);
    WiFi.begin(ssid, password);
 
    // attempt to connect to Wifi network:
    while (WiFi.status() != WL_CONNECTED) {
        Serial.print(".");
        // wait 1 second for re-trying
        delay(1000);
    }
    Serial.print("Connected to ");
    Serial.println(ssid);
 
    Serial.println("\nStarting connection to server...");
    if (!client.connect(server, 9000)) {
        Serial.println("Connection failed!");
    } else {
        Serial.println("Connected to server!");
        
        // Make a HTTP request:
        String postRequest =(String)("GET ") + "/ HTTP/1.1\r\n" + "Connection: close\r\n\r\n";  
        Serial.println(postRequest);  
        client.print(postRequest);
 
        while (client.connected()) {
            String line = client.readStringUntil('\n');
            if (line == "\r") {
                Serial.println("headers received");
                break;
            }
        }
        // if there are incoming bytes available
        // from the server, read them and print them:
        while (client.available()) {
            char c = client.read();
            if (c == '\n') {
                Serial.write('\r');
            }
            Serial.write(c);
        }
        client.stop();
    }
}
 
void loop() {
    // do nothing
}

這裏需要注意的是client.connect(server, 9000),9000是我的端口,這裏要選擇正確的端口,否則是不能請求成功的;另外,我把PC端當作服務器,所以Server URL就是我的IP地址。

發送HTTP請求,最重要的是這部分:

// Make a HTTP request:
String postRequest =(String)("GET ") + "/ HTTP/1.1\r\n" + "Connection: close\r\n\r\n";  
Serial.println(postRequest);  
client.print(postRequest);

如果請求成功,終端會給出提示:
在這裏插入圖片描述
這個請求怎麼寫呢?這裏其實有個好方法,想用瀏覽器訪問,然後回終端看瀏覽器是怎麼請求的,我們再照貓畫虎即可。

獲取數據並在LCD屏上顯示

讀取數據主要用到ArduinoJson這個庫:

//ArduinoJson to parse data, plesae check ArduinoJson for more info
    const size_t capacity = JSON_OBJECT_SIZE(5) + 100;
    DynamicJsonDocument doc(capacity);
    deserializeJson(doc, data);
 
    float temperature = doc["temperature"];
    float pressure = doc["pressure"];
    float humidity = doc["humidity"];
    float gas = doc["gas"];
    String updataTime = doc["updataTime"];

在這裏插入圖片描述
把五個數據都讀取出來,顯示就很簡單了。因爲Wio Terminal的屏幕很小,所以我分了兩頁存放,原理都是相同的:

// -----------------LCD---------------------
    tft.setFreeFont(FF17);
    tft.setTextColor(tft.color565(224,225,232));
    tft.drawString("Current Data At Home",20,10);
 
    tft.fillRoundRect(10, 45, 300, 55, 5, tft.color565(40,40,86));
    tft.fillRoundRect(10, 105, 300, 55, 5, tft.color565(40,40,86));
    tft.fillRoundRect(10, 165, 300, 55, 5, tft.color565(40,40,86));
 
    tft.setFreeFont(FM9);
    tft.drawString("temperature:", 75, 50);
    tft.drawString("pressure:",75, 110);
    tft.drawString("humidity:",75, 170);
 
    tft.setFreeFont(FMB12);
    tft.setTextColor(TFT_RED);
    tft.drawFloat(temperature,2 , 140, 75);
    tft.setTextColor(tft.color565(224,225,232));
    tft.drawFloat(pressure,2 , 140, 135);
    tft.setTextColor(TFT_GREEN);
    tft.drawFloat(humidity,2 , 140, 195);

    tft.drawString("℃", 210, 75);
    tft.drawString("KPa",210, 135);
    tft.drawString("%",210, 195);

文字的擺放位置我是慢慢試出來的,這裏要有耐心:
在這裏插入圖片描述

通過待機來省電

當然,在實際應用上,如果一直亮着屏幕,是很浪費電的,因此我結合加速度傳感器,設置了一個待機狀態,晃動Wio Terminal時才亮屏。

這塊其實也很好做,在loop()裏做就好了:

void loop()
{
    float x_raw = lis.getAccelerationX();
    float y_raw = lis.getAccelerationY();
    float z_raw = lis.getAccelerationZ();
    if (abs(accelerator_readings[0] - x_raw) >= 0.1 && abs(accelerator_readings[1] - y_raw) >= 0.1 && abs(accelerator_readings[2] - z_raw) >= 0.1){
      // Turning on the LCD backlight
      digitalWrite(LCD_BACKLIGHT, HIGH);
      getFirstData();
      delay(3000);
      getLastData();
      delay(3000);
    }
    else {
      // Turning off the LCD backlight
      digitalWrite(LCD_BACKLIGHT, LOW);
      delay(500);
      }
      
    for (uint8_t i = 0; i<3; i++){
        accelerator_readings[i] = 0.0; //this is used to remove the first read variable
      }
    
    accelerator_readings[0] = x_raw; //store x-axis readings
    accelerator_readings[1] = y_raw; //store y-axis readings
    accelerator_readings[2] = z_raw; //store z-axis readings
}

Wio Terminal自帶三個加速度傳感器,效果還是很理想的。

完整的代碼

#include <WiFiClientSecure.h>
#include <ArduinoJson.h>
#include"LIS3DHTR.h"
#include"Free_Fonts.h"
#include"TFT_eSPI.h"

TFT_eSPI tft;
LIS3DHTR<TwoWire> lis;
WiFiClient client;

const char* ssid     = "zh213";
const char* password = "zh213wei";
const char*  server = "192.168.1.102";  // Server URL
String data;
float accelerator_readings[3];

 
void setup() {
    //Initialize serial and wait for port to open:
    Serial.begin(115200);
    delay(100);
 
    pinMode(WIO_5S_PRESS, INPUT_PULLUP);
    lis.begin(Wire1);
    lis.setOutputDataRate(LIS3DHTR_DATARATE_25HZ);
    lis.setFullScaleRange(LIS3DHTR_RANGE_2G);

    float x_raw = lis.getAccelerationX();
    float y_raw = lis.getAccelerationY();
    float z_raw = lis.getAccelerationZ();
    accelerator_readings[0] = x_raw; //store x-axis readings
    accelerator_readings[1] = y_raw; //store y-axis readings
    accelerator_readings[2] = z_raw; //store z-axis readings
 
    Serial.print("Attempting to connect to SSID: ");
    Serial.println(ssid);
    WiFi.begin(ssid, password);
 
    tft.begin();
    tft.setRotation(3);
    tft.fillScreen(TFT_BLACK);
    tft.setFreeFont(FMB12);
    tft.setCursor((320 - tft.textWidth("Connecting to Wi-Fi.."))/2, 120);
    tft.print("Connecting to Wi-Fi..");
 
    // attempt to connect to Wifi network:
    while (WiFi.status() != WL_CONNECTED) {
        Serial.print(".");
        // wait 1 second for re-trying
        delay(1000);
    }
 
    Serial.print("Connected to ");
    Serial.println(ssid);
 
    tft.fillScreen(TFT_BLACK);
    tft.setCursor((320 - tft.textWidth("Connected!"))/2, 120);
    tft.print("Connected!");
 
    getFirstData();
}
 
void loop()
{
    float x_raw = lis.getAccelerationX();
    float y_raw = lis.getAccelerationY();
    float z_raw = lis.getAccelerationZ();
    if (abs(accelerator_readings[0] - x_raw) >= 0.1 && abs(accelerator_readings[1] - y_raw) >= 0.1 && abs(accelerator_readings[2] - z_raw) >= 0.1){
      // Turning on the LCD backlight
      digitalWrite(LCD_BACKLIGHT, HIGH);
      getFirstData();
      delay(3000);
      getLastData();
      delay(3000);
    }
    else {
      // Turning off the LCD backlight
      digitalWrite(LCD_BACKLIGHT, LOW);
      delay(500);
      }
      
    for (uint8_t i = 0; i<3; i++){
        accelerator_readings[i] = 0.0; //this is used to remove the first read variable
      }
    
    accelerator_readings[0] = x_raw; //store x-axis readings
    accelerator_readings[1] = y_raw; //store y-axis readings
    accelerator_readings[2] = z_raw; //store z-axis readings
}
 
void getFirstData() {
    Serial.println("\nStarting connection to server...");
    if (!client.connect(server, 9000)) {
        Serial.println("Connection failed!");
        tft.fillScreen(TFT_BLACK);
        tft.setCursor((320 - tft.textWidth("Connection failed!"))/2, 120);
        tft.print("Connection failed!");
    } else {
        Serial.println("Connected to server!");
 
        // Make a HTTP request:
        String postRequest =(String)("GET ") + "/ HTTP/1.1\r\n" + "Connection: close\r\n\r\n";  
        Serial.println(postRequest);  
        client.print(postRequest);

        while (client.connected()) {
            String line = client.readStringUntil('\n');
            if (line == "\r") {
                Serial.println("headers received");
                break;
            }
        }
 
        while(client.available())
        {
          String line = client.readStringUntil('\r');
          data = line;
        }
        Serial.println(data);
        client.stop();
        Serial.println("closing connection");
    }
 
    //ArduinoJson to parse data, plesae check ArduinoJson for more info
    const size_t capacity = JSON_OBJECT_SIZE(5) + 100;
    DynamicJsonDocument doc(capacity);
    deserializeJson(doc, data);
 
    float temperature = doc["temperature"];
    float pressure = doc["pressure"];
    float humidity = doc["humidity"];
 
// -----------------LCD---------------------
    tft.setFreeFont(FF17);
    tft.setTextColor(tft.color565(224,225,232));
    tft.drawString("Current Data At Home",20,10);
 
    tft.fillRoundRect(10, 45, 300, 55, 5, tft.color565(40,40,86));
    tft.fillRoundRect(10, 105, 300, 55, 5, tft.color565(40,40,86));
    tft.fillRoundRect(10, 165, 300, 55, 5, tft.color565(40,40,86));
 
    tft.setFreeFont(FM9);
    tft.drawString("temperature:", 75, 50);
    tft.drawString("pressure:",75, 110);
    tft.drawString("humidity:",75, 170);
 
    tft.setFreeFont(FMB12);
    tft.setTextColor(TFT_RED);
    tft.drawFloat(temperature,2 , 140, 75);
    tft.setTextColor(tft.color565(224,225,232));
    tft.drawFloat(pressure,2 , 140, 135);
    tft.setTextColor(TFT_GREEN);
    tft.drawFloat(humidity,2 , 140, 195);

    tft.drawString("℃", 210, 75);
    tft.drawString("KPa",210, 135);
    tft.drawString("%",210, 195);
}

void getLastData() {
    Serial.println("\nStarting connection to server...");
    if (!client.connect(server, 9000)) {
        Serial.println("Connection failed!");
        tft.fillScreen(TFT_BLACK);
        tft.setCursor((320 - tft.textWidth("Connection failed!"))/2, 120);
        tft.print("Connection failed!");
    } else {
        Serial.println("Connected to server!");

        // Make a HTTP request:
        String postRequest =(String)("GET ") + "/ HTTP/1.1\r\n" + "Connection: close\r\n\r\n";  
        Serial.println(postRequest);  
        client.print(postRequest);

        while (client.connected()) {
            String line = client.readStringUntil('\n');
            if (line == "\r") {
                Serial.println("headers received");
                break;
            }
        }
 
        while(client.available())
        {
          String line = client.readStringUntil('\r');
          data = line;
        }
        Serial.println(data);
        client.stop();
        Serial.println("closing connection");
    }
 
    //ArduinoJson to parse data, plesae check ArduinoJson for more info
    const size_t capacity = JSON_OBJECT_SIZE(5) + 100;
    DynamicJsonDocument doc(capacity);
    deserializeJson(doc, data);

    float humidity = doc["humidity"];
    float gas = doc["gas"];
    String updataTime = doc["updataTime"];
 
// -----------------LCD---------------------
    tft.setFreeFont(FF17);
    tft.setTextColor(tft.color565(224,225,232));
    tft.drawString("Current Data At Home",20,10);
 
    tft.fillRoundRect(10, 45, 300, 55, 5, tft.color565(40,40,86));
    tft.fillRoundRect(10, 105, 300, 55, 5, tft.color565(40,40,86));
    tft.fillRoundRect(10, 165, 300, 55, 5, tft.color565(40,40,86));
 
    tft.setFreeFont(FM9);
    tft.drawString("humidity:", 75, 50);
    tft.drawString("gas:",75, 110);
    tft.drawString("updataTime:",75, 170);
 
    tft.setFreeFont(FMB12);
    tft.setTextColor(TFT_RED);
    tft.drawFloat(humidity,2 , 140, 75);
    tft.setTextColor(tft.color565(224,225,232));
    tft.drawFloat(gas,2 , 140, 135);
    tft.setTextColor(TFT_GREEN);
    tft.drawString(updataTime , 30, 195);

    tft.drawString("%", 210, 75);
    tft.drawString("Kohms",210, 135);
}

用一個視頻展示一下效果:

簡易智能家居中控系統(獲取傳感器實時數據)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章