mirror of
https://github.com/meshtastic/firmware.git
synced 2025-04-25 17:42:48 +00:00
commit
cb0073f6fa
@ -5,7 +5,7 @@ set -e
|
|||||||
VERSION=`bin/buildinfo.py long`
|
VERSION=`bin/buildinfo.py long`
|
||||||
SHORT_VERSION=`bin/buildinfo.py short`
|
SHORT_VERSION=`bin/buildinfo.py short`
|
||||||
|
|
||||||
BOARDS_ESP32="tlora-v2 tlora-v1 tlora_v1_3 tlora-v2-1-1.6 tbeam heltec-v2.0 heltec-v2.1 tbeam0.7 meshtastic-diy-v1"
|
BOARDS_ESP32="tlora-v2 tlora-v1 tlora_v1_3 tlora-v2-1-1.6 tbeam heltec-v1 heltec-v2.0 heltec-v2.1 tbeam0.7 meshtastic-diy-v1"
|
||||||
#BOARDS_ESP32=tbeam
|
#BOARDS_ESP32=tbeam
|
||||||
|
|
||||||
# FIXME note nrf52840dk build is for some reason only generating a BIN file but not a HEX file nrf52840dk-geeksville is fine
|
# FIXME note nrf52840dk build is for some reason only generating a BIN file but not a HEX file nrf52840dk-geeksville is fine
|
||||||
|
@ -12,6 +12,8 @@
|
|||||||
default_envs = tbeam
|
default_envs = tbeam
|
||||||
;default_envs = tbeam0.7
|
;default_envs = tbeam0.7
|
||||||
;default_envs = heltec-v2.0
|
;default_envs = heltec-v2.0
|
||||||
|
;default_envs = heltec-v1
|
||||||
|
;default_envs = tlora-v1
|
||||||
;default_envs = tlora-v1
|
;default_envs = tlora-v1
|
||||||
;default_envs = tlora_v1_3
|
;default_envs = tlora_v1_3
|
||||||
;default_envs = tlora-v2
|
;default_envs = tlora-v2
|
||||||
@ -114,6 +116,9 @@ lib_deps =
|
|||||||
paulstoffregen/OneWire@^2.3.5
|
paulstoffregen/OneWire@^2.3.5
|
||||||
robtillaart/DS18B20@^0.1.11
|
robtillaart/DS18B20@^0.1.11
|
||||||
h2zero/NimBLE-Arduino@1.3.1
|
h2zero/NimBLE-Arduino@1.3.1
|
||||||
|
tobozo/ESP32-targz@^1.1.4
|
||||||
|
arduino-libraries/NTPClient#531eff39d9fbc831f3d03f706a161739203fbe2a
|
||||||
|
|
||||||
# Hmm - this doesn't work yet
|
# Hmm - this doesn't work yet
|
||||||
# board_build.ldscript = linker/esp32.extram.bss.ld
|
# board_build.ldscript = linker/esp32.extram.bss.ld
|
||||||
lib_ignore =
|
lib_ignore =
|
||||||
@ -158,6 +163,13 @@ board = ttgo-t-beam
|
|||||||
build_flags =
|
build_flags =
|
||||||
${esp32_base.build_flags} -D TBEAM_V07
|
${esp32_base.build_flags} -D TBEAM_V07
|
||||||
|
|
||||||
|
[env:heltec-v1]
|
||||||
|
;build_type = debug ; to make it possible to step through our jtag debugger
|
||||||
|
extends = esp32_base
|
||||||
|
board = heltec_wifi_lora_32
|
||||||
|
build_flags =
|
||||||
|
${esp32_base.build_flags} -D HELTEC_V1
|
||||||
|
|
||||||
[env:heltec-v2.0]
|
[env:heltec-v2.0]
|
||||||
;build_type = debug ; to make it possible to step through our jtag debugger
|
;build_type = debug ; to make it possible to step through our jtag debugger
|
||||||
extends = esp32_base
|
extends = esp32_base
|
||||||
|
2
proto
2
proto
@ -1 +1 @@
|
|||||||
Subproject commit 4a64080d160f9219768a4126414d41dc82a3d63c
|
Subproject commit 7b80bde4213c530ab3d85a19d1795025299d4633
|
@ -324,6 +324,41 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#elif defined(ARDUINO_HELTEC_WIFI_LORA_32)
|
||||||
|
|
||||||
|
// the default ESP32 Pin of 15 is the Oled SCL, set to 36 and 37 and works fine.
|
||||||
|
// Tested on Neo6m module.
|
||||||
|
#undef GPS_RX_PIN
|
||||||
|
#undef GPS_TX_PIN
|
||||||
|
#define GPS_RX_PIN 36
|
||||||
|
#define GPS_TX_PIN 37
|
||||||
|
|
||||||
|
#ifndef USE_JTAG // gpio15 is TDO for JTAG, so no I2C on this board while doing jtag
|
||||||
|
#define I2C_SDA 4 // I2C pins for this board
|
||||||
|
#define I2C_SCL 15
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define RESET_OLED 16 // If defined, this pin will be used to reset the display controller
|
||||||
|
|
||||||
|
#define LED_PIN 25 // If defined we will blink this LED
|
||||||
|
#define BUTTON_PIN 0 // If defined, this will be used for user button presses
|
||||||
|
|
||||||
|
#define USE_RF95
|
||||||
|
#define LORA_DIO0 26 // a No connect on the SX1262 module
|
||||||
|
#ifndef USE_JTAG
|
||||||
|
#define LORA_RESET 14
|
||||||
|
#endif
|
||||||
|
#define LORA_DIO1 33 // Not really used
|
||||||
|
#define LORA_DIO2 32 // Not really used
|
||||||
|
|
||||||
|
// ratio of voltage divider = 3.20 (R1=100k, R2=220k)
|
||||||
|
#define ADC_MULTIPLIER 3.2
|
||||||
|
|
||||||
|
// This string must exactly match the case used in release file names or the android updater won't work
|
||||||
|
#define HW_VENDOR HardwareModel_HELTEC_V1
|
||||||
|
|
||||||
|
#define BATTERY_PIN 13 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage
|
||||||
|
|
||||||
#elif defined(TLORA_V1)
|
#elif defined(TLORA_V1)
|
||||||
// This string must exactly match the case used in release file names or the android updater won't work
|
// This string must exactly match the case used in release file names or the android updater won't work
|
||||||
#define HW_VENDOR HardwareModel_TLORA_V1
|
#define HW_VENDOR HardwareModel_TLORA_V1
|
||||||
|
@ -11,8 +11,11 @@ enum RTCQuality {
|
|||||||
/// Some other node gave us a time we can use
|
/// Some other node gave us a time we can use
|
||||||
RTCQualityFromNet = 1,
|
RTCQualityFromNet = 1,
|
||||||
|
|
||||||
|
/// Our time is based on NTP
|
||||||
|
RTCQualityNTP= 2,
|
||||||
|
|
||||||
/// Our time is based on our own GPS
|
/// Our time is based on our own GPS
|
||||||
RTCQualityGPS = 2
|
RTCQualityGPS = 3
|
||||||
};
|
};
|
||||||
|
|
||||||
RTCQuality getRTCQuality();
|
RTCQuality getRTCQuality();
|
||||||
|
@ -96,7 +96,7 @@ void MeshPlugin::callPlugins(const MeshPacket &mp, RxSource src)
|
|||||||
assert(!pi.myReply); // If it is !null it means we have a bug, because it should have been sent the previous time
|
assert(!pi.myReply); // If it is !null it means we have a bug, because it should have been sent the previous time
|
||||||
|
|
||||||
if (wantsPacket) {
|
if (wantsPacket) {
|
||||||
DEBUG_MSG("Plugin %s wantsPacket=%d\n", pi.name, wantsPacket);
|
DEBUG_MSG("Plugin '%s' wantsPacket=%d\n", pi.name, wantsPacket);
|
||||||
|
|
||||||
pluginFound = true;
|
pluginFound = true;
|
||||||
|
|
||||||
@ -109,7 +109,10 @@ void MeshPlugin::callPlugins(const MeshPacket &mp, RxSource src)
|
|||||||
/// Also: if a packet comes in on the local PC interface, we don't check for bound channels, because it is TRUSTED and it needs to
|
/// Also: if a packet comes in on the local PC interface, we don't check for bound channels, because it is TRUSTED and it needs to
|
||||||
/// to be able to fetch the initial admin packets without yet knowing any channels.
|
/// to be able to fetch the initial admin packets without yet knowing any channels.
|
||||||
|
|
||||||
bool rxChannelOk = !pi.boundChannel || (mp.from == 0) || (ch && (strcmp(ch->settings.name, pi.boundChannel) == 0));
|
bool rxChannelOk = !pi.boundChannel || (mp.from == 0) ||
|
||||||
|
!ch ||
|
||||||
|
strlen(ch->settings.name) > 0 ||
|
||||||
|
strcmp(ch->settings.name, pi.boundChannel);
|
||||||
|
|
||||||
if (!rxChannelOk) {
|
if (!rxChannelOk) {
|
||||||
// no one should have already replied!
|
// no one should have already replied!
|
||||||
@ -134,9 +137,9 @@ void MeshPlugin::callPlugins(const MeshPacket &mp, RxSource src)
|
|||||||
// any other node.
|
// any other node.
|
||||||
if (mp.decoded.want_response && toUs && (getFrom(&mp) != ourNodeNum || mp.to == ourNodeNum) && !currentReply) {
|
if (mp.decoded.want_response && toUs && (getFrom(&mp) != ourNodeNum || mp.to == ourNodeNum) && !currentReply) {
|
||||||
pi.sendResponse(mp);
|
pi.sendResponse(mp);
|
||||||
DEBUG_MSG("Plugin %s sent a response\n", pi.name);
|
DEBUG_MSG("Plugin '%s' sent a response\n", pi.name);
|
||||||
} else {
|
} else {
|
||||||
DEBUG_MSG("Plugin %s considered\n", pi.name);
|
DEBUG_MSG("Plugin '%s' considered\n", pi.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the requester didn't ask for a response we might need to discard unused replies to prevent memory leaks
|
// If the requester didn't ask for a response we might need to discard unused replies to prevent memory leaks
|
||||||
@ -147,7 +150,7 @@ void MeshPlugin::callPlugins(const MeshPacket &mp, RxSource src)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (handled == ProcessMessage::STOP) {
|
if (handled == ProcessMessage::STOP) {
|
||||||
DEBUG_MSG("Plugin %s handled and skipped other processing\n", pi.name);
|
DEBUG_MSG("Plugin '%s' handled and skipped other processing\n", pi.name);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -101,7 +101,6 @@ ErrorCode RadioLibInterface::send(MeshPacket *p)
|
|||||||
// Sometimes when testing it is useful to be able to never turn on the xmitter
|
// Sometimes when testing it is useful to be able to never turn on the xmitter
|
||||||
#ifndef LORA_DISABLE_SENDING
|
#ifndef LORA_DISABLE_SENDING
|
||||||
printPacket("enqueuing for send", p);
|
printPacket("enqueuing for send", p);
|
||||||
uint32_t xmitMsec = getPacketTime(p);
|
|
||||||
|
|
||||||
DEBUG_MSG("txGood=%d,rxGood=%d,rxBad=%d\n", txGood, rxGood, rxBad);
|
DEBUG_MSG("txGood=%d,rxGood=%d,rxBad=%d\n", txGood, rxGood, rxBad);
|
||||||
ErrorCode res = txQueue.enqueue(p) ? ERRNO_OK : ERRNO_UNKNOWN;
|
ErrorCode res = txQueue.enqueue(p) ? ERRNO_OK : ERRNO_UNKNOWN;
|
||||||
@ -111,10 +110,6 @@ ErrorCode RadioLibInterface::send(MeshPacket *p)
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Count the packet toward our TX airtime utilization.
|
|
||||||
// We only count it if it can be added to the TX queue.
|
|
||||||
airTime->logAirtime(TX_LOG, xmitMsec);
|
|
||||||
|
|
||||||
// We want all sending/receiving to be done by our daemon thread, We use a delay here because this packet might have been sent
|
// We want all sending/receiving to be done by our daemon thread, We use a delay here because this packet might have been sent
|
||||||
// in response to a packet we just received. So we want to make sure the other side has had a chance to reconfigure its radio
|
// in response to a packet we just received. So we want to make sure the other side has had a chance to reconfigure its radio
|
||||||
startTransmitTimer(true);
|
startTransmitTimer(true);
|
||||||
@ -188,6 +183,10 @@ void RadioLibInterface::onNotify(uint32_t notification)
|
|||||||
MeshPacket *txp = txQueue.dequeue();
|
MeshPacket *txp = txQueue.dequeue();
|
||||||
assert(txp);
|
assert(txp);
|
||||||
startSend(txp);
|
startSend(txp);
|
||||||
|
|
||||||
|
// Packet has been sent, count it toward our TX airtime utilization.
|
||||||
|
uint32_t xmitMsec = getPacketTime(txp);
|
||||||
|
airTime->logAirtime(TX_LOG, xmitMsec);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// DEBUG_MSG("done with txqueue\n");
|
// DEBUG_MSG("done with txqueue\n");
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
#define RADIOLIB_SOFTWARE_SERIAL_UNSUPPORTED
|
#define RADIOLIB_SOFTWARE_SERIAL_UNSUPPORTED
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define RADIOLIB_EXCLUDE_HTTP
|
||||||
#include <RadioLib.h>
|
#include <RadioLib.h>
|
||||||
|
|
||||||
// ESP32 has special rules about ISR code
|
// ESP32 has special rules about ISR code
|
||||||
|
@ -23,6 +23,7 @@ typedef enum _HardwareModel {
|
|||||||
HardwareModel_TLORA_V1_1p3 = 8,
|
HardwareModel_TLORA_V1_1p3 = 8,
|
||||||
HardwareModel_RAK4631 = 9,
|
HardwareModel_RAK4631 = 9,
|
||||||
HardwareModel_HELTEC_V2_1 = 10,
|
HardwareModel_HELTEC_V2_1 = 10,
|
||||||
|
HardwareModel_HELTEC_V1 = 11,
|
||||||
HardwareModel_LORA_RELAY_V1 = 32,
|
HardwareModel_LORA_RELAY_V1 = 32,
|
||||||
HardwareModel_NRF52840DK = 33,
|
HardwareModel_NRF52840DK = 33,
|
||||||
HardwareModel_PPR = 34,
|
HardwareModel_PPR = 34,
|
||||||
|
@ -41,6 +41,13 @@ using namespace httpsserver;
|
|||||||
|
|
||||||
#include "mesh/http/ContentHandler.h"
|
#include "mesh/http/ContentHandler.h"
|
||||||
|
|
||||||
|
#include <HTTPClient.h>
|
||||||
|
#include <WiFiClientSecure.h>
|
||||||
|
HTTPClient http;
|
||||||
|
|
||||||
|
#define DEST_FS_USES_SPIFFS
|
||||||
|
#include <ESP32-targz.h>
|
||||||
|
|
||||||
// We need to specify some content-type mapping, so the resources get delivered with the
|
// We need to specify some content-type mapping, so the resources get delivered with the
|
||||||
// right content type and are displayed correctly in the browser
|
// right content type and are displayed correctly in the browser
|
||||||
char contentTypes[][2][32] = {{".txt", "text/plain"}, {".html", "text/html"},
|
char contentTypes[][2][32] = {{".txt", "text/plain"}, {".html", "text/html"},
|
||||||
@ -50,9 +57,59 @@ char contentTypes[][2][32] = {{".txt", "text/plain"}, {".html", "text/html"}
|
|||||||
{".css", "text/css"}, {".ico", "image/vnd.microsoft.icon"},
|
{".css", "text/css"}, {".ico", "image/vnd.microsoft.icon"},
|
||||||
{".svg", "image/svg+xml"}, {"", ""}};
|
{".svg", "image/svg+xml"}, {"", ""}};
|
||||||
|
|
||||||
|
//const char *tarURL = "https://www.casler.org/temp/meshtastic-web.tar";
|
||||||
|
const char *tarURL = "https://api-production-871d.up.railway.app/mirror/webui";
|
||||||
|
const char *certificate = NULL; // change this as needed, leave as is for no TLS check (yolo security)
|
||||||
|
|
||||||
// Our API to handle messages to and from the radio.
|
// Our API to handle messages to and from the radio.
|
||||||
HttpAPI webAPI;
|
HttpAPI webAPI;
|
||||||
|
|
||||||
|
WiFiClient *getTarHTTPClientPtr(WiFiClientSecure *client, const char *url, const char *cert = NULL)
|
||||||
|
{
|
||||||
|
if (cert == NULL) {
|
||||||
|
// New versions don't have setInsecure
|
||||||
|
// client->setInsecure();
|
||||||
|
} else {
|
||||||
|
client->setCACert(cert);
|
||||||
|
}
|
||||||
|
const char *UserAgent = "ESP32-HTTP-GzUpdater-Client";
|
||||||
|
http.setReuse(true); // handle 301 redirects gracefully
|
||||||
|
http.setUserAgent(UserAgent);
|
||||||
|
http.setConnectTimeout(10000); // 10s timeout = 10000
|
||||||
|
if (!http.begin(*client, url)) {
|
||||||
|
log_e("Can't open url %s", url);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
const char *headerKeys[] = {"location", "redirect", "Content-Type", "Content-Length", "Content-Disposition"};
|
||||||
|
const size_t numberOfHeaders = 5;
|
||||||
|
http.collectHeaders(headerKeys, numberOfHeaders);
|
||||||
|
int httpCode = http.GET();
|
||||||
|
// file found at server
|
||||||
|
if (httpCode == HTTP_CODE_FOUND || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
|
||||||
|
String newlocation = "";
|
||||||
|
String headerLocation = http.header("location");
|
||||||
|
String headerRedirect = http.header("redirect");
|
||||||
|
if (headerLocation != "") {
|
||||||
|
newlocation = headerLocation;
|
||||||
|
Serial.printf("302 (location): %s => %s\n", url, headerLocation.c_str());
|
||||||
|
} else if (headerRedirect != "") {
|
||||||
|
Serial.printf("301 (redirect): %s => %s\n", url, headerLocation.c_str());
|
||||||
|
newlocation = headerRedirect;
|
||||||
|
}
|
||||||
|
http.end();
|
||||||
|
if (newlocation != "") {
|
||||||
|
log_w("Found 302/301 location header: %s", newlocation.c_str());
|
||||||
|
return getTarHTTPClientPtr(client, newlocation.c_str(), cert);
|
||||||
|
} else {
|
||||||
|
log_e("Empty redirect !!");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (httpCode != 200)
|
||||||
|
return nullptr;
|
||||||
|
return http.getStreamPtr();
|
||||||
|
}
|
||||||
|
|
||||||
void registerHandlers(HTTPServer *insecureServer, HTTPSServer *secureServer)
|
void registerHandlers(HTTPServer *insecureServer, HTTPSServer *secureServer)
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -66,6 +123,8 @@ void registerHandlers(HTTPServer *insecureServer, HTTPSServer *secureServer)
|
|||||||
ResourceNode *nodeHotspotApple = new ResourceNode("/hotspot-detect.html", "GET", &handleHotspot);
|
ResourceNode *nodeHotspotApple = new ResourceNode("/hotspot-detect.html", "GET", &handleHotspot);
|
||||||
ResourceNode *nodeHotspotAndroid = new ResourceNode("/generate_204", "GET", &handleHotspot);
|
ResourceNode *nodeHotspotAndroid = new ResourceNode("/generate_204", "GET", &handleHotspot);
|
||||||
|
|
||||||
|
ResourceNode *nodeUpdateSPIFFS = new ResourceNode("/update", "GET", &handleUpdateSPIFFS);
|
||||||
|
|
||||||
ResourceNode *nodeRestart = new ResourceNode("/restart", "POST", &handleRestart);
|
ResourceNode *nodeRestart = new ResourceNode("/restart", "POST", &handleRestart);
|
||||||
ResourceNode *nodeFormUpload = new ResourceNode("/upload", "POST", &handleFormUpload);
|
ResourceNode *nodeFormUpload = new ResourceNode("/upload", "POST", &handleFormUpload);
|
||||||
|
|
||||||
@ -90,7 +149,8 @@ void registerHandlers(HTTPServer *insecureServer, HTTPSServer *secureServer)
|
|||||||
secureServer->registerNode(nodeJsonSpiffsBrowseStatic);
|
secureServer->registerNode(nodeJsonSpiffsBrowseStatic);
|
||||||
secureServer->registerNode(nodeJsonDelete);
|
secureServer->registerNode(nodeJsonDelete);
|
||||||
secureServer->registerNode(nodeJsonReport);
|
secureServer->registerNode(nodeJsonReport);
|
||||||
secureServer->registerNode(nodeRoot);
|
secureServer->registerNode(nodeUpdateSPIFFS);
|
||||||
|
secureServer->registerNode(nodeRoot); // This has to be last
|
||||||
|
|
||||||
// Insecure nodes
|
// Insecure nodes
|
||||||
insecureServer->registerNode(nodeAPIv1ToRadioOptions);
|
insecureServer->registerNode(nodeAPIv1ToRadioOptions);
|
||||||
@ -105,13 +165,14 @@ void registerHandlers(HTTPServer *insecureServer, HTTPSServer *secureServer)
|
|||||||
insecureServer->registerNode(nodeJsonSpiffsBrowseStatic);
|
insecureServer->registerNode(nodeJsonSpiffsBrowseStatic);
|
||||||
insecureServer->registerNode(nodeJsonDelete);
|
insecureServer->registerNode(nodeJsonDelete);
|
||||||
insecureServer->registerNode(nodeJsonReport);
|
insecureServer->registerNode(nodeJsonReport);
|
||||||
insecureServer->registerNode(nodeRoot);
|
insecureServer->registerNode(nodeUpdateSPIFFS);
|
||||||
|
insecureServer->registerNode(nodeRoot); // This has to be last
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleAPIv1FromRadio(HTTPRequest *req, HTTPResponse *res)
|
void handleAPIv1FromRadio(HTTPRequest *req, HTTPResponse *res)
|
||||||
{
|
{
|
||||||
|
|
||||||
DEBUG_MSG("+++++++++++++++ webAPI handleAPIv1FromRadio\n");
|
DEBUG_MSG("webAPI handleAPIv1FromRadio\n");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
For documentation, see:
|
For documentation, see:
|
||||||
@ -156,12 +217,12 @@ void handleAPIv1FromRadio(HTTPRequest *req, HTTPResponse *res)
|
|||||||
res->write(txBuf, len);
|
res->write(txBuf, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
DEBUG_MSG("--------------- webAPI handleAPIv1FromRadio, len %d\n", len);
|
DEBUG_MSG("webAPI handleAPIv1FromRadio, len %d\n", len);
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleAPIv1ToRadio(HTTPRequest *req, HTTPResponse *res)
|
void handleAPIv1ToRadio(HTTPRequest *req, HTTPResponse *res)
|
||||||
{
|
{
|
||||||
DEBUG_MSG("+++++++++++++++ webAPI handleAPIv1ToRadio\n");
|
DEBUG_MSG("webAPI handleAPIv1ToRadio\n");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
For documentation, see:
|
For documentation, see:
|
||||||
@ -188,7 +249,7 @@ void handleAPIv1ToRadio(HTTPRequest *req, HTTPResponse *res)
|
|||||||
webAPI.handleToRadio(buffer, s);
|
webAPI.handleToRadio(buffer, s);
|
||||||
|
|
||||||
res->write(buffer, s);
|
res->write(buffer, s);
|
||||||
DEBUG_MSG("--------------- webAPI handleAPIv1ToRadio\n");
|
DEBUG_MSG("webAPI handleAPIv1ToRadio\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleSpiffsBrowseStatic(HTTPRequest *req, HTTPResponse *res)
|
void handleSpiffsBrowseStatic(HTTPRequest *req, HTTPResponse *res)
|
||||||
@ -297,14 +358,21 @@ void handleStatic(HTTPRequest *req, HTTPResponse *res)
|
|||||||
file = SPIFFS.open(filenameGzip.c_str());
|
file = SPIFFS.open(filenameGzip.c_str());
|
||||||
res->setHeader("Content-Encoding", "gzip");
|
res->setHeader("Content-Encoding", "gzip");
|
||||||
if (!file.available()) {
|
if (!file.available()) {
|
||||||
DEBUG_MSG("File not available\n");
|
DEBUG_MSG("File not available - %s\n", filenameGzip.c_str());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
has_set_content_type = true;
|
has_set_content_type = true;
|
||||||
filenameGzip = "/static/index.html.gz";
|
filenameGzip = "/static/index.html.gz";
|
||||||
file = SPIFFS.open(filenameGzip.c_str());
|
file = SPIFFS.open(filenameGzip.c_str());
|
||||||
res->setHeader("Content-Encoding", "gzip");
|
|
||||||
res->setHeader("Content-Type", "text/html");
|
res->setHeader("Content-Type", "text/html");
|
||||||
|
if (!file.available()) {
|
||||||
|
DEBUG_MSG("File not available - %s\n", filenameGzip.c_str());
|
||||||
|
res->println("Web server is running.<br><br>The content you are looking for can't be found. Please see: <a "
|
||||||
|
"href=https://meshtastic.org/docs/getting-started/faq#wifi--web-browser>FAQ</a>.<br><br><a "
|
||||||
|
"href=/json/report>stats</a><br><br><a href=/update>Experemntal Web Content OTA Update</a>");
|
||||||
|
} else {
|
||||||
|
res->setHeader("Content-Encoding", "gzip");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
res->setHeader("Content-Length", httpsserver::intToString(file.size()));
|
res->setHeader("Content-Length", httpsserver::intToString(file.size()));
|
||||||
@ -576,6 +644,7 @@ void handleReport(HTTPRequest *req, HTTPResponse *res)
|
|||||||
res->println("},");
|
res->println("},");
|
||||||
|
|
||||||
res->println("\"device\": {");
|
res->println("\"device\": {");
|
||||||
|
res->printf("\"channel_utilization\": %3.2f%,\n", airTime->channelUtilizationPercent());
|
||||||
res->printf("\"reboot_counter\": %d\n", myNodeInfo.reboot_count);
|
res->printf("\"reboot_counter\": %d\n", myNodeInfo.reboot_count);
|
||||||
res->println("},");
|
res->println("},");
|
||||||
|
|
||||||
@ -612,6 +681,75 @@ void handleHotspot(HTTPRequest *req, HTTPResponse *res)
|
|||||||
res->println("<meta http-equiv=\"refresh\" content=\"0;url=/\" />\n");
|
res->println("<meta http-equiv=\"refresh\" content=\"0;url=/\" />\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void handleUpdateSPIFFS(HTTPRequest *req, HTTPResponse *res)
|
||||||
|
{
|
||||||
|
res->setHeader("Content-Type", "text/html");
|
||||||
|
res->setHeader("Access-Control-Allow-Origin", "*");
|
||||||
|
res->setHeader("Access-Control-Allow-Methods", "GET");
|
||||||
|
|
||||||
|
res->println("Downloading Meshtastic Web Content...");
|
||||||
|
|
||||||
|
File root = SPIFFS.open("/");
|
||||||
|
File file = root.openNextFile();
|
||||||
|
|
||||||
|
DEBUG_MSG("Deleting files from /static\n");
|
||||||
|
|
||||||
|
while (file) {
|
||||||
|
String filePath = String(file.name());
|
||||||
|
if (filePath.indexOf("/static") == 0) {
|
||||||
|
DEBUG_MSG("%s\n", file.name());
|
||||||
|
SPIFFS.remove(file.name());
|
||||||
|
}
|
||||||
|
file = root.openNextFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
// return;
|
||||||
|
|
||||||
|
WiFiClientSecure *client = new WiFiClientSecure;
|
||||||
|
Stream *streamptr = getTarHTTPClientPtr(client, tarURL, certificate);
|
||||||
|
|
||||||
|
if (streamptr != nullptr) {
|
||||||
|
|
||||||
|
TarUnpacker *TARUnpacker = new TarUnpacker();
|
||||||
|
TARUnpacker->haltOnError(false); // stop on fail (manual restart/reset required)
|
||||||
|
TARUnpacker->setTarVerify(false); // true = enables health checks but slows down the overall process
|
||||||
|
TARUnpacker->setupFSCallbacks(targzTotalBytesFn, targzFreeBytesFn); // prevent the partition from exploding, recommended
|
||||||
|
TARUnpacker->setLoggerCallback(BaseUnpacker::targzPrintLoggerCallback); // gz log verbosity
|
||||||
|
TARUnpacker->setTarProgressCallback(
|
||||||
|
BaseUnpacker::defaultProgressCallback); // prints the untarring progress for each individual file
|
||||||
|
TARUnpacker->setTarStatusProgressCallback(
|
||||||
|
BaseUnpacker::defaultTarStatusProgressCallback); // print the filenames as they're expanded
|
||||||
|
TARUnpacker->setTarMessageCallback(BaseUnpacker::targzPrintLoggerCallback); // tar log verbosity
|
||||||
|
|
||||||
|
String contentLengthStr = http.header("Content-Length");
|
||||||
|
contentLengthStr.trim();
|
||||||
|
int64_t streamSize = -1;
|
||||||
|
if (contentLengthStr != "") {
|
||||||
|
streamSize = atoi(contentLengthStr.c_str());
|
||||||
|
Serial.printf("Stream size %d\n", streamSize);
|
||||||
|
res->printf("Stream size %d\n", streamSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!TARUnpacker->tarStreamExpander(streamptr, streamSize, SPIFFS, "/static")) {
|
||||||
|
Serial.printf("tarStreamExpander failed with return code #%d\n", TARUnpacker->tarGzGetError());
|
||||||
|
} else {
|
||||||
|
// print leftover bytes if any (probably zero-fill from the server)
|
||||||
|
while (http.connected()) {
|
||||||
|
size_t streamSize = streamptr->available();
|
||||||
|
if (streamSize) {
|
||||||
|
Serial.printf("%02x ", streamptr->read());
|
||||||
|
} else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Serial.println("Failed to establish http connection");
|
||||||
|
}
|
||||||
|
|
||||||
|
res->println("<a href=/>Done</a>");
|
||||||
|
}
|
||||||
|
|
||||||
void handleRestart(HTTPRequest *req, HTTPResponse *res)
|
void handleRestart(HTTPRequest *req, HTTPResponse *res)
|
||||||
{
|
{
|
||||||
res->setHeader("Content-Type", "text/html");
|
res->setHeader("Content-Type", "text/html");
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|
||||||
void registerHandlers(HTTPServer *insecureServer, HTTPSServer *secureServer);
|
void registerHandlers(HTTPServer *insecureServer, HTTPSServer *secureServer);
|
||||||
|
|
||||||
// Declare some handler functions for the various URLs on the server
|
// Declare some handler functions for the various URLs on the server
|
||||||
@ -14,9 +15,7 @@ void handleSpiffsBrowseStatic(HTTPRequest *req, HTTPResponse *res);
|
|||||||
void handleSpiffsDeleteStatic(HTTPRequest *req, HTTPResponse *res);
|
void handleSpiffsDeleteStatic(HTTPRequest *req, HTTPResponse *res);
|
||||||
void handleBlinkLED(HTTPRequest *req, HTTPResponse *res);
|
void handleBlinkLED(HTTPRequest *req, HTTPResponse *res);
|
||||||
void handleReport(HTTPRequest *req, HTTPResponse *res);
|
void handleReport(HTTPRequest *req, HTTPResponse *res);
|
||||||
|
void handleUpdateSPIFFS(HTTPRequest *req, HTTPResponse *res);
|
||||||
void middlewareSpeedUp240(HTTPRequest *req, HTTPResponse *res, std::function<void()> next);
|
|
||||||
void middlewareSpeedUp160(HTTPRequest *req, HTTPResponse *res, std::function<void()> next);
|
|
||||||
|
|
||||||
// Interface to the PhoneAPI to access the protobufs with messages
|
// Interface to the PhoneAPI to access the protobufs with messages
|
||||||
class HttpAPI : public PhoneAPI
|
class HttpAPI : public PhoneAPI
|
||||||
|
@ -1,15 +1,18 @@
|
|||||||
#include "mesh/http/WiFiAPClient.h"
|
#include "mesh/http/WiFiAPClient.h"
|
||||||
#include "NodeDB.h"
|
#include "NodeDB.h"
|
||||||
|
#include "RTC.h"
|
||||||
#include "concurrency/Periodic.h"
|
#include "concurrency/Periodic.h"
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "mqtt/MQTT.h"
|
|
||||||
#include "mesh/http/WebServer.h"
|
#include "mesh/http/WebServer.h"
|
||||||
#include "mesh/wifi/WiFiServerAPI.h"
|
#include "mesh/wifi/WiFiServerAPI.h"
|
||||||
|
#include "mqtt/MQTT.h"
|
||||||
#include "target_specific.h"
|
#include "target_specific.h"
|
||||||
#include <DNSServer.h>
|
#include <DNSServer.h>
|
||||||
#include <ESPmDNS.h>
|
#include <ESPmDNS.h>
|
||||||
|
#include <NTPClient.h>
|
||||||
#include <WiFi.h>
|
#include <WiFi.h>
|
||||||
|
#include <WiFiUdp.h>
|
||||||
|
|
||||||
using namespace concurrency;
|
using namespace concurrency;
|
||||||
|
|
||||||
@ -18,6 +21,10 @@ static void WiFiEvent(WiFiEvent_t event);
|
|||||||
// DNS Server for the Captive Portal
|
// DNS Server for the Captive Portal
|
||||||
DNSServer dnsServer;
|
DNSServer dnsServer;
|
||||||
|
|
||||||
|
// NTP
|
||||||
|
WiFiUDP ntpUDP;
|
||||||
|
NTPClient timeClient(ntpUDP, "0.pool.ntp.org");
|
||||||
|
|
||||||
uint8_t wifiDisconnectReason = 0;
|
uint8_t wifiDisconnectReason = 0;
|
||||||
|
|
||||||
// Stores our hostname
|
// Stores our hostname
|
||||||
@ -46,10 +53,10 @@ static WifiSleepObserver wifiSleepObserver;
|
|||||||
|
|
||||||
static int32_t reconnectWiFi()
|
static int32_t reconnectWiFi()
|
||||||
{
|
{
|
||||||
if (radioConfig.has_preferences && needReconnect) {
|
const char *wifiName = radioConfig.preferences.wifi_ssid;
|
||||||
|
const char *wifiPsw = radioConfig.preferences.wifi_password;
|
||||||
|
|
||||||
const char *wifiName = radioConfig.preferences.wifi_ssid;
|
if (radioConfig.has_preferences && needReconnect && !WiFi.isConnected()) {
|
||||||
const char *wifiPsw = radioConfig.preferences.wifi_password;
|
|
||||||
|
|
||||||
if (!*wifiPsw) // Treat empty password as no password
|
if (!*wifiPsw) // Treat empty password as no password
|
||||||
wifiPsw = NULL;
|
wifiPsw = NULL;
|
||||||
@ -60,6 +67,26 @@ static int32_t reconnectWiFi()
|
|||||||
DEBUG_MSG("... Reconnecting to WiFi access point\n");
|
DEBUG_MSG("... Reconnecting to WiFi access point\n");
|
||||||
WiFi.mode(WIFI_MODE_STA);
|
WiFi.mode(WIFI_MODE_STA);
|
||||||
WiFi.begin(wifiName, wifiPsw);
|
WiFi.begin(wifiName, wifiPsw);
|
||||||
|
|
||||||
|
|
||||||
|
// Starting timeClient;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//if (*wifiName) {
|
||||||
|
if (WiFi.isConnected()) {
|
||||||
|
DEBUG_MSG("Updating NTP time\n");
|
||||||
|
if (timeClient.update()) {
|
||||||
|
DEBUG_MSG("NTP Request Success - Setting RTCQualityNTP if needed\n");
|
||||||
|
|
||||||
|
struct timeval tv;
|
||||||
|
tv.tv_sec = timeClient.getEpochTime();
|
||||||
|
tv.tv_usec = 0;
|
||||||
|
|
||||||
|
perhapsSetRTC(RTCQualityNTP, &tv);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
DEBUG_MSG("NTP Update failed\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,34 +155,38 @@ static void onNetworkConnected()
|
|||||||
MDNS.addService("https", "tcp", 443);
|
MDNS.addService("https", "tcp", 443);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DEBUG_MSG("Starting NTP time client\n");
|
||||||
|
timeClient.begin();
|
||||||
|
timeClient.setUpdateInterval(60*60); // Update once an hour
|
||||||
|
|
||||||
initWebServer();
|
initWebServer();
|
||||||
initApiServer();
|
initApiServer();
|
||||||
|
|
||||||
APStartupComplete = true;
|
APStartupComplete = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME this is kinda yucky, instead we should just have an observable for 'wifireconnected'
|
// FIXME this is kinda yucky, instead we should just have an observable for 'wifireconnected'
|
||||||
if(mqtt)
|
if (mqtt)
|
||||||
mqtt->reconnect();
|
mqtt->reconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Startup WiFi
|
// Startup WiFi
|
||||||
bool initWifi(bool forceSoftAP)
|
bool initWifi(bool forceSoftAP)
|
||||||
{
|
{
|
||||||
if (forceSoftAP) {
|
|
||||||
DEBUG_MSG("WiFi ... Forced AP Mode\n");
|
|
||||||
} else if (radioConfig.preferences.wifi_ap_mode) {
|
|
||||||
DEBUG_MSG("WiFi ... AP Mode\n");
|
|
||||||
} else {
|
|
||||||
DEBUG_MSG("WiFi ... Client Mode\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
forcedSoftAP = forceSoftAP;
|
forcedSoftAP = forceSoftAP;
|
||||||
|
|
||||||
if ((radioConfig.has_preferences && radioConfig.preferences.wifi_ssid[0]) || forceSoftAP) {
|
if ((radioConfig.has_preferences && radioConfig.preferences.wifi_ssid[0]) || forceSoftAP) {
|
||||||
const char *wifiName = radioConfig.preferences.wifi_ssid;
|
const char *wifiName = radioConfig.preferences.wifi_ssid;
|
||||||
const char *wifiPsw = radioConfig.preferences.wifi_password;
|
const char *wifiPsw = radioConfig.preferences.wifi_password;
|
||||||
|
|
||||||
|
if (forceSoftAP) {
|
||||||
|
DEBUG_MSG("WiFi ... Forced AP Mode\n");
|
||||||
|
} else if (radioConfig.preferences.wifi_ap_mode) {
|
||||||
|
DEBUG_MSG("WiFi ... AP Mode\n");
|
||||||
|
} else {
|
||||||
|
DEBUG_MSG("WiFi ... Client Mode\n");
|
||||||
|
}
|
||||||
|
|
||||||
createSSLCert();
|
createSSLCert();
|
||||||
|
|
||||||
if (!*wifiPsw) // Treat empty password as no password
|
if (!*wifiPsw) // Treat empty password as no password
|
||||||
@ -176,7 +207,6 @@ bool initWifi(bool forceSoftAP)
|
|||||||
|
|
||||||
} else {
|
} else {
|
||||||
DEBUG_MSG("Starting WIFI AP: ssid=%s, ok=%d\n", wifiName, WiFi.softAP(wifiName, wifiPsw));
|
DEBUG_MSG("Starting WIFI AP: ssid=%s, ok=%d\n", wifiName, WiFi.softAP(wifiName, wifiPsw));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0));
|
WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0));
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#include "NodeDB.h"
|
#include "NodeDB.h"
|
||||||
#include "RTC.h"
|
#include "RTC.h"
|
||||||
#include "Router.h"
|
#include "Router.h"
|
||||||
|
#include "airtime.h"
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include "mesh-pb-constants.h"
|
#include "mesh-pb-constants.h"
|
||||||
#include "mesh/generated/storeforward.pb.h"
|
#include "mesh/generated/storeforward.pb.h"
|
||||||
@ -22,20 +23,29 @@ int32_t StoreForwardPlugin::runOnce()
|
|||||||
|
|
||||||
if (radioConfig.preferences.is_router) {
|
if (radioConfig.preferences.is_router) {
|
||||||
|
|
||||||
|
// Send out the message queue.
|
||||||
if (this->busy) {
|
if (this->busy) {
|
||||||
// Send out the message queue.
|
|
||||||
|
|
||||||
// DEBUG_MSG("--- --- --- In busy loop 1 %d\n", this->packetHistoryTXQueue_index);
|
// Only send packets if the channel is less than 25% utilized.
|
||||||
storeForwardPlugin->sendPayload(this->busyTo, this->packetHistoryTXQueue_index);
|
if (airTime->channelUtilizationPercent() < 25) {
|
||||||
|
|
||||||
if (this->packetHistoryTXQueue_index == packetHistoryTXQueue_size) {
|
// DEBUG_MSG("--- --- --- In busy loop 1 %d\n", this->packetHistoryTXQueue_index);
|
||||||
strcpy(this->routerMessage, "** S&F - Done");
|
storeForwardPlugin->sendPayload(this->busyTo, this->packetHistoryTXQueue_index);
|
||||||
storeForwardPlugin->sendMessage(this->busyTo, this->routerMessage);
|
|
||||||
// DEBUG_MSG("--- --- --- In busy loop - Done \n");
|
if (this->packetHistoryTXQueue_index == packetHistoryTXQueue_size) {
|
||||||
this->packetHistoryTXQueue_index = 0;
|
strcpy(this->routerMessage, "** S&F - Done");
|
||||||
this->busy = false;
|
storeForwardPlugin->sendMessage(this->busyTo, this->routerMessage);
|
||||||
|
|
||||||
|
// DEBUG_MSG("--- --- --- In busy loop - Done \n");
|
||||||
|
this->packetHistoryTXQueue_index = 0;
|
||||||
|
this->busy = false;
|
||||||
|
} else {
|
||||||
|
this->packetHistoryTXQueue_index++;
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
this->packetHistoryTXQueue_index++;
|
DEBUG_MSG("Channel utilization is too high. Skipping this opportunity to send and will retry later.\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DEBUG_MSG("SF myNodeInfo.bitrate = %f bytes / sec\n", myNodeInfo.bitrate);
|
DEBUG_MSG("SF myNodeInfo.bitrate = %f bytes / sec\n", myNodeInfo.bitrate);
|
||||||
@ -414,9 +424,6 @@ StoreForwardPlugin::StoreForwardPlugin()
|
|||||||
// Popupate PSRAM with our data structures.
|
// Popupate PSRAM with our data structures.
|
||||||
this->populatePSRAM();
|
this->populatePSRAM();
|
||||||
|
|
||||||
// this->packetTimeMax = 2000;
|
|
||||||
// DEBUG_MSG("SF Time to Transmit maxPacketSize (%d bytes) %d ms\n", maxPacketSize, this->packetTimeMax);
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
DEBUG_MSG("Device has less than 1M of PSRAM free. Aborting startup.\n");
|
DEBUG_MSG("Device has less than 1M of PSRAM free. Aborting startup.\n");
|
||||||
DEBUG_MSG("Store & Forward Plugin - Aborting Startup.\n");
|
DEBUG_MSG("Store & Forward Plugin - Aborting Startup.\n");
|
||||||
|
@ -65,7 +65,11 @@ void setCPUFast(bool on)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setCpuFrequencyMhz(on ? 240 : 80);
|
// The Heltec LORA32 V1 runs at 26 MHz base frequency and doesn't react well to switching to 80 MHz...
|
||||||
|
#ifndef ARDUINO_HELTEC_WIFI_LORA_32
|
||||||
|
setCpuFrequencyMhz(on ? 240 : 80);
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user