Now with Websockets and Web (HMTL) based GUI
This commit is contained in:
parent
fa4542a53b
commit
c94ba558ec
247
LED-Board.ino
247
LED-Board.ino
@ -1,53 +1,124 @@
|
||||
#include <Arduino.h>
|
||||
#include <Ethernet.h>
|
||||
#include "image.h"
|
||||
|
||||
#include "src/WebSocketsServer.h"
|
||||
#include "src/image.hpp"
|
||||
#include "src/ProtocolDL.hpp"
|
||||
|
||||
#define USE_SERIAL Serial
|
||||
|
||||
|
||||
#define LOAD 7
|
||||
#define DATA 8
|
||||
#define CLOCK 9
|
||||
|
||||
struct _source_t {
|
||||
// width and height of current frame
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
|
||||
// delay until next frame is shown
|
||||
unsigned int delay;
|
||||
|
||||
// position of current pixel
|
||||
int x;
|
||||
int y;
|
||||
};
|
||||
|
||||
typedef struct _source_t source_t;
|
||||
|
||||
byte mac[] = { 0xBE, 0xB7, 0x5C, 0x30, 0xC3, 0x04 };
|
||||
IPAddress ip(10, 23, 42, 24);
|
||||
IPAddress router(10, 23, 42, 1);
|
||||
IPAddress subnet(255, 255, 254, 0);
|
||||
|
||||
EthernetServer server(9000);
|
||||
WebSocketsServer webSocket = WebSocketsServer(81);
|
||||
|
||||
Image image;
|
||||
ProtocolDL protocol = ProtocolDL(image);
|
||||
|
||||
image_t image;
|
||||
unsigned long last_activity = 0;
|
||||
|
||||
source_t source;
|
||||
bool someOneIsConnected = false;
|
||||
|
||||
void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
|
||||
static bool in_header = true;
|
||||
|
||||
switch(type) {
|
||||
case WStype_DISCONNECTED:
|
||||
USE_SERIAL.print("[");
|
||||
USE_SERIAL.print(num);
|
||||
USE_SERIAL.println("] Disconnected!");
|
||||
someOneIsConnected = false;
|
||||
break;
|
||||
case WStype_CONNECTED:
|
||||
{
|
||||
//IPAddress ip = webSocket.remoteIP(num);
|
||||
USE_SERIAL.print("[");
|
||||
USE_SERIAL.print(num);
|
||||
USE_SERIAL.print("] Connected ");
|
||||
USE_SERIAL.print(" url: ");
|
||||
//USE_SERIAL.println(payload);
|
||||
|
||||
// send message to client
|
||||
webSocket.sendTXT(num, "Connected");
|
||||
someOneIsConnected = true;
|
||||
}
|
||||
break;
|
||||
case WStype_TEXT:
|
||||
USE_SERIAL.print("[");
|
||||
USE_SERIAL.print(num);
|
||||
USE_SERIAL.print("] get Text: ");
|
||||
//USE_SERIAL.println(payload);
|
||||
|
||||
// send message to client
|
||||
// webSocket.sendTXT(num, "message here");
|
||||
|
||||
// send data to all connected clients
|
||||
// webSocket.broadcastTXT("message here");
|
||||
break;
|
||||
case WStype_BIN:
|
||||
USE_SERIAL.print("[");
|
||||
USE_SERIAL.print(num);
|
||||
USE_SERIAL.print("] get binary length: ");
|
||||
USE_SERIAL.println(length);
|
||||
|
||||
for(uint16_t i = 0; i < length; i++)
|
||||
{
|
||||
protocol.newByte(payload[i]);
|
||||
}
|
||||
|
||||
if(protocol.isComplete())
|
||||
{
|
||||
Serial.println("complete");
|
||||
send_image(&image);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void setup() {
|
||||
// setup network
|
||||
Ethernet.init(10);
|
||||
Ethernet.begin(mac, ip, router, router, subnet);
|
||||
server.begin();
|
||||
// USE_SERIAL.begin(921600);
|
||||
USE_SERIAL.begin(115200);
|
||||
|
||||
Serial.begin(115200);
|
||||
//Serial.setDebugOutput(true);
|
||||
//USE_SERIAL.setDebugOutput(true);
|
||||
|
||||
pinMode(DATA, OUTPUT);
|
||||
Ethernet.init(10);
|
||||
Ethernet.begin(mac, ip, router, router, subnet);
|
||||
|
||||
USE_SERIAL.println();
|
||||
USE_SERIAL.println();
|
||||
USE_SERIAL.println();
|
||||
|
||||
for(uint8_t t = 4; t > 0; t--) {
|
||||
USE_SERIAL.print("[SETUP] BOOT WAIT ");
|
||||
USE_SERIAL.print(t);
|
||||
USE_SERIAL.println("...");
|
||||
USE_SERIAL.flush();
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
webSocket.begin();
|
||||
webSocket.onEvent(webSocketEvent);
|
||||
|
||||
pinMode(DATA, OUTPUT);
|
||||
pinMode(CLOCK, OUTPUT);
|
||||
pinMode(LOAD, OUTPUT);
|
||||
|
||||
Serial.println("setup done");
|
||||
}
|
||||
|
||||
void send_block(image_t* p, int x, int y) {
|
||||
|
||||
void send_block(Image* p, int x, int y) {
|
||||
int order[32][2] = {
|
||||
{ 1, 1 }, // 1
|
||||
{ 1, 0 }, // 2
|
||||
@ -87,7 +158,7 @@ void send_block(image_t* p, int x, int y) {
|
||||
int x_offset = order[n][0];
|
||||
int y_offset = order[n][1];
|
||||
|
||||
byte pixel = get_pixel(p, x + x_offset, y + y_offset);
|
||||
byte pixel = p->get_pixel(x + x_offset, y + y_offset);
|
||||
digitalWrite(DATA, pixel);
|
||||
clock();
|
||||
}
|
||||
@ -96,7 +167,7 @@ void send_block(image_t* p, int x, int y) {
|
||||
clock();
|
||||
}
|
||||
|
||||
void send_image(image_t* img) {
|
||||
void send_image(Image* img) {
|
||||
for (int y = 0; y < MAX_HEIGHT; y += 8) {
|
||||
for (int x = 0; x < MAX_WIDTH; x += 4) {
|
||||
send_block(img, x, y);
|
||||
@ -119,131 +190,29 @@ void load() {
|
||||
// 0x00 0x00 0x00 0x00 0x00 0x00 0x00...
|
||||
// Width Height Delay Pixel
|
||||
|
||||
void default_image(image_t* p) {
|
||||
void default_image(Image* p) {
|
||||
static int offset = 0;
|
||||
|
||||
// reset image to maximum size
|
||||
set_size(p, 32767, 32767);
|
||||
p->set_size(32767, 32767);
|
||||
|
||||
// toggle all pixels in tilted bars
|
||||
int dim = max(p->width, p->height);
|
||||
int dim = max(p->getWidth(), p->getHeight());
|
||||
for (int n = 0; n < dim; n++) {
|
||||
int x = (n + offset) % p->width;
|
||||
int y = n % p->height;
|
||||
int x = (n + offset) % p->getWidth();
|
||||
int y = n % p->getHeight();
|
||||
|
||||
byte pixel = get_pixel(p, x, y);
|
||||
set_pixel(p, x, y, !pixel);
|
||||
byte pixel = p->get_pixel(x, y);
|
||||
p->set_pixel(x, y, !pixel);
|
||||
}
|
||||
offset++;
|
||||
}
|
||||
|
||||
bool read_header(EthernetClient cli, source_t* src) {
|
||||
// number of bytes already read from header
|
||||
static int offset = 0;
|
||||
// flag set, if header is complete
|
||||
bool complete = false;
|
||||
|
||||
while (offset < 6) {
|
||||
int value = cli.read();
|
||||
if (value == -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
switch (offset) {
|
||||
case 0:
|
||||
src->width = (value << 8);
|
||||
break;
|
||||
case 1:
|
||||
src->width |= value;
|
||||
break;
|
||||
case 2:
|
||||
src->height = (value << 8);
|
||||
break;
|
||||
case 3:
|
||||
src->height |= value;
|
||||
break;
|
||||
case 4:
|
||||
src->delay = (value << 8);
|
||||
break;
|
||||
case 5:
|
||||
src->delay |= value;
|
||||
break;
|
||||
}
|
||||
|
||||
offset++;
|
||||
}
|
||||
|
||||
if (offset > 5) {
|
||||
src->x = 0;
|
||||
src->y = 0;
|
||||
|
||||
offset = 0;
|
||||
complete = true;
|
||||
}
|
||||
|
||||
return complete;
|
||||
}
|
||||
|
||||
bool read_pixels(EthernetClient cli, source_t* src, image_t* img) {
|
||||
// copy dimension from header
|
||||
set_size(img, src->width, src->height);
|
||||
|
||||
while (true) {
|
||||
int value = cli.read();
|
||||
if (value == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
set_pixel(img, src->x, src->y, value);
|
||||
|
||||
src->x++;
|
||||
if (src->x >= src->width) {
|
||||
src->x = 0;
|
||||
src->y++;
|
||||
if (src->y >= src->height) {
|
||||
src->y = 0;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
static unsigned long last_activity = 0;
|
||||
static bool in_header = true;
|
||||
|
||||
// if an incoming client connects, there will be bytes available to read:
|
||||
EthernetClient client = server.available();
|
||||
if (client) {
|
||||
if (in_header == true) {
|
||||
if (read_header(client, &source) == true) {
|
||||
Serial.print("width=");
|
||||
Serial.print(source.width);
|
||||
Serial.print(" height=");
|
||||
Serial.print(source.height);
|
||||
Serial.print(" delay=");
|
||||
Serial.println(source.delay);
|
||||
|
||||
if ((source.width == 0) || (source.height == 0)) {
|
||||
Serial.println("invalid dimension");
|
||||
client.stop();
|
||||
} else {
|
||||
in_header = false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (read_pixels(client, &source, &image) == true) {
|
||||
Serial.println("pixels complete");
|
||||
in_header = true;
|
||||
webSocket.loop();
|
||||
|
||||
if (someOneIsConnected == false) {
|
||||
default_image(&image);
|
||||
send_image(&image);
|
||||
}
|
||||
}
|
||||
last_activity = millis();
|
||||
} else {
|
||||
if ((millis() - last_activity) > 60000) {
|
||||
default_image(&image);
|
||||
send_image(&image);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
244
WebGUI.html
Normal file
244
WebGUI.html
Normal file
@ -0,0 +1,244 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang=de">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>LED Board</title>
|
||||
<style>
|
||||
body {background-color: #ccc; margin: 0; padding: 0; text-align: center;}
|
||||
input {background-color: #ccc; border-radius: 2px;}
|
||||
.menu { background-color: rgb(4, 0, 59); color: #ccc; padding: 1.2em; width: 100%; box-sizing: border-box;}
|
||||
.panelRow{
|
||||
display: inline-block;
|
||||
font-size: 0;
|
||||
margin-top: 0px;}
|
||||
.pixel{
|
||||
float: left;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin-right:0px;
|
||||
background-color: #000;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="menu">
|
||||
IP: <input type="text" id="ip" value="10.23.42.24">
|
||||
<div style="width: 2em; display: inline-block;" ></div>
|
||||
Port: <input type="text" id="port" value="81">
|
||||
<div style="width: 2em; display: inline-block;" ></div>
|
||||
Panel width: <input type="text" id="width" value="32" style="width: 2.5em;" onchange="creatGUI()">
|
||||
height: <input type="text" id="height" value="40" style="width: 2.5em;" onchange="creatGUI()">
|
||||
<div style="width: 1em; display: inline-block;" ></div>
|
||||
Connection state: <div id="connectionState" onclick="toggleConnection()" style="background-color: red; display: inline-block; width: 1em; height: 1em; border-radius: 0.5em;"></div>
|
||||
|
||||
</div>
|
||||
|
||||
<input type="button" value="clear" onclick="clearCanvas()"><br />
|
||||
<canvas id="can" style="background-color:#000; margin: 1em;"></canvas>
|
||||
|
||||
<script type="text/javascript">
|
||||
var socket;
|
||||
document.onload = openWebSocket();
|
||||
var isTouch = (('ontouchstart' in window) || (navigator.msMaxTouchPoints > 0));
|
||||
|
||||
var canvas, ctx = false,
|
||||
prevX = 0,
|
||||
currX = 0,
|
||||
prevY = 0,
|
||||
currY = 0,
|
||||
dragMode = false;
|
||||
|
||||
var onColor = "#fd0";
|
||||
var offColor = "#310";
|
||||
|
||||
var pixelBuffer = new Array(0);
|
||||
var scaling;
|
||||
var xMax, yMax;
|
||||
var changeFlag = false;
|
||||
var frameRate = 3; //frames per second
|
||||
|
||||
creatGUI();
|
||||
setTimeout(frameRefresh, 1000 / frameRate);
|
||||
|
||||
|
||||
function openWebSocket()
|
||||
{
|
||||
ip = document.getElementById('ip').value;
|
||||
port = document.getElementById('port').value;
|
||||
panelWidthElement = document.getElementById('width');
|
||||
panelHeightElement = document.getElementById('height');
|
||||
|
||||
displayView = document.getElementById("can");
|
||||
console.log("connect to " + ip + ":" + port);
|
||||
|
||||
socket = new WebSocket('ws://' + ip + ':'+port+'', ['arduino']);
|
||||
socket.onopen = function ()
|
||||
{
|
||||
console.log('connected');
|
||||
document.getElementById('connectionState').style.backgroundColor = "#3d3";
|
||||
};
|
||||
|
||||
socket.onerror = function (error)
|
||||
{
|
||||
console.log('WebSocket Error ', error);
|
||||
document.getElementById('connectionState').style.backgroundColor = "#dd3";
|
||||
};
|
||||
|
||||
socket.onclose = function ()
|
||||
{
|
||||
console.log('WebSocket connection closed');
|
||||
document.getElementById('connectionState').style.backgroundColor = "#d33";
|
||||
};
|
||||
}
|
||||
|
||||
function frameRefresh()
|
||||
{
|
||||
if(changeFlag==true && socket.readyState === WebSocket.OPEN)
|
||||
{
|
||||
changeFlag = false;
|
||||
sendPixelBuffer();
|
||||
console.log("refreshed");
|
||||
}
|
||||
|
||||
setTimeout(frameRefresh, 1000 / frameRate);
|
||||
}
|
||||
|
||||
function toggleConnection()
|
||||
{
|
||||
if(socket.readyState === WebSocket.CLOSED)
|
||||
{
|
||||
openWebSocket();
|
||||
}
|
||||
else
|
||||
{
|
||||
socket.close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function sendPixelBuffer()
|
||||
{
|
||||
header = new Uint8Array(6);
|
||||
header[0] = Math.floor(xMax / 255);
|
||||
header[1] = xMax % 255;
|
||||
header[2] = Math.floor(yMax / 255);
|
||||
header[3] = yMax % 255;
|
||||
header[4] = 0;
|
||||
header[5] = 0;
|
||||
|
||||
socket.send(header);
|
||||
socket.send(pixelBuffer);
|
||||
}
|
||||
|
||||
function creatGUI()
|
||||
{
|
||||
scaling = 10;//(document.width / 2) / panelWidthElement.value;
|
||||
|
||||
xMax = panelWidthElement.value;
|
||||
yMax = panelHeightElement.value;
|
||||
displayView.width = panelWidthElement.value*scaling;// + "px";
|
||||
displayView.height = panelHeightElement.value*scaling;// + "px";
|
||||
|
||||
pixelBuffer = new Uint8Array(xMax*yMax);
|
||||
|
||||
initCanvas();
|
||||
}
|
||||
|
||||
function clearCanvas()
|
||||
{
|
||||
ctx = canvas.getContext("2d");
|
||||
ctx.clearRect(0, 0, displayView.width, displayView.height);
|
||||
|
||||
for(x = 0; x < xMax; x++)
|
||||
{
|
||||
|
||||
for(y = 0; y < yMax; y++)
|
||||
{
|
||||
pixelBuffer[x, xMax*y] = 0;
|
||||
drawDot(x*scaling+1, y*scaling +1, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function initCanvas() {
|
||||
canvas = document.getElementById('can');
|
||||
ctx = canvas.getContext("2d");
|
||||
w = canvas.width;
|
||||
h = canvas.height;
|
||||
|
||||
canvas.addEventListener("mousemove", function (e) {
|
||||
findxy('move', e)
|
||||
}, false);
|
||||
canvas.addEventListener("mousedown", function (e) {
|
||||
findxy('down', e)
|
||||
}, false);
|
||||
canvas.addEventListener("mouseup", function (e) {
|
||||
findxy('up', e)
|
||||
}, false);
|
||||
canvas.addEventListener("mouseout", function (e) {
|
||||
findxy('out', e)
|
||||
}, false);
|
||||
|
||||
clearCanvas();
|
||||
}
|
||||
|
||||
function findxy(res, e) {
|
||||
if (res == 'down') {
|
||||
prevX = currX;
|
||||
prevY = currY;
|
||||
currX = e.clientX - canvas.offsetLeft;
|
||||
currY = e.clientY - canvas.offsetTop;
|
||||
|
||||
drawDot(currX, currY);
|
||||
dragMode = true;
|
||||
}
|
||||
if (res == 'up' || res == "out") {
|
||||
dragMode = false;
|
||||
}
|
||||
if (res == 'move') {
|
||||
if (dragMode) {
|
||||
prevX = currX;
|
||||
prevY = currY;
|
||||
currX = e.clientX - canvas.offsetLeft;
|
||||
currY = e.clientY - canvas.offsetTop;
|
||||
drawDot(currX, currY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function drawDot(x, y, forceOff=false)
|
||||
{
|
||||
x = Math.floor(x / scaling);
|
||||
y = Math.floor(y / scaling);
|
||||
|
||||
if((pixelBuffer[x+xMax*y] == 0 || dragMode==true) && forceOff==false)
|
||||
{
|
||||
ctx.fillStyle = onColor;
|
||||
if(pixelBuffer[x+xMax*y] != 1)
|
||||
{
|
||||
changeFlag = true;
|
||||
}
|
||||
pixelBuffer[x+xMax*y] = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
ctx.fillStyle = offColor;
|
||||
|
||||
if(pixelBuffer[x+xMax*y] != 0)
|
||||
{
|
||||
changeFlag = true;
|
||||
}
|
||||
pixelBuffer[x+xMax*y] = 0;
|
||||
}
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.arc(x * scaling + scaling/2, y * scaling + scaling/2, scaling/2, 0, 2 * Math.PI);
|
||||
|
||||
ctx.fill();
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
43
image.cpp
43
image.cpp
@ -1,43 +0,0 @@
|
||||
#include "image.h"
|
||||
|
||||
bool check_bounds(image_t* p, int x, int y) {
|
||||
if (p == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((x < 0) || (y < 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((x >= p->width) || (y >= p->height)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
byte get_pixel(image_t* p, int x, int y) {
|
||||
if (check_bounds(p, x, y) == false) {
|
||||
return 0;
|
||||
}
|
||||
return p->data[y * p->width + x];
|
||||
}
|
||||
|
||||
void set_pixel(image_t* p, int x, int y, byte value) {
|
||||
if (check_bounds(p, x, y) == false) {
|
||||
return;
|
||||
}
|
||||
p->data[y * p->width + x] = value;
|
||||
}
|
||||
|
||||
void clear_pixels(image_t* p) {
|
||||
if (p == NULL) {
|
||||
return;
|
||||
}
|
||||
memset(p->data, 0, sizeof(p->data));
|
||||
}
|
||||
|
||||
void set_size(image_t* p, int width, int height) {
|
||||
p->width = min(width, MAX_WIDTH);
|
||||
p->height = min(height, MAX_HEIGHT);
|
||||
}
|
20
image.h
20
image.h
@ -1,20 +0,0 @@
|
||||
#include <Arduino.h>
|
||||
|
||||
#define MAX_WIDTH 32
|
||||
#define MAX_HEIGHT 40
|
||||
|
||||
struct _image_t {
|
||||
int width;
|
||||
int height;
|
||||
byte data[MAX_WIDTH * MAX_HEIGHT];
|
||||
};
|
||||
|
||||
typedef struct _image_t image_t;
|
||||
|
||||
byte get_pixel(image_t* p, int x, int y);
|
||||
|
||||
void set_pixel(image_t* p, int x, int y, byte value);
|
||||
|
||||
void clear_pixels(image_t* p);
|
||||
|
||||
void set_size(image_t* p, int width, int height);
|
77
src/ProtocolDL.cpp
Normal file
77
src/ProtocolDL.cpp
Normal file
@ -0,0 +1,77 @@
|
||||
#include "ProtocolDL.hpp"
|
||||
|
||||
ProtocolDL::ProtocolDL(Image& img):
|
||||
image(&img)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void ProtocolDL::newByte(uint8_t data)
|
||||
{
|
||||
switch(cnt)
|
||||
{
|
||||
case 0:
|
||||
complete = false;
|
||||
source.width = (data << 8);
|
||||
cnt = 1;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
source.width |= data;
|
||||
cnt = 2;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
source.height = (data << 8);
|
||||
cnt = 3;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
source.height |= data;
|
||||
cnt = 4;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
source.delay = (data << 8);
|
||||
cnt = 5;
|
||||
break;
|
||||
|
||||
case 5:
|
||||
source.delay |= data;
|
||||
image->set_size(source.width, source.height);
|
||||
source.x = 0;
|
||||
source.y = 0;
|
||||
cnt = 6;
|
||||
break;
|
||||
|
||||
default:
|
||||
image->set_pixel(source.x, source.y, data);
|
||||
|
||||
if(source.x == (source.width - 1) && source.y == (source.height - 1))
|
||||
{
|
||||
//this was the last pixel
|
||||
complete = true;
|
||||
cnt = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
source.x++;
|
||||
if (source.x >= source.width) {
|
||||
source.x = 0;
|
||||
source.y++;
|
||||
if (source.y >= source.height) {
|
||||
source.y = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
bool ProtocolDL::isComplete()
|
||||
{
|
||||
return complete;
|
||||
}
|
35
src/ProtocolDL.hpp
Normal file
35
src/ProtocolDL.hpp
Normal file
@ -0,0 +1,35 @@
|
||||
#ifndef PROTOCOL_DL_HPP
|
||||
#define PROTOCOL_DL_HPP
|
||||
|
||||
#include "image.hpp"
|
||||
|
||||
class ProtocolDL
|
||||
{
|
||||
public:
|
||||
ProtocolDL(Image& img);
|
||||
void newByte(uint8_t data);
|
||||
bool isComplete();
|
||||
|
||||
private:
|
||||
|
||||
|
||||
struct source_t {
|
||||
// width and height of current frame
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
|
||||
// delay until next frame is shown
|
||||
unsigned int delay;
|
||||
|
||||
// position of current pixel
|
||||
int x;
|
||||
int y;
|
||||
};
|
||||
|
||||
source_t source;
|
||||
uint16_t cnt = 0;
|
||||
bool complete = true;
|
||||
Image* image;
|
||||
};
|
||||
|
||||
#endif
|
592
src/WebSockets.cpp
Normal file
592
src/WebSockets.cpp
Normal file
@ -0,0 +1,592 @@
|
||||
/**
|
||||
* @file WebSockets.cpp
|
||||
* @date 20.05.2015
|
||||
* @author Markus Sattler
|
||||
*
|
||||
* Copyright (c) 2015 Markus Sattler. All rights reserved.
|
||||
* This file is part of the WebSockets for Arduino.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include "WebSockets.h"
|
||||
|
||||
#define LEGACY_SHA1
|
||||
|
||||
#ifdef ESP8266
|
||||
#include <core_esp8266_features.h>
|
||||
#endif
|
||||
|
||||
extern "C" {
|
||||
#ifdef CORE_HAS_LIBB64
|
||||
#include <libb64/cencode.h>
|
||||
#else
|
||||
#include "libb64/cencode_inc.h"
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef ESP8266
|
||||
#include <Hash.h>
|
||||
#else
|
||||
|
||||
|
||||
#ifdef LEGACY_SHA1
|
||||
#include "sha1/sha1.h"
|
||||
#else
|
||||
extern "C" {
|
||||
#include "libsha1/libsha1.h"
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#define WEBSOCKETS_MAX_HEADER_SIZE (14)
|
||||
|
||||
/**
|
||||
*
|
||||
* @param client WSclient_t * ptr to the client struct
|
||||
* @param code uint16_t see RFC
|
||||
* @param reason
|
||||
* @param reasonLen
|
||||
*/
|
||||
void WebSockets::clientDisconnect(WSclient_t * client, uint16_t code, char * reason, size_t reasonLen) {
|
||||
DEBUG_WEBSOCKETS("[WS][");
|
||||
DEBUG_WEBSOCKETS(client->num);
|
||||
DEBUG_WEBSOCKETS("][handleWebsocket] clientDisconnect code: ");
|
||||
DEBUG_WEBSOCKETS(code);
|
||||
DEBUG_WEBSOCKETS("\n");
|
||||
if(client->status == WSC_CONNECTED && code) {
|
||||
if(reason) {
|
||||
sendFrame(client, WSop_close, (uint8_t *) reason, reasonLen);
|
||||
} else {
|
||||
uint8_t buffer[2];
|
||||
buffer[0] = ((code >> 8) & 0xFF);
|
||||
buffer[1] = (code & 0xFF);
|
||||
sendFrame(client, WSop_close, &buffer[0], 2);
|
||||
}
|
||||
}
|
||||
clientDisconnect(client);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param client WSclient_t * ptr to the client struct
|
||||
* @param opcode WSopcode_t
|
||||
* @param payload uint8_t *
|
||||
* @param length size_t
|
||||
* @param mask bool add dummy mask to the frame (needed for web browser)
|
||||
* @param fin bool can be used to send data in more then one frame (set fin on the last frame)
|
||||
* @param headerToPayload bool set true if the payload has reserved 14 Byte at the beginning to dynamically add the Header (payload neet to be in RAM!)
|
||||
*/
|
||||
bool WebSockets::sendFrame(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool mask, bool fin, bool headerToPayload) {
|
||||
|
||||
if(client->tcp && !client->tcp->connected()) {
|
||||
DEBUG_WEBSOCKETS("[WS][");
|
||||
DEBUG_WEBSOCKETS(client->num);
|
||||
DEBUG_WEBSOCKETS("][sendFrame] not Connected!?\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(client->status != WSC_CONNECTED) {
|
||||
DEBUG_WEBSOCKETS("[WS][");
|
||||
DEBUG_WEBSOCKETS(client->num);
|
||||
DEBUG_WEBSOCKETS("][sendFrame] not in WSC_CONNECTED state!?\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
DEBUG_WEBSOCKETS("[WS][");
|
||||
DEBUG_WEBSOCKETS(client->num);
|
||||
DEBUG_WEBSOCKETS("][sendFrame] ------- send massage frame -------\n");
|
||||
|
||||
DEBUG_WEBSOCKETS("[WS][");
|
||||
DEBUG_WEBSOCKETS(client->num);
|
||||
DEBUG_WEBSOCKETS("][sendFrame] fin: ");
|
||||
DEBUG_WEBSOCKETS(fin);
|
||||
DEBUG_WEBSOCKETS(" opCode: ");
|
||||
DEBUG_WEBSOCKETS(opcode);
|
||||
DEBUG_WEBSOCKETS(" mask: ");
|
||||
DEBUG_WEBSOCKETS(mask);
|
||||
DEBUG_WEBSOCKETS(" length: ");
|
||||
DEBUG_WEBSOCKETS(length);
|
||||
DEBUG_WEBSOCKETS(" headerToPayload: ");
|
||||
DEBUG_WEBSOCKETS(headerToPayload);
|
||||
DEBUG_WEBSOCKETS("\n");
|
||||
|
||||
if(opcode == WSop_text) {
|
||||
DEBUG_WEBSOCKETS("[WS][");
|
||||
DEBUG_WEBSOCKETS(client->num);
|
||||
DEBUG_WEBSOCKETS("][sendFrame] text: ");
|
||||
DEBUG_WEBSOCKETS((char*) (payload + (headerToPayload ? 14 : 0)));
|
||||
DEBUG_WEBSOCKETS("\n");
|
||||
}
|
||||
|
||||
uint8_t maskKey[4] = { 0x00, 0x00, 0x00, 0x00 };
|
||||
uint8_t buffer[WEBSOCKETS_MAX_HEADER_SIZE] = { 0 };
|
||||
|
||||
uint8_t headerSize;
|
||||
uint8_t * headerPtr;
|
||||
uint8_t * payloadPtr = payload;
|
||||
bool useInternBuffer = false;
|
||||
bool ret = true;
|
||||
|
||||
// calculate header Size
|
||||
if(length < 126) {
|
||||
headerSize = 2;
|
||||
} else if(length < 0xFFFF) {
|
||||
headerSize = 4;
|
||||
} else {
|
||||
headerSize = 10;
|
||||
}
|
||||
|
||||
if(mask) {
|
||||
headerSize += 4;
|
||||
}
|
||||
|
||||
|
||||
#ifdef WEBSOCKETS_USE_BIG_MEM
|
||||
// only for ESP since AVR has less HEAP
|
||||
// try to send data in one TCP package (only if some free Heap is there)
|
||||
if(!headerToPayload && ((length > 0) && (length < 1400)) && (ESP.getFreeHeap() > 6000)) {
|
||||
DEBUG_WEBSOCKETS("[WS][");
|
||||
DEBUG_WEBSOCKETS(client->num);
|
||||
DEBUG_WEBSOCKETS("][sendFrame] pack to one TCP package...\n");
|
||||
|
||||
uint8_t * dataPtr = (uint8_t *) malloc(length + WEBSOCKETS_MAX_HEADER_SIZE);
|
||||
if(dataPtr) {
|
||||
memcpy((dataPtr + WEBSOCKETS_MAX_HEADER_SIZE), payload, length);
|
||||
headerToPayload = true;
|
||||
useInternBuffer = true;
|
||||
payloadPtr = dataPtr;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// set Header Pointer
|
||||
if(headerToPayload) {
|
||||
// calculate offset in payload
|
||||
headerPtr = (payloadPtr + (WEBSOCKETS_MAX_HEADER_SIZE - headerSize));
|
||||
} else {
|
||||
headerPtr = &buffer[0];
|
||||
}
|
||||
|
||||
// create header
|
||||
|
||||
// byte 0
|
||||
*headerPtr = 0x00;
|
||||
if(fin) {
|
||||
*headerPtr |= bit(7); ///< set Fin
|
||||
}
|
||||
*headerPtr |= opcode; ///< set opcode
|
||||
headerPtr++;
|
||||
|
||||
// byte 1
|
||||
*headerPtr = 0x00;
|
||||
if(mask) {
|
||||
*headerPtr |= bit(7); ///< set mask
|
||||
}
|
||||
|
||||
if(length < 126) {
|
||||
*headerPtr |= length; headerPtr++;
|
||||
} else if(length < 0xFFFF) {
|
||||
*headerPtr |= 126; headerPtr++;
|
||||
*headerPtr = ((length >> 8) & 0xFF); headerPtr++;
|
||||
*headerPtr = (length & 0xFF); headerPtr++;
|
||||
} else {
|
||||
// Normally we never get here (to less memory)
|
||||
*headerPtr |= 127; headerPtr++;
|
||||
*headerPtr = 0x00; headerPtr++;
|
||||
*headerPtr = 0x00; headerPtr++;
|
||||
*headerPtr = 0x00; headerPtr++;
|
||||
*headerPtr = 0x00; headerPtr++;
|
||||
*headerPtr = ((length >> 24) & 0xFF); headerPtr++;
|
||||
*headerPtr = ((length >> 16) & 0xFF); headerPtr++;
|
||||
*headerPtr = ((length >> 8) & 0xFF); headerPtr++;
|
||||
*headerPtr = (length & 0xFF); headerPtr++;
|
||||
}
|
||||
|
||||
if(mask) {
|
||||
if(useInternBuffer) {
|
||||
// if we use a Intern Buffer we can modify the data
|
||||
// by this fact its possible the do the masking
|
||||
for(uint8_t x = 0; x < sizeof(maskKey); x++) {
|
||||
maskKey[x] = random(0xFF);
|
||||
*headerPtr = maskKey[x]; headerPtr++;
|
||||
}
|
||||
|
||||
uint8_t * dataMaskPtr;
|
||||
|
||||
if(headerToPayload) {
|
||||
dataMaskPtr = (payloadPtr + WEBSOCKETS_MAX_HEADER_SIZE);
|
||||
} else {
|
||||
dataMaskPtr = payloadPtr;
|
||||
}
|
||||
|
||||
for(size_t x = 0; x < length; x++) {
|
||||
dataMaskPtr[x] = (dataMaskPtr[x] ^ maskKey[x % 4]);
|
||||
}
|
||||
|
||||
} else {
|
||||
*headerPtr = maskKey[0]; headerPtr++;
|
||||
*headerPtr = maskKey[1]; headerPtr++;
|
||||
*headerPtr = maskKey[2]; headerPtr++;
|
||||
*headerPtr = maskKey[3]; headerPtr++;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef NODEBUG_WEBSOCKETS
|
||||
unsigned long start = micros();
|
||||
#endif
|
||||
|
||||
if(headerToPayload) {
|
||||
// header has be added to payload
|
||||
// payload is forced to reserved 14 Byte but we may not need all based on the length and mask settings
|
||||
// offset in payload is calculatetd 14 - headerSize
|
||||
if (client->tcp->write(&payloadPtr[(WEBSOCKETS_MAX_HEADER_SIZE - headerSize)], (length + headerSize)) != (length + headerSize)) {
|
||||
ret = false;
|
||||
}
|
||||
} else {
|
||||
// send header
|
||||
if (client->tcp->write(&buffer[0], headerSize) != headerSize) {
|
||||
ret = false;
|
||||
}
|
||||
|
||||
if(payloadPtr && length > 0) {
|
||||
// send payload
|
||||
if(client->tcp->write(&payloadPtr[0], length) != length) {
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG_WEBSOCKETS("[WS][");
|
||||
DEBUG_WEBSOCKETS(client->num);
|
||||
DEBUG_WEBSOCKETS("][sendFrame] sending Frame Done (");
|
||||
DEBUG_WEBSOCKETS((micros() - start));
|
||||
DEBUG_WEBSOCKETS("us).\n");
|
||||
|
||||
#ifdef WEBSOCKETS_USE_BIG_MEM
|
||||
if(useInternBuffer && payloadPtr) {
|
||||
free(payloadPtr);
|
||||
}
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* handle the WebSocket stream
|
||||
* @param client WSclient_t * ptr to the client struct
|
||||
*/
|
||||
void WebSockets::handleWebsocket(WSclient_t * client) {
|
||||
|
||||
uint8_t buffer[8] = { 0 };
|
||||
|
||||
bool fin;
|
||||
bool rsv1;
|
||||
bool rsv2;
|
||||
bool rsv3;
|
||||
WSopcode_t opCode;
|
||||
bool mask;
|
||||
size_t payloadLen;
|
||||
|
||||
uint8_t maskKey[4];
|
||||
|
||||
uint8_t * payload = NULL;
|
||||
|
||||
DEBUG_WEBSOCKETS("[WS][");
|
||||
DEBUG_WEBSOCKETS(client->num);
|
||||
DEBUG_WEBSOCKETS("][handleWebsocket] ------- read massage frame -------\n");
|
||||
|
||||
if(!readWait(client, buffer, 2)) {
|
||||
//timeout
|
||||
clientDisconnect(client, 1002);
|
||||
return;
|
||||
}
|
||||
|
||||
// split first 2 bytes in the data
|
||||
fin = ((buffer[0] >> 7) & 0x01);
|
||||
rsv1 = ((buffer[0] >> 6) & 0x01);
|
||||
rsv2 = ((buffer[0] >> 5) & 0x01);
|
||||
rsv3 = ((buffer[0] >> 4) & 0x01);
|
||||
opCode = (WSopcode_t) (buffer[0] & 0x0F);
|
||||
|
||||
mask = ((buffer[1] >> 7) & 0x01);
|
||||
payloadLen = (WSopcode_t) (buffer[1] & 0x7F);
|
||||
|
||||
if(payloadLen == 126) {
|
||||
if(!readWait(client, buffer, 2)) {
|
||||
//timeout
|
||||
clientDisconnect(client, 1002);
|
||||
return;
|
||||
}
|
||||
payloadLen = buffer[0] << 8 | buffer[1];
|
||||
} else if(payloadLen == 127) {
|
||||
// read 64bit integer as length
|
||||
if(!readWait(client, buffer, 8)) {
|
||||
//timeout
|
||||
clientDisconnect(client, 1002);
|
||||
return;
|
||||
}
|
||||
|
||||
if(buffer[0] != 0 || buffer[1] != 0 || buffer[2] != 0 || buffer[3] != 0) {
|
||||
// really to big!
|
||||
payloadLen = 0xFFFFFFFF;
|
||||
} else {
|
||||
payloadLen = buffer[4] << 24 | buffer[5] << 16 | buffer[6] << 8 | buffer[7];
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG_WEBSOCKETS("[WS][");
|
||||
DEBUG_WEBSOCKETS(client->num);
|
||||
DEBUG_WEBSOCKETS("][handleWebsocket] fin: ");
|
||||
DEBUG_WEBSOCKETS(fin);
|
||||
DEBUG_WEBSOCKETS(" rsv1: ");
|
||||
DEBUG_WEBSOCKETS(rsv1);
|
||||
DEBUG_WEBSOCKETS(" rsv2: ");
|
||||
DEBUG_WEBSOCKETS(rsv2);
|
||||
DEBUG_WEBSOCKETS(" rsv3: ");
|
||||
DEBUG_WEBSOCKETS(rsv3);
|
||||
DEBUG_WEBSOCKETS(" opCode: ");
|
||||
DEBUG_WEBSOCKETS(opCode);
|
||||
DEBUG_WEBSOCKETS("\n");
|
||||
|
||||
DEBUG_WEBSOCKETS("[WS][");
|
||||
DEBUG_WEBSOCKETS(client->num);
|
||||
DEBUG_WEBSOCKETS("][handleWebsocket] mask: ");
|
||||
DEBUG_WEBSOCKETS(mask);
|
||||
DEBUG_WEBSOCKETS(" payloadLen: ");
|
||||
DEBUG_WEBSOCKETS(payloadLen);
|
||||
DEBUG_WEBSOCKETS("\n");
|
||||
|
||||
if(payloadLen > WEBSOCKETS_MAX_DATA_SIZE) {
|
||||
DEBUG_WEBSOCKETS("[WS][");
|
||||
DEBUG_WEBSOCKETS(client->num);
|
||||
DEBUG_WEBSOCKETS("][handleWebsocket] payload to big! (");
|
||||
DEBUG_WEBSOCKETS(payloadLen);
|
||||
DEBUG_WEBSOCKETS(")\n");
|
||||
clientDisconnect(client, 1009);
|
||||
return;
|
||||
}
|
||||
|
||||
if(mask) {
|
||||
if(!readWait(client, maskKey, 4)) {
|
||||
//timeout
|
||||
clientDisconnect(client, 1002);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(payloadLen > 0) {
|
||||
// if text data we need one more
|
||||
payload = (uint8_t *) malloc(payloadLen + 1);
|
||||
|
||||
if(!payload) {
|
||||
DEBUG_WEBSOCKETS("[WS][");
|
||||
DEBUG_WEBSOCKETS(client->num);
|
||||
DEBUG_WEBSOCKETS("][handleWebsocket] to less memory to handle payload ");
|
||||
DEBUG_WEBSOCKETS(payloadLen);
|
||||
DEBUG_WEBSOCKETS("!\n");
|
||||
clientDisconnect(client, 1011);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!readWait(client, payload, payloadLen)) {
|
||||
DEBUG_WEBSOCKETS("[WS][");
|
||||
DEBUG_WEBSOCKETS(client->num);
|
||||
DEBUG_WEBSOCKETS("][handleWebsocket] missing data!\n");
|
||||
free(payload);
|
||||
clientDisconnect(client, 1002);
|
||||
return;
|
||||
}
|
||||
|
||||
payload[payloadLen] = 0x00;
|
||||
|
||||
if(mask) {
|
||||
//decode XOR
|
||||
for(size_t i = 0; i < payloadLen; i++) {
|
||||
payload[i] = (payload[i] ^ maskKey[i % 4]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch(opCode) {
|
||||
case WSop_text:
|
||||
DEBUG_WEBSOCKETS("[WS][");
|
||||
DEBUG_WEBSOCKETS(client->num);
|
||||
DEBUG_WEBSOCKETS("][handleWebsocket] text: ");
|
||||
DEBUG_WEBSOCKETS((char*) payload);
|
||||
DEBUG_WEBSOCKETS("\n");
|
||||
// no break here!
|
||||
case WSop_binary:
|
||||
messageRecived(client, opCode, payload, payloadLen);
|
||||
break;
|
||||
case WSop_ping:
|
||||
// send pong back
|
||||
sendFrame(client, WSop_pong, payload, payloadLen);
|
||||
break;
|
||||
case WSop_pong:
|
||||
DEBUG_WEBSOCKETS("[WS][");
|
||||
DEBUG_WEBSOCKETS(client->num);
|
||||
DEBUG_WEBSOCKETS("][handleWebsocket] get pong (");
|
||||
DEBUG_WEBSOCKETS((char*) payload);
|
||||
DEBUG_WEBSOCKETS(")\n");
|
||||
break;
|
||||
case WSop_close:
|
||||
{
|
||||
uint16_t reasonCode = 1000;
|
||||
if(payloadLen >= 2) {
|
||||
reasonCode = payload[0] << 8 | payload[1];
|
||||
}
|
||||
|
||||
DEBUG_WEBSOCKETS("[WS][");
|
||||
DEBUG_WEBSOCKETS(client->num);
|
||||
DEBUG_WEBSOCKETS("][handleWebsocket] get ask for close. Code: ");
|
||||
DEBUG_WEBSOCKETS(reasonCode);
|
||||
if(payloadLen > 2) {
|
||||
DEBUG_WEBSOCKETS(" (");
|
||||
DEBUG_WEBSOCKETS((char*) (payload+2));
|
||||
DEBUG_WEBSOCKETS(")\n");
|
||||
} else {
|
||||
DEBUG_WEBSOCKETS("\n");
|
||||
}
|
||||
clientDisconnect(client, 1000);
|
||||
}
|
||||
break;
|
||||
case WSop_continuation:
|
||||
// continuation is not supported
|
||||
clientDisconnect(client, 1003);
|
||||
break;
|
||||
default:
|
||||
clientDisconnect(client, 1002);
|
||||
break;
|
||||
}
|
||||
|
||||
if(payload) {
|
||||
free(payload);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* generate the key for Sec-WebSocket-Accept
|
||||
* @param clientKey String
|
||||
* @return String Accept Key
|
||||
*/
|
||||
String WebSockets::acceptKey(String clientKey) {
|
||||
uint8_t sha1HashBin[20] = { 0 };
|
||||
#ifdef ESP8266
|
||||
sha1(clientKey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11", &sha1HashBin[0]);
|
||||
#else
|
||||
clientKey += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
||||
#ifdef LEGACY_SHA1
|
||||
// Miha Nahtigal
|
||||
char cbuff[clientKey.length()+1];
|
||||
clientKey.toCharArray(cbuff, clientKey.length()+1);//Converts String into character array
|
||||
Sha1.init();
|
||||
Sha1.print(cbuff);
|
||||
strcpy(sha1HashBin, Sha1.result());
|
||||
Serial.println(cbuff);
|
||||
//old:uint8_t *sha1HashBin = Sha1.result();
|
||||
#else
|
||||
SHA1_CTX ctx;
|
||||
SHA1Init(&ctx);
|
||||
SHA1Update(&ctx, (const unsigned char*)clientKey.c_str(), clientKey.length());
|
||||
SHA1Final(&sha1HashBin[0], &ctx);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
String key = base64_encode(sha1HashBin, 20);
|
||||
key.trim();
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* base64_encode
|
||||
* @param data uint8_t *
|
||||
* @param length size_t
|
||||
* @return base64 encoded String
|
||||
*/
|
||||
String WebSockets::base64_encode(uint8_t * data, size_t length) {
|
||||
size_t size = ((length*1.6f)+1);
|
||||
char * buffer = (char *) malloc(size);
|
||||
if(buffer) {
|
||||
base64_encodestate _state;
|
||||
base64_init_encodestate(&_state);
|
||||
int len = base64_encode_block((const char *) &data[0], length, &buffer[0], &_state);
|
||||
len = base64_encode_blockend((buffer + len), &_state);
|
||||
|
||||
String base64 = String(buffer);
|
||||
free(buffer);
|
||||
return base64;
|
||||
}
|
||||
return String("-FAIL-");
|
||||
}
|
||||
|
||||
/**
|
||||
* read x byte from tcp or get timeout
|
||||
* @param client WSclient_t *
|
||||
* @param out uint8_t * data buffer
|
||||
* @param n size_t byte count
|
||||
* @return true if ok
|
||||
*/
|
||||
bool WebSockets::readWait(WSclient_t * client, uint8_t *out, size_t n) {
|
||||
unsigned long t = millis();
|
||||
size_t len;
|
||||
|
||||
while(n > 0) {
|
||||
if(!client->tcp) {
|
||||
DEBUG_WEBSOCKETS("[readWait] tcp is null!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!client->tcp->connected()) {
|
||||
DEBUG_WEBSOCKETS("[readWait] not connected!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if((millis() - t) > WEBSOCKETS_TCP_TIMEOUT) {
|
||||
DEBUG_WEBSOCKETS("[readWait] receive TIMEOUT!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!client->tcp->available()) {
|
||||
#ifdef ESP8266
|
||||
delay(0);
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
|
||||
len = client->tcp->read((uint8_t*) out, n);
|
||||
if(len) {
|
||||
t = millis();
|
||||
out += len;
|
||||
n -= len;
|
||||
//DEBUG_WEBSOCKETS("Receive %d left %d!\n", len, n);
|
||||
} else {
|
||||
//DEBUG_WEBSOCKETS("Receive %d left %d!\n", len, n);
|
||||
}
|
||||
//DEBUG_WEBSOCKETS("Receive ");
|
||||
//DEBUG_WEBSOCKETS(len);
|
||||
//DEBUG_WEBSOCKETS("left ");
|
||||
//DEBUG_WEBSOCKETS(n);
|
||||
//DEBUG_WEBSOCKETS("!\n");
|
||||
#ifdef ESP8266
|
||||
delay(0);
|
||||
#endif
|
||||
}
|
||||
return true;
|
||||
}
|
159
src/WebSockets.h
Normal file
159
src/WebSockets.h
Normal file
@ -0,0 +1,159 @@
|
||||
/**
|
||||
* @file WebSockets.h
|
||||
* @date 20.05.2015
|
||||
* @author Markus Sattler
|
||||
*
|
||||
* Copyright (c) 2015 Markus Sattler. All rights reserved.
|
||||
* This file is part of the WebSockets for Arduino.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef WEBSOCKETS_H_
|
||||
#define WEBSOCKETS_H_
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
//#define DEBUG_WEBSOCKETS(...) Serial.print( __VA_ARGS__ )
|
||||
|
||||
#ifndef DEBUG_WEBSOCKETS
|
||||
#define DEBUG_WEBSOCKETS(...)
|
||||
#define NODEBUG_WEBSOCKETS
|
||||
#endif
|
||||
|
||||
#ifdef ESP8266
|
||||
#define WEBSOCKETS_MAX_DATA_SIZE (15*1024)
|
||||
#define WEBSOCKETS_USE_BIG_MEM
|
||||
#else
|
||||
//atmega328p has only 2KB ram!
|
||||
#define WEBSOCKETS_MAX_DATA_SIZE (2048)
|
||||
#endif
|
||||
|
||||
#define WEBSOCKETS_TCP_TIMEOUT (1500)
|
||||
|
||||
#define NETWORK_ESP8266 (1)
|
||||
#define NETWORK_W5100 (2)
|
||||
#define NETWORK_ENC28J60 (3)
|
||||
|
||||
|
||||
// select Network type based
|
||||
#ifdef ESP8266
|
||||
#define WEBSOCKETS_NETWORK_TYPE NETWORK_ESP8266
|
||||
#else
|
||||
#define WEBSOCKETS_NETWORK_TYPE NETWORK_W5100
|
||||
#endif
|
||||
|
||||
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
||||
|
||||
#ifndef ESP8266
|
||||
#error "network type ESP8266 only possible on the ESP mcu!"
|
||||
#endif
|
||||
|
||||
#include <ESP8266WiFi.h>
|
||||
#define WEBSOCKETS_NETWORK_CLASS WiFiClient
|
||||
#define WEBSOCKETS_NETWORK_SERVER_CLASS WiFiServer
|
||||
|
||||
#elif (WEBSOCKETS_NETWORK_TYPE == NETWORK_W5100)
|
||||
|
||||
#include <Ethernet.h>
|
||||
#include <SPI.h>
|
||||
#define WEBSOCKETS_NETWORK_CLASS EthernetClient
|
||||
#define WEBSOCKETS_NETWORK_SERVER_CLASS EthernetServer
|
||||
|
||||
#elif (WEBSOCKETS_NETWORK_TYPE == NETWORK_ENC28J60)
|
||||
|
||||
#include <UIPEthernet.h>
|
||||
#define WEBSOCKETS_NETWORK_CLASS UIPClient
|
||||
#define WEBSOCKETS_NETWORK_SERVER_CLASS UIPServer
|
||||
|
||||
#else
|
||||
#error "no network type selected!"
|
||||
#endif
|
||||
|
||||
|
||||
typedef enum {
|
||||
WSC_NOT_CONNECTED,
|
||||
WSC_HEADER,
|
||||
WSC_CONNECTED
|
||||
} WSclientsStatus_t;
|
||||
|
||||
typedef enum {
|
||||
WStype_ERROR,
|
||||
WStype_DISCONNECTED,
|
||||
WStype_CONNECTED,
|
||||
WStype_TEXT,
|
||||
WStype_BIN
|
||||
} WStype_t;
|
||||
|
||||
typedef enum {
|
||||
WSop_continuation = 0x00, ///< %x0 denotes a continuation frame
|
||||
WSop_text = 0x01, ///< %x1 denotes a text frame
|
||||
WSop_binary = 0x02, ///< %x2 denotes a binary frame
|
||||
///< %x3-7 are reserved for further non-control frames
|
||||
WSop_close = 0x08, ///< %x8 denotes a connection close
|
||||
WSop_ping = 0x09, ///< %x9 denotes a ping
|
||||
WSop_pong = 0x0A ///< %xA denotes a pong
|
||||
///< %xB-F are reserved for further control frames
|
||||
} WSopcode_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t num; ///< connection number
|
||||
|
||||
WSclientsStatus_t status;
|
||||
|
||||
WEBSOCKETS_NETWORK_CLASS * tcp;
|
||||
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
||||
bool isSSL; ///< run in ssl mode
|
||||
WiFiClientSecure * ssl;
|
||||
#endif
|
||||
|
||||
String cUrl; ///< http url
|
||||
uint16_t cCode; ///< http code
|
||||
|
||||
bool cIsUpgrade; ///< Connection == Upgrade
|
||||
bool cIsWebsocket; ///< Upgrade == websocket
|
||||
|
||||
String cKey; ///< client Sec-WebSocket-Key
|
||||
String cAccept; ///< client Sec-WebSocket-Accept
|
||||
String cProtocol; ///< client Sec-WebSocket-Protocol
|
||||
String cExtensions; ///< client Sec-WebSocket-Extensions
|
||||
uint16_t cVersion; ///< client Sec-WebSocket-Version
|
||||
|
||||
} WSclient_t;
|
||||
|
||||
class WebSockets {
|
||||
protected:
|
||||
virtual void clientDisconnect(WSclient_t * client);
|
||||
virtual bool clientIsConnected(WSclient_t * client);
|
||||
|
||||
virtual void messageRecived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length);
|
||||
|
||||
void clientDisconnect(WSclient_t * client, uint16_t code, char * reason = NULL, size_t reasonLen = 0);
|
||||
bool sendFrame(WSclient_t * client, WSopcode_t opcode, uint8_t * payload = NULL, size_t length = 0, bool mask = false, bool fin = true, bool headerToPayload = false);
|
||||
|
||||
|
||||
void handleWebsocket(WSclient_t * client);
|
||||
|
||||
bool readWait(WSclient_t * client, uint8_t *out, size_t n);
|
||||
|
||||
String acceptKey(String clientKey);
|
||||
String base64_encode(uint8_t * data, size_t length);
|
||||
|
||||
};
|
||||
|
||||
#endif /* WEBSOCKETS_H_ */
|
506
src/WebSocketsClient.cpp
Normal file
506
src/WebSocketsClient.cpp
Normal file
@ -0,0 +1,506 @@
|
||||
/**
|
||||
* @file WebSocketsClient.cpp
|
||||
* @date 20.05.2015
|
||||
* @author Markus Sattler
|
||||
*
|
||||
* Copyright (c) 2015 Markus Sattler. All rights reserved.
|
||||
* This file is part of the WebSockets for Arduino.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include "WebSockets.h"
|
||||
#include "WebSocketsClient.h"
|
||||
|
||||
WebSocketsClient::WebSocketsClient() {
|
||||
_cbEvent = NULL;
|
||||
_client.num = 0;
|
||||
}
|
||||
|
||||
WebSocketsClient::~WebSocketsClient() {
|
||||
disconnect();
|
||||
}
|
||||
|
||||
/**
|
||||
* calles to init the Websockets server
|
||||
*/
|
||||
void WebSocketsClient::begin(const char *host, uint16_t port, const char * url) {
|
||||
_host = host;
|
||||
_port = port;
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
||||
_fingerprint = "";
|
||||
#endif
|
||||
|
||||
_client.num = 0;
|
||||
_client.status = WSC_NOT_CONNECTED;
|
||||
_client.tcp = NULL;
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
||||
_client.isSSL = false;
|
||||
_client.ssl = NULL;
|
||||
#endif
|
||||
_client.cUrl = url;
|
||||
_client.cCode = 0;
|
||||
_client.cIsUpgrade = false;
|
||||
_client.cIsWebsocket = true;
|
||||
_client.cKey = "";
|
||||
_client.cAccept = "";
|
||||
_client.cProtocol = "";
|
||||
_client.cExtensions = "";
|
||||
_client.cVersion = 0;
|
||||
|
||||
#ifdef ESP8266
|
||||
randomSeed(RANDOM_REG32);
|
||||
#else
|
||||
// todo find better seed
|
||||
randomSeed(millis());
|
||||
#endif
|
||||
}
|
||||
|
||||
void WebSocketsClient::begin(String host, uint16_t port, String url) {
|
||||
begin(host.c_str(), port, url.c_str());
|
||||
}
|
||||
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
||||
void WebSocketsClient::beginSSL(const char *host, uint16_t port, const char * url, const char * fingerprint) {
|
||||
begin(host, port, url);
|
||||
_client.isSSL = true;
|
||||
_fingerprint = fingerprint;
|
||||
}
|
||||
|
||||
void WebSocketsClient::beginSSL(String host, uint16_t port, String url, String fingerprint) {
|
||||
beginSSL(host.c_str(), port, url.c_str(), fingerprint.c_str());
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* called in arduino loop
|
||||
*/
|
||||
void WebSocketsClient::loop(void) {
|
||||
if(!clientIsConnected(&_client)) {
|
||||
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
||||
if(_client.isSSL) {
|
||||
DEBUG_WEBSOCKETS("[WS-Client] connect wss...\n");
|
||||
if(_client.ssl) {
|
||||
delete _client.ssl;
|
||||
_client.ssl = NULL;
|
||||
_client.tcp = NULL;
|
||||
}
|
||||
_client.ssl = new WiFiClientSecure();
|
||||
_client.tcp = _client.ssl;
|
||||
} else {
|
||||
DEBUG_WEBSOCKETS("[WS-Client] connect ws...\n");
|
||||
if(_client.tcp) {
|
||||
delete _client.tcp;
|
||||
_client.tcp = NULL;
|
||||
}
|
||||
_client.tcp = new WiFiClient();
|
||||
}
|
||||
#else
|
||||
_client.tcp = new WEBSOCKETS_NETWORK_CLASS();
|
||||
#endif
|
||||
|
||||
if(!_client.tcp) {
|
||||
DEBUG_WEBSOCKETS("[WS-Client] creating Network class failed!");
|
||||
return;
|
||||
}
|
||||
|
||||
if(_client.tcp->connect(_host.c_str(), _port)) {
|
||||
DEBUG_WEBSOCKETS("[WS-Client] connected to %s:%u.\n");
|
||||
//DEBUG_WEBSOCKETS(_host.c_str());
|
||||
//DEBUG_WEBSOCKETS(_port);
|
||||
|
||||
_client.status = WSC_HEADER;
|
||||
|
||||
// set Timeout for readBytesUntil and readStringUntil
|
||||
_client.tcp->setTimeout(WEBSOCKETS_TCP_TIMEOUT);
|
||||
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
||||
_client.tcp->setNoDelay(true);
|
||||
|
||||
if(_client.isSSL && _fingerprint.length()) {
|
||||
if(!_client.ssl->verify(_fingerprint.c_str(), _host.c_str())) {
|
||||
DEBUG_WEBSOCKETS("[WS-Client] certificate mismatch\n");
|
||||
WebSockets::clientDisconnect(&_client, 1000);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// send Header to Server
|
||||
sendHeader(&_client);
|
||||
|
||||
} else {
|
||||
DEBUG_WEBSOCKETS("[WS-Client] connection to %s:%u Faild\n");
|
||||
///EBUG_WEBSOCKETS(_host.c_str());
|
||||
//DEBUG_WEBSOCKETS(_port);
|
||||
delay(10); //some litle delay to not flood the server
|
||||
}
|
||||
} else {
|
||||
handleClientData();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* set callback function
|
||||
* @param cbEvent WebSocketServerEvent
|
||||
*/
|
||||
void WebSocketsClient::onEvent(WebSocketClientEvent cbEvent) {
|
||||
_cbEvent = cbEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* send text data to client
|
||||
* @param num uint8_t client id
|
||||
* @param payload uint8_t *
|
||||
* @param length size_t
|
||||
* @param headerToPayload bool (see sendFrame for more details)
|
||||
*/
|
||||
void WebSocketsClient::sendTXT(uint8_t * payload, size_t length, bool headerToPayload) {
|
||||
if(length == 0) {
|
||||
length = strlen((const char *) payload);
|
||||
}
|
||||
if(clientIsConnected(&_client)) {
|
||||
sendFrame(&_client, WSop_text, payload, length, true, true, headerToPayload);
|
||||
}
|
||||
}
|
||||
|
||||
void WebSocketsClient::sendTXT(const uint8_t * payload, size_t length) {
|
||||
sendTXT((uint8_t *) payload, length);
|
||||
}
|
||||
|
||||
void WebSocketsClient::sendTXT(char * payload, size_t length, bool headerToPayload) {
|
||||
sendTXT((uint8_t *) payload, length, headerToPayload);
|
||||
}
|
||||
|
||||
void WebSocketsClient::sendTXT(const char * payload, size_t length) {
|
||||
sendTXT((uint8_t *) payload, length);
|
||||
}
|
||||
|
||||
void WebSocketsClient::sendTXT(String payload) {
|
||||
sendTXT((uint8_t *) payload.c_str(), payload.length());
|
||||
}
|
||||
|
||||
/**
|
||||
* send binary data to client
|
||||
* @param num uint8_t client id
|
||||
* @param payload uint8_t *
|
||||
* @param length size_t
|
||||
* @param headerToPayload bool (see sendFrame for more details)
|
||||
*/
|
||||
void WebSocketsClient::sendBIN(uint8_t * payload, size_t length, bool headerToPayload) {
|
||||
if(clientIsConnected(&_client)) {
|
||||
sendFrame(&_client, WSop_binary, payload, length, true, true, headerToPayload);
|
||||
}
|
||||
}
|
||||
|
||||
void WebSocketsClient::sendBIN(const uint8_t * payload, size_t length) {
|
||||
sendBIN((uint8_t *) payload, length);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* disconnect one client
|
||||
* @param num uint8_t client id
|
||||
*/
|
||||
void WebSocketsClient::disconnect(void) {
|
||||
if(clientIsConnected(&_client)) {
|
||||
WebSockets::clientDisconnect(&_client, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
//#################################################################################
|
||||
//#################################################################################
|
||||
//#################################################################################
|
||||
|
||||
/**
|
||||
*
|
||||
* @param client WSclient_t * ptr to the client struct
|
||||
* @param opcode WSopcode_t
|
||||
* @param payload uint8_t *
|
||||
* @param lenght size_t
|
||||
*/
|
||||
void WebSocketsClient::messageRecived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t lenght) {
|
||||
WStype_t type = WStype_ERROR;
|
||||
|
||||
switch(opcode) {
|
||||
case WSop_text:
|
||||
type = WStype_TEXT;
|
||||
break;
|
||||
case WSop_binary:
|
||||
type = WStype_BIN;
|
||||
break;
|
||||
}
|
||||
|
||||
runCbEvent(type, payload, lenght);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnect an client
|
||||
* @param client WSclient_t * ptr to the client struct
|
||||
*/
|
||||
void WebSocketsClient::clientDisconnect(WSclient_t * client) {
|
||||
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
||||
if(client->isSSL && client->ssl) {
|
||||
if(client->ssl->connected()) {
|
||||
client->ssl->flush();
|
||||
client->ssl->stop();
|
||||
}
|
||||
delete client->ssl;
|
||||
client->ssl = NULL;
|
||||
client->tcp = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
if(client->tcp) {
|
||||
if(client->tcp->connected()) {
|
||||
client->tcp->flush();
|
||||
client->tcp->stop();
|
||||
}
|
||||
delete client->tcp;
|
||||
client->tcp = NULL;
|
||||
}
|
||||
|
||||
client->cCode = 0;
|
||||
client->cKey = "";
|
||||
client->cAccept = "";
|
||||
client->cProtocol = "";
|
||||
client->cVersion = 0;
|
||||
client->cIsUpgrade = false;
|
||||
client->cIsWebsocket = false;
|
||||
|
||||
client->status = WSC_NOT_CONNECTED;
|
||||
|
||||
DEBUG_WEBSOCKETS("[WS-Client] client disconnected.\n");
|
||||
|
||||
runCbEvent(WStype_DISCONNECTED, NULL, 0);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* get client state
|
||||
* @param client WSclient_t * ptr to the client struct
|
||||
* @return true = conneted
|
||||
*/
|
||||
bool WebSocketsClient::clientIsConnected(WSclient_t * client) {
|
||||
|
||||
if(!client->tcp) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(client->tcp->connected()) {
|
||||
if(client->status != WSC_NOT_CONNECTED) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
// client lost
|
||||
if(client->status != WSC_NOT_CONNECTED) {
|
||||
DEBUG_WEBSOCKETS("[WS-Client] connection lost.\n");
|
||||
// do cleanup
|
||||
clientDisconnect(client);
|
||||
}
|
||||
}
|
||||
|
||||
if(client->tcp) {
|
||||
// do cleanup
|
||||
clientDisconnect(client);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handel incomming data from Client
|
||||
*/
|
||||
void WebSocketsClient::handleClientData(void) {
|
||||
int len = _client.tcp->available();
|
||||
if(len > 0) {
|
||||
switch(_client.status) {
|
||||
case WSC_HEADER:
|
||||
handleHeader(&_client);
|
||||
break;
|
||||
case WSC_CONNECTED:
|
||||
WebSockets::handleWebsocket(&_client);
|
||||
break;
|
||||
default:
|
||||
WebSockets::clientDisconnect(&_client, 1002);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#ifdef ESP8266
|
||||
delay(0);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* send the WebSocket header to Server
|
||||
* @param client WSclient_t * ptr to the client struct
|
||||
*/
|
||||
void WebSocketsClient::sendHeader(WSclient_t * client) {
|
||||
|
||||
DEBUG_WEBSOCKETS("[WS-Client][sendHeader] sending header...\n");
|
||||
|
||||
uint8_t randomKey[16] = { 0 };
|
||||
|
||||
for(uint8_t i = 0; i < sizeof(randomKey); i++) {
|
||||
randomKey[i] = random(0xFF);
|
||||
}
|
||||
|
||||
client->cKey = base64_encode(&randomKey[0], 16);
|
||||
|
||||
#ifndef NODEBUG_WEBSOCKETS
|
||||
unsigned long start = micros();
|
||||
#endif
|
||||
|
||||
String handshake = "GET " + client->cUrl + " HTTP/1.1\r\n"
|
||||
"Host: " + _host + "\r\n"
|
||||
"Upgrade: websocket\r\n"
|
||||
"Connection: Upgrade\r\n"
|
||||
"User-Agent: arduino-WebSocket-Client\r\n"
|
||||
"Sec-WebSocket-Version: 13\r\n"
|
||||
"Sec-WebSocket-Protocol: arduino\r\n"
|
||||
"Sec-WebSocket-Key: " + client->cKey + "\r\n";
|
||||
|
||||
if(client->cExtensions.length() > 0) {
|
||||
handshake += "Sec-WebSocket-Extensions: " + client->cExtensions + "\r\n";
|
||||
}
|
||||
|
||||
handshake += "\r\n";
|
||||
|
||||
client->tcp->write(handshake.c_str(), handshake.length());
|
||||
|
||||
DEBUG_WEBSOCKETS("[WS-Client][sendHeader] sending header... Done (%uus).\n");
|
||||
//DEBUG_WEBSOCKETS((micros() - start));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* handle the WebSocket header reading
|
||||
* @param client WSclient_t * ptr to the client struct
|
||||
*/
|
||||
void WebSocketsClient::handleHeader(WSclient_t * client) {
|
||||
|
||||
String headerLine = client->tcp->readStringUntil('\n');
|
||||
headerLine.trim(); // remove \r
|
||||
|
||||
if(headerLine.length() > 0) {
|
||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] RX: %s\n");
|
||||
DEBUG_WEBSOCKETS(headerLine.c_str());
|
||||
|
||||
if(headerLine.startsWith("HTTP/1.")) {
|
||||
// "HTTP/1.1 101 Switching Protocols"
|
||||
client->cCode = headerLine.substring(9, headerLine.indexOf(' ', 9)).toInt();
|
||||
} else if(headerLine.indexOf(':')) {
|
||||
String headerName = headerLine.substring(0, headerLine.indexOf(':'));
|
||||
String headerValue = headerLine.substring(headerLine.indexOf(':') + 2);
|
||||
|
||||
if(headerName.equalsIgnoreCase("Connection")) {
|
||||
if(headerValue.indexOf("Upgrade") >= 0) {
|
||||
client->cIsUpgrade = true;
|
||||
}
|
||||
} else if(headerName.equalsIgnoreCase("Upgrade")) {
|
||||
if(headerValue.equalsIgnoreCase("websocket")) {
|
||||
client->cIsWebsocket = true;
|
||||
}
|
||||
} else if(headerName.equalsIgnoreCase("Sec-WebSocket-Accept")) {
|
||||
client->cAccept = headerValue;
|
||||
client->cAccept.trim(); // see rfc6455
|
||||
} else if(headerName.equalsIgnoreCase("Sec-WebSocket-Protocol")) {
|
||||
client->cProtocol = headerValue;
|
||||
} else if(headerName.equalsIgnoreCase("Sec-WebSocket-Extensions")) {
|
||||
client->cExtensions = headerValue;
|
||||
} else if(headerName.equalsIgnoreCase("Sec-WebSocket-Version")) {
|
||||
client->cVersion = headerValue.toInt();
|
||||
}
|
||||
} else {
|
||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Header error (%s)\n");
|
||||
DEBUG_WEBSOCKETS(headerLine.c_str());
|
||||
}
|
||||
|
||||
} else {
|
||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Header read fin.\n");
|
||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Client settings:\n");
|
||||
|
||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cURL: %s\n");
|
||||
//DEBUG_WEBSOCKETS(client->cUrl.c_str());
|
||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cKey: %s\n");
|
||||
//DEBUG_WEBSOCKETS(client->cKey.c_str());
|
||||
|
||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Server header:\n");
|
||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cCode: %d\n");
|
||||
//DEBUG_WEBSOCKETS(client->cCode);
|
||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cIsUpgrade: %d\n");
|
||||
//DEBUG_WEBSOCKETS(client->cIsUpgrade);
|
||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cIsWebsocket: %d\n");
|
||||
//DEBUG_WEBSOCKETS(client->cIsWebsocket);
|
||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cAccept: %s\n");
|
||||
//DEBUG_WEBSOCKETS(client->cAccept.c_str());
|
||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cProtocol: %s\n");
|
||||
//DEBUG_WEBSOCKETS(client->cProtocol.c_str());
|
||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cExtensions: %s\n");
|
||||
//DEBUG_WEBSOCKETS(client->cExtensions.c_str());
|
||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cVersion: %d\n");
|
||||
//DEBUG_WEBSOCKETS(client->cVersion);
|
||||
|
||||
bool ok = (client->cIsUpgrade && client->cIsWebsocket);
|
||||
|
||||
if(ok) {
|
||||
switch(client->cCode) {
|
||||
case 101: ///< Switching Protocols
|
||||
|
||||
break;
|
||||
case 403: ///< Forbidden
|
||||
// todo handle login
|
||||
default: ///< Server dont unterstand requrst
|
||||
ok = false;
|
||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] serverCode is not 101 (%d)\n");
|
||||
//DEBUG_WEBSOCKETS(client->cCode);
|
||||
clientDisconnect(client);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(ok) {
|
||||
if(client->cAccept.length() == 0) {
|
||||
ok = false;
|
||||
} else {
|
||||
// generate Sec-WebSocket-Accept key for check
|
||||
String sKey = acceptKey(client->cKey);
|
||||
if(sKey != client->cAccept) {
|
||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Sec-WebSocket-Accept is wrong\n");
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(ok) {
|
||||
|
||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Websocket connection init done.\n");
|
||||
|
||||
client->status = WSC_CONNECTED;
|
||||
|
||||
runCbEvent(WStype_CONNECTED, (uint8_t *) client->cUrl.c_str(), client->cUrl.length());
|
||||
|
||||
} else {
|
||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] no Websocket connection close.\n");
|
||||
client->tcp->write("This is a webSocket client!");
|
||||
clientDisconnect(client);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
98
src/WebSocketsClient.h
Normal file
98
src/WebSocketsClient.h
Normal file
@ -0,0 +1,98 @@
|
||||
/**
|
||||
* @file WebSocketsClient.h
|
||||
* @date 20.05.2015
|
||||
* @author Markus Sattler
|
||||
*
|
||||
* Copyright (c) 2015 Markus Sattler. All rights reserved.
|
||||
* This file is part of the WebSockets for Arduino.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef WEBSOCKETSCLIENT_H_
|
||||
#define WEBSOCKETSCLIENT_H_
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "WebSockets.h"
|
||||
|
||||
class WebSocketsClient: private WebSockets {
|
||||
public:
|
||||
|
||||
typedef void (*WebSocketClientEvent)(WStype_t type, uint8_t * payload, size_t length);
|
||||
|
||||
WebSocketsClient(void);
|
||||
~WebSocketsClient(void);
|
||||
|
||||
void begin(const char *host, uint16_t port, const char * url = "/");
|
||||
void begin(String host, uint16_t port, String url = "/");
|
||||
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
||||
void beginSSL(const char *host, uint16_t port, const char * url = "/", const char * = "");
|
||||
void beginSSL(String host, uint16_t port, String url = "/", String fingerprint = "");
|
||||
#endif
|
||||
|
||||
void loop(void);
|
||||
|
||||
void onEvent(WebSocketClientEvent cbEvent);
|
||||
|
||||
void sendTXT(uint8_t * payload, size_t length = 0, bool headerToPayload = false);
|
||||
void sendTXT(const uint8_t * payload, size_t length = 0);
|
||||
void sendTXT(char * payload, size_t length = 0, bool headerToPayload = false);
|
||||
void sendTXT(const char * payload, size_t length = 0);
|
||||
void sendTXT(String payload);
|
||||
|
||||
void sendBIN(uint8_t * payload, size_t length, bool headerToPayload = false);
|
||||
void sendBIN(const uint8_t * payload, size_t length);
|
||||
|
||||
void disconnect(void);
|
||||
|
||||
protected:
|
||||
String _host;
|
||||
uint16_t _port;
|
||||
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
||||
String _fingerprint;
|
||||
#endif
|
||||
WSclient_t _client;
|
||||
|
||||
WebSocketClientEvent _cbEvent;
|
||||
|
||||
void messageRecived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length);
|
||||
|
||||
void clientDisconnect(WSclient_t * client);
|
||||
bool clientIsConnected(WSclient_t * client);
|
||||
|
||||
void handleNewClients(void);
|
||||
void handleClientData(void);
|
||||
|
||||
void sendHeader(WSclient_t * client);
|
||||
void handleHeader(WSclient_t * client);
|
||||
|
||||
/**
|
||||
* called for sending a Event to the app
|
||||
* @param type WStype_t
|
||||
* @param payload uint8_t *
|
||||
* @param length size_t
|
||||
*/
|
||||
virtual void runCbEvent(WStype_t type, uint8_t * payload, size_t length) {
|
||||
if(_cbEvent) {
|
||||
_cbEvent(type, payload, length);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif /* WEBSOCKETSCLIENT_H_ */
|
714
src/WebSocketsServer.cpp
Normal file
714
src/WebSocketsServer.cpp
Normal file
@ -0,0 +1,714 @@
|
||||
/**
|
||||
* @file WebSocketsServer.cpp
|
||||
* @date 20.05.2015
|
||||
* @author Markus Sattler
|
||||
*
|
||||
* Copyright (c) 2015 Markus Sattler. All rights reserved.
|
||||
* This file is part of the WebSockets for Arduino.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include "WebSockets.h"
|
||||
#include "WebSocketsServer.h"
|
||||
|
||||
WebSocketsServer::WebSocketsServer(uint16_t port) {
|
||||
_port = port;
|
||||
_server = new WEBSOCKETS_NETWORK_SERVER_CLASS(port);
|
||||
|
||||
_cbEvent = NULL;
|
||||
|
||||
}
|
||||
|
||||
WebSocketsServer::~WebSocketsServer() {
|
||||
// disconnect all clients
|
||||
disconnect();
|
||||
|
||||
// TODO how to close server?
|
||||
}
|
||||
|
||||
/**
|
||||
* calles to init the Websockets server
|
||||
*/
|
||||
void WebSocketsServer::begin(void) {
|
||||
WSclient_t * client;
|
||||
|
||||
// init client storage
|
||||
for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
|
||||
client = &_clients[i];
|
||||
|
||||
client->num = i;
|
||||
client->status = WSC_NOT_CONNECTED;
|
||||
client->tcp = NULL;
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
||||
client->isSSL = false;
|
||||
client->ssl = NULL;
|
||||
#endif
|
||||
client->cUrl = "";
|
||||
client->cCode = 0;
|
||||
client->cKey = "";
|
||||
client->cProtocol = "";
|
||||
client->cVersion = 0;
|
||||
client->cIsUpgrade = false;
|
||||
client->cIsWebsocket = false;
|
||||
}
|
||||
|
||||
#ifdef ESP8266
|
||||
randomSeed(RANDOM_REG32);
|
||||
#else
|
||||
// TODO find better seed
|
||||
randomSeed(millis());
|
||||
#endif
|
||||
|
||||
_server->begin();
|
||||
|
||||
DEBUG_WEBSOCKETS("[WS-Server] Server Started.\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* called in arduino loop
|
||||
*/
|
||||
void WebSocketsServer::loop(void) {
|
||||
handleNewClients();
|
||||
handleClientData();
|
||||
}
|
||||
|
||||
/**
|
||||
* set callback function
|
||||
* @param cbEvent WebSocketServerEvent
|
||||
*/
|
||||
void WebSocketsServer::onEvent(WebSocketServerEvent cbEvent) {
|
||||
_cbEvent = cbEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* send text data to client
|
||||
* @param num uint8_t client id
|
||||
* @param payload uint8_t *
|
||||
* @param length size_t
|
||||
* @param headerToPayload bool (see sendFrame for more details)
|
||||
*/
|
||||
void WebSocketsServer::sendTXT(uint8_t num, uint8_t * payload, size_t length, bool headerToPayload) {
|
||||
if(num >= WEBSOCKETS_SERVER_CLIENT_MAX) {
|
||||
return;
|
||||
}
|
||||
if(length == 0) {
|
||||
length = strlen((const char *) payload);
|
||||
}
|
||||
WSclient_t * client = &_clients[num];
|
||||
if(clientIsConnected(client)) {
|
||||
sendFrame(client, WSop_text, payload, length, false, true, headerToPayload);
|
||||
}
|
||||
}
|
||||
|
||||
void WebSocketsServer::sendTXT(uint8_t num, const uint8_t * payload, size_t length) {
|
||||
sendTXT(num, (uint8_t *) payload, length);
|
||||
}
|
||||
|
||||
void WebSocketsServer::sendTXT(uint8_t num, char * payload, size_t length, bool headerToPayload) {
|
||||
sendTXT(num, (uint8_t *) payload, length, headerToPayload);
|
||||
}
|
||||
|
||||
void WebSocketsServer::sendTXT(uint8_t num, const char * payload, size_t length) {
|
||||
sendTXT(num, (uint8_t *) payload, length);
|
||||
}
|
||||
|
||||
void WebSocketsServer::sendTXT(uint8_t num, String payload) {
|
||||
sendTXT(num, (uint8_t *) payload.c_str(), payload.length());
|
||||
}
|
||||
|
||||
/**
|
||||
* send text data to client all
|
||||
* @param payload uint8_t *
|
||||
* @param length size_t
|
||||
* @param headerToPayload bool (see sendFrame for more details)
|
||||
*/
|
||||
void WebSocketsServer::broadcastTXT(uint8_t * payload, size_t length, bool headerToPayload) {
|
||||
WSclient_t * client;
|
||||
if(length == 0) {
|
||||
length = strlen((const char *) payload);
|
||||
}
|
||||
|
||||
for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
|
||||
client = &_clients[i];
|
||||
if(clientIsConnected(client)) {
|
||||
sendFrame(client, WSop_text, payload, length, false, true, headerToPayload);
|
||||
}
|
||||
#ifdef ESP8266
|
||||
delay(0);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void WebSocketsServer::broadcastTXT(const uint8_t * payload, size_t length) {
|
||||
broadcastTXT((uint8_t *) payload, length);
|
||||
}
|
||||
|
||||
void WebSocketsServer::broadcastTXT(char * payload, size_t length, bool headerToPayload) {
|
||||
broadcastTXT((uint8_t *) payload, length, headerToPayload);
|
||||
}
|
||||
|
||||
void WebSocketsServer::broadcastTXT(const char * payload, size_t length) {
|
||||
broadcastTXT((uint8_t *) payload, length);
|
||||
}
|
||||
|
||||
void WebSocketsServer::broadcastTXT(String payload) {
|
||||
broadcastTXT((uint8_t *) payload.c_str(), payload.length());
|
||||
}
|
||||
|
||||
/**
|
||||
* send binary data to client
|
||||
* @param num uint8_t client id
|
||||
* @param payload uint8_t *
|
||||
* @param length size_t
|
||||
* @param headerToPayload bool (see sendFrame for more details)
|
||||
*/
|
||||
void WebSocketsServer::sendBIN(uint8_t num, uint8_t * payload, size_t length, bool headerToPayload) {
|
||||
if(num >= WEBSOCKETS_SERVER_CLIENT_MAX) {
|
||||
return;
|
||||
}
|
||||
WSclient_t * client = &_clients[num];
|
||||
if(clientIsConnected(client)) {
|
||||
sendFrame(client, WSop_binary, payload, length, false, true, headerToPayload);
|
||||
}
|
||||
}
|
||||
|
||||
void WebSocketsServer::sendBIN(uint8_t num, const uint8_t * payload, size_t length) {
|
||||
sendBIN(num, (uint8_t *) payload, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* send binary data to client all
|
||||
* @param payload uint8_t *
|
||||
* @param length size_t
|
||||
* @param headerToPayload bool (see sendFrame for more details)
|
||||
*/
|
||||
void WebSocketsServer::broadcastBIN(uint8_t * payload, size_t length, bool headerToPayload) {
|
||||
WSclient_t * client;
|
||||
for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
|
||||
client = &_clients[i];
|
||||
if(clientIsConnected(client)) {
|
||||
sendFrame(client, WSop_binary, payload, length, false, true, headerToPayload);
|
||||
}
|
||||
#ifdef ESP8266
|
||||
delay(0);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void WebSocketsServer::broadcastBIN(const uint8_t * payload, size_t length) {
|
||||
broadcastBIN((uint8_t *) payload, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* sends a WS ping to Client
|
||||
* @param num uint8_t client id
|
||||
* @param payload uint8_t *
|
||||
* @param length size_t
|
||||
* @return true if ping is send out
|
||||
*/
|
||||
bool WebSocketsServer::sendPing(uint8_t num, uint8_t * payload, size_t length) {
|
||||
if(num >= WEBSOCKETS_SERVER_CLIENT_MAX) {
|
||||
return false;
|
||||
}
|
||||
WSclient_t * client;
|
||||
client = &_clients[num];
|
||||
if(clientIsConnected(client)) {
|
||||
return sendFrame(client, WSop_ping, payload, length);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WebSocketsServer::sendPing(uint8_t num, String & payload) {
|
||||
return sendPing(num, (uint8_t *) payload.c_str(), payload.length());
|
||||
}
|
||||
|
||||
/**
|
||||
* sends a WS ping to all Client
|
||||
* @param payload uint8_t *
|
||||
* @param length size_t
|
||||
* @return true if ping is send out
|
||||
*/
|
||||
bool WebSocketsServer::broadcastPing(uint8_t * payload, size_t length) {
|
||||
WSclient_t * client;
|
||||
bool ret = true;
|
||||
for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
|
||||
client = &_clients[i];
|
||||
if(clientIsConnected(client)) {
|
||||
if(!sendFrame(client, WSop_ping, payload, length)) {
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
||||
delay(0);
|
||||
#endif
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool WebSocketsServer::broadcastPing(String & payload) {
|
||||
return broadcastPing((uint8_t *) payload.c_str(), payload.length());
|
||||
}
|
||||
|
||||
bool WebSocketsServer::clientConnected(uint8_t num) {
|
||||
WSclient_t * client;
|
||||
client = &_clients[num];
|
||||
return clientIsConnected(client);
|
||||
}
|
||||
|
||||
/**
|
||||
* disconnect all clients
|
||||
*/
|
||||
void WebSocketsServer::disconnect(void) {
|
||||
WSclient_t * client;
|
||||
for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
|
||||
client = &_clients[i];
|
||||
if(clientIsConnected(client)) {
|
||||
WebSockets::clientDisconnect(client, 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* disconnect one client
|
||||
* @param num uint8_t client id
|
||||
*/
|
||||
void WebSocketsServer::disconnect(uint8_t num) {
|
||||
if(num >= WEBSOCKETS_SERVER_CLIENT_MAX) {
|
||||
return;
|
||||
}
|
||||
WSclient_t * client = &_clients[num];
|
||||
if(clientIsConnected(client)) {
|
||||
WebSockets::clientDisconnect(client, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
||||
/**
|
||||
* get an IP for a client
|
||||
* @param num uint8_t client id
|
||||
* @return IPAddress
|
||||
*/
|
||||
IPAddress WebSocketsServer::remoteIP(uint8_t num) {
|
||||
if(num < WEBSOCKETS_SERVER_CLIENT_MAX) {
|
||||
WSclient_t * client = &_clients[num];
|
||||
if(clientIsConnected(client)) {
|
||||
return client->tcp->remoteIP();
|
||||
}
|
||||
}
|
||||
|
||||
return IPAddress();
|
||||
}
|
||||
#endif
|
||||
|
||||
//#################################################################################
|
||||
//#################################################################################
|
||||
//#################################################################################
|
||||
|
||||
/**
|
||||
*
|
||||
* @param client WSclient_t * ptr to the client struct
|
||||
* @param opcode WSopcode_t
|
||||
* @param payload uint8_t *
|
||||
* @param lenght size_t
|
||||
*/
|
||||
void WebSocketsServer::messageRecived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t lenght) {
|
||||
WStype_t type = WStype_ERROR;
|
||||
|
||||
switch(opcode) {
|
||||
case WSop_text:
|
||||
type = WStype_TEXT;
|
||||
break;
|
||||
case WSop_binary:
|
||||
type = WStype_BIN;
|
||||
break;
|
||||
}
|
||||
|
||||
runCbEvent(client->num, type, payload, lenght);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnect an client
|
||||
* @param client WSclient_t * ptr to the client struct
|
||||
*/
|
||||
void WebSocketsServer::clientDisconnect(WSclient_t * client) {
|
||||
|
||||
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
||||
if(client->isSSL && client->ssl) {
|
||||
if(client->ssl->connected()) {
|
||||
client->ssl->flush();
|
||||
client->ssl->stop();
|
||||
}
|
||||
delete client->ssl;
|
||||
client->ssl = NULL;
|
||||
client->tcp = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
if(client->tcp) {
|
||||
if(client->tcp->connected()) {
|
||||
client->tcp->flush();
|
||||
client->tcp->stop();
|
||||
}
|
||||
delete client->tcp;
|
||||
client->tcp = NULL;
|
||||
}
|
||||
|
||||
client->cUrl = "";
|
||||
client->cKey = "";
|
||||
client->cProtocol = "";
|
||||
client->cVersion = 0;
|
||||
client->cIsUpgrade = false;
|
||||
client->cIsWebsocket = false;
|
||||
|
||||
client->status = WSC_NOT_CONNECTED;
|
||||
|
||||
DEBUG_WEBSOCKETS("[WS-Server][");DEBUG_WEBSOCKETS(client->num);DEBUG_WEBSOCKETS("]");
|
||||
DEBUG_WEBSOCKETS(" client disconnected.\n");
|
||||
|
||||
|
||||
runCbEvent(client->num, WStype_DISCONNECTED, NULL, 0);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* get client state
|
||||
* @param client WSclient_t * ptr to the client struct
|
||||
* @return true = conneted
|
||||
*/
|
||||
bool WebSocketsServer::clientIsConnected(WSclient_t * client) {
|
||||
|
||||
if(!client->tcp) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(client->tcp->connected()) {
|
||||
if(client->status != WSC_NOT_CONNECTED) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
// client lost
|
||||
DEBUG_WEBSOCKETS(client->status);
|
||||
if(client->status != WSC_NOT_CONNECTED) {
|
||||
DEBUG_WEBSOCKETS("[WS-Server][");DEBUG_WEBSOCKETS(client->num);DEBUG_WEBSOCKETS("]");
|
||||
DEBUG_WEBSOCKETS(" client connection lost.\n");
|
||||
|
||||
// do cleanup
|
||||
clientDisconnect(client);
|
||||
}
|
||||
}
|
||||
|
||||
if(client->tcp) {
|
||||
// do cleanup
|
||||
clientDisconnect(client);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266)
|
||||
bool WebSocketsServer::clientExists(const WEBSOCKETS_NETWORK_CLASS &c)
|
||||
{
|
||||
WSclient_t * client;
|
||||
for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
|
||||
client = &_clients[i];
|
||||
if (clientIsConnected(client) && (*client->tcp == c))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Handle incomming Connection Request
|
||||
*/
|
||||
void WebSocketsServer::handleNewClients(void) {
|
||||
WSclient_t * client;
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
||||
while(_server->hasClient()) {
|
||||
#endif
|
||||
bool ok = false;
|
||||
// search free list entry for client
|
||||
for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
|
||||
client = &_clients[i];
|
||||
|
||||
// state is not connected or tcp connection is lost
|
||||
if(!clientIsConnected(client)) {
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
||||
// store new connection
|
||||
client->tcp = new WEBSOCKETS_NETWORK_CLASS(_server->available());
|
||||
#else
|
||||
WEBSOCKETS_NETWORK_CLASS newClient = _server->available();
|
||||
if (newClient && !clientExists(newClient)) {
|
||||
client->tcp = new WEBSOCKETS_NETWORK_CLASS(newClient);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
//if (!client->tcp->connected() || !client->tcp->available()) {
|
||||
// DEBUG_WEBSOCKETS("[WS-Client] Not Connected!\n");
|
||||
// return;
|
||||
//}
|
||||
#endif
|
||||
if(!client->tcp) {
|
||||
DEBUG_WEBSOCKETS("[WS-Server] creating Network class failed!");
|
||||
return;
|
||||
}
|
||||
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
||||
client->isSSL = false;
|
||||
client->tcp->setNoDelay(true);
|
||||
#endif
|
||||
// set Timeout for readBytesUntil and readStringUntil
|
||||
client->tcp->setTimeout(WEBSOCKETS_TCP_TIMEOUT);
|
||||
client->status = WSC_HEADER;
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
||||
IPAddress ip = client->tcp->remoteIP();
|
||||
DEBUG_WEBSOCKETS("[WS-Server][");DEBUG_WEBSOCKETS(client->num);DEBUG_WEBSOCKETS("]");
|
||||
DEBUG_WEBSOCKETS(" new client from %d.%d.%d.%d\n");
|
||||
|
||||
//DEBUG_WEBSOCKETS(ip[0]);
|
||||
//DEBUG_WEBSOCKETS(ip[1]);
|
||||
//DEBUG_WEBSOCKETS(ip[2]);
|
||||
//DEBUG_WEBSOCKETS(ip[3]);
|
||||
#else
|
||||
DEBUG_WEBSOCKETS("[WS-Server][");DEBUG_WEBSOCKETS(client->num);DEBUG_WEBSOCKETS("]");
|
||||
DEBUG_WEBSOCKETS(" new client\n");
|
||||
#endif
|
||||
ok = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!ok) {
|
||||
// no free space to handle client
|
||||
WEBSOCKETS_NETWORK_CLASS tcpClient = _server->available();
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
||||
IPAddress ip = client->tcp->remoteIP();
|
||||
DEBUG_WEBSOCKETS("[WS-Server] no free space new client from %d.%d.%d.%d\n");
|
||||
//DEBUG_WEBSOCKETS(ip[0]);
|
||||
//DEBUG_WEBSOCKETS(ip[1]);
|
||||
//DEBUG_WEBSOCKETS(ip[2]);
|
||||
//DEBUG_WEBSOCKETS(ip[3]);
|
||||
#else
|
||||
DEBUG_WEBSOCKETS("[WS-Server] no free space new client\n");
|
||||
#endif
|
||||
tcpClient.stop();
|
||||
}
|
||||
|
||||
#ifdef ESP8266
|
||||
delay(0);
|
||||
#endif
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Handel incomming data from Client
|
||||
*/
|
||||
void WebSocketsServer::handleClientData(void) {
|
||||
|
||||
WSclient_t * client;
|
||||
for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
|
||||
client = &_clients[i];
|
||||
if(clientIsConnected(client)) {
|
||||
int len = client->tcp->available();
|
||||
if(len > 0) {
|
||||
switch(client->status) {
|
||||
case WSC_HEADER:
|
||||
handleHeader(client);
|
||||
break;
|
||||
case WSC_CONNECTED:
|
||||
WebSockets::handleWebsocket(client);
|
||||
break;
|
||||
default:
|
||||
WebSockets::clientDisconnect(client, 1002);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifdef ESP8266
|
||||
delay(0);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* handle the WebSocket header reading
|
||||
* @param client WSclient_t * ptr to the client struct
|
||||
*/
|
||||
void WebSocketsServer::handleHeader(WSclient_t * client) {
|
||||
|
||||
String headerLine = client->tcp->readStringUntil('\n');
|
||||
headerLine.trim(); // remove \r
|
||||
|
||||
if(headerLine.length() > 0) {
|
||||
DEBUG_WEBSOCKETS("[WS-Server][");DEBUG_WEBSOCKETS(client->num);DEBUG_WEBSOCKETS("]");
|
||||
DEBUG_WEBSOCKETS("[handleHeader] RX: ");
|
||||
DEBUG_WEBSOCKETS(headerLine.c_str());
|
||||
DEBUG_WEBSOCKETS("\n");
|
||||
|
||||
// websocket request starts allways with GET see rfc6455
|
||||
if(headerLine.startsWith("GET ")) {
|
||||
// cut URL out
|
||||
client->cUrl = headerLine.substring(4, headerLine.indexOf(' ', 4));
|
||||
} else if(headerLine.indexOf(':')) {
|
||||
String headerName = headerLine.substring(0, headerLine.indexOf(':'));
|
||||
String headerValue = headerLine.substring(headerLine.indexOf(':') + 2);
|
||||
//Serial.print(headerName); Serial.print(":"); Serial.println(headerValue);
|
||||
|
||||
if(headerName.equalsIgnoreCase("Connection")) {
|
||||
if(headerValue.indexOf("Upgrade") >= 0) {
|
||||
client->cIsUpgrade = true;
|
||||
}
|
||||
} else if(headerName.equalsIgnoreCase("Upgrade")) {
|
||||
if(headerValue.equalsIgnoreCase("websocket")) {
|
||||
client->cIsWebsocket = true;
|
||||
}
|
||||
} else if(headerName.equalsIgnoreCase("Sec-WebSocket-Version")) {
|
||||
client->cVersion = headerValue.toInt();
|
||||
} else if(headerName.equalsIgnoreCase("Sec-WebSocket-Key")) {
|
||||
client->cKey = headerValue;
|
||||
client->cKey.trim(); // see rfc6455
|
||||
} else if(headerName.equalsIgnoreCase("Sec-WebSocket-Protocol")) {
|
||||
client->cProtocol = headerValue;
|
||||
} else if(headerName.equalsIgnoreCase("Sec-WebSocket-Extensions")) {
|
||||
client->cExtensions = headerValue;
|
||||
}
|
||||
} else {
|
||||
Serial.print("[WS-Client][handleHeader] Header error (%s)\n");
|
||||
Serial.print(headerLine.c_str());
|
||||
}
|
||||
|
||||
} else {
|
||||
DEBUG_WEBSOCKETS("[WS-Server][");DEBUG_WEBSOCKETS(client->num);DEBUG_WEBSOCKETS("]");
|
||||
DEBUG_WEBSOCKETS("[handleHeader] Header read fin.\n");
|
||||
|
||||
DEBUG_WEBSOCKETS("[WS-Server][");DEBUG_WEBSOCKETS(client->num);DEBUG_WEBSOCKETS("]");
|
||||
DEBUG_WEBSOCKETS("[handleHeader] - cURL: ");
|
||||
DEBUG_WEBSOCKETS(client->cUrl.c_str());
|
||||
DEBUG_WEBSOCKETS("\n");
|
||||
|
||||
DEBUG_WEBSOCKETS("[WS-Server][");DEBUG_WEBSOCKETS(client->num);DEBUG_WEBSOCKETS("]");
|
||||
DEBUG_WEBSOCKETS("[handleHeader] - cIsUpgrade: ");
|
||||
DEBUG_WEBSOCKETS(client->cIsUpgrade);
|
||||
DEBUG_WEBSOCKETS("\n");
|
||||
|
||||
DEBUG_WEBSOCKETS("[WS-Server][");DEBUG_WEBSOCKETS(client->num);DEBUG_WEBSOCKETS("]");
|
||||
DEBUG_WEBSOCKETS("[handleHeader] - cIsWebsocket: ");
|
||||
DEBUG_WEBSOCKETS(client->cIsWebsocket);
|
||||
DEBUG_WEBSOCKETS("\n");
|
||||
|
||||
DEBUG_WEBSOCKETS("[WS-Server][");DEBUG_WEBSOCKETS(client->num);DEBUG_WEBSOCKETS("]");
|
||||
DEBUG_WEBSOCKETS("[handleHeader] - cKey: ");
|
||||
DEBUG_WEBSOCKETS(client->cKey.c_str());
|
||||
DEBUG_WEBSOCKETS("\n");
|
||||
|
||||
DEBUG_WEBSOCKETS("[WS-Server][");DEBUG_WEBSOCKETS(client->num);DEBUG_WEBSOCKETS("]");
|
||||
DEBUG_WEBSOCKETS("[handleHeader] - cProtocol: ");
|
||||
DEBUG_WEBSOCKETS(client->cProtocol.c_str());
|
||||
DEBUG_WEBSOCKETS("\n");
|
||||
|
||||
DEBUG_WEBSOCKETS("[WS-Server][");DEBUG_WEBSOCKETS(client->num);DEBUG_WEBSOCKETS("]");
|
||||
DEBUG_WEBSOCKETS("[handleHeader] - cExtensions: ");
|
||||
DEBUG_WEBSOCKETS(client->cExtensions.c_str());
|
||||
DEBUG_WEBSOCKETS("\n");
|
||||
|
||||
DEBUG_WEBSOCKETS("[WS-Server][");DEBUG_WEBSOCKETS(client->num);DEBUG_WEBSOCKETS("]");
|
||||
DEBUG_WEBSOCKETS("[handleHeader] - cVersion: ");
|
||||
DEBUG_WEBSOCKETS(client->cVersion);
|
||||
DEBUG_WEBSOCKETS("\n\n");
|
||||
|
||||
bool ok = (client->cIsUpgrade && client->cIsWebsocket);
|
||||
Serial.println("here0");
|
||||
|
||||
if (!client->cIsUpgrade) {
|
||||
DEBUG_WEBSOCKETS("[WS-Server][");DEBUG_WEBSOCKETS(client->num);DEBUG_WEBSOCKETS("]");
|
||||
DEBUG_WEBSOCKETS("[handleHeader] Error! Not an Upgrade\n");
|
||||
}
|
||||
|
||||
if (!client->cIsWebsocket) {
|
||||
DEBUG_WEBSOCKETS("[WS-Server][");DEBUG_WEBSOCKETS(client->num);DEBUG_WEBSOCKETS("]");
|
||||
DEBUG_WEBSOCKETS("[handleHeader] Error! Not a Websocket\n");
|
||||
}
|
||||
|
||||
if(ok) {
|
||||
Serial.println("here1");
|
||||
DEBUG_WEBSOCKETS("[WS-Server][");DEBUG_WEBSOCKETS(client->num);DEBUG_WEBSOCKETS("]");
|
||||
DEBUG_WEBSOCKETS("[handleHeader] First ok test success.\n");
|
||||
|
||||
if(client->cUrl.length() == 0) {
|
||||
Serial.println("here2");
|
||||
ok = false;
|
||||
}
|
||||
if(client->cKey.length() == 0) {
|
||||
Serial.println("here3");
|
||||
ok = false;
|
||||
}
|
||||
if(client->cVersion != 13) {
|
||||
Serial.println("here4");
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
|
||||
if(ok) {
|
||||
Serial.println("here5");
|
||||
DEBUG_WEBSOCKETS("[WS-Server][");DEBUG_WEBSOCKETS(client->num);DEBUG_WEBSOCKETS("]");
|
||||
DEBUG_WEBSOCKETS("[handleHeader] Websocket connection incomming.\n");
|
||||
|
||||
|
||||
// generate Sec-WebSocket-Accept key
|
||||
String sKey = acceptKey(client->cKey);
|
||||
|
||||
DEBUG_WEBSOCKETS("[WS-Server][");DEBUG_WEBSOCKETS(client->num);DEBUG_WEBSOCKETS("]");
|
||||
DEBUG_WEBSOCKETS("[handleHeader] - sKey: ");
|
||||
DEBUG_WEBSOCKETS(sKey.c_str());
|
||||
DEBUG_WEBSOCKETS("\n");
|
||||
|
||||
client->status = WSC_CONNECTED;
|
||||
|
||||
client->tcp->write("HTTP/1.1 101 Switching Protocols\r\n"
|
||||
"Server: arduino-WebSocketsServer\r\n"
|
||||
"Upgrade: websocket\r\n"
|
||||
"Connection: Upgrade\r\n"
|
||||
"Sec-WebSocket-Version: 13\r\n"
|
||||
"Sec-WebSocket-Accept: ");
|
||||
client->tcp->write(sKey.c_str(), sKey.length());
|
||||
client->tcp->write("\r\n");
|
||||
|
||||
if(client->cProtocol.length() > 0) {
|
||||
// TODO add api to set Protocol of Server
|
||||
client->tcp->write("Sec-WebSocket-Protocol: arduino\r\n");
|
||||
}
|
||||
|
||||
// header end
|
||||
client->tcp->write("\r\n");
|
||||
|
||||
// send ping
|
||||
WebSockets::sendFrame(client, WSop_ping);
|
||||
|
||||
runCbEvent(client->num, WStype_CONNECTED, (uint8_t *) client->cUrl.c_str(), client->cUrl.length());
|
||||
|
||||
} else {
|
||||
handleNonWebsocketConnection(client);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
143
src/WebSocketsServer.h
Normal file
143
src/WebSocketsServer.h
Normal file
@ -0,0 +1,143 @@
|
||||
/**
|
||||
* @file WebSocketsServer.h
|
||||
* @date 20.05.2015
|
||||
* @author Markus Sattler
|
||||
*
|
||||
* Copyright (c) 2015 Markus Sattler. All rights reserved.
|
||||
* This file is part of the WebSockets for Arduino.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef WEBSOCKETSSERVER_H_
|
||||
#define WEBSOCKETSSERVER_H_
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "WebSockets.h"
|
||||
|
||||
#define WEBSOCKETS_SERVER_CLIENT_MAX (5)
|
||||
|
||||
|
||||
|
||||
|
||||
class WebSocketsServer: private WebSockets {
|
||||
public:
|
||||
|
||||
typedef void (*WebSocketServerEvent)(uint8_t num, WStype_t type, uint8_t * payload, size_t length);
|
||||
|
||||
WebSocketsServer(uint16_t port);
|
||||
~WebSocketsServer(void);
|
||||
|
||||
void begin(void);
|
||||
void loop(void);
|
||||
|
||||
void onEvent(WebSocketServerEvent cbEvent);
|
||||
|
||||
|
||||
void sendTXT(uint8_t num, uint8_t * payload, size_t length = 0, bool headerToPayload = false);
|
||||
void sendTXT(uint8_t num, const uint8_t * payload, size_t length = 0);
|
||||
void sendTXT(uint8_t num, char * payload, size_t length = 0, bool headerToPayload = false);
|
||||
void sendTXT(uint8_t num, const char * payload, size_t length = 0);
|
||||
void sendTXT(uint8_t num, String payload);
|
||||
|
||||
void broadcastTXT(uint8_t * payload, size_t length = 0, bool headerToPayload = false);
|
||||
void broadcastTXT(const uint8_t * payload, size_t length = 0);
|
||||
void broadcastTXT(char * payload, size_t length = 0, bool headerToPayload = false);
|
||||
void broadcastTXT(const char * payload, size_t length = 0);
|
||||
void broadcastTXT(String payload);
|
||||
|
||||
void sendBIN(uint8_t num, uint8_t * payload, size_t length, bool headerToPayload = false);
|
||||
void sendBIN(uint8_t num, const uint8_t * payload, size_t length);
|
||||
|
||||
bool sendPing(uint8_t num, uint8_t * payload = NULL, size_t length = 0);
|
||||
bool sendPing(uint8_t num, String & payload);
|
||||
|
||||
bool broadcastPing(uint8_t * payload = NULL, size_t length = 0);
|
||||
bool broadcastPing(String & payload);
|
||||
|
||||
void broadcastBIN(uint8_t * payload, size_t length, bool headerToPayload = false);
|
||||
void broadcastBIN(const uint8_t * payload, size_t length);
|
||||
|
||||
void disconnect(void);
|
||||
void disconnect(uint8_t num);
|
||||
|
||||
bool clientConnected(uint8_t num);
|
||||
|
||||
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
||||
IPAddress remoteIP(uint8_t num);
|
||||
#else
|
||||
bool clientExists(const WEBSOCKETS_NETWORK_CLASS &c);
|
||||
|
||||
#endif
|
||||
|
||||
protected:
|
||||
uint16_t _port;
|
||||
|
||||
WEBSOCKETS_NETWORK_SERVER_CLASS * _server;
|
||||
|
||||
WSclient_t _clients[WEBSOCKETS_SERVER_CLIENT_MAX];
|
||||
|
||||
WebSocketServerEvent _cbEvent;
|
||||
|
||||
void messageRecived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length);
|
||||
|
||||
void clientDisconnect(WSclient_t * client);
|
||||
bool clientIsConnected(WSclient_t * client);
|
||||
|
||||
void handleNewClients(void);
|
||||
void handleClientData(void);
|
||||
|
||||
void handleHeader(WSclient_t * client);
|
||||
|
||||
/**
|
||||
* called if a non Websocket connection is comming in.
|
||||
* Note: can be overrided
|
||||
* @param client WSclient_t * ptr to the client struct
|
||||
*/
|
||||
virtual void handleNonWebsocketConnection(WSclient_t * client) {
|
||||
DEBUG_WEBSOCKETS("[WS-Server][");
|
||||
DEBUG_WEBSOCKETS(client->num);
|
||||
DEBUG_WEBSOCKETS("][handleHeader] no Websocket connection close.\n");
|
||||
|
||||
client->tcp->write("HTTP/1.1 400 Bad Request\r\n"
|
||||
"Server: arduino-WebSocket-Server\r\n"
|
||||
"Content-Type: text/plain\r\n"
|
||||
"Content-Length: 32\r\n"
|
||||
"Connection: close\r\n"
|
||||
"Sec-WebSocket-Version: 13\r\n"
|
||||
"\r\n"
|
||||
"This is a Websocket server only!");
|
||||
clientDisconnect(client);
|
||||
}
|
||||
|
||||
/**
|
||||
* called for sending a Event to the app
|
||||
* @param num uint8_t
|
||||
* @param type WStype_t
|
||||
* @param payload uint8_t *
|
||||
* @param length size_t
|
||||
*/
|
||||
virtual void runCbEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
|
||||
if(_cbEvent) {
|
||||
_cbEvent(num, type, payload, length);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif /* WEBSOCKETSSERVER_H_ */
|
47
src/image.cpp
Normal file
47
src/image.cpp
Normal file
@ -0,0 +1,47 @@
|
||||
#include "image.hpp"
|
||||
|
||||
|
||||
bool Image::check_bounds(int x, int y) {
|
||||
if ((x < 0) || (y < 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((x >= width) || (y >= height)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
byte Image::get_pixel(int x, int y) {
|
||||
if (check_bounds(x, y) == false) {
|
||||
return 0;
|
||||
}
|
||||
return data[y * width + x];
|
||||
}
|
||||
|
||||
void Image::set_pixel(int x, int y, byte value) {
|
||||
if (check_bounds(x, y) == false) {
|
||||
return;
|
||||
}
|
||||
data[y * width + x] = value;
|
||||
}
|
||||
|
||||
void Image::clear_pixels() {
|
||||
memset(data, 0, sizeof(data));
|
||||
}
|
||||
|
||||
void Image::set_size(int w, int h) {
|
||||
width = min(w, MAX_WIDTH);
|
||||
height = min(h, MAX_HEIGHT);
|
||||
}
|
||||
|
||||
uint16_t Image::getWidth()
|
||||
{
|
||||
return width;
|
||||
}
|
||||
|
||||
uint16_t Image::getHeight()
|
||||
{
|
||||
return height;
|
||||
}
|
28
src/image.hpp
Normal file
28
src/image.hpp
Normal file
@ -0,0 +1,28 @@
|
||||
#ifndef IMAGE_HPP
|
||||
#define IMAGE_HPP
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#define MAX_WIDTH 32
|
||||
#define MAX_HEIGHT 40
|
||||
|
||||
class Image
|
||||
{
|
||||
public:
|
||||
byte get_pixel(int x, int y);
|
||||
void set_pixel(int x, int y, byte value);
|
||||
void clear_pixels();
|
||||
void set_size(int w, int h);
|
||||
uint16_t getWidth();
|
||||
uint16_t getHeight();
|
||||
|
||||
private:
|
||||
bool check_bounds(int x, int y);
|
||||
|
||||
int width;
|
||||
int height;
|
||||
byte data[MAX_WIDTH * MAX_HEIGHT];
|
||||
|
||||
};
|
||||
|
||||
#endif
|
7
src/libb64/AUTHORS
Normal file
7
src/libb64/AUTHORS
Normal file
@ -0,0 +1,7 @@
|
||||
libb64: Base64 Encoding/Decoding Routines
|
||||
======================================
|
||||
|
||||
Authors:
|
||||
-------
|
||||
|
||||
Chris Venter chris.venter@gmail.com http://rocketpod.blogspot.com
|
29
src/libb64/LICENSE
Normal file
29
src/libb64/LICENSE
Normal file
@ -0,0 +1,29 @@
|
||||
Copyright-Only Dedication (based on United States law)
|
||||
or Public Domain Certification
|
||||
|
||||
The person or persons who have associated work with this document (the
|
||||
"Dedicator" or "Certifier") hereby either (a) certifies that, to the best of
|
||||
his knowledge, the work of authorship identified is in the public domain of the
|
||||
country from which the work is published, or (b) hereby dedicates whatever
|
||||
copyright the dedicators holds in the work of authorship identified below (the
|
||||
"Work") to the public domain. A certifier, moreover, dedicates any copyright
|
||||
interest he may have in the associated work, and for these purposes, is
|
||||
described as a "dedicator" below.
|
||||
|
||||
A certifier has taken reasonable steps to verify the copyright status of this
|
||||
work. Certifier recognizes that his good faith efforts may not shield him from
|
||||
liability if in fact the work certified is not in the public domain.
|
||||
|
||||
Dedicator makes this dedication for the benefit of the public at large and to
|
||||
the detriment of the Dedicator's heirs and successors. Dedicator intends this
|
||||
dedication to be an overt act of relinquishment in perpetuity of all present
|
||||
and future rights under copyright law, whether vested or contingent, in the
|
||||
Work. Dedicator understands that such relinquishment of all rights includes
|
||||
the relinquishment of all rights to enforce (by lawsuit or otherwise) those
|
||||
copyrights in the Work.
|
||||
|
||||
Dedicator recognizes that, once placed in the public domain, the Work may be
|
||||
freely reproduced, distributed, transmitted, used, modified, built upon, or
|
||||
otherwise exploited by anyone for any purpose, commercial or non-commercial,
|
||||
and in any way, including by methods that have not yet been invented or
|
||||
conceived.
|
94
src/libb64/cdecode.c
Normal file
94
src/libb64/cdecode.c
Normal file
@ -0,0 +1,94 @@
|
||||
/*
|
||||
cdecoder.c - c source to a base64 decoding algorithm implementation
|
||||
|
||||
This is part of the libb64 project, and has been placed in the public domain.
|
||||
For details, see http://sourceforge.net/projects/libb64
|
||||
*/
|
||||
|
||||
#ifdef ESP8266
|
||||
#include <core_esp8266_features.h>
|
||||
#endif
|
||||
|
||||
#ifndef CORE_HAS_LIBB64
|
||||
#include "cdecode_inc.h"
|
||||
|
||||
int base64_decode_value(char value_in)
|
||||
{
|
||||
static const char decoding[] = {62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-2,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51};
|
||||
static const char decoding_size = sizeof(decoding);
|
||||
value_in -= 43;
|
||||
if (value_in < 0 || value_in > decoding_size) return -1;
|
||||
return decoding[(int)value_in];
|
||||
}
|
||||
|
||||
void base64_init_decodestate(base64_decodestate* state_in)
|
||||
{
|
||||
state_in->step = step_a;
|
||||
state_in->plainchar = 0;
|
||||
}
|
||||
|
||||
int base64_decode_block(const char* code_in, const int length_in, char* plaintext_out, base64_decodestate* state_in)
|
||||
{
|
||||
const char* codechar = code_in;
|
||||
char* plainchar = plaintext_out;
|
||||
char fragment;
|
||||
|
||||
*plainchar = state_in->plainchar;
|
||||
|
||||
switch (state_in->step)
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
case step_a:
|
||||
do {
|
||||
if (codechar == code_in+length_in)
|
||||
{
|
||||
state_in->step = step_a;
|
||||
state_in->plainchar = *plainchar;
|
||||
return plainchar - plaintext_out;
|
||||
}
|
||||
fragment = (char)base64_decode_value(*codechar++);
|
||||
} while (fragment < 0);
|
||||
*plainchar = (fragment & 0x03f) << 2;
|
||||
case step_b:
|
||||
do {
|
||||
if (codechar == code_in+length_in)
|
||||
{
|
||||
state_in->step = step_b;
|
||||
state_in->plainchar = *plainchar;
|
||||
return plainchar - plaintext_out;
|
||||
}
|
||||
fragment = (char)base64_decode_value(*codechar++);
|
||||
} while (fragment < 0);
|
||||
*plainchar++ |= (fragment & 0x030) >> 4;
|
||||
*plainchar = (fragment & 0x00f) << 4;
|
||||
case step_c:
|
||||
do {
|
||||
if (codechar == code_in+length_in)
|
||||
{
|
||||
state_in->step = step_c;
|
||||
state_in->plainchar = *plainchar;
|
||||
return plainchar - plaintext_out;
|
||||
}
|
||||
fragment = (char)base64_decode_value(*codechar++);
|
||||
} while (fragment < 0);
|
||||
*plainchar++ |= (fragment & 0x03c) >> 2;
|
||||
*plainchar = (fragment & 0x003) << 6;
|
||||
case step_d:
|
||||
do {
|
||||
if (codechar == code_in+length_in)
|
||||
{
|
||||
state_in->step = step_d;
|
||||
state_in->plainchar = *plainchar;
|
||||
return plainchar - plaintext_out;
|
||||
}
|
||||
fragment = (char)base64_decode_value(*codechar++);
|
||||
} while (fragment < 0);
|
||||
*plainchar++ |= (fragment & 0x03f);
|
||||
}
|
||||
}
|
||||
/* control should not reach here */
|
||||
return plainchar - plaintext_out;
|
||||
}
|
||||
|
||||
#endif
|
28
src/libb64/cdecode_inc.h
Normal file
28
src/libb64/cdecode_inc.h
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
cdecode.h - c header for a base64 decoding algorithm
|
||||
|
||||
This is part of the libb64 project, and has been placed in the public domain.
|
||||
For details, see http://sourceforge.net/projects/libb64
|
||||
*/
|
||||
|
||||
#ifndef BASE64_CDECODE_H
|
||||
#define BASE64_CDECODE_H
|
||||
|
||||
typedef enum
|
||||
{
|
||||
step_a, step_b, step_c, step_d
|
||||
} base64_decodestep;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
base64_decodestep step;
|
||||
char plainchar;
|
||||
} base64_decodestate;
|
||||
|
||||
void base64_init_decodestate(base64_decodestate* state_in);
|
||||
|
||||
int base64_decode_value(char value_in);
|
||||
|
||||
int base64_decode_block(const char* code_in, const int length_in, char* plaintext_out, base64_decodestate* state_in);
|
||||
|
||||
#endif /* BASE64_CDECODE_H */
|
115
src/libb64/cencode.c
Normal file
115
src/libb64/cencode.c
Normal file
@ -0,0 +1,115 @@
|
||||
/*
|
||||
cencoder.c - c source to a base64 encoding algorithm implementation
|
||||
|
||||
This is part of the libb64 project, and has been placed in the public domain.
|
||||
For details, see http://sourceforge.net/projects/libb64
|
||||
*/
|
||||
|
||||
#ifdef ESP8266
|
||||
#include <core_esp8266_features.h>
|
||||
#endif
|
||||
|
||||
#ifndef CORE_HAS_LIBB64
|
||||
#include "cencode_inc.h"
|
||||
|
||||
const int CHARS_PER_LINE = 72;
|
||||
|
||||
void base64_init_encodestate(base64_encodestate* state_in)
|
||||
{
|
||||
state_in->step = step_A;
|
||||
state_in->result = 0;
|
||||
state_in->stepcount = 0;
|
||||
}
|
||||
|
||||
char base64_encode_value(char value_in)
|
||||
{
|
||||
static const char* encoding = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
if (value_in > 63) return '=';
|
||||
return encoding[(int)value_in];
|
||||
}
|
||||
|
||||
int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, base64_encodestate* state_in)
|
||||
{
|
||||
const char* plainchar = plaintext_in;
|
||||
const char* const plaintextend = plaintext_in + length_in;
|
||||
char* codechar = code_out;
|
||||
char result;
|
||||
char fragment;
|
||||
|
||||
result = state_in->result;
|
||||
|
||||
switch (state_in->step)
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
case step_A:
|
||||
if (plainchar == plaintextend)
|
||||
{
|
||||
state_in->result = result;
|
||||
state_in->step = step_A;
|
||||
return codechar - code_out;
|
||||
}
|
||||
fragment = *plainchar++;
|
||||
result = (fragment & 0x0fc) >> 2;
|
||||
*codechar++ = base64_encode_value(result);
|
||||
result = (fragment & 0x003) << 4;
|
||||
case step_B:
|
||||
if (plainchar == plaintextend)
|
||||
{
|
||||
state_in->result = result;
|
||||
state_in->step = step_B;
|
||||
return codechar - code_out;
|
||||
}
|
||||
fragment = *plainchar++;
|
||||
result |= (fragment & 0x0f0) >> 4;
|
||||
*codechar++ = base64_encode_value(result);
|
||||
result = (fragment & 0x00f) << 2;
|
||||
case step_C:
|
||||
if (plainchar == plaintextend)
|
||||
{
|
||||
state_in->result = result;
|
||||
state_in->step = step_C;
|
||||
return codechar - code_out;
|
||||
}
|
||||
fragment = *plainchar++;
|
||||
result |= (fragment & 0x0c0) >> 6;
|
||||
*codechar++ = base64_encode_value(result);
|
||||
result = (fragment & 0x03f) >> 0;
|
||||
*codechar++ = base64_encode_value(result);
|
||||
|
||||
++(state_in->stepcount);
|
||||
if (state_in->stepcount == CHARS_PER_LINE/4)
|
||||
{
|
||||
*codechar++ = '\n';
|
||||
state_in->stepcount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* control should not reach here */
|
||||
return codechar - code_out;
|
||||
}
|
||||
|
||||
int base64_encode_blockend(char* code_out, base64_encodestate* state_in)
|
||||
{
|
||||
char* codechar = code_out;
|
||||
|
||||
switch (state_in->step)
|
||||
{
|
||||
case step_B:
|
||||
*codechar++ = base64_encode_value(state_in->result);
|
||||
*codechar++ = '=';
|
||||
*codechar++ = '=';
|
||||
break;
|
||||
case step_C:
|
||||
*codechar++ = base64_encode_value(state_in->result);
|
||||
*codechar++ = '=';
|
||||
break;
|
||||
case step_A:
|
||||
break;
|
||||
}
|
||||
*codechar++ = 0x00;
|
||||
|
||||
return codechar - code_out;
|
||||
}
|
||||
|
||||
#endif
|
31
src/libb64/cencode_inc.h
Normal file
31
src/libb64/cencode_inc.h
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
cencode.h - c header for a base64 encoding algorithm
|
||||
|
||||
This is part of the libb64 project, and has been placed in the public domain.
|
||||
For details, see http://sourceforge.net/projects/libb64
|
||||
*/
|
||||
|
||||
#ifndef BASE64_CENCODE_H
|
||||
#define BASE64_CENCODE_H
|
||||
|
||||
typedef enum
|
||||
{
|
||||
step_A, step_B, step_C
|
||||
} base64_encodestep;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
base64_encodestep step;
|
||||
char result;
|
||||
int stepcount;
|
||||
} base64_encodestate;
|
||||
|
||||
void base64_init_encodestate(base64_encodestate* state_in);
|
||||
|
||||
char base64_encode_value(char value_in);
|
||||
|
||||
int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, base64_encodestate* state_in);
|
||||
|
||||
int base64_encode_blockend(char* code_out, base64_encodestate* state_in);
|
||||
|
||||
#endif /* BASE64_CENCODE_H */
|
202
src/libsha1/libsha1.c
Normal file
202
src/libsha1/libsha1.c
Normal file
@ -0,0 +1,202 @@
|
||||
/* from valgrind tests */
|
||||
|
||||
/* ================ sha1.c ================ */
|
||||
/*
|
||||
SHA-1 in C
|
||||
By Steve Reid <steve@edmweb.com>
|
||||
100% Public Domain
|
||||
|
||||
Test Vectors (from FIPS PUB 180-1)
|
||||
"abc"
|
||||
A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
|
||||
"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
|
||||
84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
|
||||
A million repetitions of "a"
|
||||
34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
|
||||
*/
|
||||
|
||||
/* #define LITTLE_ENDIAN * This should be #define'd already, if true. */
|
||||
/* #define SHA1HANDSOFF * Copies data before messing with it. */
|
||||
|
||||
#ifndef ESP8266
|
||||
|
||||
#define SHA1HANDSOFF
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "libsha1.h"
|
||||
|
||||
|
||||
#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
|
||||
|
||||
/* blk0() and blk() perform the initial expand. */
|
||||
/* I got the idea of expanding during the round function from SSLeay */
|
||||
#if BYTE_ORDER == LITTLE_ENDIAN
|
||||
#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
|
||||
|(rol(block->l[i],8)&0x00FF00FF))
|
||||
#elif BYTE_ORDER == BIG_ENDIAN
|
||||
#define blk0(i) block->l[i]
|
||||
#else
|
||||
#error "Endianness not defined!"
|
||||
#endif
|
||||
#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
|
||||
^block->l[(i+2)&15]^block->l[i&15],1))
|
||||
|
||||
/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
|
||||
#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
|
||||
#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
|
||||
#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
|
||||
#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
|
||||
#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
|
||||
|
||||
|
||||
/* Hash a single 512-bit block. This is the core of the algorithm. */
|
||||
|
||||
void SHA1Transform(uint32_t state[5], const unsigned char buffer[64])
|
||||
{
|
||||
uint32_t a, b, c, d, e;
|
||||
typedef union {
|
||||
unsigned char c[64];
|
||||
uint32_t l[16];
|
||||
} CHAR64LONG16;
|
||||
#ifdef SHA1HANDSOFF
|
||||
CHAR64LONG16 block[1]; /* use array to appear as a pointer */
|
||||
memcpy(block, buffer, 64);
|
||||
#else
|
||||
/* The following had better never be used because it causes the
|
||||
* pointer-to-const buffer to be cast into a pointer to non-const.
|
||||
* And the result is written through. I threw a "const" in, hoping
|
||||
* this will cause a diagnostic.
|
||||
*/
|
||||
CHAR64LONG16* block = (const CHAR64LONG16*)buffer;
|
||||
#endif
|
||||
/* Copy context->state[] to working vars */
|
||||
a = state[0];
|
||||
b = state[1];
|
||||
c = state[2];
|
||||
d = state[3];
|
||||
e = state[4];
|
||||
/* 4 rounds of 20 operations each. Loop unrolled. */
|
||||
R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
|
||||
R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
|
||||
R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
|
||||
R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
|
||||
R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
|
||||
R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
|
||||
R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
|
||||
R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
|
||||
R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
|
||||
R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
|
||||
R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
|
||||
R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
|
||||
R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
|
||||
R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
|
||||
R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
|
||||
R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
|
||||
R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
|
||||
R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
|
||||
R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
|
||||
R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
|
||||
/* Add the working vars back into context.state[] */
|
||||
state[0] += a;
|
||||
state[1] += b;
|
||||
state[2] += c;
|
||||
state[3] += d;
|
||||
state[4] += e;
|
||||
/* Wipe variables */
|
||||
a = b = c = d = e = 0;
|
||||
#ifdef SHA1HANDSOFF
|
||||
memset(block, '\0', sizeof(block));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/* SHA1Init - Initialize new context */
|
||||
|
||||
void SHA1Init(SHA1_CTX* context)
|
||||
{
|
||||
/* SHA1 initialization constants */
|
||||
context->state[0] = 0x67452301;
|
||||
context->state[1] = 0xEFCDAB89;
|
||||
context->state[2] = 0x98BADCFE;
|
||||
context->state[3] = 0x10325476;
|
||||
context->state[4] = 0xC3D2E1F0;
|
||||
context->count[0] = context->count[1] = 0;
|
||||
}
|
||||
|
||||
|
||||
/* Run your data through this. */
|
||||
|
||||
void SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len)
|
||||
{
|
||||
uint32_t i, j;
|
||||
|
||||
j = context->count[0];
|
||||
if ((context->count[0] += len << 3) < j)
|
||||
context->count[1]++;
|
||||
context->count[1] += (len>>29);
|
||||
j = (j >> 3) & 63;
|
||||
if ((j + len) > 63) {
|
||||
memcpy(&context->buffer[j], data, (i = 64-j));
|
||||
SHA1Transform(context->state, context->buffer);
|
||||
for ( ; i + 63 < len; i += 64) {
|
||||
SHA1Transform(context->state, &data[i]);
|
||||
}
|
||||
j = 0;
|
||||
}
|
||||
else i = 0;
|
||||
memcpy(&context->buffer[j], &data[i], len - i);
|
||||
}
|
||||
|
||||
|
||||
/* Add padding and return the message digest. */
|
||||
|
||||
void SHA1Final(unsigned char digest[20], SHA1_CTX* context)
|
||||
{
|
||||
unsigned i;
|
||||
unsigned char finalcount[8];
|
||||
unsigned char c;
|
||||
|
||||
#if 0 /* untested "improvement" by DHR */
|
||||
/* Convert context->count to a sequence of bytes
|
||||
* in finalcount. Second element first, but
|
||||
* big-endian order within element.
|
||||
* But we do it all backwards.
|
||||
*/
|
||||
unsigned char *fcp = &finalcount[8];
|
||||
|
||||
for (i = 0; i < 2; i++)
|
||||
{
|
||||
uint32_t t = context->count[i];
|
||||
int j;
|
||||
|
||||
for (j = 0; j < 4; t >>= 8, j++)
|
||||
*--fcp = (unsigned char) t;
|
||||
}
|
||||
#else
|
||||
for (i = 0; i < 8; i++) {
|
||||
finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
|
||||
>> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */
|
||||
}
|
||||
#endif
|
||||
c = 0200;
|
||||
SHA1Update(context, &c, 1);
|
||||
while ((context->count[0] & 504) != 448) {
|
||||
c = 0000;
|
||||
SHA1Update(context, &c, 1);
|
||||
}
|
||||
SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */
|
||||
for (i = 0; i < 20; i++) {
|
||||
digest[i] = (unsigned char)
|
||||
((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
|
||||
}
|
||||
/* Wipe variables */
|
||||
memset(context, '\0', sizeof(*context));
|
||||
memset(&finalcount, '\0', sizeof(finalcount));
|
||||
}
|
||||
/* ================ end of sha1.c ================ */
|
||||
|
||||
|
||||
#endif
|
21
src/libsha1/libsha1.h
Normal file
21
src/libsha1/libsha1.h
Normal file
@ -0,0 +1,21 @@
|
||||
/* ================ sha1.h ================ */
|
||||
/*
|
||||
SHA-1 in C
|
||||
By Steve Reid <steve@edmweb.com>
|
||||
100% Public Domain
|
||||
*/
|
||||
|
||||
#ifndef ESP8266
|
||||
|
||||
typedef struct {
|
||||
uint32_t state[5];
|
||||
uint32_t count[2];
|
||||
unsigned char buffer[64];
|
||||
} SHA1_CTX;
|
||||
|
||||
void SHA1Transform(uint32_t state[5], const unsigned char buffer[64]);
|
||||
void SHA1Init(SHA1_CTX* context);
|
||||
void SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len);
|
||||
void SHA1Final(unsigned char digest[20], SHA1_CTX* context);
|
||||
|
||||
#endif
|
162
src/sha1/sha1.cpp
Normal file
162
src/sha1/sha1.cpp
Normal file
@ -0,0 +1,162 @@
|
||||
#include <string.h>
|
||||
//#include <avr/io.h>
|
||||
#ifdef ARDUINO_AVR_UNO
|
||||
#include <avr/pgmspace.h>
|
||||
#else
|
||||
#include <avr/pgmspace.h>
|
||||
#endif
|
||||
#include "sha1.h"
|
||||
|
||||
#define SHA1_K0 0x5a827999
|
||||
#define SHA1_K20 0x6ed9eba1
|
||||
#define SHA1_K40 0x8f1bbcdc
|
||||
#define SHA1_K60 0xca62c1d6
|
||||
|
||||
const uint8_t sha1InitState[] PROGMEM = {
|
||||
0x01,0x23,0x45,0x67, // H0
|
||||
0x89,0xab,0xcd,0xef, // H1
|
||||
0xfe,0xdc,0xba,0x98, // H2
|
||||
0x76,0x54,0x32,0x10, // H3
|
||||
0xf0,0xe1,0xd2,0xc3 // H4
|
||||
};
|
||||
|
||||
void Sha1Class::init(void) {
|
||||
memcpy_P(state.b,sha1InitState,HASH_LENGTH);
|
||||
byteCount = 0;
|
||||
bufferOffset = 0;
|
||||
}
|
||||
|
||||
uint32_t Sha1Class::rol32(uint32_t number, uint8_t bits) {
|
||||
return ((number << bits) | (number >> (32-bits)));
|
||||
}
|
||||
|
||||
void Sha1Class::hashBlock() {
|
||||
uint8_t i;
|
||||
uint32_t a,b,c,d,e,t;
|
||||
|
||||
a=state.w[0];
|
||||
b=state.w[1];
|
||||
c=state.w[2];
|
||||
d=state.w[3];
|
||||
e=state.w[4];
|
||||
for (i=0; i<80; i++) {
|
||||
if (i>=16) {
|
||||
t = buffer.w[(i+13)&15] ^ buffer.w[(i+8)&15] ^ buffer.w[(i+2)&15] ^ buffer.w[i&15];
|
||||
buffer.w[i&15] = rol32(t,1);
|
||||
}
|
||||
if (i<20) {
|
||||
t = (d ^ (b & (c ^ d))) + SHA1_K0;
|
||||
} else if (i<40) {
|
||||
t = (b ^ c ^ d) + SHA1_K20;
|
||||
} else if (i<60) {
|
||||
t = ((b & c) | (d & (b | c))) + SHA1_K40;
|
||||
} else {
|
||||
t = (b ^ c ^ d) + SHA1_K60;
|
||||
}
|
||||
t+=rol32(a,5) + e + buffer.w[i&15];
|
||||
e=d;
|
||||
d=c;
|
||||
c=rol32(b,30);
|
||||
b=a;
|
||||
a=t;
|
||||
}
|
||||
state.w[0] += a;
|
||||
state.w[1] += b;
|
||||
state.w[2] += c;
|
||||
state.w[3] += d;
|
||||
state.w[4] += e;
|
||||
}
|
||||
|
||||
void Sha1Class::addUncounted(uint8_t data) {
|
||||
buffer.b[bufferOffset ^ 3] = data;
|
||||
bufferOffset++;
|
||||
if (bufferOffset == BLOCK_LENGTH) {
|
||||
hashBlock();
|
||||
bufferOffset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(ARDUINO) && ARDUINO >= 100
|
||||
size_t
|
||||
#else
|
||||
void
|
||||
#endif
|
||||
Sha1Class::write(uint8_t data) {
|
||||
++byteCount;
|
||||
addUncounted(data);
|
||||
#if defined(ARDUINO) && ARDUINO >= 100
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
void Sha1Class::pad() {
|
||||
// Implement SHA-1 padding (fips180-2 §5.1.1)
|
||||
|
||||
// Pad with 0x80 followed by 0x00 until the end of the block
|
||||
addUncounted(0x80);
|
||||
while (bufferOffset != 56) addUncounted(0x00);
|
||||
|
||||
// Append length in the last 8 bytes
|
||||
addUncounted(0); // We're only using 32 bit lengths
|
||||
addUncounted(0); // But SHA-1 supports 64 bit lengths
|
||||
addUncounted(0); // So zero pad the top bits
|
||||
addUncounted(byteCount >> 29); // Shifting to multiply by 8
|
||||
addUncounted(byteCount >> 21); // as SHA-1 supports bitstreams as well as
|
||||
addUncounted(byteCount >> 13); // byte.
|
||||
addUncounted(byteCount >> 5);
|
||||
addUncounted(byteCount << 3);
|
||||
}
|
||||
|
||||
|
||||
uint8_t* Sha1Class::result(void) {
|
||||
// Pad to complete the last block
|
||||
pad();
|
||||
|
||||
// Swap byte order back
|
||||
for (int i=0; i<5; i++) {
|
||||
uint32_t a,b;
|
||||
a=state.w[i];
|
||||
b=a<<24;
|
||||
b|=(a<<8) & 0x00ff0000;
|
||||
b|=(a>>8) & 0x0000ff00;
|
||||
b|=a>>24;
|
||||
state.w[i]=b;
|
||||
}
|
||||
|
||||
// Return pointer to hash (20 characters)
|
||||
return state.b;
|
||||
}
|
||||
|
||||
#define HMAC_IPAD 0x36
|
||||
#define HMAC_OPAD 0x5c
|
||||
|
||||
void Sha1Class::initHmac(const uint8_t* key, int keyLength) {
|
||||
uint8_t i;
|
||||
memset(keyBuffer,0,BLOCK_LENGTH);
|
||||
if (keyLength > BLOCK_LENGTH) {
|
||||
// Hash long keys
|
||||
init();
|
||||
for (;keyLength--;) write(*key++);
|
||||
memcpy(keyBuffer,result(),HASH_LENGTH);
|
||||
} else {
|
||||
// Block length keys are used as is
|
||||
memcpy(keyBuffer,key,keyLength);
|
||||
}
|
||||
// Start inner hash
|
||||
init();
|
||||
for (i=0; i<BLOCK_LENGTH; i++) {
|
||||
write(keyBuffer[i] ^ HMAC_IPAD);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t* Sha1Class::resultHmac(void) {
|
||||
uint8_t i;
|
||||
// Complete inner hash
|
||||
memcpy(innerHash,result(),HASH_LENGTH);
|
||||
// Calculate outer hash
|
||||
init();
|
||||
for (i=0; i<BLOCK_LENGTH; i++) write(keyBuffer[i] ^ HMAC_OPAD);
|
||||
for (i=0; i<HASH_LENGTH; i++) write(innerHash[i]);
|
||||
return result();
|
||||
}
|
||||
Sha1Class Sha1;
|
47
src/sha1/sha1.h
Normal file
47
src/sha1/sha1.h
Normal file
@ -0,0 +1,47 @@
|
||||
#ifndef Sha1_h
|
||||
#define Sha1_h
|
||||
|
||||
#include <inttypes.h>
|
||||
#include "Print.h"
|
||||
|
||||
#define HASH_LENGTH 20
|
||||
#define BLOCK_LENGTH 64
|
||||
|
||||
union _buffer {
|
||||
uint8_t b[BLOCK_LENGTH];
|
||||
uint32_t w[BLOCK_LENGTH/4];
|
||||
};
|
||||
union _state {
|
||||
uint8_t b[HASH_LENGTH];
|
||||
uint32_t w[HASH_LENGTH/4];
|
||||
};
|
||||
|
||||
class Sha1Class : public Print
|
||||
{
|
||||
public:
|
||||
void init(void);
|
||||
void initHmac(const uint8_t* secret, int secretLength);
|
||||
uint8_t* result(void);
|
||||
uint8_t* resultHmac(void);
|
||||
#if defined(ARDUINO) && ARDUINO >= 100
|
||||
virtual size_t write(uint8_t);
|
||||
#else
|
||||
virtual void write(uint8_t);
|
||||
#endif
|
||||
using Print::write;
|
||||
private:
|
||||
void pad();
|
||||
void addUncounted(uint8_t data);
|
||||
void hashBlock();
|
||||
uint32_t rol32(uint32_t number, uint8_t bits);
|
||||
_buffer buffer;
|
||||
uint8_t bufferOffset;
|
||||
_state state;
|
||||
uint32_t byteCount;
|
||||
uint8_t keyBuffer[BLOCK_LENGTH];
|
||||
uint8_t innerHash[HASH_LENGTH];
|
||||
|
||||
};
|
||||
extern Sha1Class Sha1;
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user