implement remote calibration with downlinks

This commit is contained in:
Joerg Lehmann 2019-12-13 20:06:12 +01:00
parent 959c930af5
commit d15653021f
3 changed files with 271 additions and 3 deletions

125
calibration.go Normal file
View File

@ -0,0 +1,125 @@
package main
import (
"fmt"
"log"
"time"
"math"
"strings"
"strconv"
"io/ioutil"
"net/http"
)
const api_url="https://proxy1.lpn.swisscom.ch/thingpark/lrc/rest"
func MakePost(url string) {
req, err := http.NewRequest("POST", url, nil)
if err != nil {
log.Fatal("Error reading request. ", err)
}
// Set headers
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
// Set client timeout
client := &http.Client{Timeout: time.Second * 10}
// Send request
resp, err := client.Do(req)
if err != nil {
log.Fatal("Error reading response. ", err)
}
defer resp.Body.Close()
fmt.Println("response Status:", resp.Status)
fmt.Println("response Headers:", resp.Header)
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatal("Error reading body. ", err)
}
fmt.Printf("%s\n", body)
}
func ResetNodeToZero(deveui string, w1_0 int32, w2_0 int32) {
fmt.Printf("Called ResetNodeToZero for deveui %s\n", deveui)
s := fmt.Sprintf("%s/downlink?DevEUI=%s&FPort=1&Payload=00", api_url, deveui)
MakePost(s)
// values should be taken from next packet...
SetDownlinkCommand(deveui,"update_cal_from_node")
}
func CalibrateScale(deveui string, downlink_command string, w1 int32, w2 int32) {
fmt.Printf("Called CalibrateScale for deveui %s, downlink_command: %s\n", deveui, downlink_command)
var cur_cal_settings CalSettings
cur_cal_settings = GetCurrentCalibrationSettings(deveui)
var calibration_weight_gram int32
var calibration_weight2_gram int32
tokens := strings.Split(downlink_command," ")
if (len(tokens) < 2) {
// no value in gram included!
fmt.Printf("Error: invalid downlink_command: %s, examples: \"tare_a 10000\", \"tare_b 10000\", \"tare 10000 10000\" devuid: %s\n", downlink_command, deveui)
return
}
n, err := strconv.ParseInt(tokens[1], 10, 32)
if err == nil {
calibration_weight_gram = int32(n)
} else {
fmt.Printf("Error: cannot get tare weight in gram: %s, deveui: %s\n", downlink_command, deveui)
return
}
if (len(tokens) > 2) {
n, err := strconv.ParseInt(tokens[2], 10, 32)
if err == nil {
calibration_weight2_gram = int32(n)
} else {
fmt.Printf("Error: cannot get tare weight2 in gram: %s, deveui: %s\n", downlink_command, deveui)
return
}
}
if (tokens[0] == "tare_a") {
new_w1_c := float32(w1 - cur_cal_settings.w1_0) / float32(calibration_weight_gram)
valstr := fmt.Sprintf("%08X%08X%08X%08X",uint32(cur_cal_settings.w1_0),uint32(cur_cal_settings.w2_0),math.Float32bits(new_w1_c),math.Float32bits(cur_cal_settings.w2_c))
s := fmt.Sprintf("%s/downlink?DevEUI=%s&FPort=1&Payload=01%s", api_url, deveui, valstr)
MakePost(s)
} else if (tokens[0] == "tare_b") {
new_w2_c := float32(w2 - cur_cal_settings.w2_0) / float32(calibration_weight_gram)
valstr := fmt.Sprintf("%08X%08X%08X%08X",uint32(cur_cal_settings.w1_0),uint32(cur_cal_settings.w2_0),math.Float32bits(cur_cal_settings.w2_c),math.Float32bits(new_w2_c))
s := fmt.Sprintf("%s/downlink?DevEUI=%s&FPort=1&Payload=01%s", api_url, deveui, valstr)
MakePost(s)
} else if (tokens[0] == "tare") {
new_w1_c := float32(w1 - cur_cal_settings.w1_0) / float32(calibration_weight_gram)
new_w2_c := float32(w2 - cur_cal_settings.w2_0) / float32(calibration_weight2_gram)
valstr := fmt.Sprintf("%08X%08X%08X%08X",uint32(cur_cal_settings.w1_0),uint32(cur_cal_settings.w2_0),math.Float32bits(new_w1_c),math.Float32bits(new_w2_c))
s := fmt.Sprintf("%s/downlink?DevEUI=%s&FPort=1&Payload=01%s", api_url, deveui, valstr)
MakePost(s)
} else {
fmt.Printf("Error: either use tare_a or tare_b or tare; deveui %s\n", deveui)
}
SetDownlinkCommand(deveui,"update_cal_from_node")
}
func UpdateNodeCalibrationSettings(deveui string) {
fmt.Printf("Called UpdateNodeCalibrationSettings for deveui %s\n", deveui)
var cur_cal_settings CalSettings
cur_cal_settings = GetCurrentCalibrationSettings(deveui)
valstr := fmt.Sprintf("%08X%08X%08X%08X",uint32(cur_cal_settings.w1_0),uint32(cur_cal_settings.w2_0),math.Float32bits(cur_cal_settings.w1_c),math.Float32bits(cur_cal_settings.w2_c))
s := fmt.Sprintf("%s/downlink?DevEUI=%s&FPort=1&Payload=01%s", api_url, deveui, valstr)
MakePost(s)
}
func UpdateCalibrationSettingsFromNode(deveui string, w1_0 int32, w2_0 int32, w1_c float32, w2_c float32) {
fmt.Printf("Called UpdateCalibrationSettingsFromNode for deveui %s\n", deveui)
var new_cal_settings CalSettings
new_cal_settings.w1_0 = w1_0
new_cal_settings.w2_0 = w2_0
new_cal_settings.w1_c = w1_c
new_cal_settings.w2_c = w2_c
SetCurrentCalibrationSettings(deveui,new_cal_settings)
SetDownlinkCommand(deveui,"do_nothing")
}

View File

@ -9,6 +9,7 @@ import (
"os" "os"
"io/ioutil" "io/ioutil"
"log" "log"
"strings"
"net/http" "net/http"
"time" "time"
) )
@ -105,6 +106,37 @@ var file *os.File
var alertMap map[string]string var alertMap map[string]string
var alertLogMap map[string]string var alertLogMap map[string]string
func ProcessInitPacket(deveui string, w1_0 int32, w2_0 int32, w1_c float32, w2_c float32, w1 int32, w2 int32) {
var downlink_command string
fmt.Printf("Processing Init Packet for Deveui %s\n", deveui)
downlink_command = GetDownlinkCommand(deveui)
if (downlink_command == "do_nothing") {
// do nothing
fmt.Printf("Init Packet, downlink_command set to do_nothing (or not set at all), nothing to do... Deveui %s\n", deveui)
} else if (downlink_command == "tare_0") {
// reset node to 0
ResetNodeToZero(deveui, w1_0, w2_0)
} else if strings.HasPrefix(downlink_command, "tare_a ") {
// calibrate Scale A : tare_a <gram_a>
CalibrateScale(deveui, downlink_command, w1, w2)
} else if strings.HasPrefix(downlink_command, "tare_b ") {
// calibrate Scale B : tare_b <gram_b>
CalibrateScale(deveui, downlink_command, w1, w2)
} else if strings.HasPrefix(downlink_command, "tare ") {
// calibrate both Scale A and Scale B in on step : tare <gram_a> <gram_b>
CalibrateScale(deveui, downlink_command, w1, w2)
} else if (downlink_command == "update_cal_on_node") {
// update calibration settings on node
UpdateNodeCalibrationSettings(deveui)
} else if (downlink_command == "update_cal_from_node") {
// update calibration settings from node
UpdateCalibrationSettingsFromNode(deveui, w1_0, w2_0, w1_c, w2_c)
} else {
fmt.Printf("Error: Unknown downlink_command: %s (DevEUI: %s)\n", downlink_command, deveui)
}
}
func DecodePayload(s string, deveui string, devaddr string, lrrlat float32, lrrlon float32, write2file bool) { func DecodePayload(s string, deveui string, devaddr string, lrrlat float32, lrrlon float32, write2file bool) {
var ba []byte var ba []byte
var pl_1 payload_1 var pl_1 payload_1
@ -202,6 +234,7 @@ func DecodePayload(s string, deveui string, devaddr string, lrrlat float32, lrrl
WriteDatapoint(tfp,deveui,devaddr,pl_128.Vbat,pl_128.H,pl_128.P,w,pl_128.W1,pl_128.W2,pl_128.T,lrrlat,lrrlon,pl_128.Fw_version,pl_128.W1_0,pl_128.W2_0,pl_128.W1_C,pl_128.W2_C) WriteDatapoint(tfp,deveui,devaddr,pl_128.Vbat,pl_128.H,pl_128.P,w,pl_128.W1,pl_128.W2,pl_128.T,lrrlat,lrrlon,pl_128.Fw_version,pl_128.W1_0,pl_128.W2_0,pl_128.W1_C,pl_128.W2_C)
} }
ProcessInitPacket(deveui,pl_128.W1_0,pl_128.W2_0,pl_128.W1_C,pl_128.W2_C,pl_128.W1,pl_128.W2)
} else if (s[0:2] == "81") { } else if (s[0:2] == "81") {
fmt.Printf("{\n") fmt.Printf("{\n")
fmt.Printf(" version: %d,\n", pl_129.Version) fmt.Printf(" version: %d,\n", pl_129.Version)
@ -250,13 +283,13 @@ func WriteDatapoint(mytime int64, deveui string, devaddr string, v uint8, h uint
sv := "" sv := ""
if (v > 0) { if (v > 0) {
sv = fmt.Sprintf(",v=%di,vp=%di,",int32(v)*7+2510,vp) sv = fmt.Sprintf("v=%di,vp=%di,",int32(v)*7+2510,vp)
} }
sfw := "" sfw := ""
if (fw_version > 0) { if (fw_version > 0) {
sfw = fmt.Sprintf(",fw_version=%di,w1=%di,w2=%di,w1_0=%d,w2_0=%d,w1_c=%f,w2_c=%f",fw_version,w1,w2,w1_0,w2_0,w1_c,w2_c) sfw = fmt.Sprintf(",fw_version=%di,w1=%di,w2=%di,w1_0=%di,w2_0=%di,w1_c=%f,w2_c=%f",fw_version,w1,w2,w1_0,w2_0,w1_c,w2_c)
} }
s = fmt.Sprintf("measurement,deveui=%s devaddr=\"%s\"%s,h=%di,p=%di,w=%di,t=%.1f,lrrlat=%f,lrrlon=%f%s %d\n",deveui,devaddr,sv,h,int32(p)+825,w*5,float32(t)/10,lrrlat,lrrlon,sfw,mytime*60*1000*1000*1000) s = fmt.Sprintf("measurement,deveui=%s devaddr=\"%s\",%sh=%di,p=%di,w=%di,t=%.1f,lrrlat=%f,lrrlon=%f%s %d\n",deveui,devaddr,sv,h,int32(p)+825,w*5,float32(t)/10,lrrlat,lrrlon,sfw,mytime*60*1000*1000*1000)
WriteStringToFile(s) WriteStringToFile(s)

View File

@ -3,6 +3,7 @@ package main
import ( import (
"fmt" "fmt"
"time" "time"
"strconv"
"github.com/gomodule/redigo/redis" "github.com/gomodule/redigo/redis"
) )
@ -11,6 +12,13 @@ var globalPool *redis.Pool
const lastvaluesPrefix string = "lastvalues:" const lastvaluesPrefix string = "lastvalues:"
const devPrefix string = "dev:" const devPrefix string = "dev:"
type CalSettings struct {
w1_0 int32
w2_0 int32
w1_c float32
w2_c float32
}
func newPool() *redis.Pool { func newPool() *redis.Pool {
return &redis.Pool{ return &redis.Pool{
// Maximum number of idle connections in the pool. // Maximum number of idle connections in the pool.
@ -187,3 +195,105 @@ func getSmsnumber(deveui string) string {
return res return res
} }
func GetDownlinkCommand(deveui string) string {
// 0: do nothing...
res := "do_nothing"
if deveui == "" {
return res
}
conn := globalPool.Get()
defer conn.Close()
downlink_command, err := redis.String(conn.Do("HGET", devPrefix+deveui, "downlink_command"))
if err == nil {
res = downlink_command
} else {
fmt.Println(err)
res = "do_nothing"
}
return res
}
func SetDownlinkCommand(deveui string, new_command string) error {
conn := globalPool.Get()
defer conn.Close()
// SET object
_, err := conn.Do("HMSET", devPrefix+deveui, "downlink_command", new_command)
if err != nil {
return err
}
return nil
}
func GetCurrentCalibrationSettings(deveui string) CalSettings {
var res CalSettings
if deveui == "" {
return res
}
conn := globalPool.Get()
defer conn.Close()
s_w1_0, err := redis.String(conn.Do("HGET", devPrefix+deveui, "w1_0"))
if err == nil {
n, err := strconv.ParseInt(s_w1_0, 10, 32)
if err == nil {
res.w1_0 = int32(n)
}
}
s_w2_0, err := redis.String(conn.Do("HGET", devPrefix+deveui, "w2_0"))
if err == nil {
n, err := strconv.ParseInt(s_w2_0, 10, 32)
if err == nil {
res.w2_0 = int32(n)
}
}
s_w1_c, err := redis.String(conn.Do("HGET", devPrefix+deveui, "w1_c"))
if err == nil {
f, err := strconv.ParseFloat(s_w1_c, 32)
if err == nil {
res.w1_c = float32(f)
}
}
s_w2_c, err := redis.String(conn.Do("HGET", devPrefix+deveui, "w2_c"))
if err == nil {
f, err := strconv.ParseFloat(s_w2_c, 32)
if err == nil {
res.w2_c = float32(f)
}
}
return res
}
func SetCurrentCalibrationSettings(deveui string, cal_settings CalSettings) error {
if deveui == "" {
return nil
}
conn := globalPool.Get()
defer conn.Close()
w1_0 := fmt.Sprintf("%d",cal_settings.w1_0)
w2_0 := fmt.Sprintf("%d",cal_settings.w2_0)
w1_c := fmt.Sprintf("%f",cal_settings.w1_c)
w2_c := fmt.Sprintf("%f",cal_settings.w2_c)
// SET object
_, err := conn.Do("HMSET", devPrefix+deveui, "w1_0",w1_0,"w2_0",w2_0,"w1_c",w1_c,"w2_c",w2_c)
if err != nil {
return err
}
return nil
}