Compare commits
2 Commits
1706186727
...
411864dab8
| Author | SHA1 | Date | |
|---|---|---|---|
| 411864dab8 | |||
| f0cd430eb9 |
@@ -242,6 +242,9 @@
|
|||||||
<option value="921600">921600</option>
|
<option value="921600">921600</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
<button class="danger" onclick="doReset()" title="Pulse the target reset line">Reset</button>
|
||||||
|
<button onclick="doButton()" title="Momentary press of the target button / wake line">Button</button>
|
||||||
|
<button id="holdBtn" onclick="toggleHold()" title="Latch the button line held down (force-on) / release">Hold: off</button>
|
||||||
<button onclick="clearTerminal()">Clear</button>
|
<button onclick="clearTerminal()">Clear</button>
|
||||||
<button onclick="reconnect()">Reconnect</button>
|
<button onclick="reconnect()">Reconnect</button>
|
||||||
<button id="logBtn" onclick="toggleLog()">Log: --</button>
|
<button id="logBtn" onclick="toggleLog()">Log: --</button>
|
||||||
@@ -306,6 +309,7 @@
|
|||||||
// Terminal setup
|
// Terminal setup
|
||||||
const term = new Terminal({
|
const term = new Terminal({
|
||||||
cursorBlink: true,
|
cursorBlink: true,
|
||||||
|
convertEol: true, // target sends bare '\n'; treat it as CRLF so lines don't staircase
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
fontFamily: 'Menlo, Monaco, "Courier New", monospace',
|
fontFamily: 'Menlo, Monaco, "Courier New", monospace',
|
||||||
theme: {
|
theme: {
|
||||||
@@ -432,6 +436,7 @@
|
|||||||
updateStatus('btStatus', msg.btConnected);
|
updateStatus('btStatus', msg.btConnected);
|
||||||
document.getElementById('heap').textContent = `Heap: ${msg.freeHeap}`;
|
document.getElementById('heap').textContent = `Heap: ${msg.freeHeap}`;
|
||||||
updateLogUi(msg);
|
updateLogUi(msg);
|
||||||
|
updateButtonUi(msg);
|
||||||
|
|
||||||
// Show WiFi info
|
// Show WiFi info
|
||||||
const wifiInfo = document.getElementById('wifiInfo');
|
const wifiInfo = document.getElementById('wifiInfo');
|
||||||
@@ -591,6 +596,34 @@
|
|||||||
return (bytes / 1024 / 1024).toFixed(1) + ' MB';
|
return (bytes / 1024 / 1024).toFixed(1) + ' MB';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---- Target control lines (reset / button) ----
|
||||||
|
function pulseLine(path, label) {
|
||||||
|
fetch(path + '?ms=200').then(r => r.text())
|
||||||
|
.then(t => term.writeln(`\r\n\x1b[33m[${label}] ${t.trim()}\x1b[0m`))
|
||||||
|
.catch(() => term.writeln(`\r\n\x1b[31m[${label} failed]\x1b[0m`));
|
||||||
|
}
|
||||||
|
function doReset() { pulseLine('/api/reset', 'reset'); }
|
||||||
|
function doButton() {
|
||||||
|
fetch('/api/button?ms=200').then(r => r.json()).then(s => {
|
||||||
|
updateButtonUi(s);
|
||||||
|
term.writeln('\r\n\x1b[33m[button] pulsed\x1b[0m');
|
||||||
|
}).catch(() => term.writeln('\r\n\x1b[31m[button failed]\x1b[0m'));
|
||||||
|
}
|
||||||
|
let holdOn = false;
|
||||||
|
function toggleHold() {
|
||||||
|
fetch('/api/button?latch=' + (holdOn ? 'off' : 'on')).then(r => r.json()).then(s => {
|
||||||
|
updateButtonUi(s);
|
||||||
|
term.writeln(`\r\n\x1b[33m[button] hold ${holdOn ? 'ON' : 'off'}\x1b[0m`);
|
||||||
|
}).catch(() => term.writeln('\r\n\x1b[31m[hold failed]\x1b[0m'));
|
||||||
|
}
|
||||||
|
function updateButtonUi(s) {
|
||||||
|
if (typeof s.buttonLatch === 'undefined') return;
|
||||||
|
holdOn = !!s.buttonLatch;
|
||||||
|
const b = document.getElementById('holdBtn');
|
||||||
|
b.textContent = 'Hold: ' + (holdOn ? 'ON' : 'off');
|
||||||
|
b.classList.toggle('danger', holdOn);
|
||||||
|
}
|
||||||
|
|
||||||
// ---- SD logging controls ----
|
// ---- SD logging controls ----
|
||||||
let logOn = false;
|
let logOn = false;
|
||||||
function toggleLog() {
|
function toggleLog() {
|
||||||
|
|||||||
+6
-3
@@ -83,9 +83,12 @@ build_flags =
|
|||||||
; --- target UART bridge (wire to the sensor's debug UART) ---
|
; --- target UART bridge (wire to the sensor's debug UART) ---
|
||||||
-DTARGET_RX_PIN=44 ; CONFIRM: dongle RX <- sensor TX
|
-DTARGET_RX_PIN=44 ; CONFIRM: dongle RX <- sensor TX
|
||||||
-DTARGET_TX_PIN=43 ; CONFIRM: dongle TX -> sensor RX
|
-DTARGET_TX_PIN=43 ; CONFIRM: dongle TX -> sensor RX
|
||||||
; --- GPIO control lines to the target (reset / wake) ---
|
; --- GPIO control lines to the target (reset out / button out) ---
|
||||||
-DGPIO_RESET_PIN=2 ; CONFIRM: -> sensor RST
|
; GPIO38 + GPIO39 are free pins on the T3-S3 RIGHT header, right next to the
|
||||||
-DGPIO_WAKE_PIN=1 ; CONFIRM: -> sensor control/wake pin
|
; UART pins TXD=43/RXD=44 -- so reset/button/TX/RX/GND all come off one row.
|
||||||
|
; (GPIO2=SD MISO and GPIO1=battery ADC; GPIO4/12 are not broken out at all.)
|
||||||
|
-DGPIO_RESET_PIN=38 ; -> sensor RST (active-low pulse)
|
||||||
|
-DGPIO_WAKE_PIN=39 ; -> sensor button/wake (active-low pulse)
|
||||||
-DGPIO_CTRL_ACTIVE_LOW=1
|
-DGPIO_CTRL_ACTIVE_LOW=1
|
||||||
; --- microSD on FSPI/SPI2 (separate bus from LoRa, which uses HSPI/SPI3) ---
|
; --- microSD on FSPI/SPI2 (separate bus from LoRa, which uses HSPI/SPI3) ---
|
||||||
-DT3S3_SD_SCK=14
|
-DT3S3_SD_SCK=14
|
||||||
|
|||||||
+37
-7
@@ -125,6 +125,9 @@ size_t telnetLineLen[MAX_TELNET] = {0};
|
|||||||
volatile uint32_t rxBytes = 0; // from target
|
volatile uint32_t rxBytes = 0; // from target
|
||||||
volatile uint32_t txBytes = 0; // to target
|
volatile uint32_t txBytes = 0; // to target
|
||||||
|
|
||||||
|
// Button line held active (latched) rather than momentary-pulsed.
|
||||||
|
bool buttonLatched = false;
|
||||||
|
|
||||||
// SD logging (T3-S3)
|
// SD logging (T3-S3)
|
||||||
bool sdAvailable = false;
|
bool sdAvailable = false;
|
||||||
bool logEnabled = true; // ON by default once we have a date
|
bool logEnabled = true; // ON by default once we have a date
|
||||||
@@ -190,6 +193,17 @@ static void gpioPulse(int pin, uint32_t ms, Stream* reply, const char* name) {
|
|||||||
if (reply) reply->printf("[ctrl] %s pulsed pin %d for %lu ms\r\n", name, pin, (unsigned long)ms);
|
if (reply) reply->printf("[ctrl] %s pulsed pin %d for %lu ms\r\n", name, pin, (unsigned long)ms);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hold a control line active (on) or release it (off) -- a latched "press".
|
||||||
|
static void gpioLatch(int pin, bool on, Stream* reply, const char* name) {
|
||||||
|
if (pin < 0) { if (reply) reply->printf("[ctrl] %s pin not configured\r\n", name); return; }
|
||||||
|
int active = GPIO_CTRL_ACTIVE_LOW ? LOW : HIGH;
|
||||||
|
int inactive = GPIO_CTRL_ACTIVE_LOW ? HIGH : LOW;
|
||||||
|
pinMode(pin, OUTPUT);
|
||||||
|
digitalWrite(pin, on ? active : inactive);
|
||||||
|
if (reply) reply->printf("[ctrl] %s latch=%s (pin %d %s)\r\n",
|
||||||
|
name, on ? "on" : "off", pin, on ? "held" : "released");
|
||||||
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// SD logging (T3-S3)
|
// SD logging (T3-S3)
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
@@ -364,7 +378,7 @@ void handleCommand(const char* line, Stream* reply) {
|
|||||||
const char* args = line[n] ? line + n + 1 : line + n;
|
const char* args = line[n] ? line + n + 1 : line + n;
|
||||||
|
|
||||||
if (!strcmp(verb, "help")) {
|
if (!strcmp(verb, "help")) {
|
||||||
reply->print("[help] ~status ~reset [ms] ~wake [ms] ~baud <n> "
|
reply->print("[help] ~status ~reset [ms] ~button [ms|on|off] ~baud <n> "
|
||||||
"~port <int|usb|ext> ~log on|off ~gpio <pin> <0|1>\r\n"
|
"~port <int|usb|ext> ~log on|off ~gpio <pin> <0|1>\r\n"
|
||||||
" ~mesh [on|off] ~psk <base64key> ~chan <name> <base64key> ~msg <text>\r\n"
|
" ~mesh [on|off] ~psk <base64key> ~chan <name> <base64key> ~msg <text>\r\n"
|
||||||
" (any non-~ line is sent to the target UART)\r\n");
|
" (any non-~ line is sent to the target UART)\r\n");
|
||||||
@@ -372,8 +386,10 @@ void handleCommand(const char* line, Stream* reply) {
|
|||||||
printStatus(reply);
|
printStatus(reply);
|
||||||
} else if (!strcmp(verb, "reset")) {
|
} else if (!strcmp(verb, "reset")) {
|
||||||
gpioPulse(GPIO_RESET_PIN, (uint32_t)strtoul(args, nullptr, 10), reply, "reset");
|
gpioPulse(GPIO_RESET_PIN, (uint32_t)strtoul(args, nullptr, 10), reply, "reset");
|
||||||
} else if (!strcmp(verb, "wake")) {
|
} else if (!strcmp(verb, "wake") || !strcmp(verb, "button")) {
|
||||||
gpioPulse(GPIO_WAKE_PIN, (uint32_t)strtoul(args, nullptr, 10), reply, "wake");
|
if (!strncmp(args, "on", 2)) { gpioLatch(GPIO_WAKE_PIN, true, reply, "button"); buttonLatched = true; }
|
||||||
|
else if (!strncmp(args, "off", 3)) { gpioLatch(GPIO_WAKE_PIN, false, reply, "button"); buttonLatched = false; }
|
||||||
|
else { gpioPulse(GPIO_WAKE_PIN, (uint32_t)strtoul(args, nullptr, 10), reply, "button"); buttonLatched = false; }
|
||||||
} else if (!strcmp(verb, "baud")) {
|
} else if (!strcmp(verb, "baud")) {
|
||||||
uint32_t b = strtoul(args, nullptr, 10);
|
uint32_t b = strtoul(args, nullptr, 10);
|
||||||
if (b) { setBaudRate(b); reply->printf("[ctrl] baud=%lu\r\n", (unsigned long)b); }
|
if (b) { setBaudRate(b); reply->printf("[ctrl] baud=%lu\r\n", (unsigned long)b); }
|
||||||
@@ -495,6 +511,7 @@ void handleWebSocketMessage(AsyncWebSocketClient* client, uint8_t* data, size_t
|
|||||||
r["baudSerial1"] = baudSerial1;
|
r["baudSerial1"] = baudSerial1;
|
||||||
r["rx"] = rxBytes; r["tx"] = txBytes;
|
r["rx"] = rxBytes; r["tx"] = txBytes;
|
||||||
r["log"] = logEnabled; r["ntp"] = ntpSynced; r["sd"] = sdAvailable;
|
r["log"] = logEnabled; r["ntp"] = ntpSynced; r["sd"] = sdAvailable;
|
||||||
|
r["buttonLatch"] = buttonLatched;
|
||||||
#if BOARD_T3S3
|
#if BOARD_T3S3
|
||||||
r["logfile"] = logFile ? logPath : "";
|
r["logfile"] = logFile ? logPath : "";
|
||||||
#endif
|
#endif
|
||||||
@@ -539,6 +556,7 @@ static String statusJson() {
|
|||||||
doc["baudSerial1"] = baudSerial1;
|
doc["baudSerial1"] = baudSerial1;
|
||||||
doc["rx"] = rxBytes; doc["tx"] = txBytes;
|
doc["rx"] = rxBytes; doc["tx"] = txBytes;
|
||||||
doc["log"] = logEnabled; doc["ntp"] = ntpSynced; doc["sd"] = sdAvailable;
|
doc["log"] = logEnabled; doc["ntp"] = ntpSynced; doc["sd"] = sdAvailable;
|
||||||
|
doc["buttonLatch"] = buttonLatched;
|
||||||
#if BOARD_T3S3
|
#if BOARD_T3S3
|
||||||
doc["logfile"] = logFile ? logPath : "";
|
doc["logfile"] = logFile ? logPath : "";
|
||||||
#endif
|
#endif
|
||||||
@@ -563,11 +581,23 @@ void setupWebServer() {
|
|||||||
gpioPulse(GPIO_RESET_PIN, ms, nullptr, "reset");
|
gpioPulse(GPIO_RESET_PIN, ms, nullptr, "reset");
|
||||||
req->send(200, "text/plain", "reset pulsed\n");
|
req->send(200, "text/plain", "reset pulsed\n");
|
||||||
});
|
});
|
||||||
server.on("/api/wake", HTTP_GET, [](AsyncWebServerRequest* req) {
|
// Momentary pulse (?ms=), or latch the line held (?latch=on|off|1|0).
|
||||||
|
auto doButton = [](AsyncWebServerRequest* req) {
|
||||||
|
if (req->hasParam("latch")) {
|
||||||
|
String v = req->getParam("latch")->value();
|
||||||
|
bool on = (v == "1" || v.startsWith("on") || v == "true");
|
||||||
|
gpioLatch(GPIO_WAKE_PIN, on, nullptr, "button");
|
||||||
|
buttonLatched = on;
|
||||||
|
req->send(200, "application/json", statusJson());
|
||||||
|
return;
|
||||||
|
}
|
||||||
uint32_t ms = req->hasParam("ms") ? req->getParam("ms")->value().toInt() : 0;
|
uint32_t ms = req->hasParam("ms") ? req->getParam("ms")->value().toInt() : 0;
|
||||||
gpioPulse(GPIO_WAKE_PIN, ms, nullptr, "wake");
|
gpioPulse(GPIO_WAKE_PIN, ms, nullptr, "button");
|
||||||
req->send(200, "text/plain", "wake pulsed\n");
|
buttonLatched = false;
|
||||||
});
|
req->send(200, "application/json", statusJson());
|
||||||
|
};
|
||||||
|
server.on("/api/button", HTTP_GET, doButton);
|
||||||
|
server.on("/api/wake", HTTP_GET, doButton); // back-compat alias
|
||||||
server.on("/api/baud", HTTP_GET, [](AsyncWebServerRequest* req) {
|
server.on("/api/baud", HTTP_GET, [](AsyncWebServerRequest* req) {
|
||||||
if (req->hasParam("baud")) setBaudRate(req->getParam("baud")->value().toInt());
|
if (req->hasParam("baud")) setBaudRate(req->getParam("baud")->value().toInt());
|
||||||
req->send(200, "application/json", statusJson());
|
req->send(200, "application/json", statusJson());
|
||||||
|
|||||||
Reference in New Issue
Block a user