VizBlocks
VizBlocks.h
Go to the documentation of this file.
1 #ifndef VIZBLOCKS_H
2 #define VIZBLOCKS_H
3 #include "Arduino.h"
4 #include "Behaviours.h"
5 #include "ServoBehaviours.h"
6 #include "LEDBehaviours.h"
7 
8 #include <ESP8266WiFi.h>
9 #include "Adafruit_MQTT.h"
10 #include "Adafruit_MQTT_Client.h"
11 
12 #define MQTT_topic "new001"
13 #define NUM_BACKGROUND_BEHAVIOURS 5
14 
15 class VizBlocks {
16  char* _ssid;
17  char* _wifi_pass;
18  char* _id;
19  char* _server;
20  int _port;
21  BehaviourTable _behaviours;
22  Behaviour* _active = nullptr;
24  int _loop_time = 5;
25  Adafruit_MQTT_Client* _mqtt;
26  Adafruit_MQTT_Subscribe* _device_subscription;
27 
28  Adafruit_MQTT_Publish* _announce;
29  Adafruit_MQTT_Publish* _my_announce;
30  String _my_announce_channel;
31 
32  Adafruit_MQTT_Publish* _input;
33  Adafruit_MQTT_Publish* _my_input;
34  String _my_input_channel;
35 
36  WiFiClient* _client;
37  boolean _wifi;
38 
39  String capabilitiesJSON[50];
40 
41 public:
42  VizBlocks(char* id, char* ssid="VizBlocksNet", char* wifi_pass="VizBlocksAP",
43  char* server="172.20.10.8",int port=1883) : _id(id), _server(server), _port(port), _ssid(ssid), _wifi_pass(wifi_pass) {} ;
44 
45  void command_callback(char *data, uint16_t len) {
46  Serial.println("Got command: ");
47  Serial.println(data);
48  }
49 
50  void set_wifi(boolean v) { _wifi = v; }
51 
52  /*
53  * Set up the VizBlocks node - WiFi, MQTT
54  */
55  void init() {
56  Serial.setTimeout(100);
57  Serial.println();
58  Serial.println(F("VizBlocks Node starting up"));
59  Serial.println("Initialising " + String(_id));
60 
61  if( _wifi ) {
62 
63  WiFi.mode(WIFI_STA);
64  WiFi.setSleepMode(WIFI_NONE_SLEEP);
65 
66  // Connect to WiFi access point.
67  Serial.println();
68  Serial.print("Connecting to ");
69  Serial.println(_ssid);
70 
71  WiFi.begin(_ssid, _wifi_pass);
72  while (WiFi.status() != WL_CONNECTED) {
73  delay(500);
74  Serial.print(".");
75  }
76  Serial.println();
77 
78  Serial.println("WiFi connected");
79  Serial.println("IP address: "); Serial.println(WiFi.localIP());
80  // Done Wifi
81 
82  // Setup MQTT
83  _client = new WiFiClient();
84  _mqtt = new Adafruit_MQTT_Client(_client, _server, _port, "" /* mqttt username */, "" /* mqtt pass*/);
85  _device_subscription = new Adafruit_MQTT_Subscribe(_mqtt, _id);
86  _announce = new Adafruit_MQTT_Publish(_mqtt, "announce");
87  _my_announce_channel = String("announce/") + String(_id);
88  _my_announce = new Adafruit_MQTT_Publish(_mqtt, _my_announce_channel.c_str());
89 
90  // Setup MQTT subscription for this device
91  _mqtt->subscribe(_device_subscription);
92  // This *would* setup a callback, but we're not doing this right now...
93  //_device_subscription->setCallback(test_callback);
94 
95  MQTT_connect();
96  }
97 
100  Serial.println("Init finished");
101 
102  }
103 
104  /*
105  * Add a behaviour to the list of possible behaviours
106  */
107  void add(Behaviour *b) {
108  _behaviours.add(b);
109  }
110 
111  /*
112  * This is the main loop. It should be called from within loop() - really
113  * this function is the only thing you should need to call. It will manage
114  * it's own delay, so you can call as often as possible.
115  */
116  void run() {
117  int loop_start_time = millis();
118  serial_command();
119  if( _wifi ) { mqtt_command(); }
120  if( _active ) {
121  _active -> update();
122  if( ! _active->is_running() ) { _active = nullptr; }
123  }
124 
125  for (int i = 0; i < NUM_BACKGROUND_BEHAVIOURS; i++) {
126  if( _background[i] ) {
127  _background[i] -> update();
128  if( !_background[i] -> is_running() ) { _background[i] = nullptr; }
129  }
130  }
131 
132  int loop_time_taken = millis()-loop_start_time;
133  if( loop_time_taken < _loop_time ) {
134  delay( _loop_time - loop_time_taken );
135  }
136  }
137 
138  /*
139  * Read a command from the serial input and process it
140  */
141  void serial_command() {
142  if( Serial.available() ) {
143  String cmd = Serial.readStringUntil('\n');
144  cmd.replace("\r", "");
145  Serial.println(process(cmd));
146  }
147  }
148 
149  /*
150  * Read a command from the serial input and process it. It only waits for
151  * 50ms to allow other behaviours to continue.
152  */
153  void mqtt_command() {
154  MQTT_connect(); //ensure connection
155  Adafruit_MQTT_Subscribe *subscription;
156  while ((subscription = _mqtt->readSubscription(50))) {
157  if (subscription == _device_subscription) {
158  Serial.print(F("Got: "));
159  Serial.println((char *)_device_subscription->lastread);
160  Serial.println(process((char *)_device_subscription->lastread));
161  }
162  }
163  }
164 
165  /*
166  * Process a command. This means:
167  * - split the command name from the arguments
168  * - call process_command with the separated command and argument string
169  */
170  String process(String input) {
171 
172  if (!(input.indexOf("<") >= 0 || input.indexOf(">") >= 0 || input.indexOf("-") >= 0)) {
173  int index = input.indexOf(" ");
174  String command = "";
175  String args = "";
176 
177  if( index ) {
178  command = input.substring(0,index);
179  args = input.substring(index+1);
180  } else {
181  command = input;
182  }
183  return process_command(command, args);
184  }
185  }
186 
187  String input_event(String input) {
188  return process(input);
189  }
190 
191  /*
192  * Process a command and its arguments. This means:
193  * - look for a Behaviour with the right name
194  * - if found, then call that behaviour with the arguments (which are still a single string)
195  */
196  String process_command(String command, String args) {
197  Serial.println("Processing <"+command+"> <"+args+">");
198  Behaviour* b = _behaviours.get(command);
199  if(b) {
200  // Stop whatever behaviour is running in the active slot.
201  if( _active ) { _active->stop(); }
202  Serial.println( "Found behaviour: <"+command+">" );
203 
204  if ( b->is_background() ) {
205  // If this behaviour is already running in the background, refresh it and move on.
206  for (int i = 0; i < NUM_BACKGROUND_BEHAVIOURS; i++) {
207  if ( _background[i] ) {
208  if ( b->name() == _background[i]->name() ) {
209  return( b->start(args) );
210  }
211  }
212  }
213  // Else, if there is space for another background behaviour, start it running.
214  for (int i = 0; i < NUM_BACKGROUND_BEHAVIOURS; i++) {
215  if ( !_background[i] ) {
216  _background[i] = b;
217  return( b->start(args) );
218  }
219  }
220  return "Couldn't run command: " + command + ". Background behaviours full.";
221  }
222 
223  _active = b;
224  return( b->start(args) );
225  } else {
226  return "Couldn't process command: " + command;
227  }
228  }
229 
230  /*
231  * Function to connect and reconnect as necessary to the MQTT server.
232  */
233  // Should be called in the loop function and it will take care if connecting.
234  void MQTT_connect() {
235  int8_t ret;
236 
237  // Stop if already connected.
238  if (_mqtt->connected()) {
239  return;
240  }
241 
242  Serial.print("Connecting to MQTT... ");
243 
244  uint8_t retries = 3;
245  while ((ret = _mqtt->connect()) != 0) { // connect will return 0 for connected
246  Serial.println(_mqtt->connectErrorString(ret));
247  Serial.println("Retrying MQTT connection in 5 seconds...");
248  _mqtt->disconnect();
249  delay(5000); // wait 5 seconds
250  retries--;
251  if (retries == 0) {
252  // basically die and wait for WDT to reset me
253  while (1);
254  }
255  }
256  Serial.println("MQTT Connected!");
257  }
258 
260  String head = "{\"id\":\"" + String(_id) + "\",\"Behaviour\":{";
261  for (int i = 0; i < _behaviours.get_num_behaviours(); i++) {
262  Behaviour* b = _behaviours.get_by_num(i);
263  String args = b->args();
264  String body = "\"name\":\"" + b->name() + "\",\"args\":\"" + args + "\"}}";
265  String str = head + body;
266  capabilitiesJSON[i] = str;
267  }
268  }
269 
270  void announce(String doc) {
271  if( _wifi ) { _my_announce->publish(doc.c_str()); }
272  Serial.print("-->");
273  Serial.println(doc);
274  }
275 
277  String doc;
278  if( _wifi ) {
279  if( ! _announce->publish(_id) ) { Serial.println("Couldn't make announcement"); }
280  }
281  for( int i = 0; i < _behaviours.get_num_behaviours(); i++ ) {
282  doc = capabilitiesJSON[i];
283  announce(doc);
284  }
285  }
286 
287  void setID(char* id) {
288  _id = id;
289  }
290 
291  char* getId() {
292  return _id;
293  }
294 
295 };
296 
297 /*
298  * These behaviours depend on VizBlocks class so they must be included after
299  * it has been defined.
300  */
301 #include "CommsBehaviours.h"
302 #include "ButtonBehaviours.h"
303 #include "PotentiometerBehaviours.h"
304 #include "RotaryEncoderBehaviours.h"
305 
306 #endif
BehaviourTable
Definition: Behaviours.h:51
CommsBehaviours.h
Behaviour::name
virtual String name()
Definition: Behaviours.h:28
VizBlocks::getId
char * getId()
Definition: VizBlocks.h:291
VizBlocks::process_command
String process_command(String command, String args)
Definition: VizBlocks.h:196
Behaviour::args
virtual char * args()
Definition: Behaviours.h:30
BehaviourTable::get_by_num
Behaviour * get_by_num(int n)
Definition: Behaviours.h:71
VizBlocks::mqtt_command
void mqtt_command()
Definition: VizBlocks.h:153
VizBlocks::MQTT_connect
void MQTT_connect()
Definition: VizBlocks.h:234
BehaviourTable::get
Behaviour * get(String n)
Definition: Behaviours.h:64
VizBlocks::generateCapabilitiesJSON
void generateCapabilitiesJSON()
Definition: VizBlocks.h:259
RotaryEncoderBehaviours.h
VizBlocks::input_event
String input_event(String input)
Definition: VizBlocks.h:187
Behaviour
Definition: Behaviours.h:4
VizBlocks::init
void init()
Definition: VizBlocks.h:55
Behaviour::start
virtual String start(String args)
Definition: Behaviours.h:32
VizBlocks::announce
void announce(String doc)
Definition: VizBlocks.h:270
VizBlocks::add
void add(Behaviour *b)
Definition: VizBlocks.h:107
ButtonBehaviours.h
Behaviour::stop
virtual void stop()
Definition: Behaviours.h:36
PotentiometerBehaviours.h
BehaviourTable::get_num_behaviours
int get_num_behaviours()
Definition: Behaviours.h:75
ServoBehaviours.h
VizBlocks::serial_command
void serial_command()
Definition: VizBlocks.h:141
NUM_BACKGROUND_BEHAVIOURS
#define NUM_BACKGROUND_BEHAVIOURS
Definition: VizBlocks.h:13
Behaviours.h
VizBlocks::command_callback
void command_callback(char *data, uint16_t len)
Definition: VizBlocks.h:45
VizBlocks
Definition: VizBlocks.h:15
VizBlocks::set_wifi
void set_wifi(boolean v)
Definition: VizBlocks.h:50
LEDBehaviours.h
Behaviour::is_running
virtual boolean is_running()
Definition: Behaviours.h:24
BehaviourTable::add
void add(Behaviour *b)
Definition: Behaviours.h:59
VizBlocks::announce_capabilities
void announce_capabilities()
Definition: VizBlocks.h:276
VizBlocks::VizBlocks
VizBlocks(char *id, char *ssid="VizBlocksNet", char *wifi_pass="VizBlocksAP", char *server="172.20.10.8", int port=1883)
Definition: VizBlocks.h:42
VizBlocks::run
void run()
Definition: VizBlocks.h:116
VizBlocks::process
String process(String input)
Definition: VizBlocks.h:170
Behaviour::is_background
virtual boolean is_background()
Definition: Behaviours.h:26
VizBlocks::setID
void setID(char *id)
Definition: VizBlocks.h:287