Skip to content

Commit

Permalink
Bang & Olufsen quality assurance
Browse files Browse the repository at this point in the history
  • Loading branch information
Armin committed Oct 3, 2022
1 parent 662b70c commit e36bcaa
Show file tree
Hide file tree
Showing 14 changed files with 410 additions and 255 deletions.
1 change: 1 addition & 0 deletions Contributors.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,6 @@ These are the active contributors of this project that you may contact if there
- [pmalasp](https://github.com/pmalasp )
- [ElectronicsArchiver}(https://github.com/ElectronicsArchiver) improving documentation
- [Stephen Humphries](https://github.com/sjahu)Fix for: Prevent long delay caused by overflow when frame duration < repeat period #1028
- [Daniel Wallner](https://github.com/danielwallner) Bang & Olufsen protocol.

Note: Please let [ArminJo](https://github.com/ArminJo) know if you have been missed.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,12 @@ On my Arduino Nanos, I always use a 100 &ohm; series resistor and one IR LED :gr
For receiving, the **minimal CPU frequency is 4 MHz**, since the 50 µs timer ISR takes around 12 µs on a 16 MHz ATmega.<br/>
For sending, the **default software generated PWM has problems on AVR running with 8 MHz**. The PWM frequency is around 30 instead of 38 kHz and RC6 is not reliable. You can switch to timer PWM generation by `#define SEND_PWM_BY_TIMER`.

## Bang & Olufsen protocol
The Bang & Olufsen protocol decoder is not enabled by default, i.e if no protocol is enabled explicitely by #define `DECODE_<XYZ>`. It must always be enabled explicitly by `#define DECODE_BEO`.
This is because it has an **IR transmit frequency of 455 kHz** and therefore requires a different receiver harware (TSOP7000).<br/>
And because **generating a 455 kHz PWM signal is currently not implemented**, sending only works if `USE_NO_SEND_PWM` is defined!<br/>
For more info, see [ir_BangOlufsen.hpp](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/src/ir_BangOlufsen.hpp#L42).

# Handling unknown Protocols
## Disclaimer
**This library was designed to fit inside MCUs with relatively low levels of resources and was intended to work as a library together with other applications which also require some resources of the MCU to operate.**
Expand Down
1 change: 1 addition & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ See also the commit log at github: https://github.com/Arduino-IRremote/Arduino-I
- Improved Magiquest protocol.
- Fix for #1028 - Prevent long delay caused by overflow when frame duration < repeat period - Thanks to Stephen Humphries!
- Support for ATtiny816 - Thanks to elockman.
- Added Bang&Olufsen protocol. #1030.

## 3.9.0
- Improved documentation with the help of [ElectronicsArchiver}(https://github.com/ElectronicsArchiver).
Expand Down
2 changes: 1 addition & 1 deletion examples/IRDispatcherDemo/IRDispatcherDemo.ino
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
* Choose the library to be used for IR receiving
*/
#define USE_TINY_IR_RECEIVER // Recommended, but only for NEC protocol!!! If disabled and IRMP_INPUT_PIN is defined, the IRMP library is used for decoding
//#define TINY_RECEIVER_USE_ARDUINO_ATTACH_INTERRUPT // costs 112 bytes program memory + 4 bytes RAM
//#define TINY_RECEIVER_USE_ARDUINO_ATTACH_INTERRUPT // Requires additional 112 bytes program memory + 4 bytes RAM

#include "PinDefinitionsAndMore.h" // Define macros for input and output pin etc.
// Some kind of auto detect library if USE_TINY_IR_RECEIVER is deactivated
Expand Down
2 changes: 1 addition & 1 deletion examples/ReceiveAndSend/ReceiveAndSend.ino
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@

/*
* Specify which protocol(s) should be used for decoding.
* If no protocol is defined, all protocols are active.
* If no protocol is defined, all protocols (except Bang&Olufsen) are active.
* This must be done before the #include <IRremote.hpp>
*/
//#define DECODE_LG
Expand Down
2 changes: 1 addition & 1 deletion examples/ReceiveDemo/ReceiveDemo.ino
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@

/*
* Specify which protocol(s) should be used for decoding.
* If no protocol is defined, all protocols are active.
* If no protocol is defined, all protocols (except Bang&Olufsen) are active.
* This must be done before the #include <IRremote.hpp>
*/
//#define DECODE_LG
Expand Down
6 changes: 4 additions & 2 deletions examples/SimpleReceiver/SimpleReceiver.ino
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@

/*
* Specify which protocol(s) should be used for decoding.
* If no protocol is defined, all protocols are active.
* If no protocol is defined, all protocols (except Bang&Olufsen) are active.
* This must be done before the #include <IRremote.hpp>
*/
//#define DECODE_DENON // Includes Sharp
//#define DECODE_JVC
Expand All @@ -45,7 +46,6 @@
//#define DECODE_RC5
//#define DECODE_RC6

//#define DECODE_BEO
//#define DECODE_BOSEWAVE
//#define DECODE_LEGO_PF
//#define DECODE_MAGIQUEST
Expand All @@ -54,6 +54,8 @@
//#define DECODE_DISTANCE // universal decoder for pulse distance protocols
//#define DECODE_HASH // special decoder for all protocols

//#define DECODE_BEO // This protocol must always be enabled manually, i.e. it is NOT enabled if no protocol is defined

//#define DEBUG // Activate this for lots of lovely debug output from the decoders.

#include <Arduino.h>
Expand Down
42 changes: 28 additions & 14 deletions examples/UnitTest/UnitTest.ino
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,13 @@
#define DECODE_SAMSUNG
#define DECODE_LG

# ifdef USE_NO_SEND_PWM // Bang & Olufsen does not work with a standard IR receiver
#define BEO_STRICT
#define RECORD_GAP_MICROS 16000
#define DECODE_BEO
# endif
//#define ENABLE_BEO_WITHOUT_FRAME_GAP // For successful unit testing we must see the warning at ir_BangOlufsen.hpp:88:2
#if defined(DECODE_BEO)
#define RECORD_GAP_MICROS 16000 // always get the complete frame in the receive buffer
#define BEO_KHZ 38 // We send and receive Bang&Olufsen with 38 kHz here.
#endif

#define DECODE_BOSEWAVE
//#define DECODE_LEGO_PF
#define DECODE_MAGIQUEST
Expand Down Expand Up @@ -308,7 +310,7 @@ void loop() {
Serial.println(F(" LSB first"));
Serial.flush();
IrSender.sendPulseDistanceWidthFromArray(38, 3450, 1700, 450, 1250, 450, 400, &tRawData[0], 48, PROTOCOL_IS_LSB_FIRST,
SEND_STOP_BIT, 0, NO_REPEATS);
SEND_STOP_BIT, 0, NO_REPEATS);
checkReceive(0x0B, 0x10);
delay(DELAY_AFTER_SEND);

Expand All @@ -317,7 +319,7 @@ void loop() {
tRawData[0] = 0x40040D00; // MSB of tRawData[0] is sent first
tRawData[1] = 0x805;
IrSender.sendPulseDistanceWidthFromArray(38, 3450, 1700, 450, 1250, 450, 400, &tRawData[0], 48, PROTOCOL_IS_MSB_FIRST,
SEND_STOP_BIT, 0, NO_REPEATS);
SEND_STOP_BIT, 0, NO_REPEATS);
checkReceive(0x0B, 0x10);
delay(DELAY_AFTER_SEND);
#endif
Expand All @@ -326,7 +328,7 @@ void loop() {
Serial.flush();
uint32_t tRawData1[] = { 0x43D8613C, 0x3BC3BC }; // LSB of tRawData1[0] is sent first
IrSender.sendPulseDistanceWidthFromArray(38, 8900, 4450, 550, 1700, 550, 600, &tRawData1[0], 56, PROTOCOL_IS_LSB_FIRST,
SEND_STOP_BIT, 0, NO_REPEATS);
SEND_STOP_BIT, 0, NO_REPEATS);
checkReceive(0x0, 0x0); // No real check, only printing of received result
delay(DELAY_AFTER_SEND);
}
Expand All @@ -344,7 +346,6 @@ void loop() {
checkReceive(sAddress & 0xFF, sCommand);
delay(DELAY_AFTER_SEND);


#if defined(DECODE_PANASONIC) || defined(DECODE_KASEIKYO)
Serial.println(F("Send Panasonic"));
Serial.flush();
Expand Down Expand Up @@ -468,18 +469,31 @@ void loop() {
#if defined(DECODE_MAGIQUEST)
Serial.println(F("Send MagiQuest"));
Serial.flush();
IrSender.sendMagiQuest(0x6BCD0000 | (uint32_t)sAddress, IRSendData.command); // we have 31 bit address
IrSender.sendMagiQuest(0x6BCD0000 | (uint32_t) sAddress, IRSendData.command); // we have 31 bit address
checkReceive(sAddress, IRSendData.command & 0x1FF); // we have 9 bit command
delay(DELAY_AFTER_SEND);
#endif

#if defined(DECODE_BEO)
IRSendData.protocol = BANG_OLUFSEN;
Serial.print(F("Send "));
Serial.println(getProtocolString(IRSendData.protocol));
Serial.println(F("Send Bang&Olufsen"));
Serial.flush();
IrSender.write(&IRSendData, sRepeats);
checkReceive(IRSendData.address & 0x1FF, IRSendData.command & 0xFF);
IrSender.sendBangOlufsen(sAddress & 0x0FF, sCommand, sRepeats);
# if defined(ENABLE_BEO_WITHOUT_FRAME_GAP)
delay((RECORD_GAP_MICROS / 1000) + 1);
IrReceiver.printIRResultRawFormatted(&Serial, true);
uint8_t tOriginalRawlen = IrReceiver.decodedIRData.rawDataPtr->rawlen;
IrReceiver.decodedIRData.rawDataPtr->rawlen = 6;
// decode first part of frame
IrReceiver.decode();
IrReceiver.printIRResultShort(&Serial);

// Remove trailing 6 entries for next decode
IrReceiver.decodedIRData.rawDataPtr->rawlen = tOriginalRawlen - 6;
for (uint_fast8_t i = 0; i < IrReceiver.decodedIRData.rawDataPtr->rawlen; ++i) {
IrReceiver.decodedIRData.rawDataPtr->rawbuf[i] = IrReceiver.decodedIRData.rawDataPtr->rawbuf[i + 6];
}
# endif
checkReceive(sAddress & 0x0FF, sCommand);
delay(DELAY_AFTER_SEND);
#endif

Expand Down
14 changes: 14 additions & 0 deletions examples/UnitTest/UnitTest.log
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,20 @@ rawData[112]:
+ 300,- 850 + 600,- 550 + 600,- 550 + 300
Sum: 63500

Send Bang&Olufsen
Protocol=Bang&Olufsen Address=0xF1 Command=0x76 Raw-Data=0xF176 16 bits MSB first
Send with: IrSender.sendBang&Olufsen(0xF1, 0x76, <numberOfRepeats>);
rawData[44]:
-1089200
+ 250,-2850
+ 250,-2900 + 250,-15200 + 300,-2850 + 250,-9050
+ 250,-5950 + 250,-5950 + 300,-5900 + 300,-2800
+ 300,-5900 + 300,-5900 + 300,-9000 + 300,-2800
+ 300,-9000 + 300,-5900 + 300,-5900 + 250,-2850
+ 300,-9000 + 300,-5900 + 300,-2800 + 300,-12100
+ 300
Sum: 136750

Send Bosewave with no address and 8 command bits
Protocol=BoseWave Address=0x0 Command=0x76 Raw-Data=0x8976 16 bits LSB first
Send with: IrSender.sendBoseWave(0x0, 0x76, <numberOfRepeats>);
Expand Down
2 changes: 2 additions & 0 deletions src/IRProtocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,9 @@ struct PulsePauseWidthProtocolConstants {
/*
* Carrier frequencies for various protocols
*/
#if !defined(BEO_KHZ) // guard used for unit test, which sends and receive Bang&Olufsen with 38 kHz.
#define BEO_KHZ 455
#endif
#define SONY_KHZ 40
#define BOSEWAVE_KHZ 38
#define DENON_KHZ 38
Expand Down
26 changes: 14 additions & 12 deletions src/IRSend.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ void IRsend::begin(uint_fast8_t aSendPin, bool aEnableLEDFeedback, uint_fast8_t
* Interprets and sends a IRData structure.
* @param aIRSendData The values of protocol, address, command and repeat flag are taken for sending.
* @param aNumberOfRepeats Number of repeats to send after the initial data if data is no repeat.
* @return 1 if data sent, 0 if no data sent (i.e. for BANG_OLUFSEN)
*/
size_t IRsend::write(IRData *aIRSendData, int_fast8_t aNumberOfRepeats) {

Expand All @@ -146,7 +147,7 @@ size_t IRsend::write(IRData *aIRSendData, int_fast8_t aNumberOfRepeats) {
auto tCommand = aIRSendData->command;
bool tIsRepeat = (aIRSendData->flags & IRDATA_FLAGS_IS_REPEAT);
if (tIsRepeat) {
aNumberOfRepeats = -1;
aNumberOfRepeats = -1; // if aNumberOfRepeats < 0 then only a special repeat frame will be sent
}
// switch (tProtocol) { // 26 bytes bigger than if, else if, else
// case NEC:
Expand Down Expand Up @@ -238,9 +239,6 @@ size_t IRsend::write(IRData *aIRSendData, int_fast8_t aNumberOfRepeats) {
sendApple(tAddress, tCommand, aNumberOfRepeats);

#if !defined(EXCLUDE_EXOTIC_PROTOCOLS)
} else if (tProtocol == BANG_OLUFSEN) {
sendBangOlufsen(tAddress, tCommand, 9, false, aNumberOfRepeats);

} else if (tProtocol == BOSEWAVE) {
sendBoseWave(tCommand, aNumberOfRepeats);

Expand All @@ -252,6 +250,8 @@ size_t IRsend::write(IRData *aIRSendData, int_fast8_t aNumberOfRepeats) {
sendLegoPowerFunctions(tAddress, tCommand, tCommand >> 4, tIsRepeat); // send 5 autorepeats
#endif

} else {
return 0; // Not supported by write. E.g for BANG_OLUFSEN
}
return 1;
}
Expand Down Expand Up @@ -406,7 +406,7 @@ void IRsend::sendPulseDistanceWidthFromArray(uint_fast8_t aFrequencyKHz, unsigne
* Check and fallback for wrong RepeatPeriodMillis parameter. I.e the repeat period must be greater than each frame duration.
*/
auto tFrameDurationMillis = millis() - tStartOfFrameMillis;
if (aRepeatPeriodMillis > tFrameDurationMillis) {
if (aRepeatPeriodMillis > tFrameDurationMillis) {
delay(aRepeatPeriodMillis - tFrameDurationMillis);
}
}
Expand Down Expand Up @@ -460,7 +460,7 @@ void IRsend::sendPulseDistanceWidthFromArray(PulsePauseWidthProtocolConstants *a
* Check and fallback for wrong RepeatPeriodMillis parameter. I.e the repeat period must be greater than each frame duration.
*/
auto tFrameDurationMillis = millis() - tStartOfFrameMillis;
if (aProtocolConstants->RepeatPeriodMillis > tFrameDurationMillis) {
if (aProtocolConstants->RepeatPeriodMillis > tFrameDurationMillis) {
delay(aProtocolConstants->RepeatPeriodMillis - tFrameDurationMillis);
}
}
Expand Down Expand Up @@ -490,7 +490,7 @@ void IRsend::sendPulseDistanceWidth(PulsePauseWidthProtocolConstants *aProtocolC
while (tNumberOfCommands > 0) {
unsigned long tStartOfFrameMillis = millis();

if (tNumberOfCommands < ((uint_fast8_t)aNumberOfRepeats + 1) && aProtocolConstants->SpecialSendRepeatFunction != NULL) {
if (tNumberOfCommands < ((uint_fast8_t) aNumberOfRepeats + 1) && aProtocolConstants->SpecialSendRepeatFunction != NULL) {
// send special repeat
aProtocolConstants->SpecialSendRepeatFunction();
} else {
Expand All @@ -507,7 +507,7 @@ void IRsend::sendPulseDistanceWidth(PulsePauseWidthProtocolConstants *aProtocolC
* Check and fallback for wrong RepeatPeriodMillis parameter. I.e the repeat period must be greater than each frame duration.
*/
auto tFrameDurationMillis = millis() - tStartOfFrameMillis;
if (aProtocolConstants->RepeatPeriodMillis > tFrameDurationMillis) {
if (aProtocolConstants->RepeatPeriodMillis > tFrameDurationMillis) {
delay(aProtocolConstants->RepeatPeriodMillis - tFrameDurationMillis);
}
}
Expand Down Expand Up @@ -539,7 +539,7 @@ void IRsend::sendPulseDistanceWidth(uint_fast8_t aFrequencyKHz, unsigned int aHe
while (tNumberOfCommands > 0) {
unsigned long tStartOfFrameMillis = millis();

if (tNumberOfCommands < ((uint_fast8_t)aNumberOfRepeats + 1) && aSpecialSendRepeatFunction != NULL) {
if (tNumberOfCommands < ((uint_fast8_t) aNumberOfRepeats + 1) && aSpecialSendRepeatFunction != NULL) {
// send special repeat
aSpecialSendRepeatFunction();
} else {
Expand All @@ -557,7 +557,7 @@ void IRsend::sendPulseDistanceWidth(uint_fast8_t aFrequencyKHz, unsigned int aHe
* Check and fallback for wrong RepeatPeriodMillis parameter. I.e the repeat period must be greater than each frame duration.
*/
auto tFrameDurationMillis = millis() - tStartOfFrameMillis;
if (aRepeatPeriodMillis > tFrameDurationMillis) {
if (aRepeatPeriodMillis > tFrameDurationMillis) {
delay(aRepeatPeriodMillis - tFrameDurationMillis);
}
}
Expand Down Expand Up @@ -760,6 +760,7 @@ void IRsend::mark(unsigned int aMarkMicros) {
# endif
/*
* PWM pause timing
* Minimal pause duration is 4.3 us if NO_LED_FEEDBACK_CODE is defined
*/
tNextPeriodEnding += periodTimeMicros;
do {
Expand All @@ -778,6 +779,7 @@ void IRsend::mark(unsigned int aMarkMicros) {
}
}
# endif
// Just getting variables and check for end condition takes minimal 3.8 us
if (tDeltaMicros >= aMarkMicros - (112 / CLOCKS_PER_MICRO)) { // To compensate for call duration - 112 is an empirical value
#else
if (tDeltaMicros >= aMarkMicros) {
Expand All @@ -789,8 +791,7 @@ void IRsend::mark(unsigned int aMarkMicros) {
#endif
return;
}
// digitalToggleFast(_IR_TIMING_TEST_PIN); // 3.0 us per call @16MHz
} while (tMicros < tNextPeriodEnding); // 3.4 us @16MHz
} while (tMicros < tNextPeriodEnding);
} while (true);
# endif
}
Expand Down Expand Up @@ -856,6 +857,7 @@ void IRsend::customDelayMicroseconds(unsigned long aMicroseconds) {
* Enables IR output. The kHz value controls the modulation frequency in kilohertz.
* IF PWM should be generated by a timer, it uses the platform specific timerConfigForSend() function,
* otherwise it computes the delays used by the mark() function.
* If IR_SEND_PIN is defined, maximum PWM frequency for an AVR @16 MHz is 170 kHz (180 kHz if NO_LED_FEEDBACK_CODE is defined)
*/
void IRsend::enableIROut(uint_fast8_t aFrequencyKHz) {
#if defined(SEND_PWM_BY_TIMER)
Expand Down
1 change: 0 additions & 1 deletion src/IRremote.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,6 @@
#define DECODE_RC6

# if !defined(EXCLUDE_EXOTIC_PROTOCOLS) // saves around 2000 bytes program memory
#define DECODE_BEO
#define DECODE_BOSEWAVE
#define DECODE_LEGO_PF
#define DECODE_MAGIQUEST // It modifies the RAW_BUFFER_LENGTH from 100 to 112
Expand Down
6 changes: 4 additions & 2 deletions src/IRremoteInt.h
Original file line number Diff line number Diff line change
Expand Up @@ -481,8 +481,10 @@ class IRsend {
/*
* New send functions
*/
void sendBangOlufsen(uint32_t aHeader, uint8_t aData, int8_t aHeaderBits, bool aDatalink, int_fast8_t aNumberOfRepeats = NO_REPEATS);
void sendBangOlufsenRaw(uint64_t aRawData, int_fast8_t aBits, bool aDatalink, bool aBackToBack = false);
void sendBangOlufsen(uint16_t aHeader, uint8_t aData, int_fast8_t aNumberOfRepeats = NO_REPEATS, int8_t aNumberOfHeaderBits = 8);
void sendBangOlufsenDataLink(uint32_t aHeader, uint8_t aData, int_fast8_t aNumberOfRepeats = NO_REPEATS, int8_t aNumberOfHeaderBits = 8);
void sendBangOlufsenRaw(uint32_t aRawData, int_fast8_t aBits, bool aBackToBack = false);
void sendBangOlufsenRawDataLink(uint64_t aRawData, int_fast8_t aBits, bool aBackToBack = false, bool aUseDatalinkTiming = false);
void sendBoseWave(uint8_t aCommand, int_fast8_t aNumberOfRepeats = NO_REPEATS);
void sendDenon(uint8_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats, bool aSendSharp = false);
void sendDenonRaw(uint16_t aRawData, int_fast8_t aNumberOfRepeats = NO_REPEATS)
Expand Down
Loading

0 comments on commit e36bcaa

Please sign in to comment.