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


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

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

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

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

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

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

連接溫度傳感器

溫濕度傳感器

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

讀取溫度傳感器

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

go get github.com/MichaelS11/go-dht

這是我喜歡使用的一個庫征椒,因為它非常簡單娇哆,并且對我來說非常可靠勃救。

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

package main

import (    

    "fmt"

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

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

const GPIO = "GPIO17"

接下來益兄,我們需要對溫度傳感器做三件事:

  • 初始化GPIO主機
  • 創(chuàng)建一個新的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ù)就實現(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)建一個結(jié)構(gòu)體骂删,是我們將發(fā)送到服務(wù)端的數(shù)據(jù)模型。

type reading struct {
    TimeStamp   string
    Temperature float64
    Humidity    float64
}

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

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

在上面的結(jié)構(gòu)中,我們有一個時間戳欧瘪。將在應(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ù)據(jù)輸出到控制臺佛掖,你可以添加以下內(nèi)容:

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

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

var requestBody, reqerr = json.Marshal(newReading)

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

然后將該請求內(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)建一個defer來關(guān)閉請求:

defer resp.Body.Close()

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

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

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

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

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

配制包

我們需要安裝幾個Go包:

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

然后焊切,創(chuàng)建一個名為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ù)庫交互专肪。對于大型應(yīng)用程序刹勃,您可能希望使用規(guī)模更大的數(shù)據(jù)庫服務(wù)。但是為了學(xué)習(xí)和實驗嚎尤,SQLlite就很好荔仁。然后我們將使用fmt標(biāo)準(zhǔn)庫來打印我們的錯誤消息,

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

type Reading struct {
    TimeStamp   string
    Temperature float64
    Humidity    float64
}

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

var db *sql.DB

接下來关贵,讓我們創(chuàng)建一個“Check”函數(shù)來檢查和報告錯誤遇骑。它看起來是這樣的:

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

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

接下來,我們將創(chuàng)建一個init函數(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()
}

以上代碼會連接sqlite數(shù)據(jù)庫势篡。我們通過sql.Open函數(shù)來連接數(shù)據(jù)庫,參數(shù)sqlite3指定使用的數(shù)據(jù)庫類型模暗,第二個參數(shù)指定數(shù)據(jù)庫存儲文件。如果數(shù)據(jù)庫存儲文件不存在將創(chuàng)建新的念祭,最后檢查錯誤信息兑宇。

接下來,我們有一個預(yù)備聲明粱坤。表示如果這個數(shù)據(jù)表不存在隶糕,我們就創(chuàng)建它。注意站玄,我們添加了時間戳枚驻、溫度和濕度。然后株旷,我們再次調(diào)用check以確保沒有任何錯誤再登。調(diào)用statement.Exec()執(zhí)行我們的SQL查詢。

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

現(xiàn)在我們需要設(shè)置將數(shù)據(jù)存儲到數(shù)據(jù)庫中的方法晾剖。這很容易做到锉矢,并且比您想象的代碼要少。首先齿尽,讓我們創(chuàng)建一個函數(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)建了一個函數(shù)來存儲時間戳沽损,浮點數(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ù)存儲數(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!",
        })
    }
}

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

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

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

我們需要創(chuàng)建的下一個函數(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}

這里我們有一個函數(shù)英妓,它不接受輸入挽放,但返回數(shù)據(jù)庫中最近10條數(shù)據(jù)。首先蔓纠,創(chuàng)建select語句和臨時變量辑畦。我們創(chuàng)建一個名為lastTen切片。將使用一個for循環(huán)來遍歷返回的行腿倚,先將數(shù)據(jù)存放在臨時變量中纯出,然后將它們添加到切片中。 最后敷燎,從函數(shù)返回lastTen

配制API

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

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)建一個默認(rèn)的Gin路由鸵赖。然后我們會找到返回最后10個讀數(shù)的接口。這里我們創(chuàng)建了一個路由來捕獲發(fā)送到/讀取的任何GET命令拄衰。然后我們調(diào)用剛剛創(chuàng)建的getLastTen()函數(shù)卫漫,將該切片序列化為JSON,并返回一個200 OK消息肾砂。

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

總結(jié)

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

  • 如何從傳感器獲取溫度
  • 如何發(fā)送JSON中數(shù)據(jù)
  • 使用Gin構(gòu)建一個API服務(wù)來接收它
  • 將數(shù)據(jù)存儲在SQLite數(shù)據(jù)庫中
    有大量的前期工作可以幫助您理解物聯(lián)網(wǎng)是如何工作的,以及如何構(gòu)建自己的系統(tǒng)源葫。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末诗越,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子息堂,更是在濱河造成了極大的恐慌嚷狞,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件荣堰,死亡現(xiàn)場離奇詭異床未,居然都是意外死亡,警方通過查閱死者的電腦和手機振坚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進(jìn)店門薇搁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人渡八,你說我怎么就攤上這事啃洋〈酰” “怎么了?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵宏娄,是天一觀的道長问裕。 經(jīng)常有香客問我,道長孵坚,這世上最難降的妖魔是什么僻澎? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮十饥,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘祖乳。我一直安慰自己逗堵,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布眷昆。 她就那樣靜靜地躺著蜒秤,像睡著了一般。 火紅的嫁衣襯著肌膚如雪亚斋。 梳的紋絲不亂的頭發(fā)上作媚,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天,我揣著相機與錄音帅刊,去河邊找鬼纸泡。 笑死,一個胖子當(dāng)著我的面吹牛赖瞒,可吹牛的內(nèi)容都是我干的女揭。 我是一名探鬼主播,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼栏饮,長吁一口氣:“原來是場噩夢啊……” “哼吧兔!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起袍嬉,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤境蔼,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后伺通,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體箍土,經(jīng)...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年罐监,在試婚紗的時候發(fā)現(xiàn)自己被綠了涮帘。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,030評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡笑诅,死狀恐怖调缨,靈堂內(nèi)的尸體忽然破棺而出疮鲫,到底是詐尸還是另有隱情,我是刑警寧澤弦叶,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布俊犯,位于F島的核電站,受9級特大地震影響伤哺,放射性物質(zhì)發(fā)生泄漏燕侠。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一立莉、第九天 我趴在偏房一處隱蔽的房頂上張望绢彤。 院中可真熱鬧,春花似錦蜓耻、人聲如沸茫舶。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽饶氏。三九已至,卻和暖如春有勾,著一層夾襖步出監(jiān)牢的瞬間疹启,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工蔼卡, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留喊崖,地道東北人。 一個月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓雇逞,卻偏偏與公主長得像贷祈,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子喝峦,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,976評論 2 355

推薦閱讀更多精彩內(nèi)容