Simple message format for IoT telemetry
During the development of various devices, the question arises about the data transmission format.
For me, basically - almost all IoT and IIoT transfers data via HTTP.
Over the years, I have developed a very simple protocol that fulfills some requirements:
- Simple to implement
- Simple to parse
- Simple to debug
- Transfer protocol independent
- Support meta information (timestamp, etc..)
- Passing many values in one message
Many devices have no idea what time it is, so the message format does not require this field.
In order to at least somehow identify the device, the device
field is required. Authentication is carried out to the transport level.
Devices that can buffer messages (for example, try to send later), a timestamp
field is added to the metadata in which a unix timestamp is written.
Message format
Simple plain:
<device-id>|<key>=<value>|<key2>=<value2>
Simple plain with meta:
<device-id>;<meta-key>=<meta-value>,<meta-key2>=<meta-value2>|<key>=<value>|<key2>=<value2>
In JSON:
{
"device": "<device-id>",
"meta": {
"<meta-key>": "<meta-value>"
},
"data": {
"<key>": "<value>"
}
}
Example
Plain:
esp8266-<mac-address>;model=esp8266-ws001,sw-version=11,wifi-ssid=<WIFI Name>|temperature__c=25.5|humidity=50|pressure__hpa=760|altitude=100
JSON:
{
"device": "rpi-<mac-address>",
"meta": {
"model": "rpi-ws001",
"sw-version": "22",
"wifi-ssid": "<WIFI Name>"
},
"data": {
"temperature__c": "25.5",
"humidity": "50",
"pressure__hpa": "760",
"altitude": "100"
}
}
Server code
Example server code for parse telemetry message:
type Message struct {
Device string `json:"device"`
Meta map[string]string `json:"meta,omitempty"`
Data map[string]string `json:"data,omitempty"`
}
func parseMessageKV(text, delimiter string) map[string]string {
data := make(map[string]string)
for _, kv := range strings.Split(text, delimiter) {
if strings.Contains(kv, "=") {
kvmap := strings.SplitN(kv, "=", 2)
data[kvmap[0]] = kvmap[1]
}
}
return data
}
func ParseMessage(payload []byte) (*Message, error) {
msg := &Message{}
if text := string(payload); strings.Contains(text, "|") {
data := strings.SplitN(text, "|", 2)
deviceInfo := data[0]
if strings.Contains(deviceInfo, ";") && strings.Contains(deviceInfo, ",") && strings.Contains(deviceInfo, "=") {
deviceInfoMeta := strings.Split(deviceInfo, ";")
msg.Device = deviceInfoMeta[0]
msg.Meta = parseMessageKV(deviceInfoMeta[1], ",")
} else {
msg.Device = deviceInfo
}
msg.Data = parseMessageKV(data[1], "|")
} else {
if err := json.Unmarshal(payload, &msg); err != nil {
return nil, err
}
}
return msg, nil
}