feat(pkg/sensor): new support for sensor bme280

This commit is contained in:
Markus Pesch 2019-06-30 14:34:13 +02:00
parent 96eb1f4036
commit 289aaf2093
Signed by: volker.raschek
GPG Key ID: 852BCC170D81A982
20 changed files with 309 additions and 87 deletions

View File

@ -33,7 +33,7 @@ var daemonCmd = &cobra.Command{
logger := logger.NewDefaultLogger(logger.LogLevelDebug)
daemon.Start(cnf, duration, compression, logger)
daemon.Start(cnf, duration, compression, round, logger)
},
}
@ -44,5 +44,4 @@ func InitCmd(cmd *cobra.Command, cnfFile string) {
daemonCmd.Flags().BoolVar(&compression, "compression", true, "Compress measured values")
daemonCmd.Flags().StringVar(&cleanCacheInterval, "clean-cache-interval", "5m", "Minute intervall to clean cache and write measured values into logfile")
daemonCmd.Flags().Float64Var(&round, "round", 0.25, "Round values. The value 0 deactivates the function")
daemonCmd.Flags().StringVar(&temperatureUnit, "temperature-unit", "celsius", "Temperature unit")
}

53
cmd/humidity/list.go Normal file
View File

@ -0,0 +1,53 @@
package humidity
import (
"fmt"
"log"
"os"
"github.com/go-flucky/flucky/pkg/cli"
"github.com/go-flucky/flucky/pkg/config"
"github.com/go-flucky/flucky/pkg/logfile"
"github.com/go-flucky/flucky/pkg/rgbled"
"github.com/go-flucky/flucky/pkg/types/typeswitch"
"github.com/spf13/cobra"
)
var listTemperatureCmd = &cobra.Command{
Use: "list",
Short: "print humidities",
Example: fmt.Sprintf("flucky humidity logs"),
Run: func(cmd *cobra.Command, args []string) {
// read configuration
cnf, err := config.Read(configFile)
if err != nil {
log.Fatalln(err)
}
logfile := logfile.New(cnf.Device.Logfile)
rgbLEDs := cnf.GetRGBLEDs(config.ENABLED)
if err := rgbled.Logfile(rgbLEDs); err != nil {
log.Fatalln(err)
}
measuredValues, err := logfile.Read()
if err != nil {
log.Fatalln(err)
}
if err := rgbled.Off(rgbLEDs); err != nil {
log.Fatalln(err)
}
measuredValues = typeswitch.HumidityValues(measuredValues)
cli.PrintMeasuredValues(measuredValues, cnf, os.Stdout)
},
}
func init() {
humidityCmd.AddCommand(listTemperatureCmd)
}

View File

@ -41,7 +41,9 @@ var readHumidityCmd = &cobra.Command{
}
rgbLEDs := cnf.GetRGBLEDs(config.ENABLED)
rgbled.Run(rgbLEDs)
if err := rgbled.Run(rgbLEDs); err != nil {
log.Fatalln(err)
}
ctx := context.Background()
measuredValues, err := sensor.Read(ctx, sensors)
@ -54,8 +56,8 @@ var readHumidityCmd = &cobra.Command{
cli.PrintMeasuredValues(measuredValues, cnf, os.Stdout)
if logs {
logfile := logfile.New(cnf.Device.Logfile)
err := logfile.Append(compression, measuredValues)
measuredValuesLogfile := logfile.New(cnf.Device.Logfile)
err := logfile.Append(measuredValuesLogfile, compression, round, measuredValues)
if err != nil {
log.Fatalln(err)
}

View File

@ -1,7 +1,6 @@
package sensor
import (
"fmt"
"log"
"github.com/go-flucky/flucky/pkg/config"
@ -10,14 +9,20 @@ import (
)
var enabled bool
var location, wireID, wirePath string
var gpioIn string
var i2cAddress uint8
var i2cBus int
var location string
var wireID string
var addSensorCmd = &cobra.Command{
Use: "add",
Short: "Add Sensor",
Aliases: []string{"append"},
Args: cobra.ExactArgs(3),
Example: fmt.Sprintf("flucky sensor add indoor DHT11 GPIO14\nflucky sensor add --wire-id 28-011432f0bb3d outdoor DS18B20 GPIO14"),
Args: cobra.ExactArgs(2),
Example: `flucky sensor add --gpio GPIO14 indoor DHT11
flucky sensor add --wire-id 28-011432f0bb3d outdoor DS18B20
flucky sensor add --i2c-bus 1 --i2c-address 0x76 wetter-station BME280`,
Run: func(cmd *cobra.Command, args []string) {
// read configuration
cnf, err := config.Read(configFile)
@ -31,23 +36,44 @@ var addSensorCmd = &cobra.Command{
log.Fatalln(err)
}
// determine gpio port
gpio, err := types.StringToGPIO(args[2])
if err != nil {
log.Fatalln(err)
}
// create new sensor struct
sensor := &types.Sensor{
SensorName: args[0],
SensorModel: sensorModel,
SensorLocation: location,
SensorEnabled: enabled,
GPIONumber: &gpio,
WireID: &wireID,
}
// // add sensor entry to list
// determine gpio port if set
if gpioIn != "" &&
i2cAddress == 0 &&
i2cBus == 0 &&
wireID == "" {
gpio, err := types.StringToGPIO(gpioIn)
if err != nil {
log.Fatalln(err)
}
sensor.GPIONumber = &gpio
}
// set i2c connection settings
if gpioIn == "" &&
i2cAddress != 0 &&
i2cBus != 0 &&
wireID == "" {
sensor.I2CAddress = &i2cAddress
sensor.I2CBus = &i2cBus
}
// set wire connection settings
if gpioIn == "" &&
i2cAddress == 0 &&
i2cBus == 0 &&
wireID != "" {
sensor.WireID = &wireID
}
// add sensor entry to list
err = cnf.AddSensor(sensor)
if err != nil {
log.Fatalln(err)
@ -64,8 +90,10 @@ var addSensorCmd = &cobra.Command{
func init() {
sensorCmd.AddCommand(addSensorCmd)
addSensorCmd.Flags().BoolVarP(&enabled, "enabled", "e", true, "Enable Sensor")
addSensorCmd.Flags().StringVarP(&location, "location", "l", "", "Sensor location")
addSensorCmd.Flags().StringVarP(&wireID, "wire-id", "i", "", "Wire-ID")
addSensorCmd.Flags().StringVarP(&wirePath, "wire-path", "w", "/sys/bus/w1/devices", "Wire device path")
addSensorCmd.Flags().BoolVar(&enabled, "enabled", true, "Enable Sensor")
addSensorCmd.Flags().StringVar(&gpioIn, "gpio", "", "GPIO")
addSensorCmd.Flags().Uint8Var(&i2cAddress, "i2c-address", 0, "I2C-Address")
addSensorCmd.Flags().IntVar(&i2cBus, "i2c-bus", 0, "I2C-Bus")
addSensorCmd.Flags().StringVar(&location, "location", "", "Sensor location")
addSensorCmd.Flags().StringVar(&wireID, "wire-id", "", "Wire-ID")
}

View File

@ -8,6 +8,8 @@ import (
"github.com/go-flucky/flucky/pkg/cli"
"github.com/go-flucky/flucky/pkg/config"
"github.com/go-flucky/flucky/pkg/logfile"
"github.com/go-flucky/flucky/pkg/rgbled"
"github.com/go-flucky/flucky/pkg/types/typeswitch"
"github.com/spf13/cobra"
)
@ -25,11 +27,22 @@ var listTemperatureCmd = &cobra.Command{
logfile := logfile.New(cnf.Device.Logfile)
rgbLEDs := cnf.GetRGBLEDs(config.ENABLED)
if err := rgbled.Logfile(rgbLEDs); err != nil {
log.Fatalln(err)
}
measuredValues, err := logfile.Read()
if err != nil {
log.Fatalln(err)
}
if err := rgbled.Off(rgbLEDs); err != nil {
log.Fatalln(err)
}
measuredValues = typeswitch.TemperatureValues(measuredValues)
cli.PrintMeasuredValues(measuredValues, cnf, os.Stdout)
},

View File

@ -44,7 +44,9 @@ var readTemperatureCmd = &cobra.Command{
}
rgbLEDs := cnf.GetRGBLEDs(config.ENABLED)
rgbled.Run(rgbLEDs)
if err := rgbled.Run(rgbLEDs); err != nil {
log.Fatalln(err)
}
ctx := context.Background()
measuredValues, err := sensor.Read(ctx, sensors)
@ -59,14 +61,13 @@ var readTemperatureCmd = &cobra.Command{
cli.PrintMeasuredValues(measuredValues, cnf, os.Stdout)
if logs {
logfile := logfile.New(cnf.Device.Logfile)
err := logfile.Append(compression, measuredValues)
measuredValuesLogfile := logfile.New(cnf.Device.Logfile)
err := logfile.Append(measuredValuesLogfile, compression, round, measuredValues)
if err != nil {
rgbled.Error(rgbLEDs)
log.Fatalln(err)
}
}
rgbled.Off(rgbLEDs)
},
}

4
go.mod
View File

@ -3,6 +3,9 @@ module github.com/go-flucky/flucky
go 1.12
require (
github.com/d2r2/go-bsbmp v0.0.0-20190515110334-3b4b3aea8375
github.com/d2r2/go-i2c v0.0.0-20181113114621-14f8dd4e89ce
github.com/d2r2/go-logger v0.0.0-20181221090742-9998a510495e
github.com/go-flucky/go-dht v0.1.1
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/kr/pretty v0.1.0 // indirect
@ -12,4 +15,5 @@ require (
github.com/stianeikeland/go-rpio v4.2.0+incompatible
github.com/stretchr/testify v1.3.0
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
periph.io/x/periph v3.4.0+incompatible
)

6
go.sum
View File

@ -1,3 +1,9 @@
github.com/d2r2/go-bsbmp v0.0.0-20190515110334-3b4b3aea8375 h1:vdUOwcZdV+bBfGUUh5oPPWSzw9p+lBnNSuGgQwGpCH4=
github.com/d2r2/go-bsbmp v0.0.0-20190515110334-3b4b3aea8375/go.mod h1:3iz1WHlYJU9b4NJei+Q8G7DN3K05arcCMlOQ+qNCDjo=
github.com/d2r2/go-i2c v0.0.0-20181113114621-14f8dd4e89ce h1:Dog7PLNz1fPaXqHPOHonpERqsF57Oh4X76pM80T1GDY=
github.com/d2r2/go-i2c v0.0.0-20181113114621-14f8dd4e89ce/go.mod h1:AwxDPnsgIpy47jbGXZHA9Rv7pDkOJvQbezPuK1Y+nNk=
github.com/d2r2/go-logger v0.0.0-20181221090742-9998a510495e h1:ZG3JBA6rPRl0xxQ+nNSfO7tor8w+CNCTs05DNJQYbLM=
github.com/d2r2/go-logger v0.0.0-20181221090742-9998a510495e/go.mod h1:oA+9PUt8F1aKJ6o4YU1T120i7sgo1T6/1LWEEBy0BSs=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-flucky/go-dht v0.1.1 h1:dPz9F5D7oUaTd0GUGTvT4lBH2R043h1bkmhTlpQax1Y=

View File

@ -13,14 +13,16 @@ import (
)
var humiditySensorModels = map[types.SensorModel]types.SensorModel{
types.DHT11: types.DHT11,
types.DHT22: types.DHT22,
types.DHT11: types.DHT11,
types.DHT22: types.DHT22,
types.BME280: types.BME280,
}
var temperatureSensorModels = map[types.SensorModel]types.SensorModel{
types.DHT11: types.DHT11,
types.DHT22: types.DHT22,
types.DS18B20: types.DS18B20,
types.BME280: types.BME280,
}
// Configuration of flucky
@ -82,9 +84,10 @@ func (c *Configuration) AddSensor(sensor *types.Sensor) error {
return fmt.Errorf("Sensor %v with UUID %v already exists", s.SensorName, s.SensorID)
}
if *sensor.WireID != "" &&
*s.WireID == *sensor.WireID {
return fmt.Errorf("Sensor with 1wire-id %v already exists as %v", *s.WireID, s.SensorName)
if sensor.WireID != nil {
if *s.WireID == *sensor.WireID {
return fmt.Errorf("Sensor with 1wire-id %v already exists as %v", *s.WireID, s.SensorName)
}
}
}
@ -260,8 +263,6 @@ func (c *Configuration) GetHumiditySensorsByName(names []string) []sensor.Sensor
switch name {
case s.SensorID:
configHumiditySensors[s.SensorID] = s
case *s.WireID:
configHumiditySensors[s.SensorID] = s
case s.SensorName:
configHumiditySensors[s.SensorID] = s
}
@ -381,8 +382,6 @@ func (c *Configuration) GetTemperatureSensorsByName(names []string) []sensor.Sen
switch name {
case s.SensorID:
configTemperatureSensors[s.SensorID] = s
case *s.WireID:
configTemperatureSensors[s.SensorID] = s
case s.SensorName:
configTemperatureSensors[s.SensorID] = s
}
@ -464,6 +463,10 @@ func (c *Configuration) convertSensors(sensors []*types.Sensor) []sensor.Sensor
for _, s := range sensors {
switch s.SensorModel {
case types.BME280:
cachedSensors = append(cachedSensors, &sensor.BME280{
Sensor: s,
})
case types.DHT11:
cachedSensors = append(cachedSensors, &sensor.DHT11{
Sensor: s,

View File

@ -16,7 +16,7 @@ import (
)
// Start the daemon
func Start(cnf *config.Configuration, cleanCacheInterval time.Duration, compression bool, logger logger.Logger) {
func Start(cnf *config.Configuration, cleanCacheInterval time.Duration, compression bool, round float64, logger logger.Logger) {
// Info
logger.Info("Use clean-cache-interval: %v", cleanCacheInterval.String())
@ -33,7 +33,7 @@ func Start(cnf *config.Configuration, cleanCacheInterval time.Duration, compress
ctx := context.Background()
childContext, cancel := context.WithCancel(ctx)
logfile := logfile.New(cnf.Device.Logfile)
measuredValuesLogfile := logfile.New(cnf.Device.Logfile)
measuredValuesCache := make([]types.MeasuredValue, 0)
@ -66,7 +66,7 @@ func Start(cnf *config.Configuration, cleanCacheInterval time.Duration, compress
logger.Error("Can not turn on blue info light: %v", err)
}
err = logfile.Append(compression, measuredValuesCache)
err = logfile.Append(measuredValuesLogfile, compression, round, measuredValuesCache)
if err != nil {
err = rgbled.Error(rgbLEDs)
@ -92,8 +92,8 @@ func Start(cnf *config.Configuration, cleanCacheInterval time.Duration, compress
logger.Error("Can not turn on red info light: %v", err)
}
logger.Warn("Save remaining data from the cache: %v", len(measuredValuesCache))
err = logfile.Append(compression, measuredValuesCache)
logger.Warn("Save remaining data from the cache")
err = logfile.Append(measuredValuesLogfile, compression, round, measuredValuesCache)
if err != nil {
logger.Fatal("%v", err)
}

View File

@ -14,28 +14,6 @@ type csvLogfile struct {
logfile string
}
func (cl *csvLogfile) Append(compression bool, measuredValues []types.MeasuredValue) error {
allMeasuredValues, err := cl.Read()
if err != nil {
return err
}
allMeasuredValues = append(allMeasuredValues, measuredValues...)
if compression {
allMeasuredValues = Compression(allMeasuredValues)
}
err = cl.Write(allMeasuredValues)
if err != nil {
return err
}
return nil
}
func (cl *csvLogfile) Read() ([]types.MeasuredValue, error) {
if _, err := os.Stat(cl.logfile); os.IsNotExist(err) {
return nil, fmt.Errorf("%v: %v", errorLogfileNotFound, cl.logfile)

View File

@ -5,7 +5,6 @@ import (
)
type Logfile interface {
Append(compression bool, measuredValues []types.MeasuredValue) error
Read() ([]types.MeasuredValue, error)
Write(measuredValues []types.MeasuredValue) error
}

View File

@ -17,28 +17,6 @@ type jsonLogfile struct {
logfile string
}
func (jl *jsonLogfile) Append(compression bool, measuredValues []types.MeasuredValue) error {
allMeasuredValues, err := jl.Read()
if err != nil {
return err
}
allMeasuredValues = append(allMeasuredValues, measuredValues...)
if compression {
allMeasuredValues = Compression(allMeasuredValues)
}
err = jl.Write(allMeasuredValues)
if err != nil {
return err
}
return nil
}
func (jl *jsonLogfile) Read() ([]types.MeasuredValue, error) {
if _, err := os.Stat(jl.logfile); os.IsNotExist(err) {

View File

@ -1,6 +1,7 @@
package logfile
import (
"math"
"path/filepath"
"sort"
"time"
@ -9,7 +10,35 @@ import (
)
// var validUUID = regexp.MustCompile("^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[8|9|aA|bB][a-fA-F0-9]{3}-[a-fA-F0-9]{12}$")
var timeFormat = time.RFC3339
var timeFormat = "2006-01-02T15:04:05.999999Z07:00"
func Append(logfile Logfile, compression bool, round float64, measuredValues []types.MeasuredValue) error {
if round != 0 {
for _, measuredValue := range measuredValues {
measuredValue.SetValue(math.Round(measuredValue.GetValue()/round) * round)
}
}
allMeasuredValues, err := logfile.Read()
if err != nil {
return err
}
allMeasuredValues = append(allMeasuredValues, measuredValues...)
if compression {
allMeasuredValues = Compression(allMeasuredValues)
}
err = logfile.Write(allMeasuredValues)
if err != nil {
return err
}
return nil
}
// Compression the measured values. The system checks whether the measured values
// of the same type correspond to those of the predecessor. If this is the case,

112
pkg/sensor/bme280.go Normal file
View File

@ -0,0 +1,112 @@
package sensor
import (
"context"
"fmt"
"log"
"sync"
"time"
"github.com/d2r2/go-bsbmp"
"github.com/d2r2/go-i2c"
"github.com/d2r2/go-logger"
"github.com/go-flucky/flucky/pkg/types"
uuid "github.com/satori/go.uuid"
)
// BME280 is a sensor to measure humidity and temperature.
type BME280 struct {
*types.Sensor
}
// GetSensorModel returns the sensor model
func (s *BME280) GetSensorModel() types.SensorModel {
return s.Sensor.SensorModel
}
// Read measured values
func (s *BME280) Read() ([]types.MeasuredValue, error) {
// Create new connection to i2c-bus on 1 line with address 0x76.
// Use i2cdetect utility to find device address over the i2c-bus
i2c, err := i2c.NewI2C(*s.I2CAddress, *s.I2CBus)
if err != nil {
log.Fatal(err)
}
defer i2c.Close()
logger.ChangePackageLogLevel("i2c", logger.InfoLevel)
sensor, err := bsbmp.NewBMP(bsbmp.BME280, i2c)
if err != nil {
log.Fatal(err)
}
logger.ChangePackageLogLevel("bsbmp", logger.InfoLevel)
temperatureValue, err := sensor.ReadTemperatureC(bsbmp.ACCURACY_STANDARD)
if err != nil {
log.Fatal(err)
}
// p, err := sensor.ReadPressurePa(bsbmp.ACCURACY_STANDARD)
// if err != nil {
// log.Fatal(err)
// }
_, humidityValue, err := sensor.ReadHumidityRH(bsbmp.ACCURACY_STANDARD)
if err != nil {
log.Fatal(err)
}
measuredValues := []types.MeasuredValue{
&types.Humidity{
HumidityID: uuid.NewV4().String(),
HumidityValue: float64(humidityValue),
HumidityFromDate: time.Now(),
HumidityTillDate: time.Now(),
SensorID: s.SensorID,
},
&types.Temperature{
TemperatureID: uuid.NewV4().String(),
TemperatureValue: float64(temperatureValue),
TemperatureFromDate: time.Now(),
TemperatureTillDate: time.Now(),
SensorID: s.SensorID,
},
}
return measuredValues, nil
}
// ReadChannel reads the measured values from the sensor and writes them to a
// channel.
func (s *BME280) ReadChannel(measuredValuesChannel chan<- []types.MeasuredValue, errorChannel chan<- error, wg *sync.WaitGroup) {
if wg != nil {
defer wg.Done()
}
measuredValues, err := s.Read()
if err != nil {
errorChannel <- err
return
}
measuredValuesChannel <- measuredValues
}
// ReadContinously reads the measured values continously from the sensor and
// writes them to a channel.
func (s *BME280) ReadContinously(ctx context.Context, measuredValuesChannel chan<- []types.MeasuredValue, errorChannel chan<- error) {
for {
select {
case <-ctx.Done():
errorChannel <- fmt.Errorf("%v: Context closed: %v", s.SensorName, ctx.Err())
return
default:
s.ReadChannel(measuredValuesChannel, errorChannel, nil)
}
}
}

View File

@ -45,6 +45,10 @@ func (h *Humidity) GetMeasuredValueType() MeasuredValueType {
return MeasuredValueTypeHumidity
}
func (h *Humidity) SetValue(value float64) {
h.HumidityValue = value
}
func (h *Humidity) SetTillDate(date time.Time) {
h.HumidityTillDate = date
}

View File

@ -12,6 +12,7 @@ type MeasuredValue interface {
GetCreationDate() *time.Time
GetUpdateDate() *time.Time
SetValue(value float64)
SetTillDate(date time.Time)
SetCreationDate(date *time.Time)
SetUpdateDate(date *time.Time)

View File

@ -13,6 +13,8 @@ type Sensor struct {
SensorName string `json:"sensor_name" xml:"sensor_name"`
SensorLocation string `json:"sensor_location" xml:"sensor_location"`
WireID *string `json:"wire_id" xml:"wire_id"`
I2CBus *int `json:"i2c_bus" xml:"i2c_bus"`
I2CAddress *uint8 `json:"i2c_address" xml:"i2c_address"`
GPIONumber *GPIO `json:"gpio_number" xml:"gpio_number"`
SensorModel SensorModel `json:"sensor_model" xml:"sensor_model"`
SensorEnabled bool `json:"sensor_enabled" xml:"sensor_enabled"`
@ -46,6 +48,9 @@ func (s *Sensor) Name() string {
return s.SensorName
} else if s.WireID != nil {
return *s.WireID
} else if s.I2CAddress != nil &&
s.I2CBus != nil {
return fmt.Sprintf("%v/%v", *s.I2CBus, *s.I2CAddress)
}
return s.SensorID
}

View File

@ -5,7 +5,8 @@ import "fmt"
type SensorModel string
const (
DHT11 SensorModel = "DHT11"
BME280 SensorModel = "BME280"
DHT11 = "DHT11"
DHT22 = "DHT22"
DS18B20 = "DS18B20"
)
@ -13,6 +14,8 @@ const (
// SelectSensorModel converts a string into a constant
func SelectSensorModel(model string) (SensorModel, error) {
switch model {
case "BME280":
return BME280, nil
case "DHT11":
return DHT11, nil
case "DHT22":

View File

@ -47,6 +47,10 @@ func (t *Temperature) GetMeasuredValueType() MeasuredValueType {
return MeasuredValueTypeTemperature
}
func (t *Temperature) SetValue(value float64) {
t.TemperatureValue = value
}
func (t *Temperature) SetTillDate(date time.Time) {
t.TemperatureTillDate = date
}