Wednesday, November 11, 2026

How LED Edit .tol Files Work — Reading & Playing Them

 A LED Edit .tol file is a plain, unencrypted recording: a 14-byte header followed by every frame of the animation as raw R,G,B bytes. Once you know that, any microcontroller or desktop app can play it.

The "Circle" zip we started from held two kinds of files:

•        9 .tol files (Nov 2018): "Round 3", "Pink & Round 4", "Arrow Left & Right" and others. These are LED Edit's own export format, and we decoded them fully.

•        24 .led files (00_1.led to 23_1.led): the SD-card files LED Edit writes for a hardware controller. They are encrypted, and we did not decode them (see the last section).

The result is two players built on the same understanding of the format: LedEditTolPlayer.ino (ESP32 or Mega 2560, plays from an SD card with FastLED) and TolPlayer (a Java 8 Swing desktop player with wiring modes and export). This tutorial walks through how the format was worked out, step by step, so you can repeat the method on other files.

Step 1 — First look: sizes and hex dumps

The first clue is in the hex dump: .tol bytes are only 00 and 7f in neat repeating groups of three, while .led bytes look random. Always start an unknown format with the file sizes and the first few hundred bytes.

1.       List the sizes. The .led files are all exact multiples of 512 bytes (one SD-card sector), so they are written for a controller reading raw sectors. The .tol sizes end in odd numbers like 1,881,014, so they have a small header in front of evenly sized data.

2.       Dump the start of each file (od -A x -t x1z file | head, or any hex editor).

3.       Count the distinct byte values. Every .tol file uses only 2 values: 0x00 and 0x7F. That means there is no compression or encryption, just colour levels.

The start of Round 3.tol looks like this:

000000 00 00 04 15 00 0a 00 3c 00 23 00 16 03 00 00 00
000010 00 00 00 00 00 00 00 7f 00 7f 7f 00 7f 7f 00 7f
000020 7f 00 7f 7f 00 7f 7f 00 7f 00 00 00 00 00 00 00

The first 14 bytes are small big-endian numbers; after that the bytes come in groups of three, which is what RGB pixel data looks like.

Step 2 — Decoding the 14-byte header

The header gives the frame count and the pixel grid, and the file size proves it: size = 14 + frames × pixels × 3, exactly, for all 9 files.

All multi-byte numbers are big-endian (most significant byte first).

Offset

Size

Example (Round 3)

Meaning

0

4 bytes

00 00 04 15 = 1045

Frame count

4

2 bytes

00 0a = 10

Dimension A: rings (rows)

6

2 bytes

00 3c = 60

Dimension B: pixels per ring (columns)

8

2 bytes

00 23 = 35

Unknown, same in every file (maybe speed)

10

2 bytes

00 16 = 22

Unknown, same in every file

12

1 byte

03

Bytes per pixel (R, G, B)

13

1 byte

00

Reserved

How we proved it, using Round 3.tol:

•        Pixels = 10 × 60 = 600, so one frame = 600 × 3 = 1,800 bytes.

•        1,045 frames × 1,800 = 1,881,000 bytes, plus the 14-byte header = 1,881,014.

•        The real file size is 1,881,014 bytes. Exact match.

The same check holds for the short file: Left & Right.tol has 109 frames, and 14 + 109 × 1,800 = 196,214 bytes, its exact size. When a guessed layout makes the size formula come out exact for every file, the guess is right.

Step 3 — Reading the pixel data

Frame f, pixel p starts at byte 14 + (f × pixels + p) × 3, and its three bytes are R, G, B, each from 0 to 127.

\text{offset}(f, p) = 14 + (f \cdot N + p) \cdot 3, \qquad N = A \cdot B = 600

Colour order. The bytes are in R, G, B order. LED Edit's preview colours (pink = R+B, cyan = G+B) come out right in that order. Most WS2812B strips want GRB on the wire, but that swap belongs in the driver (COLOR_ORDER in FastLED), not in the file reader.

7-bit values. LED Edit stores each channel as 0 to 127, not 0 to 255. Every file here uses only 0 and 127, so sending the raw value gives half brightness. Scale it to 8 bits by shifting left one bit and copying the top bit into the bottom:

out = (v << 1) | (v >> 6);   // 0 -> 0, 64 -> 129, 127 -> 255

Proof by picture. The quickest test that a decode is right is to draw it. We drew one row of 600 pixels per frame, frame after frame downwards. A wrong frame size gives slanted noise; the right one gives clean diagonal chase stripes, shown below for Round 3.tol. The stripes break at multiples of 60 pixels, which is the first hint that the data is organised as 10 groups of 60.



Step 4 — Working out the wheel geometry

The 600 pixels are a wheel of 60 spokes × 10 rings, and the file stores the outer rim ring first and the hub ring last. Each block of 60 pixels (180 bytes) is one ring, going around the wheel.

The header only says 10 × 60; it does not say which number is rings and which is spokes, or which ring comes first. We settled it by drawing the same frames four ways and comparing them with stills from the YouTube video of the real wheel:

Variant

Pixel i is placed at

Matched the video?

A: rings, hub first

ring = i ÷ 60, spoke = i mod 60

No: the solid ring appears at the rim

B: rings, rim first

ring = 9 − (i ÷ 60), spoke = i mod 60

Yes, every still

C: spokes

spoke = i ÷ 10, ring = i mod 10

No: smeared spirals

D: spokes, zig-zag

as C, odd spokes reversed

No: smeared spirals

Variant B reproduced all three stills: the solid inner ring with groups of 3–4 spokes ("Round 3"), green at the hub with pink and blue outside ("Blue & Round 6"), and pink and cyan alternating spokes ("Pink & Round 4"). In TolPlayer this is the Hub↔Rim flip option, on by default.

Two settings the file cannot tell you, so the player leaves them adjustable: which spoke is at the top (the Rotate control) and whether your strip is RGB or GRB (the Colour control).

The comparison sheet below shows the four variants side by side for Round 3.tol.



Step 5 — Wiring modes: from picture order to strip order

The .tol file stores the picture ring by ring; the wiring mode only decides which order the physical LEDs are chained in. So a player builds a chain map: chain[k] = the file pixel that LED number k on the strip must show.

On a wheel, a row is a ring (going around) and a column is a spoke (going outwards). With R rows and C columns, chain position s becomes row r and column c like this:

Mode

Row r

Column c

On the wheel

SINGLE_ROW

s ÷ C

s mod C

Around each ring the same way, then step out

RETURN_ROW

s ÷ C

s mod C, reversed on odd rows

Rings zig-zag: around, then back

SINGLE_COLUMN

s mod R

s ÷ R

Each spoke hub to rim, all the same way

RETURN_COLUMN

s mod R, reversed on odd columns

s ÷ R

Spokes zig-zag: out, then back in

Two more switches pick the corner the chain starts from:

•        Start: hub or rim. Rim flips the row: r = R − 1 − r.

•        Direction: clockwise or anticlockwise. Anticlockwise flips the column: c = C − 1 − c.

That gives 4 modes × 2 starts × 2 directions = 16 wirings, plus Rotate for where spoke 0 sits. Finally the player looks up which file pixel sits at (r, c), using the geometry from step 4, and stores it in chain[k].

There are two ways to use the map:

1.       Export wired .tol: TolPlayer rewrites every frame so pixel k = original pixel chain[k]. The file keeps the same format, and the Arduino plays it straight from the SD card.

2.       Export map .h: TolPlayer writes WIRING_MAP[] into flash, and the Arduino remaps each frame as it reads it (USE_WIRING_MAP 1).

Raw preview does the reverse: it shows what the wheel displays if you send the file unmapped to a given wiring. If one of those previews matches what your hardware shows today, that is your wiring.

Check it with RETURN_COLUMN, hub start, clockwise: LED 0 is the hub of spoke 0 (file pixel 540), it runs out to the rim (pixel 0), then LED 10 is the rim of spoke 1 (pixel 1) and comes back in. The exported file matched the original pixel-for-pixel through that map on every frame checked.

The code: reading a .tol file

The same few lines read the format in every language: read 14 bytes, decode the big-endian numbers, then read pixels × 3 bytes per frame.

Python (quick check on a PC)

def read_tol(path):
    d = open(path, 'rb').read()
    frames = int.from_bytes(d[0:4], 'big')
    a, b = int.from_bytes(d[4:6], 'big'), int.from_bytes(d[6:8], 'big')
    n = a * b                                  # 600 pixels
    for f in range(frames):
        o = 14 + f * n * 3
        yield [tuple(min(255, v * 2) for v in d[o + 3*p : o + 3*p + 3]) for p in range(n)]

Java 8 (from TolPlayer)

int frames = ((all[0] & 0xFF) << 24) | ((all[1] & 0xFF) << 16) | ((all[2] & 0xFF) << 8) | (all[3] & 0xFF);
int dimA = ((all[4] & 0xFF) << 8) | (all[5] & 0xFF);
int dimB = ((all[6] & 0xFF) << 8) | (all[7] & 0xFF);
int pixels = dimA * dimB;
// pixel p of frame f:
int o = 14 + (f * pixels + p) * 3;
int v = all[o] & 0x7F;                 // red, 0..127
int red = (v << 1) | (v >> 6);          // 0..255

Arduino (from LedEditTolPlayer.ino)

The Arduino never loads the whole file. It reads one 1,800-byte frame at a time from the SD card straight into the FastLED buffer, which works because CRGB is stored as r, g, b in memory:

f.seek(14);                                       // skip the header
for (uint32_t fr = 0; fr < frames; fr++) {
  f.read((uint8_t *)leds, pixels * 3);            // one frame
  uint8_t *p = (uint8_t *)leds;
  for (uint16_t i = 0; i < pixels * 3; i++) p[i] = (p[i] << 1) | (p[i] >> 6);
  FastLED.show();
  delay(FRAME_MS);                                 // 40 ms = 25 fps
}

Memory is the limit on small boards: 600 pixels need 1,800 bytes of LED buffer plus about 600 bytes for the SD library. That fits an ESP32 or a Mega 2560 (8 KB RAM) but not a Uno or Nano (2 KB).

Why the .led files were not decoded

The .led files are LED Edit's output for a hardware SD-card controller. We worked out their layout, but the pixel bytes are scrambled with a per-file code we could not reverse reliably. The practical fix is to open the project in LED Edit and save it as .tol.

What we did learn:

Part

What it holds

Bytes 0–6

Text marker rzbv001

Bytes 12–13

Frame count, big-endian (e.g. 01 a0 = 416 for 00_1.led)

Bytes 0x2E–0x1FF

Random-looking bytes, probably an encrypted settings block

Rest of sector 0

Header, 512 bytes in total

Each frame

6,144 bytes = 12 sectors = 8 outputs × 256 pixels × 3 colours

End of file

40 sectors of zeros (padding)

The size check works here too: 512 + frames × 6,144 + 40 × 512 = the file size, for all 24 files.

Why it stops there:

•        The 6,144-byte frames look like bit-planes: each byte carries one bit for 8 outputs at once, so the controller can drive 8 strips in parallel.

•        The bytes are then substituted through a table that changes from file to file. A file with one colour still shows two different byte values, and those two values differ between files (0x86/0x45 in one, 0xE4/0xA3 in another).

•        Without a known plain frame to compare against, there is no reliable way to recover that table.

Quick reference

To play any LED Edit .tol file: skip 14 bytes, read A × B × 3 bytes per frame, double each value, and place pixel i on ring 9 − (i ÷ 60), spoke i mod 60.

The same method works for decoding any unknown LED file:

1.       Check the sizes: a constant header plus equal frames shows up as an exact formula.

2.       Hex-dump the start and count the distinct byte values. A handful of values means raw data; 256 evenly spread values means compressed or encrypted.

3.       Guess the header fields, then prove them with the size formula on every file.

4.       Draw the frames as a picture (one row per frame). The right frame size gives clean stripes.

5.       Try every plausible geometry and compare with a photo or video of the real display.

Still unknown:

•        Header bytes 8–11 (35 and 22 in every file). One may be LED Edit's playback speed; for now the speed is set by hand, 25 fps by default.

•        Which spoke is spoke 0 on the real wheel, and whether the strip is RGB or GRB. Set these with Rotate and Colour.

     files are from this : https://www.youtube.com/watch?v=JuB4x3bWlXc

  j  java and arduino codes : https://github.com/stark9000/led-edit-tol-decoder

 

Monday, November 2, 2026

Protecting Switches from Inductive Spikes

 

Protecting Switches from Inductive Spikes

A Beginner's Guide

Safety note: Several circuits in this guide involve mains voltage (200-240 V AC), which can be lethal. Use appropriately rated components, keep high-voltage sections enclosed and insulated, never probe live mains circuits casually, and use proper isolation and safe measurement practices (for example, a differential probe or an isolating transformer with a properly rated scope setup). If you are unsure, don’t work on live circuits.

1. The one idea you need

A coil (inductor) resists sudden changes in current.

While current flows, the coil stores energy in its magnetic field. When you switch OFF, that energy needs somewhere to go. Without a suitable current path or voltage clamp, the inductor can generate a large voltage to keep the current flowing. The voltage may rise until a protection device conducts, a parasitic path carries the current, or something breaks down, potentially damaging the switch or insulation.

Think of a long pipe of fast-flowing water. Slam the valve shut and you get a pressure spike (“water hammer”). Protection circuits give the water somewhere to go.

Key question: Every protection circuit answers one question: when the switch turns OFF, where does the stored energy go?

2. The tools

Tool

Its job

Think of it as

Flyback diode

Lets DC coil current circulate in a loop

A bypass road

Zener / TVS diode

Limits voltage to a ceiling

A pressure relief valve

RC snubber

Damps ringing and reduces rapid voltage changes

A shock absorber

RCD clamp

Captures a spike in a capacitor, bleeds it off through a resistor

A catch basin with a drain

MOV

Clamps voltage surges on suitably rated AC or DC circuits

A relief valve that can degrade with repeated or severe surges

 

Zener vs TVS: a Zener is a general voltage-limiting part. A TVS is specifically designed for transient-voltage suppression and has specified breakdown, clamping-voltage and pulse-power characteristics. Its actual energy capability depends on the device and the pulse conditions. For power circuits, check the clamping voltage at your peak current, not just the nominal breakdown voltage.

Frequency: you don’t choose a topology by frequency alone. Pick it by what the energy must do. Then use frequency to size the parts, because the faster you switch, the more often energy is dissipated (clamp losses rise with frequency).

3. How to choose: three questions

Question 1: Is there an obvious inductor (coil, motor, relay, solenoid, transformer)?

•     Yes: continue.

•     No: you may not need an inductive clamp, but wiring inductance, capacitive loads and parasitic LC circuits can still cause spikes or ringing. Add protection if a measured waveform or the component ratings show a problem.

Question 2: Is the load DC or AC?

•     DC coil: diode, Zener/TVS or a combination.

•     AC coil: never connect a plain diode directly across an ordinary AC coil. Depending on the application, use a suitably rated RC snubber, MOV or bidirectional TVS. Check the standoff voltage, clamping voltage and energy rating against the coil’s normal operating voltage.

Question 3: Where is the energy supposed to go?

Goal

Typical choice

Simple suppression, slow relay release

Flyback diode across the DC coil

Faster coil-current decay and relay release

Diode in series with a Zener or TVS, keeping the voltage within the switch’s rating

Electrically brake a motor

A proper motor-driver or braking circuit. Stopping time also depends on inertia, load and friction.

Transfer to a secondary winding (flyback converter)

The secondary rectifier is the intended path. An RCD clamp or TVS limits the extra spike from leakage inductance.

Release as a high-voltage pulse (ignition coil)

No plain diode. Use a suitably designed ignition driver with controlled voltage clamping, or a self-clamping ignition IGBT.

Ringing or fast voltage edges

RC snubber

4. Two things people mix up

dv/dt vs ringing

•     dv/dt is how fast voltage changes. A steep edge is a high dv/dt.

•     Ringing is voltage that wobbles after a sharp edge, like a plucked string. It comes from stray inductance and capacitance forming a tiny resonant circuit.

A steep edge can exist without ringing, and ringing contains steep edges. An RC snubber adds damping and limits the rate of voltage change, which helps with both, though it won’t necessarily remove them completely.

Why TRIACs may need snubbers

A TRIAC has two weak spots:

1.       False triggering (static dv/dt). A fast voltage jump on the line can turn it ON with no gate signal. This can happen even with a resistive load.

2.       Retriggering during turn-off (commutating dv/dt). When the current falls below the holding current, the TRIAC attempts to turn OFF. With an inductive load, the voltage across it may rise rapidly during recovery. If the commutating dv/dt exceeds the device’s capability, the TRIAC may retrigger.

Resistive loads cause fewer commutation problems than inductive ones, but whether you need a snubber depends on the TRIAC, the circuit and the electrical noise around it. A snubber is not a universal cure. Sometimes you also need a TRIAC with a higher dv/dt rating, or changes to the gate network.

5. RC debounce vs RC snubber

•     Debounce (Arduino button): smooths a noisy signal so one press reads as one press. Tiny parts, usually with a Schmitt-trigger input.

•     Snubber: damps power transients across a switch or load. The parts must handle real voltage, current and energy.

6. Worked examples

Circuit

What happens at turn-off

Protection

DC relay + MOSFET

Coil current needs a loop

Flyback diode across the coil

Fast-release relay

Same, but quicker decay wanted

Diode + Zener/TVS in series

Flyback converter

Magnetizing energy transfers to the secondary during the switch’s OFF-time, while leakage inductance can produce an additional voltage spike across the primary switch

Secondary rectifier for the intended energy, RCD clamp or TVS on the primary for the leakage spike

TRIAC dimmer + motor

Steep voltage step across the TRIAC

RC snubber, checked against the TRIAC’s ratings

Ignition coil

Rapid current interruption produces the high voltage needed for the spark

A suitably designed ignition driver with controlled voltage clamping, never a plain flyback diode

7. Common mistakes

1.       A plain diode across an ignition coil. The current decays slowly and the spark dies.

2.       A diode across an AC load. It conducts every half-cycle.

3.       A diode across a flyback transformer primary. It blocks energy transfer to the secondary.

4.       Assuming “X2” means “suitable”. Across mains, use a safety-rated capacitor (typically Class X2), but also check its AC voltage, pulse and surge ratings for your circuit. Use a pulse-rated resistor too.

5.       Ignoring snubber leakage. The capacitor passes a small current even when the TRIAC is OFF, so LED lamps and small loads can glow faintly or flash.

6.       Adding protection “just because there’s an inductor.” Add it when the real waveform or ratings call for it.

7.       Assuming every joule becomes heat in the clamp. In a converter, most of the energy goes to the output.

8. Two stages of design

1.       Pick the topology. Start by reading the schematic and identifying the intended current and energy paths. You can often choose an initial protection approach without measurements.

2.       Size the parts and verify. The stored energy in an ideal inductor is E = ½LI² (L in henries, I in amps, E in joules). In a real circuit that energy is shared between the clamp, winding resistance, the switch, parasitics and any intended output path. Then account for voltage limits, switching frequency and average power dissipation, and check the real waveform, because a schematic alone won’t show parasitic inductance or the true size of the transient.

 

TRIAC snubber starting point: about 100 Ω + 0.1 µF is a common value for some 200-240 V AC designs, but it is not universal. Check the TRIAC’s static and commutating dv/dt ratings, the load’s inductance and power factor, capacitor discharge current at turn-on, resistor pulse rating, and leakage when OFF. Verify with an oscilloscope, following the safety note above.

9. The summary

•     Diode: give DC coil current a place to go.

•     Zener/TVS: set a maximum voltage.

•     RC: damp ringing and reduce rapid voltage changes.

•     RCD: catch a leakage spike and burn it off.

•     Combine when needed, for example an RCD clamp for the big spike plus a small RC snubber for ringing.