Hi Everyone,
Just thought I would share a nice way to interface Bela with Arduino using Websockets. Pretty new to posting so feel free to add any corrections/insight.
This was tested on an Arduino Nano 33 iot. The library used is the following:
Built by Khoi Hoang https://github.com/khoih-prog/Websockets2_Generic
Licensed under MIT license
The library is designed to work with many wireless wifi boards so opens a lot of possibilities. I modified the Arduino websockets2 client example so that it connects directly to the bela Gui library and there is almost no noticeable latency. For running external lights in real time for level indicators or toggles of any sort it should work exceptionally well, in use as a live instrument there were some occasional noticeable lags but I'm not sure if it was due to the actual button registering or wifi latency. Multiple overlapping wifi signals or other reduncancies should be enough to make it fully useable realtime-ish.
Here's the Arduino code, which is the base websocket2 library mentioned above modified with button debounce and state detection:
#include "defines.h"
#include <Arduino_JSON.h>
#include <WebSockets2_Generic.h>
using namespace websockets2_generic;
WebsocketsClient client;
const int IPLastByte = 130; //set last byte of ip address
//BUTTONS////
unsigned long debounceDelay = 50; // the debounce time; increase if the output flickers
const int arraySize = 4;
int buttonPin[arraySize]; // the pin that the pushbutton is attached to
// Variables will change:
int buttonPushCounter[arraySize]; // counter for the number of button presses
int buttonState[arraySize]; // current state of the button
int lastButtonState[arraySize]; // previous state of the button
unsigned long lastDebounceTime[arraySize]; // the last time the output pin was toggled
void setup()
{
Serial.begin(115200);
//while (!Serial);
pinMode(2, INPUT_PULLUP);
pinMode(3, INPUT_PULLUP);
//initialize button arrays
for(int i=0; i<arraySize; i++) {
buttonPushCounter[i]=0; // counter for the number of button presses
buttonState[i]=0; // current state of the button
lastButtonState[i]=0; // previous state of the button
buttonPin[i] = i;
lastDebounceTime[i] = 0;
}
Serial.println("\nStarting SAMD-Client with WiFiNINA on " + String(BOARD_NAME));
Serial.println(WEBSOCKETS2_GENERIC_VERSION);
// check for the WiFi module:
if (WiFi.status() == WL_NO_MODULE)
{
Serial.println("Communication with WiFi module failed!");
// don't continue
return;
}
String fv = WiFi.firmwareVersion();
if (fv < WIFI_FIRMWARE_LATEST_VERSION)
{
Serial.println("Please upgrade the firmware");
}
Serial.print("Attempting to connect to SSID: ");
Serial.println(ssid);
WiFiConnect();
if (WiFi.status() == WL_CONNECTED)
{
Serial.print("Connected to Wifi, IP address: ");
Serial.println(WiFi.localIP());
Serial.print("Connecting to WebSockets Server @");
}
else
{
Serial.println("\nNo WiFi");
return;
}
// run callback when messages are received
client.onMessage(onMessageCallback);
// run callback when events are occuring
client.onEvent(onEventsCallback);
// Connect to server
client.connect(websockets_server_host, websockets_server_port, "/");
// Send a message
String WS_msg = String("Hello to Server from ") + BOARD_NAME;
client.send(WS_msg);
// Send a ping
client.ping();
}
void loop()
{
//Try to reconnect WiFi if connection was lost
if (WiFi.status() != WL_CONNECTED) {
digitalWrite(LED_BUILTIN, LOW);
Serial.println("\n--Lost WiFi connection");
WiFi.end();
WiFiConnect();
}
// let the websockets client check for incoming messages
if (client.available())
{
client.poll();
}
button(); //scan for button state changes
} //////////////////End Main Loop
void button(void) {
for (int b=2; b<arraySize; b++){
// read the pushbutton input pin:
int reading = digitalRead(buttonPin[b]);
// compare the buttonState to its previous state
if (buttonState[b] != lastButtonState[b]) {
//if state changed, start debounce timer
lastDebounceTime[b] = millis();
}
if ((millis() - lastDebounceTime[b]) > debounceDelay) {
if (reading != buttonState[b]) {
buttonState[b] = reading;
Serial.println("pushed");
if (buttonState[b] == LOW) {
// if the current state is LOW(input pullup) then the button went from off to on:
buttonPushCounter[b]++;
Serial.println("on");
Serial.print("number of button pushes: ");
Serial.println(buttonPushCounter[b]);
sendMessage("p"); //if pushed, send char
} else {
// if the current state is LOW then the button went from on to off:
Serial.println("off");
}
// Debounce
}
// save the current state as the last state, for next time through the loop
lastButtonState[b] = buttonState[b];
}
}
}
void socketConnect() {
bool connected = client.connect(websockets_server_host, websockets_server_port, "/gui_data");
while(!connected) {
connected = client.connect(websockets_server_host, websockets_server_port, "/gui_data");
Serial.println("Connnecting sockets...sockeConnect");
delay(5000);
}
}
void sendMessage(const char* button)
{
client.sendBinary(button,2);
}
void onMessageCallback(WebsocketsMessage message)
{
Serial.print("Got Message: ");
Serial.println(message.data());
}
void onEventsCallback(WebsocketsEvent event, String data)
{
if (event == WebsocketsEvent::ConnectionOpened)
{
Serial.println("Connnection Opened");
}
else if (event == WebsocketsEvent::ConnectionClosed)
{
Serial.println("Connnection Closed");
socketConnect();
}
else if (event == WebsocketsEvent::GotPing)
{
Serial.println("Got a Ping!");
}
else if (event == WebsocketsEvent::GotPong)
{
Serial.println("Got a Pong!");
}
}
void WiFiConnect() {
while (WiFi.status() != WL_CONNECTED) {
Serial.println("Connecting to ");
Serial.println(ssid);
Serial.println(" ...");
WiFi.begin(ssid, password);
delay(5000);
}
Serial.println(websockets_server_host);
// run callback when messages are received
client.onMessage([&](WebsocketsMessage message)
{
Serial.print("Got Message: ");
Serial.println(message.data());
});
// run callback when events are occuring
client.onEvent(onEventsCallback);
//sendMessage();
IPAddress IP = WiFi.localIP();
IP[3] = IPLastByte;
WiFi.config(IP, WiFi.gatewayIP(), WiFi.gatewayIP(), WiFi.subnetMask());
Serial.println("Connected to ");
Serial.println(ssid);
int tries = 5;
WiFi.lowPowerMode();
digitalWrite(LED_BUILTIN, HIGH);
}
and the defines.h file:
/****************************************************************************************************************************
defines.h
For SAMD21/SAMD51 with WiFiNINA module/shield.
Based on and modified from Gil Maimon's ArduinoWebsockets library https://github.com/gilmaimon/ArduinoWebsockets
to support STM32F/L/H/G/WB/MP1, nRF52 and SAMD21/SAMD51 boards besides ESP8266 and ESP32
The library provides simple and easy interface for websockets (Client and Server).
Example first created on: 10.05.2018
Original Author: Markus Sattler
Built by Khoi Hoang https://github.com/khoih-prog/Websockets2_Generic
Licensed under MIT license
*****************************************************************************************************************************/
#ifndef defines_h
#define defines_h
#if ( defined(ARDUINO_SAMD_ZERO) || defined(ARDUINO_SAMD_MKR1000) || defined(ARDUINO_SAMD_MKRWIFI1010) \
|| defined(ARDUINO_SAMD_NANO_33_IOT) || defined(ARDUINO_SAMD_MKRFox1200) || defined(ARDUINO_SAMD_MKRWAN1300) || defined(ARDUINO_SAMD_MKRWAN1310) \
|| defined(ARDUINO_SAMD_MKRGSM1400) || defined(ARDUINO_SAMD_MKRNB1500) || defined(ARDUINO_SAMD_MKRVIDOR4000) || defined(__SAMD21G18A__) \
|| defined(ARDUINO_SAMD_CIRCUITPLAYGROUND_EXPRESS) || defined(__SAMD21E18A__) || defined(__SAMD51__) || defined(__SAMD51J20A__) || defined(__SAMD51J19A__) \
|| defined(__SAMD51G19A__) || defined(__SAMD51P19A__) || defined(__SAMD21G18A__) )
#if defined(WEBSOCKETS_WIFININA_USE_SAMD)
#undef WEBSOCKETS_WIFININA_USE_SAMD
#endif
#define WEBSOCKETS_USE_WIFININA true
#define WEBSOCKETS_WIFININA_USE_SAMD true
#else
#error This code is intended to run only on the SAMD boards ! Please check your Tools->Board setting.
#endif
#if defined(WEBSOCKETS_WIFININA_USE_SAMD)
#if defined(ARDUINO_SAMD_ZERO)
#define BOARD_TYPE "SAMD Zero"
#elif defined(ARDUINO_SAMD_MKR1000)
#define BOARD_TYPE "SAMD MKR1000"
#elif defined(ARDUINO_SAMD_MKRWIFI1010)
#define BOARD_TYPE "SAMD MKRWIFI1010"
#elif defined(ARDUINO_SAMD_NANO_33_IOT)
#define BOARD_TYPE "SAMD NANO_33_IOT"
#elif defined(ARDUINO_SAMD_MKRFox1200)
#define BOARD_TYPE "SAMD MKRFox1200"
#elif ( defined(ARDUINO_SAMD_MKRWAN1300) || defined(ARDUINO_SAMD_MKRWAN1310) )
#define BOARD_TYPE "SAMD MKRWAN13X0"
#elif defined(ARDUINO_SAMD_MKRGSM1400)
#define BOARD_TYPE "SAMD MKRGSM1400"
#elif defined(ARDUINO_SAMD_MKRNB1500)
#define BOARD_TYPE "SAMD MKRNB1500"
#elif defined(ARDUINO_SAMD_MKRVIDOR4000)
#define BOARD_TYPE "SAMD MKRVIDOR4000"
#elif defined(ARDUINO_SAMD_CIRCUITPLAYGROUND_EXPRESS)
#define BOARD_TYPE "SAMD ARDUINO_SAMD_CIRCUITPLAYGROUND_EXPRESS"
#elif defined(ADAFRUIT_FEATHER_M0_EXPRESS)
#define BOARD_TYPE "SAMD21 ADAFRUIT_FEATHER_M0_EXPRESS"
#elif defined(ADAFRUIT_METRO_M0_EXPRESS)
#define BOARD_TYPE "SAMD21 ADAFRUIT_METRO_M0_EXPRESS"
#elif defined(ADAFRUIT_CIRCUITPLAYGROUND_M0)
#define BOARD_TYPE "SAMD21 ADAFRUIT_CIRCUITPLAYGROUND_M0"
#elif defined(ADAFRUIT_GEMMA_M0)
#define BOARD_TYPE "SAMD21 ADAFRUIT_GEMMA_M0"
#elif defined(ADAFRUIT_TRINKET_M0)
#define BOARD_TYPE "SAMD21 ADAFRUIT_TRINKET_M0"
#elif defined(ADAFRUIT_ITSYBITSY_M0)
#define BOARD_TYPE "SAMD21 ADAFRUIT_ITSYBITSY_M0"
#elif defined(ARDUINO_SAMD_HALLOWING_M0)
#define BOARD_TYPE "SAMD21 ARDUINO_SAMD_HALLOWING_M0"
#elif defined(ADAFRUIT_METRO_M4_EXPRESS)
#define BOARD_TYPE "SAMD51 ADAFRUIT_METRO_M4_EXPRESS"
#elif defined(ADAFRUIT_GRAND_CENTRAL_M4)
#define BOARD_TYPE "SAMD51 ADAFRUIT_GRAND_CENTRAL_M4"
#elif defined(ADAFRUIT_FEATHER_M4_EXPRESS)
#define BOARD_TYPE "SAMD51 ADAFRUIT_FEATHER_M4_EXPRESS"
#elif defined(ADAFRUIT_ITSYBITSY_M4_EXPRESS)
#define BOARD_TYPE "SAMD51 ADAFRUIT_ITSYBITSY_M4_EXPRESS"
#elif defined(ADAFRUIT_TRELLIS_M4_EXPRESS)
#define BOARD_TYPE "SAMD51 ADAFRUIT_TRELLIS_M4_EXPRESS"
#elif defined(ADAFRUIT_PYPORTAL)
#define BOARD_TYPE "SAMD51 ADAFRUIT_PYPORTAL"
#elif defined(ADAFRUIT_PYPORTAL_M4_TITANO)
#define BOARD_TYPE "SAMD51 ADAFRUIT_PYPORTAL_M4_TITANO"
#elif defined(ADAFRUIT_PYBADGE_M4_EXPRESS)
#define BOARD_TYPE "SAMD51 ADAFRUIT_PYBADGE_M4_EXPRESS"
#elif defined(ADAFRUIT_METRO_M4_AIRLIFT_LITE)
#define BOARD_TYPE "SAMD51 ADAFRUIT_METRO_M4_AIRLIFT_LITE"
#elif defined(ADAFRUIT_PYGAMER_M4_EXPRESS)
#define BOARD_TYPE "SAMD51 ADAFRUIT_PYGAMER_M4_EXPRESS"
#elif defined(ADAFRUIT_PYGAMER_ADVANCE_M4_EXPRESS)
#define BOARD_TYPE "SAMD51 ADAFRUIT_PYGAMER_ADVANCE_M4_EXPRESS"
#elif defined(ADAFRUIT_PYBADGE_AIRLIFT_M4)
#define BOARD_TYPE "SAMD51 ADAFRUIT_PYBADGE_AIRLIFT_M4"
#elif defined(ADAFRUIT_MONSTER_M4SK_EXPRESS)
#define BOARD_TYPE "SAMD51 ADAFRUIT_MONSTER_M4SK_EXPRESS"
#elif defined(ADAFRUIT_HALLOWING_M4_EXPRESS)
#define BOARD_TYPE "SAMD51 ADAFRUIT_HALLOWING_M4_EXPRESS"
#elif defined(SEEED_WIO_TERMINAL)
#define BOARD_TYPE "SAMD SEEED_WIO_TERMINAL"
#elif defined(SEEED_FEMTO_M0)
#define BOARD_TYPE "SAMD SEEED_FEMTO_M0"
#elif defined(SEEED_XIAO_M0)
#define BOARD_TYPE "SAMD SEEED_XIAO_M0"
#elif defined(Wio_Lite_MG126)
#define BOARD_TYPE "SAMD SEEED Wio_Lite_MG126"
#elif defined(WIO_GPS_BOARD)
#define BOARD_TYPE "SAMD SEEED WIO_GPS_BOARD"
#elif defined(SEEEDUINO_ZERO)
#define BOARD_TYPE "SAMD SEEEDUINO_ZERO"
#elif defined(SEEEDUINO_LORAWAN)
#define BOARD_TYPE "SAMD SEEEDUINO_LORAWAN"
#elif defined(SEEED_GROVE_UI_WIRELESS)
#define BOARD_TYPE "SAMD SEEED_GROVE_UI_WIRELESS"
#elif defined(__SAMD21E18A__)
#define BOARD_TYPE "SAMD21E18A"
#elif defined(__SAMD21G18A__)
#define BOARD_TYPE "SAMD21G18A"
#elif defined(__SAMD51G19A__)
#define BOARD_TYPE "SAMD51G19A"
#elif defined(__SAMD51J19A__)
#define BOARD_TYPE "SAMD51J19A"
#elif defined(__SAMD51J20A__)
#define BOARD_TYPE "SAMD51J20A"
#elif defined(__SAM3X8E__)
#define BOARD_TYPE "SAM3X8E"
#elif defined(__CPU_ARC__)
#define BOARD_TYPE "CPU_ARC"
#elif defined(__SAMD51__)
#define BOARD_TYPE "SAMD51"
#else
#define BOARD_TYPE "SAMD Unknown"
#endif
#endif
#ifndef BOARD_NAME
#define BOARD_NAME BOARD_TYPE
#endif
#include <WiFiNINA_Generic.h>
#define DEBUG_WEBSOCKETS_PORT Serial
// Debug Level from 0 to 4
#define _WEBSOCKETS_LOGLEVEL_ 3
const char* ssid = "<yourSSIDHere"; //Enter SSID
const char* password = "<yourSuperSecretPasswordHere>"; //Enter Password
const char* websockets_server_host = "<bela'sIPHere"; //xxx.xxx.xxx.xxx format
//const char* websockets_server_host = "serverip_or_name"; //Enter server address
#define WEBSOCKETS_PORT 5555 //bela's port
const uint16_t websockets_server_port = WEBSOCKETS_PORT; // Enter server port
#endif //defines_h
To receive messages I just made some ugly hard code directly into the Gui library, for a code wizard it should be easy to make a nice implementation or library of some sort for simplicity.