Skip to content

Instantly share code, notes, and snippets.

@zastrixarundell
Last active August 30, 2020 20:14
Show Gist options
  • Save zastrixarundell/96990f751fa9c36bbaaf483ac43487e9 to your computer and use it in GitHub Desktop.
Save zastrixarundell/96990f751fa9c36bbaaf483ac43487e9 to your computer and use it in GitHub Desktop.
Arduino Phoenix websockets client
#include <ESP8266WiFi.h>
#include <Config.h>
#include <PhoenixConnection.cpp>
// Values specific to the WiFi connection
const char* ssid = SSID;
const char* password = PASSWORD;
// Values specific to the WebSocket connection
const char* deviceId = DEVICE_ID;
const char* deviceName = DEVICE_NAME;
const char* websocketUrl = WEBSOCKET_URL;
const char* origin = ORIGIN;
PhoenixConnection connection(deviceId, deviceName, websocketUrl, origin);
// Toggle whether you want to use custom callbacks or the default ones.
#define CUSTOM false
void setup()
{
Serial.begin(115200);
delay(10);
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
WiFi.softAPdisconnect(true);
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.print("WiFi connected at: ");
Serial.print(WiFi.localIP());
pinMode(16, OUTPUT);
digitalWrite(16, HIGH);
#if CUSTOM == true
connection.setMessageCallback([](WebsocketsMessage message){
Serial.print("Hey, I got a new message: ");
Serial.println(message.data());
}).setJoinCallback([](){
Serial.print("So I just joined a new websocket channel with the name of: ");
Serial.println(deviceName);
}).setLeaveCallback([](){
Serial.println("Well, looks like I left!");
});
#endif
connection.connect().join();
}
void (* restart) (void) = 0;
void loop()
{
connection.poll();
connection.sendHeartbeat();
if(!connection.isConnected())
restart();
}
#include "PhoenixConnection.hpp"
PhoenixConnection::PhoenixConnection(const char* deviceId, const char* deviceName, const char* websocketUrl, const char* origin)
{
this -> deviceId = deviceId;
this -> deviceName = deviceName;
this -> websocketUrl = websocketUrl;
setDefaultJoinCallback();
setDefaultLeaveCallback();
setDefaultMessageCallback();
setEventCallback();
client.addHeader("Origin", origin);
}
PhoenixConnection& PhoenixConnection::connect()
{
client.connect(websocketUrl);
lastHeartBeat = millis();
return *this;
}
PhoenixConnection& PhoenixConnection::join()
{
const char *join_template = "{\"topic\":\"controller:%s\",\"event\":\"phx_join\",\"payload\":{\"name\":\"%s\"},\"ref\":0}";
char *result = static_cast<char*>(malloc(strlen(join_template) + strlen(deviceId) + strlen(deviceName) + 1 - 4));
sprintf(result, join_template, deviceId, deviceName);
client.send(result);
free(result);
lastHeartBeat = millis();
return *this;
}
PhoenixConnection& PhoenixConnection::setJoinCallback(const EmptyMethod& joinCallback)
{
this -> joinCallback = joinCallback;
return *this;
}
PhoenixConnection& PhoenixConnection::setLeaveCallback(const EmptyMethod& leaveCallback)
{
this -> leaveCallback = leaveCallback;
return *this;
}
PhoenixConnection& PhoenixConnection::setMessageCallback(const PartialMessageCallback& messageCallback)
{
client.onMessage(messageCallback);
return *this;
}
boolean PhoenixConnection::isConnected()
{
return client.available(true);
}
void PhoenixConnection::sendHeartbeat()
{
if(isConnected() && (millis() - lastHeartBeat >= 10000))
{
client.send("{\"topic\":\"phoenix\",\"event\":\"heartbeat\",\"payload\":{},\"ref\":0}");
lastHeartBeat = millis();
}
}
void PhoenixConnection::sendPayload(const char* payload, const long ref)
{
const char *messageTemplate = "{\"topic\":\"controller:%s\",\"event\":\"update\",\"payload\":%s,\"ref\":%d}";
char* toSend = static_cast<char*>(malloc(strlen(messageTemplate) + strlen(deviceId) - 2 + strlen(payload) + (ref/10 + 1) - 2 + 1));
sprintf(toSend, messageTemplate, deviceId, payload, ref);
client.send(toSend);
free(toSend);
}
void PhoenixConnection::poll()
{
client.poll();
}
void PhoenixConnection::setDefaultMessageCallback()
{
client.onMessage([this](WebsocketsMessage message) {
Serial.print("Got text from the server: ");
Serial.println(message.data());
});
}
void PhoenixConnection::setDefaultJoinCallback()
{
joinCallback = [this](){
Serial.print("\n\n--- Connected to Phoenix channel as '");
Serial.print(deviceName);
Serial.print("' (");
Serial.print(deviceId);
Serial.println(") ---\n");
digitalWrite(STATUS_LED, LOW);
};
}
void PhoenixConnection::setDefaultLeaveCallback()
{
leaveCallback = [](){
Serial.println("\n\n--- Disconnected from Phoenix channel ---\n");
digitalWrite(STATUS_LED, HIGH);
};
}
void PhoenixConnection::setEventCallback()
{
client.onEvent([this](WebsocketsEvent event, String message) {
if(event == WebsocketsEvent::ConnectionOpened)
joinCallback();
else if(event == WebsocketsEvent::ConnectionClosed)
leaveCallback();
});
}
#ifndef PHOENIXCONNECTIONS_H
#define PHOENIXCONNECTIONS_H
#include <ESP8266WiFi.h>
#include <ArduinoWebsockets.h>
// https://github.com/gilmaimon/ArduinoWebsockets
#define STATUS_LED 16
using namespace websockets;
typedef std::function<void ()> EmptyMethod;
/*
C++ class representing the connection to the Phoenix channels.
It has default and custom behaviours.
*/
class PhoenixConnection
{
WebsocketsClient client;
const char *deviceId, *deviceName, *websocketUrl;
unsigned long lastHeartBeat;
EmptyMethod joinCallback, leaveCallback;
public:
PhoenixConnection(const char* deviceId, const char* deviceName, const char* websocketUrl, const char* origin);
PhoenixConnection& connect();
PhoenixConnection& join();
PhoenixConnection& setJoinCallback(const EmptyMethod& joinCallback);
PhoenixConnection& setLeaveCallback(const EmptyMethod& leaveCallback);
PhoenixConnection& setMessageCallback(const PartialMessageCallback& messageCallback);
boolean isConnected();
void sendHeartbeat();
void sendPayload(const char* payload, const long ref = 0);
void poll();
private:
void setDefaultMessageCallback();
void setDefaultJoinCallback();
void setDefaultLeaveCallback();
void setEventCallback();
};
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment