Newer
Older
#ifndef WALLVIS_H
#define WALLVIS_H
#include "Arduino.h"
#include "Behaviours.h"
#include "ServoBehaviours.h"
#include "LEDBehaviours.h"
#include <ESP8266WiFi.h>
#include "Adafruit_MQTT.h"
#include "Adafruit_MQTT_Client.h"
#define MQTT_topic "new001"
class WallVis {
char* _ssid;
char* _wifi_pass;
char* _id;
char* _server;
int _port;
Behaviour* _active = nullptr;
int _loop_time = 5;
Adafruit_MQTT_Client* _mqtt;
Adafruit_MQTT_Subscribe* _device_subscription;
Adafruit_MQTT_Publish* _announce;
Adafruit_MQTT_Publish* _my_announce;
String _my_announce_channel;
Adafruit_MQTT_Publish* _input;
Adafruit_MQTT_Publish* _my_input;
String _my_input_channel;
WiFiClient* _client;
Dave Murray-Rust
committed
boolean _wifi;
public:
WallVis(char* id, char* ssid="WallVisNet", char* wifi_pass="wallvisAP",
char* server="172.20.10.8",int port=1883) : _id(id), _server(server), _port(port), _ssid(ssid), _wifi_pass(wifi_pass) {} ;
void command_callback(char *data, uint16_t len) {
Serial.println("Got command: ");
Serial.println(data);
}
/*
* Set up the WallVis node - WiFi, MQTT
*/
Serial.setTimeout(100);
Serial.println();
Serial.println(F("WallVis Node starting up"));
Serial.println("Initialising " + String(_id));
Dave Murray-Rust
committed
if( _wifi ) {
Dave Murray-Rust
committed
WiFi.mode(WIFI_STA);
Dave Murray-Rust
committed
// Connect to WiFi access point.
Serial.println();
Serial.print("Connecting to ");
Serial.println(_ssid);
Dave Murray-Rust
committed
WiFi.begin(_ssid, _wifi_pass);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println();
Serial.println("WiFi connected");
Serial.println("IP address: "); Serial.println(WiFi.localIP());
// Done Wifi
// Setup MQTT
_client = new WiFiClient();
_mqtt = new Adafruit_MQTT_Client(_client, _server, _port, "" /* mqttt username */, "" /* mqtt pass*/);
_device_subscription = new Adafruit_MQTT_Subscribe(_mqtt, _id);
Dave Murray-Rust
committed
_announce = new Adafruit_MQTT_Publish(_mqtt, "announce");
_my_announce_channel = String("announce/") + String(_id);
_my_announce = new Adafruit_MQTT_Publish(_mqtt, _my_announce_channel.c_str());
_input = new Adafruit_MQTT_Publish(_mqtt, "input");
_my_input_channel = String("input/") + String(_id);
_my_input = new Adafruit_MQTT_Publish(_mqtt, _my_input_channel.c_str());
Dave Murray-Rust
committed
// Setup MQTT subscription for this device
_mqtt->subscribe(_device_subscription);
// This *would* setup a callback, but we're not doing this right now...
//_device_subscription->setCallback(test_callback);
MQTT_connect();
}
announce_capabilities();
/*
* Add a behaviour to the list of possible behaviours
*/
void add(Behaviour *b) {
_behaviours.add(b);
}
/*
* This is the main loop. It should be called from within loop() - really
* this function is the only thing you should need to call. It will manage
* it's own delay, so you can call as often as possible.
*/
void vis_loop() {
int loop_start_time = millis();
serial_command();
Dave Murray-Rust
committed
if( _wifi ) { mqtt_command(); }
//Serial.println("Updating "+_active->name());
_active -> update();
if( ! _active->is_running() ) _active = nullptr;
}
int loop_time_taken = millis()-loop_start_time;
if( loop_time_taken < _loop_time ) {
delay( _loop_time - loop_time_taken );
}
}
/*
* Read a command from the serial input and process it
*/
void serial_command() {
if( Serial.available() ) {
String cmd = Serial.readStringUntil('\n');
Serial.println(process(cmd));
}
}
/*
* Read a command from the serial input and process it. It only waits for
* 50ms to allow other behaviours to continue.
*/
void mqtt_command() {
MQTT_connect(); //ensure connection
Adafruit_MQTT_Subscribe *subscription;
while ((subscription = _mqtt->readSubscription(50))) {
if (subscription == _device_subscription) {
Serial.print(F("Got: "));
Serial.println((char *)_device_subscription->lastread);
Serial.println(process((char *)_device_subscription->lastread));
}
}
}
/*
* Process a command. This means:
* - split the command name from the arguments
* - call process_command with the separated command and argument string
String process(String input) {
int index = input.indexOf(" ");
String command = "";
String args = "";
if( index) {
command = input.substring(0,index);
args = input.substring(index+1);
} else {
command = input;
}
return process_command(command, args);
}
String input_event(String input) {
return process(input);
}
/*
* Process a command and its arguments. This means:
* - if found, then call that behaviour with the arguments (which are still a single string)
*/
String process_command(String command, String args) {
Serial.println("Processing <"+command+"> <"+args+">");
Behaviour* b = _behaviours.get(command);
if(b) {
if( _active ) { _active->stop(); }
Serial.println( "Found behaviour: <"+command+">" );
_active = b;
return( b->start(args) );
} else {
return "Couldn't process command: " + command;
}
}
/*
* Function to connect and reconnect as necessary to the MQTT server.
*/
// Should be called in the loop function and it will take care if connecting.
void MQTT_connect() {
int8_t ret;
// Stop if already connected.
if (_mqtt->connected()) {
return;
}
Serial.print("Connecting to MQTT... ");
uint8_t retries = 3;
while ((ret = _mqtt->connect()) != 0) { // connect will return 0 for connected
Serial.println(_mqtt->connectErrorString(ret));
Serial.println("Retrying MQTT connection in 5 seconds...");
_mqtt->disconnect();
delay(5000); // wait 5 seconds
retries--;
if (retries == 0) {
// basically die and wait for WDT to reset me
while (1);
}
void generateCapabilitiesJSON() {
String head = "{\"name\":\"" + String(_id) + "\",\"behaviour\":{";
for (int i = 0; i < _behaviours.get_num_behaviours(); i++) {
Behaviour* b = _behaviours.get_by_num(i);
String args = b->args();
String body = "\"index\":\"" + String(i) + "\",\"name\":\"" + b->name() + "\",\"args\":\"" + args + "\"}}";
String str = head + body;
capabilitiesJSON[i] = str;
}
}
void announce_capabilities() {
Dave Murray-Rust
committed
if( _wifi ) {
if( ! _announce->publish(_id) ) { Serial.println("Couldn't make announcement"); }
}
for( int i = 0; i < _behaviours.get_num_behaviours(); i++ ) {
Dave Murray-Rust
committed
if( _wifi ) { _my_announce->publish(doc.c_str()); }
Serial.print("-->");
Serial.println(doc);
void input_publish(String doc) {
if( _wifi ) {
if( ! _input->publish(_id) ) { Serial.println(String("Failed to publish <") + String(doc) + String("> to input/") + String(_id)); }
}
if( _wifi ) { _my_input->publish(doc.c_str()); }
Serial.print("-->");
Serial.println(doc);
}
void setID(char* id) {
_id = id;
}
class SendCapabilities : public Behaviour {
/*
* Class that defines a behaviour that publishes a
* ButtonPressed message to the input topic of the
* MQQT broker
*/
WallVis* _node;
public:
SendCapabilities(WallVis* node, String name = "SendCapabilities") :
Behaviour(name), _node(node){ }
String start(String args) {
//This is where you do your stuff for a simple behaviour
_node->announce_capabilities();
return "SendCapabilities behaviour " + _name;
}
};
/*
* Button Behaviours depend on WallVis class so they must be included after
* it has been defined.
*/
#include "ButtonBehaviours.h"
#include "PotentiometerBehaviours.h"
#include "RotaryEncoderBehaviours.h"
/* Left here temporarily - can't use a member function as a callback
So this was the start of a way to create a static function that would work
void* current_wallvis;
void test_callback(char *data, uint16_t len) {
Serial.println("Callback: ");
Serial.println(data);
((WallVis*)current_wallvis)->process(String(data));
}
*/