Dominik Bartsch 2 years ago
parent
commit
50e714e5f8

+ 1
- 0
.gitignore View File

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

+ 2
- 1
.vscode/settings.json 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
- 0
create_gz_c_arr.py 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)

+ 9
- 32
create_gz_files.py 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
- 0
data/compress.html 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>

+ 7
- 0
data/index.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>

+ 38
- 6
data/input.js 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 = '';
}

+ 19
- 1
data/style.css 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;
}

+ 12
- 3
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 !__

+ 0
- 48
include/httpserver.h 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

+ 2
- 6
include/ota.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
}

src/filesys.cpp → lib/httpserver/filesys.cpp 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");

include/filesys.h → lib/httpserver/filesys.h 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

+ 42
- 0
lib/httpserver/httpserver.cpp 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");
}

+ 41
- 0
lib/httpserver/httpserver.h 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();
};

+ 57
- 51
lib/treppe/treppe.cpp 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");
}
}

+ 12
- 5
lib/treppe/treppe.h 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;};
};

+ 0
- 17
src/httpserver.cpp 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;
}

+ 2
- 23
src/main.cpp 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>

+ 42
- 3
templ_platformio_ini 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



+ 1
- 2
test/test_pwm.cpp View File

@@ -1,6 +1,5 @@
#include <Arduino.h>
// #include <Arduino.h>
#include "treppe.h"

#include <unity.h>

Treppe stairs(10);

Loading…
Cancel
Save