Initial commit, very much a work in progress

This commit is contained in:
Mark van Renswoude 2017-03-23 19:45:29 +01:00
commit abe13843b4
22 changed files with 755 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
.pioenvs
.piolibdeps
src/credentials.h
*.sublime-workspace

3
TODO Normal file
View File

@ -0,0 +1,3 @@
1. Add an "ease" option to the static mode
other mode to static: from 0
static to static: depends on current brightness

View File

@ -0,0 +1,22 @@
{
"folders":
[
{
"path": ".",
"file_exclude_patterns": ["*.sublime-project"]
}
]/*,
"build_systems":
[
{
"name": "PlatformIO - Build",
"cmd": ["platformio", "run"],
"working_dir": "$project_path"
},
{
"name": "PlatformIO - Upload",
"cmd": ["platformio", "run", "--target", "upload"],
"working_dir": "$project_path"
}
]*/
}

1
build.bat Normal file
View File

@ -0,0 +1 @@
@platformio run

15
platformio.ini Normal file
View File

@ -0,0 +1,15 @@
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; http://docs.platformio.org/page/projectconf.html
[env:esp12e]
platform = espressif8266_stage
board = esp12e
framework = arduino
upload_speed = 115200

193
src/components/PCA9685.cpp Normal file
View File

@ -0,0 +1,193 @@
/*
* PCA9685 library for ESP8266
* Copyright 2017 (c) Mark van Renswoude
*
* Uses brzo I2C library.
*/
#include <stdint.h>
#include <brzo_i2c.h>
#include "PCA9685.h"
#include <Arduino.h>
void PCA9685::setAddress(uint8_t address, uint16_t sclFrequency, uint8_t pinSDA, uint8_t pinSCL, uint32_t timeoutUSec)
{
}
void PCA9685::setAddress(uint8_t address, uint16_t sclFrequency)
{
}
uint8_t PCA9685::read(uint8_t registerAddress)
{
return 0;
}
void PCA9685::write(uint8_t registerAddress, uint8_t value)
{
}
inline void PCA9685::write(uint8_t data)
{
}
void PCA9685::setPWMFrequency(float frequency)
{
}
void PCA9685::setPWM(uint8_t pin, uint16_t value)
{
this->setAll(value);
}
void PCA9685::setPWM(uint8_t pin, uint16_t on, uint16_t off)
{
}
void PCA9685::setAll(uint16_t value)
{
analogWrite(9, map(value, 0, 4096, 0, 255));
}
void PCA9685::setAll(uint16_t on, uint16_t off)
{
}
/*
void PCA9685::setAddress(uint8_t address, uint16_t sclFrequency, uint8_t pinSDA, uint8_t pinSCL, uint32_t timeoutUSec)
{
brzo_i2c_setup(pinSDA, pinSCL, timeoutUSec);
this->setAddress(address, sclFrequency);
}
void PCA9685::setAddress(uint8_t address, uint16_t sclFrequency)
{
this->address = address;
this->sclFrequency = sclFrequency;
}
uint8_t PCA9685::read(uint8_t registerAddress)
{
uint8_t result;
brzo_i2c_write(&registerAddress, 1, true);
brzo_i2c_read(&result, 1, false);
return result;
}
void PCA9685::write(uint8_t registerAddress, uint8_t value)
{
uint8_t buffer[2];
buffer[0] = registerAddress;
buffer[1] = value;
brzo_i2c_write(buffer, 2, false);
}
inline void PCA9685::write(uint8_t data)
{
brzo_i2c_write(&data, 1, false);
}
#define prescaleValue 25000000 / 4096
void PCA9685::setPWMFrequency(float frequency)
{
// Credit to the Adafruit PWM Servo Driver library for these calculations
// https://github.com/adafruit/Adafruit-PWM-Servo-Driver-Library/
uint8_t prescale = floor(prescaleValue / (frequency * 0.9) - 0.5);
brzo_i2c_start_transaction(address, sclFrequency);
// Sleep while changing the frequency
uint8_t oldMode = this->read(PCA9685::RegisterMode1);
uint8_t newMode = (oldMode & !PCA9685::Mode1Restart) | PCA9685::Mode1Sleep;
this->write(PCA9685::RegisterMode1, newMode);
this->write(PCA9685::RegisterPrescale, prescale);
this->write(PCA9685::RegisterMode1, oldMode);
// According to the datasheet:
// It takes 500 us max. for the oscillator to be up and running once
// SLEEP bit has been set to logic 0
//
// The Adafruit library uses 5 milliseconds, so I'll stick to that as well.
delay(5);
// Restart and turn on auto-increment (required for SetPWM)
this->write(PCA9685::RegisterMode1, oldMode | PCA9685::Mode1Restart | PCA9685::Mode1AI | PCA9685::Mode1AllCall);
brzo_i2c_end_transaction();
}
void PCA9685::setPWM(uint8_t pin, uint16_t value)
{
if (value < 0) value = 0;
if (value > 4095) value = 4095;
if (value == 4095)
this->setPWM(pin, 4096, 0);
else if (value == 0)
this->setPWM(pin, 0, 4096);
else
this->setPWM(pin, 0, value);
}
void PCA9685::setPWM(uint8_t pin, uint16_t on, uint16_t off)
{
brzo_i2c_start_transaction(address, sclFrequency);
this->write(PCA9685::RegisterLED0OnL + (4 * pin));
this->write(on);
this->write(on >> 8);
this->write(off);
this->write(off >> 8);
brzo_i2c_end_transaction();
}
void PCA9685::setAll(uint16_t value)
{
if (value < 0) value = 0;
if (value > 4095) value = 4095;
if (value == 4095)
this->setAll(4096, 0);
else if (value == 0)
this->setAll(0, 4096);
else
this->setAll(0, value);
}
void PCA9685::setAll(uint16_t on, uint16_t off)
{
brzo_i2c_start_transaction(address, sclFrequency);
this->write(PCA9685::RegisterAllLEDOnL);
this->write(on);
this->write(on >> 8);
this->write(off);
this->write(off >> 8);
brzo_i2c_end_transaction();
}
*/

53
src/components/PCA9685.h Normal file
View File

@ -0,0 +1,53 @@
#ifndef __PCA9685
#define __PCA9685
/*
* PCA9685 library for ESP8266
* Copyright 2017 (c) Mark van Renswoude
*
* Uses brzo I2C library.
*/
#include <stdint.h>
class PCA9685
{
private:
uint8_t address;
uint16_t sclFrequency;
protected:
uint8_t read(uint8_t registerAddress);
void write(uint8_t registerAddress, uint8_t value);
inline void write(uint8_t data);
public:
static const uint16_t Off = 0;
static const uint16_t On = 4096;
static const uint8_t RegisterMode1 = 0x0;
static const uint8_t RegisterPrescale = 0xFE;
static const uint8_t RegisterLED0OnL = 0x6;
static const uint8_t RegisterAllLEDOnL = 0xFA;
static const uint8_t Mode1Restart = 0x80;
static const uint8_t Mode1AI = 0x20;
static const uint8_t Mode1Sleep = 0x10;
static const uint8_t Mode1AllCall = 0x01;
// Call this to initializes the brzo I2C library
void setAddress(uint8_t address, uint16_t sclFrequency, uint8_t pinSDA, uint8_t pinSCL, uint32_t timeoutUSec);
// Call this if you already initialized the brzo I2C library
void setAddress(uint8_t address, uint16_t sclFrequency);
void setPWMFrequency(float frequency);
void setPWM(uint8_t pin, uint16_t value);
void setPWM(uint8_t pin, uint16_t on, uint16_t off);
void setAll(uint16_t value);
void setAll(uint16_t on, uint16_t off);
};
#endif

27
src/config.h Normal file
View File

@ -0,0 +1,27 @@
#ifndef __Config
#define __Config
#include <stdint.h>
#include "credentials.h"
// The number of steps (assumed to be <= 16, as the code currently only controls 1 PCA9685 board)
static const uint8_t StepCount = 14;
// The port number on which the UDP server listens
static const uint16_t UDPPort = 3126;
// Pins for the I2C bus
static const uint8_t PinSDA = 12;
static const uint8_t PinSCL = 13;
static const uint32_t I2CTimeout = 2000;
// I2C address and clock frequency of the PCA9685 board
static const uint8_t PWMDriverAddress = 0x60;
static const uint16_t PWMDriverSCLFrequency = 400;
static const uint16_t PWMDriverPWMFrequency = 1600;
#endif

View File

@ -0,0 +1,3 @@
// Create a copy of this file called "credentials.h"
static const char* WiFiSSID = "example";
static const char* WiFiPassword = "example";

184
src/main.cpp Normal file
View File

@ -0,0 +1,184 @@
/*
* Stairs lighting
* Copyright 2017 (c) Mark van Renswoude
*/
#include <stdint.h>
#include <Stream.h>
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
#include "config.h"
#include "protocol.h"
#include "components\PCA9685.h"
//#include "modes\adc.h"
#include "modes\alternate.h"
#include "modes\custom.h"
#include "modes\slide.h"
#include "modes\static.h"
#include "stairs.h"
PCA9685* pwmDriver;
Stairs* stairs;
WiFiUDP udpServer;
uint8_t currentModeIdentifier;
IMode* currentMode;
// Forward declarations
void checkRequest();
void handleRequest(uint8_t* packet);
IMode* createMode(uint8_t identifier);
void setCurrentMode(IMode *mode, uint8_t identifier);
void handleCurrentMode();
void setup()
{
pwmDriver = new PCA9685();
pwmDriver->setAddress(PWMDriverAddress, PWMDriverSCLFrequency, PinSDA, PinSCL, I2CTimeout);
pwmDriver->setPWMFrequency(PWMDriverPWMFrequency);
stairs = new Stairs();
stairs->init(pwmDriver);
WiFi.begin(WiFiSSID, WiFiPassword);
// Run a little startup test sequence
pwmDriver->setPWM(0, PCA9685::On);
delay(300);
for (int step = 1; step < StepCount; step++)
{
pwmDriver->setPWM(step - 1, PCA9685::Off);
pwmDriver->setPWM(step, PCA9685::On);
delay(300);
}
pwmDriver->setPWM(StepCount - 1, PCA9685::Off);
// Pulsate the bottom step while WiFi is connecting
uint16_t brightness = 0;
uint16_t speed = 16;
while (WiFi.status() != WL_CONNECTED)
{
brightness += speed;
if (brightness <= 0 || brightness >= 1024)
speed = -speed;
pwmDriver->setPWM(0, brightness);
delay(16);
}
setCurrentMode(new StaticMode(), Mode::Static);
// Start the UDP server
udpServer.begin(UDPPort);
}
uint32_t currentTime;
uint8_t packet[51];
uint8_t* packetRef;
void loop()
{
currentTime = millis();
checkRequest();
handleCurrentMode();
}
void checkRequest()
{
int packetSize = udpServer.parsePacket();
if (packetSize)
{
int length = udpServer.read(packet, 50);
if (length && packet[0])
{
packet[length] = 0;
packetRef = packet;
handleRequest(packetRef);
}
}
}
void handleRequest(uint8_t* packet)
{
// Every request will result in a reply, either containing the
// requested data or a copy of the input parameters for verification.
//
// Apparantly this also makes the ESP8266 more stable, as reports
// have been made that UDP communication can stall if no replies are sent.
udpServer.beginPacket(udpServer.remoteIP(), udpServer.remotePort());
udpServer.write(Command::Reply);
switch (*packet)
{
case Command::GetMode:
udpServer.write(currentModeIdentifier);
break;
case Command::SetMode:
{
packet++;
uint8_t newIdentifier = *packet;
packet++;
IMode* newMode = createMode(newIdentifier);
if (newMode != NULL)
{
newMode->read(packet);
newMode->write(&udpServer);
setCurrentMode(newMode, newIdentifier);
}
else
udpServer.write((uint8_t)0);
break;
}
}
udpServer.endPacket();
}
IMode* createMode(uint8_t identifier)
{
if (identifier == currentModeIdentifier)
return currentMode;
switch (identifier)
{
case Mode::Static: return new StaticMode();
//case Mode::Custom: return new CustomMode();
case Mode::Alternate: return new AlternateMode();
//case Mode::Slide: return new SlideMode();
//case Mode::ADC: return new ADCInputMode();
}
return NULL;
}
void setCurrentMode(IMode* mode, uint8_t identifier)
{
currentModeIdentifier = identifier;
currentMode = mode;
currentMode->init(stairs, currentTime);
}
void handleCurrentMode()
{
currentMode->tick(stairs, currentTime);
}

25
src/mode.h Normal file
View File

@ -0,0 +1,25 @@
#ifndef __Mode
#define __Mode
#include <stdint.h>
#include <Stream.h>
class IStairs
{
public:
virtual void setStep(uint8_t step, uint16_t value) = 0;
virtual void setAll(uint16_t value) = 0;
};
class IMode
{
public:
virtual void read(uint8_t* data) = 0;
virtual void write(Stream* stream) = 0;
virtual void init(IStairs* stairs, uint32_t currentTime) = 0;
virtual void tick(IStairs* stairs, uint32_t currentTime) = 0;
};
#endif

27
src/modes/alternate.cpp Normal file
View File

@ -0,0 +1,27 @@
#include "alternate.h"
#include <Stream.h>
void AlternateMode::init(IStairs* stairs, uint32_t currentTime)
{
stairs->setAll(0);
this->lastChange = currentTime;
this->even = false;
}
void AlternateMode::tick(IStairs* stairs, uint32_t currentTime)
{
if (currentTime - this->lastChange < this->parameters.interval)
return;
this->lastChange = currentTime;
this->even = !this->even;
uint8_t stepCount = stairs->getCount();
for (uint8_t step = 0; step < stepCount; step++)
{
stairs->set(step, ((step % 2) == 0) == this->even ? this->parameters.brightness : 0);
}
}

33
src/modes/alternate.h Normal file
View File

@ -0,0 +1,33 @@
#ifndef __AlternateMode
#define __AlternateMode
#include <stdint.h>
#include "base.h"
#include "../config.h"
struct AlternateModeParameters
{
uint16_t interval;
uint16_t brightness;
};
class AlternateMode : public BaseMode<AlternateModeParameters>
{
private:
uint32_t lastChange;
bool even = false;
public:
AlternateMode()
{
parameters.interval = 500;
parameters.brightness = 4096;
}
void init(IStairs* stairs, uint32_t currentTime);
void tick(IStairs* stairs, uint32_t currentTime);
};
#endif

27
src/modes/base.h Normal file
View File

@ -0,0 +1,27 @@
#ifndef __BaseMode
#define __BaseMode
#include <stdint.h>
#include <Stream.h>
#include "../mode.h"
template <class T>
class BaseMode : public IMode
{
protected:
T parameters;
public:
virtual void read(uint8_t* data)
{
this->parameters = *reinterpret_cast<T*>(data);
}
virtual void write(Stream* stream)
{
stream->write(reinterpret_cast<uint8_t*>(&this->parameters), sizeof(T));
}
};
#endif

14
src/modes/custom.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef __CustomMode
#define __CustomMode
#include <stdint.h>
#include "base.h"
#include "../config.h"
class CustomMode : public IMode
{
private:
uint16_t values[StepCount];
};
#endif

15
src/modes/slide.h Normal file
View File

@ -0,0 +1,15 @@
#ifndef __SlideMode
#define __SlideMode
#include <stdint.h>
#include "base.h"
#include "../config.h"
class SlideMode : public IMode
{
private:
uint16_t interval;
bool up;
};
#endif

11
src/modes/static.cpp Normal file
View File

@ -0,0 +1,11 @@
#include "static.h"
void StaticMode::init(IStairs* stairs, uint32_t currentTime)
{
stairs->setAll(this->parameters.brightness);
}
void StaticMode::tick(IStairs* stairs, uint32_t currentTime)
{
}

27
src/modes/static.h Normal file
View File

@ -0,0 +1,27 @@
#ifndef __StaticMode
#define __StaticMode
#include <stdint.h>
#include "base.h"
#include "../config.h"
struct StaticModeParameters
{
uint16_t brightness = 0;
};
class StaticMode : public BaseMode<StaticModeParameters>
{
public:
StaticMode()
{
parameters.brightness = 0;
}
void init(IStairs* stairs, uint32_t currentTime);
void tick(IStairs* stairs, uint32_t currentTime);
};
#endif

25
src/protocol.h Normal file
View File

@ -0,0 +1,25 @@
#ifndef __Protocol
#define __Protocol
#include <stdint.h>
class Command
{
public:
static const uint8_t Reply = 0x01;
static const uint8_t GetMode = 0x02;
static const uint8_t SetMode = 0x03;
};
class Mode
{
public:
static const uint8_t Static = 0x01;
static const uint8_t Custom = 0x02;
static const uint8_t Alternate = 0x03;
static const uint8_t Slide = 0x04;
//static const uint8_t ADC = 0x05;
};
#endif

25
src/stairs.cpp Normal file
View File

@ -0,0 +1,25 @@
#include "stairs.h"
#include "config.h"
void Stairs::init(PCA9685* pwmDriver)
{
this->pwmDriver = pwmDriver;
}
uint8_t Stairs::getCount()
{
return StepCount;
}
void Stairs::set(uint8_t step, uint16_t value)
{
pwmDriver->setPWM(step, value);
}
void Stairs::setAll(uint16_t value)
{
pwmDriver->setAll(value);
}

20
src/stairs.h Normal file
View File

@ -0,0 +1,20 @@
#ifndef __Stairs
#define __Stairs
#include "components/PCA9685.h"
#include "modes/base.h"
class Stairs : public IStairs
{
private:
PCA9685* pwmDriver;
public:
void init(PCA9685* pwmDriver);
uint8_t getCount();
void set(uint8_t step, uint16_t value);
void setAll(uint16_t value);
};
#endif

1
upload.bat Normal file
View File

@ -0,0 +1 @@
@platformio run --target upload