使用Go從樹莓派讀取溫度傳感器數(shù)據(jù)


在本教程中,我將向您展示如何用樹莓派讀取傳感器的溫度,并使用Go構(gòu)建一個(gè)HTTP服務(wù)來存儲(chǔ)數(shù)據(jù)。
您將學(xué)到如下內(nèi)容:

  • 如何從傳感器獲取溫度
  • 如何發(fā)送JSON數(shù)據(jù)
  • 構(gòu)建一個(gè)API服務(wù)來接收溫度數(shù)據(jù)
  • 將數(shù)據(jù)存儲(chǔ)在SQLite數(shù)據(jù)庫中
    我們會(huì)用Go來實(shí)現(xiàn)。實(shí)現(xiàn)需要的硬件設(shè)備:
  • 樹莓派一個(gè)
  • AM2302傳感器
  • Linux主機(jī)

開發(fā)此功能的目的

本教程的目的是讓您深入了解物聯(lián)網(wǎng)及其工作原理。您也可以使用云服務(wù)實(shí)現(xiàn),例如:

  • AWS IoT
  • Azure IoT Edge
  • Google cloud IoT Core
    這些云服務(wù)都很好。如果你正在構(gòu)建一個(gè)真正的項(xiàng)目或從事物聯(lián)網(wǎng)專業(yè)工作,使用這些云服務(wù)是對(duì)的。他們提供優(yōu)秀的安全服務(wù),為您處理很多的事情。

云服務(wù)雖然很好,但如果你想真正學(xué)習(xí)物聯(lián)網(wǎng),你需要從細(xì)節(jié)入手。云服務(wù)需要配置客戶端,把你的數(shù)據(jù)推上去,它為你提供可視化。在本教程中,我們將自己構(gòu)建所有這些東西并理解它。

連接溫度傳感器

溫濕度傳感器

傳感器有三根線需要連接到樹莓派的GPIO,如上所示。有一根紅色的電線連到樹莓派1腳。黑色導(dǎo)線接地并連接到第6腳。橙色(有時(shí)是黃色或白色)是數(shù)據(jù)線,接到第11引腳。連接非常簡單,如果你需要額外的幫助,這里有一個(gè)很好的連接AM2302的指南:https://learn.adafruit.com/dht/connecting-to-a-dhtxx-sensor?ref=hackernoon.com

讀取溫度傳感器

我們將在樹莓派上創(chuàng)建一個(gè)Go文件來讀取傳感器的溫濕度。我把它命名為readsensor.go。你需要在樹莓派上安裝Go來運(yùn)行它。接下來,需要安裝Go-DHT庫。

go get github.com/MichaelS11/go-dht

這是我喜歡使用的一個(gè)庫,因?yàn)樗浅:唵?,并且?duì)我來說非常可靠。

首先,我們驗(yàn)證一下傳感器的功能。讓我們從頭開始構(gòu)建文件。在文件頭中添加以下內(nèi)容:

package main

import (    

    "fmt"

    "github.com/MichaelS11/go-dht"
)

這將引入Go-DHT包和fmt來格式化輸出。接下來,我將創(chuàng)建一個(gè)常量來設(shè)置GPIO.Pin引腳11是GPIO 17(傳感器數(shù)據(jù)來源引腳)。如果你喜歡,你可以使用其他的GPIO引腳。

const GPIO = "GPIO17"

接下來,我們需要對(duì)溫度傳感器做三件事:

  • 初始化GPIO主機(jī)
  • 創(chuàng)建一個(gè)新的DHT reader
  • 讀取數(shù)據(jù)
    因此在main函數(shù)中,需初始化:
    hosterr := dht.HostInit()
    if hosterr != nil {
        fmt.Println("HostInit error:", hosterr)
        return
    }

創(chuàng)建新的DHT reader:

    dht, dhterr := dht.NewDHT(GPIO, dht.Fahrenheit, "")
    if dhterr != nil {   
        fmt.Println("NewDHT error:", dhterr)
        return
    }

注意,我使用的是上面const中設(shè)置的GPIO,調(diào)用NewDHT并將參數(shù)設(shè)置為Fahrenheit。你可以選擇攝氏溫度。最后,我們將讀取傳感器并輸出結(jié)果。

    humidity, temperature, readerr := dht.Read()
    if readerr != nil {
        fmt.Println("Reader error:", readerr)
        return
    }

很好,現(xiàn)在從傳感器中讀溫濕度數(shù)據(jù)就實(shí)現(xiàn)了。接下來讓我們準(zhǔn)備將數(shù)據(jù)發(fā)送到HTTP服務(wù)。

發(fā)送JSON格式數(shù)據(jù)

我們要在import中添加一些包:

import (
    "bytes"
    "encoding/json"
    "fmt"
    "net/http"
    "time"
    "github.com/MichaelS11/go-dht"
    )

引入這些包能將數(shù)據(jù)發(fā)送到HTTP服務(wù)上去。接下來,我們需要為數(shù)據(jù)創(chuàng)建一個(gè)結(jié)構(gòu)體,是我們將發(fā)送到服務(wù)端的數(shù)據(jù)模型。

type reading struct {
    TimeStamp   string
    Temperature float64
    Humidity    float64
}

這是我們要發(fā)送的數(shù)據(jù)。我們將在這里為服務(wù)添加另一個(gè)常量。這將是您服務(wù)器的URL。

const Endpoint = "http://[YOUR DOMAIN or IP]:5000/reading"

在上面的結(jié)構(gòu)中,我們有一個(gè)時(shí)間戳。將在應(yīng)用程序的頂部創(chuàng)建它:

timeStamp := time.Now()

接下來,在dht.read函數(shù)后面添加以下代碼:

newReading := reading{TimeStamp: timeStamp.Format("2006-01-02T15:04:05-0700"),
 Temperature: temperature, Humidity: humidity}

根據(jù)讀取到的傳感器數(shù)據(jù)創(chuàng)建reading結(jié)構(gòu)體實(shí)例。如果你還想將讀取的數(shù)據(jù)輸出到控制臺(tái),你可以添加以下內(nèi)容:

fmt.Printf("Our Reading was: \nTemperature: %v\nHumidity:%v\n", temperature, humidity)

現(xiàn)在我們創(chuàng)建http請(qǐng)求內(nèi)容:

var requestBody, reqerr = json.Marshal(newReading)

if reqerr != nil {
   fmt.Println("Request error:", readerr)
   return}

然后將該請(qǐng)求內(nèi)容以POST發(fā)送到我們的服務(wù)端。

resp, resperror := http.Post(Endpoint, "application/json", bytes.NewBuffer(requestBody))
    if resperror != nil {
        fmt.Println("Response error:", resperror)
        return    }

最后,我們將創(chuàng)建一個(gè)defer來關(guān)閉請(qǐng)求:

defer resp.Body.Close()

ok!現(xiàn)在我們有了一個(gè)實(shí)際的應(yīng)用程序來讀取傳感器并將值發(fā)送到服務(wù)端,接下來需要構(gòu)建服務(wù)端代碼。源代碼庫地址:https://github.com/JeremyMorgan/GoTempSensor/blob/master/readsensor.go?ref=hackernoon.com

創(chuàng)建API服務(wù)端

因此,對(duì)于服務(wù)端設(shè)置,您幾乎可以使用任何Linux或Windows虛擬機(jī)。我使用AWS Lightsail FreeBSD機(jī)器部署服務(wù)端。您需要在機(jī)器上安裝Go,除非您想編譯可執(zhí)行文件發(fā)送到服務(wù)器上直接執(zhí)行。

如果您使用的是LightSail,請(qǐng)確保打開端口5000。這個(gè)端口需要在您使用的任何防火墻或服務(wù)上打開。我們的服務(wù)端提供以下功能:

  • 提供一個(gè)API來接收從樹莓派上發(fā)送的POST請(qǐng)求
  • 將結(jié)果存儲(chǔ)在SQLlite數(shù)據(jù)庫中
  • 檢索最近的10條傳感器數(shù)據(jù)

配制包

我們需要安裝幾個(gè)Go包:

go get github.com/gin-gonic/gin
go get github.com/mattn/go-sqlite3

然后,創(chuàng)建一個(gè)名為reader.go的文件,你想叫它什么都行。

package main

import (
    "database/sql"
    "fmt"    "net/http"
    "github.com/gin-gonic/gin"
    _ "github.com/mattn/go-sqlite3")

我們將使用database/sql庫與我們的SQLlite數(shù)據(jù)庫交互。對(duì)于大型應(yīng)用程序,您可能希望使用規(guī)模更大的數(shù)據(jù)庫服務(wù)。但是為了學(xué)習(xí)和實(shí)驗(yàn),SQLlite就很好。然后我們將使用fmt標(biāo)準(zhǔn)庫來打印我們的錯(cuò)誤消息,

Gin將處理我們的API調(diào)用,然后go-sqllite3庫將處理與我們的SQLite數(shù)據(jù)庫的交互。同樣,我們?yōu)樽x取post請(qǐng)求body創(chuàng)建一個(gè)類似的結(jié)構(gòu):

type Reading struct {
    TimeStamp   string
    Temperature float64
    Humidity    float64
}

然后下面為db創(chuàng)建一個(gè)實(shí)例:

var db *sql.DB

接下來,讓我們創(chuàng)建一個(gè)“Check”函數(shù)來檢查和報(bào)告錯(cuò)誤。它看起來是這樣的:

func Check(e error) {
    if e != nil {
            panic(e)
    }
}

設(shè)置數(shù)據(jù)庫

接下來,我們將創(chuàng)建一個(gè)init函數(shù),這樣我們就可以在啟動(dòng)時(shí)初始化并連接到數(shù)據(jù)庫。

func init() {
    db, _ = sql.Open("sqlite3", "./readings.db")
    statement, prepError := db.Prepare("CREATE TABLE IF NOT EXISTS reading (TimeStamp TEXT, Temperature NUMERIC, Humidity NUMERIC)")
Check(prepError)
statement.Exec()
}

以上代碼會(huì)連接sqlite數(shù)據(jù)庫。我們通過sql.Open函數(shù)來連接數(shù)據(jù)庫,參數(shù)sqlite3指定使用的數(shù)據(jù)庫類型,第二個(gè)參數(shù)指定數(shù)據(jù)庫存儲(chǔ)文件。如果數(shù)據(jù)庫存儲(chǔ)文件不存在將創(chuàng)建新的,最后檢查錯(cuò)誤信息。

接下來,我們有一個(gè)預(yù)備聲明。表示如果這個(gè)數(shù)據(jù)表不存在,我們就創(chuàng)建它。注意,我們添加了時(shí)間戳、溫度和濕度。然后,我們?cè)俅握{(diào)用check以確保沒有任何錯(cuò)誤。調(diào)用statement.Exec()執(zhí)行我們的SQL查詢。

存儲(chǔ)數(shù)據(jù)

現(xiàn)在我們需要設(shè)置將數(shù)據(jù)存儲(chǔ)到數(shù)據(jù)庫中的方法。這很容易做到,并且比您想象的代碼要少。首先,讓我們創(chuàng)建一個(gè)函數(shù)來保存?zhèn)鞲衅鲾?shù)據(jù)到數(shù)據(jù)庫:

func saveToDatabase(TimeStamp string, Temperature float64, Humidity float64) {
    statement, err := db.Prepare("INSERT INTO reading (TimeStamp, Temperature, Humidity) VALUES (?,?,?)")
    Check(err)
    _, err = statement.Exec(TimeStamp, Temperature, Humidity)
    Check(err)}

我們創(chuàng)建了一個(gè)函數(shù)來存儲(chǔ)時(shí)間戳,浮點(diǎn)數(shù)和濕度作為輸入數(shù)據(jù)。db.Prepare() 是為寫數(shù)據(jù)庫準(zhǔn)備sql語句,statement.Exec() 執(zhí)行數(shù)據(jù)庫操作。這里是向數(shù)據(jù)庫插入數(shù)據(jù)。下面我們創(chuàng)建接口,來接收客戶端發(fā)送的數(shù)據(jù),然后調(diào)以上函數(shù)存儲(chǔ)數(shù)據(jù):

func tempData(c *gin.Context) {
    // pull from original post and put into our struct
    if c.Request.Method == "POST" {
        var r Reading
        c.BindJSON(&r)
        // save to database here
        saveToDatabase(r.TimeStamp, r.Temperature, r.Humidity)

        c.JSON(http.StatusOK, gin.H{
            "status":  "Posted!", 
           "Message": "This worked!",
        })
    }
}

在這個(gè)函數(shù)中,我們從gin上下文中提取POST數(shù)據(jù)。我們創(chuàng)建read結(jié)構(gòu)的一個(gè)實(shí)例,并將JSON綁定到它。通過將JSON中的數(shù)據(jù)傳遞給剛剛創(chuàng)建的saveToDatabase函數(shù),將數(shù)據(jù)存儲(chǔ)在數(shù)據(jù)庫中。

然后我們返回一個(gè)JSON串,狀態(tài)為200 OK,如果你想在某個(gè)時(shí)候調(diào)用它,你可以添加一些消息。

獲得最近十條數(shù)據(jù)

我們需要?jiǎng)?chuàng)建的下一個(gè)函數(shù)是從數(shù)據(jù)庫中獲取最近接收到10條記錄。

func getLastTen() []Reading {
    // query the database for readings
    rows, _ := db.Query("SELECT TimeStamp, Temperature, Humidity from reading LIMIT 20")

    // create some temp variables
    var TimeStamp string
    var Temperature float64
    var Humidity float64

    // make a slice
    lastTen := make([]Reading, 10)

    // insert data into slice
    for rows.Next() {
        rows.Scan(&TimeStamp, &Temperature, &Humidity)
        lastTen = append(lastTen, Reading{TimeStamp: TimeStamp, Temperature: Temperature, Humidity: Humidity})
    }

    // return it
    return lastTen}

這里我們有一個(gè)函數(shù),它不接受輸入,但返回?cái)?shù)據(jù)庫中最近10條數(shù)據(jù)。首先,創(chuàng)建select語句和臨時(shí)變量。我們創(chuàng)建一個(gè)名為lastTen切片。將使用一個(gè)for循環(huán)來遍歷返回的行,先將數(shù)據(jù)存放在臨時(shí)變量中,然后將它們添加到切片中。 最后,從函數(shù)返回lastTen

配制API

我們將使用Gin作為API服務(wù)框架。使用的API非常簡單,可以手工寫,但是通過使用Gin,可以擴(kuò)展接口,使得以后更健壯。

r := gin.Default()
r.GET("/reading", func(c *gin.Context) {
        lastTen := getLastTen()
        // stuff into a JSON object and return it 
       c.JSON(200, gin.H{"message": lastTen})
    })

r.POST("/reading", tempData)
r.Run(":5000")

這將創(chuàng)建一個(gè)默認(rèn)的Gin路由。然后我們會(huì)找到返回最后10個(gè)讀數(shù)的接口。這里我們創(chuàng)建了一個(gè)路由來捕獲發(fā)送到/讀取的任何GET命令。然后我們調(diào)用剛剛創(chuàng)建的getLastTen()函數(shù),將該切片序列化為JSON,并返回一個(gè)200 OK消息。

一旦啟動(dòng)這個(gè)服務(wù),你可以接受來自樹莓派的POST命令,并將接收到的傳感器數(shù)據(jù)存儲(chǔ)在數(shù)據(jù)庫中。

總結(jié)

我知道這是一個(gè)很長的教程。以下是我們學(xué)到的:

  • 如何從傳感器獲取溫度
  • 如何發(fā)送JSON中數(shù)據(jù)
  • 使用Gin構(gòu)建一個(gè)API服務(wù)來接收它
  • 將數(shù)據(jù)存儲(chǔ)在SQLite數(shù)據(jù)庫中
    有大量的前期工作可以幫助您理解物聯(lián)網(wǎng)是如何工作的,以及如何構(gòu)建自己的系統(tǒng)。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容