Things

Thing

A Thing is the basic construct in Merle and comprises three, um, things:

  • Your platform, such as a Raspberry Pi, with I/O peripherals

  • The Merle Framework

  • Your HTML, JavaScript, and Go code

Thing Identification

All things have an ID, a model, and a name. The ID uniquely distinguishes one Thing from another. The model groups like Things.

Thing Anatomy

A Thing is an application with a main loop, a message bus, and several interfaces.

Main Loop

Thing main loop runs forever. In the main loop:

  • Initialize the platform and I/O resources

  • Monitor and service those I/O resources

Message Bus

Thing message bus is a broadcast domain, in which all connections to the bus can reach each other by broadcast.

A Thing defines a set of JSON-encoded bus messages. Here's an example message in Go and in JavaScript:

Messages are Go structs with exported members....


type MsgIdentity struct {

Msg string

Id string

Model string

Name string

Online bool

StartupTime time.Time

}

And encoded on the wire as JSON...


{

"Msg":"_ReplyIdentity",

"Id":"dc_a6_32_7a_a6_d0",

"Model":"bmp180",

"Name":"bumpy",

"Online":true,

"StartupTime":"2022-07-07T21:35:09.304426527+01:00"

}

I/O

I/O is any Thing-connected peripheral or resource we want Thing to monitor and control.

Thing API

Thing API is a WebSocket interface. Any WebSocket client can connect to Thing using standard WebSocket calls, and the WebSocket in turn is connected to Thing's message bus.

Web Server

Thing is a web server, serving up the Thing's user interface over HTTP and HTTPS.

Thing's user interface (UI) is a single HTML page. The UI is designed to be a Single-page application (SPA). The single-page part is obvious. The application part of SPA is JavaScript opening a WebSocket back to Thing, giving UI access to Thing API. That probably sounds like gobbledygook. There are two steps. First, a web browser requests Thing's page via HTTP and renders the HTML. Second, embedded JavaScript opens a WebSocket back to Thing, turning the interface from HTTP to Thing API. Now JavaScript in the browser has full access to Thing API and can sync the UI with Thing's state.

Each HTTP connection to the web server creates a new WebSocket API connection back to Thing's message bus. For every HTTP connection, the UIs are synchronized; all browser views of Thing are synchronized. Everyone see the same thing at the same time, ignoring message latencies. This is true when browsing Thing locally (localhost) or browsing over the Internet, which we'll talk about next with Thing Prime.

Thing Prime

If Thing has a public routable IP address on the Internet, then we can access Thing directly over the Internet using HTTP/HTTPS. Congratulations, your Thing is on the Internet.

However, it's common that most Things will not have a public routable IP address because they're at the edge, stuck behind NAT or carrier-grade NAT or something which hides Things from the Internet. In other words, Thing can ping to the Internet, but Thing cannot be pinged from the Internet.

To work around this limitation, we introduce Thing Prime. Thing Prime is a clone of Thing running on another platform, typically a VM, that is addressable from the Internet. Thing Prime is a proxy for Thing. Thing Prime is the same code base as Thing so there is no additional code to write to create Thing Prime; once you write Thing, you have written both.

Thing and Thing Prime connect to each in two steps. First, a secure SSH tunnel is created from Thing to Thing Prime. Second, Thing Prime opens a WebSocket back to Thing to connect to Thing API. Now Thing Prime and Thing are on the same message bus.

Thing Prime does differ from Thing is one important aspect: Thing Prime is not running on the Thing platform so Thing Prime does not have direct access to Thing's I/O and does not have a main loop. However, Thing Prime's web server serves up the same Single-Page Application as Thing. So now, one can browse Thing Prime on the Internet and see the same user interface and state as when browsing Thing on the local network.

Note when browsing Thing Prime over HTTPS, TLS secures the connection. The connection between Thing Prime and Thing is secured, as previously mentioned, with a SSH tunnel. So we have end-to-end security between the Thing endpoint and the browser on the Internet.

Mother

Every Thing has a mother. A mother is also a Thing. A child Thing has a connection to its mother, the mother Thing has a connection to its mother, and so on. With this mother/child construct, we can create an hierarchy of Things or a network of Things. Some examples of motherly Things are bridges, hubs, and controllers. We'll discuss those motherly Things in the subsequent sections.

In Merle, the connection between Thing and mother is via Thing Prime. Thing Prime is like a part of Thing still in the mother. In biology, there is this concept of microchimera where cells of the child remain with the mother after birth and stick around for several decades (weird, huh?). Thing Prime is akin to those child cells still in the mother.

The mother, being a Thing, has a main loop and a message bus. Child connect and disconnect messages are generated on the mother bus when a child connects or disconnects. The mother also decides which Things can connect. In the example above, we have two orange Things and one yellow Thing connected. The mother prevented a green Thing from connecting, maybe because the green Thing didn't have the correct SSH key or the mother isn't connecting green Things.

The Thing-to-mother connection is always secured by an SSH tunnel.

Bridges

Every mother Thing has a bridge message bus, and each child Thing is connected on the bridge bus (via Thing Prime). The bridge bus is a broadcast domain, in which all child Things can reach each other by broadcast. The mother controls the forwarding of messages on the bridge bus, AKA the forwarding rules, to children.

Let's simplify the diagramming and show a mother Thing with children:

The mother (being a Thing) has a UI, and thru the mother's UI, each child UI is available as /<child_id>.

Now, let's define three types of bridges based on bridge bus forwarding rules:

  • A strict bridge implementation would broadcast all incoming packets to all other children, maximally connecting all children. The CAN Bus Bridge example broadcasts all incoming CAN bus messages to all other children.

  • A hub is a bridge with a single forwarding rule to drop all incoming packets. That doesn't seem very useful because the children are cut off from messaging each other, but the children have a trick up their sleeve: each child Thing Prime has a full UI accessible thru the mother's web server, so we can see what each child is up to from one central location, the hub. Check out the hub example.

  • A controller is a bridge that is a hybrid of a strict bridge and a hub. A controller's forwarding rules implement controller logic, directing traffic between children or sending new messages to children. The Thermostat Controller example bridges two children, one sensing the temperature, and the other controlling a relay to a furnace or air conditioner. The controller toggles relays based on the temperature reading.