Created
February 11, 2026 04:44
-
-
Save samsp-msft/a7d577d2645e8095478f71fc774ca938 to your computer and use it in GitHub Desktop.
Arduino code to convert B&O IR pulses & IC2 into serial comms
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /********************************************************************************* | |
| B&O uses a PWM system, with 200ms pulses with specific sized gaps between them. | |
| The gap size determines the data payload: | |
| BEO_ZERO 3125 | |
| BEO_SAME 6250 - Repeat of the last Bit type | |
| BEO_ONE 9375 | |
| BEO_STOP 12500 - Marks the start of a message | |
| BEO_START 15625 - Marks the end of a message | |
| Beo4 commmands are of the form: | |
| START_PULSE | |
| Link : bit | |
| Address: Byte - Target device. 0=TV, 1=Audio, 1B = Lights | |
| Command: Byte - see Commmands.h | |
| STOP_PULSE | |
| BeoRemote One uses an additional 4bits for commmands, sending the STOP mark after | |
| 21 bits of data | |
| *********************************************************************************/ | |
| #include "Beomote.h" | |
| Beomote Beo; | |
| void Pin_ISR() | |
| { | |
| Beo.pulseISR(); | |
| } | |
| ISR(TIMER1_OVF_vect) | |
| { | |
| Beo.timerISR(); | |
| } | |
| // Pin needs to support interrupts, will depend on specific board being used. | |
| // For example, Uno can use Pin2 or Pin3. | |
| void Beomote::initialize(int pin) | |
| { | |
| irPin = pin; | |
| // Setting the pinmode of the IR pin and resetting the listener | |
| pinMode(irPin, INPUT); | |
| reset(); | |
| // Clearing Control Register A | |
| TCCR1A = 0; | |
| // Setting the phase and frequency correct pwm, and stopping the timer | |
| TCCR1B = _BV(WGM13); | |
| long cycles = (F_CPU / 2000000) * TICK; | |
| ICR1 = cycles; | |
| TCCR1B &= ~(_BV(CS10) | _BV(CS11) | _BV(CS12)); | |
| // Setting the timer overflow interrupt enable bit | |
| TIMSK1 = _BV(TOIE1); | |
| // Resetting clock select register, and starts the clock with no prescale | |
| TCCR1B |= _BV(CS10); | |
| // Setup interrupt to listen to trailing edge of pulse | |
| //attachInterrupt(digitalPinToInterrupt(pin), Pin_ISR, RISING); // TSOP | |
| attachInterrupt(digitalPinToInterrupt(pin), Pin_ISR, FALLING); // B&O eye | |
| } | |
| void Beomote::reset() | |
| { | |
| index = -1; | |
| state = WAITING; | |
| timer = 0; | |
| link = 0x00; | |
| address = 0x00; | |
| command = 0x00; | |
| //Reset Timer | |
| TCCR1B |= _BV(CS10); | |
| } | |
| int Beomote::receive(BeoCommand &cmd) | |
| { | |
| if (state == MSG_COMPLETE) | |
| { | |
| cmd.link = link; | |
| //cmd.address = (beo_address)address; | |
| //cmd.command = (beo_command)command; | |
| cmd.address = address; | |
| cmd.command = command; | |
| reset(); | |
| return 1; | |
| } | |
| return 0; | |
| } | |
| int Beomote::fuzzyCompare(int value, int target) | |
| { | |
| int fudge = BEO_ZERO / 10; | |
| return (value >= target - fudge) && (value <= target + fudge); | |
| } | |
| void Beomote::timerISR() | |
| { | |
| timer++; | |
| } | |
| void Beomote::pulseISR() | |
| { | |
| //int irData = !digitalRead(irPin); | |
| //digitalWrite(13, irData); | |
| int beoCode = 0; | |
| int beoBit; | |
| // Don't process new command if the old one hasn't been picked up | |
| if (state == MSG_COMPLETE) | |
| { | |
| return; | |
| } | |
| if (state == WAITING) | |
| { | |
| timer = 0; | |
| state = FIRST_PULSE; | |
| } | |
| else if (fuzzyCompare(timer, BEO_ZERO)) | |
| { | |
| beoCode = BEO_ZERO; | |
| beoBit = lastBeoBit = 0; | |
| } | |
| else if (fuzzyCompare(timer, BEO_SAME)) | |
| { | |
| beoCode = BEO_SAME; | |
| beoBit = lastBeoBit; | |
| } | |
| else if (fuzzyCompare(timer, BEO_ONE)) | |
| { | |
| beoCode = BEO_ONE; | |
| beoBit = lastBeoBit = 1; | |
| } | |
| else if (fuzzyCompare(timer, BEO_STOP)) | |
| { | |
| beoCode = BEO_STOP; | |
| } | |
| else if (fuzzyCompare(timer, BEO_START)) | |
| { | |
| beoCode = BEO_START; | |
| } | |
| else | |
| { | |
| // We haven't found a valid pulse size | |
| //Serial.println("Invalid pulse"); | |
| //DumpState(); | |
| reset(); | |
| } | |
| //We got a valid message, reset the timer and process it | |
| timer = 0; | |
| if (beoCode == BEO_START) | |
| { | |
| state = IN_PROGRESS; | |
| index = 0; | |
| } | |
| else if (index == 0) | |
| { | |
| link = beoBit; | |
| index++; | |
| } | |
| else if (index < 9) | |
| { | |
| address = address << 1; | |
| address |= beoBit; | |
| index++; | |
| } | |
| else if (beoCode == BEO_STOP) | |
| { | |
| state = MSG_COMPLETE; | |
| } | |
| else if (index < 21) | |
| { | |
| command = command << 1; | |
| command |= beoBit; | |
| index++; | |
| } | |
| else | |
| { | |
| #ifdef DEBUG | |
| Serial.println("How did we get here?"); | |
| debugDumpState(beoCode); | |
| #endif | |
| reset(); | |
| } | |
| } | |
| void Beomote::debugDumpState(int beoCode) | |
| { | |
| Serial.println("Beomote State:"); | |
| Serial.print(" code:"); | |
| Serial.println(beoCode); | |
| Serial.print(" state:"); | |
| Serial.println(state); | |
| Serial.print(" timer:"); | |
| Serial.println(timer); | |
| Serial.print(" index:"); | |
| Serial.println(index); | |
| Serial.print(" link:"); | |
| Serial.println(link, HEX); | |
| Serial.print(" address:"); | |
| Serial.println(address, HEX); | |
| Serial.print(" command:"); | |
| Serial.println(command, HEX); | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #ifndef _BEOMOTE_H | |
| #define _BEOMOTE_H | |
| #include "Commands.h" | |
| #include <Arduino.h> | |
| #include <avr/io.h> | |
| #include <avr/interrupt.h> | |
| // Common divisors for 3125 = 1, 5, 25, 125, 625, 3125 | |
| #define TICK 25 | |
| // Defining the Bang & Olufsen commands | |
| #define BEO_ZERO (3125 / TICK) //125 | |
| #define BEO_SAME (6250 / TICK) //250 | |
| #define BEO_ONE (9375 / TICK) //375 | |
| #define BEO_STOP (12500 / TICK) //500 | |
| #define BEO_START (15625 / TICK) //625 | |
| typedef struct { | |
| boolean link; | |
| // beo_address address; | |
| // beo_command command; | |
| unsigned char address; | |
| unsigned int command; | |
| } BeoCommand; | |
| typedef enum IrState | |
| { | |
| WAITING = 0x00, | |
| FIRST_PULSE = 0x04, | |
| IN_PROGRESS = 0x08, | |
| MSG_COMPLETE = 0x10 | |
| } IrState; | |
| class Beomote { | |
| public: | |
| void initialize(int pin); | |
| int receive(BeoCommand &cmd); | |
| // This method is only to be called by the interrupt service routine | |
| void timerISR(); | |
| void pulseISR(); | |
| private: | |
| int irPin; | |
| int lastState; | |
| int lastBeoBit; | |
| IrState state; | |
| boolean link; | |
| unsigned char address; | |
| unsigned int command; | |
| volatile int timer; | |
| int index; | |
| void reset(); | |
| int fuzzyCompare(int value, int target); | |
| void debugDumpState(int beoCode); | |
| }; | |
| extern Beomote Beo; | |
| #endif |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #ifndef _COMMANDS_H | |
| #define _COMMANDS_H | |
| typedef enum beo_address | |
| { | |
| SOURCE_VIDEO = 0x00, | |
| SOURCE_AUDIO = 0x01, | |
| SOURCE_VIDEOTAPE = 0x05, | |
| SOURCE_ALL = 0x0F, | |
| SOURCE_SPDEMO = 0x1D, | |
| SOURCE_LIGHT = 0x1B, | |
| SOURCE_BEOVISION1 = 0xA0, | |
| SOURCE_BEOSOUND =0xA5, | |
| SOURCE_BEOVISION2= 0xA8, | |
| SOURCE_BEOSOUND2=0xAD, | |
| SOURCE_BEOVISION3 = 0xB0, | |
| SOURCE_BEOSOUND3 =0xB5 | |
| } beo_address; | |
| typedef enum beo_command | |
| { | |
| NUMBER_0 = 0x00, | |
| NUMBER_1 = 0x01, | |
| NUMBER_2 = 0x02, | |
| NUMBER_3 = 0x03, | |
| NUMBER_4 = 0x04, | |
| NUMBER_5 = 0x05, | |
| NUMBER_6 = 0x06, | |
| NUMBER_7 = 0x07, | |
| NUMBER_8 = 0x08, | |
| NUMBER_9 = 0x09, | |
| CLEAR = 0x0A, | |
| STORE = 0x0B, | |
| STANDBY = 0x0C, | |
| MUTE = 0x0D, | |
| INDEX = 0x0E, | |
| RESET = 0x0E, | |
| BACK = 0x14, | |
| PICTURE_OFF = 0x1C, | |
| P_UP = 0x1E, | |
| P_DOWN = 0x1F, | |
| TUNE = 0x20, | |
| Counter=0x20, | |
| CLOCK = 0x28, | |
| FORMAT = 0x2A, | |
| REWIND = 0x32, | |
| RETURN = 0x33, | |
| FORWARD = 0x34, | |
| PLAY = 0x35, | |
| GO = 0x35, | |
| PAUSE = 0x36, | |
| RECORD = 0x37, | |
| GUIDE = 0x40, | |
| SELECT = 0x3F, | |
| INFO = 0x43, | |
| SPEAKER = 0x44, | |
| TURN = 0x46, | |
| SOUND = 0x46, | |
| SLEEP = 0x47, | |
| LOUDNESS = 0x48, | |
| PICTURE = 0x4A, | |
| BASS = 0x4D, | |
| TREBLE = 0x4E, | |
| BALANCE = 0x4F, | |
| LIST = 0x58, | |
| MENU = 0x5C, | |
| VOLUME_UP = 0x60, | |
| VOLUME_DOWN = 0x64, | |
| LEFT_REPEAT = 0x70, | |
| RIGHT_REPEAT = 0x71, | |
| UP_REPEAT = 0x72, | |
| DOWN_REPEAT = 0x73, | |
| GO_REPEAT = 0x75, | |
| GREEN_REPEAT = 0x76, | |
| YELLOW_REPEAT = 0x77, | |
| BLUE_REPEAT = 0x78, | |
| RED_REPEAT = 0x79, | |
| EXIT = 0x7F, | |
| TV = 0x80, | |
| RADIO = 0x81, | |
| VIDEO_AUX = 0x82, | |
| AUDIO_AUX = 0x83, | |
| HOME_MEDIA = 0x84, // Note uses address C0 | |
| VIDEO_TAPE = 0x85, | |
| DVD = 0x86, | |
| CAMCORD = 0x87, | |
| TEXT = 0x88, | |
| SP_DEMO = 0x89, | |
| DIGITAL_TV = 0x8A, | |
| PC = 0x8B, | |
| WEB_MEDIA = 0x8C, // Note uses address C0 | |
| DOOR_CAM = 0x8D, | |
| AUDIO_TAPE = 0x91, | |
| CD = 0x92, | |
| PHONO = 0x93, | |
| NETRADIO = 0x93, | |
| AUDIO_TAPE_2 = 0x94, | |
| N_MUSIC = 0x94, | |
| SPOTIFY = 0x96, // Note used address C0 | |
| CD2 = 0x97, | |
| LIGHT = 0x9B, | |
| MORNING = 0xF, | |
| HOME =0x25, | |
| DINNER =0x26, | |
| CINEMA =0x27, | |
| BED_TIME = 0x39, | |
| NIGHT = 0x3A, | |
| WELCOME_HOME = 0x3B, | |
| GOODBYE = 0x3C, | |
| CONTROL =0x9C, | |
| WINDOW1 = 0xF, | |
| CURTAIN1 = 0x1A, | |
| ON_OFF1 = 0x25, | |
| SHADE1 = 0x3B, | |
| DIMENSION_2D = 0xAD, | |
| DIMENSION_3D = 0xAE, | |
| AV = 0xBF, | |
| TRACKING = 0xC8, | |
| HDMI1 = 0xCE, // Note uses address C0 | |
| HDMI2 = 0x1CE, // Note uses address C0 | |
| HDMI3 = 0x2CE, // Note uses address C0 | |
| HDMI4 = 0x3CE, // Note uses address C0 | |
| MATRIX1 = 0xCF, // Note uses address C0 | |
| MATRIX2 = 0x1CF, // Note uses address C0 | |
| CINEMA_ON = 0xDA, | |
| CINEMA_OFF = 0xDB, | |
| YELLOW = 0xD4, | |
| GREEN = 0xD5, | |
| BLUE = 0xD8, | |
| RED = 0xD9, | |
| HOME_CONTROL = 0xE3, | |
| P_AND_P = 0xFA, | |
| STAND = 0xF7, | |
| YOUTUBE = 0x18C, // Note uses address C0 | |
| DEEZER = 0x196, // Note uses address C0 | |
| PATTERN_PLAY = 0x1D3, // Note uses address C0 | |
| BLUETOOTH = 0x283, // Note uses address C0 | |
| BLGW_APP = 0x28C, // Note uses address C0 | |
| Q_PLAY = 0x296, // Note uses address C0 | |
| } beo_command; | |
| #endif |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // | |
| // FILE: PCF8574.cpp | |
| // AUTHOR: Rob Tillaart | |
| // DATE: 02-febr-2013 | |
| // VERSION: 0.1.02 | |
| // PURPOSE: I2C PCF8574 library for Arduino | |
| // URL: | |
| // | |
| // HISTORY: | |
| // 0.1.02 replaced ints with uint8_t to reduce footprint; | |
| // added default value for shiftLeft() and shiftRight() | |
| // renamed status() to lastError(); | |
| // 0.1.01 added value(); returns last read 8 bit value (cached); | |
| // value() does not always reflect the latest state of the pins! | |
| // 0.1.00 initial version | |
| // | |
| #include "PCF8574.h" | |
| #include <Wire.h> | |
| PCF8574::PCF8574(int address) | |
| { | |
| _address = address; | |
| Wire.begin(); | |
| } | |
| uint8_t PCF8574::read8() | |
| { | |
| Wire.beginTransmission(_address); | |
| Wire.requestFrom(_address, 1); | |
| #if (ARDUINO < 100) | |
| _data = Wire.receive(); | |
| #else | |
| _data = Wire.read(); | |
| #endif | |
| _error = Wire.endTransmission(); | |
| return _data; | |
| } | |
| uint8_t PCF8574::value() | |
| { | |
| return _data; | |
| } | |
| void PCF8574::write8(uint8_t value) | |
| { | |
| Wire.beginTransmission(_address); | |
| _data = value; | |
| Wire.write(_data); | |
| _error = Wire.endTransmission(); | |
| } | |
| uint8_t PCF8574::read(uint8_t pin) | |
| { | |
| PCF8574::read8(); | |
| return (_data & (1<<pin)) > 0; | |
| } | |
| void PCF8574::write(uint8_t pin, uint8_t value) | |
| { | |
| PCF8574::read8(); | |
| if (value == LOW) | |
| { | |
| _data &= ~(1<<pin); | |
| } | |
| else | |
| { | |
| _data |= (1<<pin); | |
| } | |
| PCF8574::write8(_data); | |
| } | |
| void PCF8574::toggle(uint8_t pin) | |
| { | |
| PCF8574::read8(); | |
| _data ^= (1 << pin); | |
| PCF8574::write8(_data); | |
| } | |
| void PCF8574::shiftRight(uint8_t n) | |
| { | |
| if (n == 0 || n > 7 ) return; | |
| PCF8574::read8(); | |
| _data >>= n; | |
| PCF8574::write8(_data); | |
| } | |
| void PCF8574::shiftLeft(uint8_t n) | |
| { | |
| if (n == 0 || n > 7) return; | |
| PCF8574::read8(); | |
| _data <<= n; | |
| PCF8574::write8(_data); | |
| } | |
| int PCF8574::lastError() | |
| { | |
| int e = _error; | |
| _error = 0; | |
| return e; | |
| } | |
| // | |
| // END OF FILE | |
| // |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // | |
| // FILE: PCF8574.H | |
| // AUTHOR: Rob Tillaart | |
| // DATE: 02-febr-2013 | |
| // VERSION: 0.1.02 | |
| // PURPOSE: I2C PCF8574 library for Arduino | |
| // URL: | |
| // | |
| // HISTORY: | |
| // see PCF8574.cpp file | |
| // | |
| #ifndef _PCF8574_H | |
| #define _PCF8574_H | |
| #if defined(ARDUINO) && ARDUINO >= 100 | |
| #include "Arduino.h" | |
| #else | |
| #include "WProgram.h" | |
| #endif | |
| #define PCF8574_LIB_VERSION "0.1.02" | |
| class PCF8574 | |
| { | |
| public: | |
| PCF8574(int address); | |
| uint8_t read8(); | |
| uint8_t read(uint8_t pin); | |
| uint8_t value(); | |
| void write8(uint8_t value); | |
| void write(uint8_t pin, uint8_t value); | |
| void toggle(uint8_t pin); | |
| void shiftRight(uint8_t n=1); | |
| void shiftLeft(uint8_t n=1); | |
| int lastError(); | |
| private: | |
| int _address; | |
| uint8_t _data; | |
| int _error; | |
| }; | |
| #endif | |
| // | |
| // END OF FILE | |
| // |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment