Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions Firmware/simulator/smoke-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
// seed drill's relays follow and the sprayer's calibration (the pages' own password question is answered). Also opens the sprayer
// and Teensy pages inside a frame with the sandbox of an AgOpenWeb web card, where the browser's confirm() and prompt() do
// nothing: the pages' own question box must show there, fit the card and answer. A browser dialog on any page fails the check.
// Enter in a password box does what its button does; an empty box or a held Enter does nothing.
"use strict";
const fs = require("fs"), path = require("path"), http = require("http");
const {chromium} = require("playwright");
Expand Down Expand Up @@ -374,6 +375,40 @@ async function teensy(browser, port, q, name) {
if (await pg.locator("#ghInstall").isDisabled()) fail.push(what + ": Install from GitHub is not offered");
await pg.close();
}
// Enter in the XIAO page's password boxes: it does what the card's button does (here every action asks first, and the check
// says no); an empty box does nothing, and a held Enter does not also answer the question it opened
{
const what = "XIAO page, Enter in the password boxes";
const pg = await page(browser, "http://127.0.0.1:" + port + "/Boards/xiao/", what, j => { if (j.xiao.C3) j.xiao.C3.built = LATER; });
const askOpen = async (re, how) => {
await pg.waitForTimeout(300);
if (!(await pg.locator("#ask").isVisible())) { fail.push(what + ": " + how + " did not ask anything"); return; }
await expect(pg, what, "#askText", re);
await pg.keyboard.press("Escape"); await pg.waitForTimeout(200);
if (await pg.locator("#ask").isVisible()) fail.push(what + ": Escape does not close the question");
};
await pg.waitForTimeout(2500);
await pg.click('nav button[data-t="wifi"]'); await pg.waitForTimeout(300);
await pg.fill("#subnet", "192.168.7"); await pg.locator("#pwN").press("Enter"); await pg.waitForTimeout(300);
if (await pg.locator("#ask").isVisible()) fail.push(what + ": Enter in the empty Module network password asked something");
await pg.fill("#pwN", "agopengps"); await pg.locator("#pwN").press("Enter");
await askOpen(/^Change the module's subnet to 192\.168\.7 and restart it\?/, "Enter in the Module network password");
await pg.click('nav button[data-t="prog"]'); await pg.waitForTimeout(300);
if (await pg.locator("#ghInstall").isDisabled()) fail.push(what + ": Install from GitHub is not offered");
await pg.click("#ghInstall"); await pg.waitForTimeout(200);
await expect(pg, what, "#fwMsg", /^Type the XIAO network password\.$/);
await pg.locator("#pwP").press("Enter"); await pg.waitForTimeout(300);
if (await pg.locator("#ask").isVisible()) fail.push(what + ": Enter in the empty password box asked something");
await pg.fill("#pwP", "agopengps"); await pg.locator("#pwP").press("Enter"); // the password typed after Install from GitHub asked for it
await askOpen(/^Install [\d.]+, built 31 Dec 2099 23:59 from GitHub \(v[\d.]+_XIAO_ESP32C3\.bin\) on the XIAO\?$/, "Enter after Install from GitHub asked for the password");
const bin = Buffer.alloc(4096); bin[0] = 0xE9; bin[12] = 5; // an ESP32-C3 program's header (chip id 5)
await pg.setInputFiles("#binFile", {name: "v4.5_XIAO_ESP32C3.bin", mimeType: "application/octet-stream", buffer: bin}); await pg.waitForTimeout(300);
await pg.focus("#pwP");
await pg.keyboard.down("Enter"); await pg.keyboard.down("Enter"); await pg.keyboard.down("Enter"); await pg.keyboard.up("Enter"); // held: repeats
await askOpen(/^Install v4\.5_XIAO_ESP32C3\.bin on the XIAO\?$/, "a held Enter in the password box");
if (await pg.locator("#prog").isVisible()) fail.push(what + ": the installation started");
await pg.close();
}
// the seed drill module's page: the Relay24_ESP32 on the network cable sowing a pass with 19 of its 24 relays (relay 1 =
// section 1 and so on, 19 sections set); Settings: the relays set back to the program's pin config (the password asked in a
// prompt);
Expand Down Expand Up @@ -631,13 +666,18 @@ async function teensy(browser, port, q, name) {
await shot(pg, "sprayer-card-ask");
await fr.locator("#askYes").press("Escape"); await askClosed(fr, what, "Escape");
await expect(fr, what, "#msgWifi", /^$/);
await fr.locator("#pwW").press("Enter"); // Enter in the password: the same question
await askShown(fr, what, /^Save and restart the module\? Its Wi-Fi goes off/, false);
await fr.locator("#askYes").press("Escape"); await askClosed(fr, what, "Escape");
await fr.click('nav button[data-t="firmware"]'); await fr.waitForTimeout(300);
await fr.click("button[onclick*='/api/restart']");
await askShown(fr, what, /^Password of the own Wi-Fi network$/, true);
await shot(pg, "sprayer-card-password");
await fr.click("#askNo"); await askClosed(fr, what, "Cancel");
await expect(fr, what, "#msgProg", /^$/);
await fr.click("button[onclick*='/api/restart']");
await fr.locator("#askIn").press("Enter"); await fr.waitForTimeout(200);
if (!(await fr.locator("#ask").isVisible())) { fail.push(what + ": Enter with no password typed answers the question"); await fr.click("button[onclick*='/api/restart']"); }
await fr.fill("#askIn", "agopengps"); await fr.locator("#askIn").press("Enter"); await askClosed(fr, what, "Enter");
await expectSoon(fr, what, "#msgProg", /^done, restarting/);
}
Expand All @@ -654,6 +694,11 @@ async function teensy(browser, port, q, name) {
await shot(pg, "teensy-card-ask");
await fr.click("#askNo"); await askClosed(fr, what, "Cancel");
await expect(fr, what, "#fwMsg", /^$/);
await fr.focus("#reboot"); // a held Enter on Restart Teensy: one question, not also its answer
await pg.keyboard.down("Enter"); await pg.keyboard.down("Enter"); await pg.keyboard.down("Enter"); await pg.keyboard.up("Enter");
await askShown(fr, what, /^Restart the Teensy\?/, false);
if (await fr.locator("#ask").isVisible()) { await fr.click("#askNo"); await askClosed(fr, what, "Cancel"); }
await expect(fr, what, "#fwMsg", /^$/);
await fr.click("#reboot");
await fr.click("#askYes"); await askClosed(fr, what, "OK");
await expectSoon(fr, what, "#fwMsg", /^Restarting/);
Expand Down
3 changes: 2 additions & 1 deletion Firmware/v4.5_AiO_WEB/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ One row per day, in short; the details are in the sections of this file (the lin

| Date | What changed |
|---|---|
| 13 Nov 2026 | A held Enter no longer answers the page's question too: Enter on a button such as *Restart Teensy* asks once and waits for the answer ([Web page](#web-page)). |
| 12 Nov 2026 | A WAS fault or a silent Keya is told on the program's screen again when its AutoSteer button goes on: on a bench with no WAS the button only went red, with no reason ([WAS check](#was-check)). |
| 11 Nov 2026 | **Keya**: holding the wheel cuts autosteer, by the motor's own current against the program's Current Turn limit ([Keya kickout](#keya-kickout)). After an update, an old page a browser kept fetches the new one by itself ([Web page](#web-page)). |
| 10 Nov 2026 | The UM982's temperature is read (`HWSTATUSA`); one a device does not give reads *not reported* ([Identification](#identification)). Tabs and light/dark button 44 px high, 8 px corners; network name `AiO-xxxx`, with a hyphen ([Web page](#web-page)). |
Expand Down Expand Up @@ -77,7 +78,7 @@ Open `http://<module IP>/`, normally `http://192.168.5.126/` (the third number f

Dark or light: the ☀ / ☾ button at the top (remembered by the browser), otherwise the device's setting.

Every question the page asks (write to the TM171, load the receiver's settings, switch the WAS check off, install firmware, restart…) opens in a box of the page itself, not a browser pop-up: inside an AgOpenWeb web card the browser's pop-ups show nothing, so those buttons did nothing there (official `Docs/ADD_ON_CARDS.md`: the card's frame has no `allow-modals`). The tabs and the light/dark button at the top are 44 px high, with the same 8 px corners as that box's buttons: a size a finger hits on the cab's screen.
Every question the page asks (write to the TM171, load the receiver's settings, switch the WAS check off, install firmware, restart…) opens in a box of the page itself, not a browser pop-up: inside an AgOpenWeb web card the browser's pop-ups show nothing, so those buttons did nothing there (official `Docs/ADD_ON_CARDS.md`: the card's frame has no `allow-modals`). Enter is its OK, but a held Enter answers nothing: the key that pressed a button cannot also confirm it. The tabs and the light/dark button at the top are 44 px high, with the same 8 px corners as that box's buttons: a size a finger hits on the cab's screen.

The page carries the date its own firmware was compiled, and compares it with the one the module reports every second. When they differ, the page on the screen is an old one (seen on 11 Nov 2026 in Firefox after an update: the cards showed the new firmware's data, the top bar was still the old one until a refresh). It then fetches the page again at an address the browser has never seen (`/?b=<build date>`), so no kept copy can answer. If the page that comes is still old, a notice at the top says to reload it by hand (Ctrl+F5). After an update or *Go back* asked from the page, the page takes the module as restarted when its time on went back or is behind where it would be had it kept running: a *Go back* a few seconds after start-up used to leave the page waiting for ever.

Expand Down
3 changes: 2 additions & 1 deletion Firmware/v4.5_AiO_WEB/v4.5_XIAO_ESP32/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Version 4.5, the same number as the Teensy firmware it goes with (v4.5_AiO_WEB).

| Date | What changed |
|---|---|
| 13 Nov 2026 | Loading the page no longer stops the XIAO on a weak WiFi link (the Teensy saw "XIAO silent for 10 s"), and the browser keeps the page: a new visit only asks whether it changed ([What it does](#what-it-does)). |
| 13 Nov 2026 | Loading the page no longer stops the XIAO on weak WiFi ("XIAO silent for 10 s"); the browser keeps the page. Enter in a password field does what its button does ([What it does](#what-it-does)). |
| 11 Nov 2026 | The page checks that it is the page of the program running: after an update an old page a browser kept (old buttons, new data) fetches the new one by itself ([What it does](#what-it-does)). |
| 10 Nov 2026 | Tabs and the light/dark button 44 px high with 8 px corners; the WiFi tab's password to confirm in a card of its own, no longer a field across the page ([What it does](#what-it-does)). Messages give the size of an updated program, not 0 bytes. |
| 9 Nov 2026 | The page's questions (save and restart, install, the module's firmware, subnet) in its own box: the browser's pop-ups show nothing inside an AgOpenWeb web card, so those buttons did nothing there ([What it does](#what-it-does)). |
Expand Down Expand Up @@ -69,6 +69,7 @@ Version 4.5, the same number as the Teensy firmware it goes with (v4.5_AiO_WEB).

- Changes (WiFi, update, restart) **ask for the password of the XIAO's own network**, to confirm.
- The question before each one (save and restart, install, the module's firmware, the subnet) opens in a box of the page itself, not a browser pop-up: inside an AgOpenWeb web card the browser's pop-ups show nothing, so those buttons did nothing there.
- **Enter** in a password field does what its button does, and the question still comes first. On the Firmware tab: the action that asked for the password (*Type the XIAO network password*), else **Install** with a `.bin` chosen, else **Install from GitHub**; never **Restart**. An empty field does nothing, and a held Enter does not also answer the question it opened.
- The tabs and the light/dark button at the top are 44 px high, with the same 8 px corners as that box's buttons: a size a finger hits on the cab's screen.
- On the WiFi tab the password and **Save and restart the XIAO** have a card of their own, *Confirm and save*, as wide as the other two.
- The page carries the date its own program was compiled and compares it with the one the XIAO reports. When they differ (an old page the browser kept after an update), it fetches the page again at an address the browser has never seen (`/?b=<build date>`); if that page is still old, a notice says to reload it by hand.
Expand Down
Loading