Former-commit-id: 6c97ad16f16e48f56fea7a007d5d8136c51a268c
master
Ondřej Hruška 9 years ago
parent 2357472b4c
commit f77caa03f2
  1. 7
      _web-build_do.sh
  2. 161
      esp_f105motor.pro
  3. 167
      esp_meas.pro
  4. 2
      html/css/app.css
  5. 4
      html/js/all.js
  6. 77
      html/pages/about.tpl
  7. 102
      html/pages/fft.html
  8. 49
      html/pages/home.tpl
  9. 102
      html/pages/monitoring.tpl
  10. 79
      html/pages/sgm.html
  11. 128
      html/pages/status.tpl
  12. 91
      html/pages/wfm.html
  13. 6
      html/pages/wifi.tpl
  14. 12
      html_src/_start.php
  15. 2
      html_src/css/app.css
  16. 11
      html_src/gulpfile.js
  17. 2
      html_src/js-src/lib/chibi.js
  18. 285
      html_src/js-src/lib/lodash.custom.js
  19. 11
      html_src/js-src/page_home.js
  20. 91
      html_src/js-src/page_mon.js
  21. 356
      html_src/js-src/page_spectrogram.js
  22. 88
      html_src/js-src/page_status.js
  23. 271
      html_src/js-src/page_waveform.js
  24. 4
      html_src/js/all.js
  25. 1
      html_src/node_modules.tar.REMOVED.git-id
  26. 48
      html_src/page_about.php
  27. 73
      html_src/page_fft.php
  28. 21
      html_src/page_home.php
  29. 73
      html_src/page_monitoring.php
  30. 50
      html_src/page_spectrogram.php
  31. 99
      html_src/page_status.php
  32. 62
      html_src/page_waveform.php
  33. 3
      html_src/page_wifi.php
  34. 8
      html_src/sass/app.scss
  35. 4
      html_src/sass/layout/_menu.scss
  36. 17
      html_src/sass/pages/_about.scss
  37. 24
      html_src/sass/pages/_home.scss
  38. 101
      html_src/sass/pages/_wfm.scss
  39. 71
      html_src/x_page_layout.php
  40. 8
      libesphttpd/core/auth.c
  41. 2
      libesphttpd/include/auth.h
  42. 2
      libesphttpd/include/esp8266.h
  43. 2
      libesphttpd/include/logging.h
  44. 1
      libesphttpd/include/platform.h
  45. 9
      user/datalink.h
  46. 83
      user/page_about.c
  47. 23
      user/page_home.c
  48. 2
      user/page_home.h
  49. 189
      user/page_monitoring.c
  50. 15
      user/page_monitoring.h
  51. 159
      user/page_status.c
  52. 8
      user/page_status.h
  53. 191
      user/page_waveform.c
  54. 10
      user/page_waveform.h
  55. 40
      user/pers_cfg.c
  56. 23
      user/pers_cfg.h
  57. 259
      user/reporting.c
  58. 52
      user/reporting.h
  59. 33
      user/routes.c
  60. 277
      user/sampling.c
  61. 75
      user/sampling.h
  62. 2
      user/sbmp_config.h
  63. 13
      user/user_main.c
  64. 10
      user/wificontrol.c

@ -23,10 +23,5 @@ find "$BLDDIR" -name "*.map" -delete
mkdir -p "$BLDDIR/pages" mkdir -p "$BLDDIR/pages"
php "$SRCDIR/page_status.php" > "$BLDDIR/pages/status.tpl" php "$SRCDIR/page_home.php" > "$BLDDIR/pages/home.tpl"
php "$SRCDIR/page_about.php" > "$BLDDIR/pages/about.tpl"
php "$SRCDIR/page_wifi.php" > "$BLDDIR/pages/wifi.tpl" php "$SRCDIR/page_wifi.php" > "$BLDDIR/pages/wifi.tpl"
php "$SRCDIR/page_monitoring.php" > "$BLDDIR/pages/monitoring.tpl"
php "$SRCDIR/page_waveform.php" > "$BLDDIR/pages/wfm.html" # no substitutions, .html allows to gzip it.
php "$SRCDIR/page_fft.php" > "$BLDDIR/pages/fft.html" # same
php "$SRCDIR/page_spectrogram.php" > "$BLDDIR/pages/sgm.html" # same

@ -0,0 +1,161 @@
TEMPLATE = app
CONFIG += console
CONFIG -= app_bundle
CONFIG -= qt
DEFINES = ESPFS_HEATSHRINK HTTPD_MAX_CONNECTIONS=4 __ets__
INCLUDEPATH = . \
esp_iot_sdk_v1.5.2/include \
include \
user \
esphttpclient \
libesphttpd/include \
libesphttpd/espfs \
libesphttpd/core \
libesphttpd/lib/heatshrink \
sbmp/library \
/home/ondra/devel/esp/sdk/esp-open-sdk/xtensa-lx106-elf/xtensa-lx106-elf/include
SOURCES += \
libesphttpd/core/auth.c \
libesphttpd/core/base64.c \
libesphttpd/core/httpd-freertos.c \
libesphttpd/core/httpd-nonos.c \
libesphttpd/core/httpd.c \
libesphttpd/core/httpdespfs.c \
libesphttpd/core/sha1.c \
libesphttpd/espfs/espfstest/main.c \
libesphttpd/espfs/mkespfsimage/heatshrink_encoder.c \
libesphttpd/espfs/mkespfsimage/main.c \
libesphttpd/espfs/espfs.c \
libesphttpd/espfs/heatshrink_decoder.c \
libesphttpd/lib/heatshrink/heatshrink.c \
libesphttpd/lib/heatshrink/heatshrink_decoder.c \
libesphttpd/lib/heatshrink/heatshrink_encoder.c \
libesphttpd/lib/heatshrink/test_heatshrink_dynamic.c \
libesphttpd/lib/heatshrink/test_heatshrink_dynamic_theft.c \
libesphttpd/lib/heatshrink/test_heatshrink_static.c \
libesphttpd/mkupgimg/mkupgimg.c \
libesphttpd/util/captdns.c \
libesphttpd/util/cgiflash.c \
libesphttpd/util/cgiwebsocket.c \
libesphttpd/util/cgiwifi.c \
user/cgi-test.c \
user/io.c \
user/user_main.c \
user/uart_driver.c \
sbmp/library/crc32.c \
sbmp/library/sbmp_checksum.c \
sbmp/library/sbmp_datagram.c \
sbmp/library/sbmp_frame.c \
sbmp/library/sbmp_session.c \
sbmp/library/sbmp_bulk.c \
user/datalink.c \
user/serial.c \
user/uptime.c \
sbmp/library/payload_parser.c \
user/sampling.c \
user/ftoa.c \
user/routes.c \
user/utils.c \
sbmp/library/payload_builder.c \
user/cgi_reset.c \
user/cgi_ping.c \
esphttpclient/test/httpclient_test.c \
esphttpclient/httpclient.c \
user/wificontrol.c \
user/page_home.c \
user/pers_cfg.c
HEADERS += \
include/uart_hw.h \
include/user_config.h \
libesphttpd/core/base64.h \
libesphttpd/core/httpd-platform.h \
libesphttpd/espfs/espfsformat.h \
libesphttpd/espfs/heatshrink_config_custom.h \
libesphttpd/include/auth.h \
libesphttpd/include/captdns.h \
libesphttpd/include/cgiflash.h \
libesphttpd/include/cgiwebsocket.h \
libesphttpd/include/cgiwifi.h \
libesphttpd/include/esp8266.h \
libesphttpd/include/espfs.h \
libesphttpd/include/httpd.h \
libesphttpd/include/httpdespfs.h \
libesphttpd/include/platform.h \
libesphttpd/include/sha1.h \
libesphttpd/include/user_config.h \
libesphttpd/include/webpages-espfs.h \
libesphttpd/lib/heatshrink/greatest.h \
libesphttpd/lib/heatshrink/heatshrink_common.h \
libesphttpd/lib/heatshrink/heatshrink_config.h \
libesphttpd/lib/heatshrink/heatshrink_decoder.h \
libesphttpd/lib/heatshrink/heatshrink_encoder.h \
user/cgi-test.h \
user/io.h \
user/uart_register.h \
esp_iot_sdk_v1.5.2/include/json/json.h \
esp_iot_sdk_v1.5.2/include/json/jsonparse.h \
esp_iot_sdk_v1.5.2/include/json/jsontree.h \
esp_iot_sdk_v1.5.2/include/airkiss.h \
esp_iot_sdk_v1.5.2/include/at_custom.h \
esp_iot_sdk_v1.5.2/include/c_types.h \
esp_iot_sdk_v1.5.2/include/eagle_soc.h \
esp_iot_sdk_v1.5.2/include/esp_sdk_ver.h \
esp_iot_sdk_v1.5.2/include/espconn.h \
esp_iot_sdk_v1.5.2/include/espnow.h \
esp_iot_sdk_v1.5.2/include/ets_sys.h \
esp_iot_sdk_v1.5.2/include/gpio.h \
esp_iot_sdk_v1.5.2/include/ip_addr.h \
esp_iot_sdk_v1.5.2/include/mem.h \
esp_iot_sdk_v1.5.2/include/mesh.h \
esp_iot_sdk_v1.5.2/include/os_type.h \
esp_iot_sdk_v1.5.2/include/osapi.h \
esp_iot_sdk_v1.5.2/include/ping.h \
esp_iot_sdk_v1.5.2/include/pwm.h \
esp_iot_sdk_v1.5.2/include/queue.h \
esp_iot_sdk_v1.5.2/include/slc_register.h \
esp_iot_sdk_v1.5.2/include/smartconfig.h \
esp_iot_sdk_v1.5.2/include/sntp.h \
esp_iot_sdk_v1.5.2/include/spi_flash.h \
esp_iot_sdk_v1.5.2/include/spi_register.h \
esp_iot_sdk_v1.5.2/include/uart_register.h \
esp_iot_sdk_v1.5.2/include/upgrade.h \
esp_iot_sdk_v1.5.2/include/user_interface.h \
user/uart_driver.h \
sbmp/library/crc32.h \
sbmp/library/sbmp.h \
sbmp/library/sbmp_checksum.h \
sbmp/library/sbmp_config.h \
sbmp/library/sbmp_datagram.h \
sbmp/library/sbmp_frame.h \
sbmp/library/sbmp_logging.h \
sbmp/library/sbmp_session.h \
sbmp/library/sbmp_bulk.h \
user/datalink.h \
user/serial.h \
libesphttpd/include/logging.h \
user/uptime.h \
sbmp/library/payload_parser.h \
user/sampling.h \
user/sbmp_config.h \
sbmp/library/sbmp_config.example.h \
user/ftoa.h \
user/routes.h \
libesphttpd/include/espmissingprotos.h \
user/utils.h \
sbmp/library/payload_builder.h \
user/fw_version.h \
user/cgi_reset.h \
user/cgi_ping.h \
esphttpclient/espmissingincludes.h \
esphttpclient/httpclient.h \
user/wificontrol.h \
user/page_home.h \
user/pers_cfg.h
DISTFILES += \
style.astylerc \
Makefile

@ -1,167 +0,0 @@
TEMPLATE = app
CONFIG += console
CONFIG -= app_bundle
CONFIG -= qt
DEFINES = ESPFS_HEATSHRINK HTTPD_MAX_CONNECTIONS=4 __ets__
INCLUDEPATH = . \
esp_iot_sdk_v1.5.2/include \
include \
user \
esphttpclient \
libesphttpd/include \
libesphttpd/espfs \
libesphttpd/core \
libesphttpd/lib/heatshrink \
sbmp/library \
/home/ondra/devel/esp/sdk/esp-open-sdk/xtensa-lx106-elf/xtensa-lx106-elf/include
SOURCES += \
libesphttpd/core/auth.c \
libesphttpd/core/base64.c \
libesphttpd/core/httpd-freertos.c \
libesphttpd/core/httpd-nonos.c \
libesphttpd/core/httpd.c \
libesphttpd/core/httpdespfs.c \
libesphttpd/core/sha1.c \
libesphttpd/espfs/espfstest/main.c \
libesphttpd/espfs/mkespfsimage/heatshrink_encoder.c \
libesphttpd/espfs/mkespfsimage/main.c \
libesphttpd/espfs/espfs.c \
libesphttpd/espfs/heatshrink_decoder.c \
libesphttpd/lib/heatshrink/heatshrink.c \
libesphttpd/lib/heatshrink/heatshrink_decoder.c \
libesphttpd/lib/heatshrink/heatshrink_encoder.c \
libesphttpd/lib/heatshrink/test_heatshrink_dynamic.c \
libesphttpd/lib/heatshrink/test_heatshrink_dynamic_theft.c \
libesphttpd/lib/heatshrink/test_heatshrink_static.c \
libesphttpd/mkupgimg/mkupgimg.c \
libesphttpd/util/captdns.c \
libesphttpd/util/cgiflash.c \
libesphttpd/util/cgiwebsocket.c \
libesphttpd/util/cgiwifi.c \
user/cgi-test.c \
user/io.c \
user/user_main.c \
user/uart_driver.c \
sbmp/library/crc32.c \
sbmp/library/sbmp_checksum.c \
sbmp/library/sbmp_datagram.c \
sbmp/library/sbmp_frame.c \
sbmp/library/sbmp_session.c \
sbmp/library/sbmp_bulk.c \
user/datalink.c \
user/serial.c \
user/uptime.c \
sbmp/library/payload_parser.c \
user/sampling.c \
user/ftoa.c \
user/routes.c \
user/page_status.c \
user/page_waveform.c \
user/utils.c \
sbmp/library/payload_builder.c \
user/page_about.c \
user/cgi_reset.c \
user/cgi_ping.c \
esphttpclient/test/httpclient_test.c \
esphttpclient/httpclient.c \
user/page_monitoring.c \
user/reporting.c \
user/wificontrol.c
HEADERS += \
include/uart_hw.h \
include/user_config.h \
libesphttpd/core/base64.h \
libesphttpd/core/httpd-platform.h \
libesphttpd/espfs/espfsformat.h \
libesphttpd/espfs/heatshrink_config_custom.h \
libesphttpd/include/auth.h \
libesphttpd/include/captdns.h \
libesphttpd/include/cgiflash.h \
libesphttpd/include/cgiwebsocket.h \
libesphttpd/include/cgiwifi.h \
libesphttpd/include/esp8266.h \
libesphttpd/include/espfs.h \
libesphttpd/include/httpd.h \
libesphttpd/include/httpdespfs.h \
libesphttpd/include/platform.h \
libesphttpd/include/sha1.h \
libesphttpd/include/user_config.h \
libesphttpd/include/webpages-espfs.h \
libesphttpd/lib/heatshrink/greatest.h \
libesphttpd/lib/heatshrink/heatshrink_common.h \
libesphttpd/lib/heatshrink/heatshrink_config.h \
libesphttpd/lib/heatshrink/heatshrink_decoder.h \
libesphttpd/lib/heatshrink/heatshrink_encoder.h \
user/cgi-test.h \
user/io.h \
user/uart_register.h \
esp_iot_sdk_v1.5.2/include/json/json.h \
esp_iot_sdk_v1.5.2/include/json/jsonparse.h \
esp_iot_sdk_v1.5.2/include/json/jsontree.h \
esp_iot_sdk_v1.5.2/include/airkiss.h \
esp_iot_sdk_v1.5.2/include/at_custom.h \
esp_iot_sdk_v1.5.2/include/c_types.h \
esp_iot_sdk_v1.5.2/include/eagle_soc.h \
esp_iot_sdk_v1.5.2/include/esp_sdk_ver.h \
esp_iot_sdk_v1.5.2/include/espconn.h \
esp_iot_sdk_v1.5.2/include/espnow.h \
esp_iot_sdk_v1.5.2/include/ets_sys.h \
esp_iot_sdk_v1.5.2/include/gpio.h \
esp_iot_sdk_v1.5.2/include/ip_addr.h \
esp_iot_sdk_v1.5.2/include/mem.h \
esp_iot_sdk_v1.5.2/include/mesh.h \
esp_iot_sdk_v1.5.2/include/os_type.h \
esp_iot_sdk_v1.5.2/include/osapi.h \
esp_iot_sdk_v1.5.2/include/ping.h \
esp_iot_sdk_v1.5.2/include/pwm.h \
esp_iot_sdk_v1.5.2/include/queue.h \
esp_iot_sdk_v1.5.2/include/slc_register.h \
esp_iot_sdk_v1.5.2/include/smartconfig.h \
esp_iot_sdk_v1.5.2/include/sntp.h \
esp_iot_sdk_v1.5.2/include/spi_flash.h \
esp_iot_sdk_v1.5.2/include/spi_register.h \
esp_iot_sdk_v1.5.2/include/uart_register.h \
esp_iot_sdk_v1.5.2/include/upgrade.h \
esp_iot_sdk_v1.5.2/include/user_interface.h \
user/uart_driver.h \
sbmp/library/crc32.h \
sbmp/library/sbmp.h \
sbmp/library/sbmp_checksum.h \
sbmp/library/sbmp_config.h \
sbmp/library/sbmp_datagram.h \
sbmp/library/sbmp_frame.h \
sbmp/library/sbmp_logging.h \
sbmp/library/sbmp_session.h \
sbmp/library/sbmp_bulk.h \
user/datalink.h \
user/serial.h \
libesphttpd/include/logging.h \
user/uptime.h \
sbmp/library/payload_parser.h \
user/sampling.h \
user/sbmp_config.h \
sbmp/library/sbmp_config.example.h \
user/ftoa.h \
user/routes.h \
user/page_status.h \
user/page_waveform.h \
libesphttpd/include/espmissingprotos.h \
user/utils.h \
sbmp/library/payload_builder.h \
user/page_about.h \
user/fw_version.h \
user/cgi_reset.h \
user/cgi_ping.h \
esphttpclient/espmissingincludes.h \
esphttpclient/httpclient.h \
user/page_monitoring.h \
user/reporting.h \
user/wificontrol.h
DISTFILES += \
style.astylerc \
Makefile

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -1,77 +0,0 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>About - Current Analyser</title>
<link href="/css/app.css" rel="stylesheet">
<script src="/js/all.js"></script>
<script>
// server root (or URL) - used for local development with remote AJAX calls
// (this needs CORS working on the target - which I added to esp-httpd)
var _root = "";
</script>
</head>
<body class="page-about">
<div id="outer">
<nav id="menu">
<div id="brand" onclick="$('#menu').toggleClass('expanded')">Current Analyser</div>
<a href="/status">Home</a><a href="/wifi">WiFi config</a><a href="/waveform">Waveform</a><a href="/fft">FFT</a><a href="/spectrogram">Spectrogram</a><a href="/monitoring">Monitoring</a><a href="/about" class="selected">About</a></nav>
<div id="content">
<img src="/img/loader.gif" alt="Loading…" id="loader">
<h1>About</h1>
<div class="Box">
<img src="/img/cvut.svg" id="logo" class="mq-tablet-min">
<h2>Current Analyser</h2>
<img src="/img/cvut.svg" id="logo2" class="mq-phone">
<p>&copy; Ondřej Hruška, 2016 &lt;<a href="mailto:ondra@ondrovo.com" target="blank">ondra@ondrovo.com</a>&gt;</p>
<p><a href="http://measure.feld.cvut.cz/" target="blank">Katedra měření, FEL ČVUT</a><br>Department of Measurement, FEE CTU</p>
</div>
<div class="Box">
<h2>Firmware</h2>
<table>
<tr>
<th>Firmware</th>
<td>v%vers_fw%, build <i>%date%</i> at <i>%time%</i></td>
</tr>
<tr>
<th>esp-httpd&nbsp;lib</th>
<td>v%vers_httpd%</td>
</tr>
<tr>
<th>SBMP&nbsp;lib</th>
<td>v%vers_sbmp%</td>
</tr>
<tr>
<th>ESP&nbsp;IoT&nbsp;SDK</th>
<td>v%vers_sdk%</td>
</tr>
<tr>
<th>STM32&nbsp;firmware</th>
<td>v%vers_stm%</td>
</tr>
</table>
<p>
The webserver was built using the great <a href="https://github.com/Spritetm/esphttpd" target="blank">esp-httpd</a>
library by Jeroen Domburg.
</p>
</div>
<div class="NotifyMsg hidden" id="notif"></div>
</div><!-- content -->
</div><!-- outer -->
</body>
</html>

@ -1,102 +0,0 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>FFT - Current Analyser</title>
<link href="/css/app.css" rel="stylesheet">
<script src="/js/all.js"></script>
<script>
// server root (or URL) - used for local development with remote AJAX calls
// (this needs CORS working on the target - which I added to esp-httpd)
var _root = "";
</script>
</head>
<body class="page-fft">
<div id="outer">
<nav id="menu">
<div id="brand" onclick="$('#menu').toggleClass('expanded')">Current Analyser</div>
<a href="/status">Home</a><a href="/wifi">WiFi config</a><a href="/waveform">Waveform</a><a href="/fft" class="selected">FFT</a><a href="/spectrogram">Spectrogram</a><a href="/monitoring">Monitoring</a><a href="/about">About</a></nav>
<div id="content">
<img src="/img/loader.gif" alt="Loading…" id="loader">
<h1>FFT</h1>
<div class="Box center" id="samp-ctrl">
<div>
<label for="count">Bins</label>
<label for="count" class="select-wrap">
<select name="count" id="count">
<option value="16">8
<option value="32">16
<option value="64">32
<option value="128">64
<option value="256">128
<option value="512">256
<option value="1024">512
<option value="2048" selected>1024
</select>
</label>
</div>
<div>
<label for="freq">f<sub>bw</sub> <span class="mq-normal-min nb">=</span><span class="mq-tablet-max nb">(Hz)</span></label>
<input id="freq" type="number" value="2048">
<span class="mq-normal-min">Hz</span>
</div>
<div>
<a id="load" class="button btn-green">Load</a>
</div>
</div>
<div class="Box medium chartbox">
<div id="chart" class="ct-chart ct-wide ct-with-area"></div>
<div class="stats invis">
<table>
<tr>
<th>Samples</th>
<td id="stat-count"></td>
</tr>
<tr>
<th>f<sub>s</sub></th>
<td id="stat-f-s"></td>
</tr>
<tr>
<th>I<sub>peak</sub></th>
<td id="stat-i-peak"></td>
</tr>
<tr>
<th>I<sub>RMS</sub></th>
<td id="stat-i-rms"></td>
</tr>
</table>
<div class="ar"><!-- auto reload -->
<input type="number" id="ar-time" step="100" value="1000" min="0">&nbsp;ms
<input type="button" id="ar-btn" class="btn-blue narrow" value="Auto">
</div>
</div>
</div>
<div class="Box chartexport hidden">
Copy:
<a data-sep="space">1·2·3</a>
<a data-sep="comma">1,2,3</a>
<a data-sep="newline">1↵2↵3</a>
<a data-sep="csv">CSV</a>
<br>
<textarea id="copybox" class="hidden" readonly onfocus="this.select();" onmouseup="return false"></textarea>
</div>
<script>
$().ready(page_waveform.init('fft'));
</script>
<div class="NotifyMsg hidden" id="notif"></div>
</div><!-- content -->
</div><!-- outer -->
</body>
</html>

@ -0,0 +1,49 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Home - WiFi Demo</title>
<link href="/css/app.css" rel="stylesheet">
<script src="/js/all.js"></script>
<script>
// server root (or URL) - used for local development with remote AJAX calls
// (this needs CORS working on the target - which I added to esp-httpd)
var _root = "";
</script>
</head>
<body class="page-home">
<div id="outer">
<nav id="menu">
<div id="brand" onclick="$('#menu').toggleClass('expanded')">WiFi Demo</div>
<a href="/home" class="selected">Home</a><a href="/wifi">WiFi config</a></nav>
<div id="content">
<img src="/img/loader.gif" alt="Loading…" id="loader">
<h1>Home</h1>
<div class="Box">
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusamus cum eius molestias nesciunt nihil sequi? Laboriosam molestiae nesciunt
quis! Aut eius esse in laudantium obcaecati possimus quis repudiandae tenetur velit.</p>
</div>
<div class="Box">
<h2>Firmware</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusamus cum eius molestias nesciunt nihil sequi? Laboriosam molestiae nesciunt
quis! Aut eius esse in laudantium obcaecati possimus quis repudiandae tenetur velit.</p>
</div>
<script>
$().ready(page_home.init);
</script>
<div class="NotifyMsg hidden" id="notif"></div>
</div><!-- content -->
</div><!-- outer -->
</body>
</html>

@ -1,102 +0,0 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Monitoring - Current Analyser</title>
<link href="/css/app.css" rel="stylesheet">
<script src="/js/all.js"></script>
<script>
// server root (or URL) - used for local development with remote AJAX calls
// (this needs CORS working on the target - which I added to esp-httpd)
var _root = "";
</script>
</head>
<body class="page-monitoring">
<div id="outer">
<nav id="menu">
<div id="brand" onclick="$('#menu').toggleClass('expanded')">Current Analyser</div>
<a href="/status">Home</a><a href="/wifi">WiFi config</a><a href="/waveform">Waveform</a><a href="/fft">FFT</a><a href="/spectrogram">Spectrogram</a><a href="/monitoring" class="selected">Monitoring</a><a href="/about">About</a></nav>
<div id="content">
<img src="/img/loader.gif" alt="Loading…" id="loader">
<h1>Monitoring &amp; Reporting</h1>
<div class="Box">
<h2>Status</h2>
<table>
<tr>
<th>Reference:</th>
<td>
<span id="hasref" class="Valfield">%refStored%</span>
<a onclick="page_mon.captureRef()" class="button btn-green">Capture</a>
</td>
</tr>
<tr>
<th>Status:</th>
<td>
<span class="Valfield" style="vertical-align:middle;">
Δ = <span id="actual-dev">%curDeviation%</span><br>
I<sub>RMS</sub> = <span id="actual-rms">%curRMS%</span>&nbsp;mA
</span>
<a onclick="page_mon.compareNow()" class="button btn-blue">Measure</a>
</td>
</tr>
</table>
</div>
<div class="Box">
<h2>Reporting</h2>
<form action="/mon/config" method="POST">
<table>
<tr>
<th><label for="rep-on">Reporting:</label></th>
<td>
<input type="checkbox" id="rep-on" name="enabled" value="1" %rep_en%><!--
-->&nbsp;<label for="rep-on">enabled</label>
</td>
</tr>
<tr>
<th><label for="rep-interval">Interval:</label></th>
<td>
<input type="number" name="interval" id="rep-interval" style="max-width: 10em" value="%repInterval%"><!--
-->&nbsp;seconds
</td>
</tr>
<tr>
<th>Service:</th>
<td>
<input type="radio" name="service" value="xv" id="rep-svc-xv" %svc_xv%>&nbsp;<label for="rep-svc-xv">Xively</label>&nbsp;
<input type="radio" name="service" value="ts" id="rep-svc-ts" %svc_ts%>&nbsp;<label for="rep-svc-ts">ThingSpeak</label>
</td>
</tr>
<tr>
<th><label for="rep-key">API key:</label></th>
<td><input type="text" name="key" id="rep-key" value="%repKey%"></td>
</tr>
<tr class="xv-only">
<th><label for="rep-feed">Feed ID:</label></th>
<td><input type="text" name="feed" id="rep-feed" value="%repFeed%"></td>
</tr>
<tr>
<th>&nbsp;</th>
<td><input type="submit" value="Save changes"></td>
</tr>
</table>
</form>
</div>
<script>
$().ready(page_mon.init);
</script>
<div class="NotifyMsg hidden" id="notif"></div>
</div><!-- content -->
</div><!-- outer -->
</body>
</html>

@ -1,79 +0,0 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Spectrogram - Current Analyser</title>
<link href="/css/app.css" rel="stylesheet">
<script src="/js/all.js"></script>
<script>
// server root (or URL) - used for local development with remote AJAX calls
// (this needs CORS working on the target - which I added to esp-httpd)
var _root = "";
</script>
</head>
<body class="page-spectrogram">
<div id="outer">
<nav id="menu">
<div id="brand" onclick="$('#menu').toggleClass('expanded')">Current Analyser</div>
<a href="/status">Home</a><a href="/wifi">WiFi config</a><a href="/waveform">Waveform</a><a href="/fft">FFT</a><a href="/spectrogram" class="selected">Spectrogram</a><a href="/monitoring">Monitoring</a><a href="/about">About</a></nav>
<div id="content">
<img src="/img/loader.gif" alt="Loading…" id="loader">
<h1>Spectrogram</h1>
<div class="Box center" id="samp-ctrl">
<div>
<label for="count">Bins</label>
<label for="count" class="select-wrap">
<select name="count" id="count">
<option value="16">8
<option value="32">16
<option value="64">32
<option value="128">64
<option value="256">128
<option value="512">256
<option value="1024" selected>512
<option value="2048">1024
</select>
</label>
</div>
<div id="tile-cfg">
<label for="tile-x">Tile</label>
<input id="tile-x" type="number" min=1 step=1 value=4>
×
<input id="tile-y" type="number" min=1 step=1 value=1>
</div>
<div>
<label for="freq">f<sub>bw</sub> <span class="mq-normal-min nb">=</span><span class="mq-tablet-max nb">(Hz)</span></label>
<input id="freq" type="number" value="2048">
<span class="mq-normal-min">Hz</span>
</div>
<div>
<label for="interval">Interval <span class="mq-tablet-max" style="font-weight:normal;">(ms)</span></label>
<input id="interval" type="number" value="0" step=100 min=0>
<span class="mq-normal-min">ms</span>
</div>
<div>
<a id="go-btn" class="button btn-green">Start</a>
</div>
</div>
<div class="Box center">
<canvas id="sg" width=860 height=530></canvas>
</div>
<script>
$().ready(page_spectrogram.init());
</script>
<div class="NotifyMsg hidden" id="notif"></div>
</div><!-- content -->
</div><!-- outer -->
</body>
</html>

@ -1,128 +0,0 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Home - Current Analyser</title>
<link href="/css/app.css" rel="stylesheet">
<script src="/js/all.js"></script>
<script>
// server root (or URL) - used for local development with remote AJAX calls
// (this needs CORS working on the target - which I added to esp-httpd)
var _root = "";
</script>
</head>
<body class="page-home">
<div id="outer">
<nav id="menu">
<div id="brand" onclick="$('#menu').toggleClass('expanded')">Current Analyser</div>
<a href="/status" class="selected">Home</a><a href="/wifi">WiFi config</a><a href="/waveform">Waveform</a><a href="/fft">FFT</a><a href="/spectrogram">Spectrogram</a><a href="/monitoring">Monitoring</a><a href="/about">About</a></nav>
<div id="content">
<img src="/img/loader.gif" alt="Loading…" id="loader">
<h1>System Status</h1>
<div class="Box">
<h2>Runtime</h2>
<table>
<tr>
<th>Uptime:</th>
<td id="uptime">%uptime%</td>
</tr>
<tr>
<th>Free heap:</th>
<td id="heap">%heap%</td>
</tr>
<tr>
<th></th>
<td><a onclick="page_status.trigReset()" class="button btn-red">Restart system</a></td>
</tr>
</table>
</div>
<div class="Box">
<h2>WiFi</h2>
<table>
<tr>
<th>WiFi mode:</th>
<td id="wmode">%wifiMode%</td>
</tr>
</table>
</div>
<!-- WiFi info is read & updated using AJAX -->
<div class="Box sta-only" style="display:none">
<h2>WiFi Station</h2>
<table>
<tr>
<th>SSID:</th>
<td id="staSSID"></td>
</tr>
<tr>
<th>RSSI:</th>
<td>
<span id="staRSSIperc"></span>,
<span id="staRSSI"></span>
</td>
</tr>
<tr>
<th>MAC:</th>
<td id="staMAC"></td>
</tr>
</table>
</div>
<div class="Box ap-only" style="display:none">
<h2>WiFi AP</h2>
<table>
<tr>
<th>SSID:</th>
<td id="apSSID"></td>
</tr>
<tr>
<th>Hidden:</th>
<td id="apHidden"></td>
</tr>
<tr>
<th>Auth. mode:</th>
<td id="apAuth"></td>
</tr>
<tr class="ap-auth-only">
<th>Password:</th>
<td id="apPwd"></td>
</tr>
<tr>
<th>Channel:</th>
<td id="apChan"></td>
</tr>
<tr>
<th>MAC:</th>
<td id="apMAC"></td>
</tr>
</table>
</div>
<div class="Modal hidden no-close" id="reset-modal">
<div class="Dialog center">
<h2>The device has been reset.</h2>
<p class="ap-only">If you're connected to the AP, you'll have to re-connect.</p>
<p>This dialog should close when the restart is complete, please wait around 15 seconds..</p>
<p><a onclick="location.reload()" class="button btn-blue">Reload the page</a></p>
</div>
</div>
<script>
$().ready(page_status.init);
</script>
<div class="NotifyMsg hidden" id="notif"></div>
</div><!-- content -->
</div><!-- outer -->
</body>
</html>

@ -1,91 +0,0 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Waveform - Current Analyser</title>
<link href="/css/app.css" rel="stylesheet">
<script src="/js/all.js"></script>
<script>
// server root (or URL) - used for local development with remote AJAX calls
// (this needs CORS working on the target - which I added to esp-httpd)
var _root = "";
</script>
</head>
<body class="page-waveform">
<div id="outer">
<nav id="menu">
<div id="brand" onclick="$('#menu').toggleClass('expanded')">Current Analyser</div>
<a href="/status">Home</a><a href="/wifi">WiFi config</a><a href="/waveform" class="selected">Waveform</a><a href="/fft">FFT</a><a href="/spectrogram">Spectrogram</a><a href="/monitoring">Monitoring</a><a href="/about">About</a></nav>
<div id="content">
<img src="/img/loader.gif" alt="Loading…" id="loader">
<h1>Waveform</h1>
<div class="Box center" id="samp-ctrl">
<div>
<label for="count">Samples</label>
<input id="count" type="number" value="500">
</div>
<div>
<label for="freq">f<sub>s</sub> <span class="mq-normal-min nb">=</span><span class="mq-tablet-max nb">(Hz)</span></label>
<input id="freq" type="number" value="2048">
<span class="mq-normal-min">Hz</span>
</div>
<div>
<a id="load" class="button btn-green">Load</a>
</div>
</div>
<div class="Box medium chartbox">
<div id="chart" class="ct-chart ct-wide"></div>
<div class="stats invis">
<table>
<tr>
<th>Samples</th>
<td id="stat-count"></td>
</tr>
<tr>
<th>f<sub>s</sub></th>
<td id="stat-f-s"></td>
</tr>
<tr>
<th>I<sub>peak</sub></th>
<td id="stat-i-peak"></td>
</tr>
<tr>
<th>I<sub>RMS</sub></th>
<td id="stat-i-rms"></td>
</tr>
</table>
<div class="ar"><!-- auto reload -->
<input type="number" id="ar-time" step="100" value="1000" min="0">&nbsp;ms
<input type="button" id="ar-btn" class="btn-blue narrow" value="Auto">
</div>
</div>
</div>
<div class="Box chartexport hidden">
Copy:
<a data-sep="space">1·2·3</a>
<a data-sep="comma">1,2,3</a>
<a data-sep="newline">1↵2↵3</a>
<a data-sep="csv">CSV</a>
<br>
<textarea id="copybox" class="hidden" readonly onfocus="this.select();" onmouseup="return false"></textarea>
</div>
<script>
$().ready(page_waveform.init('raw'));
</script>
<div class="NotifyMsg hidden" id="notif"></div>
</div><!-- content -->
</div><!-- outer -->
</body>
</html>

@ -5,7 +5,7 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>WiFi config - Current Analyser</title> <title>WiFi config - WiFi Demo</title>
<link href="/css/app.css" rel="stylesheet"> <link href="/css/app.css" rel="stylesheet">
<script src="/js/all.js"></script> <script src="/js/all.js"></script>
@ -18,8 +18,8 @@
<body class="page-wifi"> <body class="page-wifi">
<div id="outer"> <div id="outer">
<nav id="menu"> <nav id="menu">
<div id="brand" onclick="$('#menu').toggleClass('expanded')">Current Analyser</div> <div id="brand" onclick="$('#menu').toggleClass('expanded')">WiFi Demo</div>
<a href="/status">Home</a><a href="/wifi" class="selected">WiFi config</a><a href="/waveform">Waveform</a><a href="/fft">FFT</a><a href="/spectrogram">Spectrogram</a><a href="/monitoring">Monitoring</a><a href="/about">About</a></nav> <a href="/home">Home</a><a href="/wifi" class="selected">WiFi config</a></nav>
<div id="content"> <div id="content">
<img src="/img/loader.gif" alt="Loading…" id="loader"> <img src="/img/loader.gif" alt="Loading…" id="loader">

@ -4,17 +4,11 @@
$root = $prod ? '' : 'http://192.168.1.13'; $root = $prod ? '' : 'http://192.168.1.13';
$menu = [ $menu = [
'home' => [ $prod ? '/status' : '/page_status.php', 'Home' ], 'home' => [ $prod ? '/home' : '/page_home.php', 'Home' ],
'wifi' => [ $prod ? '/wifi' : '/page_wifi.php', 'WiFi config' ], 'wifi' => [ $prod ? '/wifi' : '/page_wifi.php', 'WiFi config' ],
'waveform' => [ $prod ? '/waveform' : '/page_waveform.php', 'Waveform' ],
'fft' => [ $prod ? '/fft' : '/page_fft.php', 'FFT' ],
'spectrogram' => [ $prod ? '/spectrogram' : '/page_spectrogram.php','Spectrogram' ],
'monitoring' => [ $prod ? '/monitoring' : '/page_monitoring.php', 'Monitoring' ],
// 'transient' => [ '/transient', 'Power-on transient' ],
'about' => [ $prod ? '/about' : '/page_about.php', 'About' ],
]; ];
$appname = 'Current Analyser'; $appname = 'WiFi Demo';
function e($s) { function e($s) {
return htmlspecialchars($s, ENT_HTML5|ENT_QUOTES); return htmlspecialchars($s, ENT_HTML5|ENT_QUOTES);

File diff suppressed because one or more lines are too long

@ -21,18 +21,15 @@ elixir(function (mix) {
// JS libs // JS libs
mix.scripts([ mix.scripts([
'js-src/lib/chibi.js', 'js-src/lib/chibi.js',
'js-src/lib/chartist.js', //'js-src/lib/chartist.js',
'js-src/lib/chartist.axis-title.js', //'js-src/lib/chartist.axis-title.js',
'js-src/lib/chartist.zoom.js', //'js-src/lib/chartist.zoom.js',
'js-src/lib/lodash.custom.js', 'js-src/lib/lodash.custom.js',
'js-src/utils.js', 'js-src/utils.js',
'js-src/modal.js', 'js-src/modal.js',
'js-src/notif.js', 'js-src/notif.js',
'js-src/app.js', 'js-src/app.js',
'js-src/page_wifi.js', 'js-src/page_wifi.js',
'js-src/page_waveform.js', 'js-src/page_home.js',
'js-src/page_spectrogram.js',
'js-src/page_status.js',
'js-src/page_mon.js',
], 'js/all.js'); ], 'js/all.js');
}); });

@ -615,7 +615,7 @@
if (_.isNumber(options)) options = {timeout: options}; if (_.isNumber(options)) options = {timeout: options};
var opts = Chartist.extend({}, { var opts = _.extend({}, {
nocache: true, nocache: true,
timeout: 5000, timeout: 5000,
loader: true, loader: true,

@ -1,7 +1,7 @@
/** /**
* @license * @license
* lodash 4.6.1 (Custom Build) <https://lodash.com/> * lodash 4.6.1 (Custom Build) <https://lodash.com/>
* Build: `lodash include="range,isArray,isObject,escape,unescape,escapeRegExp,each,replace,map,isNumber,isUndefined" exports="global" -d` * Build: `lodash include="range,isArray,isObject,escape,unescape,escapeRegExp,each,replace,map,isNumber,isUndefined,extend" exports="global" -d`
* Copyright 2012-2016 The Dojo Foundation <http://dojofoundation.org/> * Copyright 2012-2016 The Dojo Foundation <http://dojofoundation.org/>
* Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE> * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
* Copyright 2009-2016 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors * Copyright 2009-2016 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
@ -18,6 +18,9 @@
/** Used as the size to enable large array optimizations. */ /** Used as the size to enable large array optimizations. */
var LARGE_ARRAY_SIZE = 200; var LARGE_ARRAY_SIZE = 200;
/** Used as the `TypeError` message for "Functions" methods. */
var FUNC_ERROR_TEXT = 'Expected a function';
/** Used to stand-in for `undefined` hash values. */ /** Used to stand-in for `undefined` hash values. */
var HASH_UNDEFINED = '__lodash_hash_undefined__'; var HASH_UNDEFINED = '__lodash_hash_undefined__';
@ -28,6 +31,7 @@
/** Used as references for various `Number` constants. */ /** Used as references for various `Number` constants. */
var INFINITY = 1 / 0, var INFINITY = 1 / 0,
MAX_SAFE_INTEGER = 9007199254740991, MAX_SAFE_INTEGER = 9007199254740991,
MAX_INTEGER = 1.7976931348623157e+308,
NAN = 0 / 0; NAN = 0 / 0;
/** `Object#toString` result references. */ /** `Object#toString` result references. */
@ -223,6 +227,27 @@
return set; return set;
} }
/**
* A faster alternative to `Function#apply`, this function invokes `func`
* with the `this` binding of `thisArg` and the arguments of `args`.
*
* @private
* @param {Function} func The function to invoke.
* @param {*} thisArg The `this` binding of `func`.
* @param {...*} args The arguments to invoke `func` with.
* @returns {*} Returns the result of `func`.
*/
function apply(func, thisArg, args) {
var length = args.length;
switch (length) {
case 0: return func.call(thisArg);
case 1: return func.call(thisArg, args[0]);
case 2: return func.call(thisArg, args[0], args[1]);
case 3: return func.call(thisArg, args[0], args[1], args[2]);
}
return func.apply(thisArg, args);
}
/** /**
* A specialized version of `_.forEach` for arrays without support for * A specialized version of `_.forEach` for arrays without support for
* iteratee shorthands. * iteratee shorthands.
@ -398,6 +423,23 @@
return value > -1 && value % 1 == 0 && value < length; return value > -1 && value % 1 == 0 && value < length;
} }
/**
* Converts `iterator` to an array.
*
* @private
* @param {Object} iterator The iterator to convert.
* @returns {Array} Returns the converted array.
*/
function iteratorToArray(iterator) {
var data,
result = [];
while (!(data = iterator.next()).done) {
result.push(data.value);
}
return result;
}
/** /**
* Converts `map` to an array. * Converts `map` to an array.
* *
@ -469,8 +511,10 @@
/** Built-in value references. */ /** Built-in value references. */
var Buffer = moduleExports ? root.Buffer : undefined, var Buffer = moduleExports ? root.Buffer : undefined,
Reflect = root.Reflect,
Symbol = root.Symbol, Symbol = root.Symbol,
Uint8Array = root.Uint8Array, Uint8Array = root.Uint8Array,
enumerate = Reflect ? Reflect.enumerate : undefined,
getPrototypeOf = Object.getPrototypeOf, getPrototypeOf = Object.getPrototypeOf,
getOwnPropertySymbols = Object.getOwnPropertySymbols, getOwnPropertySymbols = Object.getOwnPropertySymbols,
objectCreate = Object.create, objectCreate = Object.create,
@ -488,6 +532,9 @@
WeakMap = getNative(root, 'WeakMap'), WeakMap = getNative(root, 'WeakMap'),
nativeCreate = getNative(Object, 'create'); nativeCreate = getNative(Object, 'create');
/** Detect if properties shadowing those on `Object.prototype` are non-enumerable. */
var nonEnumShadows = !propertyIsEnumerable.call({ 'valueOf': 1 }, 'valueOf');
/** Used to lookup unminified function names. */ /** Used to lookup unminified function names. */
var realNames = {}; var realNames = {};
@ -1372,6 +1419,31 @@
return nativeKeys(Object(object)); return nativeKeys(Object(object));
} }
/**
* The base implementation of `_.keysIn` which doesn't skip the constructor
* property of prototypes or treat sparse arrays as dense.
*
* @private
* @param {Object} object The object to query.
* @returns {Array} Returns the array of property names.
*/
function baseKeysIn(object) {
object = object == null ? object : Object(object);
var result = [];
for (var key in object) {
result.push(key);
}
return result;
}
// Fallback for IE < 9 with es6-shim.
if (enumerate && !propertyIsEnumerable.call({ 'valueOf': 1 }, 'valueOf')) {
baseKeysIn = function(object) {
return iteratorToArray(enumerate(object));
};
}
/** /**
* The base implementation of `_.map` without support for iteratee shorthands. * The base implementation of `_.map` without support for iteratee shorthands.
* *
@ -1674,6 +1746,39 @@
return copyObject(source, getSymbols(source), object); return copyObject(source, getSymbols(source), object);
} }
/**
* Creates a function like `_.assign`.
*
* @private
* @param {Function} assigner The function to assign values.
* @returns {Function} Returns the new assigner function.
*/
function createAssigner(assigner) {
return rest(function(object, sources) {
var index = -1,
length = sources.length,
customizer = length > 1 ? sources[length - 1] : undefined,
guard = length > 2 ? sources[2] : undefined;
customizer = typeof customizer == 'function'
? (length--, customizer)
: undefined;
if (guard && isIterateeCall(sources[0], sources[1], guard)) {
customizer = length < 3 ? undefined : customizer;
length = 1;
}
object = Object(object);
while (++index < length) {
var source = sources[index];
if (source) {
assigner(object, source, index, customizer);
}
}
return object;
});
}
/** /**
* Creates a `baseEach` or `baseEachRight` function. * Creates a `baseEach` or `baseEachRight` function.
* *
@ -2397,6 +2502,59 @@
/*------------------------------------------------------------------------*/ /*------------------------------------------------------------------------*/
/**
* Creates a function that invokes `func` with the `this` binding of the
* created function and arguments from `start` and beyond provided as an array.
*
* **Note:** This method is based on the [rest parameter](https://mdn.io/rest_parameters).
*
* @static
* @memberOf _
* @category Function
* @param {Function} func The function to apply a rest parameter to.
* @param {number} [start=func.length-1] The start position of the rest parameter.
* @returns {Function} Returns the new function.
* @example
*
* var say = _.rest(function(what, names) {
* return what + ' ' + _.initial(names).join(', ') +
* (_.size(names) > 1 ? ', & ' : '') + _.last(names);
* });
*
* say('hello', 'fred', 'barney', 'pebbles');
* // => 'hello fred, barney, & pebbles'
*/
function rest(func, start) {
if (typeof func != 'function') {
throw new TypeError(FUNC_ERROR_TEXT);
}
start = nativeMax(start === undefined ? (func.length - 1) : toInteger(start), 0);
return function() {
var args = arguments,
index = -1,
length = nativeMax(args.length - start, 0),
array = Array(length);
while (++index < length) {
array[index] = args[start + index];
}
switch (start) {
case 0: return func.call(this, array);
case 1: return func.call(this, args[0], array);
case 2: return func.call(this, args[0], args[1], array);
}
var otherArgs = Array(start + 1);
index = -1;
while (++index < start) {
otherArgs[index] = args[index];
}
otherArgs[start] = array;
return apply(func, this, otherArgs);
};
}
/*------------------------------------------------------------------------*/
/** /**
* Performs a [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero) * Performs a [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
* comparison between two values to determine if they are equivalent. * comparison between two values to determine if they are equivalent.
@ -2801,6 +2959,43 @@
return value === undefined; return value === undefined;
} }
/**
* Converts `value` to an integer.
*
* **Note:** This function is loosely based on [`ToInteger`](http://www.ecma-international.org/ecma-262/6.0/#sec-tointeger).
*
* @static
* @memberOf _
* @category Lang
* @param {*} value The value to convert.
* @returns {number} Returns the converted integer.
* @example
*
* _.toInteger(3);
* // => 3
*
* _.toInteger(Number.MIN_VALUE);
* // => 0
*
* _.toInteger(Infinity);
* // => 1.7976931348623157e+308
*
* _.toInteger('3');
* // => 3
*/
function toInteger(value) {
if (!value) {
return value === 0 ? value : 0;
}
value = toNumber(value);
if (value === INFINITY || value === -INFINITY) {
var sign = (value < 0 ? -1 : 1);
return sign * MAX_INTEGER;
}
var remainder = value % 1;
return value === value ? (remainder ? value - remainder : value) : 0;
}
/** /**
* Converts `value` to a number. * Converts `value` to a number.
* *
@ -2875,6 +3070,45 @@
/*------------------------------------------------------------------------*/ /*------------------------------------------------------------------------*/
/**
* This method is like `_.assign` except that it iterates over own and
* inherited source properties.
*
* **Note:** This method mutates `object`.
*
* @static
* @memberOf _
* @alias extend
* @category Object
* @param {Object} object The destination object.
* @param {...Object} [sources] The source objects.
* @returns {Object} Returns `object`.
* @example
*
* function Foo() {
* this.b = 2;
* }
*
* function Bar() {
* this.d = 4;
* }
*
* Foo.prototype.c = 3;
* Bar.prototype.e = 5;
*
* _.assignIn({ 'a': 1 }, new Foo, new Bar);
* // => { 'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5 }
*/
var assignIn = createAssigner(function(object, source) {
if (nonEnumShadows || isPrototype(source) || isArrayLike(source)) {
copyObject(source, keysIn(source), object);
return;
}
for (var key in source) {
assignValue(object, key, source[key]);
}
});
/** /**
* Gets the value at `path` of `object`. If the resolved value is * Gets the value at `path` of `object`. If the resolved value is
* `undefined` the `defaultValue` is used in its place. * `undefined` the `defaultValue` is used in its place.
@ -2980,6 +3214,48 @@
return result; return result;
} }
/**
* Creates an array of the own and inherited enumerable property names of `object`.
*
* **Note:** Non-object values are coerced to objects.
*
* @static
* @memberOf _
* @category Object
* @param {Object} object The object to query.
* @returns {Array} Returns the array of property names.
* @example
*
* function Foo() {
* this.a = 1;
* this.b = 2;
* }
*
* Foo.prototype.c = 3;
*
* _.keysIn(new Foo);
* // => ['a', 'b', 'c'] (iteration order is not guaranteed)
*/
function keysIn(object) {
var index = -1,
isProto = isPrototype(object),
props = baseKeysIn(object),
propsLength = props.length,
indexes = indexKeys(object),
skipIndexes = !!indexes,
result = indexes || [],
length = result.length;
while (++index < propsLength) {
var key = props[index];
if (!(skipIndexes && (key == 'length' || isIndex(key, length))) &&
!(key == 'constructor' && (isProto || !hasOwnProperty.call(object, key)))) {
result.push(key);
}
}
return result;
}
/** /**
* Creates an array of own enumerable key-value pairs for `object` which * Creates an array of own enumerable key-value pairs for `object` which
* can be consumed by `_.fromPairs`. * can be consumed by `_.fromPairs`.
@ -3279,14 +3555,20 @@
Stack.prototype.set = stackSet; Stack.prototype.set = stackSet;
// Add functions that return wrapped values when chaining. // Add functions that return wrapped values when chaining.
lodash.assignIn = assignIn;
lodash.constant = constant; lodash.constant = constant;
lodash.iteratee = iteratee; lodash.iteratee = iteratee;
lodash.keys = keys; lodash.keys = keys;
lodash.keysIn = keysIn;
lodash.map = map; lodash.map = map;
lodash.property = property; lodash.property = property;
lodash.range = range; lodash.range = range;
lodash.rest = rest;
lodash.toPairs = toPairs; lodash.toPairs = toPairs;
// Add aliases.
lodash.extend = assignIn;
/*------------------------------------------------------------------------*/ /*------------------------------------------------------------------------*/
// Add functions that return unwrapped values when chaining. // Add functions that return unwrapped values when chaining.
@ -3314,6 +3596,7 @@
lodash.isUndefined = isUndefined; lodash.isUndefined = isUndefined;
lodash.last = last; lodash.last = last;
lodash.replace = replace; lodash.replace = replace;
lodash.toInteger = toInteger;
lodash.toNumber = toNumber; lodash.toNumber = toNumber;
lodash.toString = toString; lodash.toString = toString;
lodash.unescape = unescape; lodash.unescape = unescape;

@ -0,0 +1,11 @@
/** Wifi page */
var page_home = (function () {
var home = {};
/** Set up the Home page */
home.init = function () {
//
};
return home;
})();

@ -1,91 +0,0 @@
var page_mon = (function() {
var mon = {};
function updRefInfoField(ok) {
$('#hasref').html(ok ? 'OK' : 'Not set!');
}
/** Capture reference & save to flash */
mon.captureRef = function() {
$().get(_root + '/mon/setref', function(resp, status) {
if (status != 200) {
// bad response
errorMsg('Operation failed.');
} else {
try {
// OK
var j = JSON.parse(resp);
updRefInfoField(j.success);
} catch(e) {
errorMsg(e);
updRefInfoField(false);
}
}
});
};
/** Capture waveform and compare with reference */
mon.compareNow = function() {
$().get(_root + '/mon/compare', function(resp, status) {
if (status != 200) {
// bad response
errorMsg('Operation failed.');
} else {
try {
// OK
var j = JSON.parse(resp);
if (j.success) {
$('#actual-dev').html(numfmt(j.deviation, 2));
$('#actual-rms').html(numfmt(j.rms, 2));
} else {
throw 'Capture failed.';
}
} catch(e) {
errorMsg(e);
$('#actual-dev').html('--');
$('#actual-rms').html('--');
}
}
});
};
function updateXvOnly() {
// is xively
var isXv = $('#rep-svc-xv')[0].checked;
if (isXv) {
$('.xv-only').removeClass('hidden');
} else {
$('.xv-only').addClass('hidden');
}
}
mon.init = function() {
updateXvOnly();
$('#rep-svc-xv,#rep-svc-ts').on('change', updateXvOnly);
setInterval(function() {
$().get(_root + '/mon/status', function(resp, status) {
if (status == 200) {
try {
// OK
var j = JSON.parse(resp);
if (j.success) {
$('#actual-dev').html(numfmt(j.deviation, 2));
$('#actual-rms').html(numfmt(j.rms, 2));
} else {
console.error('Capture failed.');
}
} catch(e) {
errorMsg(e);
$('#actual-dev').html('--');
$('#actual-rms').html('--');
}
}
});
}, 10000);
};
return mon;
})();

@ -1,356 +0,0 @@
var page_spectrogram = (function () {
var sg = {};
var ctx;
// drawing area
var plot = {
x:50,
y:10,
w:740,//860 total
h:512,
dx: 1, // bin
dy: 1
};
var opts = {
interval: 0,
sampCount: 0,
freq:0
};
var interval = 1000;
var running = false;
var readTimeout; // timer
var readoutPending;
var readXhr;
var lastLoadMs;
var lastMarkMs;
var lastMark10s;
var colormap = [
/* [val, r, g, b] */
[0.00, 0, 0, 0],
[0.10, 41, 17, 41],
[0.25, 34, 17, 78],
[0.6, 17, 30, 105],
[1.0, 17, 57, 126],
[1.2, 17, 84, 128],
[1.3, 17, 111, 115],
[1.4, 17, 134, 96],
[1.5, 17, 155, 71],
[1.6, 68, 194, 17],
[1.75, 111, 209, 17],
[1.84, 180, 213, 17],
[1.90, 223, 217, 86],
[1.97, 248, 222, 176],
[1.99, 255, 237, 222],
[2.00, 255, 255, 255],
];
function val2color(val) {
var x1, x2, c1, c2;
val = Math.log10(1+val);
if (val > 2) val = 2;
if (val < 0) val = 0;
for (var i = 0; i < colormap.length; i++) {
var c = colormap[i];
var point = c[0];
if (val >= point) {
x1 = point;
c1 = c;
}
if (val <= point) {
x2 = point;
c2 = c;
break;
}
}
var rate = ((val - x1)/(x2 - x1));
if (x1 == x2) rate=0;
var r = Math.round((c1[1] + (c2[1] - c1[1])*rate));
var g = Math.round((c1[2] + (c2[2] - c1[2])*rate));
var b = Math.round((c1[3] + (c2[3] - c1[3])*rate));
return 'rgb('+r+','+g+','+b+')';
}
function shiftSg() {
var imageData = ctx.getImageData(plot.x+plot.dx, plot.y, plot.w-plot.dx, plot.h+10);
ctx.fillStyle = 'black';
ctx.fillRect(plot.x, plot.y, plot.w, plot.h);
ctx.clearRect(plot.x, plot.y+plot.h+1, plot.w, 10); // clear the second marks box
ctx.putImageData(imageData, plot.x, plot.y);
}
function drawSg(col) {
shiftSg();
var bc = opts.sampCount/2;
for (var i = 0; i < bc; i++) {
// resolve color from the value
var clr;
if (i*plot.dy > plot.h) {
break;
}
if (i > col.length) {
clr = '#000';
} else {
clr = val2color(col[i]);
}
ctx.fillStyle = clr;
var tx = plot.x+plot.w-plot.dx;
var ty = plot.y+plot.h-(i+1)*plot.dy;
var tw = plot.dx;
var th = plot.dy;
if (ty<plot.y) {
th -= plot.y-ty;
ty = plot.y;
}
ctx.fillRect(tx, ty, tw, th);
}
// mark every 10 s
//console.log('remain',msElapsed(lastMarkMs));
if (msElapsed(lastMarkMs) >= 950) {
lastMarkMs = msNow();
var long = false;
if (msElapsed(lastMark10s) > 9500) {
long = true;
lastMark10s = msNow();
}
ctx.strokeStyle = 'white';
ctx.beginPath();
ctx.moveTo(plot.x+plot.w-.5, plot.y+plot.h+1);
ctx.lineTo(plot.x+plot.w-.5, plot.y+plot.h+1+(long?6:2));
ctx.stroke();
}
}
function onRxData(resp, status) {
readoutPending = false;
if (status == 200) {
try {
var j = JSON.parse(resp);
if (j.success) {
// display
drawSg(j.samples);
} else {
errorMsg("Sampling failed.", 1000);
}
} catch(e) {
errorMsg(e);
}
} else {
errorMsg("Request failed.", 1000);
}
if (running)
readTimeout = setTimeout(requestData, Math.max(0, opts.interval - msElapsed(lastLoadMs))); // TODO should actually compute time remaining, this adds interval to the request time.
}
function requestData() {
if (readoutPending) {
errorMsg("Request already pending - aborting.");
readXhr.abort();
}
readoutPending = true;
lastLoadMs = msNow();
var fs = opts.freq;
var n = opts.sampCount;
var url = _root+'/measure/fft?n='+n+'&fs='+fs;
readXhr = $().get(url, onRxData, estimateLoadTime(fs,n));
return true;
}
function drawLegend() {
var gap = 8;
var barW = 10;
var barH = plot.h-12;
var barY = plot.y+6;
var barX = plot.x - gap - barW;
var vStep = (100 / barH);
for (var i = 0; i < barH; i++) {
var c1 = val2color(i * vStep);
var c2 = val2color((i + 1) * vStep);
var y = Math.floor(barY + barH - (i + 1));
var gradient = ctx.createLinearGradient(0, y + 1, 0, y);
gradient.addColorStop(0, c1);
gradient.addColorStop(1, c2);
ctx.fillStyle = gradient;
ctx.fillRect(barX, y, barW, 1);
}
// border
ctx.strokeStyle = '#000';
ctx.strokeRect(barX-.5, barY-.5, barW+1, barH+1);
vStep = (100 / barH);
ctx.font = '12px sans-serif';
ctx.fillStyle = 'white';
ctx.textAlign = 'right';
for (var i = 0; i <= plot.h; i+=barH/10) {
ctx.fillText(Math.round(i*vStep)+"", plot.x - gap - barW - gap, barY+barH-i+3);
}
}
function drawAxis() {
var gap = 8;
var rX0 = plot.x+plot.w;
var rX = rX0+gap;
var rY = plot.y;
var rH = plot.h;
var rW = 70;
ctx.clearRect(rX0+.5, rY-10, rW, rH+20);
var perBin = (opts.freq/2) / (opts.sampCount/2);
var totalBins = (plot.h / plot.dy);
var totalHz = totalBins*perBin;
//console.log("perbin=",perBin,"totalBins=",totalBins,"totalHz=",totalHz);
var step;
// get the best step size
var steps = [10, 25, 50];
var multiplier = 1;
var suc = false;
do {
for (var i = 0; i < steps.length; i++) {
if ((totalHz / (steps[i] * multiplier)) <= 21) {
step = (steps[i] * multiplier);
suc = true;
break;
}
}
if (suc) break;
multiplier *= 10;
} while (true);
step = step/perBin;
// every step-th bin has a label
ctx.font = '12px sans-serif';
ctx.fillStyle = 'white';
ctx.strokeStyle = 'white';
ctx.textAlign = 'left';
// labels and dashes
for(var i = 0; i <= totalBins+step; i+= step) {
if (i >= totalBins) {
var dist = i - totalBins;
if (dist > step/2) break;// make sure not too close
i = totalBins;
}
var hz = i*(totalHz/totalBins);
if (hz>=1000000) hz = numfmt(hz/1e6,2)+'M';
else if (hz>=1000) hz = numfmt(hz/1e3,2)+'k';
else hz = numfmt(hz,1);
var yy = Math.round(rY+rH-(plot.dy*i));
ctx.fillText(hz, rX, yy+4);
ctx.beginPath();
ctx.moveTo(rX0, yy+.5);
ctx.lineTo(rX0+gap/2, yy+.5);
ctx.stroke();
if (i >= totalBins) break;
}
// Hz label
ctx.font = '16px sans-serif';
ctx.save();
ctx.translate(rX0+50, plot.y+plot.h/2);
ctx.rotate(Math.PI/2);
ctx.textAlign = "center";
ctx.fillText("Frequency - [Hz]", 0, 0);
ctx.restore();
}
function readOpts() {
opts.interval = +$('#interval').val(); // ms
opts.freq = +$('#freq').val()*2;
opts.sampCount = +$('#count').val();
plot.dx = +$('#tile-x').val();
plot.dy = +$('#tile-y').val();
}
function clearSgArea() {
ctx.fillStyle = '#000';
ctx.fillRect(plot.x, plot.y, plot.w, plot.h);
ctx.strokeStyle = 'white';
ctx.strokeRect(plot.x-.5, plot.y-.5, plot.w+1, plot.h+1);
}
sg.init = function () {
var canvas = $('#sg')[0];
ctx = canvas.getContext('2d');
// CLS
clearSgArea();
readOpts();
drawLegend();
drawAxis();
lastMarkMs = msNow()-10000;
lastMark10s = msNow()-10000;
// update tile size on bin count selection
$('#count').on('change', function() {
var count = +$('#count').val();
var tile = Math.max(1, plot.h/(count/2));
$('#tile-x').val(Math.max(4, tile)); // use width 4 for smaller by default (rolls more nicely)
$('#tile-y').val(tile);
});
// chain Y with X
$('#tile-y').on('change', function() {
$('#tile-x').val(Math.max(4,$(this).val()));
});
$('#go-btn').on('click', function() {
running = !running;
if (running) {
readOpts();
drawAxis();
requestData();
} else {
clearTimeout(readTimeout);
}
$('#go-btn')
.toggleClass('btn-green')
.toggleClass('btn-red')
.html(running ? 'Stop' : 'Start');
});
};
return sg;
})();

@ -1,88 +0,0 @@
var page_status = (function() {
var st = {};
st.j = {};
var updateTime = 10000;
var updateInhibited = false;
st.trigReset = function() {
var modal_sel = '#reset-modal';
$().get(_root + '/system/reset', function(resp, status) {
if (status == 200) {
modal.show(modal_sel);
updateInhibited = true;
var ping_i = setInterval(function() {
$().get(_root+'/system/ping', function(resp, code){
if (code == 200) {
// device is ready
modal.hide(modal_sel);
requestUpdate();
clearInterval(ping_i);
updateInhibited = false;
}
}, {timeout: 500});
}, 1000);
}
});
};
function onUpdate(resp, status) {
if (status != 200) {
// bad response
errorMsg('Update failed.');
} else {
try {
// OK
var j = JSON.parse(resp);
st.j = j; // store for global access
$('.sta-only').toggle(j.sta);
$('.ap-only').toggle(j.ap);
$('#uptime').html(j.uptime);
$('#heap').html(j.heap + " bytes");
$('#wmode').html(j.wifiMode);
if (j.sta) {
$('#staSSID').html(j.sta.SSID);
$('#staRSSIperc').html(j.sta.RSSIperc);
$('#staRSSI').html(j.sta.RSSI);
$('#staMAC').html(j.sta.MAC);
}
if (j.ap) {
$('#apSSID').html(j.ap.SSID);
$('#apHidden').html(j.ap.hidden ? "Yes" : "No");
$('#apAuth').html(j.ap.auth);
// hide the password row if auth is Open
$('.ap-auth-only').toggle(j.ap.auth != 'Open');
$('#apPwd').html(j.ap.pwd);
$('#apChan').html(j.ap.chan);
$('#apMAC').html(j.ap.MAC);
}
// chip ID & macs don't change
} catch(e) {
errorMsg(e);
}
}
if (!updateInhibited) {
setTimeout(requestUpdate, updateTime);
}
}
function requestUpdate() {
$().get(_root+'/system/status', onUpdate);
}
st.init = function() {
requestUpdate();
};
return st;
})();

@ -1,271 +0,0 @@
var page_waveform = (function () {
var wfm = {};
var zoomResetFn;
var dataFormat;
var readoutPending = false;
var autoReload = false;
var autoReloadTime = 1;
var arTimeout = -1;
var lastLoadMs;
var zoomSavedX, zoomSavedY;
var readXhr; // read xhr
var lastObj = null; // samples are stored here
var opts = {
count: 0, // sample count
freq: 0 // sampling freq
};
function buildChart(j) {
// Build the chart
var mql = window.matchMedia('screen and (min-width: 544px)');
var isPhone = !mql.matches;
var fft = (j.stats.format == 'FFT');
var xLabel, yLabel;
if (fft) {
xLabel = 'Frequency - [ Hz ]';
yLabel = 'Magnitude - [ mA ]';
} else {
xLabel = 'Sample time - [ ms ]';
yLabel = 'Current - [ mA ]';
}
var peak = Math.max(-j.stats.min, j.stats.max);
var displayPeak = Math.max(peak, 10);
// Sidebar
$('#stat-count').html(j.stats.count);
$('#stat-f-s').html(numfmt(j.stats.freq, 2));
$('#stat-i-peak').html(numfmt(peak, 2));
$('#stat-i-rms').html(numfmt(j.stats.rms, 2));
$('.stats').removeClass('invis');
// --- chart ---
// Generate point entries
// add synthetic properties
var step = fft ? (j.stats.freq/j.stats.count) : (1000/j.stats.freq);
var points = _.map(j.samples, function (a, i) {
return {
x: i * step,
y: a
};
});
var plugins = [
Chartist.plugins.zoom({
resetOnRightMouseBtn: true,
onZoom: function (chart, reset) {
zoomResetFn = reset;
zoomSavedX = chart.options.axisX.highLow;
zoomSavedY = chart.options.axisY.highLow;
}
})
];
if (!isPhone) plugins.push( // larger than phone
Chartist.plugins.ctAxisTitle({
axisX: {
axisTitle: xLabel,
offset: {
x: 0,
y: 55
}
},
axisY: {
axisTitle: yLabel,
flipText: true,
offset: {
x: 0,
y: 15
}
}
})
);
var xHigh, xLow, yHigh, yLow;
if (zoomSavedX) {
// we have saved coords of the zoom rect, restore the zoom.
xHigh = zoomSavedX.high;
xLow = zoomSavedX.low;
yHigh = zoomSavedY.high;
yLow = zoomSavedY.low;
} else {
yHigh = fft ? undefined : displayPeak;
yLow = fft ? 0 : -displayPeak;
}
new Chartist.Line('#chart', {
series: [
{
name: 'a',
data: points
},
]
}, {
showPoint: false,
showArea: fft,
fullWidth: true,
chartPadding: (isPhone ? {right: 20, bottom: 5, left: 0} : {right: 25, bottom: 30, left: 25}),
series: {
'a': {
lineSmooth: Chartist.Interpolation.monotoneCubic()
}
},
axisX: {
type: Chartist.AutoScaleAxis,
//onlyInteger: !fft // only for raw
high: xHigh,
low: xLow,
},
axisY: {
type: Chartist.AutoScaleAxis,
//onlyInteger: true
high: yHigh,
low: yLow,
},
explicitBounds: {
xLow: 0,
yLow: fft ? 0 : undefined,
xHigh: points[points.length-1].x
},
plugins: plugins
});
}
function onRxData(resp, status) {
readoutPending = false;
if (status != 200) {
errorMsg("Request failed.", 1000);
} else {
var j = JSON.parse(resp);
if (!j.success) {
errorMsg("Sampling failed.", 1000);
} else {
$('.chartexport').removeClass('hidden');
lastObj = j;
buildChart(j);
}
}
if (autoReload)
arTimeout = setTimeout(requestReload, Math.max(0, autoReloadTime - msElapsed(lastLoadMs)));
}
function readInputs() {
opts.count = $('#count').val();
opts.freq = $('#freq').val() * (dataFormat == 'fft' ? 2 : 1); // bw 2x -> f_s
}
function requestReload() {
if (readoutPending) {
errorMsg("Request already pending - aborting.");
readXhr.abort();
}
readoutPending = true;
lastLoadMs = msNow();
var n = opts.count;
var fs = opts.freq;
var url = _root+'/measure/'+dataFormat+'?n='+n+'&fs='+fs;
readXhr = $().get(url, onRxData, estimateLoadTime(fs,n));
return true;
}
function toggleAutoReload() {
autoReloadTime = +$('#ar-time').val(); // ms
readInputs();
autoReload = !autoReload;
if (autoReload) {
requestReload();
} else {
clearTimeout(arTimeout);
}
$('#ar-btn')
.toggleClass('btn-blue')
.toggleClass('btn-red')
.val(autoReload ? 'Stop' : 'Auto');
}
wfm.init = function (format) {
// --- Load data ---
dataFormat = format;
function onLoadClick() {
readInputs();
requestReload();
}
$('#load').on('click', onLoadClick);
$('.chartexport a').on('click', function() {
var sep = $(this).data('sep');
var str = '';
var isFft = (dataFormat == 'fft');
var csvStep = isFft ? (lastObj.stats.freq/lastObj.stats.count) : (1000/lastObj.stats.freq);
var csvPlaces = isFft ? 3 : 2;
switch (sep) {
case 'space': str = lastObj.samples.join(' '); break;
case 'comma': str = lastObj.samples.join(','); break;
case 'newline': str = lastObj.samples.join('\n'); break;
case 'csv':
str = _.map(lastObj.samples, function (a, i) {
return numfmt(i * csvStep, csvPlaces) + "," + a;
}).join('\n');
break;
}
if (!copyToClipboard(str)) {
var $cb = $('#copybox');
$cb.removeClass('hidden');
$cb.val(str);
} else {
infoMsg('Copy success!');
}
});
$('#count,#freq').on('keyup', function (e) {
if (e.which == 13) {
onLoadClick();
}
});
// --- zooming ---
$('#chart').on('contextmenu', function (e) { // right click on the chart -> reset
zoomResetFn && zoomResetFn();
zoomResetFn = null;
zoomSavedX = null;
zoomSavedY = null;
e.preventDefault();
return false;
});
// auto-reload button
$('#ar-btn').on('click', toggleAutoReload);
};
return wfm;
})();

File diff suppressed because one or more lines are too long

@ -1 +0,0 @@
0648559aed5a9ec1d478b445839bdb807ced7fc5

@ -1,48 +0,0 @@
<?php $page = 'about'; include "_start.php"; ?>
<h1>About</h1>
<div class="Box">
<img src="/img/cvut.svg" id="logo" class="mq-tablet-min">
<h2><?= e($appname) ?></h2>
<img src="/img/cvut.svg" id="logo2" class="mq-phone">
<p>&copy; Ondřej Hruška, 2016 &lt;<a href="mailto:ondra@ondrovo.com" target="blank">ondra@ondrovo.com</a>&gt;</p>
<p><a href="http://measure.feld.cvut.cz/" target="blank">Katedra měření, FEL ČVUT</a><br>Department of Measurement, FEE CTU</p>
</div>
<div class="Box">
<h2>Firmware</h2>
<table>
<tr>
<th>Firmware</th>
<td>v%vers_fw%, build <i>%date%</i> at <i>%time%</i></td>
</tr>
<tr>
<th>esp-httpd&nbsp;lib</th>
<td>v%vers_httpd%</td>
</tr>
<tr>
<th>SBMP&nbsp;lib</th>
<td>v%vers_sbmp%</td>
</tr>
<tr>
<th>ESP&nbsp;IoT&nbsp;SDK</th>
<td>v%vers_sdk%</td>
</tr>
<tr>
<th>STM32&nbsp;firmware</th>
<td>v%vers_stm%</td>
</tr>
</table>
<p>
The webserver was built using the great <a href="https://github.com/Spritetm/esphttpd" target="blank">esp-httpd</a>
library by Jeroen Domburg.
</p>
</div>
<?php include "_end.php"; ?>

@ -1,73 +0,0 @@
<?php $page = 'fft'; include "_start.php"; ?>
<h1>FFT</h1>
<div class="Box center" id="samp-ctrl">
<div>
<label for="count">Bins</label>
<label for="count" class="select-wrap">
<select name="count" id="count">
<option value="16">8
<option value="32">16
<option value="64">32
<option value="128">64
<option value="256">128
<option value="512">256
<option value="1024">512
<option value="2048" selected>1024
</select>
</label>
</div>
<div>
<label for="freq">f<sub>bw</sub> <span class="mq-normal-min nb">=</span><span class="mq-tablet-max nb">(Hz)</span></label>
<input id="freq" type="number" value="2048">
<span class="mq-normal-min">Hz</span>
</div>
<div>
<a id="load" class="button btn-green">Load</a>
</div>
</div>
<div class="Box medium chartbox">
<div id="chart" class="ct-chart ct-wide ct-with-area"></div>
<div class="stats invis">
<table>
<tr>
<th>Samples</th>
<td id="stat-count"></td>
</tr>
<tr>
<th>f<sub>s</sub></th>
<td id="stat-f-s"></td>
</tr>
<tr>
<th>I<sub>peak</sub></th>
<td id="stat-i-peak"></td>
</tr>
<tr>
<th>I<sub>RMS</sub></th>
<td id="stat-i-rms"></td>
</tr>
</table>
<div class="ar"><!-- auto reload -->
<input type="number" id="ar-time" step="100" value="1000" min="0">&nbsp;ms
<input type="button" id="ar-btn" class="btn-blue narrow" value="Auto">
</div>
</div>
</div>
<div class="Box chartexport hidden">
Copy:
<a data-sep="space">1·2·3</a>
<a data-sep="comma">1,2,3</a>
<a data-sep="newline">1↵2↵3</a>
<a data-sep="csv">CSV</a>
<br>
<textarea id="copybox" class="hidden" readonly onfocus="this.select();" onmouseup="return false"></textarea>
</div>
<script>
$().ready(page_waveform.init('fft'));
</script>
<?php include "_end.php"; ?>

@ -0,0 +1,21 @@
<?php $page = 'home';
include "_start.php"; ?>
<h1>Home</h1>
<div class="Box">
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusamus cum eius molestias nesciunt nihil sequi? Laboriosam molestiae nesciunt
quis! Aut eius esse in laudantium obcaecati possimus quis repudiandae tenetur velit.</p>
</div>
<div class="Box">
<h2>Firmware</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusamus cum eius molestias nesciunt nihil sequi? Laboriosam molestiae nesciunt
quis! Aut eius esse in laudantium obcaecati possimus quis repudiandae tenetur velit.</p>
</div>
<script>
$().ready(page_home.init);
</script>
<?php include "_end.php"; ?>

@ -1,73 +0,0 @@
<?php $page = 'monitoring'; include "_start.php"; ?>
<h1>Monitoring &amp; Reporting</h1>
<div class="Box">
<h2>Status</h2>
<table>
<tr>
<th>Reference:</th>
<td>
<span id="hasref" class="Valfield">%refStored%</span>
<a onclick="page_mon.captureRef()" class="button btn-green">Capture</a>
</td>
</tr>
<tr>
<th>Status:</th>
<td>
<span class="Valfield" style="vertical-align:middle;">
Δ = <span id="actual-dev">%curDeviation%</span><br>
I<sub>RMS</sub> = <span id="actual-rms">%curRMS%</span>&nbsp;mA
</span>
<a onclick="page_mon.compareNow()" class="button btn-blue">Measure</a>
</td>
</tr>
</table>
</div>
<div class="Box">
<h2>Reporting</h2>
<form action="<?=$root?>/mon/config" method="POST">
<table>
<tr>
<th><label for="rep-on">Reporting:</label></th>
<td>
<input type="checkbox" id="rep-on" name="enabled" value="1" %rep_en%><!--
-->&nbsp;<label for="rep-on">enabled</label>
</td>
</tr>
<tr>
<th><label for="rep-interval">Interval:</label></th>
<td>
<input type="number" name="interval" id="rep-interval" style="max-width: 10em" value="%repInterval%"><!--
-->&nbsp;seconds
</td>
</tr>
<tr>
<th>Service:</th>
<td>
<input type="radio" name="service" value="xv" id="rep-svc-xv" %svc_xv%>&nbsp;<label for="rep-svc-xv">Xively</label>&nbsp;
<input type="radio" name="service" value="ts" id="rep-svc-ts" %svc_ts%>&nbsp;<label for="rep-svc-ts">ThingSpeak</label>
</td>
</tr>
<tr>
<th><label for="rep-key">API key:</label></th>
<td><input type="text" name="key" id="rep-key" value="%repKey%"></td>
</tr>
<tr class="xv-only">
<th><label for="rep-feed">Feed ID:</label></th>
<td><input type="text" name="feed" id="rep-feed" value="%repFeed%"></td>
</tr>
<tr>
<th>&nbsp;</th>
<td><input type="submit" value="Save changes"></td>
</tr>
</table>
</form>
</div>
<script>
$().ready(page_mon.init);
</script>
<?php include "_end.php"; ?>

@ -1,50 +0,0 @@
<?php $page = 'spectrogram'; include "_start.php"; ?>
<h1>Spectrogram</h1>
<div class="Box center" id="samp-ctrl">
<div>
<label for="count">Bins</label>
<label for="count" class="select-wrap">
<select name="count" id="count">
<option value="16">8
<option value="32">16
<option value="64">32
<option value="128">64
<option value="256">128
<option value="512">256
<option value="1024" selected>512
<option value="2048">1024
</select>
</label>
</div>
<div id="tile-cfg">
<label for="tile-x">Tile</label>
<input id="tile-x" type="number" min=1 step=1 value=4>
×
<input id="tile-y" type="number" min=1 step=1 value=1>
</div>
<div>
<label for="freq">f<sub>bw</sub> <span class="mq-normal-min nb">=</span><span class="mq-tablet-max nb">(Hz)</span></label>
<input id="freq" type="number" value="2048">
<span class="mq-normal-min">Hz</span>
</div>
<div>
<label for="interval">Interval <span class="mq-tablet-max" style="font-weight:normal;">(ms)</span></label>
<input id="interval" type="number" value="0" step=100 min=0>
<span class="mq-normal-min">ms</span>
</div>
<div>
<a id="go-btn" class="button btn-green">Start</a>
</div>
</div>
<div class="Box center">
<canvas id="sg" width=860 height=530></canvas>
</div>
<script>
$().ready(page_spectrogram.init());
</script>
<?php include "_end.php"; ?>

@ -1,99 +0,0 @@
<?php $page = 'home'; include "_start.php"; ?>
<h1>System Status</h1>
<div class="Box">
<h2>Runtime</h2>
<table>
<tr>
<th>Uptime:</th>
<td id="uptime">%uptime%</td>
</tr>
<tr>
<th>Free heap:</th>
<td id="heap">%heap%</td>
</tr>
<tr>
<th></th>
<td><a onclick="page_status.trigReset()" class="button btn-red">Restart system</a></td>
</tr>
</table>
</div>
<div class="Box">
<h2>WiFi</h2>
<table>
<tr>
<th>WiFi mode:</th>
<td id="wmode">%wifiMode%</td>
</tr>
</table>
</div>
<!-- WiFi info is read & updated using AJAX -->
<div class="Box sta-only" style="display:none">
<h2>WiFi Station</h2>
<table>
<tr>
<th>SSID:</th>
<td id="staSSID"></td>
</tr>
<tr>
<th>RSSI:</th>
<td>
<span id="staRSSIperc"></span>,
<span id="staRSSI"></span>
</td>
</tr>
<tr>
<th>MAC:</th>
<td id="staMAC"></td>
</tr>
</table>
</div>
<div class="Box ap-only" style="display:none">
<h2>WiFi AP</h2>
<table>
<tr>
<th>SSID:</th>
<td id="apSSID"></td>
</tr>
<tr>
<th>Hidden:</th>
<td id="apHidden"></td>
</tr>
<tr>
<th>Auth. mode:</th>
<td id="apAuth"></td>
</tr>
<tr class="ap-auth-only">
<th>Password:</th>
<td id="apPwd"></td>
</tr>
<tr>
<th>Channel:</th>
<td id="apChan"></td>
</tr>
<tr>
<th>MAC:</th>
<td id="apMAC"></td>
</tr>
</table>
</div>
<div class="Modal hidden no-close" id="reset-modal">
<div class="Dialog center">
<h2>The device has been reset.</h2>
<p class="ap-only">If you're connected to the AP, you'll have to re-connect.</p>
<p>This dialog should close when the restart is complete, please wait around 15 seconds..</p>
<p><a onclick="location.reload()" class="button btn-blue">Reload the page</a></p>
</div>
</div>
<script>
$().ready(page_status.init);
</script>
<?php include "_end.php"; ?>

@ -1,62 +0,0 @@
<?php $page = 'waveform'; include "_start.php"; ?>
<h1>Waveform</h1>
<div class="Box center" id="samp-ctrl">
<div>
<label for="count">Samples</label>
<input id="count" type="number" value="500">
</div>
<div>
<label for="freq">f<sub>s</sub> <span class="mq-normal-min nb">=</span><span class="mq-tablet-max nb">(Hz)</span></label>
<input id="freq" type="number" value="2048">
<span class="mq-normal-min">Hz</span>
</div>
<div>
<a id="load" class="button btn-green">Load</a>
</div>
</div>
<div class="Box medium chartbox">
<div id="chart" class="ct-chart ct-wide"></div>
<div class="stats invis">
<table>
<tr>
<th>Samples</th>
<td id="stat-count"></td>
</tr>
<tr>
<th>f<sub>s</sub></th>
<td id="stat-f-s"></td>
</tr>
<tr>
<th>I<sub>peak</sub></th>
<td id="stat-i-peak"></td>
</tr>
<tr>
<th>I<sub>RMS</sub></th>
<td id="stat-i-rms"></td>
</tr>
</table>
<div class="ar"><!-- auto reload -->
<input type="number" id="ar-time" step="100" value="1000" min="0">&nbsp;ms
<input type="button" id="ar-btn" class="btn-blue narrow" value="Auto">
</div>
</div>
</div>
<div class="Box chartexport hidden">
Copy:
<a data-sep="space">1·2·3</a>
<a data-sep="comma">1,2,3</a>
<a data-sep="newline">1↵2↵3</a>
<a data-sep="csv">CSV</a>
<br>
<textarea id="copybox" class="hidden" readonly onfocus="this.select();" onmouseup="return false"></textarea>
</div>
<script>
$().ready(page_waveform.init('raw'));
</script>
<?php include "_end.php"; ?>

@ -1,4 +1,5 @@
<?php $page = 'wifi'; include "_start.php"; ?> <?php $page = 'wifi';
include "_start.php"; ?>
<h1>Wireless Setup</h1> <h1>Wireless Setup</h1>

@ -6,7 +6,7 @@
@import "utils/index"; @import "utils/index";
@import "lib/chartist/chartist"; //@import "lib/chartist/chartist";
$form-label-w: 130px; $form-label-w: 130px;
$form-label-gap: 8px; $form-label-gap: 8px;
@ -15,8 +15,8 @@ $form-field-w: 250px;
$c-form-label-fg: white; $c-form-label-fg: white;
$c-form-field-bg: #303030; $c-form-field-bg: #303030;
$c-form-field-fg: white; $c-form-field-fg: white;
$c-form-highlight: #217b3a; $c-form-highlight: #214e7a;
$c-form-highlight-a: #28bc65; $c-form-highlight-a: #2972ba;
@function dist($x) { @function dist($x) {
@return modular-scale($x, 1rem, $golden); @return modular-scale($x, 1rem, $golden);
@ -49,8 +49,6 @@ $c-form-highlight-a: #28bc65;
// import all our pages // import all our pages
@import "pages/wifi"; @import "pages/wifi";
@import "pages/home"; @import "pages/home";
@import "pages/wfm";
@import "pages/about";
@include media($tablet-min) { @include media($tablet-min) {
.mq-phone { display: none; } .mq-phone { display: none; }

@ -1,5 +1,5 @@
#menu { #menu {
$menu-bg: #2bab5f; $menu-bg: #2c6cab;
flex: 0 0 15rem; flex: 0 0 15rem;
background: $menu-bg; background: $menu-bg;
@ -51,7 +51,7 @@
font-size: 130%; font-size: 130%;
color: white; color: white;
$hl: #1bd886; $hl: #1c79d6;
transition: background-color 0.2s; transition: background-color 0.2s;
text-shadow: 0 0 5px rgba(black, .4); text-shadow: 0 0 5px rgba(black, .4);

@ -1,17 +0,0 @@
.page-about {
.Box {
padding-left:dist(0);
padding-right:dist(0);
a {font-weight: bold;}
}
#logo {
float:right;
height: 130px;
}
#logo2 {
max-width: 150px;
}
}

@ -1,11 +1,17 @@
.page-home #staRSSIperc:after { .page-about {
padding-left: dist(-4); .Box {
content: '%'; padding-left:dist(0);
font-size: fsize(-1); padding-right:dist(0);
}
a {font-weight: bold;}
}
#logo {
float:right;
height: 130px;
}
.page-home #staRSSI:after { #logo2 {
padding-left: dist(-4); max-width: 150px;
content: 'dBm'; }
font-size: fsize(-1);
} }

@ -1,101 +0,0 @@
#samp-ctrl {
display: flex;
padding: dist(-2);
flex-direction: row;
@include media($phone) {
flex-direction: column;
}
justify-content: center;
align-items: stretch;
> div {
margin: dist(-2) dist(-1);
}
label {
line-height: 1.8;
font-weight: bold;
}
input,select {
width: 6em;
@include media($phone) {
width: 100%;
}
}
// -- spectrogram --
#tile-cfg input {
width: 3em;
}
#interval {
width: 4.5em;
}
}
.Box.chartbox {
display: flex;
flex-direction: row;
@include media($phone) {
flex-direction: column;
}
.stats {
flex: 0 1;
position: relative;
@include media($phone) {
table {
margin: 0 auto;
} // center the table
td,th {
width: 50%;
}
}
th,td {
@include nowrap;
}
th sub { font-weight: normal;}
td {
min-width: 100px;
}
td:after {
font-size: 90%;
padding-left: .5em;
}
//#stat-count
#stat-f-s:after {content: "Hz"}
#stat-i-peak:after {content: "mA"}
#stat-i-rms:after {content: "mA"}
padding-bottom: 50px;
// auto reload box
.ar {
position: absolute;
bottom:dist(-2);
width:100%;
text-align: center;
input[type=number] {
width: 4em;
}
input[type=button] {
margin-left: dist(-2);
}
}
}
}

@ -1,71 +0,0 @@
<?php $page='home'; include "_start.php"; ?>
<h1>Heading sdfsd sdfsd</h1>
<div class="Box">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusantium et fugit, labore molestiae provident veniam. Architecto deserunt eos esse fuga, hic inventore maxime minima natus non quas quibusdam sunt suscipit.</div>
<div class="Box wide"><b>This is a wide box for like a graph or whatever.</b><br>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aliquid asperiores consectetur debitis deleniti dicta dignissimos dolores eveniet exercitationem ipsa iste necessitatibus nemo, odit pariatur provident repellat ullam ut veniam vero!</div>
<div class="Box">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aspernatur aut commodi culpa et fugit laboriosam, minus praesentium recusandae ullam voluptates. Alias culpa, cupiditate ducimus est id ipsum optio quam voluptate!</div>
<div class="Box">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aspernatur ex rerum tempore voluptates? Accusamus corporis, dolorem enim <a href="#">This is a link</a>. ipsum molestiae molestias non odit perspiciatis repellendus sint, soluta, tempore tenetur vitae voluptate!</div>
<div class="Box">
<form action="#">
<div class="Row">
<label for="xoxo">Text</label>
<input id="xoxo" name="xoxo" type="text" value="I'm a text input">
</div>
<div class="Row checkbox">
<div class="checkbox-wrap"><input id="aa" name="aa" type="checkbox" value=asdf"></div>
<label for="aa">Checkbox</label>
</div>
<div class="Row">
<label for="sss">Number</label>
<input id="sss" name="sss" type="number" value="123">
</div>
<div class="Row">
<label for="sss">Password</label>
<input id="sss" name="sss" type="password" value="123">
</div>
<div class="Row">
<label for="sss">Yo textarea</label>
<textarea>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Alias aperiam assumenda blanditiis, commodi cumque debitis harum in inventore ipsum iusto laudantium minima molestias nam nemo nihil nulla perferendis, porro vel?</textarea>
</div>
<div class="Row buttons">
<div class="spacer"></div>
<input type="submit" value="Send">
<input type="reset" value="Clear">
</div>
<div class="Row checkbox">
<div class="checkbox-wrap"><input id="aa" name="aa" type="checkbox" value=asdf"></div>
<label for="aa">Checkbox</label>
</div>
<div class="Row">
<label for="sss">Select!!</label>
<label for="sdsd" class="select-wrap">
<select name="sdsd" id="sdsd">
<option value="sdfsf">sfsdf</option>
<option value="fff">fff</option>
<option value="dddd">dddddd</option>
</select>
</label>
</div>
</form>
</div>
<div class="Box">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aspernatur aut commodi culpa et fugit laboriosam, minus praesentium recusandae ullam voluptates. Alias culpa, cupiditate ducimus est id ipsum optio quam voluptate!</div>
<div class="Box">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aspernatur aut commodi culpa et fugit laboriosam, minus praesentium recusandae ullam voluptates. Alias culpa, cupiditate ducimus est id ipsum optio quam voluptate!</div>
<div class="Box">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aspernatur aut commodi culpa et fugit laboriosam, minus praesentium recusandae ullam voluptates. Alias culpa, cupiditate ducimus est id ipsum optio quam voluptate!</div>
<div class="Box">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aspernatur aut commodi culpa et fugit laboriosam, minus praesentium recusandae ullam voluptates. Alias culpa, cupiditate ducimus est id ipsum optio quam voluptate!</div>
<div class="Box">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aspernatur aut commodi culpa et fugit laboriosam, minus praesentium recusandae ullam voluptates. Alias culpa, cupiditate ducimus est id ipsum optio quam voluptate!</div>
<?php include "_end.php"; ?>

@ -5,9 +5,9 @@ HTTP auth implementation. Only does basic authentication for now.
/* /*
* ---------------------------------------------------------------------------- * ----------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42): * "THE BEER-WARE LICENSE" (Revision 42):
* Jeroen Domburg <jeroen@spritesmods.com> wrote this file. As long as you retain * Jeroen Domburg <jeroen@spritesmods.com> wrote this file. As long as you retain
* this notice you can do whatever you want with this stuff. If we meet some day, * this notice you can do whatever you want with this stuff. If we meet some day,
* and you think this stuff is worth it, you can buy me a beer in return. * and you think this stuff is worth it, you can buy me a beer in return.
* ---------------------------------------------------------------------------- * ----------------------------------------------------------------------------
*/ */
@ -16,7 +16,7 @@ HTTP auth implementation. Only does basic authentication for now.
#include "auth.h" #include "auth.h"
#include "base64.h" #include "base64.h"
int ICACHE_FLASH_ATTR authBasic(HttpdConnData *connData) { httpd_cgi_state authBasic(HttpdConnData *connData) {
const char *forbidden="401 Forbidden."; const char *forbidden="401 Forbidden.";
int no=0; int no=0;
int r; int r;

@ -18,6 +18,6 @@
//has. //has.
typedef int (* AuthGetUserPw)(HttpdConnData *connData, int no, char *user, int userLen, char *pass, int passLen); typedef int (* AuthGetUserPw)(HttpdConnData *connData, int no, char *user, int userLen, char *pass, int passLen);
int ICACHE_FLASH_ATTR authBasic(HttpdConnData *connData); httpd_cgi_state ICACHE_FLASH_ATTR authBasic(HttpdConnData *connData);
#endif #endif

@ -5,6 +5,7 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
//#include "espmissingprotos.h"
#ifdef FREERTOS #ifdef FREERTOS
#include <stdint.h> #include <stdint.h>
@ -35,7 +36,6 @@
#include "platform.h" #include "platform.h"
#include "espmissingprotos.h"
#include "esp_sdk_ver.h" #include "esp_sdk_ver.h"
#include "logging.h" #include "logging.h"

@ -4,7 +4,7 @@
#include <esp8266.h> #include <esp8266.h>
#include "uptime.h" #include "uptime.h"
#define VERBOSE 0 #define VERBOSE 1
// logging functions // logging functions

@ -2,6 +2,7 @@
#define PLATFORM_H #define PLATFORM_H
#include <osapi.h> #include <osapi.h>
#include "espmissingprotos.h"
#ifdef FREERTOS #ifdef FREERTOS

@ -4,13 +4,8 @@
#include <esp8266.h> #include <esp8266.h>
#include <sbmp.h> #include <sbmp.h>
// request to capture data... #define DG_MOTOR_HOME 40
#define DG_REQUEST_RAW 40 #define DG_MOTOR_GOTO 41
#define DG_REQUEST_FFT 41
// reporting
#define DG_REQUEST_STORE_REF 42 // request to capture & store reference FFT
#define DG_REQUEST_COMPARE_REF 43 // request to capture FFT & compare with reference
// wifi status & control // wifi status & control
#define DG_SETMODE_AP 44 // request AP mode (AP button pressed) #define DG_SETMODE_AP 44 // request AP mode (AP button pressed)

@ -1,83 +0,0 @@
#include <esp8266.h>
#include <httpd.h>
#include "page_about.h"
#include "fw_version.h"
#include "sbmp.h"
#include "datalink.h"
#include "serial.h"
static bool stm_vers_loaded = false;
static char stm_vers_buf[10];
static void readVersionCB(SBMP_Endpoint *ep, SBMP_Datagram *dg, void** arg)
{
(void)ep;
(void)arg;
if (dg->type != DG_SUCCESS) {
error("Response to REQUEST_VERSION not SUCCESS.");
return;
}
sprintf(stm_vers_buf, "%d.%d.%d", dg->payload[0], dg->payload[1], dg->payload[2]);
stm_vers_loaded = true;
}
/** "About" page */
httpd_cgi_state FLASH_FN tplAbout(HttpdConnData *connData, char *token, void **arg)
{
// arg is unused
(void)arg;
if (token == NULL) return HTTPD_CGI_DONE;
if (streq(token, "vers_fw")) {
httpdSend(connData, FIRMWARE_VERSION, -1);
} else if (streq(token, "date")) {
httpdSend(connData, __DATE__, -1);
} else if (streq(token, "time")) {
httpdSend(connData, __TIME__, -1);
} else if (streq(token, "vers_httpd")) {
httpdSend(connData, HTTPDVER, -1);
} else if (streq(token, "vers_sbmp")) {
httpdSend(connData, SBMP_VER, -1);
} else if (streq(token, "vers_sdk")) {
httpdSend(connData, STR(ESP_SDK_VERSION), -1);
} else if (streq(token, "vers_stm")) {
if (stm_vers_loaded) {
httpdSend(connData, stm_vers_buf, -1);
} else {
uint16_t sesn;
sbmp_ep_send_message(dlnk_ep, DG_REQUEST_STM_VERSION, NULL, 0, &sesn, NULL);
sbmp_ep_add_listener(dlnk_ep, sesn, readVersionCB, NULL);
sprintf(stm_vers_buf, "???");
// poll & wait for response
const int timeout = 100;
for (uint32_t i = 0; i < timeout * 100; i++) {
uart_poll();
if (stm_vers_loaded) {
break;
}
os_delay_us(10);
system_soft_wdt_feed(); // Feed the dog, or it'll bite.
}
httpdSend(connData, stm_vers_buf, -1); // send to view
}
}
return HTTPD_CGI_DONE;
}

@ -0,0 +1,23 @@
#include <esp8266.h>
#include <httpd.h>
#include "fw_version.h"
#include "sbmp.h"
#include "datalink.h"
#include "serial.h"
/** "Home" page */
httpd_cgi_state FLASH_FN tplHome(HttpdConnData *connData, char *token, void **arg)
{
// arg is unused
(void)arg;
(void)connData;
if (token == NULL) return HTTPD_CGI_DONE;
// if (streq(token, "vers_fw")) {
// httpdSend(connData, FIRMWARE_VERSION, -1);
// }
return HTTPD_CGI_DONE;
}

@ -3,6 +3,6 @@
#include <httpd.h> #include <httpd.h>
httpd_cgi_state tplAbout(HttpdConnData *connData, char *token, void **arg); httpd_cgi_state tplHome(HttpdConnData *connData, char *token, void **arg);
#endif // PAGE_ABOUT_H #endif // PAGE_ABOUT_H

@ -1,189 +0,0 @@
#include <esp8266.h>
#include <httpd.h>
#include "page_monitoring.h"
#include "reporting.h"
#include "ftoa.h"
httpd_cgi_state FLASH_FN cgiMonCompare(HttpdConnData *connData)
{
if (connData->conn == NULL) return HTTPD_CGI_DONE;
httpdStartResponse(connData, 200);
httpdHeader(connData, "Content-Type", "application/json");
httpdEndHeaders(connData);
// this is semi-async (waits for completion)
bool suc = capture_and_report(false);
char buf[100];
if (suc && rpt_result.ready) {
// success
char *bb = buf;
bb += sprintf(bb, "{\"success\": true, \"deviation\": ");
bb += my_ftoa(bb,rpt_result.deviation, 2);
bb += sprintf(bb, ", \"rms\": ");
bb += my_ftoa(bb,rpt_result.i_rms, 2);
bb += sprintf(bb, "}");
httpdSend(connData, buf, -1);
} else {
httpdSend(connData, "{\"success\": false}", -1);
}
return HTTPD_CGI_DONE;
}
/** This is an automated poll for current state, to update the display (measured by reporting func) */
httpd_cgi_state FLASH_FN cgiMonStatus(HttpdConnData *connData)
{
if (connData->conn == NULL) return HTTPD_CGI_DONE;
httpdStartResponse(connData, 200);
httpdHeader(connData, "Content-Type", "application/json");
httpdEndHeaders(connData);
char buf[100];
if (rpt_result.ready) {
// success
char *bb = buf;
bb += sprintf(bb, "{\"success\": true, \"deviation\": ");
bb += my_ftoa(bb,rpt_result.deviation, 2);
bb += sprintf(bb, ", \"rms\": ");
bb += my_ftoa(bb,rpt_result.i_rms, 2);
bb += sprintf(bb, "}");
httpdSend(connData, buf, -1);
} else {
httpdSend(connData, "{\"success\": false}", -1);
}
return HTTPD_CGI_DONE;
}
httpd_cgi_state FLASH_FN cgiMonSetRef(HttpdConnData *connData)
{
if (connData->conn == NULL) return HTTPD_CGI_DONE;
httpdStartResponse(connData, 200);
httpdHeader(connData, "Content-Type", "application/json");
httpdEndHeaders(connData);
// this is semi-async (waits for completion)
bool suc = capture_reporting_reference();
httpdSend(connData, suc ? "{\"success\": true}" : "{\"success\": false}", -1);
return HTTPD_CGI_DONE;
}
httpd_cgi_state FLASH_FN cgiMonitoringCfg(HttpdConnData *connData)
{
if (connData->conn == NULL) return HTTPD_CGI_DONE;
// TODO
HttpdPostData *post = connData->post;
if (post != NULL) {
char buf[64];
int blen;
// enabled=1
blen = httpdFindArg(post->buff, "enabled", buf, 64);
if (blen == -1) {
// wasn't found
rpt_conf.enabled = false;
} else {
rpt_conf.enabled = (buf[0] == '1');
}
// interval=secs
blen = httpdFindArg(post->buff, "interval", buf, 64);
if (blen != -1) {
rpt_conf.interval = (uint32_t)atoi(buf);
}
// service=xv or ts
blen = httpdFindArg(post->buff, "service", buf, 64);
if (blen != -1) {
rpt_conf.service = (buf[0] == 'x' ? RPT_XIVELY: RPT_THINGSPEAK);
}
// feed
blen = httpdFindArg(post->buff, "feed", buf, 64);
if (blen != -1) {
strcpy(rpt_conf.feed, buf);
}
// key
blen = httpdFindArg(post->buff, "key", buf, 64);
if (blen != -1) {
strcpy(rpt_conf.key, buf);
}
// Save & Apply
reporting_cfg_save();
}
httpdRedirect(connData, "/monitoring");
return HTTPD_CGI_DONE;
}
/** "Monitoring" page - fill form fields */
httpd_cgi_state FLASH_FN tplMonitoring(HttpdConnData *connData, char *token, void **arg)
{
(void)arg;
char buf[20];
if (token == NULL) return HTTPD_CGI_DONE;
if (streq(token, "refStored")) {
httpdSend(connData, true ? "OK" : "Not set!", -1); // fixme
} else if (streq(token, "curDeviation")) {
// current deviation
if (rpt_result.ready) {
my_ftoa(buf, rpt_result.deviation, 2);
} else {
sprintf(buf, "--");
}
httpdSend(connData, buf, -1);
} else if (streq(token, "curRMS")) {
// current deviation
if (rpt_result.ready) {
my_ftoa(buf, rpt_result.i_rms, 2);
} else {
sprintf(buf, "--");
}
httpdSend(connData, buf, -1);
} else if (streq(token, "rep_en")) {
if (rpt_conf.enabled) httpdSend(connData, "checked", -1);
} else if (streq(token, "repInterval")) { // interval in seconds
sprintf(buf, "%d", rpt_conf.interval);
httpdSend(connData, buf, -1);
} else if (streq(token, "svc_xv")) { // Xively checkbox
if (rpt_conf.service == RPT_XIVELY) {
httpdSend(connData, "checked", -1);
}
} else if (streq(token, "svc_ts")) { // ThingSpeak checkbox
if (rpt_conf.service == RPT_THINGSPEAK) {
httpdSend(connData, "checked", -1);
}
} else if (streq(token, "repFeed")) { // reporting feed ID
httpdSend(connData, rpt_conf.feed, -1);
} else if (streq(token, "repKey")) { // reporting key
httpdSend(connData, rpt_conf.key, -1);
}
return HTTPD_CGI_DONE;
}

@ -1,15 +0,0 @@
#ifndef PAGE_MONITORING_H
#define PAGE_MONITORING_H
#include <httpd.h>
httpd_cgi_state tplMonitoring(HttpdConnData *connData, char *token, void **arg);
httpd_cgi_state cgiMonCompare(HttpdConnData *connData);
httpd_cgi_state cgiMonStatus(HttpdConnData *connData); // same result as "compare", but doesn't measure anything
httpd_cgi_state cgiMonSetRef(HttpdConnData *connData);
httpd_cgi_state cgiMonitoringCfg(HttpdConnData *connData);
#endif // PAGE_MONITORING_H

@ -1,159 +0,0 @@
#include <esp8266.h>
#include <httpd.h>
#include "page_status.h"
#include "uptime.h"
#include "utils.h"
/** System Status page */
httpd_cgi_state FLASH_FN tplSystemStatus(HttpdConnData *connData, char *token, void **arg)
{
// arg is unused
(void)arg;
struct station_config stconf;
struct softap_config apconf;
char buff[300];
u8 mac[6];
int rssi;
// empty string if no token matches
buff[0] = 0;
if (token == NULL) return HTTPD_CGI_DONE;
// {
// "uptime": "00:01:1200:01:12",
// "heap": 3391233912,
// "wifiMode": "ClientClient",
// "sta": null,
// "ap": {"SSID": "ESP_D58987", "pwd": "", "MAC": "1a:fe:34:d5:89:87", "chan": 1, "hidden": false, "auth": Open, }
// }
int opmode = wifi_get_opmode();
bool is_sta = (opmode == STATION_MODE || opmode == STATIONAP_MODE);
bool is_ap = (opmode == SOFTAP_MODE || opmode == STATIONAP_MODE);
if (streq(token, "uptime")) {
// Uptime
uptime_str(buff);
} else if (streq(token, "heap")) {
// Free heap
sprintf(buff, "%u", system_get_free_heap_size());
} else if (streq(token, "wifiMode")) {
// WiFi mode
strcpy(buff, opmode2str(opmode));
} else if (streq(token, "staInfo")) {
if (!is_sta) {
strcpy(buff, "null");
} else {
// AP info
wifi_station_get_config(&stconf);
wifi_get_macaddr(STATION_IF, mac);
rssi = wifi_station_get_rssi();
sprintf(buff,
"{"
"\"SSID\": \"%s\", "
"\"RSSI\": %d, "
"\"RSSIperc\": %d, "
"\"MAC\": \""MACSTR"\""
"}",
stconf.ssid,
rssi,
rssi2perc(rssi),
MAC2STR(mac)
);
}
} else if (streq(token, "apInfo")) {
if (!is_ap) {
strcpy(buff, "null");
} else {
wifi_softap_get_config(&apconf);
wifi_get_macaddr(SOFTAP_IF, mac);
// AP info
sprintf(buff,
"{"
"\"SSID\": \"%s\", "
"\"pwd\": \"%s\", "
"\"MAC\": \""MACSTR"\", "
"\"chan\": %d, "
"\"hidden\": %s, "
"\"auth\": \"%s\""
"}",
apconf.ssid,
apconf.password,
MAC2STR(mac),
apconf.channel,
apconf.ssid_hidden ? "true" : "false",
auth2str(apconf.authmode)
);
}
/*} else if (streq(token, "staSSID")) {
// Station SSID (if in station mode)
if (!is_sta) {
strcpy(buff, "N/A");
} else {
strcpy(buff, (char*)stconf.ssid);
}
} else if (streq(token, "staRSSI")) {
// Signal strength if in Station mode
if (!is_sta) {
strcpy(buff, "0");
} else {
rssi = wifi_station_get_rssi();
sprintf(buff, "%d", rssi);
}
} else if (streq(token, "staRSSIperc")) {
// Signal strength if in Station mode
if (!is_sta) {
strcpy(buff, "0");
} else {
rssi = wifi_station_get_rssi();
sprintf(buff, "%d", rssi2perc(rssi));
}
} else if (streq(token, "staMAC")) {
// Station MAC addr
wifi_get_macaddr(STATION_IF, mac);
sprintf(buff, MACSTR, MAC2STR(mac));
} else if (streq(token, "apMAC")) {
// SoftAP MAC addr
wifi_get_macaddr(SOFTAP_IF, mac);
sprintf(buff, MACSTR, MAC2STR(mac));
} else if (streq(token, "chipID")) {
// Chip serial number
sprintf(buff, "%08x", system_get_chip_id());*/
}
httpdSend(connData, buff, -1);
return HTTPD_CGI_DONE;
}
/*
"sta": {
"SSID": "%staSSID%",
"RSSI": %staRSSI%,
"RSSIperc": %staRSSIperc%,
"MAC": "%staMAC%"
},
"ap": {
"MAC": "%apMAC%"
},
*/

@ -1,8 +0,0 @@
#ifndef PAGE_HOME_H
#define PAGE_HOME_H
#include <httpd.h>
httpd_cgi_state tplSystemStatus(HttpdConnData *connData, char *token, void **arg);
#endif // PAGE_HOME_H

@ -1,191 +0,0 @@
#include <esp8266.h>
#include <httpd.h>
#include "page_waveform.h"
#include "ftoa.h"
#include "sampling.h"
#include "serial.h"
#include "payload_parser.h"
// -------------------------------------------------------------------------------
// Read multiple samples from the ADC as JSON
typedef struct {
uint16_t total_count;
uint16_t done_count;
uint32_t freq;
bool success;
} tplReadSamplesJSON_state;
static httpd_cgi_state FLASH_FN tplSamplesJSON(MEAS_FORMAT fmt, HttpdConnData *connData, char *token, void **arg);
httpd_cgi_state FLASH_FN tplWaveformJSON(HttpdConnData *connData, char *token, void **arg)
{
return tplSamplesJSON(RAW, connData, token, arg);
}
httpd_cgi_state FLASH_FN tplFourierJSON(HttpdConnData *connData, char *token, void **arg)
{
return tplSamplesJSON(FFT, connData, token, arg);
}
static httpd_cgi_state FLASH_FN tplSamplesJSON(MEAS_FORMAT fmt, HttpdConnData *connData, char *token, void **arg)
{
char buff[128];
int len;
tplReadSamplesJSON_state *st = *arg;
if (token == NULL) {
// end of template, or connection closed.
if (st != NULL) free(st);
// make sure resources are freed
if (!meas_is_closed()) {
meas_close();
}
return HTTPD_CGI_DONE; // cleanup
}
if (st == NULL) {
// first call - allocate the struct
st = malloc(sizeof(tplReadSamplesJSON_state));
*arg = st;
// check how many samples are requested
uint16_t count = 1;
len = httpdFindArg(connData->getArgs, "n", buff, sizeof(buff));
if (len != -1) count = (uint16_t)atoi(buff);
if (count == 0) {
error("Count == 0");
st->success = false;
return HTTPD_CGI_DONE;
}
uint32_t freq = 0;
len = httpdFindArg(connData->getArgs, "fs", buff, sizeof(buff));
if (len != -1) freq = (uint32_t)atoi(buff);
if (freq == 0) {
error("Freq == 0");
st->success = false;
return HTTPD_CGI_DONE;
}
st->total_count = count;
st->done_count = 0;
st->freq = freq;
// --- REQUEST THE DATA ---
// This actually asks the STM32 over SBMP to start the ADC DMA,
// and we'll wait for the data to arrive.
st->success = meas_request_data(fmt, count, freq);
if (!st->success) {
error("Failed to start sampling");
}
}
// the "success" field is after the data,
// so if readout fails, success can be set to false.
if (strcmp(token, "values") == 0) {
if (!st->success) {
// failed to start sampling
return HTTPD_CGI_DONE;
}
// Wait for a chunk
uint8_t *chunk = NULL;
uint16_t chunk_len = 0;
// 10 secs or 100 ms - longer wait for intial data.
if (!meas_wait_for_chunk()) {
// meas session was already closed.
st->success = false;
return HTTPD_CGI_DONE;
}
chunk = meas_get_chunk(&chunk_len);
PayloadParser pp = pp_start(chunk, chunk_len);
// chunk of data...
for (; pp.ptr < pp.len; st->done_count++) {
// preceding comma if not the first number
if (st->done_count > 0) {
httpdSend(connData, ", ", 2);
}
float samp = pp_float(&pp);
my_ftoa(buff, samp, 3);
httpdSend(connData, buff, -1);
}
// wait for more data in this % tag
if (!meas_is_last_chunk()) {
meas_request_next_chunk();
return HTTPD_CGI_MORE; // more numbers to come
} else {
// we're done
meas_close();
return HTTPD_CGI_DONE;
}
} else if (strcmp(token, "stats") == 0) {
// the STATS json block
if (!st->success) {
httpdSend(connData, "null", 4);
} else {
// no %f in sprintf :(
MeasStats *stats = meas_get_stats();
httpdSend(connData, "{", 1);
sprintf(buff, "\"count\": %d, ", stats->count);
httpdSend(connData, buff, -1);
httpdSend(connData, "\"freq\": ", -1);
my_ftoa(buff, stats->freq, 3);
httpdSend(connData, buff, -1);
httpdSend(connData, ", \"min\": ", -1);
my_ftoa(buff, stats->min, 3);
httpdSend(connData, buff, -1);
httpdSend(connData, ", \"max\": ", -1);
my_ftoa(buff, stats->max, 3);
httpdSend(connData, buff, -1);
httpdSend(connData, ", \"rms\": ", -1);
my_ftoa(buff, stats->rms, 3);
httpdSend(connData, buff, -1);
if (fmt == FFT) {
// ... maybe something special for fft ...
}
sprintf(buff, ", \"format\": \"%s\"", fmt == RAW ? "RAW" : fmt == FFT ? "FFT" : "UNKNOWN");
httpdSend(connData, buff, -1);
httpdSend(connData, "}", 1);
}
} else if (strcmp(token, "success") == 0) {
// success status
httpdSend(connData, (st->success ? "true" : "false"), -1);
}
return HTTPD_CGI_DONE;
}

@ -1,10 +0,0 @@
#ifndef PAGE_WAVEFORM_H
#define PAGE_WAVEFORM_H
#include <httpd.h>
httpd_cgi_state tplWaveformJSON(HttpdConnData *connData, char *token, void **arg);
httpd_cgi_state tplFourierJSON(HttpdConnData *connData, char *token, void **arg);
#endif // PAGE_WAVEFORM_H

@ -0,0 +1,40 @@
#include "pers_cfg.h"
#include "datalink.h"
#include "serial.h"
#include "httpclient.h"
#include "ftoa.h"
#define RPT_CONF_MAGIC 0x24C595D5
PersistentCfg pers_conf;
/** Save reporting config to flash */
void FLASH_FN persistent_cfg_save(void)
{
info("Saving persistent user config");
system_param_save_with_protect(0x3D, &pers_conf, sizeof(PersistentCfg));
info("Config saved.");
}
/** Load the reporting config from flash */
void FLASH_FN persistent_cfg_load(void)
{
info("Loading persistent user config");
system_param_load(0x3D, 0, &pers_conf, sizeof(PersistentCfg));
if (pers_conf.magic != RPT_CONF_MAGIC) {
warn("Config block corrupted, reset to defaults.");
// invalid config, zero out
memset(&pers_conf, 0, sizeof(PersistentCfg));
pers_conf.magic = RPT_CONF_MAGIC;
// save fixed
persistent_cfg_save();
}
info("Config loaded.");
}

@ -0,0 +1,23 @@
#ifndef REPORTING_H
#define REPORTING_H
#include <esp8266.h>
typedef struct {
// ...
uint32_t magic;
} PersistentCfg;
/** Reporting config struct */
extern PersistentCfg pers_conf;
/** Save reporting config to flash */
void persistent_cfg_save(void);
/** Load the reporting config from flash */
void persistent_cfg_load(void);
#endif // REPORTING_H

@ -1,259 +0,0 @@
#include "reporting.h"
#include "datalink.h"
#include "serial.h"
#include "httpclient.h"
#include "ftoa.h"
#define RPT_CONF_MAGIC 0x24C595D5
ReportingResult rpt_result;
ReportingCfg rpt_conf;
static os_timer_t rpt_tim;
/** Timer cb */
static void FLASH_FN rpt_tim_cb(void *arg)
{
(void)arg;
// send report now...
if (rpt_conf.enabled) {
capture_and_report(true);
}
}
/** Stop / start timer & set interval based on rpt conf */
static FLASH_FN void set_timer(void)
{
os_timer_disarm(&rpt_tim);
if (rpt_conf.enabled) {
os_timer_setfn(&rpt_tim, rpt_tim_cb, NULL);
os_timer_arm(&rpt_tim, (int)(rpt_conf.interval * 1000), 1);
}
}
/** Fix unterminated strings, add magic, etc.. */
static FLASH_FN void normalize_rpt_conf(void)
{
// terminate strings
rpt_conf.feed[sizeof(rpt_conf.feed) - 1] = 0;
rpt_conf.key[sizeof(rpt_conf.key) - 1] = 0;
// set magic
rpt_conf.magic = RPT_CONF_MAGIC;
}
static FLASH_FN void dump_rpt_conf(void)
{
dbg("Enabled: %d | Interval: %d | Service: %s", rpt_conf.enabled, rpt_conf.interval, (rpt_conf.service == RPT_XIVELY ? "Xively" : "ThingSpeak"));
dbg("Key: %s | Feed: %s", rpt_conf.key, rpt_conf.feed);
}
/** Save reporting config to flash */
void FLASH_FN reporting_cfg_save(void)
{
normalize_rpt_conf(); // fix weirdness
info("Saving monitoring config");
dump_rpt_conf();
system_param_save_with_protect(0x3D, &rpt_conf, sizeof(ReportingCfg));
// start timer for the new interval time
set_timer();
info("Config saved.");
}
/** Load the reporting config from flash */
void FLASH_FN reporting_cfg_load(void)
{
info("Loading monitoring config");
system_param_load(0x3D, 0, &rpt_conf, sizeof(ReportingCfg));
if (rpt_conf.magic != RPT_CONF_MAGIC) {
warn("Config block corrupted, reset to defaults.");
// invalid config, zero out
memset(&rpt_conf, 0, sizeof(ReportingCfg));
rpt_conf.magic = RPT_CONF_MAGIC;
// save fixed
reporting_cfg_save();
} else {
dump_rpt_conf();
}
set_timer();
info("Config loaded.");
}
/** Called when response to Compare Ref is received */
static void FLASH_FN compare_ref_cb(SBMP_Endpoint *ep, SBMP_Datagram *dg, void **obj)
{
(void)obj;
sbmp_ep_remove_listener(ep, dg->session);
info("Measurement complete.");
rpt_result.success = (dg->type == DG_SUCCESS);
if (dg->type == DG_SUCCESS) {
PayloadParser pp = pp_start(dg->payload, dg->length);
rpt_result.deviation = pp_float(&pp);
rpt_result.i_rms = pp_float(&pp);
} else {
error("FAIL resp from sbmp.");
}
rpt_result.ready = true; // signal to waiting loop
}
/** Send report from rpt_result */
static void FLASH_FN do_send_report(void)
{
info("Sending report...");
char buf[100];
char *bb = buf;
char url_buf[200];
char hdrs_buf[100];
switch (rpt_conf.service) {
case RPT_XIVELY:
bb += sprintf(bb, "deviation,");
bb += my_ftoa(bb, rpt_result.deviation, 2);
bb += sprintf(bb, "\nI_rms,");
bb += my_ftoa(bb, rpt_result.i_rms, 2);
// URL
// We technically could use HTTPS, but it's not tested and probably buggy as hell
sprintf(url_buf, "http://api.xively.com/v2/feeds/%s.csv", rpt_conf.feed);
// Key
sprintf(hdrs_buf, "X-ApiKey: %s\r\n", rpt_conf.key);
http_put(url_buf, buf, hdrs_buf, http_callback_showstatus);
break;
case RPT_THINGSPEAK:
bb += sprintf(bb, "key=%s", rpt_conf.key);
bb += sprintf(bb, "&field1=");
bb += my_ftoa(bb, rpt_result.deviation, 2);
bb += sprintf(bb, "&field2=");
bb += my_ftoa(bb, rpt_result.i_rms, 2);
http_post("http://api.thingspeak.com/update", buf, NULL, http_callback_showstatus);
break;
}
}
/** Immediately send report to xively / thingspeak */
bool FLASH_FN capture_and_report(bool do_report)
{
info("Starting reporting measurmenet...");
if (rpt_result.busy) {
error("Capture busy.");
return false;
}
if (do_report) {
// don't report in AP mode
WIFI_MODE mode = wifi_get_opmode();
if (mode != STATION_MODE && mode != STATIONAP_MODE) {
warn("Not in station mode, cannot report.");
do_report = false;
}
}
rpt_result.ready = false;
rpt_result.busy = true;
uint16_t sesn;
sbmp_ep_send_message(dlnk_ep, DG_REQUEST_COMPARE_REF, NULL, 0, &sesn, NULL);
sbmp_ep_add_listener(dlnk_ep, sesn, compare_ref_cb, NULL);
// poll & wait for response
const int timeout = 500;
for (uint32_t i = 0; i < timeout * 100; i++) {
uart_poll();
if (rpt_result.ready) {
if (rpt_result.success && do_report) {
do_send_report();
}
rpt_result.busy = false;
return true; // done
}
os_delay_us(10);
system_soft_wdt_feed(); // Feed the dog, or it'll bite.
}
// timeout - remove listener
error("Measure timeout - no resp received.");
sbmp_ep_remove_listener(dlnk_ep, sesn);
rpt_result.busy = false;
return false;
}
static bool capt_ref_done;
static bool capt_ref_success;
/** Callback for "store ref" */
static void FLASH_FN store_ref_cb(SBMP_Endpoint *ep, SBMP_Datagram *dg, void **obj)
{
(void)obj;
sbmp_ep_remove_listener(ep, dg->session);
capt_ref_done = true;
capt_ref_success = (dg->type == DG_SUCCESS);
}
/** Capture reference vector for monitoring */
bool FLASH_FN capture_reporting_reference(void)
{
info("Capturing reference...");
if (rpt_result.busy) {
error("Capture busy.");
return false;
}
rpt_result.busy = true;
uint16_t sesn;
sbmp_ep_send_message(dlnk_ep, DG_REQUEST_STORE_REF, NULL, 0, &sesn, NULL);
sbmp_ep_add_listener(dlnk_ep, sesn, store_ref_cb, NULL);
capt_ref_done = false;
capt_ref_success = false;
const int timeout = 500;
for (uint32_t i = 0; i < timeout * 100; i++) {
uart_poll();
if (capt_ref_done) {
rpt_result.busy = false;
return capt_ref_success; // done
}
os_delay_us(10);
system_soft_wdt_feed(); // Feed the dog, or it'll bite.
}
// timeout - remove listener
error("Ref capture timeout - no resp received.");
sbmp_ep_remove_listener(dlnk_ep, sesn);
rpt_result.busy = false;
return false;
}

@ -1,52 +0,0 @@
#ifndef REPORTING_H
#define REPORTING_H
#include <esp8266.h>
typedef struct {
// 0
bool enabled;
// 4
uint32_t interval;
// 8
enum {
RPT_XIVELY,
RPT_THINGSPEAK
} service;
// 12
char feed[64];
// 76
char key[64];
// 80
uint32_t magic;
} ReportingCfg;
/** Comapre result is stored here */
typedef struct {
bool ready;
bool success;
bool busy;
float deviation;
float i_rms;
} ReportingResult;
/** Report result */
extern ReportingResult rpt_result;
/** Reporting config struct */
extern ReportingCfg rpt_conf;
/** Save reporting config to flash */
void reporting_cfg_save(void);
/** Load the reporting config from flash */
void reporting_cfg_load(void);
/** Immediately capture & send report to xively / thingspeak (or dont send - just for view) */
bool capture_and_report(bool do_report);
/** Capture reference vector for monitoring */
bool capture_reporting_reference(void);
#endif // REPORTING_H

@ -6,18 +6,15 @@
#include "httpdespfs.h" #include "httpdespfs.h"
#include "cgiwifi.h" #include "cgiwifi.h"
//#include "cgiflash.h" //#include "cgiflash.h"
//#include "auth.h" #include "auth.h"
//#include "cgiwebsocket.h" //#include "cgiwebsocket.h"
// user files // user files
#include "page_status.h" #include "page_home.h"
#include "page_waveform.h"
#include "page_about.h"
#include "page_monitoring.h"
#include "cgi_reset.h" #include "cgi_reset.h"
#include "cgi_ping.h" #include "cgi_ping.h"
#define WIFI_PROTECT 0 #define WIFI_PROTECT 1
#if WIFI_PROTECT #if WIFI_PROTECT
static int FLASH_FN myPassFn(HttpdConnData *connData, int no, char *user, int userLen, char *pass, int passLen); static int FLASH_FN myPassFn(HttpdConnData *connData, int no, char *user, int userLen, char *pass, int passLen);
@ -41,26 +38,12 @@ HttpdBuiltInUrl builtInUrls[] = {
// --- System control --- // --- System control ---
ROUTE_CGI("/system/reset", cgiResetDevice), ROUTE_CGI("/system/reset", cgiResetDevice),
ROUTE_CGI("/system/ping", cgiPing), ROUTE_CGI("/system/ping", cgiPing),
ROUTE_TPL_FILE("/system/status", tplSystemStatus, "/json/status.tpl"),
// --- Measurement ---
ROUTE_TPL_FILE("/measure/raw", tplWaveformJSON, "/json/samples.tpl"),
ROUTE_TPL_FILE("/measure/fft", tplFourierJSON, "/json/samples.tpl"),
ROUTE_CGI("/mon/compare", cgiMonCompare),
ROUTE_CGI("/mon/status", cgiMonStatus),
ROUTE_CGI("/mon/setref", cgiMonSetRef),
ROUTE_CGI("/mon/config", cgiMonitoringCfg), // redirects to /monitoring
// --- UI pages --- // --- UI pages ---
// System Status page // System Status page
ROUTE_TPL_FILE("/", tplSystemStatus, "/pages/status.tpl"), ROUTE_REDIRECT("/", "/home"),
ROUTE_TPL_FILE("/status", tplSystemStatus, "/pages/status.tpl"), ROUTE_REDIRECT("/home/", "/home"),
ROUTE_TPL_FILE("/about", tplAbout, "/pages/about.tpl"), ROUTE_TPL_FILE("/home", tplHome, "/pages/home.tpl"),
ROUTE_TPL_FILE("/monitoring", tplMonitoring, "/pages/monitoring.tpl"),
ROUTE_FILE("/waveform", "/pages/wfm.html"), // static file, html -> can use gzip
ROUTE_FILE("/fft", "/pages/fft.html"), // static file, html -> can use gzip
ROUTE_FILE("/spectrogram", "/pages/sgm.html"), // static file, html -> can use gzip
// --- WiFi config --- // --- WiFi config ---
#if WIFI_PROTECT #if WIFI_PROTECT
@ -104,8 +87,8 @@ static int FLASH_FN myPassFn(HttpdConnData *connData, int no, char *user, int us
(void)passLen; (void)passLen;
if (no == 0) { if (no == 0) {
os_strcpy(user, "admin"); os_strcpy(user, "wifiadmin");
os_strcpy(pass, "s3cr3t"); os_strcpy(pass, "amazing grace");
return 1; return 1;
//Add more users this way. Check against incrementing no for each user added. //Add more users this way. Check against incrementing no for each user added.
// } else if (no==1) { // } else if (no==1) {

@ -1,277 +0,0 @@
#include <esp8266.h>
#include <sbmp.h>
#include "datalink.h"
#include "sampling.h"
#include "serial.h"
// The buffer is big enough for 256 data bytes - 4*64
// chunk size for bulk transfer (must be multiple of 4 - length of uint32 / float)
// NOTE: If too large, strange errors can occur (problem with the underlying UART FIFO at high speed)
// the FIFO has 128 bytes, and should accomodate ideally the whole frame.
#define CHUNK_LEN 100
// Only one readout can happen at a time.
static struct {
bool waiting_for_measure; /*!< Still waiting for first data packet */
bool pending; /*!< Flag that data is currently being read */
uint16_t sesn; /*!< SBMP session of the readout sequence */
bool chunk_ready; /*!< Chunk was received and is ready for reading */
uint8_t received_chunk[CHUNK_LEN]; /*!< Copy of the latest received chunk of data */
uint16_t received_chunk_size; /*!< Size of the chunk in latest_chunk_copy */
uint32_t est_sampling_time; /*!< Estimated time in millis before data is captured and readout starts */
uint32_t pos;
uint32_t total;
MEAS_FORMAT format; /*!< Requested data format */
// --- data stats ---
MeasStats stats;
} rd;
bool FLASH_FN meas_is_closed(void)
{
return !rd.pending;
}
uint32_t FLASH_FN meas_estimate_duration(uint32_t count, uint32_t freq)
{
return (uint32_t)((count*1000.0f) / freq) + SAMP_RD_TMEO;
}
/** Wait for one chunk, with possible retries */
bool FLASH_FN meas_wait_for_chunk(void)
{
for (int retry_count = 0; retry_count < SAMP_RD_RETRY_COUNT; retry_count++) {
uint32_t timeout = (rd.waiting_for_measure ? rd.est_sampling_time: SAMP_RD_TMEO);
// dbg("Chunk read total timeout = %d ms", timeout);
for (uint32_t i = 0; i < timeout*100; i++) {
uart_poll(); // can stop measure & start first chunk, if rx offer
// check for closed connection - aborted by peer?
if (meas_is_closed()) {
error("Session closed by peer, readout failed.");
return false; // assume already cleaned up
}
if (meas_chunk_ready()) {
// yay!!
return true;
}
os_delay_us(10);
system_soft_wdt_feed(); // Feed the dog, or it'll bite.
}
// Data still not Rx
if (rd.waiting_for_measure) {
// only one try in this case
error("Sampling aborted due to timeout (no data offered)");
sbmp_bulk_abort(dlnk_ep, rd.sesn); // send abort msg
meas_close(); // close
return false;
} else {
warn("Data chunk not rx in time.");
dbg("Requesting again (try %d of %d).", retry_count+1, SAMP_RD_RETRY_COUNT);
sbmp_bulk_request(dlnk_ep, rd.pos, CHUNK_LEN, rd.sesn);
}
}
error("Retry count exhausted!");
sbmp_bulk_abort(dlnk_ep, rd.sesn);
meas_close();
return false;
}
/** request next chunk */
void FLASH_FN meas_request_next_chunk(void)
{
if (!rd.pending) return;
rd.chunk_ready = false; // invalidate the current chunk, so waiting for chunk is possible.
sbmp_bulk_request(dlnk_ep, rd.pos, CHUNK_LEN, rd.sesn);
}
/** Check if chunk ready to be read */
bool FLASH_FN meas_chunk_ready(void)
{
return rd.pending && rd.chunk_ready;
}
/**
* @brief Get received chunk. NULL if none.
*
* The array is valid until next chunk is requested.
* Chunk length in bytes is stored in the argument.
*/
uint8_t FLASH_FN *meas_get_chunk(uint16_t *chunk_len)
{
if (!rd.pending) {
warn("Meas not pending, cant get chunk");
return NULL;
}
if (!rd.chunk_ready) {
warn("Rx chunk not ready");
return NULL;
}
*chunk_len = rd.received_chunk_size;
return rd.received_chunk;
}
/** Check if this was the last chunk */
bool FLASH_FN meas_is_last_chunk(void)
{
return rd.pos >= rd.total;
}
/** Terminate the readout. */
void FLASH_FN meas_close(void)
{
if (!rd.pending) return; // ignore this call
sbmp_ep_remove_listener(dlnk_ep, rd.sesn);
rd.pending = false;
info("Transfer closed.");
}
MeasStats FLASH_FN *meas_get_stats(void)
{
return &rd.stats;
}
static void FLASH_FN request_data_sesn_listener(SBMP_Endpoint *ep, SBMP_Datagram *dg, void **obj)
{
(void)obj;
// dbg("Received msg in session %d, dg type %d", dg->session, dg->type);
PayloadParser pp;
switch (dg->type) {
case DG_BULK_OFFER:// Data ready notification
// data is ready to be read
pp = pp_start(dg->payload, dg->length);
rd.pos = 0;
rd.total = pp_u32(&pp);
rd.waiting_for_measure = false; // "pending" flag remains set
// --- here start the user data (common) ---
rd.stats.count = pp_u32(&pp);
rd.stats.freq = pp_float(&pp);
rd.stats.min = pp_float(&pp);
rd.stats.max = pp_float(&pp);
rd.stats.rms = pp_float(&pp);
// --- user data end ---
if (rd.format == FFT) {
// TODO read extra FFT stats ??
}
info("Offered %d bytes of data, starting readout.", rd.total);
// request first chunk
sbmp_bulk_request(ep, rd.pos, CHUNK_LEN, dg->session);
break;
case DG_BULK_DATA: // data received
// Process the received data
memcpy(rd.received_chunk, dg->payload, dg->length);
rd.chunk_ready = true;
rd.received_chunk_size = dg->length;
// move the pointer for next request
rd.pos += dg->length;
// --- Now we wait for the CGI func to retrieve the chunk and send it to the browser. ---
if (rd.pos >= rd.total) {
info("Transfer is complete.");
// transfer complete
// ask peer to release the buffer & go idle
sbmp_bulk_abort(ep, dg->session);
}
break;
case DG_BULK_ABORT: // Peer resets the readout
// this is unlikely
warn("Bulk transfer aborted by peer.");
goto cleanup;
}
return;
cleanup:
// remove the listener
meas_close();
}
bool FLASH_FN meas_request_data(MEAS_FORMAT format, uint16_t count, uint32_t freq)
{
bool suc = false;
info("Requesting data capture - %d samples @ %d Hz, fmt %d.", count, freq, format);
if (rd.pending) {
warn("Acquire request already in progress; shouldn't happen, IGNORING");
}
if (sbmp_ep_handshake_status(dlnk_ep) != SBMP_HSK_SUCCESS) {
error("Hsk status not SUCCESS, can't request data.");
return false;
}
// clean up
rd.chunk_ready = false;
rd.pos = 0;
rd.total = 0;
rd.pending = true;
rd.format = format;
memset(&rd.stats, 0, sizeof(MeasStats)); // clear the stats obj
rd.est_sampling_time = meas_estimate_duration(count, freq);
// start a message
uint16_t sesn = 0;
suc = sbmp_ep_start_message(dlnk_ep, format, sizeof(uint16_t)+sizeof(uint32_t), &sesn); // format enum matches the message types
if (!suc) goto fail;
// register the session listener
suc = sbmp_ep_add_listener(dlnk_ep, sesn, request_data_sesn_listener, NULL);
if (!suc) {
// discard the unfinished outgoing packet
sbmp_frm_reset_tx(&dlnk_ep->frm);
goto fail;
}
rd.sesn = sesn;
rd.waiting_for_measure = true; // waiting for acquisition module to start sending data
// request N values
sbmp_ep_send_u16(dlnk_ep, count);
// at freq F
sbmp_ep_send_u32(dlnk_ep, freq);
dbg("Request sent, session nr %d", sesn);
return true;
fail:
rd.waiting_for_measure = false;
rd.pending = false;
return false;
}

@ -1,75 +0,0 @@
#ifndef SAMPLING_H
#define SAMPLING_H
#include <esp8266.h>
#include <httpd.h>
#include "datalink.h"
// ms
#define SAMP_RD_TMEO 300
#define SAMP_RD_RETRY_COUNT 3
#define SAMP_RD_TMEO_TOTAL (SAMP_RD_TMEO*SAMP_RD_RETRY_COUNT+200)
typedef struct {
uint32_t count;
float freq; // actual frequency - not exact due to the prescaller limitations
float min;
float max;
float rms;
} MeasStats;
typedef enum {
RAW = DG_REQUEST_RAW, // same as the SBMP packet numbers used to request it
FFT = DG_REQUEST_FFT
} MEAS_FORMAT;
/**
* Reading procedure
* -----------------
*
* 1. meas_request_data(count)
* 2. wait for meas_chunk_ready() == true
* 3. meas_get_chunk() to read the chunk last received
* 4. if meas_is_last_chunk(), call meas_close() and DONE.
* 5. meas_request_next_chunk() to get more data, goto 2
*
* Waiting should have a timeout, if data not received, return error status to client.
*/
/** Estimate time needed for sampling (with some space) */
uint32_t meas_estimate_duration(uint32_t count,uint32_t freq);
/** Request data from the sampling module. Count - number of samples. */
bool meas_request_data(MEAS_FORMAT format, uint16_t count, uint32_t freq); // TODO specify what kind of data - currently direct samples.
/** request next chunk */
void meas_request_next_chunk(void);
/** Check if chunk ready to be read */
bool meas_chunk_ready(void);
/** Check if closed (if data was expected, this means the peer aborted the transaction) */
bool meas_is_closed(void);
/** Get the stats struct */
MeasStats *meas_get_stats(void);
/**
* @brief Get received chunk. NULL if none.
*
* The array is valid until next chunk is requested.
* Chunk length in bytes is stored in the argument.
*/
uint8_t *meas_get_chunk(uint16_t *chunk_len);
/** Check if this was the last chunk */
bool meas_is_last_chunk(void);
/** Terminate the readout. */
void meas_close(void);
/** Wait for one chunk, with possible retries. True = chunk ready, false = failed. */
bool meas_wait_for_chunk(void);
#endif // SAMPLING_H

@ -55,7 +55,7 @@
/** /**
* @brief Enable detailed logging (only for debugging, disable for better performance). * @brief Enable detailed logging (only for debugging, disable for better performance).
*/ */
#define SBMP_DEBUG 0 #define SBMP_DEBUG 1
// here are the actual logging functions // here are the actual logging functions

@ -23,7 +23,7 @@
#include "routes.h" #include "routes.h"
#include "fw_version.h" #include "fw_version.h"
#include "reporting.h" #include "pers_cfg.h"
#include "wificontrol.h" #include "wificontrol.h"
extern HttpdBuiltInUrl builtInUrls[]; extern HttpdBuiltInUrl builtInUrls[];
@ -84,7 +84,7 @@ void user_init(void)
serialInit(); serialInit();
uptime_timer_init(); uptime_timer_init();
banner("*** AC current analyser - WiFi module ***"); banner("*** WiFi Demo - Stepper motor control ***");
banner_info("(c) Ondrej Hruska, 2016"); banner_info("(c) Ondrej Hruska, 2016");
banner_info("Katedra mereni FEL CVUT"); banner_info("Katedra mereni FEL CVUT");
banner_info(""); banner_info("");
@ -95,6 +95,12 @@ void user_init(void)
// reset button etc // reset button etc
ioInit(); ioInit();
struct softap_config wificfg;
wifi_softap_get_config(&wificfg);
sprintf((char*)wificfg.ssid, "esp_motor_demo");
wificfg.ssid_len = strlen((char*)wificfg.ssid);
wifi_softap_set_config(&wificfg);
// set up SBMP // set up SBMP
datalinkInit(); datalinkInit();
@ -113,8 +119,7 @@ void user_init(void)
httpdInit(builtInUrls, 80); httpdInit(builtInUrls, 80);
// start reporting timer persistent_cfg_load();
reporting_cfg_load();
printf(LOG_EOL); printf(LOG_EOL);
banner_info("Ready"); banner_info("Ready");

@ -3,7 +3,7 @@
#include "wificontrol.h" #include "wificontrol.h"
#include <sbmp.h> #include <sbmp.h>
#include "datalink.h" #include "datalink.h"
#include "reporting.h" #include "pers_cfg.h"
// --- AP --- // --- AP ---
@ -95,6 +95,11 @@ static void FLASH_FN wifistatus_cb(void *arg)
{ {
(void)arg; (void)arg;
if (sbmp_ep_handshake_status(dlnk_ep) != SBMP_HSK_SUCCESS) {
warn("SBMP hsk not success, can't send WiFi status!");
return;
}
WIFI_MODE opmode = wifi_get_opmode(); WIFI_MODE opmode = wifi_get_opmode();
STATION_STATUS status = STATION_IDLE; STATION_STATUS status = STATION_IDLE;
@ -102,11 +107,10 @@ static void FLASH_FN wifistatus_cb(void *arg)
status = wifi_station_get_connect_status(); status = wifi_station_get_connect_status();
} }
if (sbmp_ep_start_message(dlnk_ep, DG_WIFI_STATUS, 4, NULL)) { if (sbmp_ep_start_message(dlnk_ep, DG_WIFI_STATUS, 3, NULL)) {
sbmp_ep_send_u8(dlnk_ep, opmode); // 1-Client, 2-SoftAP, 3-STA+AP sbmp_ep_send_u8(dlnk_ep, opmode); // 1-Client, 2-SoftAP, 3-STA+AP
sbmp_ep_send_u8(dlnk_ep, status == STATION_GOT_IP); // Station connected sbmp_ep_send_u8(dlnk_ep, status == STATION_GOT_IP); // Station connected
sbmp_ep_send_u8(dlnk_ep, wps_in_progress); // 1 = WPS is in progress sbmp_ep_send_u8(dlnk_ep, wps_in_progress); // 1 = WPS is in progress
sbmp_ep_send_u8(dlnk_ep, rpt_conf.enabled); // Reporting is enabled
} }
} }

Loading…
Cancel
Save