This commit is contained in:
Dominik Bartsch 2021-07-25 17:05:20 +02:00
commit 0d6f0b28e2
6 changed files with 195 additions and 138 deletions

View File

@ -1,6 +1,7 @@
<!DOCTYPE html>
<html>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<head>
<title>ESP8266 Treppenlicht</title>
<link href="/favicon.png" rel="icon" type="image/png" sizes="10x10">
@ -11,18 +12,23 @@
<div class="topbar">Treppenlicht</div>
<div class="param_block">
Active Brightness: <output id="out_act_pwm" class="val_range">50</output> %
<div class="slider">
<input type="range" class="regler" id="range_act_pwm" min="0" max="100" value="50">
</div>
<input type="button" class="control" data-action="s_oben" value="sensor_oben">
<input type="button" class="control" data-action="s_unten" value="sensor_unten">
<input type="button" class="control" data-action="on_off" value="on_off">
</div>
<div class="param_block">
Active Brightness: <output id="out_act_pwm" class="val_range">50</output> %
<div class="slider">
<input type="range" class="regler" id="range_act_pwm" data-output="out_act_pwm" min="0" max="100" value="50">
</div>
</div>
<div class="param_block">
Idle Brightness Maximum: <output id="out_idl_pwm" class="val_range">50</output> %
<label id="note">[100% == Active Brightness]</label>
<div class="slider">
<input type="range" class="regler" id="range_idl_pwm" min="0" max="100" value="50">
<input type="range" class="regler" id="range_idl_pwm" data-output="out_idl_pwm" min="0" max="100" value="50">
</div>
<label id="note">idle brightness gets controlled via LDR measurments</label>
</div>
@ -31,27 +37,27 @@
Time per stair [ms]: <output id="out_tim_sta" class="val_range">300</output> ms
<div class="slider">
<input type="range" class="regler" id="range_tim_sta" min="0" max="5000" value="300">
<input type="range" class="regler" id="range_tim_sta" data-output="out_tim_sta" min="0" max="5000" value="300">
</div>
</div>
<div class="param_block">
Time LDR [ms]: <output id="out_tim_ldr" class="val_range">500</output> ms
<div class="slider">
<input type="range" class="regler" id="range_tim_ldr" min="0" max="5000" value="500">
<input type="range" class="regler" id="range_tim_ldr" data-output="out_tim_ldr" min="0" max="5000" value="500">
</div>
</div>
<div class="param_block">
LDR Schwelle [%]: <output id="out_ldr_shw" class="val_range">50</output> %
<div class="slider">
<input type="range" class="regler" id="range_ldr_shw" min="0" max="100" value="50">
<input type="range" class="regler" id="range_ldr_shw" data-output="out_ldr_shw" min="0" max="100" value="50">
</div>
</div>
<div class="terminal">
<input type="button" id="clear_term" value="clear" onclick="clearTerminal();">
<input type="button" id="clear_term" value="clear">
<input type="checkbox" id="scroll" name="scroll" value="scroll" checked>
<label for="scroll"> autoscroll </label>
<textarea id="term">waiting for log messages ...&#10;</textarea>

View File

@ -4,10 +4,10 @@ let xhrUpd = new XMLHttpRequest();
xhrUpd.onreadystatechange = function () {
if (xhrUpd.readyState == 4) {
if (xhrUpd.status == 200) {
console.log(xhrUpd.responseText);
console.log("xhrUpd: ", xhrUpd.responseText);
}
else {
console.log("status:", xhrUpd.status);
console.log("xhrUpd: status=", xhrUpd.status);
}
}
}
@ -28,52 +28,61 @@ function reloadRangeValues() {
rangeValues = {};
}
setTimeout(reloadRangeValues, 1000);
};
reloadRangeValues();
function sendRangeValue(range, val) {
rangeValues[range] = val;
console.log(rangeValues);
};
// connect actions to range inputs
function rangeToOutput(range, output) {
document.getElementById(range).oninput = function() {
document.getElementById(output).innerHTML = this.value;
sendRangeValue(this.id, this.value);
}
}
function reloadTerminal() {
const terminal = document.querySelector("#term");
const autoscroll = document.querySelector("#scroll");
rangeToOutput("range_act_pwm", "out_act_pwm");
rangeToOutput("range_idl_pwm", "out_idl_pwm");
rangeToOutput("range_tim_sta", "out_tim_sta");
rangeToOutput("range_tim_ldr", "out_tim_ldr");
rangeToOutput("range_ldr_shw", "out_ldr_shw");
let xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
if(xhr.readyState == 4) {
if (xhr.status == 200){
console.log(xhr.responseText);
terminal = document.getElementById("term");
autoscroll = document.getElementById("scroll");
terminal.innerHTML += xhr.responseText;
fetch(`/terminal`, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: ''
})
.then(response => response.text())
.then(data => {
if(data.length > 0) {
terminal.innerHTML += data;
if (autoscroll.checked)
terminal.scrollTop = terminal.scrollHeight;
}
else {
console.log("status:", xhr.status);
}
}
}
function reloadTerminal() {
xhr.open("POST", "/terminal", true);
xhr.send();
setTimeout(reloadTerminal, 1000);
})
.catch(error => console.log('Error:', error));
};
reloadTerminal();
function clearTerminal() {
document.getElementById("term").innerHTML = '';
document.addEventListener('DOMContentLoaded', () => {
setInterval(reloadTerminal, 1000);
setInterval(reloadRangeValues, 1000);
// use data- attributes for action
document.querySelectorAll('.control').forEach((button) => {
button.onclick = () => {
fetch(`/action`, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: `control=${button.dataset.action}`
})
.then(response => console.log(response))
.catch(error => console.log('Error:', error));
}
});
document.querySelectorAll('.regler').forEach((regler) => {
regler.oninput = () => {
document.querySelector(`#${regler.dataset.output}`).innerHTML = regler.value;
rangeValues[regler.id] = regler.value;
}
});
document.querySelector('#clear_term').onclick = () => {
document.querySelector("#term").innerHTML = '';
};
});

View File

@ -113,8 +113,10 @@ linker puts them together ?!?
- testing if dimming crashes when two animations
-> ldr does not interrupt animation, animation get's finished and stairs fade out -> FSM works without collisions
- interrupt to pending from sensors
-> rising edge trigger implemented, via pending input
- settings struct
-> implemented with EEPROM
-> adapted to only change on ruhezustand
- script for gdb on windows
-> [implemented](start_xtensa_gdb_stub.cmd)
- welcome animation ?
@ -131,3 +133,9 @@ linker puts them together ?!?
- behavior when someone enters stairway from opposite direction after animation finished but person 1 is still on stairway?
-> currently stairs fade off from direction 1
-> additional wait-state between "warten hoch/runter" and "abdimmen hoch/runter" after sensor detected person?
- active brightness can be lower than idle in the moment
- change idle to precentage of active !!
- Webpage needs to load settings from eeprom !!

View File

@ -31,17 +31,33 @@ auto HTTPServer::start() -> bool {
// add static root file handler for http
this->serveStatic("/", LittleFS, "/");
this->begin();
Serial.printf("Server active on Port 80 !\n\r");
logf("Server active on Port 80 !\n\r");
return true;
}
void HTTPServer::start_apps() {
// application handler
this->on("/action", HTTP_POST, [this]() {
if (args()) {
logf("%s=%s\n", argName(0).c_str(), arg(0).c_str());
if (argName(0).equals("control")) {
if (arg(0).equals("s_oben")) {
treppe->overwrite_sensors(true, false);
logt("control => s_oben !\n");
}
else if (arg(0).equals("s_unten")) {
treppe->overwrite_sensors(false, true);
logt("control => s_unten !\n");
}
}
}
send(200, "text/plain", "accepted");
});
this->on("/update", HTTP_POST, [this]() {
if (args()) {
for (int i = 0; i < args() - 1; i++) {
Serial.printf("%s=%s\n", argName(i).c_str(), arg(i).c_str());
logf("%s=%s\n", argName(i).c_str(), arg(i).c_str());
if (argName(i).equals("range_act_pwm")) {
treppe->set_active_pwm(arg(i).toInt(), VORGABE_PROZENT);
@ -70,7 +86,7 @@ void HTTPServer::start_apps() {
});
this->on("/terminal", HTTP_POST, [this]() {
// Serial.printf("got /terminal\n");
// logf("got /terminal\n");
if (tbuf_head) {
send(200, "text/plain", tbuf);
tbuf_head = 0;

View File

@ -27,8 +27,7 @@ bool Treppe::dimmer_tick(dimmer_t *dimmer, bool dim_type) {
Serial.printf("DIM_LDR: start: %d, ziel: %d\n", dimmer->start_pwm,
dimmer->ziel_pwm);
return true;
} else // DIM_STUFEN
{
} else { // DIM_STUFEN
Serial.printf("DIM_STUFEN: stufe: %d, start: %d, ziel: %d\n",
dimmer->stufe, dimmer->start_pwm, dimmer->ziel_pwm);
@ -36,8 +35,7 @@ bool Treppe::dimmer_tick(dimmer_t *dimmer, bool dim_type) {
if (dimmer->stufe >= stufen - 1)
return true;
dimmer->stufe++;
} else // LR_RUNTER
{
} else { // LR_RUNTER
if (dimmer->stufe <= 0)
return true;
dimmer->stufe--;
@ -59,9 +57,9 @@ void Treppe::start_animation(dimmer_t *dimmer, bool dim_type, uint16_t on_pwm,
else
dimmer->stufe = stufen - 1;
dimmer->ticks = parameters.time_per_stair / INT_TIME; // [ms]
dimmer->ticks = param.time_per_stair / INT_TIME; // [ms]
} else { // DIM_LDR
dimmer->ticks = parameters.time_ldr / INT_TIME; // [ms]
dimmer->ticks = param.time_ldr / INT_TIME; // [ms]
}
if (fsm_outputs.dimmrichtung == DR_AUFDIMMEN) {
@ -108,33 +106,38 @@ void Treppe::print_state_on_change() {
}
}
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;
void Treppe::overwrite_sensors(bool s_oben, bool s_unten) {
fsm_pend.web_ctrl_s_oben = s_oben;
fsm_pend.web_ctrl_s_unten = s_unten;
}
void Treppe::read_sensors() {
const bool s_oben = digitalRead(SENSOR_OBEN);
const bool s_unten = digitalRead(SENSOR_UNTEN);
fsm_pend.sensor_oben = false;
fsm_pend.sensor_unten = false;
// rising trigger => 1 cycle true !
if (s_oben && !fsm_pend.last_s_oben) {
fsm_pend.sensor_oben = true;
}
if (s_unten && !fsm_pend.last_s_unten) {
fsm_pend.sensor_unten = true;
}
fsm_pend.last_s_oben = s_oben;
fsm_pend.last_s_unten = s_unten;
// check for manipulation over webserver
if (fsm_pend.web_ctrl_s_oben) {
fsm_pend.sensor_oben = true;
fsm_pend.web_ctrl_s_oben = false;
}
if (fsm_pend.web_ctrl_s_unten) {
fsm_pend.sensor_unten = true;
fsm_pend.web_ctrl_s_unten = false;
}
// return static_cast<bool>(pegel);
}
float Treppe::read_ldr() {
@ -190,10 +193,10 @@ bool Treppe::check_ldr() {
// follow up: averaging over many samples?
float ldr = read_ldr();
if (ldr < parameters.ldr_schwelle) {
if (ldr < param.ldr_schwelle) {
active = 1;
}
if (ldr > parameters.ldr_schwelle + LDR_HYS) {
if (ldr > param.ldr_schwelle + LDR_HYS) {
active = 0;
}
return active;
@ -206,7 +209,6 @@ void Treppe::task() {
// TODO wenn LDR geändert => idle_pwm_soll anpassen
// fsm_pend.ldr_changed = true;
fsm_inputs.ldr_schwelle = check_ldr();
#ifdef DEBUG_TIMING
@ -215,8 +217,10 @@ void Treppe::task() {
m = micros();
#endif
fsm_inputs.sensor_oben = read_sensor(SENSOR_OBEN);
fsm_inputs.sensor_unten = read_sensor(SENSOR_UNTEN);
read_sensors();
fsm_inputs.sensor_oben = fsm_pend.sensor_oben;
fsm_inputs.sensor_unten = fsm_pend.sensor_unten;
fsm_inputs.anim_beendet = fsm_pend.anim_beendet;
#ifdef DEBUG_TIMING
@ -248,7 +252,7 @@ void Treppe::task() {
fsm_outputs.status == ST_AUFDIMMEN_RUNTER ||
fsm_outputs.status == ST_ABDIMMEN_RUNTER) {
if (fsm_pend.anim_beendet)
start_animation(&dimmer_stufen, DIM_STUFEN, parameters.active_pwm,
start_animation(&dimmer_stufen, DIM_STUFEN, param.active_pwm,
idle_pwm_ist);
else
fsm_pend.anim_beendet = dimmer_tick(&dimmer_stufen, DIM_STUFEN);
@ -268,6 +272,12 @@ void Treppe::task() {
if (!fsm_pend.anim_beendet) {
fsm_pend.anim_beendet = dimmer_tick(&dimmer_ldr, DIM_LDR);
}
if (param_changed) {
param_changed = false;
param = param_pend;
save_param_to_eeprom();
}
}
#ifdef DEBUG_TIMING
@ -288,7 +298,7 @@ void Treppe::setup() {
// WARNING: before getting Parameters of Flash, make sure plausible parameters
// are written in flash!
EEPROM.get(EEP_START_ADDR, parameters); // get Parameters of flash
EEPROM.get(EEP_START_ADDR, param); // get Parameters of flash
pinMode(13, OUTPUT);
pinMode(0, OUTPUT);
@ -304,73 +314,73 @@ void Treppe::setup() {
Serial.printf("Treppe: stufen=%d\n", stufen);
}
void Treppe::saveParam() {
void Treppe::save_param_to_eeprom() {
EEPROM.put(EEP_START_ADDR,
parameters); // copy Parameters so "EEPROM"-section in RAM
param); // copy Parameters so "EEPROM"-section in RAM
EEPROM.commit(); // write "EEPROM"-section to flash
}
void Treppe::set_idle_pwm_max(const uint16_t value,
const vorgabe_typ_t vorgabe_typ) {
if (vorgabe_typ == VORGABE_PROZENT) {
parameters.idle_pwm_max = parameters.active_pwm * value / 100;
param_pend.idle_pwm_max = param_pend.active_pwm * value / 100;
} else if (vorgabe_typ == VORGABE_12BIT) {
parameters.idle_pwm_max = value;
param_pend.idle_pwm_max = value;
}
if (parameters.idle_pwm_max > parameters.active_pwm) {
parameters.idle_pwm_max = parameters.active_pwm;
if (param_pend.idle_pwm_max > param_pend.active_pwm) {
param_pend.idle_pwm_max = param_pend.active_pwm;
}
saveParam();
param_changed = true;
Serial.printf("Treppe: parameters.idle_pwm_max=%d\n",
parameters.idle_pwm_max);
Serial.printf("Treppe: param_pend.idle_pwm_max=%d\n",
param_pend.idle_pwm_max);
}
void Treppe::set_active_pwm(const uint16_t value,
const vorgabe_typ_t vorgabe_typ) {
if (vorgabe_typ == VORGABE_PROZENT) {
parameters.active_pwm = 4095 * value / 100;
param_pend.active_pwm = 4095 * value / 100;
} else if (vorgabe_typ == VORGABE_12BIT) {
parameters.active_pwm = value;
param_pend.active_pwm = value;
}
if (parameters.active_pwm > 4095) {
parameters.idle_pwm_max = 4095;
if (param_pend.active_pwm > 4095) {
param_pend.idle_pwm_max = 4095;
}
saveParam();
param_changed = true;
Serial.printf("Treppe: parameters.active_pwm=%d\n", parameters.active_pwm);
Serial.printf("Treppe: param_pend.active_pwm=%d\n", param_pend.active_pwm);
}
void Treppe::set_time_ldr(const uint16_t value) {
parameters.time_ldr = value;
if (parameters.time_ldr > TIME_MS_MAX)
parameters.time_ldr = TIME_MS_MAX;
saveParam();
param_pend.time_ldr = value;
if (param_pend.time_ldr > TIME_MS_MAX)
param_pend.time_ldr = TIME_MS_MAX;
param_changed = true;
Serial.printf("Treppe: time_ldr=%d\n", parameters.time_ldr);
Serial.printf("Treppe: time_ldr=%d\n", param_pend.time_ldr);
}
void Treppe::set_time_per_stair(const uint16_t value) {
parameters.time_per_stair = value;
if (parameters.time_per_stair > TIME_MS_MAX)
parameters.time_per_stair = TIME_MS_MAX;
saveParam();
param_pend.time_per_stair = value;
if (param_pend.time_per_stair > TIME_MS_MAX)
param_pend.time_per_stair = TIME_MS_MAX;
param_changed = true;
Serial.printf("Treppe: time_per_stair=%d\n", parameters.time_per_stair);
Serial.printf("Treppe: time_per_stair=%d\n", param_pend.time_per_stair);
}
void Treppe::set_ldr_schwelle(const uint16_t value,
const vorgabe_typ_t vorgabe_typ) {
if (vorgabe_typ == VORGABE_PROZENT) {
// ?!
parameters.ldr_schwelle = 10 * value / 100;
param_pend.ldr_schwelle = 10 * value / 100;
} else if (vorgabe_typ == VORGABE_12BIT) {
// parameters.ldr_schwelle = value;
// param_pend.ldr_schwelle = value;
}
saveParam();
param_changed = true;
Serial.printf("Treppe: ldr_schwelle=%d\n", parameters.ldr_schwelle);
Serial.printf("Treppe: ldr_schwelle=%d\n", param_pend.ldr_schwelle);
}

View File

@ -30,15 +30,21 @@ private:
uint16_t active_pwm = 2000;
uint16_t ldr_schwelle = 2; // activation value for FSM [lx]
};
stairway_param_t parameters;
stairway_param_t param;
stairway_param_t param_pend; // zwischenspeicher änderungen
bool param_changed = false;
uint16_t idle_pwm_ist = parameters.idle_pwm_max;
uint16_t idle_pwm_ist = param.idle_pwm_max;
uint16_t idle_pwm_soll = 0;
struct fsm_pending_inputs_t {
bool anim_beendet = true;
bool sensor_unten = false;
bool last_s_unten = false;
bool web_ctrl_s_unten = false;
bool sensor_oben = false;
bool last_s_oben = false;
bool web_ctrl_s_oben = false;
bool ldr_changed = false;
};
fsm_pending_inputs_t fsm_pend;
@ -77,13 +83,13 @@ private:
enum fsm_laufrichtung_t { LR_RUNTER = 0, LR_HOCH = 1 };
enum fsm_dimmrichtung_t { DR_ABDIMMEN = 0, DR_AUFDIMMEN = 1 };
void read_sensors();
void print_state_on_change();
/* DIMM */
// bool dimmer(dimmer_t* dimmer, bool dim_type);
bool dimmer_tick(dimmer_t *dimmer, bool dim_type);
void start_animation(dimmer_t *dimmer, bool dim_type, uint16_t on_pwm,
uint16_t off_pwm);
// void berechne_dimmer(dimmer_t* dimmer, bool dim_type);
void print_state_on_change();
/* LDR */
bool read_sensor(int sensor);
@ -98,13 +104,15 @@ public:
void task(); // call periodically
// Parameter section
void saveParam();
void save_param_to_eeprom();
void set_idle_pwm_max(const uint16_t value, const vorgabe_typ_t vorgabe_typ);
void set_active_pwm(const uint16_t value, const vorgabe_typ_t vorgabe_typ);
void set_time_ldr(const uint16_t value);
void set_time_per_stair(const uint16_t value);
void set_ldr_schwelle(const uint16_t value, const vorgabe_typ_t vorgabe_typ);
void overwrite_sensors(bool s_oben, bool s_unten);
};
#endif // __TREPPE_H