15 Commits
7 changed files with 323 additions and 283 deletions

No files matched your search

+15 -2
View File
@@ -54,14 +54,27 @@ else
server.streamFile(f, mime::getContentType(SRH::_path), requestMethod); 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: TODO:
- disable led stripes after defined time (webserver paramter?) - disable led stripes after defined time (webserver paramter?)
- define what to do if people enter stairs from different directions - define what to do if people enter stairs from different directions
- CAUTION: Sensor-Deadtime at LEAST 8 seconds - CAUTION: Sensor-Deadtime at LEAST 8 seconds
- --> switch off with person leaving stairs might not be possible - --> 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: TODO:
- check dir for files automatic - check dir for files automatic
- move handleNotFound to httpserver.h - move handleNotFound to httpserver.h
+1 -1
View File
@@ -1,8 +1,8 @@
#pragma once #pragma once
#include <ArduinoOTA.h>
#include <ESP8266WiFi.h> #include <ESP8266WiFi.h>
#include <WiFiUdp.h> #include <WiFiUdp.h>
#include <ArduinoOTA.h>
void ota_setup() { void ota_setup() {
ArduinoOTA.setPort(8266); ArduinoOTA.setPort(8266);
+244 -204
View File
@@ -1,138 +1,235 @@
#include "treppe.h" #include "treppe.h"
uint8_t Treppe::softstart_led(uint8_t led, uint16_t startval, uint16_t stopval){ /*
/* dimm_stufe
softstart task - dimmt stufe (0 - 15, PCA9685 outputs) mit linearen ticks
von idle bis active brightness
- get's called at regular intervals (1ms at the moment) - return false solange gedimmt wird
- dimms single led (0 - 15, PCA9685 outputs) with linear intervals vom startval to stopval - return true bei nächster stufe
- calculates pwm steps depending on startval, stopval and timeinterval */
- -> results in constanst speed bool Treppe::dimm_stufe(uint8_t stufe)
- 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()
{ {
if(state) { if (fsm_outputs.dimmrichtung == DR_AUFDIMMEN)
finish = 0; current_pwm += differenz_pwm_pro_tick;
state = 0;// set parameter memory else
} current_pwm -= differenz_pwm_pro_tick;
pwmController.setChannelPWM(stufe, static_cast<uint16_t>(current_pwm));
if(!finish) { current_tick++;
if(direction) { // aufwärts if (current_tick >= ticks_pro_stufe)
if(tick >= ticks_treppe-1) { // ziel erreicht return false;
Serial.println("[Treppe] oberster tick !"); return true;
finish = 1; }
/*
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; return;
} }
tick++; // eins hoch stufe++;
} }
else { // abwärts else
if(tick <= 0) { // ziel erreicht {
Serial.println("[Treppe] unterster tick !"); if (stufe <= 0)
finish = 1; {
anim_beendet = true;
return; return;
} }
tick--; // eins runter stufe--;
} }
current_tick = 0;
stufe = tick / ticks_pro_stufe; current_pwm = start_pwm;
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
);
} }
} }
void Treppe::setup(){ // startbedingunen für animation
void Treppe::start_animation()
{
anim_beendet = false;
if (fsm_outputs.laufrichtung == LR_HOCH)
stufe = 0;
else
stufe = stufen - 1;
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()
{
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)
{
last_in.anim_beendet = fsm_inputs.anim_beendet;
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.print(" step => ");
Serial.printf("OUT: LR: %d DR: %d ST: %d\n",
fsm_outputs.laufrichtung, fsm_outputs.dimmrichtung, fsm_outputs.status);
}
}
bool Treppe::read_sensor(int sensor)
{
/*
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);
}
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>(anim_beendet);
FSMTreppe_Obj.setExternalInputs(&fsm_inputs);
FSMTreppe_Obj.step();
fsm_outputs = FSMTreppe_Obj.getExternalOutputs();
print_state_on_change();
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(); pwmController.resetDevices();
// Deactive PCA9685 Phase Balancer due to LED Flickering // Deactive PCA9685 Phase Balancer due to LED Flickering
// https://github.com/NachtRaveVL/PCA9685-Arduino/issues/15 // https://github.com/NachtRaveVL/PCA9685-Arduino/issues/15
@@ -149,94 +246,37 @@ void Treppe::setup(){
digitalWrite(OE, 0); digitalWrite(OE, 0);
Serial.printf("differenz_pwm_pro_tick %f\n", differenz_pwm_pro_tick); Serial.printf("differenz_pwm_pro_tick %f\n", differenz_pwm_pro_tick);
Serial.println("Hello from Treppe"); Serial.printf("Treppe: initial parameters: stufen=%d\n", stufen);
Serial.print("Treppe: initial parameters: stairs=");
Serial.println(stairs);
} }
void Treppe::print_state_on_change() { // ###################################################################################################################
static FSMTreppeModelClass::ExtU_FSMTreppe_T last_in; // GEBUFFERT => Erst im Ruhezustand übernehmen !!!!
static FSMTreppeModelClass::ExtY_FSMTreppe_T last_out; void Treppe::set_idle_pwm(uint16_t _idle_brightness)
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_unten = fsm_inputs.sensor_unten;
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);
}
}
void Treppe::task(){
//Serial.printf("LDR: %f\n", ((float)analogRead(A0))/1023.*3.68);
if(finish){
direction = switch_direction;
state = switch_state;
}
fsm_inputs.ldr_schwelle = true;
fsm_inputs.sensor_oben = read_sensor(SENSOR_OBEN);
fsm_inputs.sensor_unten = read_sensor(SENSOR_UNTEN);
fsm_inputs.anim_beendet = static_cast<bool>(finish);
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; idle_brightness = _idle_brightness;
Serial.println("Treppe: idle brightness changed!"); berechne_dimmer();
return idle_brightness; Serial.printf("Treppe: idle_brightness=%d\n", 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){ void Treppe::activate_idle_pwm(bool active){
switch_direction = _direction; static uint8_t former_active = 0;
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(active != former_active && fsm_outputs.status == ST_RUHEZUSTAND){
if(state == _state) return; idle_bright_internal = idle_brightness * active;
else { // Dimming Function for all LEDS ?
switch_state = _state; pwmController.setAllChannelsPWM(idle_bright_internal);
Serial.printf("Treppe: switch_state=%d!\n", switch_state); former_active = active;
if(finish) Serial.println("apply state request immediately");
else Serial.println("currently active, state changes after activity");
} }
} }
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);
}
+51 -60
View File
@@ -1,9 +1,13 @@
#pragma once #pragma once
#include "FSMTreppe/FSMTreppe2.h" #include "FSMTreppe2/FSMTreppe2.h"
#include "PCA9685.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 SENSOR_UNTEN 12
#define OE 14 #define OE 14
@@ -11,58 +15,63 @@
class Treppe { class Treppe {
private: private:
const uint8_t stufen;
uint8_t stairs;
uint16_t time_per_stair = 300; // dimmtime per stair [ms] uint16_t time_per_stair = 300; // dimmtime per stair [ms]
uint16_t idle_brightness = 100; 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; uint16_t ldr_schwelle = 7; // activation value for FSM [lx]
uint8_t switch_direction = 0;
uint8_t state = 0;
uint8_t switch_state = 0;
bool finish = 1;
// alternative uint16_t start_pwm = 0;
uint32_t tick = 0; uint16_t ziel_pwm = 0;
uint32_t stufe = 0;
uint8_t an_aus = 0;
uint32_t ticks_treppe = 0; bool anim_beendet = true;
uint32_t ticks_pro_stufe = 0; 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; float differenz_pwm_pro_tick = 0.0;
// alternative
// initialize with i2c-Address 0, use Wire Library // initialize with i2c-Address 0, use Wire Library
PCA9685 pwmController; PCA9685 pwmController;
FSMTreppeModelClass FSMTreppe_Obj; FSMTreppeModelClass FSMTreppe_Obj;
FSMTreppeModelClass::ExtU_FSMTreppe_T fsm_inputs; FSMTreppeModelClass::ExtU_FSMTreppe_T fsm_inputs;
FSMTreppeModelClass::ExtY_FSMTreppe_T fsm_outputs; 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(); void print_state_on_change();
const uint8_t FSMTreppe_IN_animation_down = 1U; /* LDR */
const uint8_t FSMTreppe_IN_animation_up = 2U; bool read_sensor(int sensor);
const uint8_t FSMTreppe_IN_idle = 3U; float read_ldr();
bool check_ldr();
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);
}
public: public:
Treppe(uint8_t _stairs) : stairs(_stairs){ Treppe(uint8_t _stufen) : stufen(_stufen){
FSMTreppe_Obj.initialize(); FSMTreppe_Obj.initialize();
berechne_dimmer();
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;
} }
~Treppe() { ~Treppe() {
FSMTreppe_Obj.terminate(); FSMTreppe_Obj.terminate();
@@ -72,26 +81,8 @@ public:
void task(); // call periodically void task(); // call periodically
// Parameter section // Parameter section
uint16_t setIdle(uint16_t _idle_brightness); void set_idle_pwm(uint16_t _idle_brightness);
uint16_t setActive(uint16_t _active_brightness); void activate_idle_pwm(bool active);
uint16_t setTime(uint16_t _time_per_stair); void set_active_pwm(uint16_t _active_brightness);
void set_time_per_stair(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;};
}; };
Binary file not shown.
-14
View File
@@ -94,23 +94,9 @@ void setup() {
os_timer_arm(&timer1, 20, true); os_timer_arm(&timer1, 20, true);
} }
#include <random>
uint32_t c = 0; uint32_t c = 0;
void loop() { 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){ if(inter){
Serial.printf("interrupt\n"); Serial.printf("interrupt\n");
inter = 0; inter = 0;
+12 -2
View File
@@ -16,13 +16,23 @@ data_dir = data_gz
[env:hardware] [env:hardware]
platform = espressif8266 platform = espressif8266
board = nodemcuv2 ; board = nodemcuv2
board = huzzah
framework = arduino 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.filesystem = littlefs
board_build.ldscript = eagle.flash.4m1m.ld board_build.ldscript = eagle.flash.4m1m.ld
extra_scripts = pre:create_gz_files.py extra_scripts = pre:create_gz_files.py
monitor_speed = 115200 monitor_speed = 76800
[env:serial] [env:serial]
extends = env:hardware extends = env:hardware