add streaming stuff

This commit is contained in:
2018-10-03 13:32:23 +02:00
parent dff8545d65
commit 9c6ec9b231
12 changed files with 413 additions and 102 deletions

View File

@@ -1,7 +1,7 @@
{
"pin": 4,
"length": 60,
"length": 300,
"brightness": 64,
"updateInterval": 150,
"updateInterval": 50,
"defaultColor": 100
}

View File

@@ -39,7 +39,7 @@
data-name="pattern"
data-topic="pixels/pattern"
data-default="0"
data-entries='[{"text": "None", "value": "0"}, {"text": "Rainbow", "value": "1"}, {"text": "TheaterChase", "value": "2"}, {"text": "Fade", "value": "5"}]'
data-entries='[{"text": "None", "value": "0"}, {"text": "Rainbow", "value": "1"}, {"text": "TheaterChase", "value": "2"}, {"text": "Color Wipe", "value": "3"}, {"text": "Scanner", "value": "4"}, {"text": "Fade", "value": "5"}]'
></li>
<li class="form-row ParamSlider"
data-name="brightness"
@@ -53,7 +53,7 @@
data-name="totalSteps"
data-min="1"
data-max="255"
data-value="255"
data-value="16"
data-topic="pixels/totalSteps"
data-label="Steps">
</li>

View File

@@ -10798,17 +10798,17 @@ __WEBPACK_IMPORTED_MODULE_0_jquery___default()(() => {
let colors = payload.split(',');
let msg = JSON.stringify({
topic: 'pixels/state',
broadcast: "1",
broadcast: 1,
payload: JSON.stringify({
brightness: 64,
//brightness: 64,
color: parseInt(colors[0].replace('#', '0x'), 16),
color2: parseInt(colors[1].replace('#', '0x'), 16),
totalSteps: 128,
//totalSteps: 64,
pattern: 5
})
});
app.mediator.trigger('pixels/brightness', 64);
app.mediator.trigger('pixels/totalSteps', 128);
//app.mediator.trigger('pixels/brightness', 64);
//app.mediator.trigger('pixels/totalSteps', 64);
app.mediator.trigger('pixels/pattern', 5);
app.mediator.trigger('pixels/color', colors[0]);
app.mediator.trigger('pixels/color2', colors[1]);

106
data/www/test-controls.html Normal file
View File

@@ -0,0 +1,106 @@
<!DOCTYPE html>
<html>
<head>
<title>ESP Kit</title>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-32x32.png">
<link rel="stylesheet" type="text/css" href="styles.css">
<script src="script.js"></script>
</head>
<body class="sui">
<div class="content">
<!-- <form class="param-control container open collapsible">
<span class="heading">Debug</span>
<div class="content">
<ul>
<li class="form-row ParamWs"
data-name="scrollText"
data-label="Audio"
data-placeholder="some scroll text"
data-endpoint="audiosprocket.lan/audio">
</li>
</ul>
<br>
</div>
</form> -->
<form class="param-control container collapsible open" action="#" method="POST">
<span class="heading">Strip</span>
<div class="content">
<ul>
<li class="form-row ParamColor"
data-mode="2"
data-name="color"
data-label="Color">
</li>
<li class="form-row ParamColor"
data-mode="2"
data-name="color2"
data-label="Color 2">
</li>
<li class="form-row ParamSelect"
data-label="Pattern"
data-name="pattern"
></li>
<li class="form-row LedStripPatternSwitch"
data-group="stripPattern"
data-id="1"
data-mode="3"
data-name="Rainbow"
data-label="Rainbow">
</li>
<li class="form-row LedStripPatternSwitch"
data-group="stripPattern"
data-name="TheaterChase"
data-label="Theater Chase"
data-id="2"
data-mode="3">
</li>
<li class="form-row LedStripPatternSwitch"
data-group="stripPattern"
data-name="Scanner"
data-label="Scanner"
data-id="4"
data-mode="3">
</li>
</ul>
</div>
</form>
<div class="settings container collapsible open">
<span class="heading">WiFi Settings</span>
<div class="content">
<!-- <div class="Form" data-name="configForm" data-endpoint="/config.json"></div> -->
<form action="/wifiConfig" method="POST">
<!-- <li class="form-row">
<label for="ap">AP Mode</label>
<label class="switch ap-mode">
<input type="checkbox" name="apMode">
<span class="slider round" data-bind="apMode" data-state="false"></span>
</label>
</li -->
<li class="form-row">
<label for="ssid">SSID</label>
<input type="text" name="ssid" placeholder="Default AP: Th1ngs4P">
</li>
<li class="form-row">
<label for="password">PW</label>
<input type="password" name="password" placeholder="Default: th3r31sn0sp00n">
</li>
<li class="form-row">
<label for="hostName">Hostname</label>
<input type="text" name="hostName" placeholder="Default: 192.168.1.143">
</li>
<li class="form-row">
<button type="submit">Save</button>
</li>
</ul>
</form>
</div>
</div>
</div>
</body>
</html>

View File

@@ -1,3 +1,15 @@
/**
* Original NeoPattern code by Bill Earl
* https://learn.adafruit.com/multi-tasking-the-arduino-part-3/overview
*
* TODO
* - cleanup the mess
* - fnc table for patterns to replace switch case
*
* Custom modifications by 0x1d:
* - default OnComplete callback that sets pattern to reverse
* - separate animation update from timer; Update now updates directly, UpdateScheduled uses timer
*/
#ifndef __NeoPattern_INCLUDED__
#define __NeoPattern_INCLUDED__
@@ -6,25 +18,27 @@
using namespace std;
using namespace std::placeholders;
/**
* Original NeoPattern code by Bill Earl
* https://learn.adafruit.com/multi-tasking-the-arduino-part-3/overview
*
* Custom modifications by 0x1d:
* - default OnComplete callback that sets pattern to reverse
* - separate animation update from timer; Update now updates directly, UpdateScheduled uses timer
*/
// Pattern types supported:
enum pattern { NONE = 0, RAINBOW_CYCLE = 1, THEATER_CHASE = 2, COLOR_WIPE = 3, SCANNER = 4, FADE = 5 };
enum pattern
{
NONE = 0,
RAINBOW_CYCLE = 1,
THEATER_CHASE = 2,
COLOR_WIPE = 3,
SCANNER = 4,
FADE = 5
};
// Patern directions supported:
enum direction { FORWARD, REVERSE };
enum direction
{
FORWARD,
REVERSE
};
// NeoPattern Class - derived from the Adafruit_NeoPixel class
class NeoPattern : public Adafruit_NeoPixel
{
public:
// Member Variables:
pattern ActivePattern = RAINBOW_CYCLE; // which pattern is running
direction Direction = FORWARD; // direction to run the pattern
@@ -38,27 +52,50 @@ class NeoPattern : public Adafruit_NeoPixel
uint16_t Index; // current step within the pattern
uint16_t completed = 0;
// FIXME return current NeoPatternState
void (*OnComplete)(int); // Callback on completion of pattern
uint8_t *frameBuffer;
int bufferSize = 0;
// Constructor - calls base-class constructor to initialize strip
NeoPattern(uint16_t pixels, uint8_t pin, uint8_t type, void (*callback)(int))
:Adafruit_NeoPixel(pixels, pin, type)
: Adafruit_NeoPixel(pixels, pin, type)
{
frameBuffer = (uint8_t*)malloc(768);
OnComplete = callback;
TotalSteps = numPixels();
}
NeoPattern(uint16_t pixels, uint8_t pin, uint8_t type)
:Adafruit_NeoPixel(pixels, pin, type)
: Adafruit_NeoPixel(pixels, pin, type)
{
//OnComplete = bind(&NeoPattern::onCompleteDefault, this, _1);
frameBuffer = (uint8_t*)malloc(768);
TotalSteps = numPixels();
}
void onCompleteDefault(int pixels) {
void handleStream(uint8_t *data, size_t len)
{
//const uint16_t *data16 = (uint16_t *)data;
bufferSize = len;
memcpy(frameBuffer, data, len);
}
void drawFrameBuffer(int w, uint8_t *frame, int length){
for (int i = 0; i < length; i++){
uint8_t r = frame[i];
uint8_t g = frame[++i];
uint8_t b = frame[++i];
setPixelColor(i, r ,g, b);
}
}
void onCompleteDefault(int pixels)
{
//Serial.println("onCompleteDefault");
// FIXME no specific code
if(ActivePattern == THEATER_CHASE){
if (ActivePattern == THEATER_CHASE)
{
return;
}
Reverse();
@@ -68,7 +105,7 @@ class NeoPattern : public Adafruit_NeoPixel
// Update the pattern
void Update()
{
switch(ActivePattern)
switch (ActivePattern)
{
case RAINBOW_CYCLE:
RainbowCycleUpdate();
@@ -86,13 +123,16 @@ class NeoPattern : public Adafruit_NeoPixel
FadeUpdate();
break;
default:
if(bufferSize > 0){
drawFrameBuffer(TotalSteps, frameBuffer, bufferSize);
}
break;
}
}
void UpdateScheduled()
{
if((millis() - lastUpdate) > Interval) // time to update
if ((millis() - lastUpdate) > Interval) // time to update
{
lastUpdate = millis();
Update();
@@ -113,7 +153,9 @@ class NeoPattern : public Adafruit_NeoPixel
if (OnComplete != NULL)
{
OnComplete(numPixels()); // call the comlpetion callback
} else {
}
else
{
onCompleteDefault(numPixels());
}
}
@@ -123,12 +165,14 @@ class NeoPattern : public Adafruit_NeoPixel
--Index;
if (Index <= 0)
{
Index = TotalSteps-1;
Index = TotalSteps - 1;
completed = 1;
if (OnComplete != NULL)
{
OnComplete(numPixels()); // call the comlpetion callback
} else {
}
else
{
onCompleteDefault(numPixels());
}
}
@@ -141,7 +185,7 @@ class NeoPattern : public Adafruit_NeoPixel
if (Direction == FORWARD)
{
Direction = REVERSE;
Index = TotalSteps-1;
Index = TotalSteps - 1;
}
else
{
@@ -163,7 +207,7 @@ class NeoPattern : public Adafruit_NeoPixel
// Update the Rainbow Cycle Pattern
void RainbowCycleUpdate()
{
for(int i=0; i< numPixels(); i++)
for (int i = 0; i < numPixels(); i++)
{
setPixelColor(i, Wheel(((i * 256 / numPixels()) + Index) & 255));
}
@@ -186,7 +230,7 @@ class NeoPattern : public Adafruit_NeoPixel
// Update the Theater Chase Pattern
void TheaterChaseUpdate()
{
for(int i=0; i< numPixels(); i++)
for (int i = 0; i < numPixels(); i++)
{
if ((i + Index) % 3 == 0)
{
@@ -320,11 +364,11 @@ class NeoPattern : public Adafruit_NeoPixel
{
//if(WheelPos == 0) return Color(0,0,0);
WheelPos = 255 - WheelPos;
if(WheelPos < 85)
if (WheelPos < 85)
{
return Color(255 - WheelPos * 3, 0, WheelPos * 3);
}
else if(WheelPos < 170)
else if (WheelPos < 170)
{
WheelPos -= 85;
return Color(0, WheelPos * 3, 255 - WheelPos * 3);

41
package-lock.json generated Normal file
View File

@@ -0,0 +1,41 @@
{
"requires": true,
"lockfileVersion": 1,
"dependencies": {
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"color-string": {
"version": "1.5.3",
"resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz",
"integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==",
"requires": {
"color-name": "1.1.4",
"simple-swizzle": "0.2.2"
}
},
"is-arrayish": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
"integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="
},
"neopixel-utils": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/neopixel-utils/-/neopixel-utils-1.0.2.tgz",
"integrity": "sha1-e2IJvR3kmEaRu5nR46iv+8h1Zh4=",
"requires": {
"color-string": "1.5.3"
}
},
"simple-swizzle": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
"integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=",
"requires": {
"is-arrayish": "0.3.2"
}
}
}
}

View File

@@ -51,3 +51,15 @@ build_flags = -Wl,-Teagle.flash.4m1m.ld
-DSPROCKET_PRINT=0
lib_deps = ${common.lib_deps}
https://gitlab.com/wirelos/sprocket-core.git#master
[env:nodemcu]
platform = ${common.platform}
board = nodemcu
upload_speed = ${common.upload_speed}
monitor_baud = ${common.monitor_baud}
framework = ${common.framework}
build_flags = -Wl,-Teagle.flash.4m1m.ld
-DSPROCKET_PRINT=1
lib_deps = ${common.lib_deps}
https://gitlab.com/wirelos/sprocket-core.git#develop

View File

@@ -28,6 +28,7 @@ class IlluCat : public MeshSprocket {
NeoPatternDto state;
AsyncWebServer* server;
AsyncWebSocket* ws;
AsyncWebSocket* wsStream;
NeoPixelConfig pixelConfig;
SprocketConfig sprocketConfig;
@@ -72,6 +73,7 @@ class IlluCat : public MeshSprocket {
pixels = new NeoPattern(pixelConfig.length, pixelConfig.pin, NEO_GRB + NEO_KHZ800);
server = new AsyncWebServer(80);
ws = new AsyncWebSocket("/pixel");
wsStream = new AsyncWebSocket("/stream");
// add plugins
addPlugin(new OtaTcpPlugin(otaConfig));
@@ -89,6 +91,8 @@ class IlluCat : public MeshSprocket {
server->on("/pixel/api", HTTP_POST, bind(&IlluCat::patternWebRequestHandler, this, _1));
ws->onEvent(bind(&IlluCat::onWsEvent, this, _1, _2, _3, _4, _5, _6));
server->addHandler(ws);
wsStream->onEvent(bind(&IlluCat::onStream, this, _1, _2, _3, _4, _5, _6));
server->addHandler(wsStream);
return MeshSprocket::activate(scheduler, network);
} using MeshSprocket::activate;
@@ -101,6 +105,15 @@ class IlluCat : public MeshSprocket {
return defaultValue;
}
void onStream(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len) {
if(type == WS_EVT_DATA){
PRINT_MSG(Serial, SPROCKET_TYPE, WsUtils::parseFrameAsString(type, arg, data, len, 0).c_str());
pixels->ActivePattern = NONE;
pixels->handleStream(data, len);
}
}
void patternWebRequestHandler(AsyncWebServerRequest *request) {
PRINT_MSG(Serial, SPROCKET_TYPE, "POST /pixel/api");
currentMessage.topic = getRequestParameterOrDefault(request, "topic", "");

View File

@@ -25,6 +25,7 @@ class PixelPlugin : public Plugin {
pixels = neoPattern;
pixels->begin();
pixels->setBrightness(pixelConfig.brightness);
}
void activate(Scheduler* userScheduler, Network* network){
subscribe("pixels/colorWheel", bind(&PixelPlugin::colorWheel, this, _1));
@@ -34,16 +35,13 @@ class PixelPlugin : public Plugin {
subscribe("pixels/totalSteps", bind(&PixelPlugin::setTotalSteps, this, _1));
subscribe("pixels/brightness", bind(&PixelPlugin::setBrightness, this, _1));
subscribe("pixels/state", bind(&PixelPlugin::setState, this, _1));
subscribe("pixels/stream", bind(&PixelPlugin::stream, this, _1));
animation.set(TASK_MILLISECOND * pixelConfig.updateInterval, TASK_FOREVER, bind(&PixelPlugin::animate, this));
userScheduler->addTask(animation);
animation.enable();
PRINT_MSG(Serial, SPROCKET_TYPE, "NeoPixels activated");
}
void stream(String msg){
// TODO handle LED byte array stream
}
void setState(String msg) {
PRINT_MSG(Serial, SPROCKET_TYPE, msg.c_str());
state.fromJsonString(msg);

45
test/package-lock.json generated
View File

@@ -7,11 +7,56 @@
"resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz",
"integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg=="
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
"color-string": {
"version": "1.5.3",
"resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz",
"integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==",
"dev": true,
"requires": {
"color-name": "1.1.4",
"simple-swizzle": "0.2.2"
}
},
"coolhue": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/coolhue/-/coolhue-1.0.9.tgz",
"integrity": "sha512-4+ctEja6XNJ8GrV+OLbioHWssC8tT/IUSZlS6i/RXc0R+ef7g6jNGmLC4VwhEKCJXTKPQ0FqbwojYLDtpPXs1w=="
},
"is-arrayish": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
"integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==",
"dev": true
},
"neopixel-utils": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/neopixel-utils/-/neopixel-utils-1.0.2.tgz",
"integrity": "sha1-e2IJvR3kmEaRu5nR46iv+8h1Zh4=",
"dev": true,
"requires": {
"color-string": "1.5.3"
}
},
"ramda": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/ramda/-/ramda-0.25.0.tgz",
"integrity": "sha512-GXpfrYVPwx3K7RQ6aYT8KPS8XViSXUVJT1ONhoKPE9VAleW42YE+U+8VEyGWt41EnEQW7gwecYJriTI0pKoecQ=="
},
"simple-swizzle": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
"integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=",
"dev": true,
"requires": {
"is-arrayish": "0.3.2"
}
},
"ws": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-6.0.0.tgz",

52
test/stream.js Normal file
View File

@@ -0,0 +1,52 @@
const WebSocket = require('ws');
const { times, min, max } = require('ramda');
const cmdWs = new WebSocket('ws://192.168.1.247/stream');
const {Strip} = require('neopixel-utils');
const randomColor = () => Math.floor(Math.random() * 255);
let strip = Strip(8);
//strip.setPixelColor(0, [randomColor(), randomColor(), randomColor()]);
//console.log(strip.getPixelColor(0));
/* const color = (r, g, b) => {
return uint16_t((r & 0xF8) << 8)
| uint16_t((g & 0xFC) << 3)
| uint16_t(b >> 3);
} */
/* const color = (r, g, b) => {
return ((r & 0xF8) << 8)
| ((g & 0xFC) << 3)
| (b >> 3);
}
const createTest = (ws, items) => () => {
console.log(`Items: ${items}`)
let array = new Uint16Array(items);
times(index => {
const r = index === 0 ? 255 : Math.floor(Math.random() * 255)
const g = index === 0 ? 0 : Math.floor(Math.random() * 255)
const b = index === 0 ? 255 : Math.floor(Math.random() * 255)
const number = color(r, g, b)
console.log(`R: ${r} G: ${g} B: ${b}`)
//const number = Math.floor(Math.random() * 65535);
array[index] = number
console.log(number)
}
, items);
ws.send(array);
}
*/
const createTest = (ws, items) => () => {
console.log(`Items: ${items}`)
times(index => {
strip.setPixelColor(index, [randomColor(), randomColor(), randomColor()]);
} , items);
console.log(strip.buffer);
ws.send(strip.buffer);
}
cmdWs.on('message', console.log);
cmdWs.on('open', createTest(cmdWs, 5));