add webserver components, some webserver fices and improvements in templating, add real time history chart
This commit is contained in:
@@ -16,7 +16,17 @@ set(COMPONENT_SRCS
|
||||
"graphics/font.c"
|
||||
"graphics/drawing.c"
|
||||
"graphics/bitmaps.c"
|
||||
"web/websrv.c"
|
||||
"files/files_enum.c"
|
||||
"utils.c"
|
||||
)
|
||||
set(COMPONENT_ADD_INCLUDEDIRS "." "liquid" "graphics")
|
||||
|
||||
#begin staticfiles
|
||||
# generated by rebuild_file_tables
|
||||
set(COMPONENT_EMBED_FILES
|
||||
"files/embed/favicon.ico"
|
||||
"files/embed/index.html")
|
||||
#end staticfiles
|
||||
|
||||
register_component()
|
||||
|
||||
+16
-1
@@ -16,6 +16,13 @@ static float measurement_celsius;
|
||||
static const adc_atten_t atten = ADC_ATTEN_DB_0;
|
||||
static const adc_unit_t unit = ADC_UNIT_1;
|
||||
|
||||
float reg_meas_history[REG_HISTORY_LEN] = {};
|
||||
float reg_tset_history[REG_HISTORY_LEN] = {};
|
||||
uint32_t history_counter = 0;
|
||||
|
||||
// TODO move to regulator module (make extern)
|
||||
float reg_setpoint = 125;
|
||||
|
||||
static void analog_service(void *arg);
|
||||
|
||||
static TaskHandle_t hAnalog;
|
||||
@@ -63,7 +70,15 @@ static void __attribute__((noreturn)) analog_service(void *arg) {
|
||||
|
||||
measurement_celsius = celsius;
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
for (int i = 0; i < REG_HISTORY_LEN-1; i++) {
|
||||
reg_meas_history[i] = reg_meas_history[i+1];
|
||||
reg_tset_history[i] = reg_tset_history[i+1];
|
||||
}
|
||||
reg_meas_history[REG_HISTORY_LEN-1] = celsius;
|
||||
reg_tset_history[REG_HISTORY_LEN-1] = reg_setpoint;
|
||||
history_counter = (history_counter + 1) % 20;
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(500));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,11 @@
|
||||
#ifndef REFLOWER_ANALOG_H
|
||||
#define REFLOWER_ANALOG_H
|
||||
|
||||
#define REG_HISTORY_LEN 121
|
||||
extern float reg_meas_history[REG_HISTORY_LEN];
|
||||
extern float reg_tset_history[REG_HISTORY_LEN];
|
||||
extern uint32_t history_counter;
|
||||
|
||||
void analog_init();
|
||||
|
||||
float analog_read();
|
||||
|
||||
+78
-1
@@ -7,6 +7,8 @@
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <web/websrv.h>
|
||||
#include <esp_wifi.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_system.h"
|
||||
@@ -15,11 +17,68 @@
|
||||
#include "knob.h"
|
||||
#include "gui.h"
|
||||
#include "analog.h"
|
||||
#include "utils.h"
|
||||
#include "esp_event_loop.h"
|
||||
#include "nvs_flash.h"
|
||||
|
||||
/**
|
||||
* Application event handler
|
||||
*
|
||||
* @param ctx - ignored, context passed to esp_event_loop_init()
|
||||
* @param event
|
||||
* @return success
|
||||
*/
|
||||
static esp_err_t event_handler(void *ctx, system_event_t *event)
|
||||
{
|
||||
switch (event->event_id) {
|
||||
case SYSTEM_EVENT_STA_START:
|
||||
try_reconn_if_have_wifi_creds();
|
||||
break;
|
||||
|
||||
case SYSTEM_EVENT_STA_CONNECTED:
|
||||
// we should get an IP address soon
|
||||
break;
|
||||
|
||||
case SYSTEM_EVENT_STA_GOT_IP:
|
||||
// xEventGroupSetBits(g_wifi_event_group, EG_WIFI_CONNECTED_BIT);
|
||||
break;
|
||||
|
||||
case SYSTEM_EVENT_STA_DISCONNECTED:
|
||||
// xEventGroupClearBits(g_wifi_event_group, EG_WIFI_CONNECTED_BIT);
|
||||
try_reconn_if_have_wifi_creds();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// dhcp_watchdog_notify(dhcp_wd, event->event_id);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initialize WiFi in station mode, try to connect if settings are stored.
|
||||
* Set up WiFi event group & start related tasks
|
||||
*/
|
||||
static void initialise_wifi(void)
|
||||
{
|
||||
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
|
||||
ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_FLASH));
|
||||
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
|
||||
|
||||
ESP_ERROR_CHECK(esp_wifi_start());
|
||||
}
|
||||
|
||||
|
||||
void __attribute__((noreturn)) app_main()
|
||||
{
|
||||
printf("Hello world!\n");
|
||||
ESP_ERROR_CHECK(nvs_flash_init());
|
||||
//ESP_ERROR_CHECK(esp_register_shutdown_handler(cspemu_run_shutdown_handlers));
|
||||
|
||||
ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL));
|
||||
|
||||
gpio_config_t output = {
|
||||
.pin_bit_mask = (1<<22),
|
||||
@@ -27,11 +86,29 @@ void __attribute__((noreturn)) app_main()
|
||||
};
|
||||
gpio_config(&output);
|
||||
|
||||
tcpip_adapter_init();
|
||||
initialise_wifi();
|
||||
|
||||
// TODO add proper join procedure
|
||||
const char *ssid = "Chlivek_2.4G";
|
||||
const char *pass = "slepice123";
|
||||
wifi_config_t wifi_config = {};
|
||||
strncpy((char*) wifi_config.sta.ssid, ssid, sizeof(wifi_config.sta.ssid));
|
||||
if (pass) {
|
||||
strncpy((char*) wifi_config.sta.password, pass, sizeof(wifi_config.sta.password));
|
||||
}
|
||||
ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) );
|
||||
ESP_ERROR_CHECK( esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) );
|
||||
ESP_ERROR_CHECK( esp_wifi_disconnect() );
|
||||
ESP_ERROR_CHECK( esp_wifi_connect() );
|
||||
|
||||
gui_init();
|
||||
knob_init();
|
||||
analog_init();
|
||||
|
||||
try_reconn_if_have_wifi_creds();
|
||||
websrv_init();
|
||||
|
||||
bool level = 0;
|
||||
while (1) {
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 660 440">
|
||||
<style>
|
||||
.ticks, .frame {
|
||||
stroke-width: 1px;
|
||||
fill: none;
|
||||
stroke: black;
|
||||
}
|
||||
|
||||
path.major {
|
||||
stroke-width: 2px;
|
||||
}
|
||||
|
||||
.ylabels text {
|
||||
font-size: 10px;
|
||||
text-anchor: end;
|
||||
font-family: Droid Sans, sans-serif;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.grid {
|
||||
stroke-dasharray: 4;
|
||||
stroke: #999;
|
||||
}
|
||||
|
||||
.series {
|
||||
stroke-width: 2px;
|
||||
fill: none;
|
||||
}
|
||||
</style>
|
||||
<g transform="translate(50,15)">
|
||||
<path d="M100,0 v400m100,-400 v400m100,-400 v400m100,-400 v400m100,-400 v400m100,-400 v400m100,-400"
|
||||
class="grid" transform="translate(0,0)" id="grid-v" />
|
||||
<path d="M0,100 h600m-600,100 h600m-600,100 h600m-600,100"
|
||||
class="grid" stroke-dashoffset="0" id="grid-h" />
|
||||
<path d="M-10,0 h10m-10,100 h10m-10,100 h10m-10,100 h10m-10,100 h10" class="ticks" />
|
||||
<path d="M-5,10
|
||||
h5m-5,10h5m-5,10h5m-5,10h5m-5,10h5m-5,10h5m-5,10h5m-5,10h5m-5,10h5m-5,20
|
||||
h5m-5,10h5m-5,10h5m-5,10h5m-5,10h5m-5,10h5m-5,10h5m-5,10h5m-5,10h5m-5,20
|
||||
h5m-5,10h5m-5,10h5m-5,10h5m-5,10h5m-5,10h5m-5,10h5m-5,10h5m-5,10h5m-5,20
|
||||
h5m-5,10h5m-5,10h5m-5,10h5m-5,10h5m-5,10h5m-5,10h5m-5,10h5m-5,10h5" class="ticks" />
|
||||
<g class="series">
|
||||
<path d="M0,400L600,0" stroke="blue" id="ser-set" />
|
||||
<path d="M0,0L300,100L600,400" stroke="red" id="ser-actual" />
|
||||
</g>
|
||||
<path d="M0,0h600v400h-600Z" class="frame" />
|
||||
<g class="ylabels" transform="translate(0,3)">
|
||||
<text x="-15" y="0">400 °C</text>
|
||||
<text x="-15" y="100">300 °C</text>
|
||||
<text x="-15" y="200">200 °C</text>
|
||||
<text x="-15" y="300">100 °C</text>
|
||||
<text x="-15" y="400">0 °C</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.2 KiB |
@@ -0,0 +1,137 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Breadflow Web Control</title>
|
||||
<style>
|
||||
* {
|
||||
box-sizing: content-box;
|
||||
}
|
||||
h1 {
|
||||
text-align: center;
|
||||
}
|
||||
#ctab {
|
||||
width: 1400px;
|
||||
margin: 0 auto;
|
||||
border: 1px solid #ccc;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
#td-side {
|
||||
border-left: 1px solid #ccc;
|
||||
width: 340px;
|
||||
padding: 15px;
|
||||
vertical-align: top;
|
||||
padding-top: 22px;
|
||||
}
|
||||
#td-img {
|
||||
vertical-align: top;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Breadflow {version}</h1>
|
||||
|
||||
<table id="ctab">
|
||||
<tr>
|
||||
<td id="td-img">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 660 430">
|
||||
<style>
|
||||
.ticks, .frame {
|
||||
stroke-width: 1px;
|
||||
fill: none;
|
||||
stroke: black;
|
||||
}
|
||||
|
||||
path.major {
|
||||
stroke-width: 2px;
|
||||
}
|
||||
|
||||
.ylabels text {
|
||||
font-size: 10px;
|
||||
text-anchor: end;
|
||||
font-family: Droid Sans, sans-serif;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.grid {
|
||||
stroke-dasharray: 2;
|
||||
stroke: #dbdbdb;
|
||||
}
|
||||
|
||||
.series {
|
||||
stroke-width: 2px;
|
||||
fill: none;
|
||||
}
|
||||
</style>
|
||||
<g transform="translate(50,15)">
|
||||
<path d="M100,0 v400m100,-400 v400m100,-400 v400m100,-400 v400m100,-400 v400m100,-400 v400"
|
||||
class="grid" transform="translate(0,0)" id="grid-v" />
|
||||
<path d="M0,100 h600m-600,100 h600m-600,100 h600m-600,100"
|
||||
class="grid" stroke-dashoffset="0" id="grid-h" />
|
||||
<path d="M-10,0 h10m-10,100 h10m-10,100 h10m-10,100 h10m-10,100 h10" class="ticks" />
|
||||
<path d="M-5,10
|
||||
h5m-5,10h5m-5,10h5m-5,10h5m-5,10h5m-5,10h5m-5,10h5m-5,10h5m-5,10h5m-5,20
|
||||
h5m-5,10h5m-5,10h5m-5,10h5m-5,10h5m-5,10h5m-5,10h5m-5,10h5m-5,10h5m-5,20
|
||||
h5m-5,10h5m-5,10h5m-5,10h5m-5,10h5m-5,10h5m-5,10h5m-5,10h5m-5,10h5m-5,20
|
||||
h5m-5,10h5m-5,10h5m-5,10h5m-5,10h5m-5,10h5m-5,10h5m-5,10h5m-5,10h5" class="ticks" />
|
||||
<g class="series">
|
||||
<path d="{ser-set}" stroke="blue" id="ser-set" /><!--M0,400L600,0-->
|
||||
<path d="{ser-act}" stroke="red" id="ser-act" /><!--M0,0L300,100L600,400-->
|
||||
</g>
|
||||
<path d="M0,0h600v400h-600Z" class="frame" />
|
||||
<g class="ylabels" transform="translate(0,3)">
|
||||
<text x="-15" y="0">400 °C</text>
|
||||
<text x="-15" y="100">300 °C</text>
|
||||
<text x="-15" y="200">200 °C</text>
|
||||
<text x="-15" y="300">100 °C</text>
|
||||
<text x="-15" y="400">0 °C</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</td>
|
||||
<td id="td-side">
|
||||
Sidebar
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<script>
|
||||
var Qi = function (x) { return document.getElementById(x) };
|
||||
function update(data) {
|
||||
if (data) {
|
||||
let rows = data.split('\x1e');
|
||||
rows.forEach(function (v) {
|
||||
let [k, va] = v.split('\x1f');
|
||||
switch (k) {
|
||||
case 'ser-set':
|
||||
Qi('ser-set').setAttribute('d', va);
|
||||
break;
|
||||
case 'ser-act':
|
||||
Qi('ser-act').setAttribute('d', va);
|
||||
break;
|
||||
case 'timeshift':
|
||||
Qi('grid-v').setAttribute('transform', 'translate(-'+(va*5)+',0)');
|
||||
Qi('grid-h').setAttribute('stroke-dashoffset', -va * 5);
|
||||
break;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
var xhr=new XMLHttpRequest();
|
||||
xhr.onreadystatechange = function () {
|
||||
if (xhr.readyState===4){
|
||||
if (xhr.status===200) {
|
||||
update(xhr.responseText);
|
||||
}
|
||||
setTimeout(update, 500);
|
||||
}
|
||||
};
|
||||
xhr.onerror = function () {
|
||||
setTimeout(update, 500);
|
||||
};
|
||||
xhr.open('GET', '/data');
|
||||
xhr.send();
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout(update, 500);
|
||||
</script>
|
||||
@@ -0,0 +1,15 @@
|
||||
// Generated by 'rebuild_file_tables'
|
||||
#include <stdint.h>
|
||||
#include "files_enum.h"
|
||||
|
||||
extern const uint8_t _binary_favicon_ico_start[];
|
||||
extern const uint8_t _binary_favicon_ico_end[];
|
||||
extern const uint8_t _binary_index_html_start[];
|
||||
extern const uint8_t _binary_index_html_end[];
|
||||
|
||||
const struct embedded_file_info EMBEDDED_FILE_LOOKUP[] = {
|
||||
[FILE_FAVICON_ICO] = {_binary_favicon_ico_start, _binary_favicon_ico_end, "favicon.ico", "image/vnd.microsoft.icon"},
|
||||
[FILE_INDEX_HTML] = {_binary_index_html_start, _binary_index_html_end, "index.html", "text/html"},
|
||||
};
|
||||
|
||||
const size_t EMBEDDED_FILE_LOOKUP_LEN = 2;
|
||||
@@ -0,0 +1,13 @@
|
||||
// Generated by 'rebuild_file_tables'
|
||||
|
||||
#ifndef _EMBEDDED_FILES_ENUM_H
|
||||
#define _EMBEDDED_FILES_ENUM_H
|
||||
|
||||
#include "fileserver/embedded_files.h"
|
||||
|
||||
enum embedded_file_id {
|
||||
FILE_FAVICON_ICO = 0,
|
||||
FILE_INDEX_HTML = 1
|
||||
};
|
||||
|
||||
#endif // _EMBEDDED_FILES_ENUM_H
|
||||
Executable
+163
@@ -0,0 +1,163 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
// This script rebuilds the static files enum, extern symbols pointing to the embedded byte buffers,
|
||||
// and the look-up structs table. To add more files, simply add them in the 'files' directory.
|
||||
|
||||
// Note that all files will be accessible by the webserver, unless you filter them in embedded_files.c.
|
||||
|
||||
|
||||
// List all files
|
||||
$files = scandir(__DIR__.'/embed');
|
||||
|
||||
$files = array_filter(array_map(function ($f) {
|
||||
if (!is_file(__DIR__.'/embed/'.$f)) return null;
|
||||
if (preg_match('/^\.|\.kate-swp|\.bak$|~$|\.sh|\.ignore\..*$/', $f)) return null;
|
||||
|
||||
echo "Found: $f\n";
|
||||
return $f;
|
||||
}, $files));
|
||||
|
||||
sort($files);
|
||||
|
||||
$formatted = array_filter(array_map(function ($f) {
|
||||
return "\"files/embed/$f\"";
|
||||
}, $files));
|
||||
|
||||
$cmake = file_get_contents(__DIR__.'/../CMakeLists.txt');
|
||||
|
||||
$cmake = preg_replace('/#begin staticfiles\n.*#end staticfiles/s',
|
||||
"#begin staticfiles\n".
|
||||
"# generated by rebuild_file_tables\n".
|
||||
"set(COMPONENT_EMBED_FILES\n ".
|
||||
implode("\n ", $formatted) . ")\n".
|
||||
"#end staticfiles",
|
||||
$cmake);
|
||||
|
||||
file_put_contents(__DIR__.'/../CMakeLists.txt', $cmake);
|
||||
|
||||
|
||||
// Generate a list of files
|
||||
|
||||
$num = 0;
|
||||
$enum_keys = array_map(function ($f) use(&$num) {
|
||||
$a = preg_replace("/[^A-Z0-9_]+/", "_", strtoupper($f));
|
||||
return 'FILE_'. $a.' = '.($num++);
|
||||
}, $files);
|
||||
|
||||
$keylist = implode(",\n ", $enum_keys);
|
||||
|
||||
$struct_array = [];
|
||||
|
||||
$externs = array_map(function ($f) use (&$struct_array) {
|
||||
$a = preg_replace("/[^A-Z0-9_]+/", "_", strtoupper($f));
|
||||
|
||||
$start = '_binary_'. strtolower($a).'_start';
|
||||
$end = '_binary_'. strtolower($a).'_end';
|
||||
|
||||
static $mimes = array(
|
||||
'txt' => 'text/plain',
|
||||
'htm' => 'text/html',
|
||||
'html' => 'text/html',
|
||||
'php' => 'text/html',
|
||||
'css' => 'text/css',
|
||||
'js' => 'application/javascript',
|
||||
'json' => 'application/json',
|
||||
'xml' => 'application/xml',
|
||||
'swf' => 'application/x-shockwave-flash',
|
||||
'flv' => 'video/x-flv',
|
||||
|
||||
'pem' => 'application/x-pem-file',
|
||||
|
||||
// images
|
||||
'png' => 'image/png',
|
||||
'jpe' => 'image/jpeg',
|
||||
'jpeg' => 'image/jpeg',
|
||||
'jpg' => 'image/jpeg',
|
||||
'gif' => 'image/gif',
|
||||
'bmp' => 'image/bmp',
|
||||
'ico' => 'image/vnd.microsoft.icon',
|
||||
'tiff' => 'image/tiff',
|
||||
'tif' => 'image/tiff',
|
||||
'svg' => 'image/svg+xml',
|
||||
'svgz' => 'image/svg+xml',
|
||||
|
||||
// archives
|
||||
'zip' => 'application/zip',
|
||||
'rar' => 'application/x-rar-compressed',
|
||||
'exe' => 'application/x-msdownload',
|
||||
'msi' => 'application/x-msdownload',
|
||||
'cab' => 'application/vnd.ms-cab-compressed',
|
||||
|
||||
// audio/video
|
||||
'mp3' => 'audio/mpeg',
|
||||
'qt' => 'video/quicktime',
|
||||
'mov' => 'video/quicktime',
|
||||
|
||||
// adobe
|
||||
'pdf' => 'application/pdf',
|
||||
'psd' => 'image/vnd.adobe.photoshop',
|
||||
'ai' => 'application/postscript',
|
||||
'eps' => 'application/postscript',
|
||||
'ps' => 'application/postscript',
|
||||
|
||||
// ms office
|
||||
'doc' => 'application/msword',
|
||||
'rtf' => 'application/rtf',
|
||||
'xls' => 'application/vnd.ms-excel',
|
||||
'ppt' => 'application/vnd.ms-powerpoint',
|
||||
|
||||
// open office
|
||||
'odt' => 'application/vnd.oasis.opendocument.text',
|
||||
'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
|
||||
);
|
||||
|
||||
$parts = explode('.', $f);
|
||||
$suffix = end($parts);
|
||||
$mime = $mimes[$suffix] ?? 'application/octet-stream';
|
||||
|
||||
$len = filesize('embed/'.$f);
|
||||
|
||||
$struct_array[] = "[FILE_$a] = {{$start}, {$end}, \"{$f}\", \"{$mime}\"},";
|
||||
|
||||
return
|
||||
'extern const uint8_t '.$start.'[];'."\n".
|
||||
'extern const uint8_t '.$end.'[];';
|
||||
}, $files);
|
||||
|
||||
$externlist = implode("\n", $externs);
|
||||
$structlist = implode("\n ", $struct_array);
|
||||
|
||||
|
||||
file_put_contents('files_enum.h', <<<FILE
|
||||
// Generated by 'rebuild_file_tables'
|
||||
|
||||
#ifndef _EMBEDDED_FILES_ENUM_H
|
||||
#define _EMBEDDED_FILES_ENUM_H
|
||||
|
||||
#include "fileserver/embedded_files.h"
|
||||
|
||||
enum embedded_file_id {
|
||||
$keylist
|
||||
};
|
||||
|
||||
#endif // _EMBEDDED_FILES_ENUM_H
|
||||
|
||||
FILE
|
||||
);
|
||||
|
||||
$files_count = count($struct_array);
|
||||
file_put_contents("files_enum.c", <<<FILE
|
||||
// Generated by 'rebuild_file_tables'
|
||||
#include <stdint.h>
|
||||
#include "files_enum.h"
|
||||
|
||||
$externlist
|
||||
|
||||
const struct embedded_file_info EMBEDDED_FILE_LOOKUP[] = {
|
||||
$structlist
|
||||
};
|
||||
|
||||
const size_t EMBEDDED_FILE_LOOKUP_LEN = $files_count;
|
||||
|
||||
FILE
|
||||
);
|
||||
@@ -0,0 +1,70 @@
|
||||
#include <soc/timer_group_struct.h>
|
||||
#include <soc/timer_group_reg.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_wifi.h"
|
||||
#include "utils.h"
|
||||
|
||||
static const char * TAG = "utils.c";
|
||||
|
||||
static void recon_internal(void);
|
||||
|
||||
/**
|
||||
* Attempt to reconnect to AP if we have SSID stored
|
||||
*/
|
||||
void try_reconn_if_have_wifi_creds(void)
|
||||
{
|
||||
recon_internal();
|
||||
}
|
||||
|
||||
static volatile bool recurse = false;
|
||||
|
||||
static void recon_internal(void)
|
||||
{
|
||||
if (recurse) {
|
||||
ESP_LOGE(TAG, "Stopping recursion!");
|
||||
}
|
||||
recurse = true;
|
||||
|
||||
wifi_config_t wificonf;
|
||||
|
||||
// try to connect if we have a saved config
|
||||
ESP_ERROR_CHECK(esp_wifi_get_config(WIFI_IF_STA, &wificonf));
|
||||
|
||||
if (wificonf.sta.ssid[0]) {
|
||||
ESP_LOGI(TAG, "(Re)connecting using saved STA creds");
|
||||
ESP_ERROR_CHECK(esp_wifi_connect());
|
||||
} else {
|
||||
ESP_LOGI(TAG, "No WiFi creds, no (re)conn");
|
||||
}
|
||||
|
||||
recurse = false;
|
||||
}
|
||||
|
||||
|
||||
esp_err_t nvs_get_bool(nvs_handle handle, const char* key, bool* out_value)
|
||||
{
|
||||
uint8_t x = (uint8_t) *out_value;
|
||||
esp_err_t rv = nvs_get_u8(handle, key, &x);
|
||||
if (rv == ESP_OK) {
|
||||
*out_value = (bool)x;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
esp_err_t nvs_set_bool(nvs_handle handle, const char* key, bool value)
|
||||
{
|
||||
return nvs_set_u8(handle, key, (uint8_t) value);
|
||||
}
|
||||
|
||||
void feed_all_dogs(void)
|
||||
{
|
||||
TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE;
|
||||
TIMERG0.wdt_feed=1;
|
||||
TIMERG0.wdt_wprotect=0;
|
||||
|
||||
TIMERG1.wdt_wprotect=TIMG_WDT_WKEY_VALUE;
|
||||
TIMERG1.wdt_config2=CONFIG_INT_WDT_TIMEOUT_MS*2; //Set timeout before interrupt
|
||||
TIMERG1.wdt_config3=CONFIG_INT_WDT_TIMEOUT_MS*4; //Set timeout before reset
|
||||
TIMERG1.wdt_feed=1;
|
||||
TIMERG1.wdt_wprotect=0;
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* Utilities and helpers
|
||||
*/
|
||||
|
||||
#ifndef CSPEMU_UTILS_H
|
||||
#define CSPEMU_UTILS_H
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include <string.h>
|
||||
#include <nvs.h>
|
||||
#include <common_utils/utils.h>
|
||||
|
||||
/** Error check macro for FreeRTOS status codes */
|
||||
#define RTOS_ERROR_CHECK(code) if (pdPASS != (code)) ESP_ERROR_CHECK(ESP_FAIL);
|
||||
|
||||
/** Error check macro for NULL return value */
|
||||
#define NULL_CHECK(code) if (NULL == (code)) ESP_ERROR_CHECK(ESP_FAIL);
|
||||
|
||||
/** Error check macro for CSP status codes */
|
||||
#define CSP_ERROR_CHECK(code) if (CSP_ERR_NONE != (code)) ESP_ERROR_CHECK(ESP_FAIL);
|
||||
|
||||
/**
|
||||
* Reconnect to WiFi if there are saved credentials.
|
||||
* Called from the main event group handler.
|
||||
*/
|
||||
void try_reconn_if_have_wifi_creds(void);
|
||||
|
||||
/**
|
||||
* Helper to retrieve a bool value from the NVS. Internally uses 'u8'
|
||||
*
|
||||
* @param handle - NVS storage
|
||||
* @param key
|
||||
* @param out_value
|
||||
* @return success
|
||||
*/
|
||||
esp_err_t nvs_get_bool(nvs_handle handle, const char* key, bool* out_value);
|
||||
|
||||
/**
|
||||
* Helper to store a bool into the NVS. Internally uses 'u8'
|
||||
*
|
||||
* @param handle - NVS storage
|
||||
* @param key
|
||||
* @param value
|
||||
* @return success
|
||||
*/
|
||||
esp_err_t nvs_set_bool(nvs_handle handle, const char* key, bool value);
|
||||
|
||||
/**
|
||||
* Feed watchdogs (inside a busy wait loop)
|
||||
*/
|
||||
void feed_all_dogs(void);
|
||||
|
||||
/**
|
||||
* @brief malloc() and snprintf() combined
|
||||
* @attention DO NOT use in if/for/do etc without braces.
|
||||
*
|
||||
* The caller is responsible for disposing of the allocated string afterwards.
|
||||
*/
|
||||
#define malloc_sprintf(var, n, format, ...) \
|
||||
char *var = malloc(n); \
|
||||
if (!var) assert(0); \
|
||||
snprintf(var, n, format, ##__VA_ARGS__);
|
||||
|
||||
/**
|
||||
* Generate externs for an embedded file.
|
||||
* Variables {varname} and {varname}_end will be produced.
|
||||
*/
|
||||
#define efile(varname, filename) \
|
||||
extern const char varname[] asm("_binary_"filename"_start"); \
|
||||
extern const char varname##_end[] asm("_binary_"filename"_end");
|
||||
|
||||
/** Get embedded file size (must be declared with efile() first) */
|
||||
#define efsize(varname) (varname##_end - varname)
|
||||
|
||||
#endif //CSPEMU_UTILS_H
|
||||
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* TODO file description
|
||||
*
|
||||
* Created on 2020/01/06.
|
||||
*/
|
||||
|
||||
#ifndef REFLOWER_VERSION_H
|
||||
#define REFLOWER_VERSION_H
|
||||
|
||||
#define APP_VERSION "0.1"
|
||||
|
||||
#endif //REFLOWER_VERSION_H
|
||||
@@ -0,0 +1,212 @@
|
||||
#include <esp_log.h>
|
||||
#include <esp_err.h>
|
||||
|
||||
#include <fileserver/token_subs.h>
|
||||
#include <httpd_utils/captive.h>
|
||||
|
||||
#include "websrv.h"
|
||||
#include "esp_http_server.h"
|
||||
#include "utils.h"
|
||||
#include "files/files_enum.h"
|
||||
#include "version.h"
|
||||
#include "analog.h"
|
||||
|
||||
static const char *TAG="websrv";
|
||||
|
||||
static httpd_handle_t s_hServer = NULL;
|
||||
|
||||
// Embedded files (must also be listed in CMakeLists.txt as COMPONENT_EMBED_TXTFILES)
|
||||
efile(index_file, "index_html");
|
||||
|
||||
static struct tpl_kv_list build_index_replacements_kv(void)
|
||||
{
|
||||
// char name[TPL_KV_KEY_LEN];
|
||||
struct tpl_kv_list kv = tpl_kv_init();
|
||||
tpl_kv_add(&kv, "version", APP_VERSION);
|
||||
|
||||
size_t pcap1 = 300;
|
||||
size_t pcap2 = 300;
|
||||
char *path1 = malloc(pcap1);
|
||||
char *path2 = malloc(pcap2);
|
||||
assert(path1);
|
||||
assert(path2);
|
||||
path1[0] = 0;
|
||||
path2[0] = 0;
|
||||
|
||||
#define SCRATCH_SIZE 15
|
||||
char scratch1[SCRATCH_SIZE];
|
||||
char scratch2[SCRATCH_SIZE];
|
||||
|
||||
bool last_empty = true; // first is move
|
||||
bool suc;
|
||||
for (int i = 0; i < REG_HISTORY_LEN; i++) {
|
||||
int x = i*5;
|
||||
if (reg_meas_history[i] == 0) {
|
||||
snprintf(scratch1, SCRATCH_SIZE, "M%d,0", x);
|
||||
snprintf(scratch2, SCRATCH_SIZE, "M%d,0", x);
|
||||
last_empty = true;
|
||||
} else {
|
||||
snprintf(scratch1, SCRATCH_SIZE, "%c%d,%d", last_empty ? 'M' : 'L', x, (int)(400 - reg_meas_history[i]));
|
||||
snprintf(scratch2, SCRATCH_SIZE, "%c%d,%d", last_empty ? 'M' : 'L', x, (int)(400 - reg_tset_history[i]));
|
||||
last_empty = false;
|
||||
}
|
||||
|
||||
suc = append_realloc(&path1, &pcap1, scratch1);
|
||||
assert(suc);
|
||||
|
||||
suc = append_realloc(&path2, &pcap2, scratch2);
|
||||
assert(suc);
|
||||
}
|
||||
tpl_kv_add_heapstr(&kv, "ser-act", path1);
|
||||
tpl_kv_add_heapstr(&kv, "ser-set", path2);
|
||||
|
||||
tpl_kv_add_int(&kv, "timeshift", history_counter);
|
||||
|
||||
#undef SCRATCH_SIZE
|
||||
|
||||
return kv;
|
||||
}
|
||||
|
||||
/* Main page */
|
||||
static esp_err_t handler_index(httpd_req_t *req)
|
||||
{
|
||||
struct tpl_kv_list kv = build_index_replacements_kv();
|
||||
|
||||
esp_err_t suc = httpd_send_template_file(req, FILE_INDEX_HTML, tpl_kv_replacer, &kv, 0);
|
||||
tpl_kv_free(&kv);
|
||||
return suc;
|
||||
}
|
||||
|
||||
/* Update XHR for new index page data */
|
||||
static esp_err_t handler_update(httpd_req_t *req)
|
||||
{
|
||||
struct tpl_kv_list kv = build_index_replacements_kv();
|
||||
|
||||
esp_err_t suc = tpl_kv_send_as_ascii_map(req, &kv);
|
||||
tpl_kv_free(&kv);
|
||||
return suc;
|
||||
}
|
||||
|
||||
/* Set a param */
|
||||
static esp_err_t handler_set(httpd_req_t *req)
|
||||
{
|
||||
char buf[64];
|
||||
int n = httpd_req_recv(req, buf, 63);
|
||||
if (n < 0) {
|
||||
ESP_LOGW(TAG, "rx er");
|
||||
goto err;
|
||||
}
|
||||
buf[n]=0;
|
||||
|
||||
char keybuf[20];
|
||||
char valbuf[20];
|
||||
if (ESP_OK != httpd_query_key_value(buf, "key", keybuf, 20)) goto err;
|
||||
if (ESP_OK != httpd_query_key_value(buf, "value", valbuf, 20)) goto err;
|
||||
|
||||
// TODO handle
|
||||
|
||||
return httpd_resp_send(req, NULL, 0);
|
||||
|
||||
err:
|
||||
return httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, NULL);
|
||||
}
|
||||
|
||||
/* Request emulator reboot */
|
||||
static esp_err_t handler_reboot(httpd_req_t *req)
|
||||
{
|
||||
httpd_resp_send(req, "<!DOCTYPE html><html><head>"
|
||||
"<meta http-equiv=\"refresh\" content=\"10; url=/\">"
|
||||
"</head>"
|
||||
"<body>"
|
||||
"Reboot requested. Reloading in 10 seconds.<br>"
|
||||
"<a href=\"/\">Try now.</a>", -1);
|
||||
|
||||
ESP_LOGI(TAG, "Restarting ESP...");
|
||||
esp_restart();
|
||||
}
|
||||
|
||||
/* An HTTP GET handler */
|
||||
static esp_err_t handler_staticfiles(httpd_req_t *r)
|
||||
{
|
||||
const struct embedded_file_info *file;
|
||||
const char *fname = r->user_ctx;
|
||||
enum file_access_level access = FILE_ACCESS_PROTECTED;
|
||||
|
||||
// wildcard files must be public
|
||||
if (fname == NULL) {
|
||||
fname = r->uri + 1; // URI always starts with slash, but we dont want a slash in the file name
|
||||
access = FILE_ACCESS_PUBLIC;
|
||||
}
|
||||
|
||||
#ifdef USE_CAPTIVE_PORTAL
|
||||
// First check if this is a phone taken here by the captive portal
|
||||
esp_err_t rv = httpd_captive_redirect(r);
|
||||
if (rv != ESP_ERR_NOT_FOUND) return rv;
|
||||
#endif
|
||||
|
||||
if (ESP_OK != www_get_static_file(fname, access, &file)) {
|
||||
ESP_LOGW(TAG, "File not found: %s", fname);
|
||||
return httpd_resp_send_404(r);
|
||||
}
|
||||
else {
|
||||
if (streq(file->mime, "text/html")) {
|
||||
// using the template func to allow includes
|
||||
return httpd_send_template_file_struct(r, file, /*replacer*/NULL, /*ctx*/NULL, /*opts*/0);
|
||||
} else {
|
||||
return httpd_send_static_file_struct(r, file, TPL_ESCAPE_NONE, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const httpd_uri_t routes[] = {
|
||||
{
|
||||
.uri = "/",
|
||||
.method = HTTP_GET,
|
||||
.handler = handler_index,
|
||||
},
|
||||
{
|
||||
.uri = "/data",
|
||||
.method = HTTP_GET,
|
||||
.handler = handler_update,
|
||||
},
|
||||
{
|
||||
.uri = "/set",
|
||||
.method = HTTP_POST,
|
||||
.handler = handler_set,
|
||||
},
|
||||
{
|
||||
.uri = "/reboot",
|
||||
.method = HTTP_GET,
|
||||
.handler = handler_reboot,
|
||||
},
|
||||
{
|
||||
.uri = "*", // any file except protected (e.g. not HTML, PEM etc)
|
||||
.method = HTTP_GET,
|
||||
.handler = handler_staticfiles,
|
||||
},
|
||||
};
|
||||
|
||||
esp_err_t websrv_init(void)
|
||||
{
|
||||
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
|
||||
config.stack_size = 6000;
|
||||
|
||||
config.uri_match_fn = httpd_uri_match_wildcard;
|
||||
config.lru_purge_enable = true;
|
||||
|
||||
ESP_LOGI(TAG, "Starting HTTP server on port: '%d'", config.server_port);
|
||||
|
||||
esp_err_t suc = httpd_start(&s_hServer, &config);
|
||||
if (suc == ESP_OK) {
|
||||
ESP_LOGI(TAG, "HTTP server started");
|
||||
|
||||
for (int i = 0; i < sizeof(routes) / sizeof(httpd_uri_t); i++) {
|
||||
httpd_register_uri_handler(s_hServer, &routes[i]);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
ESP_LOGE(TAG, "Error starting server!");
|
||||
return suc;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Integrated webserver
|
||||
*
|
||||
* Created on 2019/07/13.
|
||||
*/
|
||||
|
||||
#ifndef CSPEMU_WEBSRV_H
|
||||
#define CSPEMU_WEBSRV_H
|
||||
|
||||
#include <esp_err.h>
|
||||
|
||||
esp_err_t websrv_init(void);
|
||||
|
||||
#endif //CSPEMU_WEBSRV_H
|
||||
Reference in New Issue
Block a user