This commit is contained in:
Dominik Bartsch 2021-06-28 15:48:29 +02:00
commit 50e714e5f8
21 changed files with 350 additions and 212 deletions

1
.gitignore vendored
View File

@ -1,6 +1,7 @@
.pio
.vscode
compress
data_gz
platformio.ini
include/wifi_credentials.h
include/*.gz.h

View File

@ -48,5 +48,6 @@
"streambuf": "cpp",
"cinttypes": "cpp",
"typeinfo": "cpp"
}
},
"python.pythonPath": "C:\\Users\\simon\\AppData\\Local\\Programs\\Python\\Python37\\python.exe"
}

38
create_gz_c_arr.py Normal file
View File

@ -0,0 +1,38 @@
#!/usr/bin/python3
import os, gzip
# https://www.mischianti.org/2020/10/26/web-server-with-esp8266-and-esp32-byte-array-gzipped-pages-and-spiffs-2/
def convert_to_gzip(src, out, f):
input_file = f'{src}{f}'
output_file = f'{out}{f}.gz.h'
output_charp = f'{f.replace(".", "_")}_gz'
top = ''
with open(input_file, 'rb') as f_in:
gz = gzip.compress(f_in.read())
gzlen = len(gz)
top += f'// filename: {f}.gz.h\n'
top += f'#define {output_charp}_len {gzlen}\n'
top += f'const char {output_charp}[] = '
top += '{'
with open(output_file, 'wb') as f_out:
for i, b in enumerate(gz):
if not i%10:
top += '\n '
top += f'0x{b:02X}, '
top = top[:-2]
top += '};'
f_out.write(top.encode(encoding='utf-8'))
src='data/'
out='compress/'
if not 'compress' in os.listdir():
os.mkdir('compress')
for f in os.listdir(src):
convert_to_gzip(src, out, f)

View File

@ -1,38 +1,15 @@
#!/usr/bin/python3
import os, gzip
# https://www.mischianti.org/2020/10/26/web-server-with-esp8266-and-esp32-byte-array-gzipped-pages-and-spiffs-2/
# https://stackoverflow.com/questions/8156707/gzip-a-file-in-python
def convert_to_gzip(src, out, f):
input_file = f'{src}{f}'
output_file = f'{out}{f}.gz.h'
output_charp = f'{f.replace(".", "_")}_gz'
src_dir='data'
out_dir='data_gz'
top = ''
with open(input_file, 'rb') as f_in:
gz = gzip.compress(f_in.read())
gzlen = len(gz)
if not 'data_gz' in os.listdir():
os.mkdir('data_gz')
top += f'// filename: {f}.gz.h\n'
top += f'#define {output_charp}_len {gzlen}\n'
top += f'const char {output_charp}[] = '
top += '{'
with open(output_file, 'wb') as f_out:
for i, b in enumerate(gz):
if not i%10:
top += '\n '
top += f'0x{b:02X}, '
top = top[:-2]
top += '};'
f_out.write(top.encode(encoding='utf-8'))
src='data/'
out='compress/'
if not 'compress' in os.listdir():
os.mkdir('compress')
for f in os.listdir(src):
convert_to_gzip(src, out, f)
for f in os.listdir(src_dir):
with open(f'{src_dir}/{f}', 'rb') as f_in:
with gzip.open(f'{out_dir}/{f}.gz', 'wb') as f_out:
f_out.writelines(f_in)

12
data/compress.html Normal file
View File

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<title>Ich bin ein komrpimierter Test</title>
I bims der Head
</head>
<body>
I bims der Body
</body>
</html>

View File

@ -60,6 +60,13 @@
</div>
</div>
<div class="terminal">
<input type="button" id="clear_term" value="clear" onclick="clearTerminal();">
<input type="checkbox" id="scroll" name="scroll" value="scroll">
<label for="scroll"> autoscroll </label>
<textarea id="term"> waiting for log messages ... </textarea>
</div>
</body>
<script src="/input.js"></script>

View File

@ -1,8 +1,40 @@
var slider = document.getElementById("range1");
var output = document.getElementById("l_pwm");
output.innerHTML = slider.value; // Display the default slider value
// var slider = document.getElementById("helligkeit");
// var output = document.getElementById("l_pwm");
// output.innerHTML = slider.value; // Display the default slider value
// Update the current slider value (each time you drag the slider handle)
slider.oninput = function() {
output.innerHTML = this.value;
// // Update the current slider value (each time you drag the slider handle)
// slider.oninput = function() {
// output.innerHTML = this.value;
// }
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 + '\n';
if(autoscroll.checked)
terminal.scrollTop = terminal.scrollHeight;
}
else {
console.log("status:", xhr.status);
}
}
}
function reloadTerminal() {
xhr.open("POST", "/app=terminal", true);
xhr.send();
setTimeout(reloadTerminal, 1000);
};
reloadTerminal();
function clearTerminal() {
document.getElementById("term").innerHTML = '';
}

View File

@ -3,7 +3,7 @@ html {
font-family: sans-serif, Arial, Helvetica;
background-color: #d4d4d4;
height: 100%;
background-image: url('Background.png');
/* background-image: url('Background.png'); */
background-repeat: repeat;
background-size: 150% 150%;
}
@ -103,4 +103,22 @@ input:checked + .slider:before {
-ms-transform: translateX(26px);
transform: translateX(26px);
}
*/
.terminal {
margin:5%;
padding: 1%;
width: 80%;
border: 1px solid black;
border-radius: 5px;
}
#term {
font-size: large;
width: 100%;
height: 10em;
}
#clear_term {
margin: 2px;
}

17
doku.md
View File

@ -68,10 +68,19 @@ TODO:
- maybe compress files ?
LED Flickering issue
### LED Flickering issue
https://github.com/NachtRaveVL/PCA9685-Arduino/issues/15
PlatformIO Library veraltet
PlatformIO Library veraltet !!
###TIMER in OTA unterbrechen !!!!!
### TIMER in OTA unterbrechen !!!!!
### further improvments
with streamFile gzip encoded Files can be sent
this reduces space on fs !
new easier script needed to just convert to .gz
maybe measure timings on ESP
__=> use serveStatic because way simpler !__

View File

@ -1,48 +0,0 @@
#ifndef __HTTPSERVER_H
#define __HTTPSERVER_H
#include "filesys.h"
#include <ESP8266WebServer.h>
class HTTPServer : public ESP8266WebServer {
private:
const char* rootDir;
bool addRootFileHandler();
bool formatFS() {
return format_fs();
}
void listRoot() {
ls(rootDir);
}
public:
HTTPServer(const int _port, const char* _rootDir) :
ESP8266WebServer(_port), rootDir(_rootDir)
{ }
~HTTPServer()
{
Serial.printf("[HTTPServer] shut down ...\n\r");
}
bool start() {
if(!mount_fs())
return false;
Serial.printf("[HTTPServer] LittleFS mounted !\n\r");
Serial.printf("[HTTPServer] root:\n\r");
this->listRoot();
Serial.printf("\n\r");
if( this->addRootFileHandler() ){
this->begin();
Serial.printf("[HTTPServer] Server active on Port 80 !\n\r");
return true;
}
Serial.printf("[HTTPServer] Not starting Server, something went wrong !\n\r");
return false;
}
};
#endif // __HTTPSERVER_H

View File

@ -1,5 +1,4 @@
#ifndef __OTA_H
#define __OTA_H
#pragma once
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
@ -46,7 +45,4 @@ void ota_setup() {
});
ArduinoOTA.begin();
}
#endif // __OTA_H
}

View File

@ -1,12 +1,5 @@
#include "filesys.h"
// listDir("/");
// deleteFile("/hello.txt");
// writeFile("/hello.txt", "Hello ");
// appendFile("/hello.txt", "World!\n\r");
// readFile("/hello.txt");
// listDir("/");
FSInfo fsinfo;
bool mount_fs() {
@ -58,7 +51,12 @@ void ls(const char * dirname) {
Serial.println();
}
void readFile(const char * path) {
File open(const char * path) {
return LittleFS.open(path, "r");
}
void printFile(const char * path) {
Serial.printf("Reading file: %s\n\r", path);
File file = LittleFS.open(path, "r");

View File

@ -1,16 +1,17 @@
#ifndef __FILESYS_H
#define __FILESYS_H
#pragma once
#include <LittleFS.h>
// some usefull wrappers for Filesystem
bool mount_fs();
bool format_fs();
File open(const char * path);
void ls(const char * dirname);
void readFile(const char * path);
void printFile(const char * path);
void writeFile(const char * path, const char * message);
void appendFile(const char * path, const char * message);
void renameFile(const char * path1, const char * path2);
void deleteFile(const char * path);
#endif // __FILESYS_H

View File

@ -0,0 +1,42 @@
#include "httpserver.h"
bool HTTPServer::start() {
if(!mount_fs()) {
logf("cant mount filesystem, EXIT !\n\r");
return false;
}
logf("[HTTPServer] LittleFS mounted !\n\r");
logf("[HTTPServer] root:\n\r");
this->listRoot();
logf("\n\r");
// default handler
this->onNotFound([this]() {
String message = "File Not Found\n\n";
message += "URI: ";
message += uri();
message += "\nMethod: ";
message += (method() == HTTP_GET) ? "GET" : "POST";
message += "\nArguments: ";
message += args();
message += "\n";
for (uint8_t i = 0; i < args(); i++) {
message += " " + argName(i) + ": " + arg(i) + "\n";
}
send(404, "text/plain", message);
});
// add static root file handler for http
this->serveStatic("/", LittleFS, "/");
// application handler
this->on("/app=terminal", HTTP_POST, [this]() {
String log_msg = "terminal: millis: ";
log_msg += millis();
send(200, "text/plain", log_msg);
});
this->begin();
Serial.printf("[HTTPServer] Server active on Port 80 !\n\r");
}

View File

@ -0,0 +1,41 @@
// Wrapper for ESP8266WebServer with Filesystem as HTTP source
#pragma once
#include <ESP8266WebServer.h>
#include <stdarg.h>
#include "filesys.h"
// debug log <ESP8266WebServer.h>
// #define DEBUGV(f,...) do { Serial.printf(PSTR(f), ##__VA_ARGS__); } while (0)
#define LOG_STR "[HTTPServer]"
class HTTPServer : public ESP8266WebServer {
private:
const char* rootDir = "/";
void listRoot() {
ls(rootDir);
}
void logf(const char *format, ...) {
va_list args;
va_start(args, format);
Serial.print(LOG_STR);
Serial.printf(format, args);
va_end(args);
}
public:
HTTPServer(const int _port, const char* _rootDir) :
ESP8266WebServer(_port), rootDir(_rootDir)
{ }
~HTTPServer()
{
Serial.printf("[HTTPServer] shut down ...\n\r");
}
bool start();
};

View File

@ -30,7 +30,7 @@ uint8_t Treppe::softstart_led(uint8_t led, uint16_t startval, uint16_t stopval){
else {
current_pwm += stepsize;
}
Serial.println((uint16_t)current_pwm);
// 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);
@ -60,46 +60,40 @@ void Treppe::ledsequence(){
lastbrightness = active_brightness;
}
status = status_build; // set parameter memory
Serial.print("----Status Changed! onoff: ");
Serial.print(state);
Serial.print(" dir: ");
Serial.println(direction);
Serial.printf("----Status Changed! onoff: %d, dir: %d\n", state, direction);
}
if(!finish){ // finish == 0 -> action pending
if(!softstart_led(led,lastbrightness, brightness)){
Serial.print("one LED finished: ");
Serial.print(led);
Serial.print(" last: ");
Serial.print(lastbrightness);
Serial.print(" curr: ");
Serial.println(brightness);
if(direction){
led++;
if(led >= stairs) {
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;
//lastbrightness = brightness;
}
}
else{
led--;
if(led < 0){
//lastbrightness = brightness;
finish = 1;
}
}
}
}
}
}
void Treppe::task_2()
void Treppe::rampe()
{
if(state) {
finish = 0;
state = 0; // set parameter memory
}
if(!finish) {
if(direction) { // aufwärts
if(tick >= ticks_treppe-1) { // ziel erreicht
Serial.println("[Treppe] oberster tick !");
state = 0;
finish = 1;
return;
}
tick++; // eins hoch
@ -107,17 +101,25 @@ void Treppe::task_2()
else { // abwärts
if(tick <= 0) { // ziel erreicht
Serial.println("[Treppe] unterster tick !");
state = 0;
finish = 1;
return;
}
tick--; // eins runter
}
stufe = tick / ticks_pro_stufe;
float new_pwm = differenz_pwm_pro_tick * (tick - ticks_pro_stufe*stufe);
if(direction)
new_pwm += differenz_pwm_pro_tick;
new_pwm += idle_brightness;
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",
@ -129,12 +131,6 @@ void Treppe::task_2()
}
}
// if(stufe > stairs || stufe < 0 || tick < 0 || tick > ticks_treppe-1) {
// Serial.println("[Treppe] ERROR, Something went wrong !");
// state = 0;
// return;
// }
void Treppe::setup(){
Serial.printf("differenz_pwm_pro_tick %f\n", differenz_pwm_pro_tick);
@ -168,29 +164,41 @@ void Treppe::task(){
current_sensor_state[1] = digitalRead(SENSOR2);
if(current_sensor_state[0] && !last_sensor_state[0] && state == 0){
setTick(0);
setAnAus(1);
setDirection(1);
setState(1);
}
if(current_sensor_state[1] && !last_sensor_state[1] && state == 0){
setTick(0);
setAnAus(0);
setDirection(0);
setState(1);
}
// first switch - off approach, use timer later
if(!current_sensor_state[0] && last_sensor_state[0] && state == 1){
setTick(ticks_treppe);
setAnAus(1);
setDirection(1);
setState(0);
}
if(!current_sensor_state[1] && last_sensor_state[1] && state == 1){
setTick(ticks_treppe);
setAnAus(1);
setDirection(0);
setState(0);
}
last_sensor_state[0] = current_sensor_state[0];
last_sensor_state[1] = current_sensor_state[1];
<<<<<<< HEAD
=======
>>>>>>> 3d13e761f497a0408c134059ff0dfadaa93ebbf4
ledsequence();
}
@ -211,22 +219,20 @@ uint16_t Treppe::setTime(uint16_t _time_per_stair){
return time_per_stair;
}
uint8_t Treppe::setDirection(uint8_t _direction){
void Treppe::setDirection(uint8_t _direction){
switch_direction = _direction;
Serial.println("Treppe: Direction changed!");
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
return switch_direction;
}
uint8_t Treppe::setState(uint8_t _state){
if(state == _state) return 1;
else{
switch_state = _state;
Serial.println("Treppe: State Request changed!");
if(finish) Serial.println("apply state request immediately");
else Serial.println("currently active, state changes after activity");
}
return 0;
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");
}
}

View File

@ -21,17 +21,22 @@ private:
uint8_t switch_state = 0;
uint8_t finish = 1;
// 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;
float differenz_pwm_pro_tick = 0.0;
// alternative
// initialize with i2c-Address 0, use Wire Library
PCA9685 pwmController;
uint8_t softstart_led(uint8_t led, uint16_t startval, uint16_t stopval);
void ledsequence();
void rampe();
public:
Treppe(uint8_t _stairs) : stairs(_stairs){
@ -42,10 +47,8 @@ public:
/ (float) ticks_pro_stufe;
}
void task(); // call periodically
void setup();
void task_2();
void task(); // call periodically
// Parameter section
uint16_t setIdle(uint16_t _idle_brightness);
@ -61,9 +64,13 @@ public:
}
// Runtime Parameter section
uint8_t setDirection(uint8_t _direction);
uint8_t setState(uint8_t _state);
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;};
};

View File

@ -1,17 +0,0 @@
#include "httpserver.h"
bool HTTPServer::addRootFileHandler() {
// //experimental, see doku.md
// server.serveStatic("/compressed", LittleFS, "/compressed.html.gzip");
this->serveStatic("", LittleFS, "/index.html");
this->serveStatic("/", LittleFS, "/index.html");
this->serveStatic("/#", LittleFS, "/index.html");
this->serveStatic("/style.css", LittleFS, "/style.css");
this->serveStatic("/input.js", LittleFS, "/input.js");
this->serveStatic("/favicon.png", LittleFS, "/favicon.png");
return true;
}

View File

@ -9,8 +9,8 @@ extern "C" {
#include "user_interface.h"
}
// OTA & WEB
#include "ota.h"
#include "wifi_credentials.h"
#include "ota.h"
#include "httpserver.h"
// BOARD
@ -34,8 +34,7 @@ void timerCallback(void *pArg)
stairs.task();
}
// HTTP
void handleNotFound();
// port 80, root directory of server '/'
HTTPServer httpServer(80, "/");
uint32_t _t=0;
@ -44,22 +43,6 @@ uint32_t _t=0;
// ===============================================
void handleNotFound() {
String message = "File Not Found\n\n";
message += "URI: ";
message += httpServer.uri();
message += "\nMethod: ";
message += (httpServer.method() == HTTP_GET) ? "GET" : "POST";
message += "\nArguments: ";
message += httpServer.args();
message += "\n";
for (uint8_t i = 0; i < httpServer.args(); i++) {
message += " " + httpServer.argName(i) + ": " + httpServer.arg(i) + "\n";
}
httpServer.send(404, "text/plain", message);
}
void setup() {
#ifdef WITH_DEBUGGING_ON
@ -95,7 +78,6 @@ void setup() {
ota_setup();
httpServer.start();
httpServer.onNotFound(handleNotFound);
Serial.println("HTTP server started !");
stairs.setup();
@ -103,9 +85,6 @@ void setup() {
os_timer_setfn(&timer1, timerCallback, &timer_flag);
os_timer_arm(&timer1, 20, true);
stairs.setState(1);
stairs.setDirection(1);
}
#include <random>

View File

@ -8,23 +8,29 @@
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env]
[platformio]
description = ESP8266 Treppenlicht Steuerung
default_envs = ota
; test_dir = test
data_dir = data_gz
[env:hardware]
platform = espressif8266
board = nodemcuv2
framework = arduino
; for http files
board_build.filesystem = littlefs
board_build.ldscript = eagle.flash.4m1m.ld
extra_scripts = pre:create_gz_files.py
monitor_speed = 115200
[env:serial]
extends = env:hardware
upload_protocol = esptool
upload_speed = 921600
[env:ota]
extends = env:hardware
; OTA => https://docs.platformio.org/en/latest/platforms/espressif8266.html#over-the-air-ota-update
upload_protocol = espota
upload_port = <!! IP ADDRESS !!>
@ -33,13 +39,46 @@ upload_flags =
--host_port=<!! PORT !!>
--auth=admin
[env:debug]
extends = env:hardware
; look at doku.md
build_flags = -DWITH_DEBUGGING_ON -Os -g3 -ggdb3
upload_protocol = esptool
upload_speed = 921600
# This file is used compile and run tests located in the `unit` directory.
# For more info, see:
# https://docs.platformio.org/en/latest/plus/unit-testing.html
# https://github.com/ThrowTheSwitch/Unity
# https://github.com/ThrowTheSwitch/Unity/blob/master/docs/UnityAssertionsReference.md
# To prepare coverage data for lcov, add ${coverage.build_flags} to env:test build flags
# To actually generate coverage report:
# $ `pio test` / run the test `program` manually
# $ lcov --include (readlink -f ../espurna)'/*' --capture --directory .pio/build/test/ --output-file test.info
# $ genhtml --ignore-errors source test.info --output-directory out
[coverage]
build_flags = -lgcov -fprofile-arcs -ftest-coverage
[env:test]
extends = env:hardware
[env:native]
platform = native
test_build_project_src = true
build_flags =
-DMANUFACTURER="PLATFORMIO"
-DDEVICE="TEST"
-std=gnu++11
-g
-Os
-I lib/treppe
-I lib/PCA9685-Arduino

View File

@ -1,6 +1,5 @@
#include <Arduino.h>
// #include <Arduino.h>
#include "treppe.h"
#include <unity.h>
Treppe stairs(10);