Added exponential brightness to PWM conversion

This commit is contained in:
Mark van Renswoude 2017-03-24 23:04:58 +01:00
parent aa3bcf5eaf
commit b3d980c2ac
10 changed files with 113 additions and 34 deletions

View File

@ -87,14 +87,14 @@ Modes
Sets all steps to the same brightness.
Parameters:<br>
**brightness** (word): value in range 0 (off) to 4096 (fully on).
**brightness** (word): value in range 0 (off) to 4095 (fully on).
### Custom
Sets the brightness for each of the steps individually.
Parameters:<br>
**brightness** (word[stepCount]): array of brightness values in range 0 - 4096. The number of values must be equal to the number of steps are reported in the Ping response. Bottom step first.
**brightness** (word[stepCount]): array of brightness values in range 0 - 4095. The number of values must be equal to the number of steps are reported in the Ping response. Bottom step first.
### Alternate
@ -102,7 +102,7 @@ Alternates between even and odd steps being lit. Bring out our next contestant!
Parameters:<br>
**interval** (word): The time each set of steps is lit in milliseconds.<br>
**brightness** (word): value in range 0 (off) to 4096 (fully on).
**brightness** (word): value in range 0 (off) to 4095 (fully on).
### Slide

View File

@ -20,7 +20,7 @@ class PCA9685
public:
static const uint16_t Off = 0;
static const uint16_t On = 4096;
static const uint16_t On = 4095;
static const uint8_t RegisterMode1 = 0x0;
static const uint8_t RegisterPrescale = 0xFE;

View File

@ -47,18 +47,18 @@ void setup()
// Run a little startup test sequence
pwmDriver->setAll(PCA9685::Off);
pwmDriver->setPWM(0, PCA9685::On);
stairs->setAll(IStairs::Off);
stairs->set(0, IStairs::On);
delay(300);
for (int step = 1; step < StepCount; step++)
{
pwmDriver->setPWM(step - 1, PCA9685::Off);
pwmDriver->setPWM(step, PCA9685::On);
stairs->set(step - 1, IStairs::Off);
stairs->set(step, IStairs::On);
delay(300);
}
pwmDriver->setPWM(StepCount - 1, PCA9685::Off);
stairs->set(StepCount - 1, IStairs::Off);
// Pulsate the bottom step while WiFi is connecting
@ -71,7 +71,7 @@ void setup()
if (brightness <= 0 || brightness >= 1024)
speed = -speed;
pwmDriver->setPWM(0, brightness);
stairs->set(0, brightness);
delay(16);
}
@ -84,7 +84,15 @@ void setup()
uint32_t currentTime;
uint8_t packet[51];
// Note: the packet size must at least be able to accomodate the
// 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];
uint8_t* packetRef;
void loop()
@ -101,12 +109,11 @@ void checkRequest()
int packetSize = udpServer.parsePacket();
if (packetSize)
{
memset(packet, 0, sizeof(packet));
int length = udpServer.read(packet, 50);
if (length && packet[0])
{
packet[length] = 0;
packetRef = packet;
handleRequest(packetRef);
}
}
@ -147,6 +154,7 @@ void handleRequest(uint8_t* packet)
newMode->read(packet);
udpServer.write(Command::SetMode);
udpServer.write(newIdentifier);
newMode->write(&udpServer);
setCurrentMode(newMode, newIdentifier);

View File

@ -8,6 +8,9 @@
class IStairs
{
public:
static const uint16_t Off = 0;
static const uint16_t On = 4095;
virtual uint8_t getCount() = 0;
virtual void set(uint8_t step, uint16_t value) = 0;
virtual void setAll(uint16_t value) = 0;

View File

@ -23,7 +23,7 @@ class AlternateMode : public BaseMode<AlternateModeParameters>
AlternateMode()
{
parameters.interval = 500;
parameters.brightness = 4096;
parameters.brightness = IStairs::On;
}
void init(IStairs* stairs, uint32_t currentTime);

29
src/modes/custom.cpp Normal file
View File

@ -0,0 +1,29 @@
#include "custom.h"
#include <stdint.h>
void CustomMode::read(uint8_t* data)
{
// The packet is zeroed before we get our hands on it and
// the size should also be larger as noted in main.cpp,
// so a straight-up copy should be safe.
memcpy(this->values, data, sizeof(this->values));
}
void CustomMode::write(Stream* stream)
{
stream->write(reinterpret_cast<uint8_t*>(&this->values), sizeof(this->values));
}
void CustomMode::init(IStairs* stairs, uint32_t currentTime)
{
for (uint8_t step = 0; step < StepCount; step++)
stairs->set(step, this->values[step]);
}
void CustomMode::tick(IStairs* stairs, uint32_t currentTime)
{
}

View File

@ -9,6 +9,13 @@ class CustomMode : public IMode
{
private:
uint16_t values[StepCount];
public:
void read(uint8_t* data);
void write(Stream* stream);
void init(IStairs* stairs, uint32_t currentTime);
void tick(IStairs* stairs, uint32_t currentTime);
};
#endif

View File

@ -1,6 +1,11 @@
#include <Math.h>
#include "stairs.h"
#include "config.h"
static const float brightnessFactor = ((IStairs::On - IStairs::Off) + 1) * log10(2) / log10(PCA9685::On);
void Stairs::init(PCA9685* pwmDriver)
{
this->pwmDriver = pwmDriver;
@ -13,13 +18,24 @@ uint8_t Stairs::getCount()
}
void Stairs::set(uint8_t step, uint16_t value)
void Stairs::set(uint8_t step, uint16_t brightness)
{
pwmDriver->setPWM(step, value);
pwmDriver->setPWM(step, this->getPWMValue(brightness));
}
void Stairs::setAll(uint16_t value)
void Stairs::setAll(uint16_t brightness)
{
pwmDriver->setAll(value);
pwmDriver->setAll(this->getPWMValue(brightness));
}
uint16_t Stairs::getPWMValue(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;
}

View File

@ -9,12 +9,15 @@ class Stairs : public IStairs
private:
PCA9685* pwmDriver;
protected:
uint16_t getPWMValue(uint16_t brightness);
public:
void init(PCA9685* pwmDriver);
uint8_t getCount();
void set(uint8_t step, uint16_t value);
void setAll(uint16_t value);
void set(uint8_t step, uint16_t brightness);
void setAll(uint16_t brightness);
};
#endif

View File

@ -9,6 +9,32 @@ function lsb(value) { return value & 0xFF; }
function msb(value) { return (value >> 8) & 0xFF; }
/*
Alternating
var message = new Buffer([protocol.Command.SetMode, protocol.Mode.Alternate, lsb(500), msb(500), lsb(128), msb(128)]);
client.send(message, 0, message.length, 3126, '10.138.2.12', function(err, bytes) {
if (err) throw err;
console.log('UDP message sent');
});
*/
var client = dgram.createSocket('udp4');
client.on('listening', function()
{
var address = client.address();
console.log('UDP client listening on ' + address.address + ":" + address.port);
});
client.on('message', function (message, remote)
{
console.log('< ' + remote.address + ':' + remote.port +' - ' + message.toString('hex'));
});
setInterval(function()
{
// 0x00, 0x10 = 4096
@ -17,21 +43,8 @@ setInterval(function()
speed = -speed;
var message = new Buffer([protocol.Command.SetMode, protocol.Mode.Static, lsb(on), msb(on)]);
var client = dgram.createSocket('udp4');
client.on('listening', function()
{
var address = client.address();
console.log('UDP client listening on ' + address.address + ":" + address.port);
});
client.on('message', function (message, remote)
{
console.log(remote.address + ':' + remote.port +' - ' + message.toString('hex'));
client.close();
});
client.send(message, 0, message.length, 3126, '10.138.2.12', function(err, bytes) {
if (err) throw err;
console.log('UDP message sent');
console.log('> ' + '10.138.2.12' + ':' + '3126' + ' - ' + message.toString('hex'));
});
}, 200);