This is a documentation file for the basics of using a microcontroller to connect to the server.
To run this an auth server for microcontrollers needs to be started. In the development environment this can be via running the python mock server script.
After running python bin/mock-server.py
a mock server running on http://127.0.0.1:5000
will be started. This gives basic information mostly made for a singular device currently.
Make sure that the environment variable for the auth server is set to the correct mock address if this is the way it's being used.
The microcontrollers are communicating with the servers via websockets. The path for the microcontroller endpoint is: /microcontrollers/websocket
.
Given the following Caddy (a proxy server) configuration:
my.subdomain {
encode gzip
reverse_proxy 127.0.0.1:4000
}
The endclient would have to connect via this URL: wss://my.subdomain/microcontrollers/websocket
. Note, while not using SSL the URI would start with ws://
.
For the microcontroller to actually authenticate with the websocket connection, a custom header needs to be sent. The header in question is: X-API-Key: api_token
. The API token is present on the authentication server and if it's correctly communicated, the authentication server will send the required information to continue with the connection.
To see the exact specs of the authentication key take a look at the documentation page.
When the device authenticates and connects to the server, a message is sent to it on the connect event. The message in question is:
{
"ref": null,
"payload": {
"user_id": 1,
"location_id": 2,
"controller_id": 3
},
"topic": "microcontroller:3",
"event": "metadata",
"join_ref": null
}
While official documentation doesn't exist as to how to communicate with Phoenix when not using a JavaScript library there is, thankfully, this blogpost about it.
In a nutshell the websocket needs to send a message to the Phoenix server every minute. This message should take have this format:
{
"topic": "phoenix",
"event": "heartbeat",
"payload": {},
"ref": 0
}
topic
: Usually this is the room the event relates to.event
: This defines which handler will get invoked on the server side (or potentially client-side if going the other direction). There are some built-in events mostly prefixed with phx_.payload
: The actual data associated with the event. For some events (like phx_join) the payload is ignored.ref
: Just an idenfifier for the message. When you get back a reply it will have the same ref value as the event that it is replying to. Since channels are asynchronous you could quickly send two events before receiving a reply and you would need to use ref to know which event it relates to. In my examples I have hard coded ref to 0 but in reality you probably want a counter and some helper function to get the next reference number (or use a uuid).
To join the rquired microcontroller channel for the v1
version of the API, the following data needs to be sent to the server:
{
"topic": "microcontroller:v1:$controller_id",
"event": "phx_join",
"payload": {},
"ref": 0,
"join_ref": 0
}
After the controller has joined the channel, it can send its' reading information in the following format:
{
"topic": "microcontroller:v1:$controller_id",
"event": "upload_readings",
"payload": {
"$sensor-id": [
{
"type": "$reading_type",
"value": "$value"
},
{
"type": "$reading_type",
"value": "$value"
}
]
},
"ref": 0,
"join_ref": 0
}
To receive informations as to how to control a motor the following inforamtion needs to be intercepted:
{
"topic": "microcontroller:v1:$controller_id",
"event": "control_update",
"payload": {
"control": [
{
"$control_id": $control_value
}
]
}
}