I am receiving sensor data over MQTT. I want to check if the temperature is over 20 degrees, and if it is, send a message.
var f MQTT.MessageHandler = func(client MQTT.Client, msg MQTT.Message) {
type Data struct {
Sensor string `json:"sensor"`
Temp []int `json: "temperature"`
Hum []int `json: "humidity"`
}
var sensorData []Data
message := ""
err := json.Unmarshal(msg.Payload(), &sensorData)
if err != nil {
panic(err)
}else {
//access certain element in the Data struct
for _, i := range sensorData {
if i.Temp[2] > 20 { //where the temperature is stored
fmt.Println("Temperature too high")
message = "Temperature too high"
}else{
fmt.Println("Perfect temperature")
message = "Perfect temperature"
}
}
}
// Publish further instructions to "sensor/instruction"
token := client.Publish("sensor/instruction", 0, false, message)
token.Wait()
}
Currently I am publishing two JSON objects to be received. I think part of the problem is distinguishing between the two objects. This is the error I am getting panic: json: cannot unmarshal object into Go value of type []main.Data
. These are the JSON objects I am publishing to a topic:
{'sensor': 'DHT22', 'temperature': [22.7, 22.7, 22.8], 'humidity': [51.9, 52.0, 52.0]}
{'actuator': 'arduinoLED', 'blink': ['false', 'false', 'false']}
The msg.Payload()
gives
{"sensor": "DHT22", "temperature": [22.9, 22.9, 22.9], "humidity": [50.9, 50.9, 50.9]}
{"actuator": "arduinoLED", "blink": ["false", "false", "false"]
These objects are published constantly in a loop. I want to unmarshal the first object and access a specific element.
Slice vs single object
You are declaring a slice:
var sensorData []Data
But then your payload is not an array but rather only one object:
{'sensor': 'DHT22', 'temperature': [22.7, 22.7, 22.8], 'humidity': [51.9, 52.0, 52.0]}
So that error message is telling you it cannot unmarshal a single object as a slice.
Try changing that declaration to:
var sensorData Data
Then, instead of iterating over it, you need to just check that one instance of the data.
Additional mismatches between the payload and the struct type
After that, you'll probably get another error since the temperature
and humidity
array seem to contain decimal numbers:
`{'sensor': 'DHT22', 'temperature': [22.7, 22.7, 22.8], 'humidity': [51.9, 52.0, 52.0]`
But you are declaring those as int
slices in your code:
Temp []int `json: "temperature"`
Hum []int `json: "humidity"`
So you'll need to change those to be []float64
Differentiating the two different payloads
About differentiating the two object types, it'd be better if you try to do that, but even if you don't, Go's json
library will ignore problems if field names do not match, so what will happen is that when de-serializing your actuator
payloads into Data
, all fields will have their default values, but no error will be received.
This check will probably throw a panic
, cause the array will be empty in that case:
if i.Temp[2] > 20
One "hacky" way of solving this issue would be to only process the data if the sensor
field is not a blank string.
Assuming you always receive a sensor name in the sensor
messages, the only case when that will result in an empty string is if you just processed one of the other messages.
There are two main reasons for your error One is you have float
value for temperature and humidity but you are passing slice of int
type Data struct {
Sensor string `json:"sensor"`
Temp []int `json: "temperature"` // this should be []float64
Hum []int `json: "humidity"` // this should be []float64
}
Other is there are two objects in msg.Payload
which is not a slice of Data
struct. Working code.
package main
import (
"encoding/json"
"fmt"
)
type Data struct {
Sensor string `json:"sensor"`
Temp []float64 `json:"temperature"`
Hum []float64 `json:"humidity"`
}
func main() {
bytes := []byte(`{
"sensor": "DHT22",
"temperature": [22.7, 22.7, 22.8],
"humidity": [51.9, 52.0, 52.0]
}`)
var sensorData Data
if err := json.Unmarshal(bytes, &sensorData); err != nil {
fmt.Println(err)
}
fmt.Println(sensorData)
}
Check working code on Go playground