-
Welding at zero volts
an hour ago • 0 commentsNow the fun part. Switching a big inductive-ish load at a random point on the mains sine wave means a random inrush current every time — hard on the relay contacts and inconsistent weld energy. Switch at the zero crossing and it's clean and repeatable.
The board's PC817 optocoupler already senses mains L. On the safe isolated side it pulls the input high near zero volts and low while the mains is away from zero. I built a zero-cross monitor test mode to characterise it (no mains-side probing — the opto isolates me):
- ~50 clean pulses per second, i.e. one per mains cycle
- the edges sit within ~0.35 ms of the true zero crossing
- the edges bounce — the slow RC edge crosses the input threshold a few times — so the firmware ignores further edges for 3 ms after accepting one
The relay's own mechanics are the other half. It takes 15 ms to close and 5 ms to release. Zeros come every 10 ms. So:
- to close on a zero: energize the coil 5 ms after an edge → contacts meet 15 ms later, exactly on a zero, two half-cycles along
- to open on a zero: de-energize 5 ms after an edge → contacts part 5 ms later, on the next zero
Both the make and the break of weld current now land at zero volts, every weld. And because I chased the microsecond timer bugs earlier, the 5 ms offsets are actually accurate.
Last touch: the settings (pre-weld, pause, weld times) persist across power cycles. The chip can rewrite its own flash via a boot-ROM API, so I reserved a 4 KB sector and append 4-byte records into it — 1024 saves before it needs erasing, comfortably over 100k saves in the flash's lifetime. Set your times once, they're there next time.
It welds. Firmware, programmer, schematics-as-far-as-I-have-them, and the full pinout are on GitHub.
-
The relay that wouldn't stay closed
an hour ago • 0 commentsWith timing fixed, I tried the power relay. It clicked — for a fraction of a second — then dropped, even though my code was holding the pin steady for a full 3-second test.
Then I remembered the arming circuit from Log 1. The relay's driver chain runs off a 1 µF capacitor that's AC-coupled to the MCU pin. A steady level charges it once through the coupling cap, and then it drains. To hold the relay you have to keep feeding it edges.
The fix is almost funny: toggle the arm pin every millisecond — a ~500 Hz charge pump — for the entire time the relay must stay closed. Do that and the capacitor stays topped up and the relay holds rock-solid. Stop toggling and it drops within a second. Which is, of course, exactly the safety behavior the original designers built in: a hung MCU can't hold mains power to the transformer.
While I was at it I made the trigger hold-to-fire: the button has to stay held for the whole weld cycle, and releasing it ends the cycle immediately. Combined with the door-switch interlock in the AC path, that's three independent things that all have to be true for current to flow.
-
Firmware from zero, debugged through seven segments
an hour ago • 0 commentsWith the flash blank, everything had to be written from scratch: startup code, interrupt vectors, display driver, encoder handling, menu, timers. The toolchain is Toshiba's 2009-era TLCS-870/C1 IDE with the cc870c C compiler — C89, 2 KB of RAM, no debugger, no UART free for printf (the UART pins are the programming interface).
The only output device is the 4-digit 7-segment display. So the display became the debug console.
display_hex16()shows any 16-bit value as hex (the segment font grew letters A–F), and a compile-time diagnostic mode turns the encoder into a channel selector: millisecond counter, raw timer capture register, interrupt counter, register readbacks. It sounds primitive. It found every bug in this project.The board mapping came first — tracing which MCU pins drive the display digit selects
-
The datasheet was lying (six bugs from one bad table)
an hour ago • 0 commentsBlank chip, my code running, display lighting up. Time to weld. Except the timing was wrong — a 3-second test pulse took over 12 seconds — and the zero-cross timer read out nonsense.
I had no debugger. What I had was a 4-digit 7-segment display, so I built a test mode that shows raw register values in hex and jumps between them with the encoder. The chip told me its own story, one register at a time.
The root cause of everything: the register-reference markdown I started from had OCR errors in the timer chapter's bit tables. Six bugs fell out of it:
- 4× slow milliseconds. The time-base timer divider was one setting off (fcgck/2¹² instead of /2¹⁰). Every duration in the firmware ran 4× long. This alone was the original "welds take forever" symptom.
- The microsecond timer was never clocked. The peripheral clock-enable bit for the 16-bit timer is bit 0 of the power register — the excerpt said bit 2. Writes to the timer's own registers were silently ignored because the block had no clock.
- The boot ROM leaves that timer running. After every flash cycle the bootloader had configured the timer for its baud detection and left it going — and its mode register is write-locked while running. Fix: stop the timer before configuring it.
- Wrong timer mode. The mode register's bit fields were laid out differently than the excerpt claimed; my "timer mode, 1 MHz" value was actually selecting external-trigger mode. The chip's own boot-ROM code, quoted elsewhere in the real datasheet, gave the correct value.
- Auto-capture bit in the wrong place. The feature that lets you read the live counter is a different bit than documented — I found the truth by pulling the register table out of the datasheet PDF using the word x-coordinates, since the plain-text extraction was ambiguous.
- Interrupt enables in the wrong register. Two interrupts (the µs timer and the zero-cross input) had their enable bits placed in the wrong of two enable registers. Neither ever fired. A simple formula — bit = (0xFFFE − vector)/2, split across two registers — got both right.
Each fix, the display showed a little more life: a counter that finally counted, an overflow tally that finally incremented. When the last one landed,
timer_us()was accurate to the microsecond and the 3-second pulse took exactly 3 seconds.Lesson filed under "trust the primary source": the official PDF was correct all along; a well-meaning transcription of it cost a day.
-
Locked out: talking to a chip nobody talks to
an hour ago • 0 commentsThe TMP89FM42 has a Serial PROM bootloader mode: pull MODE high at reset and it speaks UART at whatever baud rate you open with. No official programmer needed — in theory.
An Arduino UNO became the programmer (AltSoftSerial for jitter-free timing; regular SoftwareSerial corrupted bytes — 0x63 arriving as 0xE3). Getting the protocol right took longer than the wiring:
- The protocol is byte-by-byte interactive. Send a byte, wait for the echo, send the next. Send a whole frame at once and the chip ignores you. The datasheet does not say this anywhere.
- Every session starts with two magic bytes (0x86 for baud detection, then 0x79), then a command byte.
- Addresses and lengths are 3 bytes each — on a chip with a 16-bit address space.
- Any error drops the chip into a silent idle state. A password error doesn't even send an error code. Reset, start over.
That last one mattered, because the original firmware is password protected. The read command requires two magic addresses (where the password length and password string live in flash) — get them wrong and the chip just goes quiet. I scanned dozens of plausible locations. Nothing. Samsung's firmware wasn't coming out.
Fortunately I never wanted it — a chip erase wipes the flash and the security along with it. Blank chip, fully writable, mine now.
The Arduino sketch and a Python driver script now do the whole cycle unattended: enter PROM mode, erase, stream the Intel HEX, verify the checksum, reboot. Protocol notes (including the undocumented behaviors) are in the GitHub repo.
-
Why another microwave spot welder
an hour ago • 0 commentsThe transformer is the boring part. Yes, I rewound the secondary — a few turns of fat cable, hundreds of amps at a volt or two. Every spot welder build does that.
What caught my eye was the control board on its way to the bin. A Samsung microwave mainboard is a little marvel of cost-engineered safety design:
- a zero-cross detector (PC817 opto straight off mains L)
- a relay arming circuit that deserves its own paragraph
- the display, encoder-friendly button inputs, a piezo, relays
- a Toshiba TMP89FM42 — an 8-bit TLCS-870/C1 with 32 KB flash
The arming circuit is the part I fell in love with. The power relay isn't just switched by an MCU pin. Its driver chain is powered from a 1 µF capacitor, and that capacitor is charged through a PNP transistor that is AC-coupled to the MCU pin — a 4.7 nF cap in series with the base. A stuck-high or stuck-low pin does nothing. Only a continuously toggling pin keeps the chain alive. If the firmware crashes, the relay physically cannot stay closed. That's the kind of hardware you want between a microcontroller and a transformer that can weld steel.
Plan: erase the original firmware, write my own controller — weld pulse timing via the display and encoder, welds synchronized to mains zero crossings, all the safety behavior preserved.
Spoiler: the chip had other plans.
wichers