Close
0%
0%

Oh No, Not Another Microwave Spot Welder

I didn't just rewind the microwave transformer — I kept its control board and rewrote the firmware to run a spot welder.

Similar projects worth following
Every DIY spot welder rewinds the microwave transformer and bins the control board. This one keeps the board and rewrites its firmware instead.

That board already has everything a welder controller needs: zero-cross detection (a PC817 straight off mains), relays, a display and piezo, and a hardware safety chain that cannot hold the power relay closed unless the MCU keeps actively toggling a pin — crashed firmware, relay drops out. Plus a usable 8-bit MCU: a Toshiba TMP89FM42.

The catch: the stock firmware is password-locked, the MCU obscure, the only toolchain a 2009-era Windows IDE. So I erased the chip (which also clears the security), built an Arduino-based programmer, and wrote a new controller from scratch.

The result: a three-parameter weld cycle set with a rotary encoder, switching synced to mains zero crossings with the relay's own timing compensated, settings stored in flash, and a hold-to-fire trigger that aborts the instant you release it.

Hardware

One modification to the board: the original button/membrane panel is removed and a rotary encoder (with push button) plus a trigger button are wired to the same MCU inputs — the internal pull-ups mean they just switch to ground.

Everything else is stock Samsung: the TMP89FM42 drives a multiplexed 4-digit common-anode display through KRA22x pre-drivers, a PC817 optocoupler senses mains L for zero-cross detection, and the power relay sits behind a lovely piece of defensive engineering — a Darlington chain powered from a 1 µF capacitor that is AC-coupled to an MCU pin. A stuck output can never hold the relay: the firmware has to keep pumping the pin at ~500 Hz to keep the capacitor topped up. Stop pumping, relay drops.

Zero-cross synchronized welding

The PC817 gives an edge within ~0.35 ms of every mains zero crossing (measured). The relay operates in 15 ms and releases in 5 ms (datasheet, confirmed). With zeros every 10 ms at 50 Hz, the firmware:

  • energizes the coil at edge + 5 ms → contacts close 15 ms later — exactly on a zero crossing, two half-cycles after the edge
  • de-energizes at edge + 5 ms → contacts open 5 ms later — on the next zero

So both the make and the break of the weld current happen at zero volts, every time.

Firmware

Written in C for the Toshiba cc870c compiler (TLCS-870/C1). Everything is a non-blocking state machine — display multiplexing and the millisecond timebase run from timer interrupts, and the main loop juggles the menu, the encoder, and the weld cycle without a single blocking delay.

  • Weld cycle: P.1 (pre-weld) → dLAy (pause) → P.2 (weld), each 0–5000 ms
  • Hold-to-fire: releasing the trigger ends the cycle immediately
  • Settings persist in flash using the chip's boot-ROM programming API, with 4-byte records appended into a 4 KB sector — 1024 saves per erase,
    100k saves over the flash's life
  • Compile-time test modes: relay timing, timer diagnostics (register readback on the 7-segment display!), zero-cross monitor

Programming the chip

No official programmer needed: an Arduino UNO bit-bangs the chip's Serial PROM bootloader protocol (byte-by-byte interactive — bursting frames does not work, which took a while to discover). A Python script drives the full cycle: enter PROM mode, chip erase, write Intel HEX, verify checksum, reboot. The protocol notes on GitHub document several behaviors the datasheet doesn't mention.

The debugging war story

The build logs cover it in detail, but the short version: the datasheet excerpt I initially worked from had OCR-mangled register tables, which planted six compounding configuration bugs — a 4× slow millisecond timer, a microsecond timer that was never actually clocked, an auto-capture bit that was actually in a different position, interrupt enables in the wrong register, and a boot ROM that leaves a timer running (and its mode register write-locked) after every flash cycle. All of it diagnosed through a 4-digit display, ending with extracting register bit positions from the datasheet PDF's word coordinates.

  • Welding at zero volts

    wichers38 分鐘前 0 comments

    Now 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

    wichers39 分鐘前 0 comments

    With 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

    wichers40 分鐘前 0 comments

    With 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)

    wichers41 分鐘前 0 comments

    Blank 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:

    1. 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.
    2. 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.
    3. 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.
    4. 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.
    5. 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.
    6. 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

    wichers42 分鐘前 0 comments

    The 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

    wichers43 分鐘前 0 comments

    The 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.

View all 6 project logs

Enjoy this project?

Share

Discussions

Does this project spark your interest?

Become a member to follow this project and never miss any updates