閑來無事-夏天防止花被渴死

来源:https://www.cnblogs.com/niexianda/archive/2023/06/08/17467341.html
-Advertisement-
Play Games

### 扯淡時間 前段時間,辦了一張流量卡。 有了新的手機號碼那就可以薅一波資本主義的羊毛了,所以我在京東上使用0.1大洋包郵的價格喜提了一個多肉,(在此之前我養過挺多的花,所有的都是忘了澆水被渴死了)此次痛並思痛,一定要讓我0.1大洋的的多肉看到明年的太陽。 ### 思路 > 養花幾乎不用管,只需 ...


扯淡時間

前段時間,辦了一張流量卡。 有了新的手機號碼那就可以薅一波資本主義的羊毛了,所以我在京東上使用0.1大洋包郵的價格喜提了一個多肉,(在此之前我養過挺多的花,所有的都是忘了澆水被渴死了)此次痛並思痛,一定要讓我0.1大洋的的多肉看到明年的太陽。

思路

養花幾乎不用管,只需要兩件事

  1. 充足的陽光:我現在住的距離太陽還是挺近的,陽光的事情不用擔心。
  2. 有營養的土和充足的水: 土比較好搞定,去小區的花壇裡面扣點就行了,主要就是水,經常想不起來去澆水,所以得搞一個能知道土壤濕度的東西,提醒我澆水。

我的思路如下

  1. 收集數據-首先手機花盆裡面的土壤濕度
  2. 存儲數據-將花盆的濕度進行持久化存儲
  3. 數據展示通知-頁面通過讀取持久化存儲的信息展示出來,並可以設定一個預警值,在一定的規則下麵通知到我

思路有了就開乾

收集數據

我的這個實現思路也算是傳說中的物聯網了,畢竟花盆都上網了嘛,現在實現這種的板子有很多,那個便宜來那個就行了。 經過百度一下,選擇了ESP8266-NodeMCU + 濕度感測器 作為數據收集方式(為啥選擇這個呢?因為便宜啊,還自帶wifi能上網。硬體真便宜,一共20塊搞定,還包郵),esp8266可以使用Arduino進行開發,語法跟c差不多,庫啥的自己搜搜吧,我也是自己搜的。俺看的是這個教程

收集數據主要是乾兩件事:1. 讀取濕度,2. 上報數據 代碼如下

#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <ArduinoJson.h>
// 讀取溫度
#define PIN_AO A0  //土壤感測器AO接ESP8266引腳A0
//#define PIN_DO 4  //濕度高於設定值時,DO輸出高電平,模塊提示燈亮

int M0 = 1024;  //在空氣中AO讀取的值最大為1024,代表乾燥時的讀數
int M1 = 164;   //浸泡在水裡的最小值 464(最小值會改變),代表100%濕度

float i=0;
float j=0;

#define SERVER_IP "http://區域網ip:3001/sendCurrentTemp"

const char* name = "名字";               //這裡改成你的設備當前環境下要連接的接入點名字
const char* password = "密碼";  //這裡改成你的設備當前環境下要連接的接入點密碼
// 上報時間間隔
int outTime = 1000 * 60 * 5;
// int outTime = 1000 * 60;
void setup() {
  // 設置引腳
  pinMode(PIN_AO, INPUT);

  Serial.begin(115200);  // 啟動串口通訊,波特率設置為115200

  Serial.println("未連接");


  Serial.println("開始連接");

  WiFi.begin(name, password);
  Serial.print("正在連接到");
  Serial.print(name);

  while (WiFi.status() != WL_CONNECTED)  //判定網路狀態
  {
    delay(500);

    Serial.print("網路連接成功");

    Serial.print("連接到的接入點名字:");

    Serial.println(name);  // 告知用戶建立的接入點WiFi名

    Serial.print("連接到的接入點密碼:");

    Serial.println(password);  // 顯示用戶建立的接入點WiFi密碼

    Serial.print("無線模式成功開啟,網路連接成功");
  }
  if (WiFi.status() == WL_CONNECTED) {
    Serial.print("無線IP地址為: ");
    Serial.println(WiFi.localIP());
  }
}

void loop() {
  // put your main code here, to run repeatedly:

  if (WiFi.status() == WL_CONNECTED) {
    http_post();
    WiFi.forceSleepBegin();  // Wifi Off
    delay(outTime);
    WiFi.forceSleepWake();  // Wifi On
  }
}



void http_post() {

  //創建 WiFiClient 實例化對象
  WiFiClient client;

  //創建http對象
  HTTPClient http;

  //配置請求地址
  http.begin(client, SERVER_IP);  //HTTP請求
  Serial.print("[HTTP] begin...\n");
  // 長度
  DynamicJsonDocument doc(96);

  float data = analogRead(PIN_AO);
  Serial.println(data);
  i = data / 1023;
  j = (1 - i) * 100;

  Serial.println(j);

  // 寫入當前溫度值
  doc["temp"] = j;
  String output;
  serializeJson(doc, output);

  //啟動連接併發送HTTP報頭和報文
  int httpCode = http.POST(output);
  Serial.print("[HTTP] POST...\n");

  //連接失敗時 httpCode時為負
  if (httpCode > 0) {
    //將伺服器響應頭列印到串口
    Serial.printf("[HTTP] POST... code: %d\n", httpCode);

    //將從伺服器獲取的數據列印到串口
    if (httpCode == HTTP_CODE_OK) {
      const String& payload = http.getString();
      Serial.println("received payload:\n<<");
      Serial.println(payload);
      Serial.println(">>");
    }
  } else {
    Serial.printf("[HTTP] POST... failed, error: %s\n", http.errorToString(httpCode).c_str());
  }
  //關閉http連接
  http.end();
}

存儲數據

數據存儲,得需要一個伺服器,我這裡正好一個衝動消費買的樹莓派,就讓他當伺服器吧(你要有個這個可以不用買esp8266了,直接開乾就完了) 我的思路,在樹莓派上起一個服務,讓esp8266可以通過http的協議上報溫度,數據展示也可以通過這個服務來獲取數據,當然了我還想外網訪問:我這裡用的是花生殼,搞了一個內網穿透,這個送1g流量,6塊還能給一個https的功能變數名稱,省了備案的事情。

  1. 服務簡單的來就使用nodejs啟動一個服務吧
  2. 資料庫我使用的SQLite,看人家說這個挺小的,還支持關聯查詢。 代碼如下
const http = require('http');
const os = require('os');
const urlInfo = require('url')
// 讀取資料庫
const querystring = require("querystring")
var sqlite3 = require('sqlite3').verbose()

// 要使用的埠號
const PORT_NUMBER = 3001


var db = new sqlite3.Database('./temp.db', sqlite3.OPEN_READWRITE, (err) => {
    if (err) {
        return console.log(err.message)
    }
    console.log('資料庫鏈接成功')
})

/** 追加信息 */
const appendTemp = (temp) => {
    return new Promise((resolve, reject) => {
        const currentTime = new Date().getTime()
        const addData = `INSERT INTO temp (time,temp)  VALUES(${currentTime},${temp})`
        db.run(addData, function (err, data) {
            if (err) {
                console.log(err)
                reject(err)
            }
            resolve()
        })
    })

}

const selectTemp = (paramInfo) => {
    return new Promise((resolve, reject) => {
        const { pageIndex = 1, pageSize = 10, all = false,startTimestamp,endTimestamp } = paramInfo
        let sqlStr = `select * from temp`
        if (startTimestamp && endTimestamp) {
            sqlStr = `select * from temp where time >= ${startTimestamp} and time <= ${endTimestamp}`
        }
        // 預設展示所有
        if (all === false) {
            sqlStr += ` limit(${(pageIndex - 1) * pageSize}),${pageSize}`
        }
        db.all(sqlStr, function (err, data) {
            if (err) {
                return console.log(err)
            }
            resolve(data)
        })
    })
}


// 2. 創建服務
// req(request):本次請求  res(response):本次響應     每次收到瀏覽器的請求,它就會執行一次回調
const server = http.createServer(function (req, res) {
    const { method, url } = req
    // 接收到請求數據
    if (method === 'POST' && url === '/sendCurrentTemp') {
        //創建空字元疊加數據片段
        var data = '';
        //2.註冊data事件接收數據(每當收到一段表單提交的數據,該方法會執行一次)
        req.on('data', (chunk) => {
            // chunk 預設是一個二進位數據,和 data 拼接會自動 toString
            data += chunk;
        })
        req.on('end', () => {
            try {
                console.log(data)
                const info = JSON.parse(data)
                appendTemp(info.temp)
                res.end('{status:200}');
            } catch (e) {
                console.error(e)
            }
        })
    }
    const { pathname, path } = urlInfo.parse(req.url)
    if (method === 'GET' && pathname === '/getTemp') {
        // 返回查詢的信息
        selectTemp(param2Obj(path)).then(list => {
            let retObj = {
                status: 200,
                list
            }
            res.end(JSON.stringify(retObj));
        })
    }

    // 獲取數據請求
});


// 3. 啟動服務
server.listen(PORT_NUMBER, function () {
    console.log(`伺服器啟動成功,請在http://${getIpAddress()}:${PORT_NUMBER}中訪問....`)
});


/** 獲取當前ip地址 */
function getIpAddress() {
    var ifaces = os.networkInterfaces()
    for (var dev in ifaces) {
        let iface = ifaces[dev]
        for (let i = 0; i < iface.length; i++) {
            let { family, address, internal } = iface[i]
            if (family === 'IPv4' && address !== '127.0.0.1' && !internal) {
                return address
            }
        }
    }
}


function param2Obj(url) {
    const search = url.split('?')[1]
    if (!search) {
        return {}
    }
    const paramObj = JSON.parse('{"' + (search).replace(/"/g, '\\"').replace(/&/g, '","').replace(/=/g, '":"') + '"}')
    Object.keys(paramObj).map(key => {
        paramObj[key] = decodeURIComponent(paramObj[key])
    })
    return paramObj
}

數據展示

使用vue寫一個頁面,去從服務拉取到數據

  1. 展示曲線
  2. 展示所有數據分頁展示(後期想加個圖片,每隔一段時間上傳一個圖片,搞一個延遲攝影的效果)

全部代碼地址

最後放一張效果圖吧


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 經過前幾篇文章的講解,初步瞭解ASP.NET Core MVC項目創建,啟動運行,以及命名約定,創建控制器,視圖,模型,接收參數,傳遞數據ViewData,ViewBag,路由,頁面佈局,wwwroot和客戶端庫,Razor語法,EnityFrameworkCore與資料庫,HttpContext,... ...
  • ## 前言 本人之前開發了一個叫[電子腦殼](https://github.com/maker-community/ElectronBot.DotNet)的上位機應用,給稚暉君[ElectronBot](https://github.com/peng-zhihui/ElectronBot)開源機器人 ...
  • # 安裝指定版本的mysql(安裝mysql5.7) ## 目標:解決需求,安裝mysql5.7 ## 前言: > 安裝軟體的三種方式: > > 1. rpm 安裝 > 2. 源代碼編譯安裝 > 3. yum倉庫安裝 > - 本地光碟 > - 阿裡雲yum源 > - 自建yum倉庫文件夾 > 4. ...
  • # 【Linux基礎】shell編程(一) 變數 [TOC] ## 什麼是shell編程 簡單的命令可以在命令行中直接輸入,但是複雜的命令需要寫在腳本里。例如一個簡單的shell腳本: ```shell #!/bin/bash #輸出一行 echo "Hello World!" ``` \#開始的行 ...
  • # 離線安裝rpm包以及自建yum倉庫 ## 離線安裝rpm - yum支持如下參數 > --downloadnoly 只下載不安裝 > > --downloaddir=directory 下載到指定目錄下 - 因此可以線上下載好需要的rpm包到建立的倉庫,後續離線安裝rpm包 > 創建存放rpm包 ...
  • # 伺服器Ubuntu Server 22.04安裝低版本MySQL5.7 最近在騰訊雲買了個伺服器準備部署我的Django項目,由於需要保證伺服器和本地開發的環境相同,所以要在Ubuntu 22.04安裝一個與本地Windows版本相同的MySQL5.7.36 ## 第一個問題 首次安裝我嘗試使用 ...
  • 我平時經常要看 PDF,但是我看書賊慢,一個 PDF 差不多幾十上百頁,看一遍要花挺長時間。 我記性還不好,看完之後,過些日子就記不清 PDF 是講什麼的了。為了找到 PDF 里的某些信息,又得再花時間。 不過,現在這些問題都不是問題了。 因為我最近發現了一個神器,1 分鐘就能讀完一個 PDF。 上 ...
  • ## 前言 本篇文章主要介紹的關於本人從剛工作到現在使用kafka的經驗,內容非常多,包含了kafka的常用命令,在生產環境中遇到的一些場景處理,kafka的一些web工具推薦等等。由於kafka這塊的記錄以及經驗是從我剛開始使用kafka,從2017年開始,可能裡面有些內容過時,請見諒。溫馨提醒, ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...