Skip to content

Releases: Forged-in-Feathers-Technology/observore

v0.13.0

Choose a tag to compare

@github-actions github-actions released this 03 Oct 19:00
1f25a68

A ninth board, a household census that watches without acting, and two fixes to
touch on the Cheap Yellow Displays.

If you have a CYD and have calibrated its touch: do it again

Any calibration saved before this release is discarded on upgrade. The
arithmetic behind it paired each touch channel with the wrong screen axis, so
every calibration an older build wrote is wrong — presses landing in the wrong
row, the bottom of the screen out of reach. Keeping one would look exactly like
the bug still being there.

The device says so on its serial log and falls back to its compiled bounds,
which work. Run calibrate on the system page once and it is sorted.

This affects all four resistive boards: both 2.8" revisions, the 3.5", and the
new C5.

New board: RockBase NM-CYD-C5

A 2.8" Cheap Yellow Display with an ESP32-C5 behind it — the first board
here that both sees 5 GHz and has a screen to say so on. Every other display
board is a plain ESP32 that cannot sweep the upper band at all, and every board
that could was headless.

It looks identical to the plain-ESP32 2.8" CYD and takes a different image.
Go by the chip marking rather than the shape of the board. The installer checks
the chip underneath, so a wrong-chip profile is refused rather than flashed.

The household census

A stationary device now learns what is always around it — the doorbell, the
printer, your own phones — counted in days, not sightings: three distinct
days inside a sixteen-day window. A doorbell seen four hundred times this
evening has one day to its name.

Nothing is suppressed on the strength of it yet, deliberately. This project
has twice silenced the thing it exists to notice, both times with a judgement
that had never been watched before it was trusted. The census is reportable
first, on /api/status as census: {known, household, days}, and acting on it
is a later change against a table that has had time to be wrong in public.

Smaller things

  • The boot banner names the build: version, board, and which OTA slot it
    booted from. A device that quietly rolled back to its previous slot used to
    look identical to one that was never updated.
  • The watch dial walks on the round AMOLED — eight positions on a
    three-pixel circle, one step a minute — so a panel showing the same bright
    ring for weeks does not keep it. Held still while you are looking at it.
  • On-device touch calibration for resistive panels, so a sheet teaches the
    device its own bounds instead of inheriting one bench unit's measurements.
  • A release is held briefly before a press is over on the C5 CYD, whose
    touch interrupt is not wired, so a dip in the pressure reading cannot split
    one press into two characters.

What's Changed

  • The flasher page looks like the site it belongs to by @ponack in #141
  • Let a panel teach the device about itself by @ponack in #142
  • Ship the C5 Cheap Yellow Display by @ponack in #143
  • Say which build you are by @ponack in #144
  • Let the dial wander so the panel does not keep it by @ponack in #145
  • Learn the furniture, and do nothing with it yet by @ponack in #147
  • Pair each touch channel with the axis it actually runs along by @ponack in #149
  • One finger, one tap, on a panel with no interrupt line by @ponack in #148
  • Do not trust a calibration written by the arithmetic we fixed by @ponack in #151
  • docs: v0.13.0 tag line by @ponack in #150

Full Changelog: v0.12.0...v0.13.0

v0.12.0

Choose a tag to compare

@github-actions github-actions released this 02 Oct 17:46
da453f9

A class that says a device followed you, and two new boards — one round, one that watches both Wi-Fi bands and has a screen to say what it found.

tailing

A follower is a device that is unidentified and persistently nearby, which in a house describes the neighbours. tailing is a follower that was beside you while you were somewhere else, and it alerts on its own, because that is a claim about what something did rather than about what it is.

It took five corrections, and every one came from carrying a board outdoors rather than from reading code:

what I believed was evidence what it actually described
duration everybody in a restaurant
surviving an address rotation every modern phone, every fifteen minutes
the board having moved a walk around one house
the access points changing a town; in a field the only ones for half a mile are your own, and they reach the barn
presence before and after a round trip comes home, so the whole household qualifies

What survives is narrow and measured. A journey counts when the access points fade twelve decibels or two thirds of them go, asked every twenty seconds while walking rather than requiring you to stand still. A device promotes only if, at the far end, it was heard five or more times, at −70 dBm or better, within twelve decibels of its strength before you left.

The last of those timings mattered more than the thresholds: the window used to open when the board was picked up — at the front door, surrounded by everything that lives in the house — and the strongest reading in it was taken on the way out.

Proven on hardware, which the previous release could not say:

desk → kitchen → garage → garden → house
journeys 1 | tailing 1 (−35 dBm, in a pocket) | follower 15 (the house)
score 11 = tailing 6 + a Flipper 3 + the follower class at its cap 2

Only the round board has the accelerometer this needs. The rest report zero journeys for ever and behave exactly as before.

Two boards

Waveshare ESP32-S3-Touch-AMOLED-1.43 — a round 466×466 panel, capacitive touch, a real-time clock, battery sensing and the motion sensor above. It ships with either of two panel controllers, so the firmware asks at boot rather than being built for one; a stranger cannot know which revision arrived in the post. The text grid is the largest square inside the circle, 41×20, and touches in the crescents outside it are clamped in rather than discarded — that glass is live, and the seventy pixels under the button bar are where a finger aiming at it lands.

It also has a watch face, which is a disguise as much as a clock: a device glanced at in a pocket is far less conspicuous showing the time than showing a list of everybody's Bluetooth addresses. The threat level reaches the wearer as the colour of the hour markers and nothing else — a red banner across the face would give away precisely what the disguise was for.

RockBase NM-CYD-C5 — a Cheap Yellow Display with an ESP32-C5 behind it, and the first board here that both sees 5 GHz and has a screen. Built by CI and not offered for download yet: one unit, and its touch calibration is still the default rather than a measurement.

A baseline may not silence a protected class

This one matters to every board. Setting a baseline wrote the most durable rule each device supports, and only one branch of that decision checked whether the class was protected — so anything broadcasting a name of four characters or more walked past it. A Flipper Zero went unreported on two boards for a week.

The cost scales badly, because a name rule matches as a substring: a baseline taken near a body camera would have written AXON BODY 3 and quieted every Axon Body 3 its owner ever walked past. Protected classes are silenced by address alone now, and the baseline says when it has done it.

An existing rule is not deleted by upgrading. If you have baselined a Flipper, a tracker or a body camera, clear it — which you can now do from the glass, with a count on the label and a confirming tap, rather than needing a console the board may not have.

Smaller things

  • Memory. The round board's internal heap floor was three kilobytes. Its Wi-Fi buffers now live in the eight megabytes of PSRAM that was holding nothing but a watch face: floor 18.5 KB, and 99.7% of frames still captured. The same change took the C5 board from 7 KB to 16 KB.
  • The baseline asks twice before silencing a room. It is the most destructive action here and was the only one with no guard — hit twice by accident in a day on a board with no case.
  • The screen lists the heaviest finding first, recency only breaking ties. A crowd of unidentified devices used to push a body camera off the bottom.
  • A promotion announces itself. Findings are reported once, when first identified, so a device promoted to tailing reached the log, the history and the notifier not at all — the most important event this device can produce was visible only to somebody watching the screen.
  • tools/check_ci.sh and tools/flashboard.sh are in the repository, because eight builds were broken for ten commits and the wrong firmware has been flashed to the wrong board three times.

Said plainly

The round board's screen flashed occasionally on battery, and stopped after the memory work. Phantom touches were ruled out by counting them: zero in sixty-five untouched minutes. That is a correlation and not a diagnosis — the failing allocation was never caught in the act.

509 host checks. Nine board profiles build; seven ship.

v0.11.0

Choose a tag to compare

@github-actions github-actions released this 28 Sep 02:40
5528c11

An eighth board with a round screen, a class that needed a motion sensor to exist, and a fix for a baseline that could silence the one thing this device is for.

A baseline may silence your Flipper, not every Flipper

This is the part with users waiting on it, and it affects every board.

Setting a baseline writes the most durable rule each device supports — a name, else an advert shape, else an address — and only the shape branch checked whether the class was protected. The name branch ran first, so anything broadcasting a name of four characters or more walked straight past the check, while the comment above it promised the opposite.

A Flipper Zero went unreported on two boards here for a week, because a baseline had turned Flipper Arala75h into a name rule. The cost scales badly, because a name rule matches as a substring and the interesting classes broadcast model numbers rather than pet names: a baseline taken near a body camera would have written AXON BODY 3 and quieted every Axon Body 3 the owner ever walked past.

Protected classes are now silenced by address and nothing else — the device in front of you goes quiet, another of the same model does not. Tap-to-ignore follows the same rule, since the two paths had drifted into deciding separately and both had the hole.

And the baseline says when it has done it: baseline: 14 ignored, incl 1 hunter on the glass, a protected count on /api/baseline, a sentence in the console. A week is too long to find out.

If you have already baselined a Flipper, a tracker or a body camera, the old rule is still in your device's storage. This stops new ones being written; it does not delete what is there. Clear it in the console's mute list.

A round screen

Waveshare's ESP32-S3-Touch-AMOLED-1.43: 466×466 over four data lines, capacitive touch, 16 MB of flash and 8 MB of octal PSRAM. The detector needed no porting — the S3 was already a shipped target — so the work was the panel, the touch and the shape.

A round display gets no new drawing model, only two insets: the largest square inside the circle is 41 columns by 20 rows, slightly more text than the 2.8" boards manage. Touches outside that square are clamped into it rather than discarded, because the crescents are live glass rather than bezel — and the seventy pixels below the button bar are exactly where a finger aiming at the bar lands.

Two facts that the vendor's wiki got wrong and its source got right: the pin table contradicted itself (SDA and SCL on one GPIO), and the factory firmware logs Install SH8601 panel driver on a board whose own ID register says CO5300. Taking either at face value produces a blank screen.

This board is not in the flasher yet. Waveshare ships it with either of two panel controllers, and this image hardcodes the one in front of me; a stranger with the other variant would get a blank screen and no way to know why. Runtime detection comes first.

tailing: the one claim persistence could never make

A follower in a room is the room — the neighbour's phone through a wall outlasts anything measurable, which is why that class is capped below the verdict and cannot alert. A follower in two places, with a walk in between, is not the room. It came too.

evidence verdict alone
follower persisted in one place clear, class capped at 2
tailing persisted across a journey alert, 6 points

A journey is sustained movement then settling — picked up, carried, put down — counted on arrival rather than departure, with fifteen seconds of carrying minimum so that knocking a desk cannot let the device claim it has been somewhere. It is the only class no signature can produce: the classifier cannot see it, and the tracker promotes into it, one-way.

Your own phone travels with you. A board that has not been told what is yours will report it. Baseline before setting off.

Only the round board has an accelerometer. The other seven report zero journeys for ever, promote nothing, and behave exactly as they did.

Not verified on hardware. The sensor initialises and reports movement on the board; promotion is proven on the host only. Said plainly because shipping a detection nobody has watched fire deserves saying.

Smaller things

  • Capacitive touch needs a bare finger or a capacitive stylus; a passive plastic one registers as nothing at all, correctly and permanently, and looks identical to an idle screen.
  • One shared I2C bus, because three chips sit on two wires and the driver refuses a second bus on one port.
  • A no-LED option, for a board that has none — better than pointing the driver at a pin that is only probably free.
  • tools/flashboard.sh is in the repo now. idf.py ignores -DSDKCONFIG_DEFAULTS when an sdkconfig exists and set-target writes one, so the wrong firmware builds cleanly and the first symptom is a dark screen on a board you just reflashed. It checks and refuses. It has caught that three times.
  • The console stopped painting follower in alert red, which it had not deserved since the class was capped.

458 host checks. Eight board profiles build; seven of them ship.

v0.10.1

Choose a tag to compare

@github-actions github-actions released this 27 Sep 15:06
cf4ec07

A board on a desk in an ordinary room read 99 and alert, permanently. This release is about why, and it took four attempts to get right.

The score is what is here now, not a history of it

The score accumulated. Every device re-added its points every two minutes against a decay of one point a minute in total, so anything worth two points or more outran the decay by itself and the number climbed to its ceiling and stayed. Ninety-nine meant "something persistent has been here a while" — never "how much is here", and never "how serious". A Flipper and a few phones pinned a board at alert indefinitely, and a level that is always on is not read.

The score is now a sum over the devices currently tracked, worked out when asked. It rises when something arrives and falls when it leaves; the table's own thirty-minute expiry brings it down, slowly enough that nothing flickers. The accumulator, the decay and the per-device cooldown are deleted rather than retuned — with a sum there is nothing to throttle, because one device present fifty times is still one device. An empty room now returns to zero, which the accumulator could never do while anything remained.

Unidentified devices cannot move the verdict

The follower class — "unidentified, but persistently nearby" — is capped at two points in total, however many there are. Every one of them is still listed, named and announced. None of them changes what the device says.

Getting there meant being wrong twice about what makes a follower alarming. Duration is not evidence: everybody in a restaurant has been near you for an hour. Rotation is not evidence either, and that one took hardware to see — every modern phone changes its Bluetooth address every quarter of an hour, so "survived a rotation" describes a phone behaving normally rather than a device evading notice. A house full of them put a board at seventeen and alert. Capping the class at the top of caution fixed the number and left the verdict wrong in a quieter way: two boards soaked overnight in an ordinary room sat at exactly the cap for eight hours, amber throughout, with nothing identified on either. A warning that is always on is not a warning.

What remains true is that a follower is, by definition, unidentified. Worth listing, worth a glance, not worth an alarm — the detector cannot say what the thing is.

Six points is one alert

The weights now say what they mean, which fixed a gap nobody had noticed:

was now alone reads
bodycam, ALPR, deauth-flood 5 6 alert
tracker, drone, smart-glasses, hunter 3 3 caution
follower class, total 4 each 2 capped clear

A body camera scored five and alert began at six, so the clearest detection this device can make used to arrive as amber unless something else happened to be in the room with it. The same was true of a plate reader and of a deauthentication flood in progress.

Six names an act: a body camera is recording, a plate reader is reading plates, a flood is knocking devices off a network. Three names a capability — a Flipper, a drone, a tracker are things that could be aimed at you and usually are not — so they read caution alone and alert in company.

Two being less than the gap between caution and alert buys an invariant: no quantity of unidentified devices can carry an identified one over the line. A Flipper in an empty room and a Flipper in a crowded bar both read caution, because the crowd is not evidence about the Flipper. That relationship is a _Static_assert rather than a coincidence between three numbers in two files — raising the cap stops the build, which was verified by doing it.

A false pass that had been available all along

Checking that assertion revealed why the stale-binary trap in this repository keeps working: the host test Makefile listed only .c files as prerequisites, so a header-only change left the old binary in place and make && ./run reported on code that was no longer there. It had already produced one false "this fails without the fix". Headers are prerequisites now.

Known issue: a baseline can still silence a threat class

Setting a baseline writes the most durable rule each device supports, and the name branch runs before the class-protection check. Anything broadcasting a name of four characters or more is silenced regardless of class — including a Flipper, and including a body camera, where the rule matches by substring and would quiet every AXON BODY 3 you ever walk past rather than the one in the room.

Found by a Flipper Zero going unreported on two boards in this release's own soak test. A fix is next: protected classes get an address-specific rule or none, and the baseline says out loud when it has muted one. Until then, check the console's mute list if something you expect to see is missing.

Verified on hardware

The 2.8" and 3.5" Cheap Yellow Displays, side by side through the change: 99 and alert on the accumulator, 17 and alert with presence capped, 5 and caution for eight hours with the class capped at caution, and finally score 2, clear, seven devices listed on each — an hour and a half in, followers re-formed, sitting at the ceiling and going nowhere.

439 host checks. All seven board profiles build.

v0.10.0

Choose a tag to compare

@github-actions github-actions released this 26 Sep 14:36
7bdd5de

Until now everything this device recognised was equipment that watches. This release adds the other kind, and a screen that dims itself.

Equipment that transmits at other radios

A deauthentication flood is the most actionable thing on Wi-Fi a detector can see: it is how a handshake is forced into the air to be captured, and how a camera goes offline shortly before something happens in front of it. Eight such frames naming one address inside ten seconds is a flood; one is an access point doing its job. Scored at five, reported at most once a minute.

Two limits are stated rather than glossed. It names a victim, not a culprit — an attacker spoofs the access point's address, so what is reported is the address the flood was sent as, and inventing an attribution the air does not carry would be worse than saying less. And the sniffer sits on one channel for a few seconds at a time, so a flood elsewhere in the band is missed entirely. This finds what passes under the aerial and claims nothing about what does not.

Hunter gear — a Flipper Zero, a pwnagotchi, a WiFi Pineapple — scores three rather than five, because the presence of a tool is capability where a flood is an act.

One footnote worth keeping: Flipper's Bluetooth company identifier is 0x0E29. The widely copied 0x0FBA belongs to Cosonic, who make headsets, so every project carrying that constant reports their customers as hacking tools. There is a test here asserting 0x0FBA is not a Flipper.

Things that were never threats

Another detector is recognised. SquachWatch announces itself in mesh mode so two of them can find each other; one is reported as peer-detector and scored at nothing. Another detector in the room is a fact about the room, not a threat in it — a class that moved the score would make a meetup read as an incident.

Household fixtures are named. A solar gateway broadcasting Envoy / 1219… from a fixed address was arriving as a follower, a class defined as "unidentified but persistently nearby", when it is neither. Enphase equipment is now a fixture worth nothing, and naming beats muting: an ignore rule hides a thing, a class explains it, and the explanation survives a Clear ignores.

More generally, anything publishing both a fixed public address and a stable name has opted out of being hard to identify, so persistence alone says less about it. Those now need an hour before the follower heuristic promotes them, against five minutes for a nameless or rotating device. A longer span rather than an exemption, deliberately: a cheap tracker with a fixed name from a public address would still be following you.

Earbuds are not trackers. Every Fast Pair device in pairing mode used to arrive as a tracker worth three points, which is how a crowded cafe reads as four trackers and how a person learns to stop believing the score. A Fast Pair advert of exactly three bytes is the discoverable frame — a model ID — and is now an accessory at one point. Everything else under that UUID keeps full tracker weight, because no byte in it reliably tells a tag from a headphone and the expensive mistake is the other direction. Nothing is dropped; only weighted.

A screen that dims itself

The Cheap Yellow Displays carry a photoresistor, found while looking for a battery sense that does not exist. The backlight gains an Auto setting that uses it, and Auto is the default where the sensor is wired: a bright panel in a dark room announces the device, and asking a person to remember to dim it defeats the point.

It learns its own range. Fixed thresholds taken from a bare board were useless on a board in a case — the room produced 1,157 to 1,298 counts against edges at 1,250 and above, so two of the four steps could never be reached and the screen simply stayed bright. Now the extremes seen are remembered and the steps divide the range the board actually experiences.

Enabled on the 3.5" board, where it was measured. The 2.8" boards are documented to have the same sensor on the same pin, but that has not been checked here and nothing is enabled on a board that has not been tested.

Two bugs, both found by testing the above

Retirement did not survive a reboot. The safety net added in v0.9.1 kept its statistics in RAM, so every restart un-retired every over-broad ignore rule and the device went deaf again until each re-earned its nine addresses — hours, on a quiet night, and a device that reboots for each update would have spent part of every day blinded. The verdict is now written down; the counters still are not.

Ignoring something left it on the screen. Muting suppresses what arrives next and said nothing about what was already in the device table, which sat there for half an hour — so the thing just dismissed stayed in front of you, which reads as the ignore having failed. Adding a rule now sweeps the table.

Not verified against hardware

Said plainly, because shipping detection nobody has watched fire deserves saying. The Flipper signature was confirmed against a real unit — recognised by service UUID at −43 dBm, scored three, shown on the glass. pwnagotchi, Pineapple and the deauth flood were not: there is no such gear here, and transmitting deauth frames to test the third is not something this project will do. All three have host tests and constants taken from published sources rather than guessed.

Sizes

Measured on the binaries attached here, against v0.9.1.

board size change
cyd-3248s035r-st7796 1,294,672 +6,928
cyd-2432s028r-ili9341 1,289,680 +2,144
cyd-2432s028r-st7789 1,289,168 +2,144
devkit-esp32c5 1,535,424 +2,272
xiao-esp32c5 1,487,616 +2,272
xiao-esp32c6 1,436,464 +2,272
xiao-esp32s3 1,177,472 +2,064

Installing

From the console, where Check now asks straight away — or from the system page on a board with a screen. Otherwise observore.forgedinfeatherstechnology.com. Settings and ignore rules come with you.

v0.9.1

Choose a tag to compare

@github-actions github-actions released this 26 Sep 02:53
fa0b28c

If you are running v0.9.0 and have pressed Set baseline, your detector may be deaf. This release fixes that, and tells you whether it happened.

What went wrong

A baseline writes a rule for everything in range so that the device stops reporting what you already know about. For a device with no name and a rotating address, it used the shape of the advert — which fields are present, the company ID, the service UUID. That shape is not a device. It is a model, and often not even that: plenty of phones, watches and earbuds broadcast the same shape as each other.

So one press could write a rule that silenced a whole population of devices, including ones that walked in afterwards. Measured on two boards two metres apart in the same house:

ignore rules sightings admitted
a C5 with no recent baseline 60, mostly addresses 8,348 in 2.5 hours
a 2.8" CYD after one baseline 46, of which 21 shapes 25 in 1.9 hours
a 3.5" CYD after one baseline 34, of which 20 shapes 6 in 4 hours, 192,754 discarded

Throughout, both CYDs reported clear, which is exactly what a quiet room looks like. Nothing on the screen or in the console distinguished the two.

What changed

A shape has to belong to one device. A fingerprint rule is written only where exactly one device in range carries that shape at the time. Two devices sharing it means it describes a model, and both are muted by address instead — which can only ever silence one device each.

A name has to be a name. Name rules match as substrings, which is right when a person types Ring and wrong when a baseline takes whatever it hears. The SSID 42 had matched 102 different addresses here. A baseline now needs at least four characters before it will write a name rule.

Every rule says what it has done. The Ignored panel, and /api/mutes, now show how many sightings each rule has discarded and how many distinct addresses it has covered. A fingerprint rule that has covered more than eight addresses is describing a population, so the device stops honouring it and says so in the log. That is the net for what the other two changes cannot see: a shape shared with devices that were not in the room when you pressed the button.

After you update

Open the Ignored panel and read the new reach figures. Anything covering an implausible number of addresses should go — the × beside it removes it.

Over-broad shape rules retire themselves from now on. Over-broad name rules do not, deliberately: muting a brand by name is something people do on purpose, and the device cannot tell a considered choice from a harvested fragment. If you see a short name rule with a hundred addresses against it, that is one to delete by hand.

Why the tests did not catch it

The test written when this feature shipped proved that a baseline wrote a rule of the right kind. It never asked whether the device could still detect anything afterwards. The new tests do: a device of the same model arriving after the baseline must still be heard, and an over-broad rule must retire itself. All four fail against the old code.

Sizes

Measured on the binaries attached here, against v0.9.0.

board size change
cyd-3248s035r-st7796 1,287,744 +1,040
cyd-2432s028r-ili9341 1,287,536 +1,040
cyd-2432s028r-st7789 1,287,024 +1,040
devkit-esp32c5 1,533,152 +1,104
xiao-esp32c5 1,485,344 +1,104
xiao-esp32c6 1,434,192 +1,104
xiao-esp32s3 1,175,408 +1,056

Installing

From the console, where Check now asks straight away — or from the system page on a board with a screen. Otherwise observore.forgedinfeatherstechnology.com. Settings and ignore rules come with you.

v0.9.0

Choose a tag to compare

@github-actions github-actions released this 25 Sep 21:43
c9e5287

A seventh board, a screen you can set up and operate without a browser, and the first measured battery life.

The screen stands on its own

Until now a board with a panel could show you what it saw and very little else. Everything it needed doing was done from a console you reached over the network — which you could only reach if you already knew the address, which it only ever printed to serial.

The network page fixes the first half. It lists what the last patrol scan saw, and tapping one opens a keyboard for its password. The password is typed blind: the key you press is shown, because a keyboard that does not say what it registered is unusable on a resistive panel, but the field itself shows dots. A show button reveals it for fifteen seconds and turns the field amber while it does. Nothing is kept afterwards.

The system page fixes the rest. It now says where the console is — the one fact on that screen a person has to take somewhere else, and on these boards there was nowhere else to read it. The password is still never drawn; a screen faces a room, and an address on a local network is a different kind of secret. Beside it: what the last version check found, a check for updates button that asks straight away, and an install button that appears only when there is something to install and asks a second time before it starts.

And on the watch page, touching a finding offers to ignore it. A second tap writes the same rule the console would — the device's broadcast name where it has one, its address otherwise. Deliberately not its advert fingerprint: that identifies a kind of device rather than an individual, and muting your own tracker that way would silence a stranger's.

Both confirmations lapse after five seconds and are cancelled by changing page, so a press nobody meant does nothing.

The 3.5" Cheap Yellow Display

cyd-3248s035r-st7796 — the ESP32-3248S035R, 480x320 where every screen before it was 320x240. The panel geometry is a build option now, and the text grid, the pages and the keyboard all follow it: sixty columns by twenty rows here against forty by fifteen, with wider keys and an action strip that gets a second row and a margin because the screen can spare them.

The resistive R model only. The capacitive C variant carries a different controller on a different bus; the flasher entry says so rather than letting anyone flash this and find a dead panel.

Bringing it up found three things that were wrong for every board, not just this one. Touch pressure was measured from one of the controller's two Z channels, and that channel scales with position — the button bar on this board sits where it reads small, so firm presses measured about 130 against a threshold of 300 and were discarded, on a panel that was working perfectly. The action strip sat directly against the button bar, a sixteen-pixel target with no margin. And calibration bounds taken from a press short of an edge clamp the rows nearest it together, so the keyboard's bottom row, the action strip and the bar all answered as one.

25.1 hours

The run record added in v0.8.1 exists to answer one question, and now it has. An ESP32-C5 devkit on a 2,000 mAh cell, on v0.8.3, untouched from a full charge until the cell's protection cut off: 90,311 seconds — 25.1 hours, filed as ended by power-on.

That is with the radio at full duty, which is the only way this device works: no light sleep, a passive dual-band scan and a channel sweep every two minutes, and the Bluetooth scanner running throughout. About 80 mA average, so a pack's capacity in milliamp-hours over eighty is a fair guess at hours for any other cell.

It took three attempts. Two runs ended because the firmware was reflashed mid-run, and a third gave six hours because that build sampled the heap a hundred times a second and never let the chip idle. That six-hour run sitting among the twenty-five-hour ones in the same list is the argument for keeping the record.

Sizes

Measured on the binaries attached here, against v0.8.3.

board size change
cyd-3248s035r-st7796 1,286,704 new
cyd-2432s028r-ili9341 1,286,496 +4,528
cyd-2432s028r-st7789 1,285,984 +4,528
devkit-esp32c5 1,532,048 +976
xiao-esp32c5 1,484,240 +976
xiao-esp32c6 1,433,088 +976
xiao-esp32s3 1,174,352 +896

Installing

From the console, where Check now asks straight away — or, on a board with a screen, from the system page without a browser at all. Otherwise from observore.forgedinfeatherstechnology.com, which now offers seven boards. Settings come with you either way.

v0.8.3

Choose a tag to compare

@github-actions github-actions released this 24 Sep 15:35
c0c9ff3

A fix for v0.8.2, which left the Cheap Yellow Display unable to check for its own updates.

If you are on v0.8.2 with a CYD, you need a cable once

v0.8.2 left a board with no PSRAM about twelve kilobytes short of internal memory during an uplink window, and that is the memory the TLS handshake behind a version check needs. Every check failed, reporting that it could not connect. Installing requires a successful check first, so a CYD on v0.8.2 cannot update itself at all.

Flash this release over USB from observore.forgedinfeatherstechnology.com — your settings and ignore rules are in NVS and come with you. From v0.8.3 onward the device updates itself again.

No other board is affected. The C5, C6 and S3 have PSRAM or no screen, and they updated over the air from v0.8.1 to v0.8.2 without trouble.

What was wrong and what it measures now

The screen work in v0.8.2 took memory in four places, all of them generous rather than necessary: two task stacks, the snapshot the drawing task renders from, and — the largest single piece — the console's scratch buffer, which is held for the whole uplink window and so competes directly with the handshake.

free heap, uplink window low-water
v0.8.1 33.8 KB —
v0.8.2 21.6 KB 10.9 KB
v0.8.3 34.8 KB 27.1 KB

The console now reports a shorter device list per request on a board without PSRAM, which is the visible cost and a smaller one than a device that cannot update.

A failed check also logs the free heap alongside the error now. "Cannot connect" on its own points at the network, and that is very nearly where this went.

Why it was not caught

The heap drop was visible when the screen build was first flashed, and noted at the time, and then the release went out without anyone asking the affected board to check for an update. Patrol mode has twenty kilobytes more free memory than an uplink window, so nothing looked wrong in the place it was easiest to look. The README now says to measure a display build during an uplink window, and that is the test this release was verified with before it was tagged.

Sizes

Measured on the binaries attached here, against v0.8.2. The increase is the extra logging; the memory this release gives back is runtime, not flash.

board size change
cyd-2432s028r-ili9341 1,281,968 +80
cyd-2432s028r-st7789 1,281,456 +80
devkit-esp32c5 1,531,072 +80
xiao-esp32c5 1,483,264 +64
xiao-esp32c6 1,432,112 +80
xiao-esp32s3 1,173,456 +80

Installing

CYD owners on v0.8.2: over USB, as above. Everyone else: from the console, where Check now asks straight away rather than waiting for the daily check.

v0.8.2

Choose a tag to compare

@github-actions github-actions released this 24 Sep 11:59
3489819

The screen answers back: three buttons, a page about the device itself, and a backlight that dims.

Touch on the Cheap Yellow Display

The board has carried a touch controller since the port, unused. Now the bottom of the screen is a bar of three buttons.

page moves between the watch page and a new system page: version, board, uptime, what has been seen, free memory, and how long the previous runs lasted with how each one ended. That last part answers a question the device could not answer about itself until this month — a run that ended in brownout or power-on on a device running on a battery is the battery's life, measured rather than guessed.

baseline does what holding the physical button does, and says so on the screen. The work happens in the main loop, which can be most of a minute away inside a scan, so it reports twice: once that it heard you, once that it is done.

light steps the backlight through four levels and remembers the choice. A 2.8" panel at full brightness is a beacon in a dark room, which is the wrong thing for this device to be, and one that comes back from a power cut at full brightness at three in the morning has told the room something.

Nothing about the screen's two rules changed. Nothing on it is gated, and the console password is still never drawn on it. Someone who can press the glass is already standing in front of it; the authentication belongs to the network-facing console.

The screen no longer freezes while the radio works

Drawing moved to a task of its own. The main loop spends about thirty seconds of every patrol cycle inside a blocking scan, and until now the screen was redrawn from that loop — so a finger on the glass could go unanswered for half a minute. The loop now publishes a status and the drawing task renders it on its own clock. The same change makes the watch page tick over during a scan instead of standing still.

Calibrating a panel that is not this one

Three things vary between assemblies and all three are build options: the bounds of the resistive sheet, whether its axes are crossed relative to the landscape display, and which way each one runs. Build with CONFIG_OBSERVORE_TOUCH_LOG_RAW=y, press the screen, read the values off the log.

Check the crossing first, and check it with a press to the left and then a press to the right at the same height. That single move swings one raw axis across its whole range while the other barely stirs. Corner presses will not tell you: a corner moves both axes at once, and two corners sharing an edge look exactly like a dead axis. They were read that way here, through a board swap and a hunt for a fault that did not exist, before one deliberate left-right press settled it in ten seconds. The README says so, in the hope that the next person keeps the day.

Sizes

Measured on the binaries attached here, against v0.8.1.

board size change
cyd-2432s028r-ili9341 1,281,888 +10,544
cyd-2432s028r-st7789 1,281,376 +10,528
devkit-esp32c5 1,530,992 +16
xiao-esp32c5 1,483,200 +16
xiao-esp32c6 1,432,032 +16
xiao-esp32s3 1,173,376 +48

The boards without a panel are unchanged, as they should be: touch and the screen compile out entirely.

Installing

From the console if you are on v0.7.0 or later — Check now in the Firmware panel asks straight away rather than waiting for the daily check. Otherwise from a browser at observore.forgedinfeatherstechnology.com; settings come with you either way.

v0.8.1

Choose a tag to compare

@github-actions github-actions released this 20 Sep 21:54
59e65fc

A check-for-updates button, a memory of how long each run lasted, and a console that is quiet when nobody is looking.

Check now

Three releases in a row were noticed only after a reboot, because the daily check had already run that morning and the release came out in the afternoon. The Firmware panel now has Check now: it asks in the window the console is already holding open, and the answer appears in the same panel a few seconds later, with when it was last checked. A check that fails to connect is tried once more five seconds later before it says so — on the bench the first attempt seconds after the uplink came up did exactly that, and the second did not. POST /api/update/check is the same thing without the button.

This release is the first one a device found on its own: a C5 on v0.7.1, seventy-two hours into an unattended run, reported v0.8.0 at its scheduled check with nobody touching it. It is also the last one that will need waiting for.

How long the last run lasted

The header now says how long the previous run lasted and how it ended, and hovering it lists the eight before that. The device writes its uptime to flash every five minutes; at boot, the last value written is the length of the run that just ended, filed with the reason this boot happened. A run shorter than five minutes leaves nothing behind, which is right — that was a false start, not a run.

This is the battery question answered from measurement. A device found up for seven hours after a night on battery used to say only that; the run before it was gone. Now a run that ended in power-on or brownout on a device that was on battery is the battery's life, to within five minutes, and the runs before it say whether that is getting worse. The same list is on /api/status under runs. The flash cost was checked and is stated in the source: a lifetime measured in centuries, so this is not the thing that wears out.

A tab in the background is quiet

The console polls only while it can be seen. A tab left open in the background used to reconnect every uplink window, ask for everything, and stop reading, and six stalled responses in the network stack's send buffers is how a browser tab once took the device down. A hidden tab now sends nothing — which also means the device is not held on the uplink by a page nobody is looking at, and gets back to detecting. Switching back to the tab refreshes it at once.

A consequence worth knowing: a visible tab still holds the window open, up to its ceiling, and for a week that is what made the console feel always-on. It was in fact keeping the device on the uplink most of the time. Without a tab open the device is reachable for its thirty-second window every two and a half minutes, and the page, once loaded, keeps it there while you read.

Sizes

Measured on the binaries attached here, against v0.8.0.

board size change
cyd-2432s028r-ili9341 1,271,344 +1,808
cyd-2432s028r-st7789 1,270,848 +1,824
devkit-esp32c5 1,530,976 +2,128
xiao-esp32c5 1,483,184 +2,144
xiao-esp32c6 1,432,016 +2,128
xiao-esp32s3 1,173,328 +1,840

Installing

From the console if you are on v0.7.0 or later. Otherwise from a browser at observore.forgedinfeatherstechnology.com; settings come with you either way.