Merge branch 'master' of github.com:meshtastic/Meshtastic-device

# Conflicts:
#	variants/heltec_v1/variant.h
This commit is contained in:
Thomas Göttgens 2022-04-05 11:03:51 +02:00
commit a2df441e1f
11 changed files with 172 additions and 133 deletions

View File

@ -19,6 +19,7 @@ default_envs = tbeam
;default_envs = rak4631_5005 ;default_envs = rak4631_5005
;default_envs = rak4631_5005_eink ;default_envs = rak4631_5005_eink
;default_envs = rak4631_19003 ;default_envs = rak4631_19003
;default_envs = nano-g1
;default_envs = meshtastic-diy-v1 ;default_envs = meshtastic-diy-v1
;default_envs = meshtastic-diy-v1.1 ;default_envs = meshtastic-diy-v1.1

View File

@ -240,6 +240,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define HW_VENDOR HardwareModel_T_ECHO #define HW_VENDOR HardwareModel_T_ECHO
#elif defined(NANO_G1)
#define HW_VENDOR HardwareModel_NANO_G1
#elif NRF52_SERIES #elif NRF52_SERIES
#define HW_VENDOR HardwareModel_NRF52_UNKNOWN #define HW_VENDOR HardwareModel_NRF52_UNKNOWN

View File

@ -15,7 +15,7 @@ uint8_t oled_probe(byte addr)
r = Wire.read(); r = Wire.read();
} }
r &= 0x0f; r &= 0x0f;
if (r == 0x08) { if (r == 0x08 || r == 0x00) {
o_probe = 2; // SH1106 o_probe = 2; // SH1106
} else if ( r == 0x03 || r == 0x06 || r == 0x07) { } else if ( r == 0x03 || r == 0x06 || r == 0x07) {
o_probe = 1; // SSD1306 o_probe = 1; // SSD1306

View File

@ -854,8 +854,9 @@ int32_t Screen::runOnce()
} }
// Show boot screen for first 3 seconds, then switch to normal operation. // Show boot screen for first 3 seconds, then switch to normal operation.
// serialSinceMsec adjusts for additional serial wait time during nRF52 bootup
static bool showingBootScreen = true; static bool showingBootScreen = true;
if (showingBootScreen && (millis() > 5000)) { if (showingBootScreen && (millis() > (5000 + serialSinceMsec))) {
DEBUG_MSG("Done with boot screen...\n"); DEBUG_MSG("Done with boot screen...\n");
stopBootScreen(); stopBootScreen();
showingBootScreen = false; showingBootScreen = false;

View File

@ -78,6 +78,8 @@ uint8_t cardkb_found;
// The I2C address of the Faces Keyboard (if found) // The I2C address of the Faces Keyboard (if found)
uint8_t faceskb_found; uint8_t faceskb_found;
uint32_t serialSinceMsec;
bool axp192_found; bool axp192_found;
Router *router = NULL; // Users of router don't care what sort of subclass implements that API Router *router = NULL; // Users of router don't care what sort of subclass implements that API
@ -142,6 +144,8 @@ void setup()
} }
#endif #endif
serialSinceMsec = millis();
DEBUG_MSG("\n\n//\\ E S H T /\\ S T / C\n\n"); DEBUG_MSG("\n\n//\\ E S H T /\\ S T / C\n\n");
initDeepSleep(); initDeepSleep();

View File

@ -30,6 +30,8 @@ extern uint32_t timeLastPowered;
extern uint32_t rebootAtMsec; extern uint32_t rebootAtMsec;
extern uint32_t shutdownAtMsec; extern uint32_t shutdownAtMsec;
extern uint32_t serialSinceMsec;
// If a thread does something that might need for it to be rescheduled ASAP it can set this flag // If a thread does something that might need for it to be rescheduled ASAP it can set this flag
// This will supress the current delay and instead try to run ASAP. // This will supress the current delay and instead try to run ASAP.
extern bool runASAP; extern bool runASAP;

View File

@ -61,6 +61,8 @@ typedef enum _HardwareModel {
HardwareModel_DIY_V1 = 39, HardwareModel_DIY_V1 = 39,
/* RAK WisBlock ESP32 core: https://docs.rakwireless.com/Product-Categories/WisBlock/RAK11200/Overview/ */ /* RAK WisBlock ESP32 core: https://docs.rakwireless.com/Product-Categories/WisBlock/RAK11200/Overview/ */
HardwareModel_RAK11200 = 40, HardwareModel_RAK11200 = 40,
/* B&Q Consulting Nano Edition G1: https://uniteng.com/wiki/doku.php?id=meshtastic:nano */
HardwareModel_NANO_G1 = 41,
/* Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits. */ /* Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits. */
HardwareModel_PRIVATE_HW = 255 HardwareModel_PRIVATE_HW = 255
} HardwareModel; } HardwareModel;

View File

@ -8,10 +8,11 @@
#include "mesh/http/WiFiAPClient.h" #include "mesh/http/WiFiAPClient.h"
#include "power.h" #include "power.h"
#include "sleep.h" #include "sleep.h"
#include <FSCommon.h>
#include <HTTPBodyParser.hpp> #include <HTTPBodyParser.hpp>
#include <HTTPMultipartBodyParser.hpp> #include <HTTPMultipartBodyParser.hpp>
#include <HTTPURLEncodedBodyParser.hpp> #include <HTTPURLEncodedBodyParser.hpp>
#include <FSCommon.h> #include <json11.hpp>
#ifndef NO_ESP32 #ifndef NO_ESP32
#include "esp_task_wdt.h" #include "esp_task_wdt.h"
@ -272,8 +273,6 @@ void handleAPIv1ToRadio(HTTPRequest *req, HTTPResponse *res)
DEBUG_MSG("webAPI handleAPIv1ToRadio\n"); DEBUG_MSG("webAPI handleAPIv1ToRadio\n");
} }
bool firstFile = 1;
void htmlDeleteDir(const char * dirname) void htmlDeleteDir(const char * dirname)
{ {
File root = FSCom.open(dirname); File root = FSCom.open(dirname);
@ -301,70 +300,72 @@ void htmlDeleteDir(const char * dirname)
root.close(); root.close();
} }
void htmlListDir(HTTPResponse *res, const char * dirname, uint8_t levels) std::vector<std::map<char *, char *>>* htmlListDir(std::vector<std::map<char *, char *>> *fileList, const char *dirname, uint8_t levels)
{ {
File root = FSCom.open(dirname); File root = FSCom.open(dirname);
if(!root){ if(!root){
return; return NULL;
} }
if(!root.isDirectory()){ if(!root.isDirectory()){
return; return NULL;
} }
// iterate over the file list
File file = root.openNextFile(); File file = root.openNextFile();
while(file){ while(file){
if(file.isDirectory() && !String(file.name()).endsWith(".")) { if(file.isDirectory() && !String(file.name()).endsWith(".")) {
if(levels){ if(levels){
htmlListDir(res, file.name(), levels -1); htmlListDir(fileList, file.name(), levels -1);
} }
} else { } else {
if (firstFile) { std::map<char*, char*> thisFileMap;
firstFile = 0; thisFileMap[strdup("size")] = strdup(String(file.size()).c_str());
} else { thisFileMap[strdup("name")] = strdup(String(file.name()).substring(1).c_str());
res->println(",");
}
res->println("{");
if (String(file.name()).substring(1).endsWith(".gz")) { if (String(file.name()).substring(1).endsWith(".gz")) {
String modifiedFile = String(file.name()).substring(1); String modifiedFile = String(file.name()).substring(1);
modifiedFile.remove((modifiedFile.length() - 3), 3); modifiedFile.remove((modifiedFile.length() - 3), 3);
res->print("\"nameModified\": \"" + modifiedFile + "\","); thisFileMap[strdup("nameModified")] = strdup(modifiedFile.c_str());
res->print("\"name\": \"" + String(file.name()).substring(1) + "\",");
} else {
res->print("\"name\": \"" + String(file.name()).substring(1) + "\",");
} }
res->print("\"size\": " + String(file.size())); fileList->push_back(thisFileMap);
res->print("}");
} }
file.close(); file.close();
file = root.openNextFile(); file = root.openNextFile();
} }
root.close(); root.close();
return fileList;
} }
void handleFsBrowseStatic(HTTPRequest *req, HTTPResponse *res) void handleFsBrowseStatic(HTTPRequest *req, HTTPResponse *res)
{ {
res->setHeader("Content-Type", "application/json"); res->setHeader("Content-Type", "application/json");
res->setHeader("Access-Control-Allow-Origin", "*"); res->setHeader("Access-Control-Allow-Origin", "*");
res->setHeader("Access-Control-Allow-Methods", "GET"); res->setHeader("Access-Control-Allow-Methods", "GET");
res->println("{");
res->println("\"data\": {");
res->print("\"files\": [");
htmlListDir(res, "/", 10);
res->print("],");
res->print("\"filesystem\" : {");
res->print("\"total\" : " + String(FSCom.totalBytes()) + ",");
res->print("\"used\" : " + String(FSCom.usedBytes()) + ",");
res->print("\"free\" : " + String(FSCom.totalBytes() - FSCom.usedBytes()));
res->println("}");
res->println("},");
res->println("\"status\": \"ok\"");
res->println("}");
}
using namespace json11;
auto fileList = htmlListDir(new std::vector<std::map<char *, char *>>(), "/", 10);
// create json output structure
Json filesystemObj = Json::object{
{"total", String(FSCom.totalBytes()).c_str()},
{"used", String(FSCom.usedBytes()).c_str()},
{"free", String(FSCom.totalBytes() - FSCom.usedBytes()).c_str()},
};
Json jsonObjInner = Json::object{{"files", Json(*fileList)},
{"filesystem", filesystemObj}
};
Json jsonObjOuter = Json::object{{"data", jsonObjInner}, {"status", "ok"}};
// serialize and write it to the stream
std::string jsonStr = jsonObjOuter.dump();
res->print(jsonStr.c_str());
}
void handleFsDeleteStatic(HTTPRequest *req, HTTPResponse *res) void handleFsDeleteStatic(HTTPRequest *req, HTTPResponse *res)
{ {
using namespace json11;
ResourceParameters *params = req->getParams(); ResourceParameters *params = req->getParams();
std::string paramValDelete; std::string paramValDelete;
@ -375,15 +376,15 @@ void handleFsDeleteStatic(HTTPRequest *req, HTTPResponse *res)
std::string pathDelete = "/" + paramValDelete; std::string pathDelete = "/" + paramValDelete;
if (FSCom.remove(pathDelete.c_str())) { if (FSCom.remove(pathDelete.c_str())) {
Serial.println(pathDelete.c_str()); Serial.println(pathDelete.c_str());
res->println("{"); Json jsonObjOuter = Json::object{{"status", "ok"}};
res->println("\"status\": \"ok\""); std::string jsonStr = jsonObjOuter.dump();
res->println("}"); res->print(jsonStr.c_str());
return; return;
} else { } else {
Serial.println(pathDelete.c_str()); Serial.println(pathDelete.c_str());
res->println("{"); Json jsonObjOuter = Json::object{{"status", "Error"}};
res->println("\"status\": \"Error\""); std::string jsonStr = jsonObjOuter.dump();
res->println("}"); res->print(jsonStr.c_str());
return; return;
} }
} }
@ -600,6 +601,7 @@ void handleFormUpload(HTTPRequest *req, HTTPResponse *res)
void handleReport(HTTPRequest *req, HTTPResponse *res) void handleReport(HTTPRequest *req, HTTPResponse *res)
{ {
using namespace json11;
ResourceParameters *params = req->getParams(); ResourceParameters *params = req->getParams();
std::string content; std::string content;
@ -618,104 +620,89 @@ void handleReport(HTTPRequest *req, HTTPResponse *res)
res->println("<pre>"); res->println("<pre>");
} }
res->println("{"); // data->airtime->tx_log
std::vector<String> txLogValues;
res->println("\"data\": {");
res->println("\"airtime\": {");
uint32_t *logArray; uint32_t *logArray;
res->print("\"tx_log\": [");
logArray = airTime->airtimeReport(TX_LOG); logArray = airTime->airtimeReport(TX_LOG);
for (int i = 0; i < airTime->getPeriodsToLog(); i++) { for (int i = 0; i < airTime->getPeriodsToLog(); i++) {
uint32_t tmp; uint32_t tmp;
tmp = *(logArray + i); tmp = *(logArray + i);
res->printf("%d", tmp); txLogValues.push_back(String(tmp));
if (i != airTime->getPeriodsToLog() - 1) {
res->print(", ");
}
} }
res->println("],"); // data->airtime->rx_log
res->print("\"rx_log\": ["); std::vector<String> rxLogValues;
logArray = airTime->airtimeReport(RX_LOG); logArray = airTime->airtimeReport(RX_LOG);
for (int i = 0; i < airTime->getPeriodsToLog(); i++) { for (int i = 0; i < airTime->getPeriodsToLog(); i++) {
uint32_t tmp; uint32_t tmp;
tmp = *(logArray + i); tmp = *(logArray + i);
res->printf("%d", tmp); rxLogValues.push_back(String(tmp));
if (i != airTime->getPeriodsToLog() - 1) {
res->print(", ");
}
} }
res->println("],"); // data->airtime->rx_all_log
res->print("\"rx_all_log\": ["); std::vector<String> rxAllLogValues;
logArray = airTime->airtimeReport(RX_ALL_LOG); logArray = airTime->airtimeReport(RX_ALL_LOG);
for (int i = 0; i < airTime->getPeriodsToLog(); i++) { for (int i = 0; i < airTime->getPeriodsToLog(); i++) {
uint32_t tmp; uint32_t tmp;
tmp = *(logArray + i); tmp = *(logArray + i);
res->printf("%d", tmp); rxAllLogValues.push_back(String(tmp));
if (i != airTime->getPeriodsToLog() - 1) {
res->print(", ");
}
} }
res->println("],"); Json jsonObjAirtime = Json::object{
res->printf("\"channel_utilization\": %3.2f%,\n", airTime->channelUtilizationPercent()); {"tx_log", Json(txLogValues)},
res->printf("\"utilization_tx\": %3.2f%,\n", airTime->utilizationTXPercent()); {"rx_log", Json(rxLogValues)},
res->printf("\"seconds_since_boot\": %u,\n", airTime->getSecondsSinceBoot()); {"rx_all_log", Json(rxAllLogValues)},
res->printf("\"seconds_per_period\": %u,\n", airTime->getSecondsPerPeriod()); {"channel_utilization", Json(airTime->channelUtilizationPercent())},
res->printf("\"periods_to_log\": %u\n", airTime->getPeriodsToLog()); {"utilization_tx", Json(airTime->utilizationTXPercent())},
{"seconds_since_boot", Json(int(airTime->getSecondsSinceBoot()))},
res->println("},"); {"seconds_per_period", Json(int(airTime->getSecondsPerPeriod()))},
{"periods_to_log", Json(airTime->getPeriodsToLog())},
res->println("\"wifi\": {"); };
res->println("\"rssi\": " + String(WiFi.RSSI()) + ",");
// data->wifi
String ipStr;
if (radioConfig.preferences.wifi_ap_mode || isSoftAPForced()) { if (radioConfig.preferences.wifi_ap_mode || isSoftAPForced()) {
res->println("\"ip\": \"" + String(WiFi.softAPIP().toString().c_str()) + "\""); ipStr = String(WiFi.softAPIP().toString());
} else { } else {
res->println("\"ip\": \"" + String(WiFi.localIP().toString().c_str()) + "\""); ipStr = String(WiFi.localIP().toString());
} }
Json jsonObjWifi = Json::object{
{"rssi", String(WiFi.RSSI())},
{"ip", ipStr.c_str()}
};
res->println("},"); // data->memory
Json jsonObjMemory = Json::object{{"heap_total", Json(int(ESP.getHeapSize()))},
{"heap_free", Json(int(ESP.getFreeHeap()))},
{"psram_total", Json(int(ESP.getPsramSize()))},
{"psram_free", Json(int(ESP.getFreePsram()))},
{"fs_total", String(FSCom.totalBytes()).c_str()},
{"fs_used", String(FSCom.usedBytes()).c_str()},
{"fs_free", String(FSCom.totalBytes() - FSCom.usedBytes()).c_str()}};
res->println("\"memory\": {"); // data->power
res->printf("\"heap_total\": %d,\n", ESP.getHeapSize()); Json jsonObjPower = Json::object{{"battery_percent", Json(powerStatus->getBatteryChargePercent())},
res->printf("\"heap_free\": %d,\n", ESP.getFreeHeap()); {"battery_voltage_mv", Json(powerStatus->getBatteryVoltageMv())},
res->printf("\"psram_total\": %d,\n", ESP.getPsramSize()); {"has_battery", BoolToString(powerStatus->getHasBattery())},
res->printf("\"psram_free\": %d,\n", ESP.getFreePsram()); {"has_usb", BoolToString(powerStatus->getHasUSB())},
res->println("\"fs_total\" : " + String(FSCom.totalBytes()) + ","); {"is_charging", BoolToString(powerStatus->getIsCharging())}};
res->println("\"fs_used\" : " + String(FSCom.usedBytes()) + ",");
res->println("\"fs_free\" : " + String(FSCom.totalBytes() - FSCom.usedBytes()));
res->println("},");
res->println("\"power\": {"); // data->device
res->printf("\"battery_percent\": %u,\n", powerStatus->getBatteryChargePercent()); Json jsonObjDevice = Json::object{{"reboot_counter", Json(int(myNodeInfo.reboot_count))}};
res->printf("\"battery_voltage_mv\": %u,\n", powerStatus->getBatteryVoltageMv());
res->printf("\"has_battery\": %s,\n", BoolToString(powerStatus->getHasBattery()));
res->printf("\"has_usb\": %s,\n", BoolToString(powerStatus->getHasUSB()));
res->printf("\"is_charging\": %s\n", BoolToString(powerStatus->getIsCharging()));
res->println("},");
res->println("\"device\": {"); // data->radio
res->printf("\"reboot_counter\": %d\n", myNodeInfo.reboot_count); Json jsonObjRadio = Json::object{{"frequency", Json(RadioLibInterface::instance->getFreq())},
res->println("},"); {"lora_channel", Json(int(RadioLibInterface::instance->getChannelNum()))}};
res->println("\"radio\": {"); // collect data to inner data object
res->printf("\"frequecy\": %f,\n", RadioLibInterface::instance->getFreq()); Json jsonObjInner = Json::object{{"airtime", jsonObjAirtime}, {"wifi", jsonObjWifi}, {"memory", jsonObjMemory},
res->printf("\"lora_channel\": %d\n", RadioLibInterface::instance->getChannelNum()); {"power", jsonObjPower}, {"device", jsonObjDevice}, {"radio", jsonObjRadio}};
res->println("}");
res->println("},"); // create json output structure
Json jsonObjOuter = Json::object{{"data", jsonObjInner}, {"status", "ok"}};
res->println("\"status\": \"ok\""); // serialize and write it to the stream
res->println("}"); std::string jsonStr = jsonObjOuter.dump();
res->print(jsonStr.c_str());
} }
/* /*
@ -912,6 +899,8 @@ void handleRestart(HTTPRequest *req, HTTPResponse *res)
void handleBlinkLED(HTTPRequest *req, HTTPResponse *res) void handleBlinkLED(HTTPRequest *req, HTTPResponse *res)
{ {
using namespace json11;
res->setHeader("Content-Type", "application/json"); res->setHeader("Content-Type", "application/json");
res->setHeader("Access-Control-Allow-Origin", "*"); res->setHeader("Access-Control-Allow-Origin", "*");
res->setHeader("Access-Control-Allow-Methods", "POST"); res->setHeader("Access-Control-Allow-Methods", "POST");
@ -938,28 +927,25 @@ void handleBlinkLED(HTTPRequest *req, HTTPResponse *res)
screen->blink(); screen->blink();
} }
res->println("{"); Json jsonObjOuter = Json::object{{"status", "ok"}};
res->println("\"status\": \"ok\""); std::string jsonStr = jsonObjOuter.dump();
res->println("}"); res->print(jsonStr.c_str());
} }
void handleScanNetworks(HTTPRequest *req, HTTPResponse *res) void handleScanNetworks(HTTPRequest *req, HTTPResponse *res)
{ {
using namespace json11;
res->setHeader("Content-Type", "application/json"); res->setHeader("Content-Type", "application/json");
res->setHeader("Access-Control-Allow-Origin", "*"); res->setHeader("Access-Control-Allow-Origin", "*");
res->setHeader("Access-Control-Allow-Methods", "GET"); res->setHeader("Access-Control-Allow-Methods", "GET");
// res->setHeader("Content-Type", "text/html"); // res->setHeader("Content-Type", "text/html");
int n = WiFi.scanNetworks(); int n = WiFi.scanNetworks();
res->println("{");
res->println("\"data\": {");
if (n == 0) {
// No networks found.
res->println("\"networks\": []");
} else {
res->println("\"networks\": [");
// build list of network objects
std::vector<Json> networkObjs;
if (n > 0) {
for (int i = 0; i < n; ++i) { for (int i = 0; i < n; ++i) {
char ssidArray[50]; char ssidArray[50];
String ssidString = String(WiFi.SSID(i)); String ssidString = String(WiFi.SSID(i));
@ -967,19 +953,20 @@ void handleScanNetworks(HTTPRequest *req, HTTPResponse *res)
ssidString.toCharArray(ssidArray, 50); ssidString.toCharArray(ssidArray, 50);
if (WiFi.encryptionType(i) != WIFI_AUTH_OPEN) { if (WiFi.encryptionType(i) != WIFI_AUTH_OPEN) {
res->printf("{\"ssid\": \"%s\",\"rssi\": %d}", ssidArray, WiFi.RSSI(i)); Json thisNetwork = Json::object{{"ssid", ssidArray}, {"rssi", WiFi.RSSI(i)}};
if (i != n - 1) { networkObjs.push_back(thisNetwork);
res->printf(",");
}
} }
// Yield some cpu cycles to IP stack. // Yield some cpu cycles to IP stack.
// This is important in case the list is large and it takes us time to return // This is important in case the list is large and it takes us time to return
// to the main loop. // to the main loop.
yield(); yield();
} }
res->println("]");
} }
res->println("},");
res->println("\"status\": \"ok\""); // build output structure
res->println("}"); Json jsonObjOuter = Json::object{{"data", networkObjs}, {"status", "ok"}};
// serialize and write it to the stream
std::string jsonStr = jsonObjOuter.dump();
res->print(jsonStr.c_str());
} }

View File

@ -0,0 +1,8 @@
; The 1.0 release of the nano-g1 board
[env:nano-g1]
extends = esp32_base
board = ttgo-t-beam
lib_deps =
${esp32_base.lib_deps}
build_flags =
${esp32_base.build_flags} -D NANO_G1 -I variants/nano-g1

View File

@ -0,0 +1,30 @@
// #define BUTTON_NEED_PULLUP // if set we need to turn on the internal CPU pullup during sleep
#define I2C_SDA 21
#define I2C_SCL 22
#define BUTTON_PIN 36 // The middle button GPIO on the Nano G1
//#define BUTTON_PIN_ALT 13 // Alternate GPIO for an external button if needed. Does anyone use this? It is not documented anywhere.
#define EXT_NOTIFY_OUT 13 // Default pin to use for Ext Notify Module.
// common pinout for their SX1262 vs RF95 modules - both can be enabled and we will probe at runtime for RF95 and if
// not found then probe for SX1262
#define USE_RF95
#define USE_SX1262
#define LORA_DIO0 26 // a No connect on the SX1262 module
#define LORA_RESET 23
#define LORA_DIO1 33 // SX1262 IRQ
#define LORA_DIO2 32 // SX1262 BUSY
#define LORA_DIO3 // Not connected on PCB
#ifdef USE_SX1262
#define SX126X_CS RF95_NSS // FIXME - we really should define LORA_CS instead
#define SX126X_DIO1 LORA_DIO1
#define SX126X_BUSY LORA_DIO2
#define SX126X_RESET LORA_RESET
#define SX126X_E22 // Not really an E22
// Internally the module hooks the SX1262-DIO2 in to control the TX/RX switch (which is the default for the sx1262interface
// code)
#endif

View File

@ -1,4 +1,4 @@
[VERSION] [VERSION]
major = 1 major = 1
minor = 3 minor = 3
build = 4 build = 5