Repository navigation
improving fetch stability and speedup rendering - #104
Open
lmoiseichuk wants to merge 12 commits into
Open
lmoiseichuk wants to merge 12 commits into
lmoiseichuk wants to merge 12 commits into
Conversation
Every length in radar_theme.h was a constexpr tuned for a 240x240 panel. They are now variables that initMetrics() computes from tft.width() and tft.height(), scaled against a 240 reference, so a different panel gets the same design rather than a second set of hand-tuned constants. Values that are not lengths -- the ring count, the track horizon in seconds, the reference range in km, plain ratios -- stay constexpr. The k-prefixed names and their storage in the header keep the shape the colours in this file have always had, so no call site changes. status_screens.cpp gets the same treatment: centre, spinner radius, text panel width, line gaps and VLW point sizes all scale. No behaviour change on the 240x240 build. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Selected with -DPLANE_RADAR_PANEL_GC9B72, env supermini_gc9b72. The default supermini env and the GC9A01 wiring are untouched. LovyanGFX gained Panel_GC9B72 in 1.2.28, so the lib_deps floor moves up from ^1.2.7. The panel needs a backlight line, which the 1.28" modules tie on at the module: it sits on GPIO5 through Light_PWM so it can be dimmed if the 3V3 rail sags under WiFi transmit -- a 2.1" backlight draws several times what the 1.28" one does. LEDC channel 0, because the C3 has only six channels and the 7 used in most LovyanGFX examples is out of range on it. The 360x360 frame buffer is 259,200 B at 16 bits, which does not fit on an ESP32-C3 once WiFi is up -- measured, 188,404 B largest free block. The frame sprite now walks a depth ladder and lands on 8-bit RGB332 at 129,600 B. There is deliberately no 4bpp rung: LovyanGFX has no RGB-to-palette-index converter, so a blended write to a palette sprite goes through copy_bit_affine and corrupts every antialiased primitive we draw, fillSmoothCircle and drawWideLine among them. RGB332 keeps them. Unlike the GC9A01 modules this panel must not be inverted; its init sequence does not set inversion itself. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Both were hard-coded off. They are now per-panel pins in config.h, defaulting to -1 on the GC9A01 where neither is broken out. kDisplayPinMiso feeds LovyanGFX's bus config. It makes the panel readable, which the antialiased primitives need on the direct-to-panel path taken when no frame sprite fits, and it silences the "SPI Does not have default pins on ESP32C3" error that -1 provoked at every boot. kDisplayPinTe is polled by displayWaitForFrameStart() before each pushSprite. No SPI panel in LovyanGFX consumes TE -- getScanLine() returns -1 for all of them -- so the edge is caught here instead. A full 360x360 frame is 259,200 B on the wire, roughly 104 ms at 20 MHz, which is long enough to tear without it. The wait gives up after 25 ms so a miswired line cannot stall the main loop. Also drops the ESP32-C6 material from the README. It was documenting hardware that is not supported yet and needs a PlatformIO platform this project does not use; the wiring table is now the one board that works. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Brings in upstream's ADS-B pipeline, dead reckoning at 4 Hz, the TLS fix, the portal re-render and the RGB332 frame buffer. One conflict, in ensureFrameSprite(): this branch tried 16bpp first and fell back to 8bpp; upstream (MatixYo#100) now always uses RGB332 to free the frame's 57 KB at 240x240. Resolved in upstream's favour -- one byte a pixel on every panel, which at 360x360 is 129,600 B instead of 259,200 -- keeping this branch's fallback to drawing straight to the panel when even that does not fit, and its note on why no palette depth is tried. Both supermini and supermini_gc9b72 build. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DL3fMjXoiVk9VGMHmQkfyW
MatixYo#94 (e23b82c) stopped a TLS retry storm by replacing performGetWithPoll with a single GET and a 5000 ms connect timeout. When main was merged into MatixYo#90's branch (dc969f6, "Merge branch 'main' into feature/adsb-pipeline"), the conflict was resolved by keeping performGetWithPoll, and MatixYo#90 took it back to main (78958a5). It calls GET() again every 5 ms until a 6000 ms deadline whenever the result is CONNECTION_REFUSED or NOT_CONNECTED. A TLS setup that cannot allocate its record buffers comes back that way. mbedtls_ssl_setup fails with -32512 (SSL - Memory allocation failed), WiFiClientSecure::connect returns 0, and HTTPClient reports a refused connection. Nothing frees heap between attempts, so the same allocation fails every 5 ms until the deadline. The storm fires whenever the TLS setup cannot get its heap, or a connect is refused. On the 240x240 board the heap is there and it stays latent; it was seen on a fork driving a 360x360 panel, whose larger frame buffer leaves less heap. There each fetch repeated the failed allocation about 170 times, gave up after 6 s with "adsb: HTTP -11", and logged about 900 allocation failures a minute. Go back to MatixYo#94's behaviour: one GET with the 5000 ms connect timeout. A failed connect is retried by the next fetch, kAdsbFetchIntervalMs later. The background task, streaming parse and chunked framing from MatixYo#90 are unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DL3fMjXoiVk9VGMHmQkfyW
The fetch task was given 16384 B of stack. Its high-water mark after successful fetches on an ESP32-C3 left 7,648 B of that unused, so a fetch -- TLS handshake, chunked body, filtered parse -- peaks at about 8.7 KB. A fetch whose connect fails peaks lower, at about 7.8 KB. The stack is allocated from the same heap the fetch's TLS connection needs for its two 16,717 B record buffers, so 7.6 KB of slack there is 7.6 KB less for TLS. Give the task 12288 B: about 3.5 KB over the measured peak, and 4 KB back to the heap. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DL3fMjXoiVk9VGMHmQkfyW
drawLargeAirportRunways measured the distance to each runway's airport on every frame: a cosf and a sqrtf per runway, for 1,706 runways, all soft-float on the ESP32-C3. At the default location that is 1,701 distances a frame for the one airport in range, and on a 360x360 panel it took 71 ms of a 233 ms frame. Work out the square around the range once per frame, in the airport table's own units of 1e-7 degree, and compare each airport with it before measuring: an airport outside the square is out of range, and integer comparisons say so. Only the few inside it are measured, by the same code as before, so the same airports are drawn. A margin of 0.001 degree keeps rounding from turning away an airport the distance test would take. Measured on the board: the runway overlay takes 17 ms instead of 71 at the 25 km range and 28 ms instead of 82 at 10 km, and a frame 178 ms instead of 233; one airport passes the square, and is measured once a frame. The airports in range are the same as before in 35 cases -- five locations, both sides of the 180th meridian among them, at the four preset fetch radii and at 100, 200 and 700 km -- and in 478 more with an airport a millionth of the radius inside or outside the circle's north, south, east or west point. Without the margin, 81 of those 478 would have been turned away. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DL3fMjXoiVk9VGMHmQkfyW
On a 360x360 panel the frame sprite is 129,600 B even at 8bpp. With it, WiFi, the LAN config portal and the fetch task, about 48 KB of heap was free before each ADS-B fetch, in no block over 29 KB. The TLS connection allocates two 16,717 B record buffers and peaks at about 52 KB, so every fetch failed in mbedtls_ssl_setup with -32512 and no aircraft were shown. Cap the frame sprite at what the 240x240 panel's full frame takes, 57,600 B. A panel whose frame is larger is composited in the fewest equal horizontal bands that fit: three of 120 rows, 43,200 B, at 360x360. Each band has the whole radar drawn into it -- grid, runways, aircraft and labels, clipped to the band -- and is pushed complete, so every pixel still goes straight from the old frame to the new one and nothing flickers. The aircraft are placed once per frame, so one that straddles two bands is drawn at the same point in both. Rendered both ways on the board, a banded frame matched a full one on all 129,600 pixels, with aircraft, tags and dots across both seams. The 240x240 panel's frame fits the cap, so it keeps its full-frame sprite and draws exactly as before, as one band. The placement is static, which costs 1,792 B of RAM on both panels. It has to outlive every band's grid drawing, and held on the loop task's stack it cut that task's spare stack from 5,900 B to 2,972 B. On the 360x360 board, all 88 fetches in ten minutes succeeded, in 1.6-1.9 s each. Before each: 131-134 KB free, largest block 69,620 B (144 KB and 122,868 B before the first). The free heap fell by about 3 KB over the first two minutes and then held at 131.1 KB for the other eight; the lowest it reached was 77,460 B. Banding costs render time. With no aircraft and the fetch idle, a banded frame takes 210 ms, where the full frame took 178 ms at the parent commit -- also with none, as no fetch succeeded there. Aircraft are drawn into every band: 26-29 ms for three, about 35 ms for four. Between fetches a frame stays under 246 ms, inside the 250 ms redraw interval, so the radar still redraws at about 4 Hz. While a fetch runs, its TLS handshake and parse share the ESP32-C3's one core with the render, and a frame takes 300-380 ms on average and up to 460 ms. Over two-minute runs the radar drew 3.5-3.6 frames a second. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DL3fMjXoiVk9VGMHmQkfyW
MatixYo#94 (e23b82c) stopped a TLS retry storm by replacing performGetWithPoll with a single GET and a 5000 ms connect timeout. When main was merged into MatixYo#90's branch (dc969f6, "Merge branch 'main' into feature/adsb-pipeline"), the conflict was resolved by keeping performGetWithPoll, and MatixYo#90 took it back to main (78958a5). It calls GET() again every 5 ms until a 6000 ms deadline whenever the result is CONNECTION_REFUSED or NOT_CONNECTED. A TLS setup that cannot allocate its record buffers comes back that way. mbedtls_ssl_setup fails with -32512 (SSL - Memory allocation failed), WiFiClientSecure::connect returns 0, and HTTPClient reports a refused connection. Nothing frees heap between attempts, so the same allocation fails every 5 ms until the deadline. The storm fires whenever the TLS setup cannot get its heap, or a connect is refused. On the 240x240 board the heap is there and it stays latent; it was seen on a fork driving a 360x360 panel, whose larger frame buffer leaves less heap. There each fetch repeated the failed allocation about 170 times, gave up after 6 s with "adsb: HTTP -11", and logged about 900 allocation failures a minute. Go back to MatixYo#94's behaviour: one GET with the 5000 ms connect timeout. A failed connect is retried by the next fetch, kAdsbFetchIntervalMs later. The background task, streaming parse and chunked framing from MatixYo#90 are unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DL3fMjXoiVk9VGMHmQkfyW
The fetch task was given 16384 B of stack. Its high-water mark after successful fetches on an ESP32-C3 left 7,648 B of that unused, so a fetch -- TLS handshake, chunked body, filtered parse -- peaks at about 8.7 KB. A fetch whose connect fails peaks lower, at about 7.8 KB. The stack is allocated from the same heap the fetch's TLS connection needs for its two 16,717 B record buffers, so 7.6 KB of slack there is 7.6 KB less for TLS. Give the task 12288 B: about 3.5 KB over the measured peak, and 4 KB back to the heap. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DL3fMjXoiVk9VGMHmQkfyW
drawLargeAirportRunways measured the distance to each runway's airport on every frame: a cosf and a sqrtf per runway, for 1,706 runways, all soft-float on the ESP32-C3. At the default location that is 1,701 distances a frame for the one airport in range, and on a 360x360 panel it took 71 ms of a 233 ms frame. Work out the square around the range once per frame, in the airport table's own units of 1e-7 degree, and compare each airport with it before measuring: an airport outside the square is out of range, and integer comparisons say so. Only the few inside it are measured, by the same code as before, so the same airports are drawn. A margin of 0.001 degree keeps rounding from turning away an airport the distance test would take. Measured on the board: the runway overlay takes 17 ms instead of 71 at the 25 km range and 28 ms instead of 82 at 10 km, and a frame 178 ms instead of 233; one airport passes the square, and is measured once a frame. The airports in range are the same as before in 35 cases -- five locations, both sides of the 180th meridian among them, at the four preset fetch radii and at 100, 200 and 700 km -- and in 478 more with an airport a millionth of the radius inside or outside the circle's north, south, east or west point. Without the margin, 81 of those 478 would have been turned away. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DL3fMjXoiVk9VGMHmQkfyW
The three ADS-B fixes as they go upstream -- one connect attempt per fetch, the fetch task's stack sized from its use, and the square gate in front of the airport distance test -- rebuilt on upstream/main. This main already had the same patches on top of the GC9B72 work, so the merge leaves the tree unchanged and rewrites neither history. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DL3fMjXoiVk9VGMHmQkfyW
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
ADS-B fetch: one connect attempt again, a stack sized to its use, and no per-frame airport maths
Fixes #…
1. Make one connect attempt per ADS-B fetch again
#94 replaced the retrying
performGetWithPoll()with a single attempt and a 5 sconnect timeout. #90's branch took main in at dc969f6 and resolved the conflict
by keeping
performGetWithPoll(), and #90 brought it back to main at 78958a5.It retries
http.GET()every 5 ms until a 6 s deadline whenever the connect isrefused -- and a TLS setup that cannot get its buffers (
mbedtls_ssl_setup,-32512) comes back as "connection refused".
HTTP -11after the 6 s deadlineLatent on the 240x240 board; it fires whenever a connect is refused or TLS
cannot get heap. Seen on a fork driving a 360x360 panel, where it failed every
fetch.
2. Size the ADS-B fetch task's stack from its measured use
For scale: Arduino-ESP32 2.0.14's mbedTLS (
MAX_CONTENT_LEN=16384) allocates two16,717 B record buffers per connection, and a whole fetch peaks at ~52 KB.
3. Skip measuring airports outside the square around the range
drawLargeAirportRunwaysmeasured every runway's airport on every frame -- acosfand asqrtffor each of 1,706 runways, in software floating point onthe ESP32-C3: 1,701 distances a frame for the one airport in range at the
default location. Now the square around the range is worked out once per frame
in the table's 1e-7 degree units; an airport outside it is skipped with integer
comparisons, and the few inside are measured by the unchanged code.
Same airports in range as before in 35 cases (five locations, both sides of the
180th meridian included, at the preset fetch radii and 100/200/700 km) and in
478 more with an airport a millionth of the radius inside or outside the
circle's edge. +21 lines, no existing line changed.
Tested
ESP32-C3 SuperMini, espressif32@6.5.0 / Arduino-ESP32 2.0.14. Each commit applies
cleanly to main 139a3ac and builds
supermini. Board measurements were taken on a360x360 build (a fork), since no 240x240 board was at hand; the airport and stack
changes don't depend on the panel. On that fork, with these commits, the fetch
ran 88 of 88 times over ten minutes with 0 TLS failures, and free heap levelled
off.