Using Enviro Grow (Pico W Abroad) to water my plants (Part II).

Part I is here.

The Plan

I’ve been thinking about MQTT, but I’m not sure it’s the right approach. I want to manage things online, which gives me two options (there are probably many more):

  1. Fetch next action: The board will call an API to ask “what should I do now?”. It’ll send moisture measurements occasionally and the API will decide what to do next (given time and measurements).
  2. Using MQTT, send measurements and listen to messages. The messages will be sent when something needs to happen (like watering). This means that the board can’t go to sleep.

In some ways, having a device listening to instruction feels nice - it’s a little robot and you tell it what to do. If I want it to water a plant, I just tell it to water a plant, and it waters a plant. So it’s nice.

But it does mean that scheduling is on me. It’s not hard though. Let’s see how hard it is to get MQTT to work with AWS IoT Core.

Note: It looks like the device can be offline using AWS IoT Device Shadow. I’ll investigate later, but for now I’ll do the simplest thing.

AWS IoT Core

To connect to AWS IoT, I need to use a client certificate and connect to port 8883. The endpoint is given by the command:

aws iot describe-endpoint --endpoint-type iot:Data-ATS

The Design

There should be a separate post about the design. We’ll leave it for now. Still iterating.

coreMQTT

This is a disappointing development. coreMQTT is a blocking client. It sends CONN and waits for CONNACK, but it waits in a loop. It tries to read until either:

  1. It times out, or
  2. If the timeout is zero, until it attempted several times and didn’t receive anything

This means that it doesn’t work with an asynchronous model, where it needs to relinquish control to allow the socket to receive some data.

Which means that if we want to use coreMQTT, we have to use the threads-safe method. Which makes me a bit sad. I’ll have a look at the lwIP MQTT implementation, but as far as I remember, it didn’t have the right interface.

Lessons:

  1. If the same data structure is accessed from the main thread and from a callback, make sure that you use a mutex. Yes, it’s basic, but initially I implemented a fixed-sized cyclic buffer with the assumption that everything will run in the main thread. Well, it didn’t, but I forgot about it.
  2. Read the documentation. If it says that an input to a function must be freed, then as weird as it sounds, it must be freed. Otherwise, memory runs out, and it just fails silently.

But, looks like that’s working now.