summaryrefslogtreecommitdiffstats
path: root/_log
diff options
context:
space:
mode:
authorSadeep Madurange <sadeep@asciimx.com>2026-06-05 21:00:02 +0800
committerSadeep Madurange <sadeep@asciimx.com>2026-06-12 19:49:59 +0800
commita8d7508006db8309550755a72719de83bde3cdd9 (patch)
treef641b17405c674d3799ebca1f135538bfc930b7d /_log
parent293923a31d434c37cae454a4f1d9977772d11f02 (diff)
downloadwww-minimalist.tar.gz
Improve writing.minimalist
Diffstat (limited to '_log')
-rw-r--r--_log/2d-geometry-kernel.md39
-rw-r--r--_log/arduino-due.md52
-rw-r--r--_log/bumblebee.md13
-rw-r--r--_log/e-reader.md40
-rw-r--r--_log/etlas.md38
-rw-r--r--_log/fpm-door-lock-rf.md46
-rw-r--r--_log/neo4j-a-star-search.md6
7 files changed, 88 insertions, 146 deletions
diff --git a/_log/2d-geometry-kernel.md b/_log/2d-geometry-kernel.md
index fbdf842..5275ad8 100644
--- a/_log/2d-geometry-kernel.md
+++ b/_log/2d-geometry-kernel.md
@@ -1,37 +1,34 @@
---
-title: 2D geometry kernel for building design
+title: Geometry kernel for building design
date: 2022-07-31
layout: post
---
Written in 2026, backdated to 2022.
-Joined real estate firm mid-migration (C# to Java). Java version lacked a
-geometry kernel; project stalled.
+Joined real estate firm mid-migration from C# to Java. Lacking a geometry
+kernel, the project had stalled.
-Geometries were small, mostly 2D—no frame budget or low-latency constraints.
-Architects supplied test cases and tolerances; numerical parity with Rhino was
-mandatory.
+Building geometries are small—mostly 2D. No frame budgets or low-latency
+constraints. Numerical parity with Rhino was mandatory. Architects and
+structural engineers supplied test cases and tolerances.
Implemented polygon clipping with Sutherland–Hodgman. No drama.
-Fortune's algorithm for Voronoi diagrams was a missed opportunity. Implemented
-the beach line with an array (O(N<sup>2</sup>)). Didn't pursue the balanced
-binary tree (O(N log N))—ran out of time.
+Fortune's algorithm was a missed opportunity. Implemented the beach line using
+a linear list instead of the balanced binary tree. Planned to return to this.
+Never had the chance.
-Polygon offsets had always been problematic. Two Z and H-shaped floor plans
-produced self-intersections that tripped Rhino. Straight skeletons had too many
-edge cases we didn't need—hard to get right under time pressure. Implemented a
-custom solver that fixed invalid loops by backtracking.
+Z and H-shaped floor plan offsets produced self-intersections that even Rhino
+mishandled. Couldn't implement straight skeletons under time pressure. Wrote a
+custom solver that fixed invalid loops by backtracking instead.
-Finding the largest inscribed rectangle was messy. No single algorithm covered
-both convex and concave shapes. Found a paper on the convex case, but couldn't
-translate the math to code. Fell back to a brute-force grid search: 12% gain,
-but not the true optimum.
+Problem of finding the largest inscribed rectangle surprised me. No single
+algorithm covered both convex and concave shapes. Brute-force grid search
+yielded 12% more buildable area—but not the true optimum.
-Rhino's BLAS-based linear algebra and Java's Binary Space Partitioning were
-numerically incompatible. Replaced BSP trees with vector-based structures.
-JBLAS reconciled most floating-point issues.
+Java BSP library produced results numerically incompatible with Rhino's.
+Replaced BSP trees with vector-based primitives and JBLAS.
-Migration resumed. No regressions in the building layouts.
+Migration resumed.
diff --git a/_log/arduino-due.md b/_log/arduino-due.md
index 1330dbf..a55122c 100644
--- a/_log/arduino-due.md
+++ b/_log/arduino-due.md
@@ -4,21 +4,8 @@ date: 2024-09-16
layout: post
---
-Three on-chip memories: ROM, flash0, flash1. ARM Cortex-M chips boot into
-0x00000. GPNVM bits map one of them to 0x00000:
-
- - GPNVM1=0 → ROM (default).
- - GPNVM1=1 and GPNVM2=0 → flash0.
- - GPNVM1=1 and GPNVM2=1 → flash1.
-
-On power-up, control jumps to ROM. Without a user program, Atmel's SAM-BA
-bootloader traps execution in the ROM. Must remap memory via SWD to force chip
-to boot from flash, bypassing factory bootloader.
-
Toolchain: ST-LINK/V2 programmer, OpenOCD, ARM GNU Compiler Toolchain.
-Connect ST-LINK/v2 to Arduino Due's DEBUG port:
-
<table style="border: none; width: 100%;">
<tr style="border: none;">
<td style="border: none; width: 50%; vertical-align: top; background-color: transparent;">
@@ -32,7 +19,10 @@ Connect ST-LINK/v2 to Arduino Due's DEBUG port:
</tr>
</table>
-Remap memory using OpenOCD:
+ATSAM3X8E boots into 0x00000, which is mapped to ROM. ROM contains the SAM-BA
+bootloader.
+
+Remap start address to flash0 (flash driver section, OpenOCD manual AT91SAM3):
```
$ openocd -f openocd-due.cfg
@@ -43,32 +33,8 @@ $ telnet localhost 4444
> at91sam3 gpnvm show
```
-Full command list in OpenOCD manual AT91SAM3 (flash driver section).
-
-Initialize vector table with the stack pointer and the reset vector:
-
-```
-__attribute__((noreturn)) void _reset(void)
-{
- unsigned long *dst, *src;
- extern unsigned long _sbss, _ebss;
- extern unsigned long _sdata, _edata, _sidata;
-
- for (dst = &_sbss; dst < &_ebss; dst++)
- *dst = 0;
- for (dst = &_sdata, src = &_sidata; dst < &_edata;)
- *dst++ = *src++;
-
- main();
-}
-
-extern const unsigned int _sp;
-
-__attribute__ ((section(".vtor"))) const void* _tab[] = { &_sp, _reset };
-```
-
-Linker script: Place the vector table at the start of flash0 and initialize the
-stack pointer to the top of the RAM:
+Place the vector table at the start of flash0 and initialize the stack pointer
+to the top of the RAM:
```
MEMORY
@@ -81,13 +47,15 @@ SECTIONS
{
.text :
{
- KEEP(*(.vtor))
+ KEEP(*(.vtor)) /* Vector table defined in main.c */
*(.text*)
*(.rodata*)
} > rom
+
+ /* Data, bss sections */
}
-_sp = ORIGIN(ram) + LENGTH(ram);
+_sp = ORIGIN(ram) + LENGTH(ram); /* Descending stack */
```
Build and upload the program:
diff --git a/_log/bumblebee.md b/_log/bumblebee.md
index 09165b0..7962279 100644
--- a/_log/bumblebee.md
+++ b/_log/bumblebee.md
@@ -6,21 +6,18 @@ project: true
thumbnail: thumb_sm.png
---
-One year at trading firm. Scripts are saturating CPUs, stalling servers.
+One year at trading firm; scripts are saturating CPUs, stalling servers.
Forced to restart them.
-2025-02: Built tool to record browser sessions and synthesize better scripts.
+2025-02: Built a C# WinForms app to record browser sessions and synthesize
+better scripts.
<video style="max-width:100%; margin-bottom: 10px" controls="" poster="poster.png">
<source src="bee.mp4" type="video/mp4">
</video>
-Stack: C# WinForms, WebView2 (Edge), Scintilla.NET editor.
-
-Injected JS hooks, WebView2 and the editor generate events. If-else blocks
-convert them to Selenium code. Optimizer squashes multiple events into single
-commands (e.g., calendar clicks → text input), uses heuristics to improve DOM
-addressing (xpath, id, element).
+JS hooks, WebView2 browser, Scintilla.NET editor emit events. Backend
+interprets them and emits Selenium browser automation code.
Two linear lists store events and code—no time for ASTs. Mid-session manual
edits desync lists, block optimizer. Workaround: only edit script after
diff --git a/_log/e-reader.md b/_log/e-reader.md
index bc979ea..583788d 100644
--- a/_log/e-reader.md
+++ b/_log/e-reader.md
@@ -6,20 +6,20 @@ project: true
thumbnail: thumb_sm.png
---
-Built an e-reader using an ESP-WROOM-32, a 7.5" Waveshare e-paper display, and
-a three-button interface (prev/next/sleep).
+Stitched together an e-reader using an ESP-WROOM-32, a 7.5" Waveshare e-paper
+display, and a three-button interface (prev/next/sleep).
<video style="max-width:100%;" controls="" poster="poster.png">
<source src="ereader.mp4" type="video/mp4">
</video>
-ESP-32-WROOM has 512 KB SRAM and 4 MB flash. Internal flash is unsuitable for
-storing books due to P/E cycle limit. Used HTTP Range requests to stream them
-on-demand. Saved reading progress to RTC memory to survive deep sleep without
+ESP-WROOM-32 has 512 KB SRAM and 4 MB flash. Internal flash is unsuitable for
+storing books due to P/E cycle limit. Streamed pages via HTTP on-demand
+instead. Reading progress is saved in RTC memory to survive deep sleep without
flash wear.
Rasterized PDFs into sequences of bitmaps. 1 byte = 8 pixels, 1 page = 48 KB
-(display resolution), headerless. Optimized for Range requests without
+(the display resolution), headerless; optimized for HTTP Range requests without
server-side logic:
```
@@ -34,30 +34,18 @@ esp_http_client_set_header(http_client, "Range", buf);
esp_http_client_perform(http_client);
```
-Implemented a three-page circular buffer (prev/current/next)—maximum possible
-with 512 KB. GPIO interrupts triggered by button presses cycle the buffer,
-update the screen, and prefetch the next page.
+A circular buffer stores three pages—maximum possible with the 512 KB.
+Button-triggered GPIO interrupts cycle the buffer, update the screen, and
+prefetch the next page.
-```
-c_page_num++;
-pg.page_num = c_page_num + 2;
-pg.page_buf = pages[(c_page_num + 1) % PAGE_LEN];
-
-xSemaphoreGive(mutex);
-xQueueSend(http_evt_queue, &pg, portMAX_DELAY);
-
-epd_draw_async(pages[c_page_num % PAGE_LEN], PAGE_SIZE);
-epd_draw_await();
-```
+Reader isn't as responsive as I'd hoped. Scheduling GPIO, SPI, and HTTP tasks
+on a single core causes input lag.
-System isn't as responsive as I'd hoped. Scheduling GPIO, SPI, and HTTP tasks
-on a single core causes input lag. Pinned the GPIO/SPI tasks to one core and
-the HTTP task to the other.
+Pinned the GPIO and SPI tasks to one core and the HTTP task to the other.
-Better, but screen updates block user input; page turning feels sluggish.
+Better, but screen updates block user input. Page turning feels sluggish.
-Moved the SPI buffers to DMA and made the transfers async, hoping to shave off
-a few more cycles.
+Moved SPI buffers to DMA and made transfers async, hoping to offload the CPU.
Can't think of anything else. Led to [Etlas](../etlas/).
diff --git a/_log/etlas.md b/_log/etlas.md
index 9f09322..d0ff35d 100644
--- a/_log/etlas.md
+++ b/_log/etlas.md
@@ -6,8 +6,7 @@ project: true
thumbnail: thumb_sm.jpg
---
-Repurposed the [e-reader](../e-reader/) for regular use as a news, stocks, and
-weather dashboard.
+Repurposed the [e-reader](../e-reader/) for regular use as a news tracker.
<table style="border: none;">
<tr style="border: none;">
@@ -19,14 +18,13 @@ weather dashboard.
</tr>
</table>
-Swapped out the ESP-32-WROOM for an ESP32 NodeMCU D1—smaller, better form
+Swapped out the ESP-WROOM-32 for an ESP32 NodeMCU D1—smaller, better form
factor. Connected a DHT22 sensor for temperature and humidity data.
-Weather: 26µs/50µs/70µs pulses from the DHT22's single-wire protocol are too
-fast for ESP32 standard APIs. Ported <a
+Weather: DHT22's 26µs/50µs/70µs pulses are too fast for the ESP32 standard
+APIs. Ported the following bit banging <a
href="https://github.com/Fonger/ESP8266-RTOS-DHT" class="external"
-target="_blank" rel="noopener noreferrer">this</a> clever bit-banging from
-ESP8266 to ESP32:
+target="_blank" rel="noopener noreferrer">routine</a> from ESP8266:
```
static inline int dht_await_pin_state(int state, int timeout)
@@ -44,8 +42,6 @@ static inline int dht_await_pin_state(int state, int timeout)
static inline int dht_get_raw_data(unsigned char buf[BUFLEN])
{
- /* init code and preamble */
-
for (i = 0; i < BUFLEN; i++) {
if (!(pwl = dht_await_pin_state(1, 50))) {
rc = 4;
@@ -63,23 +59,17 @@ static inline int dht_get_raw_data(unsigned char buf[BUFLEN])
}
```
-Stocks: Obtained two weeks EOD data from Polygon.io—maximum possible with 512
-KB RAM. Deployed a simple Flask API on VPS to manage the watchlist and relay
-the feed. Wrapped the API in FastCGI and exposed it through chroot-ed
-htpasswd + slowcgi + httpd—battle-tested OpenBSD base-system tools.
-
-Custom stepped graph works but the code is crude. vTaskDelay() is needed to
-keep the watchdog timer from triggering—revisit with Bresenham's line
-algorithm.
+Stocks: Two weeks' EOD data from Polygon.io—maximum possible with 512 KB RAM.
+Flask API on a VPS manages the watchlist, relays the feed. The API is exposed
+via chroot-ed htpasswd + slowcgi + httpd.
-News: Used Channel NewsAsia RSS feed for news. Hand-coded the XML parsing in
-C—no Flask backend at the time. Now that I have one for stocks, will move the
-feed through the Flask backend in the next revision.
+News: Channel NewsAsia RSS feed. Parsed XML on the MCU as there was no Flask
+backend at the time. Now that I have one for stocks, moving it through the
+backend in the next revision.
-epd_init() stalled intermittently on first refresh() after flash. Toggling
-delay values in refresh() resolved it. If the first refresh succeeded, system
-remained stable. Could not find the root cause. Suspect noisy supply due to
-powering display via MCU.
+epd_init() stalled intermittently on first refresh() after flash. If the first
+refresh succeeded, the system remained stable. Could not find the root cause.
+Suspect noisy supply caused by powering the display via the MCU.
Commit: <a
href="https://git.asciimx.com/etlas/commit/?id=a92c86ac1592c2137d3d1fec1668eacc2d0ca581"
diff --git a/_log/fpm-door-lock-rf.md b/_log/fpm-door-lock-rf.md
index 417368f..fb4ec29 100644
--- a/_log/fpm-door-lock-rf.md
+++ b/_log/fpm-door-lock-rf.md
@@ -8,23 +8,23 @@ thumbnail: thumb_sm.jpeg
Wanted to unlock the door with fingerprint, wirelessly to avoid drilling.
-2024-11: Started with basic 433MHz RF modules and two Arduinos. Connected data
-lines of the transceivers to UART RXD/TXDs of the MCUs. Unreliable—constant
-packet loss.
+2024-11: Started with basic 433MHz RF modules and two Arduinos. Connected the
+data lines of the transceivers to the UART RXD/TXDs of the MCUs.
+Unreliable—constant packet loss.
-2025-01: Switched to RFM69 modules. Ball-ache to program. Followed the
-datasheet as well as I could, audited the code multiple times, cross-checked
-with RadioHead and RFM69 drivers. No luck.
+2025-01: Switched to RFM69 modules. Implemented the driver from scratch.
+Checked the datasheet, audited code, cross-checked with RadioHead and RFM69
+drivers. Driver non-functional.
-The ATmega328P runs at 5V and the RFM69 at 3.3V. Suspect logic-level converter
-(LLC) issues. Not enough swing?
+ATmega328P chips run at 5V, while RFM69s run at 3.3V. Suspect logic-level
+converter (LLC) issues. Not enough swing?
-2025-04: Ditched the RFM69s. Switched to NRF24L01+ modules. Data pins 5V
-tolerant, no LLC required. Spent six weekends writing a clean-room driver.
-Finally some success.
+2025-04: Ditched the RFM69s with NRF24L01+ modules—data pins are 5V tolerant,
+no LLC required. Implemented the driver over six weekends. Wireless modules are
+now operational.
-Implemented a simple XOR cipher for the RF channel—good enough for my threat
-model. The Key is cycled to resist replay attacks:
+Encrypted the RF channel with XOR cipher—sufficient for the threat model; key
+recycled to resist replay attacks:
```
static inline void keygen(char *buf, uint8_t n)
@@ -44,15 +44,15 @@ static inline void keygen(char *buf, uint8_t n)
}
```
-Protocol: FPM sends SYN. Servo responds with a session key. Both xor-ed with a
-static key. Session key is used thereafter. Command set is kept private—serves
-as an authentication layer for the endpoints.
+Protocol: FPM sends SYN. Servo responds with session key. Both XOR-ed with a
+static key. Session key is used thereafter. Private command set serves as an
+authentication layer for the endpoints.
-2025-05: Wrote drivers for R503 and FPM10A sensors. UART RX sequence was
-tricky—adopted the Adafruit C++ implementation to C. Chose the R503 for the
-lock due to its built-in LEDs and better form factor.
+2025-05: Ported the Adafruit C++ drivers for R503 and FPM10A fingerprint
+sensors to C. Chose R503 for the lock due to its built-in LEDs and better form
+factor.
-2025-06: Two PCBs for FPM (front) and servo (back) controllers.
+2025-06: Designed two PCBs for FPM (front) and servo (back) controllers:
<table style="border: none; width: 100%">
<tr style="border: none;">
@@ -79,13 +79,13 @@ lock due to its built-in LEDs and better form factor.
PCB specs: 2-layer, 1oz copper, 0.3mm traces (0.5mm for power). Ground plane.
-2025-06: NRF24L01+ stopped working after mounting on PCB. Too close to the PWM
-line. Soldering a large 47uF (16V) electrolytic capacitor between VCC and
+2025-06: NRF24L01+ stopped working after mounting on the PCB. Too close to the
+PWM line. Soldering a large 47uF (16V) electrolytic capacitor between VCC and
ground fixed it.
Power problems became clear. Linear regulators dissipated too much heat. The
sensor and the servo drew 13.8mA and 4.6mA quiescent currents—unacceptable for
-a battery-powered device. Servo inrush current exceeds 1A. 0.3mm tracks cuts it
+a battery-powered device. Servo inrush current exceeds 1A. 0.3mm tracks cut it
too close.
Verdict: Functional but not practical. Battery died in under 24 hours. Led to
diff --git a/_log/neo4j-a-star-search.md b/_log/neo4j-a-star-search.md
index 21f09b5..3ab0b69 100644
--- a/_log/neo4j-a-star-search.md
+++ b/_log/neo4j-a-star-search.md
@@ -9,7 +9,9 @@ Written in 2026, backdated to 2018.
Before v3.4.0, Neo4J algorithms plugin shipped with Dijkstra's shortest path
search. The algorithm was too slow for our marine vessel tracking application.
-Forked and added A* search. Used the haversine function to steer the search:
+Forked the project and added the A* search algorithm.
+
+Haversine function steers the search:
```
private double computeHeuristic(
@@ -33,7 +35,7 @@ private double computeHeuristic(
}
```
-Core search loop updates costs when a better path is found:
+When a better path is found, the core search loop updates the costs:
```
private void updateCosts(