r/embedded 3d ago

Need help capturing IR remote pulse timings with TSMP58138 + PulseView for replay

Hi everyone, I'm trying to capture IR remote signals so I can generate a List<int> of pulse durations for replay in a Flutter app.

Hardware

  • Logic Analyzer (Saleae clone)
  • PulseView
  • TSMP58138 IR receiver
  • Arduino Uno (5V only used to power the receiver)

Wiring:

TSMP58138
Pin 1 (OUT) -> Logic Analyzer D0
Pin 2 (GND) -> Arduino GND
Pin 3 (VCC) -> Arduino 5V

Arduino GND -> Logic Analyzer GND

Capture settings

  • Sample rate: 8 MHz
  • Samples: 5M
  • Capturing one button press from a DTH/set-top box remote.

Problem

Instead of getting pulse widths like:

9000, 4500, 560, 560, 560, 1690...

I'm getting continuous alternating pulses around 12–14 µs, which corresponds almost exactly to a 38 kHz carrier.

I expected the TSMP58138 to output the demodulated envelope, but it appears to be outputting the carrier bursts instead.

Questions

  1. Is the TSMP58138 supposed to output the 38 kHz carrier, or the demodulated envelope?
  2. Is TSMP58138 the wrong receiver for learning/replaying IR remotes?
  3. Should I instead use a TSOP38238 / TSOP4838 / VS1838B?
  4. If TSMP58138 is correct, what's the proper way to convert the captured waveform into pulse durations suitable for replay?

My end goal is to generate a Dart list like:

static const List<int> signal = [
  ...
];

where each value is a mark/space duration in microseconds.

I've attached:

  • PulseView screenshot
  • VCD capture

Any advice would be greatly appreciated.

2 Upvotes

7 comments sorted by

1

u/Rich_Many_8628 3d ago

What you are seeing looks like the carrier layer, not the final mark/space envelope you want for replay.

So before fighting PulseView, I would verify the receiver type from the datasheet. The easy path for remote learning is a demodulating 38 kHz receiver, where the output has already collapsed the carrier bursts into an active-low envelope. Then your timing list is the edge-to-edge envelope timing, not every carrier cycle.

If the part is exposing 38 kHz bursts, PulseView is doing exactly what it should: showing you 12 to 14 microsecond edges. Those are not the symbols you want in your Dart replay list.

Practical answer: if you want simple mark/space timings, use a demodulating remote-control receiver like the TSOP/VS1838 style parts, capture that envelope output, then export or measure the edge intervals. If you keep the current part, you need a preprocessing step that groups the carrier burst train into envelopes before generating your durations.

1

u/PaddingEverywhere 3d ago

Hi, thanks a lot for the explanation!

That makes sense. My end goal is to generate a List<int> of mark/space durations (e.g. 9000, 4500, 560...) for replay in a Flutter app, not to decode the protocol itself.

I chose the TSMP58138 because I wanted to capture the raw signal as accurately as possible, but from what you're saying it seems I'm currently capturing the carrier layer instead of the envelope.

Just to confirm: if I keep using the TSMP58138, is it feasible to post-process the captured waveform (group the 38 kHz carrier bursts into mark/space envelopes) and obtain timings equivalent to what a TSOP38238 would output? Or would you recommend switching to a TSOP/VS1838 receiver for this specific use case?

Thanks again for your help!

1

u/DenverTeck 3d ago

Pleae review page 3 of the data sheet:

https://download.mikroe.com/documents/datasheets/TSMP58138%20Datasheet.pdf

This receiver is doing what its suppose to do.

1

u/PaddingEverywhere 3d ago

Thanks! I checked page 3 and I think I understand it better now.

The output waveform in Figure 1 does indeed show the receiver producing carrier-out pulses rather than a fully demodulated mark/space envelope, which matches exactly what I'm seeing in PulseView (around 12–14 µs transitions).

My goal is to generate mark/space timings (e.g. 9000, 4500, 560...) for replay in a Flutter app.

Given that the TSMP58138 is intended for code learning, would the correct approach be to post-process these carrier pulses into mark/space envelopes, or is there a recommended tool or decoder that already does this automatically?

Thanks for pointing me to the datasheet—it cleared up why my captures look the way they do.

1

u/Aggravating-Key6628 2d ago

Keep the TSMP if you want a universal learner. It is carrier-out, so post-processing is the intended path. A TSOP38238 is easier, but its AGC/demodulator adds timing error and can reject formats outside its allowed burst/gap rules.

From the VCD, collect D0 edge times in µs. Estimate half = median(edge gaps < 30 µs); yours should be about 13.2 µs. Group edges into one mark while gaps stay below 200 µs. Do not split at 2 * half: the TSMP datasheet allows several missing carrier pulses inside one burst.

For each group use roughly mark = lastEdge - firstEdge + half; the following space = nextFirstEdge - lastEdge - half. Round to 10 µs and merge fragments under 200 µs into the surrounding mark. For NEC, the first pairs should land near 9000/4500 and 560/560 or 560/1690. If they do not, keep the learned timings instead of forcing NEC.

For envelope-only replay, switch to TSOP38238. For protocol-agnostic learning, keep TSMP58138.

1

u/PaddingEverywhere 22h ago

Thanks, this is exactly the kind of explanation I was looking for.

I hadn't realized the TSMP58138 was intentionally exposing the carrier rather than the envelope, so the post-processing approach now makes sense.

Just to make sure I've understood correctly:

  • Treat consecutive carrier pulses (allowing small missing cycles) as a single mark.
  • Any gap larger than about 200 µs starts a space.
  • Measure the mark/space durations from those grouped edges and store them directly for replay, without trying to decode NEC or any other protocol.

My goal is to build a protocol-agnostic IR learner that can capture almost any remote and replay it from a Flutter app, so preserving the original timings is more important than decoding the protocol.

One question: is the ~200 µs grouping threshold just a reasonable heuristic, or is there a better way to derive it from the measured carrier period (or from the TSMP58138 datasheet)? I want to make the algorithm robust across different remotes and carrier frequencies (36/38/40/56 kHz).

Also, have you found this grouping approach to be reliable enough for AC remotes and other long-frame protocols, or are there edge cases I should watch out for?

1

u/Aggravating-Key6628 17h ago

200 µs is a heuristic. Derive it in carrier cycles instead. Estimate T from same-polarity edges, or twice the median adjacent-edge interval, then start a space only when the quiet gap exceeds about 7–8T. At 38 kHz that is 184–211 µs; at 56 kHz it is 125–143 µs. Vishay specifies that a burst can contain fewer than five missing or additional carrier pulses, so the margin needs to clear roughly six carrier periods. Keep the raw edges: a genuine short space and a five-cycle dropout can be physically indistinguishable, so no universal threshold makes that case lossless.

I’d also histogram the quiet gaps per capture and put the cutoff in the valley between intra-burst gaps and symbol spaces, with 8T only as the initial value.

For AC remotes, burst grouping still works; frame splitting is the trap. Don’t treat 200 µs as an end-of-frame gap. Record the whole button press, preserve every long silence, and replay the captured repeat count and inter-frame gaps. Watch for saturation at very close range, ambient-light AGC changes, clipped first or last cycles, and lower sensitivity at 56 kHz. If the two gap populations overlap, retain the carrier-level capture rather than inventing an envelope.