Added GetRange/SetRange functionality (untested)
Fixed custom mode on frontend
This commit is contained in:
parent
4fbbee8f5b
commit
8fa9b63dfc
|
@ -21,6 +21,8 @@ The first byte of a request is the command. Further data depends on the command.
|
|||
| 0x01 | Ping |
|
||||
| 0x03 | GetMode |
|
||||
| 0x04 | SetMode |
|
||||
| 0x05 | GetRange |
|
||||
| 0x06 | SetRange |
|
||||
|
||||
|
||||
Response
|
||||
|
@ -73,6 +75,32 @@ Input parameters:<br>
|
|||
Output parameters:<br>
|
||||
_Same as input parameters_
|
||||
|
||||
|
||||
### GetRange
|
||||
Gets the current range configuration. Each step has it's own parameters which adjust the PWM curve, which can be used to compensate for the differences in LED strips or signal strength.
|
||||
|
||||
The ranges are stored on the ESP8266's filesystem and will be restored after a power loss.
|
||||
|
||||
Input parameters:<br>
|
||||
_none_
|
||||
|
||||
Output parameters:<br>
|
||||
*useScaling* (byte): 0 (off) or 1 (on), default 1. If enabled, the brightness value will be converted to a PWM value using an exponential function. If disabled, the brightness is used as is (but still accounting for rangeStart/rangeEnd).
|
||||
_repeated for each step:_<br>
|
||||
*rangeStart* (word): value in range 0 (off) to 4095 (fully on), default 0. Determines the minimum PWM on value for a brightness of 1.
|
||||
*rangeEnd* (word): value in range 0 (off) to 4095 (fully on), default 4094. Determines the maximum PWM on value for a brightness of 4094.
|
||||
|
||||
|
||||
### SetRange
|
||||
Sets the current range configuration.
|
||||
|
||||
Input parameters:<br>
|
||||
_See GetRange_
|
||||
|
||||
Output parameters:<br>
|
||||
_Same as input parameters_
|
||||
|
||||
|
||||
Modes
|
||||
=====
|
||||
|
||||
|
|
|
@ -89,10 +89,10 @@ uint32_t currentTime;
|
|||
// command with the largest parameter list, there is no overflow
|
||||
// checking in the mode classes!
|
||||
//
|
||||
// At the time of writing, CustomMode is the largest:
|
||||
// Set Mode (1) + Custom (1) + StepCount * Word (2)
|
||||
// = 30 for 14 steps
|
||||
uint8_t packet[50];
|
||||
// At the time of writing, Get/SetRange is the largest:
|
||||
// Set Range (1) + UseScaling (1) + StepCount * 2x Word (4)
|
||||
// = 66 for the maximum of 16 steps
|
||||
uint8_t packet[100];
|
||||
uint8_t* packetRef;
|
||||
|
||||
void loop()
|
||||
|
|
|
@ -12,6 +12,8 @@ class Command
|
|||
static const uint8_t Reply = 0x02;
|
||||
static const uint8_t GetMode = 0x03;
|
||||
static const uint8_t SetMode = 0x04;
|
||||
static const uint8_t GetRange = 0x05;
|
||||
static const uint8_t SetRange = 0x06;
|
||||
};
|
||||
|
||||
|
||||
|
|
101
src/stairs.cpp
101
src/stairs.cpp
|
@ -1,14 +1,26 @@
|
|||
#include <Math.h>
|
||||
#include <FS.h>
|
||||
#include "stairs.h"
|
||||
#include "config.h"
|
||||
|
||||
|
||||
static const float brightnessFactor = ((IStairs::On - IStairs::Off) + 1) * log10(2) / log10(PCA9685::On);
|
||||
|
||||
const static float factorBase = log10(2) / log10(PCA9685::On);
|
||||
|
||||
|
||||
struct Header
|
||||
{
|
||||
uint8_t version;
|
||||
uint8_t rangeCount;
|
||||
bool useScaling;
|
||||
};
|
||||
|
||||
|
||||
void Stairs::init(PCA9685* pwmDriver)
|
||||
{
|
||||
this->pwmDriver = pwmDriver;
|
||||
|
||||
SPIFFS.begin();
|
||||
this->readRange();
|
||||
}
|
||||
|
||||
|
||||
|
@ -20,22 +32,95 @@ uint8_t Stairs::getCount()
|
|||
|
||||
void Stairs::set(uint8_t step, uint16_t brightness)
|
||||
{
|
||||
pwmDriver->setPWM(step, this->getPWMValue(brightness));
|
||||
pwmDriver->setPWM(step, this->getPWMValue(step, brightness));
|
||||
}
|
||||
|
||||
|
||||
void Stairs::setAll(uint16_t brightness)
|
||||
{
|
||||
pwmDriver->setAll(this->getPWMValue(brightness));
|
||||
//pwmDriver->setAll(this->getPWMValue(brightness));
|
||||
|
||||
for (uint8_t step = 0; step < StepCount; step++)
|
||||
pwmDriver->setPWM(step, this->getPWMValue(step, brightness));
|
||||
}
|
||||
|
||||
|
||||
uint16_t Stairs::getPWMValue(uint16_t brightness)
|
||||
uint16_t Stairs::getPWMValue(uint8_t step, uint16_t brightness)
|
||||
{
|
||||
if (brightness == IStairs::Off || brightness == IStairs::On)
|
||||
return brightness;
|
||||
|
||||
// Correct for the exponential perception of brightness
|
||||
// Source: https://diarmuid.ie/blog/pwm-exponential-led-fading-on-arduino-or-other-platforms/
|
||||
return pow(2, (brightness / brightnessFactor)) - 1;
|
||||
if (step < 0 || step >= StepCount)
|
||||
return brightness;
|
||||
|
||||
Range* range = this->ranges[step];
|
||||
|
||||
if (this->useScaling)
|
||||
{
|
||||
float factor = ((range->end - range->start) + 1) * factorBase;
|
||||
brightness = pow(2, (brightness / factor)) - 1 + range->start;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (brightness < range->start) brightness = range->start;
|
||||
if (brightness > range->end) brightness = range->end;
|
||||
}
|
||||
|
||||
return brightness;
|
||||
}
|
||||
|
||||
|
||||
void Stairs::getRange(Stream* stream)
|
||||
{
|
||||
stream->write(this->useScaling ? 1 : 0);
|
||||
stream->write(reinterpret_cast<uint8_t*>(&this->ranges), sizeof(this->ranges));
|
||||
}
|
||||
|
||||
|
||||
void Stairs::setRange(uint8_t* data)
|
||||
{
|
||||
this->useScaling = *data;
|
||||
data++;
|
||||
|
||||
memcpy(this->ranges, data, sizeof(this->ranges));
|
||||
}
|
||||
|
||||
|
||||
void Stairs::readRange()
|
||||
{
|
||||
File f = SPIFFS.open("/range", "r");
|
||||
if (!f)
|
||||
return;
|
||||
|
||||
if (!f.available())
|
||||
return;
|
||||
|
||||
Header header;
|
||||
f.readBytes(reinterpret_cast<char*>(&header), sizeof(Header));
|
||||
|
||||
if (header.version != 1)
|
||||
return;
|
||||
|
||||
this->useScaling = (header.useScaling == 1);
|
||||
|
||||
memset(this->ranges, 0, sizeof(this->ranges));
|
||||
f.readBytes(reinterpret_cast<char*>(&this->ranges), header.rangeCount * sizeof(Range));
|
||||
f.close();
|
||||
}
|
||||
|
||||
|
||||
void Stairs::writeRange()
|
||||
{
|
||||
File f = SPIFFS.open("/range", "w");
|
||||
if (!f)
|
||||
return;
|
||||
|
||||
Header header;
|
||||
header.version = 1;
|
||||
header.useScaling = this->useScaling;
|
||||
header.rangeCount = StepCount;
|
||||
|
||||
f.write(reinterpret_cast<uint8_t*>(&header), sizeof(Header));
|
||||
f.write(reinterpret_cast<uint8_t*>(&this->ranges), sizeof(this->ranges));
|
||||
f.close();
|
||||
}
|
20
src/stairs.h
20
src/stairs.h
|
@ -3,14 +3,29 @@
|
|||
|
||||
#include "components/PCA9685.h"
|
||||
#include "modes/base.h"
|
||||
#include "config.h"
|
||||
|
||||
|
||||
struct Range
|
||||
{
|
||||
uint16_t start;
|
||||
uint16_t end;
|
||||
};
|
||||
|
||||
|
||||
class Stairs : public IStairs
|
||||
{
|
||||
private:
|
||||
PCA9685* pwmDriver;
|
||||
|
||||
bool useScaling;
|
||||
Range* ranges[StepCount];
|
||||
|
||||
protected:
|
||||
uint16_t getPWMValue(uint16_t brightness);
|
||||
void readRange();
|
||||
void writeRange();
|
||||
|
||||
uint16_t getPWMValue(uint8_t step, uint16_t brightness);
|
||||
|
||||
public:
|
||||
void init(PCA9685* pwmDriver);
|
||||
|
@ -18,6 +33,9 @@ class Stairs : public IStairs
|
|||
uint8_t getCount();
|
||||
void set(uint8_t step, uint16_t brightness);
|
||||
void setAll(uint16_t brightness);
|
||||
|
||||
void getRange(Stream* stream);
|
||||
void setRange(uint8_t* data);
|
||||
};
|
||||
|
||||
#endif
|
|
@ -31,7 +31,7 @@
|
|||
<div class="parameters" data-bind="visible: mode() == 'Static'" style="display: none">
|
||||
<div class="header">Static parameters</div>
|
||||
<div class="parameter">
|
||||
Brightness: <input type="range" min="0" max="4095" data-bind="value: static.brightness, valueUpdate: 'input'" />
|
||||
Brightness: <input type="range" min="0" max="4095" data-bind="value: static.brightness, valueUpdate: 'input'" /> <input type="number" min="0" max="4095" data-bind="value: static.brightness" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -39,7 +39,7 @@
|
|||
<div class="header">Custom parameters</div>
|
||||
<!-- ko foreach: custom.brightness -->
|
||||
<div class="parameter">
|
||||
Step <span data-bind="text: $root.custom.brightness().length - $index()"></span>: <input type="range" min="0" max="4095" data-bind="value: $data, valueUpdate: 'input'" />
|
||||
Step <span data-bind="text: $root.custom.brightness().length - $index()"></span>: <input type="range" min="0" max="4095" data-bind="value: $data.value, valueUpdate: 'input'" /> <input type="number" min="0" max="4095" data-bind="value: $data.value" />
|
||||
</div>
|
||||
<!-- /ko -->
|
||||
</div>
|
||||
|
@ -50,14 +50,14 @@
|
|||
Interval: <input type="number" data-bind="value: alternate.interval" />
|
||||
</div>
|
||||
<div class="parameter">
|
||||
Brightness: <input type="range" min="0" max="4095" data-bind="value: alternate.brightness, valueUpdate: 'input'" />
|
||||
Brightness: <input type="range" min="0" max="4095" data-bind="value: alternate.brightness, valueUpdate: 'input'" /> <input type="number" min="0" max="4095" data-bind="value: alternate.brightness" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="parameters" data-bind="visible: mode() == 'Slide'" style="display: none">
|
||||
<div class="header">Sliding parameters</div>
|
||||
<div class="parameter">
|
||||
Brightness: <input type="range" min="0" max="4095" data-bind="value: slide.brightness, valueUpdate: 'input'" />
|
||||
Brightness: <input type="range" min="0" max="4095" data-bind="value: slide.brightness, valueUpdate: 'input'" /> <input type="number" min="0" max="4095" data-bind="value: slide.brightness" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -44,7 +44,9 @@ var StairsViewModel = function()
|
|||
break;
|
||||
|
||||
case 'Custom':
|
||||
url += '?brightness=' + encodeURIComponent(self.custom.brightness().map(function(value) { return value(); }).join());
|
||||
var values = self.custom.brightness().map(function(item) { return item.value(); });
|
||||
values.reverse();
|
||||
url += '?brightness=' + encodeURIComponent(values.join());
|
||||
break;
|
||||
|
||||
case 'Alternate':
|
||||
|
@ -104,12 +106,20 @@ var StairsViewModel = function()
|
|||
{
|
||||
self.updatingFromServer = true;
|
||||
|
||||
// Initialize the 'Custom' values based on the step count
|
||||
// Initialize the 'Custom' values based on the step count.
|
||||
// Each item is an object containing an observable; we can't
|
||||
// add the observable directly otherwise the foreach template
|
||||
// will unwrap it before we can attach it to the range input.
|
||||
var values = [];
|
||||
for (var index = 0; index < data.stepCount; index++)
|
||||
values.push(ko.observable(0));
|
||||
values.push({ value: ko.observable(0) });
|
||||
|
||||
self.custom.brightness(values);
|
||||
|
||||
// TODO get current mode
|
||||
// TODO get range settings
|
||||
// TODO allow tweaking of range settings
|
||||
|
||||
self.loading(false);
|
||||
|
||||
self.updatingFromServer = false;
|
||||
|
|
Loading…
Reference in New Issue