在本教程中,我将向您展示如何用树莓派读取传感器的温度,并使用Go构建一个HTTP服务来存储数据。
您将学到如下内容:
- 如何从传感器获取温度
- 如何发送JSON数据
- 构建一个API服务来接收温度数据
- 将数据存储在SQLite数据库中
我们会用Go来实现。实现需要的硬件设备: - 树莓派一个
- AM2302传感器
- Linux主机
开发此功能的目的
本教程的目的是让您深入了解物联网及其工作原理。您也可以使用云服务实现,例如:
- AWS IoT
- Azure IoT Edge
- Google cloud IoT Core
这些云服务都很好。如果你正在构建一个真正的项目或从事物联网专业工作,使用这些云服务是对的。他们提供优秀的安全服务,为您处理很多的事情。
云服务虽然很好,但如果你想真正学习物联网,你需要从细节入手。云服务需要配置客户端,把你的数据推上去,它为你提供可视化。在本教程中,我们将自己构建所有这些东西并理解它。
连接温度传感器
传感器有三根线需要连接到树莓派的GPIO,如上所示。有一根红色的电线连到树莓派1脚。黑色导线接地并连接到第6脚。橙色(有时是黄色或白色)是数据线,接到第11引脚。连接非常简单,如果你需要额外的帮助,这里有一个很好的连接AM2302的指南:https://learn.adafruit.com/dht/connecting-to-a-dhtxx-sensor?ref=hackernoon.com。
读取温度传感器
我们将在树莓派上创建一个Go文件来读取传感器的温湿度。我把它命名为readsensor.go。你需要在树莓派上安装Go来运行它。接下来,需要安装Go-DHT库。
go get github.com/MichaelS11/go-dht
这是我喜欢使用的一个库,因为它非常简单,并且对我来说非常可靠。
首先,我们验证一下传感器的功能。让我们从头开始构建文件。在文件头中添加以下内容:
package main
import (
"fmt"
"github.com/MichaelS11/go-dht"
)
这将引入Go-DHT包和fmt来格式化输出。接下来,我将创建一个常量来设置GPIO.Pin引脚11是GPIO 17(传感器数据来源引脚)。如果你喜欢,你可以使用其他的GPIO引脚。
const GPIO = "GPIO17"
接下来,我们需要对温度传感器做三件事:
- 初始化GPIO主机
- 创建一个新的DHT reader
- 读取数据
因此在main函数中,需初始化:
hosterr := dht.HostInit()
if hosterr != nil {
fmt.Println("HostInit error:", hosterr)
return
}
创建新的DHT reader:
dht, dhterr := dht.NewDHT(GPIO, dht.Fahrenheit, "")
if dhterr != nil {
fmt.Println("NewDHT error:", dhterr)
return
}
注意,我使用的是上面const中设置的GPIO,调用NewDHT并将参数设置为Fahrenheit。你可以选择摄氏温度。最后,我们将读取传感器并输出结果。
humidity, temperature, readerr := dht.Read()
if readerr != nil {
fmt.Println("Reader error:", readerr)
return
}
很好,现在从传感器中读温湿度数据就实现了。接下来让我们准备将数据发送到HTTP服务。
发送JSON格式数据
我们要在import中添加一些包:
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"time"
"github.com/MichaelS11/go-dht"
)
引入这些包能将数据发送到HTTP服务上去。接下来,我们需要为数据创建一个结构体,是我们将发送到服务端的数据模型。
type reading struct {
TimeStamp string
Temperature float64
Humidity float64
}
这是我们要发送的数据。我们将在这里为服务添加另一个常量。这将是您服务器的URL。
const Endpoint = "http://[YOUR DOMAIN or IP]:5000/reading"
在上面的结构中,我们有一个时间戳。将在应用程序的顶部创建它:
timeStamp := time.Now()
接下来,在dht.read函数后面添加以下代码:
newReading := reading{TimeStamp: timeStamp.Format("2006-01-02T15:04:05-0700"),
Temperature: temperature, Humidity: humidity}
根据读取到的传感器数据创建reading结构体实例。如果你还想将读取的数据输出到控制台,你可以添加以下内容:
fmt.Printf("Our Reading was: \nTemperature: %v\nHumidity:%v\n", temperature, humidity)
现在我们创建http请求内容:
var requestBody, reqerr = json.Marshal(newReading)
if reqerr != nil {
fmt.Println("Request error:", readerr)
return}
然后将该请求内容以POST发送到我们的服务端。
resp, resperror := http.Post(Endpoint, "application/json", bytes.NewBuffer(requestBody))
if resperror != nil {
fmt.Println("Response error:", resperror)
return }
最后,我们将创建一个defer来关闭请求:
defer resp.Body.Close()
ok!现在我们有了一个实际的应用程序来读取传感器并将值发送到服务端,接下来需要构建服务端代码。源代码库地址:https://github.com/JeremyMorgan/GoTempSensor/blob/master/readsensor.go?ref=hackernoon.com
创建API服务端
因此,对于服务端设置,您几乎可以使用任何Linux或Windows虚拟机。我使用AWS Lightsail FreeBSD机器部署服务端。您需要在机器上安装Go,除非您想编译可执行文件发送到服务器上直接执行。
如果您使用的是LightSail,请确保打开端口5000。这个端口需要在您使用的任何防火墙或服务上打开。我们的服务端提供以下功能:
- 提供一个API来接收从树莓派上发送的POST请求
- 将结果存储在SQLlite数据库中
- 检索最近的10条传感器数据
配制包
我们需要安装几个Go包:
go get github.com/gin-gonic/gin
go get github.com/mattn/go-sqlite3
然后,创建一个名为reader.go的文件,你想叫它什么都行。
package main
import (
"database/sql"
"fmt" "net/http"
"github.com/gin-gonic/gin"
_ "github.com/mattn/go-sqlite3")
我们将使用database/sql库与我们的SQLlite数据库交互。对于大型应用程序,您可能希望使用规模更大的数据库服务。但是为了学习和实验,SQLlite就很好。然后我们将使用fmt标准库来打印我们的错误消息,
Gin将处理我们的API调用,然后go-sqllite3库将处理与我们的SQLite数据库的交互。同样,我们为读取post请求body创建一个类似的结构:
type Reading struct {
TimeStamp string
Temperature float64
Humidity float64
}
然后下面为db创建一个实例:
var db *sql.DB
接下来,让我们创建一个“Check”函数来检查和报告错误。它看起来是这样的:
func Check(e error) {
if e != nil {
panic(e)
}
}
设置数据库
接下来,我们将创建一个init函数,这样我们就可以在启动时初始化并连接到数据库。
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数据库。我们通过sql.Open函数来连接数据库,参数sqlite3指定使用的数据库类型,第二个参数指定数据库存储文件。如果数据库存储文件不存在将创建新的,最后检查错误信息。
接下来,我们有一个预备声明。表示如果这个数据表不存在,我们就创建它。注意,我们添加了时间戳、温度和湿度。然后,我们再次调用check以确保没有任何错误。调用statement.Exec()执行我们的SQL查询。
存储数据
现在我们需要设置将数据存储到数据库中的方法。这很容易做到,并且比您想象的代码要少。首先,让我们创建一个函数来保存传感器数据到数据库:
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)}
我们创建了一个函数来存储时间戳,浮点数和湿度作为输入数据。db.Prepare() 是为写数据库准备sql语句,statement.Exec() 执行数据库操作。这里是向数据库插入数据。下面我们创建接口,来接收客户端发送的数据,然后调以上函数存储数据:
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!",
})
}
}
在这个函数中,我们从gin上下文中提取POST数据。我们创建read结构的一个实例,并将JSON绑定到它。通过将JSON中的数据传递给刚刚创建的saveToDatabase函数,将数据存储在数据库中。
然后我们返回一个JSON串,状态为200 OK,如果你想在某个时候调用它,你可以添加一些消息。
获得最近十条数据
我们需要创建的下一个函数是从数据库中获取最近接收到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}
这里我们有一个函数,它不接受输入,但返回数据库中最近10条数据。首先,创建select语句和临时变量。我们创建一个名为lastTen切片。将使用一个for循环来遍历返回的行,先将数据存放在临时变量中,然后将它们添加到切片中。 最后,从函数返回lastTen
配制API
我们将使用Gin作为API服务框架。使用的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")
这将创建一个默认的Gin路由。然后我们会找到返回最后10个读数的接口。这里我们创建了一个路由来捕获发送到/读取的任何GET命令。然后我们调用刚刚创建的getLastTen()函数,将该切片序列化为JSON,并返回一个200 OK消息。
一旦启动这个服务,你可以接受来自树莓派的POST命令,并将接收到的传感器数据存储在数据库中。
总结
我知道这是一个很长的教程。以下是我们学到的:
- 如何从传感器获取温度
- 如何发送JSON中数据
- 使用Gin构建一个API服务来接收它
- 将数据存储在SQLite数据库中
有大量的前期工作可以帮助您理解物联网是如何工作的,以及如何构建自己的系统。