Version/revision of the library used
v2.9.0 (master as of 19 Aug 2026, commit afd43878)
Describe the bug
When the last byte of a MWM message has bit 7 = 0, the decoder silently drops that byte. The stop bit for the last byte (always a space) merges with the trailing gap in the raw buffer. On the receive side, decodeMWM() relies on the stop bit to finalize the last byte. When it's absent — either because the raw buffer was truncated, or because sendMWM() produced a buffer where rawlen-- removed the merged stop+gap entry — the decoder reaches the end of the buffer without storing the final byte.
This affects any message whose last byte has bit 7 = 0. In practice, many MWM command codes end in 0x00, 0x04, 0x39, or similar values with bit 7 clear, making this a common failure mode.
To Reproduce
- Flash the stock
IRrecvDumpV2 example to an ESP8266 (GPIO 14 for IR receive).
- Send a 9-byte MWM message via Tasmota
IRsend raw where the last byte has bit 7 = 0, with an explicit stop bit space followed by a trailing mark:
IRsend 0,866,826,440,400,834,844,442,400,812,870,1268,412,436,1244,2092,416,442,400,866,398,1700,396,864,1232,1668,442,812,1706,436,1244,2954,1240,1244,438,2124,400,442,400,816,1284,870,417,1000
The ESP8266 decodes this correctly: 72 bits, 9 bytes, state[9] = {0x96, 0x19, 0x07, 0x09, 0x0E, 0xDE, 0xC0, 0x04, 0x39}.
- Now send the same message but without the stop bit space (entry
417):
IRsend 0,866,826,440,400,834,844,442,400,812,870,1268,412,436,1244,2092,416,442,400,866,398,1700,396,864,1232,1668,442,812,1706,436,1244,2954,1240,1244,438,2124,400,442,400,816,1284,870,1000
The ESP8266 decodes only 64 bits, 8 bytes, silently dropping the last byte 0x39. The state[] array is {0x96, 0x19, 0x07, 0x09, 0x0E, 0xDE, 0xC0, 0x04} — the final byte is simply gone with no error or indication.
Example code used
Stock IRrecvDumpV2.ino with no modifications, compiled against library v2.9.0:
// examples/IRrecvDumpV2/IRrecvDumpV2.ino (untouched)
IRrecv irrecv(kRecvPin, kCaptureBufferSize, kTimeout, true);
// ...
if (irrecv.decode(&results)) {
Serial.print(resultToHumanReadableBasic(&results));
Serial.println(resultToSourceCode(&results));
}
Expected behaviour
The decoder should either:
- Store the last complete byte even when the stop bit is missing (graceful truncation), or
- Report an error / return
false indicating the message is incomplete.
Currently it silently drops the byte, producing a valid-looking but wrong decode with no indication that data was lost. A consumer (e.g. Tasmota, Home Assistant) has no way to know the message was truncated.
Root cause
In decodeMWM() (src/ir_MWM.cpp), the main decode loop reads bits via getRClevel() and accumulates them into a byte. After reading 8 data bits and detecting a start bit for the next byte (case 9), the current byte is stored. However, if the raw buffer ends before the stop bit of the last byte, the loop exits without ever reaching case 9 for that byte, and the partially-read data is lost:
// Simplified flow in decodeMWM():
while (data_bits < nbits && (level = getRClevel(...)) != kDone) {
switch (state) {
case 0: // waiting for start bit
case 1-8: // reading data bits
case 9: // stop bit → store byte, reset for next byte
}
}
// After loop: last byte is never stored if case 9 wasn't reached
For bytes where bit 7 = 0, the stop bit (space) is a separate raw entry from the last data bit (mark). When that entry is absent, the decoder has 9 bits of data but never finalizes the byte.
For bytes where bit 7 = 1, the stop bit (space) merges with the preceding data bit (also space) in the raw buffer, so the byte is finalized via case 9 before the loop ends. This is why only messages ending in a byte with bit 7 = 0 are affected.
Output of raw data from IRrecvDumpV2.ino
Test 1: With explicit stop bit — CORRECT (72 bits, 9 bytes)
Timestamp : 000729.559
Library : v2.9.0
Protocol : MWM
Code : 0x961907090EDEC00439 (72 Bits)
uint16_t rawData[43] = {872, 824, 474, 374, 858, 832, 472, 380, 826, 858, 1284, 404, 420, 1264, 2124, 396, 468, 378, 880, 386, 1720, 386, 880, 1220, 1694, 426, 828, 1696, 456, 1234, 2986, 1216, 1228, 464, 2148, 382, 456, 390, 832, 1272, 892, 402, 1022}; // MWM
uint8_t state[9] = {0x96, 0x19, 0x07, 0x09, 0x0E, 0xDE, 0xC0, 0x04, 0x39};
Test 2: Without stop bit — BUG (64 bits, 8 bytes, last byte dropped)
Timestamp : 000804.508
Library : v2.9.0
Protocol : MWM
Code : 0x961907090EDEC004 (64 Bits)
uint16_t rawData[41] = {872, 824, 472, 378, 854, 832, 474, 378, 826, 858, 1286, 400, 420, 1268, 2126, 392, 474, 374, 884, 388, 1716, 386, 880, 1224, 1690, 426, 832, 1692, 456, 1232, 2986, 1220, 1254, 426, 2154, 384, 472, 378, 796, 1308, 876}; // MWM
uint8_t state[8] = {0x96, 0x19, 0x07, 0x09, 0x0E, 0xDE, 0xC0, 0x04};
Real MWM device capture (for reference — full bundle)
Timestamp : 000043.317
Library : v2.9.0
Protocol : MWM
Code : 0x961907090EDEC00439 (72 Bits)
uint16_t rawData[157] = {866, 826, 440, 400, 834, 844, 442, 400, 812, 870, 1268, 412, 436, 1244, 2092, 416, 442, 400, 866, 398, 1700, 396, 864, 1232, 1668, 442, 812, 1706, 436, 1244, 2954, 1240, 1244, 438, 2124, 400, 442, 400, 816, 1284, 870, 33308, 446, 818, 442, 830, 816, 870, 838, 832, 868, 404, 812, 470, 1218, 876, 1704, 394, 446, 2080, 442, 1240, 436, 404, 1700, 826, 384, 458, 384, 440, 870, 400, 436, 814, 446, 396, 1694, 826, 440, 400, 442, 400, 816, 1280, 442, 1666, 1696, 400, 812, 440, 446, 394, 1246, 440, 2126, 396, 2124, 398, 438, 1244, 438, 400, 436, 1658, 838, 430, 388, 1280, 2124, 400, 1240, 452, 416, 412, 812, 32742, 822, 870, 442, 404, 812, 870, 436, 408, 838, 844, 1272, 400, 446, 1244, 2132, 400, 472, 374, 812, 444, 1698, 414, 816, 1284, 1708, 414, 844, 1672, 444, 1236, 2950, 1258, 1266, 418, 2114, 404, 436, 404, 812, 1280, 812}; // MWM
uint8_t state[9] = {0x96, 0x19, 0x07, 0x09, 0x0E, 0xDE, 0xC0, 0x04, 0x39};
This real capture from a Disney MWM wand decodes correctly because the full raw buffer (157 entries) includes the stop bit for all 9 bytes. The 157-entry buffer exceeds the default kRawBuf = 100, which is why this issue is more commonly seen with smaller buffers or when the stop bit is lost.
What brand/model IR demodulator are you using?
Something random off Banggood or Aliexpress. I don't think it's VS1838b because it doesn't have the metal cage.
Circuit diagram and hardware used (if applicable)
- ESP8266 NodeMCU
- IR demodulator on GPIO 14 (D5)
- IR transmitter on GPIO 4 (D2)
- Separate ESP8266 running Tasmota for sending raw timing pulses to the receiver under test.
I have followed the steps in the Troubleshooting Guide & read the FAQ
Yes
Has this library/code previously worked as expected for you?
No — this is a newly-reported issue with the MWM decoder.
Other useful information
Proposed fix: After the main decode loop in decodeMWM(), add a post-loop check: if data_bits indicates a partially-read byte exists (i.e. data_bits > 0 and we've accumulated 8+ bits since the last stored byte), store the byte. This handles the case where the stop bit is absent due to buffer truncation, rawlen-- in sendMWM, or any other scenario where the final stop bit is missing.
The same issue exists on the send side: sendMWM() calls space(kMWMMinGap) after the last byte's space(kMWMTick) stop bit. Since both are spaces, the raw buffer merges them into one entry. If rawlen-- is then applied (as in some use cases), the merged entry — which contains both the stop bit and the gap — is removed, stripping the stop bit from the transmitted signal.
Version/revision of the library used
v2.9.0 (master as of 19 Aug 2026, commit
afd43878)Describe the bug
When the last byte of a MWM message has bit 7 = 0, the decoder silently drops that byte. The stop bit for the last byte (always a space) merges with the trailing gap in the raw buffer. On the receive side,
decodeMWM()relies on the stop bit to finalize the last byte. When it's absent — either because the raw buffer was truncated, or becausesendMWM()produced a buffer whererawlen--removed the merged stop+gap entry — the decoder reaches the end of the buffer without storing the final byte.This affects any message whose last byte has bit 7 = 0. In practice, many MWM command codes end in
0x00,0x04,0x39, or similar values with bit 7 clear, making this a common failure mode.To Reproduce
IRrecvDumpV2example to an ESP8266 (GPIO 14 for IR receive).IRsendraw where the last byte has bit 7 = 0, with an explicit stop bit space followed by a trailing mark:state[9] = {0x96, 0x19, 0x07, 0x09, 0x0E, 0xDE, 0xC0, 0x04, 0x39}.417):0x39. Thestate[]array is{0x96, 0x19, 0x07, 0x09, 0x0E, 0xDE, 0xC0, 0x04}— the final byte is simply gone with no error or indication.Example code used
Stock
IRrecvDumpV2.inowith no modifications, compiled against library v2.9.0:Expected behaviour
The decoder should either:
falseindicating the message is incomplete.Currently it silently drops the byte, producing a valid-looking but wrong decode with no indication that data was lost. A consumer (e.g. Tasmota, Home Assistant) has no way to know the message was truncated.
Root cause
In
decodeMWM()(src/ir_MWM.cpp), the main decode loop reads bits viagetRClevel()and accumulates them into a byte. After reading 8 data bits and detecting a start bit for the next byte (case 9), the current byte is stored. However, if the raw buffer ends before the stop bit of the last byte, the loop exits without ever reaching case 9 for that byte, and the partially-read data is lost:For bytes where bit 7 = 0, the stop bit (space) is a separate raw entry from the last data bit (mark). When that entry is absent, the decoder has 9 bits of data but never finalizes the byte.
For bytes where bit 7 = 1, the stop bit (space) merges with the preceding data bit (also space) in the raw buffer, so the byte is finalized via case 9 before the loop ends. This is why only messages ending in a byte with bit 7 = 0 are affected.
Output of raw data from IRrecvDumpV2.ino
Test 1: With explicit stop bit — CORRECT (72 bits, 9 bytes)
Test 2: Without stop bit — BUG (64 bits, 8 bytes, last byte dropped)
Real MWM device capture (for reference — full bundle)
This real capture from a Disney MWM wand decodes correctly because the full raw buffer (157 entries) includes the stop bit for all 9 bytes. The 157-entry buffer exceeds the default
kRawBuf = 100, which is why this issue is more commonly seen with smaller buffers or when the stop bit is lost.What brand/model IR demodulator are you using?
Something random off Banggood or Aliexpress. I don't think it's VS1838b because it doesn't have the metal cage.
Circuit diagram and hardware used (if applicable)
I have followed the steps in the Troubleshooting Guide & read the FAQ
Yes
Has this library/code previously worked as expected for you?
No — this is a newly-reported issue with the MWM decoder.
Other useful information
Proposed fix: After the main decode loop in
decodeMWM(), add a post-loop check: ifdata_bitsindicates a partially-read byte exists (i.e. data_bits > 0 and we've accumulated 8+ bits since the last stored byte), store the byte. This handles the case where the stop bit is absent due to buffer truncation,rawlen--insendMWM, or any other scenario where the final stop bit is missing.The same issue exists on the send side:
sendMWM()callsspace(kMWMMinGap)after the last byte'sspace(kMWMTick)stop bit. Since both are spaces, the raw buffer merges them into one entry. Ifrawlen--is then applied (as in some use cases), the merged entry — which contains both the stop bit and the gap — is removed, stripping the stop bit from the transmitted signal.