Using a raspberry Pi as a DIY equivalent to Logitech Harmony

54 replies

Hello gents,

Stumbled upon this post a few days ago and it's been a big help.

I've got a Pi4 here now and am looking to get a nano + IR Eye in the near future. Overall my system is very similar to yours.

Could you please also send the files to me? I've noticed that the github link is not working anymore either.

 

Thanks!

I sent a mail through the forum, if you answer that, I'll have your address and will reply with the files attached.

 

Thanks for the very quick response! I've sent an email back to you.

Thanks to crossbytje I have an Arduino Nano running and reporting back IR codes with GND/5V/IR Signal connected (see https://archivedforum2.beoworld.org/forums/p/16023/138995.aspx for wiring).

I have an IR eye with a 5 wire connection - the other two are TX/RX I believe - would like to get them hooked up. Right now I don't have the eye LEDs or local buttons responding - any idea on how to make that happen? Not sure of what protocol is going on via TX/RX.

 

--- Miniterm on /dev/tty.usbserial-14310  115200,8,N,1 ---

--- Quit: Ctrl+] | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H ---

initialized

0:160:96

0:160:100

0:160:50

0:160:53

0:160:67

0:160:202

I have the receivers that use I2C (they are labelled with timer, vol, play/stop). You can supposedly use commands with the I2c device, a PCF 8574T, which is a TTL expander, to set pins for input or output. I have only gotten output to work - aka reading the buttons.

My wiring is: viewed from screw side of connector of IR puck:

Gnd, +5V, IR -> D3, SDL -> A4. SCL -> A5.

 

---- Arduino code ---

#include <Commands.h>

//#define PCF8574_LOW_MEMORY
#include <pcf8574.h>

#include <Beomote.h>

#include <Wire.h>

PCF8574 PCF_20(0x20);
uint8_t lastButtons=0;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(57600);
  Serial.println("initialized");
  Beo.initialize(3); // I/O pin for the Eye IR input
}
int mode =0;

void loop() {
  // put your main code here, to run repeatedly:
  BeoCommand cmd;

  if (Beo.receive(cmd))  {
    // Serial.print("Link:");
    Serial.print(cmd.link);
    // Serial.print(", Address:");
    Serial.print(":");
    Serial.print(cmd.address);
    // Serial.print(", Command:");
    Serial.print(":");
    Serial.println(cmd.command);
    //    Serial.println("");
  }
  else
  {
    uint8_t value = PCF_20.read8();
    uint8_t buttons = value ^ 0xFF;
    // Capture state, then act when buttons released
    if (buttons !=0) {
      lastButtons = buttons;
    } else if (lastButtons != 0) {
      
      Serial.print("0:254:");
      Serial.println(lastButtons);
 
      lastButtons=0;
    }
  }
}
---- PCF8574/library.prperties -----
name=PCF8574 library
version=0.9.0
author=Reef
maintainer=Renzo Mischianti <renzo.mischianti@gmail.com>
sentence=Arduino/ESP8266 library for PCF8574
paragraph=Use i2c digital expander with Arduino and ESP8266. Can read write digital values with only 2 wire (perfect for ESP-01).
category=Sensors
url=https://github.com/xreef/PCF8574_library
architectures=*
---- pcf8574.cpp ----
//
//    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
//
--- pcf8574.h -----
//
//    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
//
---- Beomote.cpp ----
/*********************************************************************************
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);
}
---- beomote.h ----
#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
------ commands.h --- not used AFAIK -------
#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

djmp wrote:

Thanks to crossbytje I have an Arduino Nano running and reporting back IR codes with GND/5V/IR Signal connected (see https://archivedforum2.beoworld.org/forums/p/16023/138995.aspx for wiring).

I have an IR eye with a 5 wire connection - the other two are TX/RX I believe - would like to get them hooked up. Right now I don't have the eye LEDs or local buttons responding - any idea on how to make that happen? Not sure of what protocol is going on via TX/RX.

 

--- Miniterm on /dev/tty.usbserial-14310  115200,8,N,1 ---

--- Quit: Ctrl+] | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H ---

initialized

0:160:96

0:160:100

0:160:50

0:160:53

0:160:67

0:160:202

Excellent, that's the hard stuff done.Those messages coming back are

[Link}:[Source addr]:[cmd]

 

  • AFAIK Link is whether the remote is sending a link command
  • Source Addr is the source the remote is set to - I generally only use this to detect which type of remote is in use. I have Beoremote 1, Beo4 and the cute little Beo1 from a beovision. I do that as I remap some of those buttons in software, so that the directional arrows act as arrows.
  • Cmd which maps to the button pressed.

 

You pressed 

Vol Up, Vol Dn, Left, Go, Info, Up, probably on a beoremote 1 in TV mode.

My most recent mappings are:

val convertBeoCode = [Integer code, Integer addr|
    var cmd = ""
    switch (code){
        case 0x00: cmd = "NUMBER_0"
        case 0x01: cmd = "NUMBER_1"
        case 0x02: cmd = "NUMBER_2"
        case 0x03: cmd = "NUMBER_3"
        case 0x04: cmd = "NUMBER_4"
        case 0x05: cmd = "NUMBER_5"
        case 0x06: cmd = "NUMBER_6"
        case 0x07: cmd = "NUMBER_7"
        case 0x08: cmd = "NUMBER_8"
        case 0x09: cmd = "NUMBER_9"
        case 0x0A: cmd = "CLEAR"
        case 0x0B: cmd = "STORE"
        case 0x0C: cmd = "STANDBY"
        case 0x0D: cmd = "MUTE"
        //case 0x0E: cmd = "INDEX"
        case 0x0E: cmd = "RESET"

        case 0x13: cmd = "GO"
        case 0x14: cmd = "BACK"

        case 0x1C: cmd = "PICTURE_OFF"
        
        case 0x1E: if (addr == 0x80) cmd = "UP" else cmd = "CHANNEL_UP"
        case 0x1F: if (addr == 0x80) cmd = "DOWN" else cmd = "CHANNEL_DOWN"
      //  case 0x20: cmd = "TUNE"
        case 0x20: cmd = "COUNTER"
        case 0x28: cmd = "CLOCK"
        case 0x2A: cmd = "FORMAT"

        case 0x32: if (addr >= 0xA0) cmd = "REWIND" else cmd = "LEFT"
        case 0x33: cmd = "RETURN"
        case 0x34: if (addr >= 0xA0) cmd = "FORWARD" else cmd = "RIGHT"
        case 0x35: if (addr >= 0xA0) cmd = "PLAY" else cmd = "GO"
        
        
        //case 0x35: cmd = "GO"
        case 0x36: cmd = "PAUSE"
        // Map record to exit on older TV remotes
        case 0x37: if (addr == 0x80) cmd = "EXIT" else cmd = "RECORD"
        case 0x3F: cmd = "SELECT"

        case 0x40: cmd = "GUIDE"

        case 0x43: cmd = "INFO"
        case 0x44: cmd = "SPEAKER"

        case 0x46: cmd = "TURN"
        case 0x46: cmd = "SOUND"
        case 0x47: cmd = "SLEEP"
        case 0x48: cmd = "LOUDNESS"
        case 0x4A: cmd = "PICTURE"

        case 0x4D: cmd = "BASS"
        case 0x4E: cmd = "TREBLE"
        case 0x4F: cmd = "BALANCE"

        case 0x58: cmd = "LIST"
        case 0x5C: cmd = "MENU"
        
        case 0x60: cmd = "VOLUME_UP"
        case 0x64: cmd = "VOLUME_DOWN"

        //REPEAT Section
        case 0x70: cmd = "LEFT"
        case 0x71: cmd = "RIGHT"
        case 0x72: cmd = "UP"
        case 0x73: cmd = "DOWN"
        case 0x75: cmd = "GO"
        case 0x76: cmd = "GREEN"
        case 0x77: cmd = "YELLOW"
        case 0x78: cmd = "BLUE"
        case 0x79: cmd = "RED"

        case 0x7F: cmd = "EXIT"

        case 0x80: cmd = "TV"
        case 0x81: cmd = "RADIO"
        case 0x82: cmd = "VIDEO_AUX"
        case 0x83: cmd = "AUDIO_AUX"
        case 0x84: cmd = "HOME_MEDIA"
        case 0x85: cmd = "VIDEO_TAPE"
        case 0x86: cmd = "DVD"
        case 0x87: cmd = "CAMCORD"
        case 0x88: cmd = "TEXT"
        case 0x89: cmd = "SP_DEMO"
        case 0x8A: cmd = "DIGITAL_TV"
        case 0x8B: cmd = "PC"
        case 0x8C: cmd = "WEB_MEDIA"
        case 0x8D: cmd = "DOOR_CAM"
        
        case 0x91: cmd = "AUDIO_TAPE"
        case 0x92: cmd = "CD"
        //case 0x93: cmd = "PHONO"
        case 0x93: cmd = "NETRADIO"
        //case 0x94: cmd = "AUDIO_TAPE_2"
        case 0x94: cmd = "N_MUSIC"
        case 0x96: cmd = "SPOTIFY"
        case 0x97: cmd = "CD2"
        case 0x9B: cmd = "LIGHT"
        //case 0xF: cmd = "MORNING"

        case 0xAD: cmd = "DIMENSION_2D"
        case 0xAE: cmd = "DIMENSION_3D"

        case 0xBF: cmd = "AV"
        case 0xC8: cmd = "TRACKING"

        case 0xC1: cmd = "RANDOM"

        case 0xCA: cmd = "UP"
        case 0xCB: cmd = "DOWN"
        case 0xCC: cmd = "LEFT"
        case 0xCD: cmd = "RIGHT"

        case 0xCE: cmd = "HDMI1"
        case 0x1CE: cmd = "HDMI2"
        case 0x2CE: cmd = "HDMI3"
        case 0x3CE: cmd = "HDMI4"
        case 0xCF: cmd = "MATRIX1"
        case 0x1CF: cmd = "MATRIX2"

        case 0xDA: cmd = "CINEMA_ON"
        case 0xDB: cmd = "CINEMA_OFF"

        case 0xD4: cmd = "YELLOW"
        case 0xD5: cmd = "GREEN"
        case 0xD8: cmd = "BLUE"
        case 0xD9: cmd = "RED"

        case 0xE3: cmd = "HOME_CONTROL"

        case 0xFA: cmd = "P_AND_P"
        case 0xF7: cmd = "STAND"

        case 0x18C: cmd = "YOUTUBE"
        case 0x196: cmd = "DEEZER"
        case 0x1D3: cmd = "PATTERN_PLAY"
        case 0x283: cmd = "BLUETOOTH"
        case 0x28C: cmd = "BLGW_APP"
        case 0x296: cmd = "Q_PLAY"
/*
        case 0x25: cmd = "HOME"
        case 0x26: cmd = "DINNER"
        case 0x27: cmd = "CINEMA"
        case 0x39: cmd = "BED_TIME"
        case 0x3A: cmd = "NIGHT"
        case 0x3B: cmd = "WELCOME_HOME"
        case 0x3C: cmd = "GOODBYE"
        case 0x9C: cmd = "CONTROL"
        case 0xF: cmd = "WINDOW1"
        case 0x1A: cmd = "CURTAIN1"
        case 0x25: cmd = "ON_OFF1"
        case 0x3B: cmd = "SHADE1"
*/
    }
    cmd
]

Thanks! Should have been more clear in my question though. I have the IR codes via USB and thanks for the remote control mappings.

I'm talking about accessing the LEDs and the Timer/Play/Up/Down buttons on the eye. I'm guessing that's i2c coming out of the TX/RX connections on the eye? Haven't hooked them up yet to the Arduino but was wondering if someone had.

dupe

dupe

The older eyes have connectors for both LEDs which you can simply feed 5V to light up. But I guess that's not very helpful to you.

I experimented a little with the buttons on mine, but I didn't find a way to read any signal from pressing them. I finally gave up on the button use, since I have the eye installed in a hidden place anyway. 

or
BeoWorld Gold Membership card
Join BeoWorld

Register to access the forums, reference material, and exclusive member benefits — including our monthly prize draw.

Become a Member

December 2026 Prize Draw

Gold Members Only

As a Gold member you're automatically entered into our exclusive prize draw — your chance to win iconic Bang & Olufsen products. A unique benefit of BeoWorld Gold membership, every draw brings a new opportunity to win something special.