В предыдущем посте мы показали, как отправлять и получать сообщения с устройств IoT с помощью брокера MQTT. В этом посте мы распространим эту идею на реальный пример.
Предположим, у вас есть устройство Интернета вещей, которое измеряет температуру и влажность в теплице (Нетрудно сделать его с помощью Raspberry Pi или Arduino).
Мы хотим отслеживать условия в теплице удаленно с другого компьютера или, возможно, с помощью централизованной службы регистрации. В предыдущем посте мы показали реализацию кода для отправки сообщений на Go, поэтому мы расширим этот пример.
Вместо того, чтобы просто отправлять строку «температура — x, влажность — y», давайте определим структуру сообщения и устройства. Предположим, у вас есть (или вы хотите добавить в будущем) устройство для мониторинга влажности или осадков, и вы также хотите подключить его.
Чтобы оставить открытой возможность использования нескольких устройств и типов, нам нужна модель данных, способная справиться с этим.
type Message struct { Time time.Time `json:"time"` Device Device `json:"device"` } type Device interface { ID() string Name() string } type TempRHDevice struct { Id string `json:"id"` DeviceName string `json:"name,omitempty"` Temp float32 `json:"temp,omitempty"` Rh float32 `json:"rh,omitempty"` } func (t TempRHDevice) ID() string { return t.Id } func (t TempRHDevice) Name() string { return t.DeviceName }
Структура сообщения — это то, что будет отправлено брокеру MQTT. Мы создали интерфейс для обработки общих атрибутов наших устройств Интернета вещей и абстрагирования деталей конкретных устройств.
TempRHDevice — это наше устройство, которое измеряет температуру и влажность. Он реализует интерфейс устройства, поэтому его можно использовать в сообщении.
Далее нам нужно отправить сообщение брокеру. В этом примере мы будем использовать формат JSON для его простоты. Обратите внимание, что в крупномасштабной системе с тысячами и более устройствами может потребоваться использовать более компактный формат.
message := generateRandomMessage() payload, err := json.Marshal(message) if err != nil { panic(err) } token := client.Publish(topic, 0, false, payload)
Go упрощает маршалинг в JSON. После маршалирования сообщение json отправляется брокеру.
Что еще мы будем делать с данными, когда они у нас появятся: сохранить их в базе данных, отобразить на информационной панели, проверить значения на предмет тревожных состояний. Нам нужно преобразовать json, чтобы его можно было использовать.
На принимающей стороне нам просто нужно демаршалировать json в структуру. Мы будем использовать структуру, аналогичную той, которая используется на отправляющей стороне; но нам нужен способ демаршалинга в конкретный тип, а не интерфейс устройства в сообщении. Мы добавим собственный метод демаршалинга в Message, чтобы сделать код немного чище
type rawMessage struct { Time time.Time `json:"time"` Device TempRHDevice `json:"device"` } func (m *Message) UnmarshalJSON(data []byte) error { var raw rawMessage if err := json.Unmarshal(data, &raw); err != nil { return err } m.Time = raw.Time m.Device = &raw.Device return nil } ... func processMsg(ctx context.Context, .... ... case msg, ok :=Здесь уместно отметить, что этот метод усложняется при добавлении большего количества типов устройств. Например, как метод UnmarshalJSON узнает, какой тип устройства содержит сообщение. Мы могли бы использовать хитрую логику в UnmarshalJSON, чтобы определить тип.
В качестве альтернативы помните, что MQTT можно использовать для публикации в нескольких темах, и общепринятой практикой является использование иерархического соглашения об именовании тем. Таким образом, в случае нескольких типов устройств в примере с теплицей рекомендуется публиковать разные типы устройств в разных темах. Именно так мы и будем решать эту проблему в дальнейшем по мере добавления новых типов устройств.
В примере с теплицей названия тем могут быть структурированы следующим образом:
/greenhouse/temprh/deviceid /greenhouse/moisture/deviceidВ MQTT мы можем подписаться на темы, используя тему с подстановочным знаком, например:
if token := client.Subscribe("/greenhouse/#", 0, nil); token.Wait() && token.Error() != nil { fmt.Println(token.Error()) os.Exit(1) }который будет соответствовать всем устройствам в пространстве имен парника. тогда нам просто нужно будет добавить логику в процессMsg(), чтобы просмотреть тему входящего сообщения и знать, как его демаршалировать.
Теперь, когда у нас есть сообщение устройства в удобной для использования форме, что с ним делать? В следующем посте этой серии мы продемонстрируем наш подход к сохранению сообщения в PostGres.
Как обычно, полный исходный код отправителя можно найти здесь, а код подписчика — здесь.
Поделитесь своим мнением в комментариях.
Спасибо!
Отказ от ответственности: Все предоставленные ресурсы частично взяты из Интернета. В случае нарушения ваших авторских прав или других прав и интересов, пожалуйста, объясните подробные причины и предоставьте доказательства авторских прав или прав и интересов, а затем отправьте их по электронной почте: [email protected]. Мы сделаем это за вас как можно скорее.
Copyright© 2022 湘ICP备2022001581号-3