15 Commits
7 changed files with 327 additions and 287 deletions

No files matched your search

+15 -2
View File
@@ -54,14 +54,27 @@ else
server.streamFile(f, mime::getContentType(SRH::_path), requestMethod);
```
LED_sequence_v1:
### HW issues
- GPIO2 used as input -> must be high during startup
- PCB Solution: cut trace to GPIO2, solder bridge to GPIO16
- -> disadvantage: GPIO16 is not interrupt capable
### FSM 2 - to do
- stairway light switches off after timeout
- switches concurrently on again if sensor is still high
- desired behavior? Defective Sensor would cause continuos animation
- edge detection / interrupts as solution ?
### LED_sequence_v1:
TODO:
- disable led stripes after defined time (webserver paramter?)
- define what to do if people enter stairs from different directions
- CAUTION: Sensor-Deadtime at LEAST 8 seconds
- --> switch off with person leaving stairs might not be possible
Webserver on v0.3.0 now working with LittleFS
### Webserver on v0.3.0 now working with LittleFS
TODO:
- check dir for files automatic
- move handleNotFound to httpserver.h
+2 -2
View File
@@ -1,8 +1,8 @@
#pragma once
#include <ArduinoOTA.h>
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>
void ota_setup() {
ArduinoOTA.setPort(8266);
@@ -45,4 +45,4 @@ void ota_setup() {
});
ArduinoOTA.begin();
}
}
+244 -204
View File
@@ -1,242 +1,282 @@
#include "treppe.h"
uint8_t Treppe::softstart_led(uint8_t led, uint16_t startval, uint16_t stopval){
/*
softstart task
- get's called at regular intervals (1ms at the moment)
- dimms single led (0 - 15, PCA9685 outputs) with linear intervals vom startval to stopval
- calculates pwm steps depending on startval, stopval and timeinterval
- -> results in constanst speed
- returns 1 if led dimming is running
- returns 0 if led dimming is finished
*/
static uint8_t lastled = 255;
static float current_pwm = 0;
static float stepsize = 1.0;
if(led != lastled){
pwmController.setChannelPWM(led, (uint16_t)startval);
lastled = led;
current_pwm = startval;
stepsize = INT_TIME*abs(stopval - startval)/(float)time_per_stair; // only valid at 1ms function call interval
return 1;
}
if(startval > stopval){
current_pwm -= stepsize;
}
else {
current_pwm += stepsize;
}
// Serial.println((uint16_t)current_pwm);
pwmController.setChannelPWM(led, (uint16_t)current_pwm);
if(current_pwm > stopval - stepsize && current_pwm < stopval + stepsize){
if(stopval == 0) pwmController.setChannelPWM(led, 0);
return 0;
}
return 1;
}
void Treppe::ledsequence(){
static int8_t led = 0;
static uint16_t brightness = 0;
static uint16_t lastbrightness = 0;
static uint16_t status = 0;
uint16_t status_build = 0;
status_build |= direction << 8;
status_build |= state;
if(status_build != status){ // check if any parameter changed
finish = 0; // set state unfinished -> start action
if(direction) led = 0; // reset led counter depending of direction
else led = stairs-1;
if(state){
brightness = active_brightness; // set brightness value depending of on/off
lastbrightness = idle_brightness;
}
else{
brightness = idle_brightness;
lastbrightness = active_brightness;
}
status = status_build; // set parameter memory
Serial.printf("----Status Changed! onoff: %d, dir: %d\n", state, direction);
}
if(!finish){ // finish == 0 -> action pending
if(!softstart_led(led,lastbrightness, brightness)){
Serial.printf("one LED finished: led: %d, last: %d, curr %d\n",
led, lastbrightness, brightness);
if(direction){
led++;
if(led >= stairs)
finish = 1;
}
else{
led--;
if(led < 0)
finish = 1;
}
}
}
}
void Treppe::rampe()
/*
dimm_stufe
- dimmt stufe (0 - 15, PCA9685 outputs) mit linearen ticks
von idle bis active brightness
- return false solange gedimmt wird
- return true bei nächster stufe
*/
bool Treppe::dimm_stufe(uint8_t stufe)
{
if(state) {
finish = 0;
state = 0;// set parameter memory
}
if (fsm_outputs.dimmrichtung == DR_AUFDIMMEN)
current_pwm += differenz_pwm_pro_tick;
else
current_pwm -= differenz_pwm_pro_tick;
pwmController.setChannelPWM(stufe, static_cast<uint16_t>(current_pwm));
if(!finish) {
if(direction) { // aufwärts
if(tick >= ticks_treppe-1) { // ziel erreicht
Serial.println("[Treppe] oberster tick !");
finish = 1;
current_tick++;
if (current_tick >= ticks_pro_stufe)
return false;
return true;
}
/*
animation tick
- nach dem dimmen einer stufe wird die stufe weitergezählt
- abbruch am ende => anim_beendet = true;
*/
void Treppe::anim_tick()
{
if (!dimm_stufe(stufe))
{
Serial.printf("anim_tick(): stufe: %d, start: %d, ziel: %d, current %f\n",
stufe, start_pwm, ziel_pwm, current_pwm);
if (fsm_outputs.laufrichtung == LR_HOCH)
{
if (stufe >= stufen - 1)
{
anim_beendet = true;
return;
}
tick++; // eins hoch
stufe++;
}
else { // abwärts
if(tick <= 0) { // ziel erreicht
Serial.println("[Treppe] unterster tick !");
finish = 1;
else
{
if (stufe <= 0)
{
anim_beendet = true;
return;
}
tick--; // eins runter
stufe--;
}
stufe = tick / ticks_pro_stufe;
float new_pwm = 0.0;
if(an_aus) {
new_pwm = differenz_pwm_pro_tick * (tick - ticks_pro_stufe*stufe);
new_pwm += idle_brightness;
if(direction) new_pwm += differenz_pwm_pro_tick;
}
else {
new_pwm = active_brightness - differenz_pwm_pro_tick * (tick - ticks_pro_stufe*stufe);
new_pwm += idle_brightness;
if(direction) new_pwm -= differenz_pwm_pro_tick;
}
pwmController.setChannelPWM(stufe, (uint16_t) new_pwm);
Serial.printf("tick %04u, led %02d:%02u, pwm %4.1f\n",
tick,
stufe,
(tick - ticks_pro_stufe*stufe),
new_pwm
);
current_tick = 0;
current_pwm = start_pwm;
}
}
void Treppe::setup(){
// startbedingunen für animation
void Treppe::start_animation()
{
anim_beendet = false;
pwmController.resetDevices();
// Deactive PCA9685 Phase Balancer due to LED Flickering
// https://github.com/NachtRaveVL/PCA9685-Arduino/issues/15
// see also lib/PCA9685-Arduin/PCA9685.h:204
pwmController.init(PCA9685_PhaseBalancer_None);
//pwmController.init(PCA9685_PhaseBalancer_Linear);
pwmController.setPWMFrequency(100);
pwmController.setAllChannelsPWM(idle_brightness);
pinMode(A0, INPUT);
pinMode(SENSOR_OBEN, INPUT);
pinMode(SENSOR_UNTEN, INPUT);
pinMode(OE, OUTPUT);
digitalWrite(OE, 0);
if (fsm_outputs.laufrichtung == LR_HOCH)
stufe = 0;
else
stufe = stufen - 1;
Serial.printf("differenz_pwm_pro_tick %f\n", differenz_pwm_pro_tick);
Serial.println("Hello from Treppe");
Serial.print("Treppe: initial parameters: stairs=");
Serial.println(stairs);
if (fsm_outputs.dimmrichtung == DR_AUFDIMMEN)
{
start_pwm = idle_bright_internal;
ziel_pwm = active_brightness;
}
else
{
start_pwm = active_brightness;
ziel_pwm = idle_bright_internal;
}
current_tick = 0;
current_pwm = start_pwm;
}
void Treppe::print_state_on_change() {
void Treppe::print_state_on_change()
{
static FSMTreppeModelClass::ExtU_FSMTreppe_T last_in;
static FSMTreppeModelClass::ExtY_FSMTreppe_T last_out;
if(
fsm_inputs.anim_beendet != last_in.anim_beendet ||
fsm_inputs.sensor_oben != last_in.sensor_oben ||
fsm_inputs.sensor_unten != last_in.sensor_unten ||
fsm_outputs.dimmrichtung != last_out.dimmrichtung ||
fsm_outputs.laufrichtung != last_out.laufrichtung ||
fsm_outputs.status != last_out.status
) {
if (
fsm_inputs.anim_beendet != last_in.anim_beendet ||
fsm_inputs.sensor_oben != last_in.sensor_oben ||
fsm_inputs.sensor_unten != last_in.sensor_unten ||
fsm_outputs.dimmrichtung != last_out.dimmrichtung ||
fsm_outputs.laufrichtung != last_out.laufrichtung ||
fsm_outputs.status != last_out.status)
{
last_in.anim_beendet = fsm_inputs.anim_beendet;
last_in.sensor_oben = fsm_inputs.sensor_oben;
last_in.sensor_oben = fsm_inputs.sensor_oben;
last_in.sensor_unten = fsm_inputs.sensor_unten;
last_out.dimmrichtung = fsm_outputs.dimmrichtung;
last_out.laufrichtung = fsm_outputs.laufrichtung;
last_out.status = fsm_outputs.status;
Serial.printf("FSM IN: s_u: %d, s_o: %d, beendet: %d =>",
fsm_inputs.sensor_oben, fsm_inputs.sensor_unten, fsm_inputs.anim_beendet);
Serial.printf("FSM IN: s_u: %d, s_o: %d, beendet: %d =>",
fsm_inputs.sensor_oben, fsm_inputs.sensor_unten, fsm_inputs.anim_beendet);
Serial.print(" step => ");
Serial.printf("OUT: LR: %d DR: %d ST: %d\n",
fsm_outputs.laufrichtung, fsm_outputs.dimmrichtung, fsm_outputs.status);
Serial.printf("OUT: LR: %d DR: %d ST: %d\n",
fsm_outputs.laufrichtung, fsm_outputs.dimmrichtung, fsm_outputs.status);
}
}
void Treppe::task(){
//Serial.printf("LDR: %f\n", ((float)analogRead(A0))/1023.*3.68);
bool Treppe::read_sensor(int sensor)
{
if(finish){
direction = switch_direction;
state = switch_state;
/*
reads sensors with edge detection
returns true if motion was detected
returns false if no motion was detected
returns false if motion was detected, but state did not change back to not detected
*/
uint8_t pegel = digitalRead(sensor);
static uint8_t pegel_alt[2] = {0, 0};
uint8_t index = 0;
if (sensor == SENSOR_OBEN)
index = 0;
else
index = 1;
if (pegel == 1 && pegel_alt[index] == 0)
{
pegel_alt[index] = pegel;
return true;
}
else
{
pegel_alt[index] = pegel;
return false;
}
//return static_cast<bool>(pegel);
}
fsm_inputs.ldr_schwelle = true;
fsm_inputs.sensor_oben = read_sensor(SENSOR_OBEN);
float Treppe::read_ldr()
{
/*
Reads Illuminance in Lux
FUTURE USE : show current Illuminance on Webserver in order to calibrate
Voltage Divider 1 (R13, R14):
R13 = 220k, R14 = 82k
V(ADC) = V(in1) * R14/(R13+R14)
-> V(in1) = V(ADC) * (R13+R14)/R14
V(ADC) = analogRead(A0)/1023.00
-> V(in1) = analogRead(A0)/1023.00 * (R13+R14)/R14
= analogRead(A0) * (R13+R14)/(R14*1023.00)
= analogRead(A0) * (220k+82k)/(82k*1023.00)
= analogRead(A0) * 0.0036
Voltage Divider 2 (LDR, R1 || (R13+R14))
R1 = 47k, R13+R14 = 302k -> R1||(R13+R14) = 40,67k
Vcc/V(in1) = R(LDR) / (R1||(R13+R14))
-> R(LDR) = Vcc/V(in1) * (R1||(R13+R14))
R(LDR) = 3.3V * 40.67k / V(in1)
Join formulas:
R(LDR) = 3.3V * 40.67k / (0.0036 * analogRead(A0))
= 37280.00/analogRead(A0)
ldr_ohm = R(LDR)
E(LDR) = 79.735 * R(LDR)^-0.498 (see Excel Regression)
ldr_value = E(LDR)
*/
float ldr_ohm = 37280.00 / analogRead(A0);
float ldr_value = 79.735 * pow(ldr_ohm, -0.498);
return ldr_value;
}
bool Treppe::check_ldr()
{
static uint8_t active = 0;
#ifdef LDRDEBUG
Serial.printf("R(LDR) = %f kOhm %f lux\n", ldr_value, lux);
return true;
#endif
// follow up: averaging over many samples?
float ldr = read_ldr();
if (ldr < ldr_schwelle)
active = 1;
if (ldr > ldr_schwelle + LDR_HYS)
active = 0;
activate_idle_pwm(active);
return active;
}
void Treppe::task()
{
fsm_inputs.ldr_schwelle = check_ldr();
fsm_inputs.sensor_oben = read_sensor(SENSOR_OBEN);
fsm_inputs.sensor_unten = read_sensor(SENSOR_UNTEN);
fsm_inputs.anim_beendet = static_cast<bool>(finish);
fsm_inputs.anim_beendet = static_cast<bool>(anim_beendet);
FSMTreppe_Obj.setExternalInputs(&fsm_inputs);
FSMTreppe_Obj.step();
fsm_outputs = FSMTreppe_Obj.getExternalOutputs();
print_state_on_change();
direction = fsm_outputs.laufrichtung;
state = fsm_outputs.dimmrichtung;
// setTick(ticks_treppe);
// setAnAus(1);
// setDirection(0);
// setState(0);
ledsequence();
}
uint16_t Treppe::setIdle(uint16_t _idle_brightness){
idle_brightness = _idle_brightness;
Serial.println("Treppe: idle brightness changed!");
return idle_brightness;
}
uint16_t Treppe::setActive(uint16_t _active_brightness){
active_brightness = _active_brightness;
Serial.println("Treppe: active brightness changed!");
return active_brightness;
}
uint16_t Treppe::setTime(uint16_t _time_per_stair){
time_per_stair = _time_per_stair;
Serial.println("Treppe: time changed!");
return time_per_stair;
}
void Treppe::setDirection(uint8_t _direction){
switch_direction = _direction;
Serial.printf("Treppe: switch_direction=%d!\n", switch_direction);
if(finish) Serial.println("apply direction request immediately");
else Serial.println("currently active, dir change afterwards");
// to do: implement state command variable to determine dimm-state
}
void Treppe::setState(uint8_t _state){
if(state == _state) return;
else {
switch_state = _state;
Serial.printf("Treppe: switch_state=%d!\n", switch_state);
if(finish) Serial.println("apply state request immediately");
else Serial.println("currently active, state changes after activity");
if (fsm_outputs.status > ST_RUHEZUSTAND)
{
if (anim_beendet == true &&
(fsm_outputs.status == ST_AUFDIMMEN_HOCH || fsm_outputs.status == ST_ABDIMMEN_HOCH ||
fsm_outputs.status == ST_AUFDIMMEN_RUNTER || fsm_outputs.status == ST_ABDIMMEN_RUNTER))
{
start_animation();
}
if (!anim_beendet)
anim_tick();
}
}
void Treppe::berechne_dimmer()
{
ticks_pro_stufe = time_per_stair / INT_TIME; // [ms]
differenz_pwm_pro_tick = (float)(active_brightness - idle_brightness) / (float)ticks_pro_stufe;
}
void Treppe::setup()
{
pwmController.resetDevices();
// Deactive PCA9685 Phase Balancer due to LED Flickering
// https://github.com/NachtRaveVL/PCA9685-Arduino/issues/15
// see also lib/PCA9685-Arduin/PCA9685.h:204
pwmController.init(PCA9685_PhaseBalancer_None);
//pwmController.init(PCA9685_PhaseBalancer_Linear);
pwmController.setPWMFrequency(100);
pwmController.setAllChannelsPWM(idle_brightness);
pinMode(A0, INPUT);
pinMode(SENSOR_OBEN, INPUT);
pinMode(SENSOR_UNTEN, INPUT);
pinMode(OE, OUTPUT);
digitalWrite(OE, 0);
Serial.printf("differenz_pwm_pro_tick %f\n", differenz_pwm_pro_tick);
Serial.printf("Treppe: initial parameters: stufen=%d\n", stufen);
}
// ###################################################################################################################
// GEBUFFERT => Erst im Ruhezustand übernehmen !!!!
void Treppe::set_idle_pwm(uint16_t _idle_brightness)
{
idle_brightness = _idle_brightness;
berechne_dimmer();
Serial.printf("Treppe: idle_brightness=%d\n", idle_brightness);
}
void Treppe::activate_idle_pwm(bool active){
static uint8_t former_active = 0;
if(active != former_active && fsm_outputs.status == ST_RUHEZUSTAND){
idle_bright_internal = idle_brightness * active;
// Dimming Function for all LEDS ?
pwmController.setAllChannelsPWM(idle_bright_internal);
former_active = active;
}
}
void Treppe::set_active_pwm(uint16_t _active_brightness)
{
active_brightness = _active_brightness;
berechne_dimmer();
Serial.printf("Treppe: active_brightness=%d\n", active_brightness);
}
void Treppe::set_time_per_stair(uint16_t _time_per_stair)
{
time_per_stair = _time_per_stair;
berechne_dimmer();
Serial.printf("Treppe: time_per_stair=%d\n", time_per_stair);
}
+54 -63
View File
@@ -1,9 +1,13 @@
#pragma once
#include "FSMTreppe/FSMTreppe2.h"
#include "FSMTreppe2/FSMTreppe2.h"
#include "PCA9685.h"
#define SENSOR_OBEN 2
// #define LDRDEBUG // comment in to override LDR measurement
#define LDR_HYS 5.0 // Hysteresis for switching off FSM [lux]
#define SENSOR_OBEN 16
#define SENSOR_UNTEN 12
#define OE 14
@@ -11,58 +15,63 @@
class Treppe {
private:
uint8_t stairs;
uint16_t time_per_stair = 300; // dimmtime per stair [ms]
const uint8_t stufen;
uint16_t time_per_stair = 300; // dimmtime per stair [ms]
uint16_t idle_brightness = 100;
uint16_t active_brightness = 300;
uint16_t idle_bright_internal = 0;
uint16_t active_brightness = 500;
uint8_t direction = 0;
uint8_t switch_direction = 0;
uint8_t state = 0;
uint8_t switch_state = 0;
bool finish = 1;
uint16_t ldr_schwelle = 7; // activation value for FSM [lx]
// alternative
uint32_t tick = 0;
uint32_t stufe = 0;
uint8_t an_aus = 0;
uint32_t ticks_treppe = 0;
uint32_t ticks_pro_stufe = 0;
uint16_t start_pwm = 0;
uint16_t ziel_pwm = 0;
bool anim_beendet = true;
uint8_t stufe = 0;
uint16_t current_tick = 0;
float current_pwm = 0.0;
uint16_t ticks_pro_stufe = 0;
float differenz_pwm_pro_tick = 0.0;
// alternative
// initialize with i2c-Address 0, use Wire Library
PCA9685 pwmController;
FSMTreppeModelClass FSMTreppe_Obj;
FSMTreppeModelClass::ExtU_FSMTreppe_T fsm_inputs;
FSMTreppeModelClass::ExtY_FSMTreppe_T fsm_outputs;
enum fsm_status_t {
ST_INAKTIV_LDR =0,
ST_RUHEZUSTAND =1,
ST_AUFDIMMEN_HOCH =2,
ST_WARTEN_HOCH =3,
ST_ABDIMMEN_HOCH =4,
ST_AUFDIMMEN_RUNTER =5,
ST_WARTEN_RUNTER =6,
ST_ABDIMMEN_RUNTER =7
};
enum fsm_laufrichtung_t {
LR_RUNTER=0,
LR_HOCH=1
};
enum fsm_dimmrichtung_t {
DR_ABDIMMEN=0,
DR_AUFDIMMEN=1
};
/* DIMM */
bool dimm_stufe(uint8_t stufe);
void anim_tick();
void start_animation();
void berechne_dimmer();
void print_state_on_change();
const uint8_t FSMTreppe_IN_animation_down = 1U;
const uint8_t FSMTreppe_IN_animation_up = 2U;
const uint8_t FSMTreppe_IN_idle = 3U;
uint8_t softstart_led(uint8_t led, uint16_t startval, uint16_t stopval);
void ledsequence();
void rampe();
bool read_sensor(int sensor) {
int pegel = digitalRead(sensor);
return static_cast<bool>(pegel);
}
/* LDR */
bool read_sensor(int sensor);
float read_ldr();
bool check_ldr();
public:
Treppe(uint8_t _stairs) : stairs(_stairs){
Treppe(uint8_t _stufen) : stufen(_stufen){
FSMTreppe_Obj.initialize();
ticks_pro_stufe = time_per_stair / 20; // [ms]
ticks_treppe = ticks_pro_stufe * stairs;
differenz_pwm_pro_tick = (float) (active_brightness - idle_brightness)
/ (float) ticks_pro_stufe;
berechne_dimmer();
}
~Treppe() {
FSMTreppe_Obj.terminate();
@@ -72,26 +81,8 @@ public:
void task(); // call periodically
// Parameter section
uint16_t setIdle(uint16_t _idle_brightness);
uint16_t setActive(uint16_t _active_brightness);
uint16_t setTime(uint16_t _time_per_stair);
void setTick(uint32_t _tick) {
tick = _tick;
Serial.printf("Treppe: Tick: %u!\n", tick);
}
uint32_t getTicks() {
return ticks_treppe;
}
// Runtime Parameter section
void setDirection(uint8_t _direction);
void setState(uint8_t _state);
void setAnAus(uint8_t _an_aus) {
an_aus = _an_aus;
}
uint8_t getState() { return state;};
uint8_t getFinished() { return finish;};
uint8_t getDirection() {return direction;};
void set_idle_pwm(uint16_t _idle_brightness);
void activate_idle_pwm(bool active);
void set_active_pwm(uint16_t _active_brightness);
void set_time_per_stair(uint16_t _time_per_stair);
};
Binary file not shown.
-14
View File
@@ -94,23 +94,9 @@ void setup() {
os_timer_arm(&timer1, 20, true);
}
#include <random>
uint32_t c = 0;
void loop() {
// if(stairs.getState() == 0) {
// delay(1000);
// // uint32_t t = rand() % stairs.getTicks();
// // uint32_t d = rand() % 2;
// // stairs.setTick(t);
// // stairs.setDirection(d);
// stairs.setDirection(!stairs.getDirection());
// stairs.setState(1);
// }
if(inter){
Serial.printf("interrupt\n");
inter = 0;
+12 -2
View File
@@ -16,13 +16,23 @@ data_dir = data_gz
[env:hardware]
platform = espressif8266
board = nodemcuv2
; board = nodemcuv2
board = huzzah
framework = arduino
; for pio check
; check_tool = cppcheck
check_tool = clangtidy
check_flags =
; clangtidy: --fix-errors
clangtidy: lib/httpserver/* lib/treppe/* lib/PCA9685-Arduino/*
check_skip_packages = yes
board_build.filesystem = littlefs
board_build.ldscript = eagle.flash.4m1m.ld
extra_scripts = pre:create_gz_files.py
monitor_speed = 115200
monitor_speed = 76800
[env:serial]
extends = env:hardware