kopia lustrzana https://github.com/meshtastic/firmware
Merge branch 'master' into eink
commit
04c54840f4
|
@ -57,6 +57,7 @@
|
|||
"HFSR",
|
||||
"Meshtastic",
|
||||
"NEMAGPS",
|
||||
"RDEF",
|
||||
"Ublox",
|
||||
"bkpt",
|
||||
"cfsr",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
theme: jekyll-theme-cayman
|
||||
|
||||
title: Meshtastic
|
||||
description: An opensource hiking, pilot, skiing, Signal-App-extending GPS mesh communicator
|
||||
description: An opensource hiking, pilot, skiing, secure GPS mesh communicator
|
||||
google_analytics: G-DRZ5H5EXHV
|
||||
|
||||
include: [".well-known"]
|
||||
|
|
|
@ -32,11 +32,15 @@ From lower to higher power consumption.
|
|||
onEntry: setBluetoothOn(true)
|
||||
onExit:
|
||||
|
||||
- full on (ON) - Everything is on
|
||||
- serial API usage (SERIAL) - Screen is on, device doesn't sleep, bluetooth off
|
||||
onEntry: setBluetooth off, screen on
|
||||
onExit:
|
||||
|
||||
- full on (ON) - Everything is on, can eventually timeout and lower to a lower power state
|
||||
onEntry: setBluetoothOn(true), screen.setOn(true)
|
||||
onExit: screen.setOn(false)
|
||||
|
||||
- serial API usage (SERIAL) - Screen is on, device doesn't sleep, bluetooth off
|
||||
- has power (POWER) - Screen is on, device doesn't sleep, bluetooth on, will stay in this state as long as we have power
|
||||
onEntry: setBluetooth off, screen on
|
||||
onExit:
|
||||
|
||||
|
@ -56,9 +60,11 @@ From lower to higher power consumption.
|
|||
- While in NB/DARK/ON: If we receive EVENT_NODEDB_UPDATED we transition to ON (so the new screen can be shown)
|
||||
- While in DARK: While the phone talks to us over BLE (EVENT_CONTACT_FROM_PHONE) reset any sleep timers and stay in DARK (needed for bluetooth sw update and nice user experience if the user is reading/replying to texts)
|
||||
- while in LS/NB/DARK: if SERIAL_CONNECTED, go to serial
|
||||
- while in any state: if we have AC power, go to POWER
|
||||
|
||||
### events that decrease cpu activity
|
||||
|
||||
- While in POWER: if lose AC go to ON
|
||||
- While in SERIAL: if SERIAL_DISCONNECTED, go to NB
|
||||
- While in ON: If PRESS event occurs, reset screen_on_secs timer and tell the screen to handle the pess
|
||||
- While in ON: If it has been more than screen_on_secs since a press, lower to DARK
|
||||
|
|
|
@ -172,7 +172,7 @@ build_flags =
|
|||
-Isdk-nrfxlib/crypto/nrf_oberon/include -Lsdk-nrfxlib/crypto/nrf_oberon/lib/cortex-m4/hard-float/ -lliboberon_3.0.3
|
||||
;-DCFG_DEBUG=3
|
||||
src_filter =
|
||||
${arduino_base.src_filter} -<esp32/> -<nimble/>
|
||||
${arduino_base.src_filter} -<esp32/> -<nimble/> -<meshwifi/>
|
||||
lib_ignore =
|
||||
BluetoothOTA
|
||||
monitor_port = /dev/ttyACM1
|
||||
|
@ -267,7 +267,7 @@ lib_deps =
|
|||
; The Portduino based sim environment on top of linux
|
||||
[env:linux]
|
||||
platform = https://github.com/geeksville/platform-portduino.git
|
||||
src_filter = ${env.src_filter} -<esp32/> -<nimble/> -<nrf52/>
|
||||
src_filter = ${env.src_filter} -<esp32/> -<nimble/> -<nrf52/> -<meshwifi/>
|
||||
build_flags = ${arduino_base.build_flags} -O0
|
||||
framework = arduino
|
||||
board = linux_x86_64
|
||||
|
|
|
@ -119,6 +119,7 @@ void Power::readPowerStatus()
|
|||
const PowerStatus powerStatus =
|
||||
PowerStatus(hasBattery ? OptTrue : OptFalse, batteryLevel->isVBUSPlug() ? OptTrue : OptFalse,
|
||||
batteryLevel->isChargeing() ? OptTrue : OptFalse, batteryVoltageMv, batteryChargePercent);
|
||||
DEBUG_MSG("Read power stat %d\n", powerStatus.getHasUSB());
|
||||
newStatus.notifyObservers(&powerStatus);
|
||||
|
||||
// If we have a battery at all and it is less than 10% full, force deep sleep
|
||||
|
@ -237,9 +238,11 @@ void Power::loop()
|
|||
}
|
||||
if (axp.isVbusRemoveIRQ()) {
|
||||
DEBUG_MSG("USB unplugged\n");
|
||||
powerFSM.trigger(EVENT_POWER_DISCONNECTED);
|
||||
}
|
||||
if (axp.isVbusPlugInIRQ()) {
|
||||
DEBUG_MSG("USB plugged In\n");
|
||||
powerFSM.trigger(EVENT_POWER_CONNECTED);
|
||||
}
|
||||
if (axp.isBattPlugInIRQ()) {
|
||||
DEBUG_MSG("Battery inserted\n");
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
#include "MeshService.h"
|
||||
#include "NodeDB.h"
|
||||
#include "configuration.h"
|
||||
#include "main.h"
|
||||
#include "graphics/Screen.h"
|
||||
#include "main.h"
|
||||
#include "sleep.h"
|
||||
#include "target_specific.h"
|
||||
|
||||
|
@ -123,6 +123,12 @@ static void serialEnter()
|
|||
screen.setOn(true);
|
||||
}
|
||||
|
||||
static void powerEnter()
|
||||
{
|
||||
screen.setOn(true);
|
||||
setBluetoothEnable(true);
|
||||
}
|
||||
|
||||
static void onEnter()
|
||||
{
|
||||
screen.setOn(true);
|
||||
|
@ -155,16 +161,20 @@ State stateDARK(darkEnter, NULL, NULL, "DARK");
|
|||
State stateSERIAL(serialEnter, NULL, NULL, "SERIAL");
|
||||
State stateBOOT(bootEnter, NULL, NULL, "BOOT");
|
||||
State stateON(onEnter, NULL, NULL, "ON");
|
||||
State statePOWER(powerEnter, NULL, NULL, "POWER");
|
||||
Fsm powerFSM(&stateBOOT);
|
||||
|
||||
void PowerFSM_setup()
|
||||
{
|
||||
powerFSM.add_timed_transition(&stateBOOT, &stateON, 3 * 1000, NULL, "boot timeout");
|
||||
// If we already have AC power go to POWER state after init, otherwise go to ON
|
||||
bool hasPower = powerStatus && powerStatus->getHasUSB();
|
||||
DEBUG_MSG("PowerFSM init, USB power=%d\n", hasPower);
|
||||
powerFSM.add_timed_transition(&stateBOOT, hasPower ? &statePOWER : &stateON, 3 * 1000, NULL, "boot timeout");
|
||||
|
||||
powerFSM.add_transition(&stateLS, &stateDARK, EVENT_WAKE_TIMER, wakeForPing, "Wake timer");
|
||||
|
||||
// Note we don't really use this transition, because when we wake from light sleep we _always_ transition to NB and then it
|
||||
// handles things powerFSM.add_transition(&stateLS, &stateNB, EVENT_RECEIVED_PACKET, NULL, "Received packet");
|
||||
// Note we don't really use this transition, because when we wake from light sleep we _always_ transition to NB and then
|
||||
// it handles things powerFSM.add_transition(&stateLS, &stateNB, EVENT_RECEIVED_PACKET, NULL, "Received packet");
|
||||
|
||||
powerFSM.add_transition(&stateNB, &stateNB, EVENT_RECEIVED_PACKET, NULL, "Received packet, resetting win wake");
|
||||
|
||||
|
@ -172,7 +182,10 @@ void PowerFSM_setup()
|
|||
powerFSM.add_transition(&stateLS, &stateON, EVENT_PRESS, NULL, "Press");
|
||||
powerFSM.add_transition(&stateNB, &stateON, EVENT_PRESS, NULL, "Press");
|
||||
powerFSM.add_transition(&stateDARK, &stateON, EVENT_PRESS, NULL, "Press");
|
||||
powerFSM.add_transition(&statePOWER, &statePOWER, EVENT_PRESS, screenPress, "Press");
|
||||
powerFSM.add_transition(&stateON, &stateON, EVENT_PRESS, screenPress, "Press"); // reenter On to restart our timers
|
||||
powerFSM.add_transition(&stateSERIAL, &stateSERIAL, EVENT_PRESS, screenPress,
|
||||
"Press"); // Allow button to work while in serial API
|
||||
|
||||
// Handle critically low power battery by forcing deep sleep
|
||||
powerFSM.add_transition(&stateBOOT, &stateSDS, EVENT_LOW_BATTERY, NULL, "LowBat");
|
||||
|
@ -199,6 +212,14 @@ void PowerFSM_setup()
|
|||
powerFSM.add_transition(&stateDARK, &stateSERIAL, EVENT_SERIAL_CONNECTED, NULL, "serial API");
|
||||
powerFSM.add_transition(&stateON, &stateSERIAL, EVENT_SERIAL_CONNECTED, NULL, "serial API");
|
||||
|
||||
powerFSM.add_transition(&stateLS, &statePOWER, EVENT_POWER_CONNECTED, NULL, "power connect");
|
||||
powerFSM.add_transition(&stateNB, &statePOWER, EVENT_POWER_CONNECTED, NULL, "power connect");
|
||||
powerFSM.add_transition(&stateDARK, &statePOWER, EVENT_POWER_CONNECTED, NULL, "power connect");
|
||||
powerFSM.add_transition(&stateON, &statePOWER, EVENT_POWER_CONNECTED, NULL, "power connect");
|
||||
|
||||
powerFSM.add_transition(&statePOWER, &stateON, EVENT_POWER_DISCONNECTED, NULL, "power disconnected");
|
||||
powerFSM.add_transition(&stateSERIAL, &stateON, EVENT_POWER_DISCONNECTED, NULL, "power disconnected");
|
||||
|
||||
powerFSM.add_transition(&stateSERIAL, &stateNB, EVENT_SERIAL_DISCONNECTED, NULL, "serial disconnect");
|
||||
|
||||
powerFSM.add_transition(&stateDARK, &stateDARK, EVENT_CONTACT_FROM_PHONE, NULL, "Contact from phone");
|
||||
|
|
|
@ -13,9 +13,11 @@
|
|||
#define EVENT_BLUETOOTH_PAIR 7
|
||||
#define EVENT_NODEDB_UPDATED 8 // NodeDB has a big enough change that we think you should turn on the screen
|
||||
#define EVENT_CONTACT_FROM_PHONE 9 // the phone just talked to us over bluetooth
|
||||
#define EVENT_LOW_BATTERY 10 // Battery is critically low, go to sleep
|
||||
#define EVENT_LOW_BATTERY 10 // Battery is critically low, go to sleep
|
||||
#define EVENT_SERIAL_CONNECTED 11
|
||||
#define EVENT_SERIAL_DISCONNECTED 12
|
||||
#define EVENT_POWER_CONNECTED 13
|
||||
#define EVENT_POWER_DISCONNECTED 14
|
||||
|
||||
extern Fsm powerFSM;
|
||||
|
||||
|
|
|
@ -40,6 +40,8 @@ void SerialConsole::onConnectionChanged(bool connected)
|
|||
if (connected) { // To prevent user confusion, turn off bluetooth while using the serial port api
|
||||
powerFSM.trigger(EVENT_SERIAL_CONNECTED);
|
||||
} else {
|
||||
// FIXME, we get no notice of serial going away, we should instead automatically generate this event if we haven't
|
||||
// received a packet in a while
|
||||
powerFSM.trigger(EVENT_SERIAL_DISCONNECTED);
|
||||
}
|
||||
}
|
|
@ -95,6 +95,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
// Standard definitions for ESP32 targets
|
||||
//
|
||||
|
||||
#define HAS_WIFI
|
||||
|
||||
#define GPS_SERIAL_NUM 1
|
||||
#define GPS_RX_PIN 34
|
||||
#ifdef USE_JTAG
|
||||
|
|
|
@ -33,7 +33,7 @@ void WiFiServerAPI::loop()
|
|||
if (client.connected()) {
|
||||
StreamAPI::loop();
|
||||
} else {
|
||||
DEBUG_MSG("Client dropped connection, closing UDP server\n");
|
||||
DEBUG_MSG("Client dropped connection, closing TCP server\n");
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ WiFiServerPort::WiFiServerPort() : WiFiServer(MESHTASTIC_PORTNUM) {}
|
|||
|
||||
void WiFiServerPort::init()
|
||||
{
|
||||
DEBUG_MSG("Listening on TCP port %d\n", MESHTASTIC_PORTNUM);
|
||||
DEBUG_MSG("API server sistening on TCP port %d\n", MESHTASTIC_PORTNUM);
|
||||
begin();
|
||||
}
|
||||
|
||||
|
@ -52,6 +52,14 @@ void WiFiServerPort::loop()
|
|||
{
|
||||
auto client = available();
|
||||
if (client) {
|
||||
new WiFiServerAPI(client);
|
||||
// Close any previous connection (see FIXME in header file)
|
||||
if (openAPI)
|
||||
delete openAPI;
|
||||
|
||||
openAPI = new WiFiServerAPI(client);
|
||||
}
|
||||
|
||||
if (openAPI)
|
||||
// Allow idle processing so the API can read from its incoming stream
|
||||
openAPI->loop();
|
||||
}
|
|
@ -29,6 +29,13 @@ class WiFiServerAPI : public StreamAPI
|
|||
*/
|
||||
class WiFiServerPort : public WiFiServer
|
||||
{
|
||||
/** The currently open port
|
||||
*
|
||||
* FIXME: We currently only allow one open TCP connection at a time, because we depend on the loop() call in this class to
|
||||
* delegate to the worker. Once coroutines are implemented we can relax this restriction.
|
||||
*/
|
||||
WiFiServerAPI *openAPI = NULL;
|
||||
|
||||
public:
|
||||
WiFiServerPort();
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
#include "graphics/images.h"
|
||||
#include "main.h"
|
||||
#include "mesh-pb-constants.h"
|
||||
#include "meshwifi/meshwifi.h"
|
||||
#include "target_specific.h"
|
||||
#include "utils.h"
|
||||
|
||||
|
@ -709,6 +710,11 @@ void Screen::drawDebugInfoSettingsTrampoline(OLEDDisplay *display, OLEDDisplayUi
|
|||
screen->debugInfo.drawFrameSettings(display, state, x, y);
|
||||
}
|
||||
|
||||
void Screen::drawDebugInfoWiFiTrampoline(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||
{
|
||||
Screen *screen = reinterpret_cast<Screen *>(state->userData);
|
||||
screen->debugInfo.drawFrameWiFi(display, state, x, y);
|
||||
}
|
||||
|
||||
// restore our regular frame list
|
||||
void Screen::setFrames()
|
||||
|
@ -740,6 +746,11 @@ void Screen::setFrames()
|
|||
// call a method on debugInfoScreen object (for more details)
|
||||
normalFrames[numframes++] = &Screen::drawDebugInfoSettingsTrampoline;
|
||||
|
||||
if (isWifiAvailable()) {
|
||||
// call a method on debugInfoScreen object (for more details)
|
||||
normalFrames[numframes++] = &Screen::drawDebugInfoWiFiTrampoline;
|
||||
}
|
||||
|
||||
ui.setFrames(normalFrames, numframes);
|
||||
ui.enableAllIndicators();
|
||||
|
||||
|
@ -798,18 +809,18 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
|
|||
{
|
||||
concurrency::LockGuard guard(&lock);
|
||||
snprintf(channelStr, sizeof(channelStr), "%s", channelName.c_str());
|
||||
|
||||
// Display power status
|
||||
if (powerStatus->getHasBattery())
|
||||
drawBattery(display, x, y + 2, imgBattery, powerStatus);
|
||||
else if (powerStatus->knowsUSB())
|
||||
display->drawFastImage(x, y + 2, 16, 8, powerStatus->getHasUSB() ? imgUSB : imgPower);
|
||||
// Display nodes status
|
||||
drawNodes(display, x + (SCREEN_WIDTH * 0.25), y + 2, nodeStatus);
|
||||
// Display GPS status
|
||||
drawGPS(display, x + (SCREEN_WIDTH * 0.63), y + 2, gpsStatus);
|
||||
}
|
||||
|
||||
// Display power status
|
||||
if (powerStatus->getHasBattery())
|
||||
drawBattery(display, x, y + 2, imgBattery, powerStatus);
|
||||
else if (powerStatus->knowsUSB())
|
||||
display->drawFastImage(x, y + 2, 16, 8, powerStatus->getHasUSB() ? imgUSB : imgPower);
|
||||
// Display nodes status
|
||||
drawNodes(display, x + (SCREEN_WIDTH * 0.25), y + 2, nodeStatus);
|
||||
// Display GPS status
|
||||
drawGPS(display, x + (SCREEN_WIDTH * 0.63), y + 2, gpsStatus);
|
||||
|
||||
// Draw the channel name
|
||||
display->drawString(x, y + FONT_HEIGHT, channelStr);
|
||||
// Draw our hardware ID to assist with bluetooth pairing
|
||||
|
@ -828,6 +839,135 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
|
|||
}
|
||||
|
||||
// Jm
|
||||
void DebugInfo::drawFrameWiFi(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||
{
|
||||
#ifdef HAS_WIFI
|
||||
const char *wifiName = radioConfig.preferences.wifi_ssid;
|
||||
const char *wifiPsw = radioConfig.preferences.wifi_password;
|
||||
|
||||
displayedNodeNum = 0; // Not currently showing a node pane
|
||||
|
||||
display->setFont(ArialMT_Plain_10);
|
||||
|
||||
// The coordinates define the left starting point of the text
|
||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||
|
||||
if (radioConfig.preferences.wifi_ap_mode) {
|
||||
display->drawString(x, y, String("WiFi: Software AP"));
|
||||
} else if (WiFi.status() != WL_CONNECTED) {
|
||||
display->drawString(x, y, String("WiFi: Not Connected"));
|
||||
} else {
|
||||
display->drawString(x, y, String("WiFi: Connected"));
|
||||
|
||||
display->drawString(x + SCREEN_WIDTH - display->getStringWidth("RSSI " + String(WiFi.RSSI())), y,
|
||||
"RSSI " + String(WiFi.RSSI()));
|
||||
}
|
||||
|
||||
/*
|
||||
- WL_CONNECTED: assigned when connected to a WiFi network;
|
||||
- WL_NO_SSID_AVAIL: assigned when no SSID are available;
|
||||
- WL_CONNECT_FAILED: assigned when the connection fails for all the attempts;
|
||||
- WL_CONNECTION_LOST: assigned when the connection is lost;
|
||||
- WL_DISCONNECTED: assigned when disconnected from a network;
|
||||
- WL_IDLE_STATUS: it is a temporary status assigned when WiFi.begin() is called and remains active until the number of
|
||||
attempts expires (resulting in WL_CONNECT_FAILED) or a connection is established (resulting in WL_CONNECTED);
|
||||
- WL_SCAN_COMPLETED: assigned when the scan networks is completed;
|
||||
- WL_NO_SHIELD: assigned when no WiFi shield is present;
|
||||
|
||||
*/
|
||||
|
||||
if (WiFi.status() == WL_CONNECTED) {
|
||||
if (radioConfig.preferences.wifi_ap_mode) {
|
||||
display->drawString(x, y + FONT_HEIGHT * 1, "IP: " + String(WiFi.softAPIP().toString().c_str()));
|
||||
} else {
|
||||
display->drawString(x, y + FONT_HEIGHT * 1, "IP: " + String(WiFi.localIP().toString().c_str()));
|
||||
}
|
||||
} else if (WiFi.status() == WL_NO_SSID_AVAIL) {
|
||||
display->drawString(x, y + FONT_HEIGHT * 1, "SSID Not Found");
|
||||
} else if (WiFi.status() == WL_CONNECTION_LOST) {
|
||||
display->drawString(x, y + FONT_HEIGHT * 1, "Connection Lost");
|
||||
} else if (WiFi.status() == WL_CONNECT_FAILED) {
|
||||
display->drawString(x, y + FONT_HEIGHT * 1, "Connection Failed");
|
||||
} else if (WiFi.status() == WL_DISCONNECTED) {
|
||||
display->drawString(x, y + FONT_HEIGHT * 1, "Disconnected");
|
||||
} else if (WiFi.status() == WL_IDLE_STATUS) {
|
||||
display->drawString(x, y + FONT_HEIGHT * 1, "Idle ... Reconnecting");
|
||||
} else {
|
||||
// Codes:
|
||||
// https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/wifi.html#wi-fi-reason-code
|
||||
if (getWifiDisconnectReason() == 2) {
|
||||
display->drawString(x, y + FONT_HEIGHT * 1, "Authentication Invalid");
|
||||
} else if (getWifiDisconnectReason() == 3) {
|
||||
display->drawString(x, y + FONT_HEIGHT * 1, "De-authenticated");
|
||||
} else if (getWifiDisconnectReason() == 4) {
|
||||
display->drawString(x, y + FONT_HEIGHT * 1, "Disassociated Expired");
|
||||
} else if (getWifiDisconnectReason() == 5) {
|
||||
display->drawString(x, y + FONT_HEIGHT * 1, "AP - Too Many Clients");
|
||||
} else if (getWifiDisconnectReason() == 6) {
|
||||
display->drawString(x, y + FONT_HEIGHT * 1, "NOT_AUTHED");
|
||||
} else if (getWifiDisconnectReason() == 7) {
|
||||
display->drawString(x, y + FONT_HEIGHT * 1, "NOT_ASSOCED");
|
||||
} else if (getWifiDisconnectReason() == 8) {
|
||||
display->drawString(x, y + FONT_HEIGHT * 1, "Disassociated");
|
||||
} else if (getWifiDisconnectReason() == 9) {
|
||||
display->drawString(x, y + FONT_HEIGHT * 1, "ASSOC_NOT_AUTHED");
|
||||
} else if (getWifiDisconnectReason() == 10) {
|
||||
display->drawString(x, y + FONT_HEIGHT * 1, "DISASSOC_PWRCAP_BAD");
|
||||
} else if (getWifiDisconnectReason() == 11) {
|
||||
display->drawString(x, y + FONT_HEIGHT * 1, "DISASSOC_SUPCHAN_BAD");
|
||||
} else if (getWifiDisconnectReason() == 13) {
|
||||
display->drawString(x, y + FONT_HEIGHT * 1, "IE_INVALID");
|
||||
} else if (getWifiDisconnectReason() == 14) {
|
||||
display->drawString(x, y + FONT_HEIGHT * 1, "MIC_FAILURE");
|
||||
} else if (getWifiDisconnectReason() == 15) {
|
||||
display->drawString(x, y + FONT_HEIGHT * 1, "4WAY_HANDSHAKE_TIMEOUT");
|
||||
} else if (getWifiDisconnectReason() == 16) {
|
||||
display->drawString(x, y + FONT_HEIGHT * 1, "GROUP_KEY_UPDATE_TIMEOUT");
|
||||
} else if (getWifiDisconnectReason() == 17) {
|
||||
display->drawString(x, y + FONT_HEIGHT * 1, "IE_IN_4WAY_DIFFERS");
|
||||
} else if (getWifiDisconnectReason() == 18) {
|
||||
display->drawString(x, y + FONT_HEIGHT * 1, "Invalid Group Cipher");
|
||||
} else if (getWifiDisconnectReason() == 19) {
|
||||
display->drawString(x, y + FONT_HEIGHT * 1, "Invalid Pairwise Cipher");
|
||||
} else if (getWifiDisconnectReason() == 20) {
|
||||
display->drawString(x, y + FONT_HEIGHT * 1, "AKMP_INVALID");
|
||||
} else if (getWifiDisconnectReason() == 21) {
|
||||
display->drawString(x, y + FONT_HEIGHT * 1, "UNSUPP_RSN_IE_VERSION");
|
||||
} else if (getWifiDisconnectReason() == 22) {
|
||||
display->drawString(x, y + FONT_HEIGHT * 1, "INVALID_RSN_IE_CAP");
|
||||
} else if (getWifiDisconnectReason() == 23) {
|
||||
display->drawString(x, y + FONT_HEIGHT * 1, "802_1X_AUTH_FAILED");
|
||||
} else if (getWifiDisconnectReason() == 24) {
|
||||
display->drawString(x, y + FONT_HEIGHT * 1, "CIPHER_SUITE_REJECTED");
|
||||
} else if (getWifiDisconnectReason() == 200) {
|
||||
display->drawString(x, y + FONT_HEIGHT * 1, "BEACON_TIMEOUT");
|
||||
} else if (getWifiDisconnectReason() == 201) {
|
||||
display->drawString(x, y + FONT_HEIGHT * 1, "AP Not Found");
|
||||
} else if (getWifiDisconnectReason() == 202) {
|
||||
display->drawString(x, y + FONT_HEIGHT * 1, "AUTH_FAIL");
|
||||
} else if (getWifiDisconnectReason() == 203) {
|
||||
display->drawString(x, y + FONT_HEIGHT * 1, "ASSOC_FAIL");
|
||||
} else if (getWifiDisconnectReason() == 204) {
|
||||
display->drawString(x, y + FONT_HEIGHT * 1, "HANDSHAKE_TIMEOUT");
|
||||
} else if (getWifiDisconnectReason() == 205) {
|
||||
display->drawString(x, y + FONT_HEIGHT * 1, "Connection Failed");
|
||||
} else {
|
||||
display->drawString(x, y + FONT_HEIGHT * 1, "Unknown Status");
|
||||
}
|
||||
}
|
||||
|
||||
display->drawString(x, y + FONT_HEIGHT * 2, "SSID: " + String(wifiName));
|
||||
display->drawString(x, y + FONT_HEIGHT * 3, "PWD: " + String(wifiPsw));
|
||||
|
||||
/* Display a heartbeat pixel that blinks every time the frame is redrawn */
|
||||
#ifdef SHOW_REDRAWS
|
||||
if (heartbeat)
|
||||
display->setPixel(0, 0);
|
||||
heartbeat = !heartbeat;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void DebugInfo::drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||
{
|
||||
displayedNodeNum = 0; // Not currently showing a node pane
|
||||
|
@ -838,30 +978,24 @@ void DebugInfo::drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *stat
|
|||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||
|
||||
char batStr[20];
|
||||
if (powerStatus->getHasBattery())
|
||||
{
|
||||
if (powerStatus->getHasBattery()) {
|
||||
int batV = powerStatus->getBatteryVoltageMv() / 1000;
|
||||
int batCv = (powerStatus->getBatteryVoltageMv() % 1000) / 10;
|
||||
|
||||
snprintf(batStr, sizeof(batStr), "B %01d.%02dV %3d%% %c%c",
|
||||
batV,
|
||||
batCv,
|
||||
powerStatus->getBatteryChargePercent(),
|
||||
powerStatus->getIsCharging() ? '+' : ' ',
|
||||
powerStatus->getHasUSB() ? 'U' : ' ');
|
||||
snprintf(batStr, sizeof(batStr), "B %01d.%02dV %3d%% %c%c", batV, batCv, powerStatus->getBatteryChargePercent(),
|
||||
powerStatus->getIsCharging() ? '+' : ' ', powerStatus->getHasUSB() ? 'U' : ' ');
|
||||
|
||||
// Line 1
|
||||
display->drawString(x, y, batStr);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// Line 1
|
||||
display->drawString(x, y, String("USB"));
|
||||
}
|
||||
}
|
||||
|
||||
display->drawString(x + SCREEN_WIDTH - display->getStringWidth("Mode " + String(channelSettings.modem_config)),
|
||||
y, "Mode " + String(channelSettings.modem_config));
|
||||
|
||||
|
||||
//TODO: Display status of the BT radio
|
||||
// display->drawString(x + SCREEN_WIDTH - display->getStringWidth("BT On"), y, "BT On");
|
||||
|
||||
// Line 2
|
||||
uint32_t currentMillis = millis();
|
||||
|
@ -874,20 +1008,13 @@ void DebugInfo::drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *stat
|
|||
minutes %= 60;
|
||||
hours %= 24;
|
||||
|
||||
display->drawString(x, y + FONT_HEIGHT * 1, String(days) + "d "
|
||||
+ (hours < 10 ? "0" : "") + String(hours) + ":"
|
||||
+ (minutes < 10 ? "0" : "") + String(minutes) + ":"
|
||||
+ (seconds < 10 ? "0" : "") + String(seconds));
|
||||
display->drawString(x + SCREEN_WIDTH - display->getStringWidth("Mode " + String(channelSettings.modem_config)), y + FONT_HEIGHT * 1, "Mode " + String(channelSettings.modem_config));
|
||||
|
||||
// Line 3
|
||||
// TODO: Use this line for WiFi information.
|
||||
// display->drawString(x + (SCREEN_WIDTH - (display->getStringWidth("WiFi: 192.168.0.100"))) / 2, y + FONT_HEIGHT * 2, "WiFi: 192.168.0.100");
|
||||
display->drawString(x, y + FONT_HEIGHT * 1,
|
||||
String(days) + "d " + (hours < 10 ? "0" : "") + String(hours) + ":" + (minutes < 10 ? "0" : "") +
|
||||
String(minutes) + ":" + (seconds < 10 ? "0" : "") + String(seconds));
|
||||
|
||||
// Line 4
|
||||
drawGPScoordinates(display, x, y + FONT_HEIGHT * 3, gpsStatus);
|
||||
|
||||
|
||||
/* Display a heartbeat pixel that blinks every time the frame is redrawn */
|
||||
#ifdef SHOW_REDRAWS
|
||||
if (heartbeat)
|
||||
|
|
|
@ -47,6 +47,7 @@ class DebugInfo
|
|||
/// Renders the debug screen.
|
||||
void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
|
||||
void drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
|
||||
void drawFrameWiFi(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
|
||||
|
||||
std::string channelName;
|
||||
|
||||
|
@ -220,6 +221,8 @@ class Screen : public concurrency::PeriodicTask
|
|||
|
||||
static void drawDebugInfoSettingsTrampoline(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
|
||||
|
||||
static void drawDebugInfoWiFiTrampoline(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
|
||||
|
||||
/// Queue of commands to execute in doTask.
|
||||
TypedQueue<ScreenCmd> cmdQueue;
|
||||
/// Whether we are using a display
|
||||
|
|
12
src/main.cpp
12
src/main.cpp
|
@ -37,6 +37,8 @@
|
|||
#include "SPILock.h"
|
||||
#include "graphics/Screen.h"
|
||||
#include "main.h"
|
||||
#include "meshwifi/meshhttp.h"
|
||||
#include "meshwifi/meshwifi.h"
|
||||
#include "sleep.h"
|
||||
#include "target_specific.h"
|
||||
#include <OneButton.h>
|
||||
|
@ -217,9 +219,9 @@ void setup()
|
|||
|
||||
// Currently only the tbeam has a PMU
|
||||
power = new Power();
|
||||
power->setup();
|
||||
power->setStatusHandler(powerStatus);
|
||||
powerStatus->observe(&power->newStatus);
|
||||
power->setup(); // Must be after status handler is installed, so that handler gets notified of the initial configuration
|
||||
|
||||
// Init our SPI controller (must be before screen and lora)
|
||||
initSPI();
|
||||
|
@ -328,6 +330,9 @@ void setup()
|
|||
}
|
||||
#endif
|
||||
|
||||
// Initialize Wifi
|
||||
initWifi();
|
||||
|
||||
if (!rIf)
|
||||
recordCriticalError(ErrNoRadio);
|
||||
else
|
||||
|
@ -394,6 +399,8 @@ void loop()
|
|||
userButtonAlt.tick();
|
||||
#endif
|
||||
|
||||
loopWifi();
|
||||
|
||||
// Show boot screen for first 3 seconds, then switch to normal operation.
|
||||
static bool showingBootScreen = true;
|
||||
if (showingBootScreen && (millis() > 3000)) {
|
||||
|
@ -420,5 +427,8 @@ void loop()
|
|||
// feel slow
|
||||
msecstosleep = 10;
|
||||
|
||||
// TODO: This should go into a thread handled by FreeRTOS.
|
||||
handleWebResponse();
|
||||
|
||||
delay(msecstosleep);
|
||||
}
|
||||
|
|
|
@ -10,6 +10,8 @@ extern bool ssd1306_found;
|
|||
extern bool isCharging;
|
||||
extern bool isUSBPowered;
|
||||
|
||||
|
||||
|
||||
// Global Screen singleton.
|
||||
extern graphics::Screen screen;
|
||||
//extern Observable<meshtastic::PowerStatus> newPowerStatus; //TODO: move this to main-esp32.cpp somehow or a helper class
|
||||
|
@ -23,4 +25,4 @@ const char *getDeviceName();
|
|||
|
||||
|
||||
|
||||
void nrf52Setup(), esp32Setup(), nrf52Loop(), esp32Loop();
|
||||
void nrf52Setup(), esp32Setup(), nrf52Loop(), esp32Loop();
|
||||
|
|
|
@ -6,87 +6,14 @@
|
|||
#include "configuration.h"
|
||||
#include "mesh.pb.h"
|
||||
|
||||
// US channel settings
|
||||
#define CH0_US 903.08f // MHz
|
||||
#define CH_SPACING_US 2.16f // MHz
|
||||
#define NUM_CHANNELS_US 13
|
||||
// Map from old region names to new region enums
|
||||
struct RegionInfo {
|
||||
RegionCode code;
|
||||
uint8_t numChannels;
|
||||
uint8_t powerLimit; // Or zero for not set
|
||||
float freq;
|
||||
float spacing;
|
||||
const char *name; // EU433 etc
|
||||
};
|
||||
|
||||
// EU433 channel settings
|
||||
#define CH0_EU433 433.175f // MHz
|
||||
#define CH_SPACING_EU433 0.2f // MHz
|
||||
#define NUM_CHANNELS_EU433 8
|
||||
|
||||
// EU865 channel settings
|
||||
#define CH0_EU865 865.2f // MHz
|
||||
#define CH_SPACING_EU865 0.3f // MHz
|
||||
#define NUM_CHANNELS_EU865 10
|
||||
|
||||
// CN channel settings
|
||||
#define CH0_CN 470.0f // MHz
|
||||
#define CH_SPACING_CN 2.0f // MHz FIXME, this is just a guess for 470-510
|
||||
#define NUM_CHANNELS_CN 20
|
||||
|
||||
// JP channel settings (AS1 bandplan)
|
||||
#define CH0_JP 920.0f // MHz
|
||||
#define CH_SPACING_JP 0.5f
|
||||
#define NUM_CHANNELS_JP 10
|
||||
|
||||
// TW channel settings (AS2 bandplan 923-925MHz)
|
||||
#define CH0_TW 923.0f // MHz
|
||||
#define CH_SPACING_TW 0.2
|
||||
#define NUM_CHANNELS_TW 10
|
||||
|
||||
// AU/NZ channel settings 915-928MHz
|
||||
#define CH0_ANZ 916.0f // MHz - avoid overcrowding on 915.0
|
||||
#define CH_SPACING_ANZ 0.5f
|
||||
#define NUM_CHANNELS_ANZ 20
|
||||
|
||||
// KR channel settings (KR920-923)
|
||||
// Start from TTN download channel freq. (921.9f is for download, others are for uplink)
|
||||
#define CH0_KR 921.9f // MHz
|
||||
#define CH_SPACING_KR 0.2f
|
||||
#define NUM_CHANNELS_KR 8
|
||||
|
||||
// FIXME add defs for other regions and use them here
|
||||
#ifdef HW_VERSION_US
|
||||
#define CH0 CH0_US
|
||||
#define CH_SPACING CH_SPACING_US
|
||||
#define NUM_CHANNELS NUM_CHANNELS_US
|
||||
#elif defined(HW_VERSION_EU433)
|
||||
#define CH0 CH0_EU433
|
||||
#define CH_SPACING CH_SPACING_EU433
|
||||
#define NUM_CHANNELS NUM_CHANNELS_EU433
|
||||
#elif defined(HW_VERSION_EU865)
|
||||
#define CH0 CH0_EU865
|
||||
#define CH_SPACING CH_SPACING_EU865
|
||||
#define NUM_CHANNELS NUM_CHANNELS_EU865
|
||||
#elif defined(HW_VERSION_CN)
|
||||
#define CH0 CH0_CN
|
||||
#define CH_SPACING CH_SPACING_CN
|
||||
#define NUM_CHANNELS NUM_CHANNELS_CN
|
||||
#elif defined(HW_VERSION_JP)
|
||||
// Also called AS1 bandplan
|
||||
#define CH0 CH0_JP
|
||||
#define CH_SPACING CH_SPACING_JP
|
||||
#define NUM_CHANNELS NUM_CHANNELS_JP
|
||||
#elif defined(HW_VERSION_TW)
|
||||
// Also called AS2 bandplan
|
||||
#define CH0 CH0_TW
|
||||
#define CH_SPACING CH_SPACING_TW
|
||||
#define NUM_CHANNELS NUM_CHANNELS_TW
|
||||
#elif defined(HW_VERSION_ANZ)
|
||||
// Australia and NZ
|
||||
#define CH0 CH0_ANZ
|
||||
#define CH_SPACING CH_SPACING_ANZ
|
||||
#define NUM_CHANNELS NUM_CHANNELS_ANZ
|
||||
#elif defined(HW_VERSION_KR)
|
||||
// Republic of Korea
|
||||
#define CH0 CH0_KR
|
||||
#define CH_SPACING CH_SPACING_KR
|
||||
#define NUM_CHANNELS NUM_CHANNELS_KR
|
||||
#else
|
||||
// HW version not set - assume US
|
||||
#define CH0 CH0_US
|
||||
#define CH_SPACING CH_SPACING_US
|
||||
#define NUM_CHANNELS NUM_CHANNELS_US
|
||||
#endif
|
||||
extern const RegionInfo regions[];
|
|
@ -197,12 +197,14 @@ void MeshService::loop()
|
|||
}
|
||||
|
||||
/// The radioConfig object just changed, call this to force the hw to change to the new settings
|
||||
void MeshService::reloadConfig()
|
||||
bool MeshService::reloadConfig()
|
||||
{
|
||||
// If we can successfully set this radio to these settings, save them to disk
|
||||
nodeDB.resetRadioConfig(); // Don't let the phone send us fatally bad settings
|
||||
bool didReset = nodeDB.resetRadioConfig(); // Don't let the phone send us fatally bad settings
|
||||
configChanged.notifyObservers(NULL);
|
||||
nodeDB.saveToDisk();
|
||||
|
||||
return didReset;
|
||||
}
|
||||
|
||||
/// The owner User record just got updated, update our node DB and broadcast the info into the mesh
|
||||
|
|
|
@ -63,8 +63,10 @@ class MeshService
|
|||
*/
|
||||
void handleToRadio(MeshPacket &p);
|
||||
|
||||
/// The radioConfig object just changed, call this to force the hw to change to the new settings
|
||||
void reloadConfig();
|
||||
/** The radioConfig object just changed, call this to force the hw to change to the new settings
|
||||
* @return true if client devices should be sent a new set of radio configs
|
||||
*/
|
||||
bool reloadConfig();
|
||||
|
||||
/// The owner User record just got updated, update our node DB and broadcast the info into the mesh
|
||||
void reloadOwner();
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include "CryptoEngine.h"
|
||||
#include "GPS.h"
|
||||
#include "MeshRadio.h"
|
||||
#include "NodeDB.h"
|
||||
#include "PacketHistory.h"
|
||||
#include "PowerFSM.h"
|
||||
|
@ -13,6 +14,7 @@
|
|||
#include "configuration.h"
|
||||
#include "error.h"
|
||||
#include "mesh-pb-constants.h"
|
||||
#include "meshwifi/meshwifi.h"
|
||||
#include <pb_decode.h>
|
||||
#include <pb_encode.h>
|
||||
|
||||
|
@ -102,14 +104,21 @@ const char *getChannelName()
|
|||
|
||||
NodeDB::NodeDB() : nodes(devicestate.node_db), numNodes(&devicestate.node_db_count) {}
|
||||
|
||||
void NodeDB::resetRadioConfig()
|
||||
bool NodeDB::resetRadioConfig()
|
||||
{
|
||||
bool didFactoryReset = false;
|
||||
|
||||
/// 16 bytes of random PSK for our _public_ default channel that all devices power up on (AES128)
|
||||
static const uint8_t defaultpsk[] = {0xd4, 0xf1, 0xbb, 0x3a, 0x20, 0x29, 0x07, 0x59,
|
||||
0xf0, 0xbc, 0xff, 0xab, 0xcf, 0x4e, 0x69, 0xbf};
|
||||
|
||||
if (radioConfig.preferences.sds_secs == 0) {
|
||||
DEBUG_MSG("RadioConfig reset!\n");
|
||||
if (radioConfig.preferences.factory_reset) {
|
||||
DEBUG_MSG("Performing factory reset!\n");
|
||||
installDefaultDeviceState();
|
||||
didFactoryReset = true;
|
||||
} else if (radioConfig.preferences.sds_secs == 0) {
|
||||
DEBUG_MSG("Fixing bogus RadioConfig!\n");
|
||||
|
||||
radioConfig.preferences.send_owner_interval = 4; // per sw-design.md
|
||||
radioConfig.preferences.position_broadcast_secs = 15 * 60;
|
||||
radioConfig.preferences.wait_bluetooth_secs = 120;
|
||||
|
@ -123,8 +132,8 @@ void NodeDB::resetRadioConfig()
|
|||
radioConfig.has_preferences = true;
|
||||
|
||||
// radioConfig.modem_config = RadioConfig_ModemConfig_Bw125Cr45Sf128; // medium range and fast
|
||||
// channelSettings.modem_config = ChannelSettings_ModemConfig_Bw500Cr45Sf128; // short range and fast, but wide bandwidth
|
||||
// so incompatible radios can talk together
|
||||
// channelSettings.modem_config = ChannelSettings_ModemConfig_Bw500Cr45Sf128; // short range and fast, but wide
|
||||
// bandwidth so incompatible radios can talk together
|
||||
channelSettings.modem_config = ChannelSettings_ModemConfig_Bw125Cr48Sf4096; // slow and long range
|
||||
|
||||
channelSettings.tx_power = 0; // default
|
||||
|
@ -147,12 +156,20 @@ void NodeDB::resetRadioConfig()
|
|||
radioConfig.preferences.position_broadcast_secs = 6 * 60;
|
||||
radioConfig.preferences.ls_secs = 60;
|
||||
}
|
||||
|
||||
return didFactoryReset;
|
||||
}
|
||||
|
||||
void NodeDB::installDefaultDeviceState()
|
||||
{
|
||||
// We try to preserve the region setting because it will really bum users out if we discard it
|
||||
String oldRegion = myNodeInfo.region;
|
||||
RegionCode oldRegionCode = radioConfig.preferences.region;
|
||||
|
||||
memset(&devicestate, 0, sizeof(devicestate));
|
||||
|
||||
*numNodes = 0; // Forget node DB
|
||||
|
||||
// init our devicestate with valid flags so protobuf writing/reading will work
|
||||
devicestate.has_my_node = true;
|
||||
devicestate.has_radio = true;
|
||||
|
@ -181,6 +198,12 @@ void NodeDB::installDefaultDeviceState()
|
|||
// owner.short_name now
|
||||
sprintf(owner.long_name, "Unknown %02x%02x", ourMacAddr[4], ourMacAddr[5]);
|
||||
sprintf(owner.short_name, "?%02X", (unsigned)(myNodeInfo.my_node_num & 0xff));
|
||||
|
||||
// Restore region if possible
|
||||
if (oldRegionCode != RegionCode_Unset)
|
||||
radioConfig.preferences.region = oldRegionCode;
|
||||
if (oldRegion.length())
|
||||
strcpy(myNodeInfo.region, oldRegion.c_str());
|
||||
}
|
||||
|
||||
void NodeDB::init()
|
||||
|
@ -213,13 +236,29 @@ void NodeDB::init()
|
|||
|
||||
// We set these _after_ loading from disk - because they come from the build and are more trusted than
|
||||
// what is stored in flash
|
||||
strncpy(myNodeInfo.region, optstr(HW_VERSION), sizeof(myNodeInfo.region));
|
||||
if (xstr(HW_VERSION)[0])
|
||||
strncpy(myNodeInfo.region, optstr(HW_VERSION), sizeof(myNodeInfo.region));
|
||||
else
|
||||
DEBUG_MSG("This build does not specify a HW_VERSION\n"); // Eventually new builds will no longer include this build flag
|
||||
|
||||
// Check for the old style of region code strings, if found, convert to the new enum.
|
||||
// Those strings will look like "1.0-EU433"
|
||||
if (radioConfig.preferences.region == RegionCode_Unset && strncmp(myNodeInfo.region, "1.0-", 4) == 0) {
|
||||
const char *regionStr = myNodeInfo.region + 4; // EU433 or whatever
|
||||
for (const RegionInfo *r = regions; r->code != RegionCode_Unset; r++)
|
||||
if (strcmp(r->name, regionStr) == 0) {
|
||||
radioConfig.preferences.region = r->code;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
strncpy(myNodeInfo.firmware_version, optstr(APP_VERSION), sizeof(myNodeInfo.firmware_version));
|
||||
strncpy(myNodeInfo.hw_model, HW_VENDOR, sizeof(myNodeInfo.hw_model));
|
||||
|
||||
resetRadioConfig(); // If bogus settings got saved, then fix them
|
||||
|
||||
DEBUG_MSG("NODENUM=0x%x, dbsize=%d\n", myNodeInfo.my_node_num, *numNodes);
|
||||
DEBUG_MSG("legacy_region=%s, region=%d, NODENUM=0x%x, dbsize=%d\n", myNodeInfo.region, radioConfig.preferences.region,
|
||||
myNodeInfo.my_node_num, *numNodes);
|
||||
}
|
||||
|
||||
// We reserve a few nodenums for future use
|
||||
|
@ -406,6 +445,12 @@ void NodeDB::updateFrom(const MeshPacket &mp)
|
|||
updateTextMessage = true;
|
||||
powerFSM.trigger(EVENT_RECEIVED_TEXT_MSG);
|
||||
notifyObservers(true); // Force an update whether or not our node counts have changed
|
||||
|
||||
// This is going into the wifidev feature branch
|
||||
// Only update the WebUI if WiFi is enabled
|
||||
//#if WiFi_MODE != 0
|
||||
// notifyWebUI();
|
||||
//#endif
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -46,8 +46,13 @@ class NodeDB
|
|||
/// write to flash
|
||||
void saveToDisk();
|
||||
|
||||
// Reinit radio config if needed, because sometimes a buggy android app might send us bogus settings
|
||||
void resetRadioConfig();
|
||||
/** Reinit radio config if needed, because either:
|
||||
* a) sometimes a buggy android app might send us bogus settings or
|
||||
* b) the client set factory_reset
|
||||
*
|
||||
* @return true if the config was completely reset, in that case, we should send it back to the client
|
||||
*/
|
||||
bool resetRadioConfig();
|
||||
|
||||
/// given a subpacket sniffed from the network, update our DB state
|
||||
/// we updateGUI and updateGUIforNode if we think our this change is big enough for a redraw
|
||||
|
|
|
@ -243,7 +243,10 @@ void PhoneAPI::handleSetRadio(const RadioConfig &r)
|
|||
{
|
||||
radioConfig = r;
|
||||
|
||||
service.reloadConfig();
|
||||
bool didReset = service.reloadConfig();
|
||||
if (didReset) {
|
||||
state = STATE_SEND_MY_INFO; // Squirt a completely new set of configs to the client
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -10,6 +10,24 @@
|
|||
#include <pb_decode.h>
|
||||
#include <pb_encode.h>
|
||||
|
||||
#define RDEF(name, freq, spacing, num_ch, power_limit) \
|
||||
{ \
|
||||
RegionCode_##name, num_ch, power_limit, freq, spacing, #name \
|
||||
}
|
||||
|
||||
const RegionInfo regions[] = {
|
||||
RDEF(US, 903.08f, 2.16f, 13, 0), RDEF(EU433, 433.175f, 0.2f, 8, 0), RDEF(EU865, 865.2f, 0.3f, 10, 0),
|
||||
RDEF(CN, 470.0f, 2.0f, 20, 0),
|
||||
RDEF(JP, 920.0f, 0.5f, 10, 13), // See https://github.com/meshtastic/Meshtastic-device/issues/346 power level 13
|
||||
RDEF(ANZ, 916.0f, 0.5f, 20, 0), // AU/NZ channel settings 915-928MHz
|
||||
RDEF(KR, 921.9f, 0.2f, 8, 0), // KR channel settings (KR920-923) Start from TTN download channel
|
||||
// freq. (921.9f is for download, others are for uplink)
|
||||
RDEF(TW, 923.0f, 0.2f, 10, 0), // TW channel settings (AS2 bandplan 923-925MHz)
|
||||
RDEF(Unset, 903.08f, 2.16f, 13, 0) // Assume US freqs if unset, Must be last
|
||||
};
|
||||
|
||||
static const RegionInfo *myRegion;
|
||||
|
||||
/**
|
||||
* ## LoRaWAN for North America
|
||||
|
||||
|
@ -77,7 +95,15 @@ RadioInterface::RadioInterface()
|
|||
{
|
||||
assert(sizeof(PacketHeader) == 4 || sizeof(PacketHeader) == 16); // make sure the compiler did what we expected
|
||||
|
||||
myNodeInfo.num_channels = NUM_CHANNELS;
|
||||
if (!myRegion) {
|
||||
const RegionInfo *r = regions;
|
||||
for (; r->code != RegionCode_Unset && r->code != radioConfig.preferences.region; r++)
|
||||
;
|
||||
myRegion = r;
|
||||
DEBUG_MSG("Wanted region %d, using %s\n", radioConfig.preferences.region, r->name);
|
||||
|
||||
myNodeInfo.num_channels = myRegion->numChannels; // Tell our android app how many channels we have
|
||||
}
|
||||
|
||||
// Can't print strings this early - serial not setup yet
|
||||
// DEBUG_MSG("Set meshradio defaults name=%s\n", channelSettings.name);
|
||||
|
@ -127,9 +153,12 @@ void RadioInterface::applyModemConfig()
|
|||
|
||||
power = channelSettings.tx_power;
|
||||
|
||||
assert(myRegion); // Should have been found in init
|
||||
|
||||
// If user has manually specified a channel num, then use that, otherwise generate one by hashing the name
|
||||
int channel_num = (channelSettings.channel_num ? channelSettings.channel_num - 1 : hash(channelSettings.name)) % NUM_CHANNELS;
|
||||
freq = CH0 + CH_SPACING * channel_num;
|
||||
int channel_num =
|
||||
(channelSettings.channel_num ? channelSettings.channel_num - 1 : hash(channelSettings.name)) % myRegion->numChannels;
|
||||
freq = myRegion->freq + myRegion->spacing * channel_num;
|
||||
|
||||
DEBUG_MSG("Set radio: name=%s, config=%u, ch=%d, power=%d\n", channelSettings.name, channelSettings.modem_config, channel_num,
|
||||
power);
|
||||
|
@ -143,9 +172,9 @@ void RadioInterface::limitPower()
|
|||
{
|
||||
uint8_t maxPower = 255; // No limit
|
||||
|
||||
#ifdef HW_VERSION_JP
|
||||
maxPower = 13; // See https://github.com/meshtastic/Meshtastic-device/issues/346
|
||||
#endif
|
||||
if (myRegion->powerLimit)
|
||||
maxPower = myRegion->powerLimit;
|
||||
|
||||
if (power > maxPower) {
|
||||
DEBUG_MSG("Lowering transmit power because of regulatory limits\n");
|
||||
power = maxPower;
|
||||
|
|
|
@ -59,3 +59,4 @@ PB_BIND(ManufacturingData, ManufacturingData, AUTO)
|
|||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -25,6 +25,18 @@ typedef enum _Constants {
|
|||
Constants_Unused = 0
|
||||
} Constants;
|
||||
|
||||
typedef enum _RegionCode {
|
||||
RegionCode_Unset = 0,
|
||||
RegionCode_US = 1,
|
||||
RegionCode_EU433 = 2,
|
||||
RegionCode_EU865 = 3,
|
||||
RegionCode_CN = 4,
|
||||
RegionCode_JP = 5,
|
||||
RegionCode_ANZ = 6,
|
||||
RegionCode_KR = 7,
|
||||
RegionCode_TW = 8
|
||||
} RegionCode;
|
||||
|
||||
typedef enum _Data_Type {
|
||||
Data_Type_OPAQUE = 0,
|
||||
Data_Type_CLEAR_TEXT = 1,
|
||||
|
@ -108,6 +120,8 @@ typedef struct _RadioConfig_UserPreferences {
|
|||
char wifi_ssid[33];
|
||||
char wifi_password[64];
|
||||
bool wifi_ap_mode;
|
||||
RegionCode region;
|
||||
bool factory_reset;
|
||||
pb_size_t ignore_incoming_count;
|
||||
uint32_t ignore_incoming[3];
|
||||
} RadioConfig_UserPreferences;
|
||||
|
@ -230,6 +244,10 @@ typedef struct _ToRadio {
|
|||
#define _Constants_MAX Constants_Unused
|
||||
#define _Constants_ARRAYSIZE ((Constants)(Constants_Unused+1))
|
||||
|
||||
#define _RegionCode_MIN RegionCode_Unset
|
||||
#define _RegionCode_MAX RegionCode_TW
|
||||
#define _RegionCode_ARRAYSIZE ((RegionCode)(RegionCode_TW+1))
|
||||
|
||||
#define _Data_Type_MIN Data_Type_OPAQUE
|
||||
#define _Data_Type_MAX Data_Type_CLEAR_READACK
|
||||
#define _Data_Type_ARRAYSIZE ((Data_Type)(Data_Type_CLEAR_READACK+1))
|
||||
|
@ -248,7 +266,7 @@ typedef struct _ToRadio {
|
|||
#define MeshPacket_init_default {0, 0, 0, {SubPacket_init_default}, 0, 0, 0, 0, 0}
|
||||
#define ChannelSettings_init_default {0, _ChannelSettings_ModemConfig_MIN, {0, {0}}, "", 0, 0, 0, 0}
|
||||
#define RadioConfig_init_default {false, RadioConfig_UserPreferences_init_default, false, ChannelSettings_init_default}
|
||||
#define RadioConfig_UserPreferences_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, 0, {0, 0, 0}}
|
||||
#define RadioConfig_UserPreferences_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, _RegionCode_MIN, 0, 0, {0, 0, 0}}
|
||||
#define NodeInfo_init_default {0, false, User_init_default, false, Position_init_default, 0, 0}
|
||||
#define MyNodeInfo_init_default {0, 0, 0, "", "", "", 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
#define DeviceState_init_default {false, RadioConfig_init_default, false, MyNodeInfo_init_default, false, User_init_default, 0, {NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default}, 0, {MeshPacket_init_default}, false, MeshPacket_init_default, 0, 0, 0}
|
||||
|
@ -264,7 +282,7 @@ typedef struct _ToRadio {
|
|||
#define MeshPacket_init_zero {0, 0, 0, {SubPacket_init_zero}, 0, 0, 0, 0, 0}
|
||||
#define ChannelSettings_init_zero {0, _ChannelSettings_ModemConfig_MIN, {0, {0}}, "", 0, 0, 0, 0}
|
||||
#define RadioConfig_init_zero {false, RadioConfig_UserPreferences_init_zero, false, ChannelSettings_init_zero}
|
||||
#define RadioConfig_UserPreferences_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, 0, {0, 0, 0}}
|
||||
#define RadioConfig_UserPreferences_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, _RegionCode_MIN, 0, 0, {0, 0, 0}}
|
||||
#define NodeInfo_init_zero {0, false, User_init_zero, false, Position_init_zero, 0, 0}
|
||||
#define MyNodeInfo_init_zero {0, 0, 0, "", "", "", 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
#define DeviceState_init_zero {false, RadioConfig_init_zero, false, MyNodeInfo_init_zero, false, User_init_zero, 0, {NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero}, 0, {MeshPacket_init_zero}, false, MeshPacket_init_zero, 0, 0, 0}
|
||||
|
@ -322,7 +340,9 @@ typedef struct _ToRadio {
|
|||
#define RadioConfig_UserPreferences_wifi_ssid_tag 12
|
||||
#define RadioConfig_UserPreferences_wifi_password_tag 13
|
||||
#define RadioConfig_UserPreferences_wifi_ap_mode_tag 14
|
||||
#define RadioConfig_UserPreferences_ignore_incoming_tag 102
|
||||
#define RadioConfig_UserPreferences_region_tag 15
|
||||
#define RadioConfig_UserPreferences_factory_reset_tag 100
|
||||
#define RadioConfig_UserPreferences_ignore_incoming_tag 103
|
||||
#define RouteDiscovery_route_tag 2
|
||||
#define User_id_tag 1
|
||||
#define User_long_name_tag 2
|
||||
|
@ -477,7 +497,9 @@ X(a, STATIC, SINGULAR, UINT32, min_wake_secs, 11) \
|
|||
X(a, STATIC, SINGULAR, STRING, wifi_ssid, 12) \
|
||||
X(a, STATIC, SINGULAR, STRING, wifi_password, 13) \
|
||||
X(a, STATIC, SINGULAR, BOOL, wifi_ap_mode, 14) \
|
||||
X(a, STATIC, REPEATED, UINT32, ignore_incoming, 102)
|
||||
X(a, STATIC, SINGULAR, UENUM, region, 15) \
|
||||
X(a, STATIC, SINGULAR, BOOL, factory_reset, 100) \
|
||||
X(a, STATIC, REPEATED, UINT32, ignore_incoming, 103)
|
||||
#define RadioConfig_UserPreferences_CALLBACK NULL
|
||||
#define RadioConfig_UserPreferences_DEFAULT NULL
|
||||
|
||||
|
@ -613,11 +635,11 @@ extern const pb_msgdesc_t ManufacturingData_msg;
|
|||
#define SubPacket_size 274
|
||||
#define MeshPacket_size 313
|
||||
#define ChannelSettings_size 84
|
||||
#define RadioConfig_size 277
|
||||
#define RadioConfig_UserPreferences_size 188
|
||||
#define RadioConfig_size 282
|
||||
#define RadioConfig_UserPreferences_size 193
|
||||
#define NodeInfo_size 132
|
||||
#define MyNodeInfo_size 110
|
||||
#define DeviceState_size 5429
|
||||
#define DeviceState_size 5434
|
||||
#define DebugString_size 258
|
||||
#define FromRadio_size 322
|
||||
#define ToRadio_size 316
|
||||
|
|
|
@ -0,0 +1,793 @@
|
|||
#include "meshwifi/meshhttp.h"
|
||||
#include "NodeDB.h"
|
||||
#include "configuration.h"
|
||||
#include "main.h"
|
||||
#include "meshwifi/meshwifi.h"
|
||||
#include <WebServer.h>
|
||||
#include <WiFi.h>
|
||||
|
||||
WebServer webserver(80);
|
||||
|
||||
// Maximum number of messages for chat history. Don't make this too big -- it'll use a
|
||||
// lot of memory!
|
||||
const uint16_t maxMessages = 50;
|
||||
|
||||
struct message_t {
|
||||
char sender[10];
|
||||
char message[250];
|
||||
int32_t gpsLat;
|
||||
int32_t gpsLong;
|
||||
uint32_t time;
|
||||
bool fromMe;
|
||||
};
|
||||
|
||||
struct messages_t {
|
||||
message_t history[maxMessages];
|
||||
};
|
||||
|
||||
messages_t messages_history;
|
||||
|
||||
String something = "";
|
||||
String sender = "";
|
||||
|
||||
void handleWebResponse()
|
||||
{
|
||||
if (isWifiAvailable() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We're going to handle the DNS responder here so it
|
||||
// will be ignored by the NRF boards.
|
||||
handleDNSResponse();
|
||||
|
||||
webserver.handleClient();
|
||||
}
|
||||
|
||||
void initWebServer()
|
||||
{
|
||||
webserver.onNotFound(handleNotFound);
|
||||
webserver.on("/json/chat/send/channel", handleJSONChatHistory);
|
||||
webserver.on("/json/chat/send/user", handleJSONChatHistory);
|
||||
webserver.on("/json/chat/history/channel", handleJSONChatHistory);
|
||||
webserver.on("/json/chat/history/dummy", handleJSONChatHistoryDummy);
|
||||
webserver.on("/json/chat/history/user", handleJSONChatHistory);
|
||||
webserver.on("/json/stats", handleJSONChatHistory);
|
||||
webserver.on("/hotspot-detect.html", handleHotspot);
|
||||
webserver.on("/css/style.css", handleStyleCSS);
|
||||
webserver.on("/scripts/script.js", handleScriptsScriptJS);
|
||||
webserver.on("/", handleRoot);
|
||||
webserver.begin();
|
||||
}
|
||||
|
||||
void handleJSONChatHistory()
|
||||
{
|
||||
int i;
|
||||
|
||||
String out = "";
|
||||
out += "{\n";
|
||||
out += " \"data\" : {\n";
|
||||
out += " \"chat\" : ";
|
||||
out += "[";
|
||||
out += "\"" + sender + "\"";
|
||||
out += ",";
|
||||
out += "\"" + something + "\"";
|
||||
out += "]\n";
|
||||
|
||||
for (i = 0; i < maxMessages; i++) {
|
||||
out += "[";
|
||||
out += "\"" + String(messages_history.history[i].sender) + "\"";
|
||||
out += ",";
|
||||
out += "\"" + String(messages_history.history[i].message) + "\"";
|
||||
out += "]\n";
|
||||
}
|
||||
|
||||
out += "\n";
|
||||
out += " }\n";
|
||||
out += "}\n";
|
||||
|
||||
webserver.send(200, "application/json", out);
|
||||
return;
|
||||
}
|
||||
|
||||
void handleNotFound()
|
||||
{
|
||||
String message = "";
|
||||
message += "File Not Found\n\n";
|
||||
message += "URI: ";
|
||||
message += webserver.uri();
|
||||
message += "\nMethod: ";
|
||||
message += (webserver.method() == HTTP_GET) ? "GET" : "POST";
|
||||
message += "\nArguments: ";
|
||||
message += webserver.args();
|
||||
message += "\n";
|
||||
|
||||
for (uint8_t i = 0; i < webserver.args(); i++) {
|
||||
message += " " + webserver.argName(i) + ": " + webserver.arg(i) + "\n";
|
||||
}
|
||||
Serial.println(message);
|
||||
webserver.send(404, "text/plain", message);
|
||||
}
|
||||
|
||||
/*
|
||||
This supports the Apple Captive Network Assistant (CNA) Portal
|
||||
*/
|
||||
void handleHotspot()
|
||||
{
|
||||
DEBUG_MSG("Hotspot Request\n");
|
||||
|
||||
/*
|
||||
If we don't do a redirect, be sure to return a "Success" message
|
||||
otherwise iOS will have trouble detecting that the connection to the SoftAP worked.
|
||||
*/
|
||||
|
||||
String out = "";
|
||||
// out += "Success\n";
|
||||
out += "<meta http-equiv=\"refresh\" content=\"0;url=http://meshtastic.org/\" />\n";
|
||||
webserver.send(200, "text/html", out);
|
||||
return;
|
||||
}
|
||||
|
||||
void notifyWebUI()
|
||||
{
|
||||
DEBUG_MSG("************ Got a message! ************\n");
|
||||
MeshPacket &mp = devicestate.rx_text_message;
|
||||
NodeInfo *node = nodeDB.getNode(mp.from);
|
||||
sender = (node && node->has_user) ? node->user.long_name : "???";
|
||||
|
||||
static char tempBuf[256]; // mesh.options says this is MeshPacket.encrypted max_size
|
||||
assert(mp.decoded.which_payload == SubPacket_data_tag);
|
||||
snprintf(tempBuf, sizeof(tempBuf), "%s", mp.decoded.data.payload.bytes);
|
||||
|
||||
something = tempBuf;
|
||||
}
|
||||
|
||||
/*
|
||||
To convert text to c strings:
|
||||
|
||||
https://tomeko.net/online_tools/cpp_text_escape.php?lang=en
|
||||
*/
|
||||
void handleRoot()
|
||||
{
|
||||
|
||||
String out = "";
|
||||
out +=
|
||||
"<!DOCTYPE html>\n"
|
||||
"<html lang=\"en\" >\n"
|
||||
"<!-- Updated 20200923 - Change JSON input -->\n"
|
||||
"<!-- Updated 20200924 - Replace FontAwesome with SVG -->\n"
|
||||
"<head>\n"
|
||||
" <meta charset=\"UTF-8\">\n"
|
||||
" <title>Meshtastic - Chat</title>\n"
|
||||
" <link rel=\"stylesheet\" href=\"css/style.css\">\n"
|
||||
"\n"
|
||||
"</head>\n"
|
||||
"<body>\n"
|
||||
"<center><h1>This area is under development. Please don't file bugs.</h1></center><!-- Add SVG for Symbols -->\n"
|
||||
"<svg aria-hidden=\"true\" style=\"position: absolute; width: 0; height: 0; overflow: hidden;\" version=\"1.1\" "
|
||||
"xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n"
|
||||
"<defs>\n"
|
||||
"<symbol id=\"icon-map-marker\" viewBox=\"0 0 16 28\">\n"
|
||||
"<path d=\"M12 10c0-2.203-1.797-4-4-4s-4 1.797-4 4 1.797 4 4 4 4-1.797 4-4zM16 10c0 0.953-0.109 1.937-0.516 2.797l-5.688 "
|
||||
"12.094c-0.328 0.688-1.047 1.109-1.797 1.109s-1.469-0.422-1.781-1.109l-5.703-12.094c-0.406-0.859-0.516-1.844-0.516-2.797 "
|
||||
"0-4.422 3.578-8 8-8s8 3.578 8 8z\"></path>\n"
|
||||
"</symbol>\n"
|
||||
"<symbol id=\"icon-circle\" viewBox=\"0 0 24 28\">\n"
|
||||
"<path d=\"M24 14c0 6.625-5.375 12-12 12s-12-5.375-12-12 5.375-12 12-12 12 5.375 12 12z\"></path>\n"
|
||||
"</symbol>\n"
|
||||
"</defs>\n"
|
||||
"</svg>\n"
|
||||
"<div class=\"grid\">\n"
|
||||
"\t<div class=\"top\">\n"
|
||||
"\t\t<div class=\"top-text\">Meshtastic - Chat</div>\n"
|
||||
"\t</div>\n"
|
||||
"\n"
|
||||
"\t<div class=\"side clearfix\">\n"
|
||||
" <div class=\"channel-list\" id=\"channel-list\">\n"
|
||||
"\t <div class=\"side-header\">\n"
|
||||
"\t\t<div class=\"side-text\">Users</div>\n"
|
||||
"\t </div>\n"
|
||||
" <ul class=\"list\" id='userlist-id'>\n"
|
||||
" </ul>\n"
|
||||
" </div>\n"
|
||||
" </div>\n"
|
||||
" <div class=\"content\">\n"
|
||||
" <div class=\"content-header clearfix\">\n"
|
||||
"<!-- <div class=\"content-about\"> -->\n"
|
||||
" <div class=\"content-from\">\n"
|
||||
"\t\t <span class=\"content-from-highlight\" id=\"content-from-id\">All Users</span>\n"
|
||||
"\t\t </div>\n"
|
||||
"<!-- </div> -->\n"
|
||||
" </div> <!-- end content-header -->\n"
|
||||
" \n"
|
||||
" <div class=\"content-history\" id='chat-div-id'>\n"
|
||||
" <ul id='chat-history-id'>\n"
|
||||
"\t\t</ul>\n"
|
||||
" \n"
|
||||
" </div> <!-- end content-history -->\n"
|
||||
" \n"
|
||||
" <div class=\"content-message clearfix\">\n"
|
||||
" <textarea name=\"message-to-send\" id=\"message-to-send\" placeholder =\"Type your message\" "
|
||||
"rows=\"3\"></textarea>\n"
|
||||
" \n"
|
||||
" \n"
|
||||
" <button>Send</button>\n"
|
||||
"\n"
|
||||
" </div> <!-- end content-message -->\n"
|
||||
" \n"
|
||||
" </div> <!-- end content -->\n"
|
||||
" \n"
|
||||
" </div> <!-- end container -->\n"
|
||||
"\n"
|
||||
"<script src=\"/scripts/script.js\"></script>\n"
|
||||
"\n"
|
||||
"</body>\n"
|
||||
"</html>\n"
|
||||
"";
|
||||
webserver.send(200, "text/html", out);
|
||||
return;
|
||||
}
|
||||
|
||||
void handleStyleCSS()
|
||||
{
|
||||
|
||||
String out = "";
|
||||
out +=
|
||||
"/* latin-ext */\n"
|
||||
"@font-face {\n"
|
||||
" font-family: 'Lato';\n"
|
||||
" font-style: normal;\n"
|
||||
" font-weight: 400;\n"
|
||||
" src: local('Lato Regular'), local('Lato-Regular'), url(./Google.woff2) format('woff2');\n"
|
||||
" unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"\n"
|
||||
"*, *:before, *:after {\n"
|
||||
" box-sizing: border-box;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"body {\n"
|
||||
" background: #C5DDEB;\n"
|
||||
" font: 14px/20px \"Lato\", Arial, sans-serif;\n"
|
||||
" padding: 40px 0;\n"
|
||||
" color: white;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"\n"
|
||||
" \n"
|
||||
".grid {\n"
|
||||
" display: grid;\n"
|
||||
" grid-template-columns:\n"
|
||||
"\t1fr 4fr;\n"
|
||||
" grid-template-areas:\n"
|
||||
"\t\"header header\"\n"
|
||||
"\t\"sidebar content\";\n"
|
||||
" margin: 0 auto;\n"
|
||||
" width: 750px;\n"
|
||||
" background: #444753;\n"
|
||||
" border-radius: 5px;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
".top {grid-area: header;}\n"
|
||||
".side {grid-area: sidebar;}\n"
|
||||
".main {grid-area: content;}\n"
|
||||
"\n"
|
||||
".top {\n"
|
||||
" border-bottom: 2px solid white;\n"
|
||||
"}\n"
|
||||
".top-text {\n"
|
||||
" font-weight: bold;\n"
|
||||
" font-size: 24px;\n"
|
||||
" text-align: center;\n"
|
||||
" padding: 20px;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
".side {\n"
|
||||
" width: 260px;\n"
|
||||
" float: left;\n"
|
||||
"}\n"
|
||||
".side .side-header {\n"
|
||||
" padding: 20px;\n"
|
||||
" border-bottom: 2px solid white;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
".side .side-header .side-text {\n"
|
||||
" padding-left: 10px;\n"
|
||||
" margin-top: 6px;\n"
|
||||
" font-size: 16px;\n"
|
||||
" text-align: left;\n"
|
||||
" font-weight: bold;\n"
|
||||
" \n"
|
||||
"}\n"
|
||||
"\n"
|
||||
".channel-list ul {\n"
|
||||
" padding: 20px;\n"
|
||||
" height: 570px;\n"
|
||||
" list-style-type: none;\n"
|
||||
"}\n"
|
||||
".channel-list ul li {\n"
|
||||
" padding-bottom: 20px;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
".channel-list .channel-name {\n"
|
||||
" font-size: 20px;\n"
|
||||
" margin-top: 8px;\n"
|
||||
" padding-left: 8px;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
".channel-list .message-count {\n"
|
||||
" padding-left: 16px;\n"
|
||||
" color: #92959E;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
".icon {\n"
|
||||
" display: inline-block;\n"
|
||||
" width: 1em;\n"
|
||||
" height: 1em;\n"
|
||||
" stroke-width: 0;\n"
|
||||
" stroke: currentColor;\n"
|
||||
" fill: currentColor;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
".icon-map-marker {\n"
|
||||
" width: 0.5714285714285714em;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
".icon-circle {\n"
|
||||
" width: 0.8571428571428571em;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
".content {\n"
|
||||
" display: flex;\n"
|
||||
" flex-direction: column;\n"
|
||||
" flex-wrap: nowrap;\n"
|
||||
"/* width: 490px; */\n"
|
||||
" float: left;\n"
|
||||
" background: #F2F5F8;\n"
|
||||
"/* border-top-right-radius: 5px;\n"
|
||||
" border-bottom-right-radius: 5px; */\n"
|
||||
" color: #434651;\n"
|
||||
"}\n"
|
||||
".content .content-header {\n"
|
||||
" flex-grow: 0;\n"
|
||||
" padding: 20px;\n"
|
||||
" border-bottom: 2px solid white;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
".content .content-header .content-from {\n"
|
||||
" padding-left: 10px;\n"
|
||||
" margin-top: 6px;\n"
|
||||
" font-size: 20px;\n"
|
||||
" text-align: center;\n"
|
||||
" font-size: 16px;\n"
|
||||
"}\n"
|
||||
".content .content-header .content-from .content-from-highlight {\n"
|
||||
" font-weight: bold;\n"
|
||||
"}\n"
|
||||
".content .content-header .content-num-messages {\n"
|
||||
" color: #92959E;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
".content .content-history {\n"
|
||||
" flex-grow: 1;\n"
|
||||
" padding: 20px 20px 20px;\n"
|
||||
" border-bottom: 2px solid white;\n"
|
||||
" overflow-y: scroll;\n"
|
||||
" height: 375px;\n"
|
||||
"}\n"
|
||||
".content .content-history ul {\n"
|
||||
" list-style-type: none;\n"
|
||||
" padding-inline-start: 10px;\n"
|
||||
"}\n"
|
||||
".content .content-history .message-data {\n"
|
||||
" margin-bottom: 10px;\n"
|
||||
"}\n"
|
||||
".content .content-history .message-data-time {\n"
|
||||
" color: #a8aab1;\n"
|
||||
" padding-left: 6px;\n"
|
||||
"}\n"
|
||||
".content .content-history .message {\n"
|
||||
" color: white;\n"
|
||||
" padding: 8px 10px;\n"
|
||||
" line-height: 20px;\n"
|
||||
" font-size: 14px;\n"
|
||||
" border-radius: 7px;\n"
|
||||
" margin-bottom: 30px;\n"
|
||||
" width: 90%;\n"
|
||||
" position: relative;\n"
|
||||
"}\n"
|
||||
".content .content-history .message:after {\n"
|
||||
" bottom: 100%;\n"
|
||||
" left: 7%;\n"
|
||||
" border: solid transparent;\n"
|
||||
" content: \" \";\n"
|
||||
" height: 0;\n"
|
||||
" width: 0;\n"
|
||||
" position: absolute;\n"
|
||||
" pointer-events: none;\n"
|
||||
" border-bottom-color: #86BB71;\n"
|
||||
" border-width: 10px;\n"
|
||||
" margin-left: -10px;\n"
|
||||
"}\n"
|
||||
".content .content-history .my-message {\n"
|
||||
" background: #86BB71;\n"
|
||||
"}\n"
|
||||
".content .content-history .other-message {\n"
|
||||
" background: #94C2ED;\n"
|
||||
"}\n"
|
||||
".content .content-history .other-message:after {\n"
|
||||
" border-bottom-color: #94C2ED;\n"
|
||||
" left: 93%;\n"
|
||||
"}\n"
|
||||
".content .content-message {\n"
|
||||
" flex-grow: 0;\n"
|
||||
" padding: 10px;\n"
|
||||
"}\n"
|
||||
".content .content-message textarea {\n"
|
||||
" width: 100%;\n"
|
||||
" border: none;\n"
|
||||
" padding: 10px 10px;\n"
|
||||
" font: 14px/22px \"Lato\", Arial, sans-serif;\n"
|
||||
" margin-bottom: 10px;\n"
|
||||
" border-radius: 5px;\n"
|
||||
" resize: none;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
".content .content-message button {\n"
|
||||
" float: right;\n"
|
||||
" color: #94C2ED;\n"
|
||||
" font-size: 16px;\n"
|
||||
" text-transform: uppercase;\n"
|
||||
" border: none;\n"
|
||||
" cursor: pointer;\n"
|
||||
" font-weight: bold;\n"
|
||||
" background: #F2F5F8;\n"
|
||||
"}\n"
|
||||
".content .content-message button:hover {\n"
|
||||
" color: #75b1e8;\n"
|
||||
"}\n"
|
||||
"/* Tooltip container */\n"
|
||||
".tooltip {\n"
|
||||
" color: #86BB71;\n"
|
||||
" position: relative;\n"
|
||||
" display: inline-block;\n"
|
||||
" border-bottom: 1px dotted black; /* If you want dots under the hoverable text */\n"
|
||||
"}\n"
|
||||
"/* Tooltip text */\n"
|
||||
".tooltip .tooltiptext {\n"
|
||||
" visibility: hidden;\n"
|
||||
" width: 120px;\n"
|
||||
" background-color: #444753;\n"
|
||||
" color: #fff;\n"
|
||||
" text-align: center;\n"
|
||||
" padding: 5px 0;\n"
|
||||
" border-radius: 6px;\n"
|
||||
" /* Position the tooltip text - see examples below! */\n"
|
||||
" position: absolute;\n"
|
||||
" z-index: 1;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"/* Show the tooltip text when you mouse over the tooltip container */\n"
|
||||
".tooltip:hover .tooltiptext {\n"
|
||||
" visibility: visible;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
".online, .offline, .me {\n"
|
||||
" margin-right: 3px;\n"
|
||||
" font-size: 10px;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
".online {\n"
|
||||
" color: #86BB71;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
".offline {\n"
|
||||
" color: #E38968;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
".me {\n"
|
||||
" color: #94C2ED;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
".align-left {\n"
|
||||
" text-align: left;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
".align-right {\n"
|
||||
" text-align: right;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
".float-right {\n"
|
||||
" float: right;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
".clearfix:after {\n"
|
||||
" visibility: hidden;\n"
|
||||
" display: block;\n"
|
||||
" font-size: 0;\n"
|
||||
" content: \" \";\n"
|
||||
" clear: both;\n"
|
||||
" height: 0;\n"
|
||||
"}";
|
||||
|
||||
webserver.send(200, "text/css", out);
|
||||
return;
|
||||
}
|
||||
|
||||
void handleScriptsScriptJS()
|
||||
{
|
||||
String out = "";
|
||||
out += "String.prototype.toHHMMSS = function () {\n"
|
||||
" var sec_num = parseInt(this, 10); // don't forget the second param\n"
|
||||
" var hours = Math.floor(sec_num / 3600);\n"
|
||||
" var minutes = Math.floor((sec_num - (hours * 3600)) / 60);\n"
|
||||
" var seconds = sec_num - (hours * 3600) - (minutes * 60);\n"
|
||||
"\n"
|
||||
" if (hours < 10) {hours = \"0\"+hours;}\n"
|
||||
" if (minutes < 10) {minutes = \"0\"+minutes;}\n"
|
||||
" if (seconds < 10) {seconds = \"0\"+seconds;}\n"
|
||||
"// return hours+':'+minutes+':'+seconds;\n"
|
||||
"\treturn hours+'h'+minutes+'m';\n"
|
||||
"}\n"
|
||||
"String.prototype.padLeft = function (length, character) { \n"
|
||||
" return new Array(length - this.length + 1).join(character || ' ') + this; \n"
|
||||
"};\n"
|
||||
"\n"
|
||||
"Date.prototype.toFormattedString = function () {\n"
|
||||
" return [String(this.getFullYear()).substr(2, 2),\n"
|
||||
"\t\t\tString(this.getMonth()+1).padLeft(2, '0'),\n"
|
||||
" String(this.getDate()).padLeft(2, '0')].join(\"/\") + \" \" +\n"
|
||||
" [String(this.getHours()).padLeft(2, '0'),\n"
|
||||
" String(this.getMinutes()).padLeft(2, '0')].join(\":\");\n"
|
||||
"};\n"
|
||||
"\n"
|
||||
"function getData(file) {\n"
|
||||
"\tfetch(file)\n"
|
||||
"\t.then(function (response) {\n"
|
||||
"\t\treturn response.json();\n"
|
||||
"\t})\n"
|
||||
"\t.then(function (datafile) {\n"
|
||||
"\t\tupdateData(datafile);\n"
|
||||
"\t})\n"
|
||||
"\t.catch(function (err) {\n"
|
||||
"\t\tconsole.log('error: ' + err);\n"
|
||||
"\t});\n"
|
||||
"}\n"
|
||||
"\t\n"
|
||||
"function updateData(datafile) {\n"
|
||||
"// Update System Details\n"
|
||||
"\tupdateSystem(datafile);\n"
|
||||
"//\tUpdate Userlist and message count\n"
|
||||
"\tupdateUsers(datafile);\n"
|
||||
"// Update Chat\n"
|
||||
"\tupdateChat(datafile);\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"function updateSystem(datafile) {\n"
|
||||
"// Update System Info \n"
|
||||
"\tvar sysContainer = document.getElementById(\"content-from-id\");\n"
|
||||
"\tvar newHTML = datafile.data.system.channel;\n"
|
||||
"\tvar myDate = new Date( datafile.data.system.timeGPS *1000);\n"
|
||||
"\tnewHTML += ' @' + myDate.toFormattedString();\n"
|
||||
"\tvar newSec = datafile.data.system.timeSinceStart;\n"
|
||||
"\tvar strsecondUp = newSec.toString();\n"
|
||||
"\tnewHTML += ' Up:' + strsecondUp.toHHMMSS();\n"
|
||||
"\tsysContainer.innerHTML = newHTML;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"function updateUsers(datafile) {\n"
|
||||
"\tvar mainContainer = document.getElementById(\"userlist-id\");\n"
|
||||
"\tvar htmlUsers = '';\n"
|
||||
"\tvar timeBase = datafile.data.system.timeSinceStart;\n"
|
||||
"//\tvar lookup = {};\n"
|
||||
" for (var i = 0; i < datafile.data.users.length; i++) {\n"
|
||||
" htmlUsers += formatUsers(datafile.data.users[i],timeBase);\n"
|
||||
"\t}\n"
|
||||
"\tmainContainer.innerHTML = htmlUsers;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"function formatUsers(user,timeBase) {\n"
|
||||
"\tnewHTML = '<li class=\"clearfix\">';\n"
|
||||
" newHTML += '<div class=\"channel-name clearfix\">' + user.NameLong + '(' + user.NameShort + ')</div>';\n"
|
||||
" newHTML += '<div class=\"message-count clearfix\">';\n"
|
||||
"\tvar secondsLS = timeBase - user.lastSeen;\n"
|
||||
"\tvar strsecondsLS = secondsLS.toString();\n"
|
||||
"\tnewHTML += '<svg class=\"icon icon-circle '+onlineStatus(secondsLS)+'\"><use "
|
||||
"xlink:href=\"#icon-circle\"></use></svg></i>Seen: '+strsecondsLS.toHHMMSS()+' ago ';\n"
|
||||
"\tif (user.lat == 0 || user.lon == 0) {\n"
|
||||
"\t\tnewHTML += '';\n"
|
||||
"\t} else {\n"
|
||||
"\t\tnewHTML += '<div class=\"tooltip\"><svg class=\"icon icon-map-marker\"><use "
|
||||
"xlink:href=\"#icon-map-marker\"></use></svg><span class=\"tooltiptext\">lat:' + user.lat + ' lon:'+ user.lon+ "
|
||||
"'</span>';\n"
|
||||
"\t}\n"
|
||||
" newHTML += '</div></div>';\n"
|
||||
" newHTML += '</li>';\n"
|
||||
"\treturn(newHTML);\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"function onlineStatus(time) {\n"
|
||||
"\tif (time < 3600) {\n"
|
||||
"\t\treturn \"online\"\n"
|
||||
"\t} else {\n"
|
||||
"\t\treturn \"offline\"\n"
|
||||
"\t}\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"function updateChat(datafile) {\n"
|
||||
"// Update Chat\n"
|
||||
"\tvar chatContainer = document.getElementById(\"chat-history-id\");\n"
|
||||
"\tvar htmlChat = '';\n"
|
||||
"\tvar timeBase = datafile.data.system.timeSinceStart;\n"
|
||||
"\tfor (var i = 0; i < datafile.data.chat.length; i++) {\n"
|
||||
"\t\thtmlChat += formatChat(datafile.data.chat[i],timeBase);\n"
|
||||
"\t}\n"
|
||||
"\tchatContainer.innerHTML = htmlChat;\n"
|
||||
"\tscrollHistory();\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"function formatChat(data,timeBase) {\n"
|
||||
"\tvar secondsTS = timeBase - data.timestamp;\n"
|
||||
"\tvar strsecondsTS = secondsTS.toString();\n"
|
||||
"\tnewHTML = '<li class=\"clearfix\">';\n"
|
||||
"\tif (data.local == 1) {\n"
|
||||
"\t\tnewHTML += '<div class=\"message-data\">';\n"
|
||||
"\t\tnewHTML += '<span class=\"message-data-name\" >' + data.NameLong + '(' + data.NameShort + ')</span>';\n"
|
||||
"\t\tnewHTML += '<span class=\"message-data-time\" >' + strsecondsTS.toHHMMSS() + ' ago</span>';\n"
|
||||
"\t\tnewHTML += '</div>';\n"
|
||||
"\t\tnewHTML += '<div class=\"message my-message\">' + data.chatLine + '</div>';\n"
|
||||
"\t} else {\n"
|
||||
"\t\tnewHTML += '<div class=\"message-data align-right\">';\n"
|
||||
"\t\tnewHTML += '<span class=\"message-data-time\" >' + strsecondsTS.toHHMMSS() + ' ago</span> ';\n"
|
||||
"\t\tnewHTML += '<span class=\"message-data-name\" >' + data.NameLong + '(' + data.NameShort + ')</span>';\n"
|
||||
"//\t\tnewHTML += '<i class=\"fa fa-circle online\"></i>';\n"
|
||||
"\t\tnewHTML += '</div>';\n"
|
||||
"\t\tnewHTML += '<div class=\"message other-message float-right\">' + data.chatLine + '</div>';\n"
|
||||
"\t}\n"
|
||||
"\n"
|
||||
" newHTML += '</li>';\n"
|
||||
"\treturn(newHTML);\t\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"function scrollHistory() {\n"
|
||||
"\tvar chatContainer = document.getElementById(\"chat-div-id\");\n"
|
||||
"\tchatContainer.scrollTop = chatContainer.scrollHeight;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"\n"
|
||||
"getData('/json/chat/history/dummy');\n"
|
||||
"\n"
|
||||
"\n"
|
||||
"//window.onload=function(){\n"
|
||||
"//\talert('onload');\n"
|
||||
"// Async - Run scroll 0.5sec after onload event\n"
|
||||
"//\tsetTimeout(scrollHistory(),500);\n"
|
||||
"// }";
|
||||
|
||||
webserver.send(200, "text/javascript", out);
|
||||
return;
|
||||
}
|
||||
|
||||
void handleJSONChatHistoryDummy()
|
||||
{
|
||||
String out = "";
|
||||
out += "{\n"
|
||||
"\t\"data\": {\n"
|
||||
"\t\t\"system\": {\n"
|
||||
"\t\t\t\"timeSinceStart\": 3213544,\n"
|
||||
"\t\t\t\"timeGPS\": 1600830985,\n"
|
||||
"\t\t\t\"channel\": \"ourSecretPlace\"\n"
|
||||
"\t\t},\n"
|
||||
"\t\t\"users\": [{\n"
|
||||
"\t\t\t\t\"NameShort\": \"J\",\n"
|
||||
"\t\t\t\t\"NameLong\": \"John\",\n"
|
||||
"\t\t\t\t\"lastSeen\": 3207544,\n"
|
||||
"\t\t\t\t\"lat\" : -2.882243,\n"
|
||||
"\t\t\t\t\"lon\" : -111.038580\n"
|
||||
"\t\t\t},\n"
|
||||
"\t\t\t{\n"
|
||||
"\t\t\t\t\"NameShort\": \"D\",\n"
|
||||
"\t\t\t\t\"NameLong\": \"David\",\n"
|
||||
"\t\t\t\t\"lastSeen\": 3212544,\n"
|
||||
"\t\t\t\t\"lat\" : -12.24452,\n"
|
||||
"\t\t\t\t\"lon\" : -61.87351\n"
|
||||
"\t\t\t},\n"
|
||||
"\t\t\t{\n"
|
||||
"\t\t\t\t\"NameShort\": \"P\",\n"
|
||||
"\t\t\t\t\"NameLong\": \"Peter\",\n"
|
||||
"\t\t\t\t\"lastSeen\": 3213444,\n"
|
||||
"\t\t\t\t\"lat\" : 0,\n"
|
||||
"\t\t\t\t\"lon\" : 0\n"
|
||||
"\t\t\t},\n"
|
||||
"\t\t\t{\n"
|
||||
"\t\t\t\t\"NameShort\": \"M\",\n"
|
||||
"\t\t\t\t\"NameLong\": \"Mary\",\n"
|
||||
"\t\t\t\t\"lastSeen\": 3211544,\n"
|
||||
"\t\t\t\t\"lat\" : 16.45478,\n"
|
||||
"\t\t\t\t\"lon\" : 11.40166\n"
|
||||
"\t\t\t}\n"
|
||||
"\t\t],\n"
|
||||
"\t\t\"chat\": [{\n"
|
||||
"\t\t\t\t\"local\": 0,\n"
|
||||
"\t\t\t\t\"NameShort\": \"J\",\n"
|
||||
"\t\t\t\t\"NameLong\": \"John\",\n"
|
||||
"\t\t\t\t\"chatLine\": \"Hello\",\n"
|
||||
"\t\t\t\t\"timestamp\" : 3203544\n"
|
||||
"\t\t\t},\n"
|
||||
"\t\t\t{\n"
|
||||
"\t\t\t\t\"local\": 0,\n"
|
||||
"\t\t\t\t\"NameShort\": \"D\",\n"
|
||||
"\t\t\t\t\"NameLong\": \"David\",\n"
|
||||
"\t\t\t\t\"chatLine\": \"Hello There\",\n"
|
||||
"\t\t\t\t\"timestamp\" : 3204544\n"
|
||||
"\t\t\t},\n"
|
||||
"\t\t\t{\n"
|
||||
"\t\t\t\t\"local\": 0,\n"
|
||||
"\t\t\t\t\"NameShort\": \"J\",\n"
|
||||
"\t\t\t\t\"NameLong\": \"John\",\n"
|
||||
"\t\t\t\t\"chatLine\": \"Where you been?\",\n"
|
||||
"\t\t\t\t\"timestamp\" : 3205544\n"
|
||||
"\t\t\t},\n"
|
||||
"\t\t\t{\n"
|
||||
"\t\t\t\t\"local\": 0,\n"
|
||||
"\t\t\t\t\"NameShort\": \"D\",\n"
|
||||
"\t\t\t\t\"NameLong\": \"David\",\n"
|
||||
"\t\t\t\t\"chatLine\": \"I was on Channel 2\",\n"
|
||||
"\t\t\t\t\"timestamp\" : 3206544\n"
|
||||
"\t\t\t},\n"
|
||||
"\t\t\t{\n"
|
||||
"\t\t\t\t\"local\": 0,\n"
|
||||
"\t\t\t\t\"NameShort\": \"J\",\n"
|
||||
"\t\t\t\t\"NameLong\": \"John\",\n"
|
||||
"\t\t\t\t\"chatLine\": \"With Mary again?\",\n"
|
||||
"\t\t\t\t\"timestamp\" : 3207544\n"
|
||||
"\t\t\t},\n"
|
||||
"\t\t\t{\n"
|
||||
"\t\t\t\t\"local\": 0,\n"
|
||||
"\t\t\t\t\"NameShort\": \"D\",\n"
|
||||
"\t\t\t\t\"NameLong\": \"David\",\n"
|
||||
"\t\t\t\t\"chatLine\": \"She's better looking than you\",\n"
|
||||
"\t\t\t\t\"timestamp\" : 3208544\n"
|
||||
"\t\t\t},\n"
|
||||
"\t\t\t{\n"
|
||||
"\t\t\t\t\"local\": 0,\n"
|
||||
"\t\t\t\t\"NameShort\": \"M\",\n"
|
||||
"\t\t\t\t\"NameLong\": \"Mary\",\n"
|
||||
"\t\t\t\t\"chatLine\": \"Well, Hi\",\n"
|
||||
"\t\t\t\t\"timestamp\" : 3209544\n"
|
||||
"\t\t\t},\n"
|
||||
"\t\t\t{\n"
|
||||
"\t\t\t\t\"local\": 0,\n"
|
||||
"\t\t\t\t\"NameShort\": \"D\",\n"
|
||||
"\t\t\t\t\"NameLong\": \"David\",\n"
|
||||
"\t\t\t\t\"chatLine\": \"You're Here\",\n"
|
||||
"\t\t\t\t\"timestamp\" : 3210544\n"
|
||||
"\t\t\t},\n"
|
||||
"\t\t\t{\n"
|
||||
"\t\t\t\t\"local\": 0,\n"
|
||||
"\t\t\t\t\"NameShort\": \"M\",\n"
|
||||
"\t\t\t\t\"NameLong\": \"Mary\",\n"
|
||||
"\t\t\t\t\"chatLine\": \"Wanted to say Howdy.\",\n"
|
||||
"\t\t\t\t\"timestamp\" : 3211544\n"
|
||||
"\t\t\t},\n"
|
||||
"\t\t\t{\n"
|
||||
"\t\t\t\t\"local\": 0,\n"
|
||||
"\t\t\t\t\"NameShort\": \"D\",\n"
|
||||
"\t\t\t\t\"NameLong\": \"David\",\n"
|
||||
"\t\t\t\t\"chatLine\": \"Better come down and visit sometime\",\n"
|
||||
"\t\t\t\t\"timestamp\" : 3212544\n"
|
||||
"\t\t\t},\n"
|
||||
"\t\t\t{\n"
|
||||
"\t\t\t\t\"local\": 1,\n"
|
||||
"\t\t\t\t\"NameShort\": \"P\",\n"
|
||||
"\t\t\t\t\"NameLong\": \"Peter\",\n"
|
||||
"\t\t\t\t\"chatLine\": \"Where is everybody?\",\n"
|
||||
"\t\t\t\t\"timestamp\" : 3213444\n"
|
||||
"\t\t\t}\n"
|
||||
"\t\t]\n"
|
||||
"\t}\n"
|
||||
"}";
|
||||
|
||||
webserver.send(200, "application/json", out);
|
||||
return;
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <functional>
|
||||
|
||||
void initWebServer();
|
||||
|
||||
void handleNotFound();
|
||||
|
||||
void handleWebResponse();
|
||||
|
||||
void handleJSONChatHistory();
|
||||
|
||||
void notifyWebUI();
|
||||
|
||||
void handleHotspot();
|
||||
|
||||
|
||||
void handleStyleCSS();
|
||||
void handleRoot();
|
||||
void handleScriptsScriptJS();
|
||||
void handleJSONChatHistoryDummy();
|
|
@ -0,0 +1,255 @@
|
|||
#include "meshwifi.h"
|
||||
#include "NodeDB.h"
|
||||
#include "WiFiServerAPI.h"
|
||||
#include "configuration.h"
|
||||
#include "main.h"
|
||||
#include "meshwifi/meshhttp.h"
|
||||
#include <DNSServer.h>
|
||||
#include <WiFi.h>
|
||||
|
||||
static void WiFiEvent(WiFiEvent_t event);
|
||||
|
||||
// DNS Server for the Captive Portal
|
||||
DNSServer dnsServer;
|
||||
|
||||
static WiFiServerPort *apiPort;
|
||||
|
||||
uint8_t wifiDisconnectReason = 0;
|
||||
|
||||
bool isWifiAvailable()
|
||||
{
|
||||
const char *wifiName = radioConfig.preferences.wifi_ssid;
|
||||
const char *wifiPsw = radioConfig.preferences.wifi_password;
|
||||
|
||||
if (*wifiName && *wifiPsw) {
|
||||
|
||||
// Once every 10 seconds, try to reconnect.
|
||||
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Disable WiFi
|
||||
void deinitWifi()
|
||||
{
|
||||
/*
|
||||
Note from Jm (jm@casler.org - Sept 16, 2020):
|
||||
|
||||
A bug in the ESP32 SDK was introduced in Oct 2019 that keeps the WiFi radio from
|
||||
turning back on after it's shut off. See:
|
||||
https://github.com/espressif/arduino-esp32/issues/3522
|
||||
|
||||
Until then, WiFi should only be allowed when there's no power
|
||||
saving on the 2.4g transceiver.
|
||||
*/
|
||||
|
||||
WiFi.mode(WIFI_MODE_NULL);
|
||||
DEBUG_MSG("WiFi Turned Off\n");
|
||||
// WiFi.printDiag(Serial);
|
||||
}
|
||||
|
||||
// Startup WiFi
|
||||
void initWifi()
|
||||
{
|
||||
if (isWifiAvailable() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (radioConfig.has_preferences) {
|
||||
const char *wifiName = radioConfig.preferences.wifi_ssid;
|
||||
const char *wifiPsw = radioConfig.preferences.wifi_password;
|
||||
|
||||
if (*wifiName && *wifiPsw) {
|
||||
if (radioConfig.preferences.wifi_ap_mode) {
|
||||
|
||||
IPAddress apIP(192, 168, 42, 1);
|
||||
WiFi.onEvent(WiFiEvent);
|
||||
|
||||
WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0));
|
||||
DEBUG_MSG("STARTING WIFI AP: ssid=%s, ok=%d\n", wifiName, WiFi.softAP(wifiName, wifiPsw));
|
||||
DEBUG_MSG("MY IP ADDRESS: %s\n", WiFi.softAPIP().toString().c_str());
|
||||
|
||||
dnsServer.start(53, "*", apIP);
|
||||
|
||||
} else {
|
||||
WiFi.mode(WIFI_MODE_STA);
|
||||
WiFi.onEvent(WiFiEvent);
|
||||
// esp_wifi_set_ps(WIFI_PS_NONE); // Disable power saving
|
||||
|
||||
//WiFiEventId_t eventID = WiFi.onEvent(
|
||||
WiFi.onEvent(
|
||||
[](WiFiEvent_t event, WiFiEventInfo_t info) {
|
||||
Serial.print("\nWiFi lost connection. Reason: ");
|
||||
Serial.println(info.disconnected.reason);
|
||||
|
||||
/*
|
||||
If we are disconnected from the AP for some reason,
|
||||
save the error code.
|
||||
|
||||
For a reference to the codes:
|
||||
https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/wifi.html#wi-fi-reason-code
|
||||
*/
|
||||
wifiDisconnectReason = info.disconnected.reason;
|
||||
},
|
||||
WiFiEvent_t::SYSTEM_EVENT_STA_DISCONNECTED);
|
||||
|
||||
DEBUG_MSG("JOINING WIFI: ssid=%s\n", wifiName);
|
||||
if (WiFi.begin(wifiName, wifiPsw) == WL_CONNECTED) {
|
||||
DEBUG_MSG("MY IP ADDRESS: %s\n", WiFi.localIP().toString().c_str());
|
||||
} else {
|
||||
DEBUG_MSG("Started Joining WIFI\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
} else
|
||||
DEBUG_MSG("Not using WIFI\n");
|
||||
}
|
||||
|
||||
/// Perform idle loop processing required by the wifi layer
|
||||
void loopWifi()
|
||||
{
|
||||
// FIXME, once we have coroutines - just use a coroutine instead of this nasty loopWifi()
|
||||
if (apiPort)
|
||||
apiPort->loop();
|
||||
}
|
||||
|
||||
static void initApiServer()
|
||||
{
|
||||
// Start API server on port 4403
|
||||
if (!apiPort) {
|
||||
apiPort = new WiFiServerPort();
|
||||
apiPort->init();
|
||||
}
|
||||
}
|
||||
|
||||
// Called by the Espressif SDK to
|
||||
static void WiFiEvent(WiFiEvent_t event)
|
||||
{
|
||||
DEBUG_MSG("************ [WiFi-event] event: %d ************\n", event);
|
||||
|
||||
switch (event) {
|
||||
case SYSTEM_EVENT_WIFI_READY:
|
||||
DEBUG_MSG("WiFi interface ready\n");
|
||||
break;
|
||||
case SYSTEM_EVENT_SCAN_DONE:
|
||||
DEBUG_MSG("Completed scan for access points\n");
|
||||
break;
|
||||
case SYSTEM_EVENT_STA_START:
|
||||
DEBUG_MSG("WiFi client started\n");
|
||||
break;
|
||||
case SYSTEM_EVENT_STA_STOP:
|
||||
DEBUG_MSG("WiFi clients stopped\n");
|
||||
break;
|
||||
case SYSTEM_EVENT_STA_CONNECTED:
|
||||
DEBUG_MSG("Connected to access point\n");
|
||||
break;
|
||||
case SYSTEM_EVENT_STA_DISCONNECTED:
|
||||
DEBUG_MSG("Disconnected from WiFi access point\n");
|
||||
// Event 5
|
||||
|
||||
reconnectWiFi();
|
||||
break;
|
||||
case SYSTEM_EVENT_STA_AUTHMODE_CHANGE:
|
||||
DEBUG_MSG("Authentication mode of access point has changed\n");
|
||||
break;
|
||||
case SYSTEM_EVENT_STA_GOT_IP:
|
||||
DEBUG_MSG("Obtained IP address: \n");
|
||||
Serial.println(WiFi.localIP());
|
||||
|
||||
// Start web server
|
||||
initWebServer();
|
||||
initApiServer();
|
||||
|
||||
break;
|
||||
case SYSTEM_EVENT_STA_LOST_IP:
|
||||
DEBUG_MSG("Lost IP address and IP address is reset to 0\n");
|
||||
break;
|
||||
case SYSTEM_EVENT_STA_WPS_ER_SUCCESS:
|
||||
DEBUG_MSG("WiFi Protected Setup (WPS): succeeded in enrollee mode\n");
|
||||
break;
|
||||
case SYSTEM_EVENT_STA_WPS_ER_FAILED:
|
||||
DEBUG_MSG("WiFi Protected Setup (WPS): failed in enrollee mode\n");
|
||||
break;
|
||||
case SYSTEM_EVENT_STA_WPS_ER_TIMEOUT:
|
||||
DEBUG_MSG("WiFi Protected Setup (WPS): timeout in enrollee mode\n");
|
||||
break;
|
||||
case SYSTEM_EVENT_STA_WPS_ER_PIN:
|
||||
DEBUG_MSG("WiFi Protected Setup (WPS): pin code in enrollee mode\n");
|
||||
break;
|
||||
case SYSTEM_EVENT_AP_START:
|
||||
DEBUG_MSG("WiFi access point started\n");
|
||||
Serial.println(WiFi.softAPIP());
|
||||
|
||||
// Start web server
|
||||
initWebServer();
|
||||
initApiServer();
|
||||
|
||||
break;
|
||||
case SYSTEM_EVENT_AP_STOP:
|
||||
DEBUG_MSG("WiFi access point stopped\n");
|
||||
break;
|
||||
case SYSTEM_EVENT_AP_STACONNECTED:
|
||||
DEBUG_MSG("Client connected\n");
|
||||
break;
|
||||
case SYSTEM_EVENT_AP_STADISCONNECTED:
|
||||
DEBUG_MSG("Client disconnected\n");
|
||||
break;
|
||||
case SYSTEM_EVENT_AP_STAIPASSIGNED:
|
||||
DEBUG_MSG("Assigned IP address to client\n");
|
||||
break;
|
||||
case SYSTEM_EVENT_AP_PROBEREQRECVED:
|
||||
DEBUG_MSG("Received probe request\n");
|
||||
break;
|
||||
case SYSTEM_EVENT_GOT_IP6:
|
||||
DEBUG_MSG("IPv6 is preferred\n");
|
||||
break;
|
||||
case SYSTEM_EVENT_ETH_START:
|
||||
DEBUG_MSG("Ethernet started\n");
|
||||
break;
|
||||
case SYSTEM_EVENT_ETH_STOP:
|
||||
DEBUG_MSG("Ethernet stopped\n");
|
||||
break;
|
||||
case SYSTEM_EVENT_ETH_CONNECTED:
|
||||
DEBUG_MSG("Ethernet connected\n");
|
||||
break;
|
||||
case SYSTEM_EVENT_ETH_DISCONNECTED:
|
||||
DEBUG_MSG("Ethernet disconnected\n");
|
||||
break;
|
||||
case SYSTEM_EVENT_ETH_GOT_IP:
|
||||
DEBUG_MSG("Obtained IP address\n");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void handleDNSResponse()
|
||||
{
|
||||
if (radioConfig.preferences.wifi_ap_mode) {
|
||||
dnsServer.processNextRequest();
|
||||
}
|
||||
}
|
||||
|
||||
void reconnectWiFi()
|
||||
{
|
||||
const char *wifiName = radioConfig.preferences.wifi_ssid;
|
||||
const char *wifiPsw = radioConfig.preferences.wifi_password;
|
||||
|
||||
if (radioConfig.has_preferences) {
|
||||
|
||||
if (*wifiName && *wifiPsw) {
|
||||
|
||||
DEBUG_MSG("... Reconnecting to WiFi access point");
|
||||
|
||||
WiFi.mode(WIFI_MODE_STA);
|
||||
WiFi.begin(wifiName, wifiPsw);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t getWifiDisconnectReason()
|
||||
{
|
||||
return wifiDisconnectReason;
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
#pragma once
|
||||
|
||||
#include "configuration.h"
|
||||
#include <Arduino.h>
|
||||
#include <functional>
|
||||
|
||||
#ifdef HAS_WIFI
|
||||
#include <DNSServer.h>
|
||||
#include <WiFi.h>
|
||||
#endif
|
||||
|
||||
void initWifi();
|
||||
void deinitWifi();
|
||||
|
||||
/// Perform idle loop processing required by the wifi layer
|
||||
void loopWifi();
|
||||
|
||||
bool isWifiAvailable();
|
||||
|
||||
void handleDNSResponse();
|
||||
|
||||
void reconnectWiFi();
|
||||
|
||||
uint8_t getWifiDisconnectReason();
|
||||
|
|
@ -1,10 +1,9 @@
|
|||
#include "BluetoothUtil.h"
|
||||
#include "BluetoothSoftwareUpdate.h"
|
||||
#include "NimbleBluetoothAPI.h"
|
||||
#include "NodeDB.h" // FIXME - we shouldn't really douch this here - we are using it only because we currently do wifi setup when ble gets turned on
|
||||
#include "PhoneAPI.h"
|
||||
#include "PowerFSM.h"
|
||||
#include "WiFi.h"
|
||||
#include <WiFi.h>
|
||||
#include "configuration.h"
|
||||
#include "esp_bt.h"
|
||||
#include "host/util/util.h"
|
||||
|
@ -13,6 +12,7 @@
|
|||
#include "services/gap/ble_svc_gap.h"
|
||||
#include "services/gatt/ble_svc_gatt.h"
|
||||
#include <Arduino.h>
|
||||
#include "meshwifi/meshwifi.h"
|
||||
|
||||
static bool pinShowing;
|
||||
|
||||
|
@ -503,33 +503,8 @@ void reinitBluetooth()
|
|||
nimble_port_freertos_init(ble_host_task);
|
||||
}
|
||||
|
||||
void initWifi()
|
||||
{
|
||||
// Note: Wifi is not yet supported ;-)
|
||||
strcpy(radioConfig.preferences.wifi_ssid, "");
|
||||
strcpy(radioConfig.preferences.wifi_password, "");
|
||||
if (radioConfig.has_preferences) {
|
||||
const char *wifiName = radioConfig.preferences.wifi_ssid;
|
||||
|
||||
if (*wifiName) {
|
||||
const char *wifiPsw = radioConfig.preferences.wifi_password;
|
||||
if (radioConfig.preferences.wifi_ap_mode) {
|
||||
DEBUG_MSG("STARTING WIFI AP: ssid=%s, ok=%d\n", wifiName, WiFi.softAP(wifiName, wifiPsw));
|
||||
} else {
|
||||
WiFi.mode(WIFI_MODE_STA);
|
||||
DEBUG_MSG("JOINING WIFI: ssid=%s\n", wifiName);
|
||||
if (WiFi.begin(wifiName, wifiPsw) == WL_CONNECTED) {
|
||||
DEBUG_MSG("MY IP ADDRESS: %s\n", WiFi.localIP().toString().c_str());
|
||||
} else {
|
||||
DEBUG_MSG("Started Joining WIFI\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
} else
|
||||
DEBUG_MSG("Not using WIFI\n");
|
||||
}
|
||||
|
||||
bool bluetoothOn;
|
||||
bool firstTime = 1;
|
||||
|
||||
// Enable/disable bluetooth.
|
||||
void setBluetoothEnable(bool on)
|
||||
|
@ -542,11 +517,29 @@ void setBluetoothEnable(bool on)
|
|||
Serial.printf("Pre BT: %u heap size\n", ESP.getFreeHeap());
|
||||
// ESP_ERROR_CHECK( heap_trace_start(HEAP_TRACE_LEAKS) );
|
||||
reinitBluetooth();
|
||||
initWifi();
|
||||
|
||||
// Don't try to reconnect wifi before bluetooth is configured.
|
||||
// WiFi is initialized from main.cpp in setup() .
|
||||
if (firstTime) {
|
||||
firstTime = 0;
|
||||
} else {
|
||||
initWifi();
|
||||
}
|
||||
} else {
|
||||
|
||||
/*
|
||||
// If WiFi is in use, disable shutting down the radio.
|
||||
if (isWifiAvailable()) {
|
||||
return;
|
||||
}
|
||||
*/
|
||||
|
||||
// shutdown wifi
|
||||
deinitWifi();
|
||||
|
||||
// We have to totally teardown our bluetooth objects to prevent leaks
|
||||
deinitBLE();
|
||||
WiFi.mode(WIFI_MODE_NULL); // shutdown wifi
|
||||
|
||||
Serial.printf("Shutdown BT: %u heap size\n", ESP.getFreeHeap());
|
||||
// ESP_ERROR_CHECK( heap_trace_stop() );
|
||||
// heap_trace_dump();
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
#include "meshwifi/meshhttp.h"
|
||||
#include "meshwifi/meshwifi.h"
|
||||
|
||||
void initWifi() {}
|
||||
|
||||
void deinitWifi() {}
|
||||
|
||||
bool isWifiAvailable()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void handleWebResponse() {}
|
||||
|
||||
/// Perform idle loop processing required by the wifi layer
|
||||
void loopWifi() {}
|
|
@ -0,0 +1 @@
|
|||
../nrf52/wifi-stubs.cpp
|
Ładowanie…
Reference in New Issue