Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • design-informatics/vizblocks/vizblocksframework
  • jrevans/wallvisframework
2 results
Show changes
Commits on Source (65)
Showing with 3224 additions and 1446 deletions
#ifndef BEHAVIOUR_h
#define BEHAVIOUR_h
class Behaviour {
protected:
boolean _interruptable = true;
boolean _temp = false;
boolean _priority = false;
boolean _running = false;
boolean _background = false;
String _name = "name";
public:
Behaviour(String name) : _name(name) {};
~Behaviour() {};
//Can this behaviour be interruped
virtual boolean is_interruptable() { return _interruptable; };
//Can this behaviour be run quickly without stopping what's going on (e.g. comms, debug)
virtual boolean is_temp() { return _temp; };
//Should this behaviour override others
virtual boolean is_priority() { return _priority; };
//Is the behaviour running
virtual boolean is_running() { return _running; };
//What's the name of this behaviour
virtual boolean is_background() { return _background; };
//What's the name of this behaviour
virtual String name() { return _name; };
//What arguments does the behaviour take? Override this to document your behaviour
virtual char* args() { return "null"; };
//Start the behaviour, with arguments (don't know why this can't be virtual?)
virtual String start(String args) { Serial.println("Base start called <"+args+">"); };
//Update the behaviour periodically
virtual void update() { };
//Start the behaviour, with arguments (don't know why this can't be virtual?)
virtual void stop() { _running = false; };
};
/*
* Example way to make a simple behaviour
*/
class TestBehaviour : public Behaviour {
public:
TestBehaviour(String n) : Behaviour(n) {}
String start(String args) {
return "Test behaviour " + _name + " with (" + args + ")";
}
};
class BehaviourTable {
Behaviour* behaviours[40];
public:
int num = 0;
BehaviourTable() {}
void add(Behaviour *b) {
behaviours[num] = b;
num++;
}
Behaviour* get(String n) {
for( int i = 0; i < num; i++ ) {
if( behaviours[i]->name() == n) { return behaviours[i]; }
}
return nullptr;
}
Behaviour* get_by_num(int n) {
return behaviours[n];
}
int get_num_behaviours() {
return num;
}
};
#endif
#ifndef BUTTON_h
#define BUTTON_h
class Button {
private:
int _pin;
int _id;
bool _state;
volatile bool _inputFlag = false;
bool _changeFlag = false;
bool _pressedFlag = false;
bool _holdFlag = false;
unsigned long _previousTimer;
int _clickInterval = 1000;
int _holdInterval = 1000;
int _repeatInterval = 1000;
void (*_cb)(Button*, uint8_t, bool); // Callback function
bool _read() {
return digitalRead(_pin);
}
void _setClickInterval(int x) {
_clickInterval = x;
}
void _setHoldInterval(int x) {
_holdInterval = x;
}
void _setRepeatInterval(int x) {
_repeatInterval = x;
}
public:
// Public members:
static const uint8_t kEventPressed = 0; // Button was pressed
static const uint8_t kEventReleased = 1; // Button was released
static const uint8_t kEventClicked = 2; // Button was clicked (pressed and released within _clickInterval)
static const uint8_t kEventHeld = 3; // Button was held down for longer than _holdInterval
static const uint8_t kEventTick = 4; // Event released every _repeatInterval when button held
// Public functions:
Button(int pin, int id = 99) : _pin(pin), _id(id) {
pinMode(_pin, INPUT_PULLUP);
_state = _read();
_previousTimer = millis();
}
void initInterrupts(void(*function)()) {
attachInterrupt(_pin, function, CHANGE);
}
void setEventHandler(void(*function)(Button*, uint8_t, bool)) {
_cb = function;
}
bool getState() {
return _state;
}
int getId() {
return _id;
}
int getClickInterval() {
return _clickInterval;
}
int getHoldInterval() {
return _holdInterval;
}
int getRepeatInterval() {
return _repeatInterval;
}
void check() {
unsigned long timer = millis();
unsigned long deltaTime = timer - _previousTimer;
_state = _read();
if (_inputFlag == true) {
_inputFlag = false;
// Button pressed
if (_state == LOW
&& _pressedFlag == false) {
_pressedFlag = true;
_previousTimer = timer;
_cb(this, kEventPressed, _state);
return;
// Button clicked
} else if (_state == HIGH
&& deltaTime < _clickInterval
&& _holdFlag == false) {
_pressedFlag = false;
_previousTimer = timer;
_cb(this, kEventClicked, _state);
return;
// Button released
} else if (_state == HIGH) {
_pressedFlag = false;
_holdFlag = false;
_previousTimer = timer;
_cb(this, kEventReleased, _state);
return;
}
}
// Button held
if (_state == LOW
&& deltaTime > _holdInterval
&& _holdFlag == false) {
_holdFlag = true;
_previousTimer = timer;
_cb(this, kEventHeld, _state);
return;
// Button tick
} else if (_state == LOW
&& deltaTime > _repeatInterval
&& _holdFlag == true) {
_previousTimer = timer;
_cb(this, kEventTick, _state);
return;
}
}
void ICACHE_RAM_ATTR tick() {
_inputFlag = true;
}
};
#endif
#ifndef BUTTON_BEHAVIOUR_h
#define BUTTON_BEHAVIOUR_h
#include "Arduino.h"
#include "Behaviours.h"
#include <VizBlocks.h>
class ButtonPressed : public Behaviour {
/*
* Class that defines a behaviour that publishes a
* ButtonPressed message to the input topic of the
* MQQT broker
*/
VizBlocks* _node;
public:
ButtonPressed(VizBlocks* node, String name = "ButtonPressed") :
Behaviour(name), _node(node){ }
char* args() {return "<String buttonId>"; };
String start(String args) {
//This is where you do your stuff for a simple behaviour
String str = "{\"id\":\"" + String(_node->getId()) + "\",\"Input\":{\"type\":\"" + String(name()) + "\",\"button\":\"" + args + "\"}}";
_node->announce(str);
return "ButtonPressed behaviour " + _name + " with (" + args + ")";
}
};
class ButtonReleased : public Behaviour {
/*
* Class that defines a behaviour that publishes a
* ButtonReleased message to the input topic of the
* MQQT broker
*/
VizBlocks* _node;
public:
ButtonReleased(VizBlocks* node, String name = "ButtonReleased") :
Behaviour(name), _node(node){ }
char* args() {return "<String buttonId>"; };
String start(String args) {
//This is where you do your stuff for a simple behaviour
String str = "{\"id\":\"" + String(_node->getId()) + "\",\"Input\":{\"type\":\"" + String(name()) + "\",\"button\":\"" + args + "\"}}";
_node->announce(str);
return "ButtonReleased behaviour " + _name + " with (" + args + ")";
}
};
class ButtonClicked : public Behaviour {
/*
* Class that defines a behaviour that publishes a
* ButtonClicked message to the input topic of the
* MQQT broker
*/
VizBlocks* _node;
public:
ButtonClicked(VizBlocks* node, String name = "ButtonClicked") :
Behaviour(name), _node(node){ }
char* args() {return "<String buttonId>"; };
String start(String args) {
//This is where you do your stuff for a simple behaviour
String str = "{\"id\":\"" + String(_node->getId()) + "\",\"Input\":{\"type\":\"" + String(name()) + "\",\"button\":\"" + args + "\"}}";
_node->announce(str);
return "ButtonClicked behaviour " + _name + " with (" + args + ")";
}
};
class ButtonHeld : public Behaviour {
/*
* Class that defines a behaviour that publishes a
* ButtonLongPressed message to the input topic of the
* MQQT broker
*/
VizBlocks* _node;
public:
ButtonHeld(VizBlocks* node, String name = "ButtonHeld") :
Behaviour(name), _node(node){ }
char* args() {return "<String buttonId>"; };
String start(String args) {
//This is where you do your stuff for a simple behaviour
String str = "{\"id\":\"" + String(_node->getId()) + "\",\"Input\":{\"type\":\"" + String(name()) + "\",\"button\":\"" + args + "\"}}";
_node->announce(str);
return "ButtonHeld behaviour " + _name + " with (" + args + ")";
}
};
class ButtonTick : public Behaviour {
/*
* Class that defines a behaviour that publishes a
* ButtonRepeatPressed message to the input topic of the
* MQQT broker
*/
VizBlocks* _node;
public:
ButtonTick(VizBlocks* node, String name = "ButtonTick") :
Behaviour(name), _node(node){ }
char* args() {return "<String buttonId>"; };
String start(String args) {
//This is where you do your stuff for a simple behaviour
String str = "{\"id\":\"" + String(_node->getId()) + "\",\"Input\":{\"type\":\"" + String(name()) + "\",\"button\":\"" + args + "\"}}";
_node->announce(str);
return "ButtonTick behaviour " + _name + " with (" + args + ")";
}
};
#endif
#ifndef COMMS_BEHAVIOUR_h
#define COMMS_BEHAVIOUR_h
#include "Arduino.h"
#include "Behaviours.h"
#include <VizBlocks.h>
/*
* --------------------------------------------------
* ---------------- SendCapabilities ----------------
* --------------------------------------------------
*/
class SendCapabilities : public Behaviour {
VizBlocks* _node;
public:
SendCapabilities(VizBlocks* 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;
}
};
/*
* --------------------------------------------------
* ---------------------- Link ----------------------
* --------------------------------------------------
*/
class Link : public Behaviour {
VizBlocks* _node;
String _peerId;
const int _timeoutInterval = 5000;
unsigned long _t = 0;
public:
Link(VizBlocks* node, String name = "Link") : Behaviour(name), _node(node) { _background = true; }
char* args() {return "<String peerId>"; };
String start(String args) {
_running = true;
if (args == name() || args.indexOf(" ")>0) {
return "Invalid args (" + args + ") in behaviour " + name();
}
_t = millis();
if (args == _peerId) {
return "Link ping from (" + _peerId + ")";
}
_peerId = args;
String str = "{\"id\":\"" + String(_node->getId()) + "\",\"Link\":{\"peerId\":\"" + _peerId + "\"}}";
_node->announce(str);
return "New link with (" + _peerId + ")";
}
void update() {
if (millis() > (_t+_timeoutInterval)) {
String str = "{\"id\":\"" + String(_node->getId()) + "\",\"Unlink\":{\"peerId\":\"" + _peerId + "\"}}";
_node->announce(str);
_peerId = "";
_running = false;
}
}
};
/*
* --------------------------------------------------
* ------------------- PingServer -------------------
* --------------------------------------------------
*/
class PingServer : public Behaviour {
VizBlocks* _node;
String str;
const int _interval = 4000;
unsigned long _t = 0;
public:
PingServer(VizBlocks* node, String name = "PingServer") : Behaviour(name), _node(node) { _background = true; }
String start(String args) {
_background = true;
_running = true;
_t = millis();
str = "{\"id\":\"" + String(_node->getId()) + "\",\"PingServer\":{}}";
_node->announce(str);
return "Pinging server";
}
void update() {
if (millis() > (_t+_interval)) {
_t = millis();
_node->announce(str);
}
}
};
#endif
This diff is collapsed.
#ifndef LED_BEHAVIOUR_h
#define LED_BEHAVIOUR_h
#include "Arduino.h"
#include "Behaviours.h"
#include <Adafruit_NeoPixel.h>
class NumLEDs : public Behaviour {
Adafruit_NeoPixel* _strip;
uint32_t _color;
public:
NumLEDs(Adafruit_NeoPixel* strip, String name = "NumLEDs", uint32_t color=0xFFFFFFFF) :
Behaviour(name), _strip(strip), _color(color){ }
char* args() {return "<int num_leds>"; };
String start(String args) {
int val = args.toInt();
//Always clear the strip first
_strip->clear();
if( val > 0 ) {
_strip->fill(_color, 0, val);
}
_strip->show();
return "";
}
};
class BrightnessLEDs : public Behaviour {
Adafruit_NeoPixel* _strip;
uint32_t _hue;
uint32_t _sat;
public:
BrightnessLEDs(Adafruit_NeoPixel* strip, String name = "BrightnessLEDs", uint32_t hue=0, uint32_t sat=0) :
Behaviour(name), _strip(strip), _hue(hue), _sat(sat){ }
char* args() {return "<int brightness>"; };
String start(String args) {
int val = args.toInt();
_strip->clear();
_strip->fill(_strip->ColorHSV(_hue,_sat,val));
_strip->show();
return "";
}
};
class BreathingLEDs : public Behaviour {
Adafruit_NeoPixel* _strip;
uint _hue;
uint _sat;
int32_t _current = 0;
//Allows us to have slightly slower behaviours on the go...
int _factor = 4;
int _rate = 0;
int _direction = 1;
public:
BreathingLEDs(Adafruit_NeoPixel* strip, String name = "BreathingLEDs", uint32_t hue=0, uint32_t sat=0) :
Behaviour(name), _strip(strip), _hue(hue * 255), _sat(sat) { }
char* args() {return "<int rate (1-255ish)>"; };
String start(String args) {
_current = 0;
_direction = 1;
_running = true;
int val = args.toInt();
_rate = val;
return "";
}
void update() {
if( _rate <= 0 ) {
_strip->fill(0);
_strip->show();
return;
}
_current = _current + (_rate * _direction);
if( _current < 0 ) {
_current = 0;
_direction = 1;
}
if( _current > 255 * _factor ) {
_current = 255 * _factor;
_direction = -1;
}
_strip->fill(_strip->ColorHSV(_hue,_sat,_current / _factor));
_strip->show();
}
};
#endif
MIT License
Copyright (c) 2021 Design Informatics (Dave Murray-Rust, Evan Morgan, Joe Revans)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
#ifndef NAMEDICTIONARY_h
#define NAMEDICTIONARY_h
typedef struct {
String key;
String value;
} keyValuePair;
class NameDictionary {
keyValuePair data[50] = {
{"c2c373", "abe"},
{"c31d9d", "aja"},
{"c2b603", "ace"},
{"da58f5", "ali"},
{"da6195", "alf"},
{"da50d8", "amy"},
{"da5649", "ann"},
{"c2b2d6", "art"},
{"da516d", "aya"},
{"c2a597", "bea"},
{"da5331", "bev"},
{"8e07b", "bob"},
{"c318a1", "bud"},
{"da5a56", "deb"},
{"da4a70", "cal"},
{"c2c5c2", "cam"},
{"c2a5e6", "che"},
{"c2a23f", "dot"},
{"c2c415", "dan"},
{"c2bf2a", "dax"},
{"30d6ed", "fin"},
{"30d00b", "jim"},
{"1a6b16", "joe"},
{"8810e9", "kat"},
{"31054b", "kev"},
{"30ce48", "sam"},
{"30db8b", "tim"},
};
int _arraySize;
public:
NameDictionary() {
_arraySize = sizeof(data) / sizeof(keyValuePair);
};
String get(String key) {
for (int i = 0; i < _arraySize; i++) {
if (key == data[i].key) {
return data[i].value;
}
}
return key;
};
void list() {
for (int i = 0; i < _arraySize; i++) {
Serial.println(data[i].key + " : " + data[i].value);
}
};
void values() {
for (int i = 0; i < _arraySize; i++) {
Serial.println(data[i].value);
}
};
void keys() {
for (int i = 0; i < _arraySize; i++) {
Serial.println(data[i].key);
}
};
int length() {
return _arraySize;
}
};
#endif
#ifndef POTENTIOMETER_h
#define POTENTIOMETER_h
class Potentiometer {
int EMA_S = 0; //initialization of EMA S
float EMA_a = 0.6;
int _pin;
int _id;
int _value;
int _previousReading;
bool _inputFlag = false;
bool _changeFlag = false;
unsigned long _previousTimer;
int _interval = 200;
void (*_cb)(Potentiometer*, uint8_t, uint8_t); // Callback function
int _read() {
int sensorValue = analogRead(_pin); //read the sensor value using ADC
EMA_S = (EMA_a*sensorValue) + ((1-EMA_a)*EMA_S); //run the EMA
int mappedValue = map(EMA_S, 5, 1023, 0, 100);
return mappedValue;
}
void _setValue(int x) {
_value = x;
}
public:
static const uint8_t kEventStableUpdate = 0;
static const uint8_t kEventUnstableUpdate = 1;
Potentiometer(int pin, int id = 99) : _pin(pin), _id(id) {
pinMode(pin, INPUT);
EMA_S = analogRead(_pin); //set EMA S for t=1
_value = EMA_S;
_previousReading = EMA_S;
_previousTimer = millis();
_inputFlag = true;
};
void setEventHandler(void(*function)(Potentiometer*, uint8_t, uint8_t)) {
_cb = function;
}
int getValue() {
return _value;
}
int getId() {
return _id;
}
void check() {
unsigned long timer = millis();
unsigned long deltaTime = timer - _previousTimer;
int reading = _read();
int deltaValue = abs(reading - _value);
if (reading != _value) {
_inputFlag = true;
}
if (_inputFlag == true && deltaValue > 1) {
_inputFlag = false;
_changeFlag = true;
_previousTimer = timer;
_setValue(reading);
_cb(this, kEventUnstableUpdate, getValue());
}
if (_changeFlag == true && deltaTime > _interval) {
_changeFlag = false;
_cb(this, kEventStableUpdate, getValue());
}
}
};
#endif
#ifndef POTENTIOMETER_BEHAVIOUR_h
#define POTENTIOMETER_BEHAVIOUR_h
#include "Arduino.h"
#include "Behaviours.h"
#include <VizBlocks.h>
class PotentiometerUpdated : public Behaviour {
/*
* Class that defines a behaviour that publishes a
* ButtonPressed message to the input topic of the
* MQQT broker
*/
VizBlocks* _node;
public:
PotentiometerUpdated(VizBlocks* node, String name = "PotentiometerUpdated") :
Behaviour(name), _node(node){ }
char* args() {return "<String potentiometerId> <int value>"; };
String start(String args) {
//This is where you do your stuff for a simple behaviour
int index = args.indexOf(" ");
String pot = "";
String value = "";
if ( index > 0 ) {
pot = args.substring(0, index);
value = args.substring(index+1);
} else {
return "PotentiometerUpdated behaviour args error!";
}
String str = "{\"id\":\"" + String(_node->getId()) + "\",\"Input\":{\"type\":\"" + String(name()) + "\",\"pot\":\"" + pot + "\",\"value\":\"" + value + "\"}}";
_node->announce(str);
return "PotentiometerUpdated behaviour " + _name + " with (" + args + ")";
}
};
#endif
# VizBlocks Framework # VizBlocks Framework
This is an Arduino library that lets you easily create nodes to stick on the wall. Each node will listen on an MQTT channel, and respond to commands. So a node with a servo motor in might respond to: This is an Arduino library that lets you easily create nodes to stick on the wall. Each node will listen on an MQTT channel, and respond to commands. So a node with a servo motor in might respond to:
* `wiggle 30` - wiggle by 30 degrees a few times * `Wiggle 30` - wiggle by 30 degrees a few times
* `goto 20` - move to 20 degrees * `GoTo 20` - move to 20 degrees
These commands can be put in via NodeRED, or through the USB Serial connection for testing. There is a growing library of `Behaviours` that represent the different things the node might do. These commands can be put in via NodeRED, or through the USB Serial connection for testing. There is a growing library of `Behaviour`s that represent the different things the node might do.
## Installation [Find the project on GitLab here.](https://git.ecdf.ed.ac.uk/design-informatics/vizblocks/vizblocksframework)
One way to install this is put it in you personal Arduino libraries directory. On a mac, this is `~/Documents/Arduino/libraries`. It is best to check it out with a capitalised name, i.e. `VizBlocksFramework` not `VizBlocksframework`. To do this, you can use: ## Installation
One way to install this is put it in you personal Arduino libraries directory. On a mac, this is `~/Documents/Arduino/libraries`. It is best to check it out with a capitalised name, i.e. `VizBlocksFramework` not `vizblocksframework`. To do this, you can use:
`git clone https://git.ecdf.ed.ac.uk/design-informatics/VizBlocks/VizBlocksframework.git VizBlocksFramework` ```
git clone https://git.ecdf.ed.ac.uk/design-informatics/vizblocks/vizblocksframework.git VizBlocksFramework
```
The library will then appear in your list of Arduino libraries, along with examples. The library will then appear in your list of Arduino libraries, along with examples.
...@@ -24,12 +26,10 @@ This library depends on: ...@@ -24,12 +26,10 @@ This library depends on:
Because this is not a single sketch, development needs a bit of thinking about. It is probably easier to use another IDE - Atom is good! You will also have to keep pushing/pulling changes to make sure you have the latest version. Because this is not a single sketch, development needs a bit of thinking about. It is probably easier to use another IDE - Atom is good! You will also have to keep pushing/pulling changes to make sure you have the latest version.
# Usage # Usage
The library is designed to be used within normal Arduino sketches. It wraps up most of the business of connecting to MQTT etc. in a `VizBlock` class, and you then give it a bunch of `Behaviour`s that do interesting things.
The library is designed to be used within normal Arduino sketches. It wraps up most of the business of connecting to MQTT etc. in a `VizBlocks` class, and you then give it a bunch of `Behaviours` that do interesting things.
## Setup ## Setup
The node is set up with all of the connection information for the WiFi network and for NodeRE, e.g.: The node is set up with all of the connection information for the WiFi network and for Node-RED, e.g.:
```arduino ```arduino
#include <VizBlocks.h> #include <VizBlocks.h>
...@@ -40,10 +40,14 @@ VizBlocks node( ...@@ -40,10 +40,14 @@ VizBlocks node(
"VizBlocksAP", //WiFi Password "VizBlocksAP", //WiFi Password
"192.168.4.1",//IP address of Node RED server "192.168.4.1",//IP address of Node RED server
1883 //Port for Node RED server 1883 //Port for Node RED server
"", //MQTT username
"", //MQTT password
"" // SHA1 fingerprint (for mqtt server using SSL/TLS)
); );
``` ```
A very simple sketch would then simply initialise this, and call its main function in a loop: A very simple sketch would then simply initialise this, and call its main function in a loop:
``` ```
void setup() void setup()
{ {
...@@ -54,44 +58,41 @@ void setup() ...@@ -54,44 +58,41 @@ void setup()
void loop() void loop()
{ {
node.vis_loop(); node.run();
} }
``` ```
## Adding behaviours ## Adding behaviours
However, this would not do anything useful - it needs to know what to do. So we should add some `Behaviour`s. Each behaviour is one thing that the node knows how to do - set a number of LEDs to be active, move a servo to a certain angle, wiggle a servo for a bit etc. However, this would not do anything useful - it needs to know what to do. So we should add some `Behaviour`s. Each behaviour is one thing that the node knows how to do - set a number of LEDs to be active, move a servo to a certain angle, wiggle a servo for a bit etc.
Any number of behaviours can be added. They each need a unique name, so that they can be called correctly. Behaviours can also have parameters. In this example, we are adding three different behaviours, that all wiggle a servo. They have different speeds of wiggling, though (the last parameter) and different names. (`s1` is the Servo object they are working on). Any number of behaviours can be added. They each need a unique name, so that they can be called correctly. Behaviours can also have parameters. In this example, we are adding three different behaviours, that all wiggle a servo. They have different speeds of wiggling, though (the last parameter) and different names. (`s1` is the Servo object they are working on).
``` ```
node.add(new ServoWiggle(s1, "wiggle") ); node.add( new ServoWiggle(s1, "Wiggle") );
node.add(new ServoWiggle(s1, "slow_wiggle", 10) ); node.add( new ServoWiggle(s1, "Wiggle_Slow", 10) );
node.add(new ServoWiggle(s1, "fast_wiggle", 1) ); node.add( new ServoWiggle(s1, "Wiggle_Fast", 1) );
``` ```
Now, the node will respond to three commands - `wiggle`, `slow_wiggle` and `fast_wiggle`. Now, the node will respond to three commands - `Wiggle`, `Wiggle_Slow` and `Wiggle_Fast`.
You can see a full example of this in the `ExampleNode` sketch. You can see a full example of this in the `ServoMotorNode` sketch.
## Calling Behaviours ## Calling Behaviours
Behaviours can currently be called in two ways: Behaviours can currently be called in two ways:
* via Node RED, by sending a message on the channel. So, instead of sending '100' to make the node wiggle, now you have to sent 'wiggle 100'. * via Node RED, by sending a message on the channel. So, instead of sending '100' to make the node wiggle, now you have to send 'wiggle 100'.
* you can also do this using the Arduino serial monitor, to make it easier to test * you can also do this using the Arduino serial monitor, to make it easier to test
Each `Behaviour` has a unique name, and some number of parameters (mostly none, or one). Each `Behaviour` has a unique name, and some number of parameters (mostly none, or one).
# Behaviours # Behaviours
Behaviours are defined as classes. Each class has a unique name, and a constructor that takes several arguments. These are for things that should not change, e.g. the Servo object that is being wiggled. When the behaviour is called, it is called with arguments for *just that action*. So, for wiggling a servo, the constructor is: Behaviours are defined as classes. Each class has a unique name, and a constructor that takes several arguments. These are for things that should not change, e.g. the Servo object that is being wiggled. When the behaviour is called, it is called with arguments for *just that action*. So, for wiggling a servo, the constructor is:
`ServoWiggle( <servo_object>, <name>, <wiggle_speed>)` `ServoWiggle( <servo_object>, <name>, <wiggle_speed> )`
When the behaviour is then called, it is given the angle through which to wiggle, so the size of the wiggle can be controlled. So if the name is `wiggle`, it would be called by sending: When the behaviour is then called, it is given the angle through which to wiggle, so the size of the wiggle can be controlled. So if the name is `Wiggle`, it would be called by sending:
`wiggle 40` `Wiggle 40`
The current set of behaviours and their parameters are: The current set of behaviours and their parameters are:
...@@ -114,32 +115,25 @@ The next ones that are about to be written are something like the following: ...@@ -114,32 +115,25 @@ The next ones that are about to be written are something like the following:
_Please update this table as you generate new behaviours!_ _Please update this table as you generate new behaviours!_
## Creating behaviours ## Creating behaviours
At first - make behaviours with your sketch, while you are testing them. See the examples for this! At first - make behaviours with your sketch, while you are testing them. See the examples for this!
When you've got something stable, add it to the library. If it fits within an existing file, add it to that (e.g. if it's another thing tthat Servos do, add it to `ServoBehaviours.h`). If it's a new kind of thing, then create a new file and add it in there. (Make sure you do the #IFNDEF stuff - ask if you don't know what that is!). When you've got something stable, add it to the library. If it fits within an existing file, add it to that (e.g. if it's another thing that Servos do, add it to `ServoBehaviours.h`). If it's a new kind of thing, then create a new file and add it in there. (Make sure you do the #IFNDEF stuff - ask if you don't know what that is!).
## Helpful link:
## Some commands from the examples:
Node Red commands for Evan's blocks: Node Red commands for Evan's blocks:
Servos: Servos:
* `wiggle <int>` - int between 0 - 180 * `Wiggle <int>` - int between 0 - 180
* `slow_wiggle <int>` - int between 0 - 180 * `Slow_Wiggle <int>` - int between 0 - 180
* `fast_wiggle <int>` - int between 0 - 180 * `Fast_Wiggle <int>` - int between 0 - 180
* `goto <int>` - int between 0 - 180 * `GoTo <int>` - int between 0 - 180
* `drop` - behaviour that rotates from 0 to 180 and back again * `Drop` - behaviour that rotates from 0 to 180 and back again
LEDs: LEDs:
* `green_leds <int>` - turns LEDs green, int between 0 - 12 * `LightSome_Green <int>` - turns LEDs green, int between 0 - 12
* `red_leds <int>` - turns LEDs red, int between 0 - 12 * `LightSome_Red <int>` - turns LEDs red, int between 0 - 12
* `leds <int>` - turns LEDs white, int between 0 - 12 * `LightSome <int>` - turns LEDs white, int between 0 - 12
* `green <int>` - turns all LEDs green with set brightness, int between 0 - 100 ? * `LightAll_Green <int>` - turns all LEDs green with set brightness, int between 0 - 100 ?
* `red <int>` - turns all LEDs red with set brightness, int between 0 - 100 ? * `LightAll_Red <int>` - turns all LEDs red with set brightness, int between 0 - 100 ?
* `breathe <int>` - LEDs increase / decrease brightness with set speed, int between ? * `Breathe <int>` - LEDs increase / decrease brightness with set speed, int between ?
* `breathe_blue <int>` - blue LEDs increase / decrease brightness with set speed, int between ? * `Breathe_Blue <int>` - blue LEDs increase / decrease brightness with set speed, int between ?
## Helpful link:
https://docs.gitlab.com/ee/gitlab-basics/start-using-git.html
#ifndef ROTARYENCODER_h
#define ROTARYENCODER_h
class RotaryEncoder {
// Private members:
int _pinA;
int _pinB;
int _id;
volatile int _state[2];
volatile int _position;
volatile bool _inputFlag = false;
bool _changeFlag = false;
unsigned long _previousTimer;
int _interval = 200;
void (*_cb)(RotaryEncoder*, uint8_t, int); // Callback function
// {newPin2, newPin1, oldPin2, oldPin1}
int movements[5][4][4] = {
{ // No movement
{0, 0, 0, 0},
{0, 1, 0, 1},
{1, 0, 1, 0},
{1, 1, 1, 1}
},
{ // +1
{0, 0, 0, 1},
{0, 1, 1, 1},
{1, 0, 0, 0},
{1, 1, 1, 0}
},
{ // -1
{0, 0, 1, 0},
{0, 1, 0, 0},
{1, 0, 1, 1},
{1, 1, 0, 1}
},
{ // +2
{0, 0, 1, 1},
{1, 1, 0, 0}
},
{ // -2
{0, 1, 1, 0},
{1, 0, 0, 1}
},
};
// Private Functions:
void _setState(int a, int b) {
_state[0] = a;
_state[1] = b;
}
void _incrementPosition(int delta) {
_position = _position + delta;
}
int _findChange(int state1[2], volatile int state2[2]) {
int stateAppend[] = {state1[1], state1[0], state2[1], state2[0]};
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
if (_compareArrays(stateAppend, movements[i][j])) {
if (i == 0) {
return 0;
}
else if (i == 1) {
return 1;
}
else if (i == 2) {
return -1;
}
else if (i == 3) {
return 2;
}
else if (i == 4) {
return -2;
}
}
}
}
for (int i = 3; i < 5; i++) {
for (int j = 0; j < 2; j++) {
if (_compareArrays(stateAppend, movements[i][j])) {
if (i == 3) {
return 2;
}
else if (i == 4) {
return -2;
}
}
}
}
Serial.println("INVALID DATA");
return 0;
}
boolean _compareArrays(int a[4], int b[4]) {
if (a[0] != b[0]) {
return false;
}
if (a[1] != b[1]) {
return false;
}
if (a[2] != b[2]) {
return false;
}
if (a[3] != b[3]) {
return false;
}
return true;
}
public:
// Public members:
static const uint8_t kEventStableUpdate = 0;
static const uint8_t kEventUnstableUpdate = 1;
//Public Functions:
RotaryEncoder(int pinA, int pinB, int id = 99) : _pinA(pinA), _pinB(pinB), _id(id) {
pinMode(_pinA, INPUT_PULLUP);
pinMode(_pinB, INPUT_PULLUP);
_previousTimer = millis();
_setState(digitalRead(_pinA), digitalRead(_pinB));
setPosition(0);
}
void initInterrupts(void(*function)()) {
attachInterrupt(_pinA, function, CHANGE);
attachInterrupt(_pinB, function, CHANGE);
}
void setEventHandler(void(*function)(RotaryEncoder*, uint8_t, int)) {
_cb = function;
}
int getPostition() {
return _position;
}
int getId() {
return _id;
}
void setPosition(int value) {
_position = value;
}
void check() {
unsigned long timer = millis();
unsigned long deltaTime = timer - _previousTimer;
if (_inputFlag == true) {
_inputFlag = false;
_changeFlag = true;
_previousTimer = timer;
_cb(this, kEventUnstableUpdate, getPostition());
}
if (_changeFlag == true && deltaTime > _interval) {
_changeFlag = false;
_cb(this, kEventStableUpdate, getPostition());
}
}
void ICACHE_RAM_ATTR tick() {
int tempState[] = {digitalRead(_pinA), digitalRead(_pinB)};
int delta = _findChange(tempState, _state);
if (delta != 0) {
_incrementPosition(delta);
_inputFlag = true;
}
_setState(tempState[0], tempState[1]);
}
};
#endif
#ifndef ROTARYENCODER_BEHAVIOUR_h
#define ROTARYENCODER_BEHAVIOUR_h
#include "Arduino.h"
#include "Behaviours.h"
#include <VizBlocks.h>
class RotaryEncoderUpdated : public Behaviour {
/*
* Class that defines a behaviour that publishes a
* ButtonPressed message to the input topic of the
* MQQT broker
*/
VizBlocks* _node;
public:
RotaryEncoderUpdated(VizBlocks* node, String name = "RotaryEncoderUpdated") :
Behaviour(name), _node(node){ }
char* args() {return "<String rotaryEncoderId> <int position>"; };
String start(String args) {
//This is where you do your stuff for a simple behaviour
int index = args.indexOf(" ");
String encoder = "";
String position = "";
if ( index > 0 ) {
encoder = args.substring(0, index);
position = args.substring(index+1);
} else {
return "RotaryEncoderUpdated behaviour args error!";
}
String str = "{\"id\":\"" + String(_node->getId()) + "\",\"Input\":{\"type\":\"" + String(name()) + "\",\"encoder\":\"" + encoder + "\",\"position\":\"" + position + "\"}}";
_node->announce(str);
return "RotaryEncoderUpdated behaviour " + _name + " with (" + args + ")";
}
};
#endif
#ifndef SERVO_BEHAVIOUR_h
#define SERVO_BEHAVIOUR_h
#include "Arduino.h"
#include "Behaviours.h"
#include <Servo.h>
class ServoGoto : public Behaviour {
Servo _servo;
public:
ServoGoto(Servo servo, String name = "ServoGoto") : Behaviour(name), _servo(servo){ }
//ServoMove(Servo servo, String name) : Behaviour(name), _servo(servo) {}
char* args() {return "<int angle>"; };
String start(String args) {
Serial.println("Goto: '"+args+"'");
int val = args.toInt();
_servo.write(val);
return "";
}
};
class ServoWiggle : public Behaviour {
Servo _servo;
int _start_time = 0;
int _wiggle_time = 300;
int _num_wiggles = 5;
int _wiggles = 0;
int _wiggle_angle = 0;
//Calculate wiggle time by multiplying the angle by this...
int _wiggle_factor = 5;
public:
ServoWiggle(Servo servo, String name = "ServoWiggle", int slowness=3) : Behaviour(name), _servo(servo),_wiggle_factor(slowness) {}
char* args() {return "<int wiggle_angle>"; };
String start(String args) {
_wiggle_angle = args.toInt();
_wiggles = 0;
_running = true;
_wiggle_time = _wiggle_factor * _wiggle_angle;
return "Wiggling " + String(_num_wiggles) + " times";
}
void update() {
int time_since = millis() - _start_time;
if( time_since > _wiggle_time ) {
_wiggles++;
_start_time = millis();
int angle = ( _wiggles % 2 ) ? (90+_wiggle_angle) : (90-_wiggle_angle);
if( _wiggles > _num_wiggles ) {
angle = 90;
_running = false;
}
Serial.println("Wiggling to: " + String(angle));
_servo.write(angle);
}
}
};
class ServoRotateReturn : public Behaviour {
Servo _servo;
int _start_angle = 0;
int _end_angle = 180;
int _delay = 30;
int _num_rotations = 1;
int _rotations = 0;
int _pause = 500;
public:
ServoRotateReturn(Servo servo, String name="ServoRotateReturn", int delay=30, int pause=500, int start_angle = 2, int end_angle=178 ) :
Behaviour(name), _servo(servo),_delay(delay), _pause(pause), _start_angle(start_angle), _end_angle(end_angle) {}
char* args() {return "<int number_of_cycles>"; };
String start(String args) {
_num_rotations = args.toInt();
_rotations = 0;
_running = true;
}
void update() {
_servo.write(_start_angle);
delay(_pause);
for(int i = 0; i < _end_angle; i++) {
_servo.write(i);
delay(_delay);
}
_servo.write(_end_angle);
delay(_pause);
for(int i = 180; i >= _start_angle; i--) {
_servo.write(i);
delay(_delay/2);
}
_servo.write(_start_angle);
_rotations++;
if( _rotations >= _num_rotations ) {
_running = false;
}
}
};
#endif
#ifndef VizBlocks_H
#define VizBlocks_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"
#define NUM_BACKGROUND_BEHAVIOURS 5
class VizBlocks {
char* _ssid;
char* _wifi_pass;
char* _id;
char* _server;
int _port;
BehaviourTable _behaviours;
Behaviour* _active = nullptr;
Behaviour* _background[NUM_BACKGROUND_BEHAVIOURS];
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;
boolean _wifi;
String capabilitiesJSON[50];
public:
VizBlocks(char* id, char* ssid="VizBlocksNet", char* wifi_pass="VizBlocksAP",
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);
}
void set_wifi(boolean v) { _wifi = v; }
/*
* Set up the VizBlocks node - WiFi, MQTT
*/
void init() {
Serial.setTimeout(100);
Serial.println();
Serial.println(F("VizBlocks Node starting up"));
Serial.println("Initialising " + String(_id));
if( _wifi ) {
WiFi.mode(WIFI_STA);
// Connect to WiFi access point.
Serial.println();
Serial.print("Connecting to ");
Serial.println(_ssid);
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);
_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());
// 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();
}
generateCapabilitiesJSON();
announce_capabilities();
Serial.println("Init finished");
}
/*
* 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 run() {
int loop_start_time = millis();
serial_command();
if( _wifi ) { mqtt_command(); }
if( _active ) {
_active -> update();
if( ! _active->is_running() ) { _active = nullptr; }
}
for (int i = 0; i < NUM_BACKGROUND_BEHAVIOURS; i++) {
if( _background[i] ) {
_background[i] -> update();
if( !_background[i] -> is_running() ) { _background[i] = 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');
cmd.replace("\r", "");
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) {
if (!(input.indexOf("<") >= 0 || input.indexOf(">") >= 0 || input.indexOf("-") >= 0)) {
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:
* - look for a Behaviour with the right name
* - 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) {
// Stop whatever behaviour is running in the active slot.
if( _active ) { _active->stop(); }
Serial.println( "Found behaviour: <"+command+">" );
if ( b->is_background() ) {
// If this behaviour is already running in the background, refresh it and move on.
for (int i = 0; i < NUM_BACKGROUND_BEHAVIOURS; i++) {
if ( _background[i] ) {
if ( b->name() == _background[i]->name() ) {
return( b->start(args) );
}
}
}
// Else, if there is space for another background behaviour, start it running.
for (int i = 0; i < NUM_BACKGROUND_BEHAVIOURS; i++) {
if ( !_background[i] ) {
_background[i] = b;
return( b->start(args) );
}
}
return "Couldn't run command: " + command + ". Background behaviours full.";
}
_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);
}
}
Serial.println("MQTT Connected!");
}
void generateCapabilitiesJSON() {
String head = "{\"id\":\"" + 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 = "\"name\":\"" + b->name() + "\",\"args\":\"" + args + "\"}}";
String str = head + body;
capabilitiesJSON[i] = str;
}
}
void announce(String doc) {
if( _wifi ) { _my_announce->publish(doc.c_str()); }
Serial.print("-->");
Serial.println(doc);
}
void announce_capabilities() {
String doc;
if( _wifi ) {
if( ! _announce->publish(_id) ) { Serial.println("Couldn't make announcement"); }
}
for( int i = 0; i < _behaviours.get_num_behaviours(); i++ ) {
doc = capabilitiesJSON[i];
announce(doc);
}
}
void setID(char* id) {
_id = id;
}
char* getId() {
return _id;
}
};
/*
* These behaviours depend on VizBlocks class so they must be included after
* it has been defined.
*/
#include "CommsBehaviours.h"
#include "ButtonBehaviours.h"
#include "PotentiometerBehaviours.h"
#include "RotaryEncoderBehaviours.h"
#endif
# Doxygen instructions
**A quick run down of using doxygen**
## Intro
Basically Doxygen is going to look at every `.cpp` and `.h` file and look out for specifically formatted comments.
The full documentation can be [found here](http://www.doxygen.nl/manual/docblocks.html), but here is enough to get started. A Doxyfile has already been generated and options can be tweaked as necessary.
### Example
See the example output by going to `docs/html/index.html` in this repository.
### Install
#### Windows
You can install the binary [following these instruction](http://www.doxygen.nl/manual/install.html#install_bin_windows)
#### macOS
```
brew install doxygen
```
#### Linux Ubuntu / Debian
```
sudo apt-get install doxygen
```
### Comment blocks
Comment blocks can take a number of forms but I suggest using Javadoc c-style comments
> ... starting with two \*'s, like this:
>
> ```cpp
> /**
> * ... text ...
> */
> ```
#### Commands
Commands basically allow for comments to broken down into sections. They can start with either `\` or `@`.
There are a ton of different commands, though all the is needed is just a `\brief` tag at most. [A full reference can be found here](http://www.doxygen.nl/manual/commands.html). You don't need to be beholden to these commands, [you can use your own custom commands if you like](http://www.doxygen.nl/manual/custcmd.html).
A boiler plate commented file would look something like
```cpp
/** \file filename.h
\brief A Documented file.
filename.h MUST be the same name as the actual file
*/
/** \class Test
\brief A test class.
A more detailed class description.
*/
class Test{};
/**
\fn void example(int arg1);
\brief an example function
\param arg1 the first argument
*/
void example(int arg1);
```
The `doxygen_test.h` is provided as an example.
### Running Doxygen
run in the command line as
```sh
cd path/to/repo/
doxygen Doxyfile
```
### Markdown
[Doxygen supports Markdown!](http://www.doxygen.nl/manual/markdown.html) So you can use markdown in comment blocks if necessary.
In the doxygen file you can also point the frontpage to a `.md` file.
```make
USE_MDFILE_AS_MAINPAGE =
```
This will populate the mainpage with html from the specified markdown file.
### Custom Output
You can specify a html header, footer and custom css files. The easiest approach would be to generate these first
```sh
doxygen -w html header.html footer.html customdoxygen.css
```
then edit them as necessary.
Change these options in the Doxyfile
```make
HTML_HEADER = header.html
HTML_FOOTER = footer.html
HTML_EXTRA_STYLESHEET = my_customdoxygen.css
```
<!-- HTML header for doxygen 1.8.18-->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=9" />
<meta name="generator" content="Doxygen 1.9.1" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>VizBlocks: src/Behaviours.h File Reference</title>
<link href="tabs.css" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="dynsections.js"></script>
<link href="search/search.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="search/searchdata.js"></script>
<script type="text/javascript" src="search/search.js"></script>
<link href="my_customdoxygen.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="top">
<!-- do not remove this div, it is closed by doxygen! -->
<header id="header" class="desktop">
<div>
<div class="inner-header">
<div class="logotype">
<a href="https://edinburgh-college-of-art.github.io/vizblocks-website/" aria-hidden="true" title="Site home"
>VizBlocks</a
>
</div>
<div class="navigation">
<button
onclick="toggleBurger()"
type="button"
aria-label="toggle burger menu"
>
<i class="fa fa-bars"></i>
</button>
<nav id="menu" class="collapse">
<a href="https://edinburgh-college-of-art.github.io/vizblocks-website/" class="home">Home</a>
<a href="https://edinburgh-college-of-art.github.io/vizblocks-website/about" class="about">About</a>
<a
href="https://edinburgh-college-of-art.github.io/vizblocks-website/tutorials/0_getting_started.html"
class="start"
>Get Started</a
>
<a href="https://edinburgh-college-of-art.github.io/vizblocks-website/tutorials" class="tutorials"
>Tutorials</a
>
<a href="https://edinburgh-college-of-art.github.io/vizblocks-website/docs/html" class="docs"
>Technical Info</a
>
<a href="https://edinburgh-college-of-art.github.io/vizblocks-website/contact" class="content">Contact</a>
</nav>
</div>
</div>
</div>
<script>
const toggleBurger = () => {
const menu = document.getElementById("menu");
if (menu.className === "collapse") {
menu.className = "extend";
} else {
menu.className = "collapse";
}
};
const touchDevice = "ontouchstart" in document.documentElement;
if (touchDevice) {
const header = document.getElementById("header");
header.className = "mobile";
}
</script>
</header>
<!-- end header part -->
</div>
</body>
</html>
<!-- Generated by Doxygen 1.9.1 -->
<script type="text/javascript">
/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&amp;dn=gpl-2.0.txt GPL-v2 */
var searchBox = new SearchBox("searchBox", "search",false,'Search','.html');
/* @license-end */
</script>
<script type="text/javascript" src="menudata.js"></script>
<script type="text/javascript" src="menu.js"></script>
<script type="text/javascript">
/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&amp;dn=gpl-2.0.txt GPL-v2 */
$(function() {
initMenu('',true,false,'search.php','Search');
$(document).ready(function() { init_search(); });
});
/* @license-end */</script>
<div id="main-nav"></div>
<!-- window showing the filter options -->
<div id="MSearchSelectWindow"
onmouseover="return searchBox.OnSearchSelectShow()"
onmouseout="return searchBox.OnSearchSelectHide()"
onkeydown="return searchBox.OnSearchSelectKey(event)">
</div>
<!-- iframe showing the search results (closed by default) -->
<div id="MSearchResultsWindow">
<iframe src="javascript:void(0)" frameborder="0"
name="MSearchResults" id="MSearchResults">
</iframe>
</div>
<div id="nav-path" class="navpath">
<ul>
<li class="navelem"><a class="el" href="dir_68267d1309a1af8e8297ef4c3efbcdba.html">src</a></li> </ul>
</div>
</div><!-- top -->
<div class="header">
<div class="summary">
<a href="#nested-classes">Classes</a> </div>
<div class="headertitle">
<div class="title">Behaviours.h File Reference</div> </div>
</div><!--header-->
<div class="contents">
<p>Functionality for block behaviours.
<a href="#details">More...</a></p>
<div class="textblock"><code>#include &lt;Arduino.h&gt;</code><br />
</div>
<p><a href="_behaviours_8h_source.html">Go to the source code of this file.</a></p>
<table class="memberdecls">
<tr class="heading"><td colspan="2"><h2 class="groupheader"><a name="nested-classes"></a>
Classes</h2></td></tr>
<tr class="memitem:"><td class="memItemLeft" align="right" valign="top">class &#160;</td><td class="memItemRight" valign="bottom"><a class="el" href="class_behaviour.html">Behaviour</a></td></tr>
<tr class="memdesc:"><td class="mdescLeft">&#160;</td><td class="mdescRight">Parent class for all other behaviours. <a href="class_behaviour.html#details">More...</a><br /></td></tr>
<tr class="separator:"><td class="memSeparator" colspan="2">&#160;</td></tr>
<tr class="memitem:"><td class="memItemLeft" align="right" valign="top">class &#160;</td><td class="memItemRight" valign="bottom"><a class="el" href="class_behaviour_table.html">BehaviourTable</a></td></tr>
<tr class="memdesc:"><td class="mdescLeft">&#160;</td><td class="mdescRight">A container for a block's behaviours. <a href="class_behaviour_table.html#details">More...</a><br /></td></tr>
<tr class="separator:"><td class="memSeparator" colspan="2">&#160;</td></tr>
</table>
<a name="details" id="details"></a><h2 class="groupheader">Detailed Description</h2>
<div class="textblock"><p>Functionality for block behaviours. </p>
<p>This file contains the parent <a class="el" href="class_behaviour.html" title="Parent class for all other behaviours.">Behaviour</a> class and a <a class="el" href="class_behaviour_table.html" title="A container for a block&#39;s behaviours.">BehaviourTable</a> class for storing the array of a block's capabilities. </p>
</div></div><!-- contents -->
<!-- HTML footer for doxygen 1.8.18-->
<!-- start footer part -->
<hr class="footer"/><address class="footer"><small>
Generated by &#160;<a href="http://www.doxygen.org/index.html">
<img class="footer" src="doxygen.png" alt="doxygen"/>
</a> 1.9.1
</small></address>
</body>
</html>
This diff is collapsed.
<!-- HTML header for doxygen 1.8.18-->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=9" />
<meta name="generator" content="Doxygen 1.9.1" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>VizBlocks: src/Button.h File Reference</title>
<link href="tabs.css" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="dynsections.js"></script>
<link href="search/search.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="search/searchdata.js"></script>
<script type="text/javascript" src="search/search.js"></script>
<link href="my_customdoxygen.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="top">
<!-- do not remove this div, it is closed by doxygen! -->
<header id="header" class="desktop">
<div>
<div class="inner-header">
<div class="logotype">
<a href="https://edinburgh-college-of-art.github.io/vizblocks-website/" aria-hidden="true" title="Site home"
>VizBlocks</a
>
</div>
<div class="navigation">
<button
onclick="toggleBurger()"
type="button"
aria-label="toggle burger menu"
>
<i class="fa fa-bars"></i>
</button>
<nav id="menu" class="collapse">
<a href="https://edinburgh-college-of-art.github.io/vizblocks-website/" class="home">Home</a>
<a href="https://edinburgh-college-of-art.github.io/vizblocks-website/about" class="about">About</a>
<a
href="https://edinburgh-college-of-art.github.io/vizblocks-website/tutorials/0_getting_started.html"
class="start"
>Get Started</a
>
<a href="https://edinburgh-college-of-art.github.io/vizblocks-website/tutorials" class="tutorials"
>Tutorials</a
>
<a href="https://edinburgh-college-of-art.github.io/vizblocks-website/docs/html" class="docs"
>Technical Info</a
>
<a href="https://edinburgh-college-of-art.github.io/vizblocks-website/contact" class="content">Contact</a>
</nav>
</div>
</div>
</div>
<script>
const toggleBurger = () => {
const menu = document.getElementById("menu");
if (menu.className === "collapse") {
menu.className = "extend";
} else {
menu.className = "collapse";
}
};
const touchDevice = "ontouchstart" in document.documentElement;
if (touchDevice) {
const header = document.getElementById("header");
header.className = "mobile";
}
</script>
</header>
<!-- end header part -->
</div>
</body>
</html>
<!-- Generated by Doxygen 1.9.1 -->
<script type="text/javascript">
/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&amp;dn=gpl-2.0.txt GPL-v2 */
var searchBox = new SearchBox("searchBox", "search",false,'Search','.html');
/* @license-end */
</script>
<script type="text/javascript" src="menudata.js"></script>
<script type="text/javascript" src="menu.js"></script>
<script type="text/javascript">
/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&amp;dn=gpl-2.0.txt GPL-v2 */
$(function() {
initMenu('',true,false,'search.php','Search');
$(document).ready(function() { init_search(); });
});
/* @license-end */</script>
<div id="main-nav"></div>
<!-- window showing the filter options -->
<div id="MSearchSelectWindow"
onmouseover="return searchBox.OnSearchSelectShow()"
onmouseout="return searchBox.OnSearchSelectHide()"
onkeydown="return searchBox.OnSearchSelectKey(event)">
</div>
<!-- iframe showing the search results (closed by default) -->
<div id="MSearchResultsWindow">
<iframe src="javascript:void(0)" frameborder="0"
name="MSearchResults" id="MSearchResults">
</iframe>
</div>
<div id="nav-path" class="navpath">
<ul>
<li class="navelem"><a class="el" href="dir_68267d1309a1af8e8297ef4c3efbcdba.html">src</a></li> </ul>
</div>
</div><!-- top -->
<div class="header">
<div class="summary">
<a href="#nested-classes">Classes</a> </div>
<div class="headertitle">
<div class="title">Button.h File Reference</div> </div>
</div><!--header-->
<div class="contents">
<p>Event-driven button functionality. This file contains a class for an event-driven button input. It is based on the <a href="https://github.com/bxparks/AceButton">AceButton Library by bxparks</a>. The implimentation here is configured for esp822 boards and simplified. It is however, far less configurable.
<a href="#details">More...</a></p>
<div class="textblock"><code>#include &lt;Arduino.h&gt;</code><br />
</div>
<p><a href="_button_8h_source.html">Go to the source code of this file.</a></p>
<table class="memberdecls">
<tr class="heading"><td colspan="2"><h2 class="groupheader"><a name="nested-classes"></a>
Classes</h2></td></tr>
<tr class="memitem:"><td class="memItemLeft" align="right" valign="top">class &#160;</td><td class="memItemRight" valign="bottom"><a class="el" href="class_button.html">Button</a></td></tr>
<tr class="memdesc:"><td class="mdescLeft">&#160;</td><td class="mdescRight">Event-driven button class. <a href="class_button.html#details">More...</a><br /></td></tr>
<tr class="separator:"><td class="memSeparator" colspan="2">&#160;</td></tr>
</table>
<a name="details" id="details"></a><h2 class="groupheader">Detailed Description</h2>
<div class="textblock"><p>Event-driven button functionality. This file contains a class for an event-driven button input. It is based on the <a href="https://github.com/bxparks/AceButton">AceButton Library by bxparks</a>. The implimentation here is configured for esp822 boards and simplified. It is however, far less configurable. </p>
</div></div><!-- contents -->
<!-- HTML footer for doxygen 1.8.18-->
<!-- start footer part -->
<hr class="footer"/><address class="footer"><small>
Generated by &#160;<a href="http://www.doxygen.org/index.html">
<img class="footer" src="doxygen.png" alt="doxygen"/>
</a> 1.9.1
</small></address>
</body>
</html>