Merge branch 'wifi-experimental' into master_sq9mdd

# Conflicts:
#	platformio.ini
#	src/TTGO_T-Beam_LoRa_APRS_config.h
pull/16/head
Łukasz Nidecki 2021-02-24 21:30:42 +01:00
commit 5367cbd63b
13 zmienionych plików z 1624 dodań i 64 usunięć

5
.gitignore vendored
Wyświetl plik

@ -8,4 +8,7 @@
CMakeLists.txt
CMakeListsPrivate.txt
CMakeListsUser.txt
cmake-build-*
cmake-build-*
/include/version.h
/data_embed/*.out
/src/TTGO_T-Beam_LoRa_APRS.ino.cpp

Wyświetl plik

@ -0,0 +1,132 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>TTGO-T-Beam-LoRa-APRS <!--VERSION--></title>
<link rel="stylesheet" href="/style.css" type="text/css">
<script src="/js.js" type="text/javascript"></script>
<link rel="icon" href="data:,">
</head>
<body>
<div class="container">
<section>
<div class="grid-container full">
<h2 class="u-full-width">WiFi Settings</h2>
</div>
<article>
<form action="/save_wifi_cfg" method="post">
<div class="grid-container quarters">
<div>
<div id="wifi_list">
</div>
<input type="button" value="Scan WiFi" id="scan_wifi_btn" onclick="scanWifi();">
</div>
<div>
<label for="wifi_ssid">SSID</label>
<input class="u-full-width" type="text" name="wifi_ssid" placeholder="Your Wifi SSID"
id="wifi_ssid">
</div>
<div>
<label for="wifi_password">Password</label>
<input class="u-full-width" type="password" name="wifi_password" id="wifi_password">
</div>
<div>
<input class="button-primary" type="submit" value="Save">
</div>
</div>
</form>
</article>
</section>
<section>
<div class="grid-container full">
<h2 class="u-full-width">APRS Settings</h2>
</div>
<article>
<form action="/save_aprs_cfg" method="post">
<div class="grid-container quarters">
<div>
<label for="aprs_callsign">Callsign and SSID</label>
<input class="u-full-width" type="text" minlength="3" name="aprs_callsign"
placeholder="NOCALL-1" id="aprs_callsign">
</div>
<div>
<label for="aprs_relay_path">Relay Path</label>
<input class="u-full-width" type="text" minlength="0" name="aprs_relay_path"
id="aprs_relay_path">
</div>
<div>
<label for="aprs_s_table">Symbol Table</label>
<input class="u-full-width" type="text" minlength="1" maxlength="1" name="aprs_s_table"
id="aprs_s_table">
</div>
<div>
<label for="aprs_symbol">Symbol</label>
<input class="u-full-width" type="text" minlength="1" maxlength="1" name="aprs_symbol"
id="aprs_symbol">
</div>
<div>
<label for="aprs_comment">Comment</label>
<input class="u-full-width" type="text" minlength="0" maxlength="64" name="aprs_comment"
id="aprs_comment">
</div>
<div>
<label for="aprs_batt">Show Battery</label>
<input name="aprs_batt" id="aprs_batt" type="checkbox" value="1">
</div>
<div>
<label for="aprs_alt">Show Altitude</label>
<input name="aprs_alt" id="aprs_alt" type="checkbox" value="1">
</div>
</div>
<div class="grid-container quarters">
<div>
<label for="aprs_fixed_beac">Fixed Beacon (when&nbsp;no&nbsp;GPS)</label>
<input name="aprs_fixed_beac" id="aprs_fixed_beac" type="checkbox" value="1">
</div>
<div>
<label for="aprs_fb_interv">Fixed Beacon Interval (s)</label>
<input name="aprs_fb_interv" id="aprs_fb_interv" type="number" min="120">
</div>
<div>
<label for="aprs_lat_p">Latitude Preset</label>
<input class="u-full-width" type="text" minlength="0" name="aprs_lat_p" id="aprs_lat_p">
</div>
<div>
<label for="aprs_lon_p">Longitude Preset</label>
<input class="u-full-width" type="text" minlength="0" name="aprs_lon_p" id="aprs_lon_p">
</div>
</div>
<div class="grid-container full">
<div>
<input class="button-primary u-full-width" type="submit" value="Save">
</div>
</div>
</form>
</article>
</section>
<section>
<div class="grid-container full">
<h2 class="u-full-width">Actions</h2>
</div>
<article>
<div class="grid-container quarters">
<form action="/reboot" method="post">
<div>
<input class="button-primary" type="submit" value="Reboot">
</div>
</form>
<form action="/restore" method="post">
<div>
<input class="button-primary" type="submit" value="Factory reset">
</div>
</form>
</div>
</article>
</section>
</div>
<footer>
<p><!--VERSION--></p>
</footer>
</body>
</html>

42
data_embed/js.js 100644
Wyświetl plik

@ -0,0 +1,42 @@
/**/
function scanWifi() {
let scanBtn = document.getElementById('scan_wifi_btn');
let wifiListContainer = document.getElementById("wifi_list");
wifiListContainer.innerHTML = 'Scanning...';
scanBtn.disabled = true;
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
scanBtn.disabled = false;
if (this.readyState == 4 && this.status == 200) {
wifiListContainer.innerHTML = this.responseText;
const networks_found_list = document.querySelector('#networks_found_list');
networks_found_list.addEventListener('change', event => {
document.getElementById('wifi_ssid').value = networks_found_list.value;
});
}
};
xhttp.open("GET", "/scan_wifi", true);
xhttp.send();
}
window.onload = function () {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
const response = JSON.parse(this.responseText);
for (const [key, value] of Object.entries(response)) {
let element = document.getElementById(key);
if (element){
if (element.type && element.type == "checkbox"){
element.checked = value;
} else {
element.value = value;
}
}
}
}
};
xhttp.open("GET", "/cfg", true);
xhttp.send();
};

Wyświetl plik

@ -0,0 +1,843 @@
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
/* Document
========================================================================== */
/**
* 1. Correct the line height in all browsers.
* 2. Prevent adjustments of font size after orientation changes in iOS.
*/
html {
line-height: 1.15; /* 1 */
-webkit-text-size-adjust: 100%; /* 2 */
}
/* Sections
========================================================================== */
/**
* Remove the margin in all browsers.
*/
body {
margin: 0;
}
/**
* Render the `main` element consistently in IE.
*/
main {
display: block;
}
/**
* Correct the font size and margin on `h1` elements within `section` and
* `article` contexts in Chrome, Firefox, and Safari.
*/
h1 {
font-size: 2em;
margin: 0.67em 0;
}
/* Grouping content
========================================================================== */
/**
* 1. Add the correct box sizing in Firefox.
* 2. Show the overflow in Edge and IE.
*/
hr {
box-sizing: content-box; /* 1 */
height: 0; /* 1 */
overflow: visible; /* 2 */
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
pre {
font-family: monospace, monospace; /* 1 */
font-size: 1em; /* 2 */
}
/* Text-level semantics
========================================================================== */
/**
* Remove the gray background on active links in IE 10.
*/
a {
background-color: transparent;
}
/**
* 1. Remove the bottom border in Chrome 57-
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
*/
abbr[title] {
border-bottom: none; /* 1 */
text-decoration: underline; /* 2 */
text-decoration: underline dotted; /* 2 */
}
/**
* Add the correct font weight in Chrome, Edge, and Safari.
*/
b,
strong {
font-weight: bolder;
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
code,
kbd,
samp {
font-family: monospace, monospace; /* 1 */
font-size: 1em; /* 2 */
}
/**
* Add the correct font size in all browsers.
*/
small {
font-size: 80%;
}
/**
* Prevent `sub` and `sup` elements from affecting the line height in
* all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
/* Embedded content
========================================================================== */
/**
* Remove the border on images inside links in IE 10.
*/
img {
border-style: none;
}
/* Forms
========================================================================== */
/**
* 1. Change the font styles in all browsers.
* 2. Remove the margin in Firefox and Safari.
*/
button,
input,
optgroup,
select,
textarea {
font-family: inherit; /* 1 */
font-size: 100%; /* 1 */
line-height: 1.15; /* 1 */
margin: 0; /* 2 */
}
/**
* Show the overflow in IE.
* 1. Show the overflow in Edge.
*/
button,
input { /* 1 */
overflow: visible;
}
/**
* Remove the inheritance of text transform in Edge, Firefox, and IE.
* 1. Remove the inheritance of text transform in Firefox.
*/
button,
select { /* 1 */
text-transform: none;
}
/**
* Correct the inability to style clickable types in iOS and Safari.
*/
button,
[type="button"],
[type="reset"],
[type="submit"] {
-webkit-appearance: button;
}
/**
* Remove the inner border and padding in Firefox.
*/
button::-moz-focus-inner,
[type="button"]::-moz-focus-inner,
[type="reset"]::-moz-focus-inner,
[type="submit"]::-moz-focus-inner {
border-style: none;
padding: 0;
}
/**
* Restore the focus styles unset by the previous rule.
*/
button:-moz-focusring,
[type="button"]:-moz-focusring,
[type="reset"]:-moz-focusring,
[type="submit"]:-moz-focusring {
outline: 1px dotted ButtonText;
}
/**
* Correct the padding in Firefox.
*/
fieldset {
padding: 0.35em 0.75em 0.625em;
}
/**
* 1. Correct the text wrapping in Edge and IE.
* 2. Correct the color inheritance from `fieldset` elements in IE.
* 3. Remove the padding so developers are not caught out when they zero out
* `fieldset` elements in all browsers.
*/
legend {
box-sizing: border-box; /* 1 */
color: inherit; /* 2 */
display: table; /* 1 */
max-width: 100%; /* 1 */
padding: 0; /* 3 */
white-space: normal; /* 1 */
}
/**
* Add the correct vertical alignment in Chrome, Firefox, and Opera.
*/
progress {
vertical-align: baseline;
}
/**
* Remove the default vertical scrollbar in IE 10+.
*/
textarea {
overflow: auto;
}
/**
* 1. Add the correct box sizing in IE 10.
* 2. Remove the padding in IE 10.
*/
[type="checkbox"],
[type="radio"] {
box-sizing: border-box; /* 1 */
padding: 0; /* 2 */
}
/**
* Correct the cursor style of increment and decrement buttons in Chrome.
*/
[type="number"]::-webkit-inner-spin-button,
[type="number"]::-webkit-outer-spin-button {
height: auto;
}
/**
* 1. Correct the odd appearance in Chrome and Safari.
* 2. Correct the outline style in Safari.
*/
[type="search"] {
-webkit-appearance: textfield; /* 1 */
outline-offset: -2px; /* 2 */
}
/**
* Remove the inner padding in Chrome and Safari on macOS.
*/
[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
/**
* 1. Correct the inability to style clickable types in iOS and Safari.
* 2. Change font properties to `inherit` in Safari.
*/
::-webkit-file-upload-button {
-webkit-appearance: button; /* 1 */
font: inherit; /* 2 */
}
/* Interactive
========================================================================== */
/*
* Add the correct display in Edge, IE 10+, and Firefox.
*/
details {
display: block;
}
/*
* Add the correct display in all browsers.
*/
summary {
display: list-item;
}
/* Misc
========================================================================== */
/**
* Add the correct display in IE 10+.
*/
template {
display: none;
}
/**
* Add the correct display in IE 10.
*/
[hidden] {
display: none;
}
/*
* Barebones V3
* Copyright 2019 Steve Cochran
*
* Based of Skeleton by Dave Gamache
*
* Free to use under the MIT license.
*/
/* Table of contents
- Media Breakpoints
- Variables
- Grid
- Base Styles
- Typography
- Links
- Buttons
- Forms
- Lists
- Code
- Tables
- Spacing
- Utilities
- Clearing
- Media Queries
*/
/* ENV Variables
*/
/* Media breakpoint variables for use in media queries
* Note: this section is currently commented out pending release of
* final CSS env() spec
* Breakpoints based on
* https://medium.freecodecamp.org/the-100-correct-way-to-do-css-breakpoints-88d6a5ba1862
*
*/
/* CSS Variables
*/
html {
/* default theme: light background, dark text, blue accent */
--theme-hue: 0; /* white */
--accent-hue: 194; /* blue */
--text-color-richer: hsl(var(--theme-hue), 0%, 5%); /* #0d0d0d */
--text-color-normal: hsl(var(--theme-hue), 0%, 13%); /* #222222 text color; button:hover:focus color */
--text-color-softer: hsl(var(--theme-hue), 0%, 33%); /* #555555 button color; button:hover border */
--accent-color: hsl(var(--accent-hue), 86%, 57%); /* #33C3F0 link; button-primary bg+border; textarea,select:focus border */
--accent-color-hover: hsl(var(--accent-hue), 76%, 49%); /* #1EAEDB link hover; button-primary:hover:focus bg+border */
--border-color: hsl(var(--theme-hue), 0%, 73%); /* #bbbbbb button border */
--border-color-softer: hsl(var(--theme-hue), 0%, 82%); /* #d1d1d1 textarea,select,code,td,hr border */
--background-color: white; /* transparent body background; textarea,select background */
--background-color-softer: hsl(var(--theme-hue), 0%, 95%);
--code-background: hsl(var(--theme-hue), 0%, 95%); /* #f1f1f1 code background*/
--button-primary-color: white;
/* Note: Skeleton was based off a 10px font sizing for REM */
/* 62.5% of typical 16px browser default = 10px */
--base-font-size: 62.5%;
/* Grid Defaults - default to match orig skeleton settings */
--grid-max-width: 960px;
}
/* Dark Theme
Note: prefers-color-scheme selector support is still limited, but
included for future and an example of defining a different base 'theme'
*/
@media (prefers-color-scheme: dark) {
:html {
/* dark theme: light background, dark text, blue accent */
--theme-hue: 0; /* black */
--accent-hue: 194; /* blue */
--text-color-richer: hsl(var(--theme-hue), 0%, 95%); /* */
--text-color-normal: hsl(var(--theme-hue), 0%, 80%); /* text color; button:hover:focus color */
--text-color-softer: hsl(var(--theme-hue), 0%, 67%); /* button color; button:hover border */
--accent-color: hsl(var(--accent-hue), 76%, 49%); /* link; button-primary bg+border; textarea,select:focus border */
--accent-color-hover: hsl(var(--accent-hue), 86%, 57%); /* link hover; button-primary:hover:focus bg+border */
--border-color: hsl(var(--theme-hue), 0%, 27%); /* button border */
--border-color-softer: hsl(var(--theme-hue), 0%, 20%); /* textarea,select,code,td,hr border */
--background-color: hsl(var(--theme-hue), 0%, 12%); /* body background; textarea,select background */
--background-color-softer: hsl(var(--theme-hue), 0%, 18%);
--code-background: hsl(var(--theme-hue), 0%, 5%); /* code background*/
--button-primary-color: white;
}
img.value-img {
filter: invert(0.8);
}
/* TODO - test dialing back image intensity on dark background
img {
opacity: .80;
transition: opacity .5s ease-in-out;
}
img:hover {
opacity: 1;
}
*/
}
/* Grid
*/
/* CSS Grid depends much more on CSS than HTML, so there is less boilerplate
than with skeleton. Only basic 1-4 column grids are included.
Any additional needs should be made using custom CSS directives */
.grid-container {
position: relative;
max-width: var(--grid-max-width);
margin: 0 auto;
padding: 20px;
text-align: center;
display: grid;
grid-gap: 20px;
gap: 20px;
/* by default use min 200px wide columns auto-fit into width */
grid-template-columns: minmax(200px, 1fr);
}
/* grids to 3 columns above mobile sizes */
@media (min-width: 600px) {
.grid-container {
grid-template-columns: repeat(3, 1fr);
padding: 10px 0;
}
/* basic grids */
.grid-container.fifths {
grid-template-columns: repeat(5, 1fr);
}
.grid-container.quarters {
grid-template-columns: repeat(4, 1fr);
}
.grid-container.thirds {
grid-template-columns: repeat(3, 1fr);
}
.grid-container.halves {
grid-template-columns: repeat(2, 1fr);
}
.grid-container.full {
grid-template-columns: 1fr;
}
}
/* Base Styles
*/
html {
font-size: var(--base-font-size);
scroll-behavior: smooth;
}
body {
font-size: 1.6rem; /* changed from 15px in orig skeleton */
line-height: 1.6;
font-weight: 400;
font-family: "Raleway", "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif;
color: var(--text-color-normal);
background-color: var(--background-color);;
}
/* Typography
*/
h1, h2, h3, h4, h5, h6 {
margin-top: 0;
margin-bottom: 2rem;
font-weight: 300; }
h1 { font-size: 4.0rem; line-height: 1.2; letter-spacing: -.1rem;}
h2 { font-size: 3.6rem; line-height: 1.25; letter-spacing: -.1rem; }
h3 { font-size: 3.0rem; line-height: 1.3; letter-spacing: -.1rem; }
h4 { font-size: 2.4rem; line-height: 1.35; letter-spacing: -.08rem; }
h5 { font-size: 1.8rem; line-height: 1.5; letter-spacing: -.05rem; }
h6 { font-size: 1.5rem; line-height: 1.6; letter-spacing: 0; }
/* Larger than phablet */
@media (min-width: 600px) {
h1 { font-size: 5.0rem; }
h2 { font-size: 4.2rem; }
h3 { font-size: 3.6rem; }
h4 { font-size: 3.0rem; }
h5 { font-size: 2.4rem; }
h6 { font-size: 1.5rem; }
}
p {
margin-top: 0; }
/* Links
*/
a {
color: var(--accent-color); }
a:hover {
color: var(--accent-color-hover); }
/* Buttons
*/
.button,
button,
input[type="submit"],
input[type="reset"],
input[type="button"] {
display: inline-block;
height: 38px;
padding: 0 30px;
color: var(--text-color-softer);
text-align: center;
font-size: 11px;
font-weight: 600;
line-height: 38px;
letter-spacing: .1rem;
text-transform: uppercase;
text-decoration: none;
white-space: nowrap;
background-color: transparent;
border-radius: 4px;
border: 1px solid var(--border-color);
cursor: pointer;
box-sizing: border-box; }
.button:hover,
button:hover,
input[type="submit"]:hover,
input[type="reset"]:hover,
input[type="button"]:hover,
.button:focus,
button:focus,
input[type="submit"]:focus,
input[type="reset"]:focus,
input[type="button"]:focus {
color: var(--text-color-normal);
border-color: var(--text-color-softer);
outline: 0; }
.button.button-primary,
button.button-primary,
input[type="submit"].button-primary,
input[type="reset"].button-primary,
input[type="button"].button-primary {
color: var(--button-primary-color);
background-color: var(--accent-color);
border-color: var(--accent-color); }
.button.button-primary:hover,
button.button-primary:hover,
input[type="submit"].button-primary:hover,
input[type="reset"].button-primary:hover,
input[type="button"].button-primary:hover,
.button.button-primary:focus,
button.button-primary:focus,
input[type="submit"].button-primary:focus,
input[type="reset"].button-primary:focus,
input[type="button"].button-primary:focus {
color: var(--button-primary-color);
background-color: var(--accent-color-hover);
border-color: var(--accent-color-hover); }
/* Forms
*/
input[type="email"],
input[type="number"],
input[type="search"],
input[type="text"],
input[type="tel"],
input[type="url"],
input[type="password"],
textarea,
select {
height: 38px;
padding: 6px 10px; /* The 6px vertically centers text on FF, ignored by Webkit */
background-color: var(--background-color);
border: 1px solid var(--border-color-softer);
border-radius: 4px;
box-shadow: none;
box-sizing: border-box; }
/* Removes awkward default styles on some inputs for iOS */
input[type="email"],
input[type="number"],
input[type="search"],
input[type="text"],
input[type="tel"],
input[type="url"],
input[type="password"],
input[type="button"],
input[type="submit"],
textarea {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none; }
textarea {
min-height: 65px;
padding-top: 6px;
padding-bottom: 6px; }
input[type="email"]:focus,
input[type="number"]:focus,
input[type="search"]:focus,
input[type="text"]:focus,
input[type="tel"]:focus,
input[type="url"]:focus,
input[type="password"]:focus,
textarea:focus,
select:focus {
border: 1px solid var(--accent-color);
outline: 0; }
label,
legend {
display: block;
margin-bottom: .5rem;
font-weight: 600; }
fieldset {
padding: 0;
border-width: 0; }
input[type="checkbox"],
input[type="radio"] {
display: inline; }
label > .label-body {
display: inline-block;
margin-left: .5rem;
font-weight: normal; }
/* Lists
*/
ul {
list-style: circle inside; }
ol {
list-style: decimal inside; }
ol, ul {
padding-left: 0;
margin-top: 0; }
ul ul, ul ol, ol ol, ol ul {
font-size: 100%;
margin: 1rem 0 1rem 3rem;
color: var(--text-color-softer);
}
li {
margin-bottom: 0.5rem; }
/* Code
*/
code {
padding: .2rem .5rem;
margin: 0 .2rem;
font-size: 90%;
white-space: nowrap;
background: var(--code-background);
border: 1px solid var(--border-color-softer);
border-radius: 4px; }
pre > code {
display: block;
padding: 1rem 1.5rem;
white-space: pre;
overflow: auto; }
/* Tables
*/
th,
td {
padding: 12px 15px;
text-align: left;
border-bottom: 1px solid var(--border-color-softer); }
th:first-child,
td:first-child {
padding-left: 0; }
th:last-child,
td:last-child {
padding-right: 0; }
/* Spacing
*/
button,
.button {
margin-bottom: 1rem; }
input,
textarea,
select,
fieldset {
margin-bottom: 1.5rem; }
pre,
blockquote,
dl,
figure,
table,
p,
ul,
ol,
form {
margin-bottom: 2.5rem; }
/* Utilities
*/
.u-full-width {
width: 100%;
box-sizing: border-box; }
.u-max-full-width {
max-width: 100%;
box-sizing: border-box; }
.u-pull-right {
float: right; }
.u-pull-left {
float: left; }
.u-align-left {
text-align: left; }
.u-align-right {
text-align: right; }
/* Misc
*/
hr {
margin-top: 3rem;
margin-bottom: 3.5rem;
border-width: 0;
border-top: 1px solid var(--border-color-softer); }
/* Clearing
*/
/* Self Clearing Goodness */
.container:after,
.row:after,
.u-cf {
content: "";
display: table;
clear: both; }
/* Media Queries
*/
/*
Note: The best way to structure the use of media queries is to create the queries
near the relevant code. For example, if you wanted to change the styles for buttons
on small devices, paste the mobile query code up in the buttons section and style it
there.
*/
/* Larger than mobile (default point when grid becomes active) */
@media (min-width: 600px) {
}
/* Larger than phablet */
@media (min-width: 900px) {
.container {
max-width: 900px;
}
}
/* Larger than tablet */
@media (min-width: 1200px) {}
footer {
text-align: center
}
.container {
position: relative;
margin: 0 auto;
}
section {
border-radius: 4px;
border: 1px solid var(--border-color);
margin-top: 5px;
padding: 5px;
}

Wyświetl plik

@ -6,8 +6,11 @@
#include "BluetoothSerial.h"
extern BluetoothSerial SerialBT;
#endif
#if defined(ENABLE_WIFI)
#include "taskWebServer.h"
#endif
extern QueueHandle_t tncToSendQueue;
extern QueueHandle_t tncReceivedQueue;
void taskTNC(void *parameter);
[[noreturn]] void taskTNC(void *parameter);

Wyświetl plik

@ -0,0 +1,44 @@
#include <Arduino.h>
#include "TTGO_T-Beam_LoRa_APRS_config.h"
#include <WiFi.h>
#include <WebServer.h>
#include <ESPmDNS.h>
#include <Preferences.h>
#ifndef TASK_WEBSERVER
#define TASK_WEBSERVER
#define ENABLE_PREFERENCES
extern Preferences preferences;
#ifdef KISS_PROTOCOL
extern WiFiServer tncServer;
#endif
// MAX 15 chars for preferenece key!!!
static const char *const PREF_APRS_CALLSIGN = "aprs_callsign";
static const char *const PREF_APRS_RELAY_PATH = "aprs_relay_path";
static const char *const PREF_APRS_RELAY_PATH_INIT = "aprs_relay_init";
static const char *const PREF_APRS_SYMBOL_TABLE = "aprs_s_table";
static const char *const PREF_APRS_SYMBOL = "aprs_symbol";
static const char *const PREF_APRS_COMMENT = "aprs_comment";
static const char *const PREF_APRS_COMMENT_INIT = "aprs_comm_init";
static const char *const PREF_APRS_SHOW_ALTITUDE = "aprs_alt";
static const char *const PREF_APRS_SHOW_ALTITUDE_INIT = "aprs_alt_init";
static const char *const PREF_APRS_SHOW_BATTERY = "aprs_batt";
static const char *const PREF_APRS_SHOW_BATTERY_INIT = "aprs_batt_init";
static const char *const PREF_APRS_LATITUDE_PRESET = "aprs_lat_p";
static const char *const PREF_APRS_LATITUDE_PRESET_INIT = "aprs_lat_p_init";
static const char *const PREF_APRS_LONGITUDE_PRESET = "aprs_lon_p";
static const char *const PREF_APRS_LONGITUDE_PRESET_INIT = "aprs_lon_p_init";
static const char *const PREF_APRS_FIXED_BEACON_PRESET = "aprs_fixed_beac";
static const char *const PREF_APRS_FIXED_BEACON_PRESET_INIT = "aprs_fix_b_init";
static const char *const PREF_APRS_FIXED_BEACON_INTERVAL_PRESET = "aprs_fb_interv";
static const char *const PREF_APRS_FIXED_BEACON_INTERVAL_PRESET_INIT = "aprs_fb_in_init";
typedef struct {
String callsign;
} tWebServerCfg;
[[noreturn]] void taskWebServer(void *parameter);
#endif

Wyświetl plik

@ -9,39 +9,37 @@
; https://docs.platformio.org/page/projectconf.html
[env]
platform = espressif32
framework = arduino
monitor_speed = 115200
build_flags = -Wl,--gc-sections,--relax
lib_deps =
board_build.partitions = no_ota.csv
board_build.embed_files =
data_embed/index.html.out
data_embed/style.css.out
data_embed/js.js.out
extra_scripts =
pre:tools/buildscript_versioning.py
pre:tools/compress_assets.py
lib_deps =
RadioHead
TinyGPSPlus
#DHT sensor library for ESPx
Adafruit SSD1306
Adafruit GFX Library
Adafruit Unified Sensor
AXP202X_Library
OneWire
#DallasTemperature
#adafruit/Adafruit BME280 Library@^2.1.2
[env:ttgo-t-beam-v1.0]
platform = espressif32
board = ttgo-t-beam
framework = arduino
build_flags = -DT_BEAM_V1_0
build_flags = ${env.build_flags} -DT_BEAM_V1_0
[env:ttgo-t-beam-v0.7]
platform = espressif32
[env:ttgo-t-beam_v0.7]
board = ttgo-t-beam
framework = arduino
build_flags = -DT_BEAM_V0_7
build_flags = ${env.build_flags} -DT_BEAM_V0_7
[env:ttgo-lora32-v2]
platform = espressif32
board = ttgo-lora32-v1
framework = arduino
[env:ttgo-lora32-v1]
platform = espressif32
board = ttgo-lora32-v1
framework = arduino
board = ttgo-lora32-v1

Wyświetl plik

@ -25,6 +25,10 @@
#ifdef KISS_PROTOCOL
#include "taskTNC.h"
#endif
#ifdef ENABLE_WIFI
#include "taskWebServer.h"
#endif
#include "version.h"
// I2C LINES
#define I2C_SDA 21
@ -63,6 +67,16 @@ boolean gps_state = true;
boolean key_up = true;
boolean t_lock = false;
boolean fixed_beacon_enabled = false;
#ifdef SHOW_ALT
boolean showAltitude = true;
#else
boolean showAltitude = false;
#endif
#ifdef SHOW_BATT
boolean showBattery = true;
#else
boolean showBattery = false;
#endif
// Variables and Constants
String loraReceivedFrameString = ""; //data on buff is copied to this string
@ -111,6 +125,12 @@ ulong time_delay = 0;
float average_course[ANGLE_AVGS];
float avg_c_y, avg_c_x;
uint8_t txPower = TXdbmW;
#ifdef ENABLE_WIFI
tWebServerCfg webServerCfg;
#endif
static const adc_atten_t atten = ADC_ATTEN_DB_6;
static const adc_unit_t unit = ADC_UNIT_1;
#ifdef T_BEAM_V1_0
@ -148,10 +168,9 @@ void prepareAPRSFrame(){
double Tspeed=0, Tcourse=0;
String Speedx, Coursex;
int i;
#ifdef SHOW_ALT
String Altx;
int Talt;
#endif
String Altx;
int Talt;
Tlat=gps.location.lat();
Tlon=gps.location.lng();
@ -178,31 +197,31 @@ void prepareAPRSFrame(){
outString += ">APLM0:!";
}
if(gps_state && gps.location.isValid()){
if(gps_state && gps.location.isValid()) {
outString += aprsSymbolTable;
ax25_base91enc(helper_base91, 4, aprs_lat);
for (i=0; i<4; i++) {
for (i = 0; i < 4; i++) {
outString += helper_base91[i];
}
ax25_base91enc(helper_base91, 4, aprs_lon);
for (i=0; i<4; i++) {
for (i = 0; i < 4; i++) {
outString += helper_base91[i];
}
outString += aprsSymbol;
ax25_base91enc(helper_base91, 1, (uint32_t) Tcourse/4 );
ax25_base91enc(helper_base91, 1, (uint32_t) Tcourse / 4);
outString += helper_base91[0];
ax25_base91enc(helper_base91, 1, (uint32_t) (log1p(Tspeed)/0.07696));
ax25_base91enc(helper_base91, 1, (uint32_t) (log1p(Tspeed) / 0.07696));
outString += helper_base91[0];
outString += "H";
#ifdef SHOW_ALT
Talt=gps.altitude.meters() * 3.28d;
if (showAltitude) {
Talt = gps.altitude.meters() * 3.28d;
Altx = Talt;
outString += "/A=";
for (i = 0; i < (6-Altx.length()); ++i) {
for (i = 0; i < (6 - Altx.length()); ++i) {
outString += "0";
}
outString += Talt;
#endif
}
}else{
outString += aprsLatPreset;
outString += aprsSymbolTable;
@ -210,11 +229,13 @@ void prepareAPRSFrame(){
outString += aprsSymbol;
}
outString += aprsComment;
#ifdef SHOW_BATT // battery is not frame part move after comment
if (showBattery) {
outString += " Batt=";
outString += String(BattVolts, 2);
outString += ("V");
#endif
}
#ifdef KISS_PROTOCOL
sendToTNC(outString);
#else
@ -355,8 +376,88 @@ void sendTelemetryFrame() {
// + SETUP --------------------------------------------------------------+//
void setup(){
#ifdef DIGI_PATH
relay_path = DIGI_PATH;
#else
relay_path = "";
#endif
#ifdef FIXED_BEACON_EN
fixed_beacon_enabled = true;
fixed_beacon_enabled = true;
#endif
#ifdef ENABLE_PREFERENCES
int clear_preferences = 0;
if(digitalRead(BUTTON)==LOW){
clear_preferences = 1;
}
preferences.begin("cfg", false);
aprsSymbolTable = preferences.getString(PREF_APRS_SYMBOL_TABLE);
if (aprsSymbolTable.isEmpty()){
preferences.putString(PREF_APRS_SYMBOL_TABLE, APRS_SYMBOL_TABLE);
aprsSymbolTable = preferences.getString(PREF_APRS_SYMBOL_TABLE);
}
aprsSymbol = preferences.getString(PREF_APRS_SYMBOL);
if (aprsSymbol.isEmpty()){
preferences.putString(PREF_APRS_SYMBOL, APRS_SYMBOL);
aprsSymbol = preferences.getString(PREF_APRS_SYMBOL, APRS_SYMBOL);
}
if (!preferences.getBool(PREF_APRS_COMMENT_INIT)){
preferences.putBool(PREF_APRS_COMMENT_INIT, true);
preferences.putString(PREF_APRS_COMMENT, MY_COMMENT);
}
aprsComment = preferences.getString(PREF_APRS_COMMENT);
if (!preferences.getBool(PREF_APRS_RELAY_PATH_INIT)){
preferences.putBool(PREF_APRS_RELAY_PATH_INIT, true);
preferences.putString(PREF_APRS_RELAY_PATH, DIGI_PATH);
}
relay_path = preferences.getString(PREF_APRS_RELAY_PATH);
if (!preferences.getBool(PREF_APRS_SHOW_ALTITUDE_INIT)){
preferences.putBool(PREF_APRS_SHOW_ALTITUDE_INIT, true);
preferences.putBool(PREF_APRS_SHOW_ALTITUDE, showAltitude);
}
showAltitude = preferences.getBool(PREF_APRS_SHOW_ALTITUDE);
if (!preferences.getBool(PREF_APRS_SHOW_BATTERY_INIT)){
preferences.putBool(PREF_APRS_SHOW_BATTERY_INIT, true);
preferences.putBool(PREF_APRS_SHOW_BATTERY, showBattery);
}
showBattery = preferences.getBool(PREF_APRS_SHOW_BATTERY);
if (!preferences.getBool(PREF_APRS_LATITUDE_PRESET_INIT)){
preferences.putBool(PREF_APRS_LATITUDE_PRESET_INIT, true);
preferences.putString(PREF_APRS_LATITUDE_PRESET, LATIDUDE_PRESET);
}
aprsLatPreset = preferences.getString(PREF_APRS_LATITUDE_PRESET);
if (!preferences.getBool(PREF_APRS_LONGITUDE_PRESET_INIT)){
preferences.putBool(PREF_APRS_LONGITUDE_PRESET_INIT, true);
preferences.putString(PREF_APRS_LONGITUDE_PRESET, LONGITUDE_PRESET);
}
aprsLonPreset = preferences.getString(PREF_APRS_LONGITUDE_PRESET);
if (!preferences.getBool(PREF_APRS_FIXED_BEACON_PRESET_INIT)){
preferences.putBool(PREF_APRS_FIXED_BEACON_PRESET_INIT, true);
preferences.putBool(PREF_APRS_FIXED_BEACON_PRESET, fixed_beacon_enabled);
}
fixed_beacon_enabled = preferences.getBool(PREF_APRS_FIXED_BEACON_PRESET);
if (!preferences.getBool(PREF_APRS_FIXED_BEACON_INTERVAL_PRESET_INIT)){
preferences.putBool(PREF_APRS_FIXED_BEACON_INTERVAL_PRESET_INIT, true);
preferences.putInt(PREF_APRS_FIXED_BEACON_INTERVAL_PRESET, fix_beacon_interval/1000);
}
fix_beacon_interval = preferences.getInt(PREF_APRS_FIXED_BEACON_INTERVAL_PRESET) * 1000;
if (clear_preferences){
delay(1000);
if(digitalRead(BUTTON)==LOW){
clear_preferences = 2;
}
}
#endif
for (int i=0;i<ANGLE_AVGS;i++) { // set average_course to "0"
@ -386,13 +487,32 @@ void setup(){
if(!display.begin(SSD1306_SWITCHCAPVCC, SSD1306_ADDRESS)) {
for(;;); // Don't proceed, loop forever
}
#ifdef ENABLE_PREFERENCES
if (clear_preferences == 2){
writedisplaytext("LoRa-APRS","","","Factory reset","","",0);
delay(1000);
if(digitalRead(BUTTON)==LOW){
clear_preferences = 3;
preferences.clear();
preferences.end();
writedisplaytext("LoRa-APRS","","Factory reset","Done!","","",0);
delay(2000);
ESP.restart();
} else {
writedisplaytext("LoRa-APRS","","Factory reset","Cancel","","",0);
delay(2000);
}
}
#endif
writedisplaytext("LoRa-APRS","","Init:","Display OK!","","",1000);
Tcall = prepareCallsign(String(CALLSIGN));
#ifdef DIGI_PATH
relay_path = DIGI_PATH;
#else
relay_path = "";
#ifdef ENABLE_PREFERENCES
Tcall = preferences.getString(PREF_APRS_CALLSIGN);
if (Tcall.isEmpty()){
preferences.putString(PREF_APRS_CALLSIGN, String(CALLSIGN));
Tcall = preferences.getString(PREF_APRS_CALLSIGN);
}
#endif
if (!rf95.init()) {
@ -405,7 +525,7 @@ void setup(){
}
writedisplaytext("LoRa-APRS","","Init:","RF95 OK!","","",250);
writedisplaytext(" "+Tcall,"","Init:","Waiting for GPS","","",250);
xTaskCreate(taskGPS, "taskGPS", 10000, nullptr, 1, nullptr);
xTaskCreate(taskGPS, "taskGPS", 5000, nullptr, 1, nullptr);
writedisplaytext(" "+Tcall,"","Init:","GPS Task Created!","","",250);
#ifndef T_BEAM_V1_0
adc1_config_width(ADC_WIDTH_BIT_12);
@ -428,6 +548,12 @@ void setup(){
SerialBT.begin(String("TTGO LORA APRS ") + Tcall);
writedisplaytext("LoRa-APRS","","Init:","BT OK!","","",250);
#endif
#ifdef ENABLE_WIFI
webServerCfg = {.callsign = Tcall};
xTaskCreate(taskWebServer, "taskWebServer", 40000, (void*)(&webServerCfg), 1, nullptr);
writedisplaytext("LoRa-APRS","","Init:","WiFi task started"," =:-) ","",250);
#endif
writedisplaytext("LoRa-APRS","","Init:","FINISHED OK!"," =:-) ","",250);
writedisplaytext("","","","","","",0);
time_to_refresh = millis() + showRXTime;

Wyświetl plik

@ -6,12 +6,12 @@
// licensed under CC BY-NC-SA
// USER DATA - USE THESE LINES TO MODIFY YOUR PREFERENCES
#define KISS_PROTOCOL // If enabled send and receive data in SIMPLE KISS format to serial port
#define CALLSIGN "SQ9MDD-11" // enter your callsign here - less then 6 letter callsigns please add "spaces" so total length is 6 (without SSID)
#define DIGI_PATH "WIDE1-1" // one hope please (WIDE1-1)
#define FIXED_BEACON_EN // allows cyclic sending of a bicon when GPS is turned off
#define LATIDUDE_PRESET "5215.03N" // please in APRS notation: DDMM.mmN or DDMM.mmS (used for manual or fixed beacon sending)
#define LONGITUDE_PRESET "02055.59E" // please in APRS notation: DDDMM.mmE or DDDMM.mmW (used for manual or fixed beacon sending)
#define KISS_PROTOCOL // If enabled send and receive data in SIMPLE KISS format to serial port
#define CALLSIGN "NOCALL-0" // enter your callsign here - less then 6 letter callsigns please add "spaces" so total length is 6 (without SSID)
#define DIGI_PATH "WIDE1-1" // one hope please (WIDE1-1)
#define FIXED_BEACON_EN // allows cyclic sending of a bicon when GPS is turned off
#define LATIDUDE_PRESET "0000.00N" // please in APRS notation: DDMM.mmN or DDMM.mmS (used for manual or fixed beacon sending)
#define LONGITUDE_PRESET "00000.00E" // please in APRS notation: DDDMM.mmE or DDDMM.mmW (used for manual or fixed beacon sending)
#define APRS_SYMBOL_TABLE "/"
#define APRS_SYMBOL "[" // other symbols are: "[" => RUNNER, "b" => BICYCLE, "<" => MOTORCYCLE, "R" => Recreation Vehicle
#define MY_COMMENT "Lora Tracker" // add your coment here - if empty then no comment is sent
@ -26,7 +26,12 @@
//#define ENABLE_TNC_SELF_TELEMETRY
//#define LOCAL_KISS_ECHO // echoing KISS frame back
//#define T_BEAM_V1_0 // if enabled t-beam v1.0 disabled t-beam V.0.7
// AUTOMATICALLY SET BY platformio.ini env!
//#define KISS_DEBUG
#define ENABLE_WIFI
#define NETWORK_TNC_PORT 8001
//#define ENABLE_WIFI_CLIENT_DEBUG
#define MAX_TIME_TO_NEXT_TX 360000L // TRANSMIT INTERVAL set here MAXIMUM time in ms(!) for smart beaconing - minimum time is always 1 min = 60 secs = 60000L !!!
#define FIX_BEACON_INTERVAL 1800000L // Fixed beacon interwal (when GPS is disabled and FIXED_BEACON_EN is enabled) 30min default

Wyświetl plik

@ -1,20 +1,58 @@
#include "taskTNC.h"
#ifdef ENABLE_BLUETOOTH
BluetoothSerial SerialBT;
#endif
String inTNCData = "";
QueueHandle_t tncToSendQueue = nullptr;
QueueHandle_t tncReceivedQueue = nullptr;
#ifdef ENABLE_WIFI
#define MAX_WIFI_CLIENTS 6
WiFiClient * clients[MAX_WIFI_CLIENTS];
typedef void (*f_connectedClientCallback_t) (WiFiClient *, int, const String *);
void iterateWifiClients(f_connectedClientCallback_t callback, const String *data){
for (int i=0; i<MAX_WIFI_CLIENTS; i++) {
auto client = clients[i];
if (client != nullptr) {
if (client->connected()) {
callback(client, i, data);
} else {
#ifdef ENABLE_WIFI_CLIENT_DEBUG
Serial.println(String("Disconnected client ") + client->remoteIP().toString() + ":" + client->remotePort());
#endif
delete client;
clients[i] = nullptr;
}
}
}
}
#endif
#ifdef ENABLE_WIFI
#define IN_TNC_BUFFERS (2+MAX_WIFI_CLIENTS)
#else
#define IN_TNC_BUFFERS 2
#endif
String inTNCDataBuffers[IN_TNC_BUFFERS];
QueueHandle_t tncToSendQueue = nullptr;
/**
* Handle incoming TNC KISS data character
* @param character
*/
void handleKISSData(char character) {
inTNCData.concat(character);
if (character == (char) FEND && inTNCData.length() > 3) {
const String &TNC2DataFrame = decode_kiss(inTNCData);
void handleKISSData(char character, int bufferIndex) {
String *inTNCData = &inTNCDataBuffers[bufferIndex];
if (inTNCData->length() == 0 && character != (char) FEND){
// kiss frame begins with C0
return;
}
inTNCData->concat(character);
if (character == (char) FEND && inTNCData->length() > 3) {
const String &TNC2DataFrame = decode_kiss(*inTNCData);
#ifdef LOCAL_KISS_ECHO
Serial.print(inTNCData);
@ -23,18 +61,30 @@ void handleKISSData(char character) {
SerialBT.print(inTNCData);
}
#endif
#ifdef ENABLE_WIFI
iterateWifiClients([](WiFiClient *client, const String *data){
if (client->connected()){
client->print(*data);
client->flush();
}
}, &inTNCData);
#endif
#endif
auto *buffer = new String();
buffer->concat(TNC2DataFrame);
if (xQueueSend(tncToSendQueue, &buffer, (1000 / portTICK_PERIOD_MS)) != pdPASS){
delete buffer;
}
inTNCData = "";
inTNCData->clear();
}
if (inTNCData->length() > 255){
// just in case of garbage input reset data
inTNCData->clear();
}
}
void taskTNC(void *parameter) {
[[noreturn]] void taskTNC(void *parameter) {
tncToSendQueue = xQueueCreate(4,sizeof(String *));
tncReceivedQueue = xQueueCreate(4,sizeof(String *));
String *loraReceivedFrameString = nullptr;
@ -42,23 +92,75 @@ void taskTNC(void *parameter) {
while (true) {
while (Serial.available() > 0) {
char character = Serial.read();
handleKISSData(character);
handleKISSData(character, 0);
}
#ifdef ENABLE_BLUETOOTH
if (SerialBT.hasClient()) {
while (SerialBT.available() > 0) {
char character = SerialBT.read();
handleKISSData(character);
if (SerialBT.hasClient()) {
while (SerialBT.available() > 0) {
char character = SerialBT.read();
handleKISSData(character, 1);
}
}
}
#endif
#ifdef ENABLE_WIFI
WiFiClient new_client = tncServer.available();
if (new_client.connected()){
bool new_client_handled = false;
for (int i=0; i < MAX_WIFI_CLIENTS; i++) {
auto client = clients[i];
if (client == nullptr) {
client = new WiFiClient(new_client);
clients[i] = client;
new_client_handled = true;
#ifdef ENABLE_WIFI_CLIENT_DEBUG
Serial.println(String("New client #") +String(i) + ": " + client->remoteIP().toString() + ":" + client->remotePort());
#endif
break;
}
}
#ifdef ENABLE_WIFI_CLIENT_DEBUG
for (int i = 0; i < MAX_WIFI_CLIENTS; ++i) {
auto client = clients[i];
if (client != nullptr){
Serial.println(String("Client #") +String(i) + ": " + client->remoteIP().toString() + ":" + client->remotePort());
}
}
#endif
if (!new_client_handled){
#ifdef ENABLE_WIFI_CLIENT_DEBUG
Serial.println(String("Refusing client "));
#endif
new_client.stop();
}
}
iterateWifiClients([](WiFiClient * client, int clientIdx, const String * unused){
while (client->available() > 0) {
char character = client->read();
handleKISSData(character, 2+clientIdx);
}
}, nullptr);
#endif
if (xQueueReceive(tncReceivedQueue, &loraReceivedFrameString, (1 / portTICK_PERIOD_MS)) == pdPASS) {
Serial.print(encode_kiss(*loraReceivedFrameString));
const String &kissEncoded = encode_kiss(*loraReceivedFrameString);
Serial.print(kissEncoded);
#ifdef ENABLE_BLUETOOTH
if (SerialBT.hasClient()){
SerialBT.print(encode_kiss(*loraReceivedFrameString));
SerialBT.print(kissEncoded);
}
#endif
#ifdef ENABLE_WIFI
iterateWifiClients([](WiFiClient *client, int clientIdx, const String *data){
if (client->connected()){
client->print(*data);
client->flush();
}
}, &kissEncoded);
#endif
delete loraReceivedFrameString;
}
vTaskDelay(50 / portTICK_PERIOD_MS);

Wyświetl plik

@ -0,0 +1,210 @@
#include "taskWebServer.h"
/**
* @see board_build.embed_txtfiles in platformio.ini
*/
extern const char web_index_html[] asm("_binary_data_embed_index_html_out_start");
extern const char web_index_html_end[] asm("_binary_data_embed_index_html_out_end");
extern const char web_style_css[] asm("_binary_data_embed_style_css_out_start");
extern const char web_style_css_end[] asm("_binary_data_embed_style_css_out_end");
extern const char web_js_js[] asm("_binary_data_embed_js_js_out_start");
extern const char web_js_js_end[] asm("_binary_data_embed_js_js_out_end");
String apSSID = "";
String apPassword = "xxxxxxxxxx";
WebServer server(80);
Preferences preferences;
#ifdef KISS_PROTOCOL
WiFiServer tncServer(NETWORK_TNC_PORT);
#endif
void sendCacheHeader() { server.sendHeader("Cache-Control", "max-age=3600"); }
void sendGzipHeader() { server.sendHeader("Content-Encoding", "gzip"); }
String jsonEscape(String s){
s.replace("\"", "\\\"");
s.replace("\\", "\\\\");
return s;
}
String jsonLineFromPreferenceString(const char *preferenceName, bool last=false){
return String("\"") + preferenceName + "\":\"" + jsonEscape(preferences.getString(preferenceName)) + (last ? + R"(")" : + R"(",)");
}
String jsonLineFromPreferenceBool(const char *preferenceName, bool last=false){
return String("\"") + preferenceName + "\":" + (preferences.getBool(preferenceName) ? "true" : "false") + (last ? + R"()" : + R"(,)");
}
String jsonLineFromPreferenceInt(const char *preferenceName, bool last=false){
return String("\"") + preferenceName + "\":" + (preferences.getInt(preferenceName)) + (last ? + R"()" : + R"(,)");
}
String jsonLineFromString(const char *name, const char *value, bool last=false){
return String("\"") + name + "\":" + jsonEscape(value) + (last ? + R"()" : + R"(,)");
}
void handle_NotFound(){
sendCacheHeader();
server.send(404, "text/plain", "Not found");
}
void handle_Index() {
sendGzipHeader();
server.send_P(200, "text/html", web_index_html, web_index_html_end - web_index_html);
}
void handle_Style() {
sendCacheHeader();
sendGzipHeader();
server.send_P(200, "text/css", web_style_css, web_style_css_end - web_style_css);
}
void handle_Js() {
sendCacheHeader();
sendGzipHeader();
server.send_P(200, "text/javascript", web_js_js, web_js_js_end-web_js_js);
}
void handle_ScanWifi() {
String listResponse = R"(<label for="networks_found_list">Networks found:</label><select class="u-full-width" id="networks_found_list">)";
int n = WiFi.scanNetworks();
listResponse += "<option value=\"\">Select Network</option>";
for (int i = 0; i < n; ++i) {
listResponse += "<option value=\""+WiFi.SSID(i)+"\">" + WiFi.SSID(i) + "</option>";
}
listResponse += "</select>";
server.send(200,"text/html", listResponse);
}
void handle_SaveWifiCfg() {
if (!server.hasArg("wifi_ssid") || !server.hasArg("wifi_password")){
server.send(500, "text/plain", "Invalid request");
}
if (!server.arg("wifi_ssid").length() || !server.arg("wifi_password").length()){
server.send(403, "text/plain", "Empty SSID or Password");
}
preferences.putString("wifi_ssid", server.arg("wifi_ssid"));
preferences.putString("wifi_password", server.arg("wifi_password"));
server.sendHeader("Location", "/");
server.send(302,"text/html", "");
}
void handle_Reboot() {
server.sendHeader("Location", "/");
server.send(302,"text/html", "");
ESP.restart();
}
void handle_Restore() {
server.sendHeader("Location", "/");
server.send(302,"text/html", "");
preferences.clear();
preferences.end();
ESP.restart();
}
void handle_Cfg() {
String jsonData = "{";
jsonData += R"("wifi_ssid":")" + jsonEscape(preferences.getString("wifi_ssid")) + R"(",)";
jsonData += R"("wifi_password":")" + jsonEscape((preferences.getString("wifi_password").isEmpty() ? String("") : "*")) + R"(",)";
jsonData += jsonLineFromPreferenceString(PREF_APRS_CALLSIGN);
jsonData += jsonLineFromPreferenceString(PREF_APRS_RELAY_PATH);
jsonData += jsonLineFromPreferenceString(PREF_APRS_SYMBOL_TABLE);
jsonData += jsonLineFromPreferenceString(PREF_APRS_SYMBOL);
jsonData += jsonLineFromPreferenceString(PREF_APRS_COMMENT);
jsonData += jsonLineFromPreferenceString(PREF_APRS_LATITUDE_PRESET);
jsonData += jsonLineFromPreferenceString(PREF_APRS_LONGITUDE_PRESET);
jsonData += jsonLineFromPreferenceInt(PREF_APRS_FIXED_BEACON_INTERVAL_PRESET);
jsonData += jsonLineFromPreferenceBool(PREF_APRS_SHOW_BATTERY);
jsonData += jsonLineFromPreferenceBool(PREF_APRS_FIXED_BEACON_PRESET);
jsonData += jsonLineFromPreferenceBool(PREF_APRS_SHOW_ALTITUDE);
jsonData += jsonLineFromString("FreeHeap", String(ESP.getFreeHeap()).c_str());
jsonData += jsonLineFromString("HeapSize", String(ESP.getHeapSize()).c_str());
jsonData += jsonLineFromString("FreeSketchSpace", String(ESP.getFreeSketchSpace()).c_str(), true);
jsonData += "}";
server.send(200,"application/json", jsonData);
}
void handle_SaveAPRSCfg() {
if (server.hasArg(PREF_APRS_CALLSIGN) && !server.arg(PREF_APRS_CALLSIGN).isEmpty()){
preferences.putString(PREF_APRS_CALLSIGN, server.arg(PREF_APRS_CALLSIGN));
}
if (server.hasArg(PREF_APRS_SYMBOL_TABLE) && !server.arg(PREF_APRS_SYMBOL_TABLE).isEmpty()){
preferences.putString(PREF_APRS_SYMBOL_TABLE, server.arg(PREF_APRS_SYMBOL_TABLE));
}
if (server.hasArg(PREF_APRS_SYMBOL) && !server.arg(PREF_APRS_SYMBOL).isEmpty()){
preferences.putString(PREF_APRS_SYMBOL, server.arg(PREF_APRS_SYMBOL));
}
if (server.hasArg(PREF_APRS_RELAY_PATH)){
preferences.putString(PREF_APRS_RELAY_PATH, server.arg(PREF_APRS_RELAY_PATH));
}
if (server.hasArg(PREF_APRS_COMMENT)){
preferences.putString(PREF_APRS_COMMENT, server.arg(PREF_APRS_COMMENT));
}
if (server.hasArg(PREF_APRS_LATITUDE_PRESET)){
preferences.putString(PREF_APRS_LATITUDE_PRESET, server.arg(PREF_APRS_LATITUDE_PRESET));
}
if (server.hasArg(PREF_APRS_FIXED_BEACON_INTERVAL_PRESET)){
preferences.putInt(PREF_APRS_FIXED_BEACON_INTERVAL_PRESET, server.arg(PREF_APRS_FIXED_BEACON_INTERVAL_PRESET).toInt());
}
if (server.hasArg(PREF_APRS_LONGITUDE_PRESET)){
preferences.putString(PREF_APRS_LONGITUDE_PRESET, server.arg(PREF_APRS_LONGITUDE_PRESET));
}
preferences.putBool(PREF_APRS_SHOW_BATTERY, server.hasArg(PREF_APRS_SHOW_BATTERY));
preferences.putBool(PREF_APRS_SHOW_ALTITUDE, server.hasArg(PREF_APRS_SHOW_ALTITUDE));
preferences.putBool(PREF_APRS_FIXED_BEACON_PRESET, server.hasArg(PREF_APRS_FIXED_BEACON_PRESET));
server.sendHeader("Location", "/");
server.send(302,"text/html", "");
}
[[noreturn]] void taskWebServer(void *parameter) {
auto *webServerCfg = (tWebServerCfg*)parameter;
apSSID = webServerCfg->callsign + " AP";
server.on("/", handle_Index);
server.on("/favicon.ico", handle_NotFound);
server.on("/style.css", handle_Style);
server.on("/js.js", handle_Js);
server.on("/scan_wifi", handle_ScanWifi);
server.on("/save_wifi_cfg", handle_SaveWifiCfg);
server.on("/reboot", handle_Reboot);
server.on("/cfg", handle_Cfg);
server.on("/save_aprs_cfg", handle_SaveAPRSCfg);
server.on("/restore", handle_Restore);
server.onNotFound(handle_NotFound);
String wifi_password = preferences.getString("wifi_password");
String wifi_ssid = preferences.getString("wifi_ssid");
if (!wifi_password.length() || !wifi_ssid.length()){
WiFi.softAP(apSSID.c_str(), apPassword.c_str());
} else {
WiFi.begin(wifi_ssid.c_str(), wifi_password.c_str());
Serial.println("Connecting to " + wifi_ssid);
while (WiFi.status() != WL_CONNECTED) {
Serial.print("Not connected: ");
Serial.println((int)WiFi.status());
vTaskDelay(500/portTICK_PERIOD_MS);
}
}
server.begin();
#ifdef KISS_PROTOCOL
tncServer.begin();
#endif
if (MDNS.begin(webServerCfg->callsign.c_str())) {
MDNS.setInstanceName(webServerCfg->callsign + " TTGO LoRa APRS TNC " + TXFREQ + "MHz");
MDNS.addService("http", "tcp", 80);
#ifdef KISS_PROTOCOL
MDNS.addService("kiss-tnc", "tcp", NETWORK_TNC_PORT);
#endif
}
while (true){
server.handleClient();
vTaskDelay(5/portTICK_PERIOD_MS);
}
}

Wyświetl plik

@ -0,0 +1,39 @@
FILENAME_BUILDNO = '.pio/versioning'
FILENAME_VERSION_H = 'include/version.h'
version = 'v0.1.'
import datetime
build_no = 0
try:
with open(FILENAME_BUILDNO) as f:
build_no = int(f.readline()) + 1
except:
print('Starting build number from 1..')
build_no = 1
with open(FILENAME_BUILDNO, 'w+') as f:
f.write(str(build_no))
print('Build number: {}'.format(build_no))
version_string = "{} - {}".format(version+str(build_no), datetime.datetime.now())
hf = """
#ifndef BUILD_NUMBER
#define BUILD_NUMBER "{}"
#endif
#ifndef VERSION
#define VERSION "{}"
#endif
#ifndef VERSION_SHORT
#define VERSION_SHORT "{}"
#endif
""".format(build_no, version_string, version+str(build_no))
with open(FILENAME_VERSION_H, 'w+') as f:
f.write(hf)
with open("data_embed/index.html", "r") as f:
index_html_content = f.read()
index_html_content = index_html_content.replace('<!--VERSION-->', version_string)
with open("data_embed/index.html.out", "w") as f:
f.write(index_html_content)

Wyświetl plik

@ -0,0 +1,13 @@
import gzip
assets_list = {
'data_embed/index.html.out': 'data_embed/index.html.out',
'data_embed/js.js': 'data_embed/js.js.out',
'data_embed/style.css': 'data_embed/style.css.out',
}
for src_file_name, out_file_name in assets_list.items():
with open(src_file_name, 'rb') as f:
content = f.read()
with open(out_file_name, 'wb') as f:
f.write(gzip.compress(content, compresslevel=9))