WebSockets are an easy way to exchange data between a client and server, full-duplex, over a single TCP connection. This example uses the ArduinoWebSockets library, which needs only a few modifications to work with the Industruino's D21G platform.
We will run a websocket server on a laptop (e.g. the node.js/simple.js example included in the above library), and our Industruino will connect over Ethernet to this server.
- start the websocket server
- start the Industruino websocket client
- the Industruino connects to the server and send a hello message
- the server sends a hello message to the Industruino client
- whenever an Industruino button is pressed, it sends 'ENTER', 'UP', 'DOWN' to the server
- when the server disconnects (timeout of 1min), the Industruino displays 'disconnected'
- the Industruino client connects again when the 'ENTER' button is pressed
Above is the console output of the simple.js websocket server on a Linux Ubuntu laptop.
The library configuration files need 2 small modifications:
- config.h needs to refer to the W5500 chip because that is the one used in the Industruino Ethernet module
- platform.h needs to explicitely state that we have an SAMD architecture; Industruino does not define ARDUINO_SAMD_ZERO so we have to include after line 18:
#define PLATFORM_ARCH PLATFORM_ARCHITECTURE_SAMD21
The Serial Monitor has a lot of debugging information in case of trouble.
The code for the client is below; you will need to edit the server IP address as applicable. The code retrieves a unique MAC address from the RTC EEPROM included in the D21G topboard, you can of course set the MAC address manually to any other constant.
/*
WEBSOCKET DEMO for Industruino
using library https://github.com/skaarj1989/ArduinoWebSockets
based on simple-client.ino example
configure config.h line 24 for W5500 (Ethernet chip used in Industruino Ethernet module)
edit platform.h to include a line after line 18:
#define PLATFORM_ARCH PLATFORM_ARCHITECTURE_SAMD21 // include for INDUSTRUINO
(because ARDUINO_SAMD_ZERO is not defined for Industruino)
you need a websocket server, e.g. the one included in the library node.js/simple.js (tested on Linux Ubuntu)
working on Industruino D21G + Ethernet
> the sketch connects to the server
> displays a message from the server
> sends a string message to the server
> whenever a button is pressed on the Industruino, it sends 'ENTER', 'UP', 'DOWN' to the server
> displays 'disconnected' when the server closes the connection
> re-connects again to the server when the Enter button is pressed
*/
#include <WebSocketClient.h> // includes SPI and Ethernet2
WebSocketClient client;
#include <Wire.h> // for RTC EEPROM MAC
byte mac[6]; // read from RTC EEPROM
const char *server = "192.168.1.121";
const int port = 3000;
#include <UC1701.h>
static UC1701 lcd;
unsigned long pressed_timestamp;
unsigned long pressed_interval = 200;
boolean ws_connected = false;
/////////////////////////////////////////////////////////////////////////
void onOpen(WebSocket &ws) {
SerialUSB.println("Connected");
lcd.setCursor(0, 6);
lcd.print("connected ");
ws_connected = true;
char message[] = "Hello from your Industruino websocket client!";
ws.send(TEXT, message, strlen(message));
}
void onClose(WebSocket &ws, const eWebSocketCloseEvent code, const char *reason, uint16_t length) {
SerialUSB.println("Disconnected");
lcd.setCursor(0, 6);
lcd.print("disconnected ");
ws_connected = false;
}
void onMessage(WebSocket &ws, const eWebSocketDataType dataType, const char *message, uint16_t length) {
switch (dataType) {
case TEXT:
SerialUSB.print("Received: "); SerialUSB.println(message);
lcd.setCursor(0, 6);
lcd.print(message);
lcd.print(" ");
break;
case BINARY:
SerialUSB.println("Received binary data");
break;
}
}
void onError(const eWebSocketError code) {
SerialUSB.print("Error: "); SerialUSB.println(code);
lcd.setCursor(0, 6);
lcd.print("error: ");
lcd.print(code);
lcd.print(" ");
}
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
void setup() {
SerialUSB.begin(115200);
SerialUSB.println("Starting sketch: WebSocket DEMO");
Wire.begin(); // for MAC from RTC EEPROM
pinMode(26, OUTPUT);
digitalWrite(26, HIGH); // LCD full backlight
lcd.begin();
lcd.setCursor(0, 0);
lcd.print("WebSocket DEMO");
SerialUSB.println("Get unique MAC from RTC EEPROM");
readMACfromRTC(); // MAC stored in RTC EEPROM
if (Ethernet.begin(mac) == 0) {
SerialUSB.println("failed to start Ethernet with DCHP, stop here");
while (true) ;
}
SerialUSB.print("Client IP: ");
SerialUSB.println(Ethernet.localIP());
lcd.setCursor(0, 2);
lcd.print("IP: ");
lcd.print(Ethernet.localIP());
lcd.setCursor(0, 3);
lcd.print("server:");
lcd.setCursor(0, 4);
lcd.print(server);
lcd.print(":");
lcd.print(port);
SerialUSB.print("Trying to connect to server: ");
SerialUSB.print(server);
SerialUSB.print(":");
SerialUSB.println(port);
client.setOnOpenCallback(onOpen);
client.setOnCloseCallback(onClose);
client.setOnMessageCallback(onMessage);
if (!client.open(server, port)) {
SerialUSB.println("Connection failed, stop here");
lcd.setCursor(0, 6);
lcd.print("connection failed");
while (true) ;
}
SerialUSB.println("Connected");
}
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
void loop() {
client.listen();
if (SerialUSB.available()) {
char msg[1];
msg[0] = SerialUSB.read();
client.send(TEXT, msg, 1);
}
if (!digitalRead(24) && millis() - pressed_timestamp > pressed_interval) {
char msg[] = "ENTER";
client.send(TEXT, msg, strlen(msg));
pressed_timestamp = millis();
if (!ws_connected) {
if (!client.open(server, port)) {
SerialUSB.println("Connection failed, stop here");
lcd.setCursor(0, 6);
lcd.print("connection failed");
while (true) ;
}
}
}
if (!digitalRead(23) && millis() - pressed_timestamp > pressed_interval) {
char msg[] = "DOWN";
client.send(TEXT, msg, strlen(msg));
pressed_timestamp = millis();
}
if (!digitalRead(25) && millis() - pressed_timestamp > pressed_interval) {
char msg[] = "UP";
client.send(TEXT, msg, strlen(msg));
pressed_timestamp = millis();
}
}
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
// the RTC has a MAC address stored in EEPROM - 8 bytes 0xf0 to 0xf7
void readMACfromRTC() {
SerialUSB.println("READING MAC from RTC EEPROM");
int mac_index = 0;
for (int i = 0; i < 8; i++) { // read 8 bytes of 64-bit MAC address, 3 bytes valid OUI, 5 bytes unique EI
byte m = readByte(0x57, 0xf0 + i);
SerialUSB.print(m, HEX);
if (i < 7) SerialUSB.print(":");
if (i != 3 && i != 4) { // for 6-bytes MAC, skip first 2 bytes of EI
mac[mac_index] = m;
mac_index++;
}
}
SerialUSB.println();
SerialUSB.print("Extracted 6-byte MAC address: ");
for (int u = 0; u < 6; u++) {
SerialUSB.print(mac[u], HEX);
if (u < 5) SerialUSB.print(":");
}
SerialUSB.println();
}
/////////////////////////////////////////////////////////////////////////
// the RTC has a MAC address stored in EEPROM
uint8_t readByte(uint8_t i2cAddr, uint8_t dataAddr) {
Wire.beginTransmission(i2cAddr);
Wire.write(dataAddr);
Wire.endTransmission(false); // don't send stop
Wire.requestFrom(i2cAddr, 1);
return Wire.read();
}