Zune BT Console: Difference between revisions

From LVL1
Jump to navigation Jump to search
 
(9 intermediate revisions by the same user not shown)
Line 1: Line 1:
[[File:Zune BT Console.jpg|frame|Zune Bluetooth Console: That which has been created.]]
Introduction, Problem Statement and the Twist


Introduction, Problem Statement and the Twist
[[File:Zune BT Console.jpg|Zune Bluetooth Console: That which has been created.|right|thumbnail|x400px]]


The LVL1 Hackerspace in Louisville Kentucky has seen many mp3 players, cell phones and streaming services.  But when I, Director of Legal Evil Emeritus, indulge in the Hack-a-Thons, I bring a Zune.  And although there are many invasive hacks in wild, I thought I would attempt BT without damaging a device and adding the AVRCP functionality that the common BT fob does not add to the experience.  To that end, this is my story/hack, and I am sticking to it.
The LVL1 Hackerspace in Louisville Kentucky has seen many mp3 players, cell phones and streaming services.  But when I, Director of Legal Evil Emeritus, indulge in the Hack-a-Thons, I bring a Zune.  And although there are many invasive hacks in wild, I thought I would attempt BT without damaging a device and adding the AVRCP functionality that the common BT fob does not add to the experience.  To that end, this is my story/hack, and I am sticking to it.
Line 26: Line 26:


[[File:Zune BT PMBD.jpg|Pseudo Madness Block Diagram|right|thumbnail|x400px]]
[[File:Zune BT PMBD.jpg|Pseudo Madness Block Diagram|right|thumbnail|x400px]]
== Reasoning ==


'''Not Invasive / No Shell Cracking'''
'''Not Invasive / No Shell Cracking'''
Line 43: Line 45:
** Don’t Crack a Single Microsoft Casing
** Don’t Crack a Single Microsoft Casing
** The Road Less Travelled Has The More Interesting Potholes
** The Road Less Travelled Has The More Interesting Potholes
== Decision ==
One must build.


=Parts=
=Parts=
Line 48: Line 54:
[[File:Zune BT EoF.jpg|Elements of Fate|right|thumbnail|x400px]]
[[File:Zune BT EoF.jpg|Elements of Fate|right|thumbnail|x400px]]


== List ==
{| class="wikitable"
{| class="wikitable"
|-
|-
Line 72: Line 79:
| USB A to micro USB || 1 || Power / Programming
| USB A to micro USB || 1 || Power / Programming
|}
|}
== Notes ==
Nothing used is unusual or vintage.
=== Unusual ===
The minimal Arduino is an old item I purchased from somewhere.  It is the ATMEL 328P with a few capacitors and resistors (under the chip) and resonator.
=== Vintage ===
Bare perfboard is my goto.


=Wiring Components=
=Wiring Components=
Line 156: Line 170:
<Br>
<Br>
https://github.com/cyborg5/IRLib2
https://github.com/cyborg5/IRLib2
* AdaFruit for libraries so often used that they don't always get mentioned.


== The Future of the Past ==
== The Future of the Past ==
Line 165: Line 182:
=The Final Reveal=
=The Final Reveal=


[[File:Zune BT CT.jpg|frame|Face]]
[[File:Zune BT CT.jpg|Face|right|thumbnail|x400px]]


[[File:Zune BT CB.jpg|frame|Underside]]
[[File:Zune BT CB.jpg|Underside|right|thumbnail|x400px]]


== Wire-Wrap ==
== Wire-Wrap ==
Line 175: Line 192:
Strain relief for cables and their sockets cannot be over stated as a good thing for DIY projects.
Strain relief for cables and their sockets cannot be over stated as a good thing for DIY projects.


== My inspiring Bluetooth Speaker ==
=Software=
[[File:Zune BT Speaker CMA3569.jpg|Inspiring|left|thumbnail|x200px]]
 
== ESP32 Console Code ==
<code>
/* Original Attribution */
 
/*
  Streaming of sound data with Bluetooth to other Bluetooth device.
  We generate 2 tones which will be sent to the 2 channels.
 
  Copyright (C) 2020 Phil Schatzmann
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.
  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
 
/* Added SSD1306 functionality and I2C functions for the IR sending arduino communication */
 
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include "AudioTools.h"
#include "AudioTools/AudioLibs/A2DPStream.h"
 
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
bool display_active = false;
// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
 
AudioInfo info24(44100, 2, 24);
 
BluetoothA2DPSource a2dp_source;
 
#define I2S_NUM        I2S_NUM_0
#define I2S_BCK        14
#define I2S_WS          15
#define I2S_DATA_IN    32
#define I2S_MCK        0
 
// MD0 and MD1 left to float
// FMT set to HIGH
// Clean power need to prevent interference with signal
 
// The supported audio codec in ESP32 A2DP is SBC. SBC audio stream is encoded
// from PCM data normally formatted as 44.1kHz sampling rate, two-channel 16-bit sample data
 
I2SStream i2s;
 
#define SAMPLE_BUFFER_SIZE 128
 
/* Receives the two 24bits in 32bit packages from the PCM1808 and shifts the 16bit right */
/* The shifted values are then stored in the 16bit stereo Frame */
int32_t get_sound_data(Frame* data, int32_t frameCount)
{
    struct Xrame
    {
      uint32_t left_sample;
      uint32_t right_sample;
    }  xrame[SAMPLE_BUFFER_SIZE];
 
    int bytesRead = i2s.readBytes((uint8_t*)xrame, sizeof(Xrame) * frameCount);
 
    for (int i = 0; i < frameCount; ++i)       
    {
      data[i].channel1 = xrame[i].left_sample>>16;
      data[i].channel2 = xrame[i].right_sample>>16;
    }
 
    return frameCount;
}
 
bool isValid(const char* ssid, esp_bd_addr_t address, int rssi)
{
  if (display_active)
    {
      display.clearDisplay();
      display.setCursor(0,0);
      display.print(ssid);
      display.display();
    }
  Serial.print("available SSID: ");
  Serial.println(ssid);
/* Test for automatically connecting to my BT speaker */ 
  if (strcmp(ssid,"CMA3569") == 0)
    return true;
  else
    return false;
}
 
void setup()
{
  Wire.begin();
  Serial.begin(115200);
  Serial.println("Start"); ///Info
 
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C))
    {
      Serial.println(F("SSD1306 allocation failed"));
    }
  else
    {
      display_active = true;
      display.clearDisplay();
      display.setTextColor(WHITE);
      display.setTextSize(1);
      display.setCursor(0,0);   
      display.display(); 
    }
 
  // I2S configuration
  Serial.println("I2S Configure Data Loaded"); /// Info 
 
  auto cfg = i2s.defaultConfig(RX_MODE);
  cfg.i2s_format = I2S_STD_FORMAT; // or try with I2S_LSB_FORMAT
  cfg.copyFrom(info24);
 
  cfg.pin_mck = I2S_MCK;
  cfg.pin_bck = I2S_BCK;
  cfg.pin_ws = I2S_WS;
  cfg.pin_data = I2S_DATA_IN;
  cfg.auto_clear = true;
//  cfg.use_apll = true; // from your logic: not sure if this works with the new API?
  i2s.begin(cfg);
 
/* Button mapping but no functions implemented */
  pinMode(25,INPUT_PULLUP);
  pinMode(26,INPUT_PULLUP);
  pinMode(27,INPUT_PULLUP);
  pinMode(33,INPUT_PULLUP);
 
  Serial.println("I2S Activate"); /// Info
 
  Serial.println("Activate BT Functions"); /// Info
 
  a2dp_source.set_ssid_callback(isValid); 
  a2dp_source.set_auto_reconnect(false);
  a2dp_source.set_data_callback_in_frames(get_sound_data);
  a2dp_source.set_avrc_passthru_command_callback(button_handler);
  a2dp_source.start("Quixote"); 
 
}
 
/* This is referring to the buttons on the BT speaker */
void button_handler(uint8_t id, bool isReleased)
{
  int IRAddress = 0x55; // This is the address of the IR sending arduino
  if (isReleased) {
    Serial.print("button id ");
    Serial.print(id);
    Serial.println(" released");
    if (id == 70)
      {
        Wire.beginTransmission(IRAddress); // Start communication with the slave
        Wire.write(0); // Send the number 42
        Wire.endTransmission(); // End the transmission
      }
    if (id == 68)
      {
        Wire.beginTransmission(IRAddress); // Start communication with the slave
        Wire.write(0); // Send the number 42
        Wire.endTransmission(); // End the transmission
      }
    if (id == 75)
      {
        Wire.beginTransmission(IRAddress); // Start communication with the slave
        Wire.write(2); // Send the number 42
        Wire.endTransmission(); // End the transmission
      }
    if (id == 76)
      {
        Wire.beginTransmission(IRAddress); // Start communication with the slave
        Wire.write(1); // Send the number 42
        Wire.endTransmission(); // End the transmission
      }
 
  }
}
 
void loop()
{
  delay(1000);
}
 
</code>
 
== Minimal Arduino Code ==
<code>
/* Original Attribution*/
/* send.ino Example sketch for IRLib2
*  Illustrates how to send a code.
*/
/* Just made the sketch respond to I2C from a table of commands */
 
#include <Wire.h>
#include <IRLibSendBase.h>    // First include the send base
//Now include only the protocols you wish to actually use.
//The lowest numbered protocol should be first but remainder
//can be any order.
#include <IRLib_P04_RC6.h> 
#include <IRLibCombo.h>    // After all protocols, include this
// All of the above automatically creates a universal sending
// class called "IRsend" containing only the protocols you want.
// Now declare an instance of that sender.
 
IRsend mySender;
// Variable to store received data
byte receivedData;
 
// Event handler for receiving data
void receiveEvent(int numBytes)
{
  while (Wire.available())
    {
      receivedData = Wire.read(); // Read received byte
      Serial.print("Received: ");
      process_command(receivedData);
      Serial.println(receivedData);
    }
}
 
void setup()
{
  // Initialize as I2C slave with address 0x55
  Wire.begin(0x55);
  // Define a handler for receiving data
  Wire.onReceive(receiveEvent);
 
  Serial.begin(9600);
  delay(2000); while (!Serial); //delay for Leonardo
  Serial.println(F("Every time you press a key is a serial monitor we will send."));
}
 
 
uint32_t irCommands[] = {
  0x800F646E, // Command 0 - play/pause
  0x800F6420, // Command 1 - skip<<
  0x800F6421, // Command 2 - skip>>
  0x800F6410, // Command 3 - vol +
  0x800F6411  // Command 4 – vol -
};
 
void process_command(int index)
{
  if (index >= 0 && index < sizeof(irCommands))
    {
      uint32_t command = irCommands[index];
      Serial.print("Sending IR command: ");
      Serial.println(command, HEX);
      // Send RC6 command (you can change protocol if needed)
      mySender.send(RC6, command, 32); // 32 = number of bits
    }
  else
    {
      Serial.println("Invalid index");
    }
}
 
void loop()
{
  if (Serial.available())
    {
      int index = Serial.read() - '0'; // Read ASCII digit and convert to int
      process_command(index);
    }
}
 
</code>
 
 
=My inspiring Bluetooth Speaker=
[[File:Zune BT Speaker CMA3569.jpg|Inspiring|left|thumbnail|x400px]]

Latest revision as of 12:37, 9 September 2025

Introduction, Problem Statement and the Twist

Zune Bluetooth Console: That which has been created.

The LVL1 Hackerspace in Louisville Kentucky has seen many mp3 players, cell phones and streaming services. But when I, Director of Legal Evil Emeritus, indulge in the Hack-a-Thons, I bring a Zune. And although there are many invasive hacks in wild, I thought I would attempt BT without damaging a device and adding the AVRCP functionality that the common BT fob does not add to the experience. To that end, this is my story/hack, and I am sticking to it.

The Story, Probably Apocryphal

All the cool kids know, “Zune is where its at”, but the whole Bluetooth thing is the next level. This collision of paradigms must be reconciled. My choice was to respect the integrity of the Zune ecosystem while bringing in the new hot topic. So I collected my options and evaluated them as follows:

Whys and Wherefores

There are already Zune mods/hacks for adding Bluetooth. Most are permutations on putting a commercial BT transmitter inside the Zune. These work, but I have a BT speaker with volume, play/pause and skip buttons. These functions can not “inform” the Zune to do anything through a common audio input only BT transmitter. The functions for the button do exist on the Zune and are incorporated in the IR remotes for Zune docks. This mean that a Zune in a dock, connected to a BT transmitter and in line of sight of the dock remote can perform the functions. But I don’t want to keep a remote and line of sight to perform what the speaker already has buttons for as well as headphones with similar buttons.

My spin on the build is to use a BT transmitter that can trap the button functions and relay them to the Zune, via the dock. This means that if I emulate a dock remote based on the AVCRP signals trapped by the BT transmitter then mission accomplished.

My first idea was to use the KCX_BT_Emitter and monitor the serial communications line for the signals and react appropriately. But the KCX doesn’t always show the signals on the serial line. After multiple ways of resetting the module, initializing the connection, and other strategies, it was not consistent.

Next was to use an ESP32 and look for the signal. I found a library that already did what I wanted. I needed to make adjustments for my hardware choices. When I posted about my project, the author discussed some changes and update suggestions.

Now I had the ability to send audio to BT devices and trap the signals from the buttons, I needed to supply directions to the dock. This would be done by emulating a dock remote. This method did not require any hardware changes to the Zune ecosystem hardware. It also allows functioning across the 3 generations of Zune docks and players.

I realize that without the ESP32, this is simply using a BT transmitter and an IR LED bright enough to be seen through walls. And yet, this is a solution with self imposed restraints that allowed me to delve into BT and Zunes. I guess I just had an opportunity to learn and I took it.

Add Bluetooth Function To Zune Usage

Pseudo Madness Block Diagram

Reasoning

Not Invasive / No Shell Cracking

    • Plug In Commercial BT Module
    • Safe, Simple and Finite

Invasive / Shell Cracking

    • Remove Commercial BT Module Shell
    • Crack Open Zune Shell
    • Solder Power and Audio Lines from Zune to Module
    • Seal It Up
    • Risky Operations / Finite Results

Embrace A Special Kind of Madness

    • Build an External BT Sending Device
    • Incorporate Zune Response to BT Speaker/ Headphone AVRCP Messages
    • Don’t Crack a Single Microsoft Casing
    • The Road Less Travelled Has The More Interesting Potholes

Decision

One must build.

Parts

Elements of Fate

List

Item Quantity Purpose
ESP32 Wrover 1 Bluetooth Transmitter
Minimal Arduino 1 IR Transmitter via I2C
PCM1808 1 I2S ADC
SSD1306 1 Info Display
IR LED 1 IR Transmission
IR Photo-Transistor 1 IR Receiver
Logic Level Shifter 1 I2C 3.3V vs 5V Translation
Stereo Phono Socket 1 Analog Audio In
Tactile Switches 4 Future Use
USB A to micro USB 1 Power / Programming

Notes

Nothing used is unusual or vintage.

Unusual

The minimal Arduino is an old item I purchased from somewhere. It is the ATMEL 328P with a few capacitors and resistors (under the chip) and resonator.

Vintage

Bare perfboard is my goto.

Wiring Components

Signal Wiring

Not Quite a Fritzing
ESP32 PCM1808 OLED Display Buttons Level Shifter
0 SCK
14 BCK
15 LRC
32 OUT
21 SCL LV2
22 SDA LV1
25 Green
26 Red
27 White
33 Black
Minimal Arduino IR LED Unit IR RX Unit Level Shifter
D3 DAT
D5 DAT
A4 HL1
A5 HL2

Power Wiring

  • ESP32 Wrover provides 5V and 3.3V power.
  • The PCM1808 requires 5V and 3.3V with the signals using 3.3V
  • The SSD1306 Display requires 3.3V
  • The minimal Arduino runs on 5V supplied by the ESP32 Wrover
  • A jumper on the Arduino allows it to be isolated from the ESP32 5V line when programming it is necessary.
  • The IR LED and IR RX require 5V
  • The Level Shifter provides the bridge between the 3.3V signals of the ESP32 I2C and 3.3V I2C of the Arduino

Notes on the Build

PCM1808

When I chose this I2S ADC, I didn’t have much info. I looked at the datasheet and went forth. Most of the implementations on the web seemed to say there where problems, it was peculiar or it just worked.

My experience was complicated by a pcm1808 module that did not work. After some frustrations and verification that the timing signals into the PCM1808 with a scope were in spec, I bought another PCM1808. I plugged it in and it just worked from the standpoint of sending data down the I2S path.

Next came the 24bit output per channel and the need to make it 16bit per channel. This had an interesting twist. The 24bits would be in a left justified 32bit storage. The datasheet for the pcm1808 alluded to this along with a stray web reference. So, 16 bit right shift and storage in the proper variable type yielded success.

IR Send / Receive Library

There are several libraries out there. A couple had the effect of decoding the Zune IR Remotes, but could not duplicate the IR remote when sending. This was annoying. I simply looked for other libraries until I found one that created output that the docks recognized. This libraries and another are identified in the software portion of this write-up.

Software

This was made infinitely more possible by two libraries that handle the heavy lifting:


  • A Simple Arduino Bluetooth Music Receiver and Sender for the ESP32

Phil Schatzmann
https://github.com/pschatzmann/ESP32-A2DP


  • IRLib2 A Library for Receiving, Decoding and Sending Infrared Signals Using Arduino

Cyborg5 Chris Young
https://github.com/cyborg5/IRLib2


  • AdaFruit for libraries so often used that they don't always get mentioned.

The Future of the Past

This implementation is for only a few dock remote functions. Theoretically, it has a hard limit of implementing all IR remote functions. So be it. The constraints on the Zune ecosystem were known at the beginning. To that end, the project does have an IR decoder capability already wired up. The AVCRP also has limits. So be it. I got this project to do what I wanted. That means success.

But if you have another device to add BT to and an IR option to implement that devices function, then there are other ecosystems to explore. Good Luck and Safe Journey.

The Final Reveal

Face
Underside

Wire-Wrap

Wire-wrap is supposed to not require soldering, but component posts are no longer sharp edged and squared off. As a result, I soldered the wrap wires. The future can be so cruel!

Zip Ties

Strain relief for cables and their sockets cannot be over stated as a good thing for DIY projects.

Software

ESP32 Console Code

/* Original Attribution */

/*

 Streaming of sound data with Bluetooth to other Bluetooth device.
 We generate 2 tones which will be sent to the 2 channels.
 
 Copyright (C) 2020 Phil Schatzmann
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.
 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
  • /

/* Added SSD1306 functionality and I2C functions for the IR sending arduino communication */

  1. include <Wire.h>
  2. include <Adafruit_GFX.h>
  3. include <Adafruit_SSD1306.h>
  4. include "AudioTools.h"
  5. include "AudioTools/AudioLibs/A2DPStream.h"
  1. define SCREEN_WIDTH 128 // OLED display width, in pixels
  2. define SCREEN_HEIGHT 64 // OLED display height, in pixels

bool display_active = false; // Declaration for an SSD1306 display connected to I2C (SDA, SCL pins) Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

AudioInfo info24(44100, 2, 24);

BluetoothA2DPSource a2dp_source;

  1. define I2S_NUM I2S_NUM_0
  2. define I2S_BCK 14
  3. define I2S_WS 15
  4. define I2S_DATA_IN 32
  5. define I2S_MCK 0

// MD0 and MD1 left to float // FMT set to HIGH // Clean power need to prevent interference with signal

// The supported audio codec in ESP32 A2DP is SBC. SBC audio stream is encoded // from PCM data normally formatted as 44.1kHz sampling rate, two-channel 16-bit sample data

I2SStream i2s;

  1. define SAMPLE_BUFFER_SIZE 128

/* Receives the two 24bits in 32bit packages from the PCM1808 and shifts the 16bit right */ /* The shifted values are then stored in the 16bit stereo Frame */ int32_t get_sound_data(Frame* data, int32_t frameCount) {

   struct Xrame 
   {
     uint32_t left_sample;
     uint32_t right_sample;
   }  xrame[SAMPLE_BUFFER_SIZE];
   int bytesRead = i2s.readBytes((uint8_t*)xrame, sizeof(Xrame) * frameCount);
   for (int i = 0; i < frameCount; ++i)        
   {
     data[i].channel1 = xrame[i].left_sample>>16;
     data[i].channel2 = xrame[i].right_sample>>16;
   }
   return frameCount;

}

bool isValid(const char* ssid, esp_bd_addr_t address, int rssi) {

  if (display_active)
    {
      display.clearDisplay();
      display.setCursor(0,0);
      display.print(ssid);
      display.display();
    }
  Serial.print("available SSID: ");
  Serial.println(ssid);

/* Test for automatically connecting to my BT speaker */

  if (strcmp(ssid,"CMA3569") == 0)
    return true;
  else 
    return false;

}

void setup() {

 Wire.begin();
 Serial.begin(115200);
 Serial.println("Start"); ///Info
 
 if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) 
   {
     Serial.println(F("SSD1306 allocation failed")); 
   }
  else
   {
     display_active = true;
     display.clearDisplay();
     display.setTextColor(WHITE);
     display.setTextSize(1);
     display.setCursor(0,0);    
     display.display();  
   }
 // I2S configuration
 Serial.println("I2S Configure Data Loaded"); /// Info  
 auto cfg = i2s.defaultConfig(RX_MODE);
 cfg.i2s_format = I2S_STD_FORMAT; // or try with I2S_LSB_FORMAT
 cfg.copyFrom(info24);
 
 cfg.pin_mck = I2S_MCK; 
 cfg.pin_bck = I2S_BCK; 
 cfg.pin_ws = I2S_WS; 
 cfg.pin_data = I2S_DATA_IN; 
 cfg.auto_clear = true;

// cfg.use_apll = true; // from your logic: not sure if this works with the new API?

 i2s.begin(cfg);

/* Button mapping but no functions implemented */

 pinMode(25,INPUT_PULLUP);
 pinMode(26,INPUT_PULLUP);
 pinMode(27,INPUT_PULLUP);
 pinMode(33,INPUT_PULLUP);
 Serial.println("I2S Activate"); /// Info
 Serial.println("Activate BT Functions"); /// Info
 
 a2dp_source.set_ssid_callback(isValid);  
 a2dp_source.set_auto_reconnect(false);
 a2dp_source.set_data_callback_in_frames(get_sound_data);
 a2dp_source.set_avrc_passthru_command_callback(button_handler);
 a2dp_source.start("Quixote");  

}

/* This is referring to the buttons on the BT speaker */ void button_handler(uint8_t id, bool isReleased) {

 int IRAddress = 0x55; // This is the address of the IR sending arduino
 if (isReleased) {
   Serial.print("button id ");
   Serial.print(id);
   Serial.println(" released");
   if (id == 70)
     {
       Wire.beginTransmission(IRAddress); // Start communication with the slave
       Wire.write(0); // Send the number 42
       Wire.endTransmission(); // End the transmission
     }
   if (id == 68)
     {
       Wire.beginTransmission(IRAddress); // Start communication with the slave
       Wire.write(0); // Send the number 42
       Wire.endTransmission(); // End the transmission
     }
   if (id == 75)
     {
       Wire.beginTransmission(IRAddress); // Start communication with the slave
       Wire.write(2); // Send the number 42
       Wire.endTransmission(); // End the transmission
     }
   if (id == 76)
     {
       Wire.beginTransmission(IRAddress); // Start communication with the slave
       Wire.write(1); // Send the number 42
       Wire.endTransmission(); // End the transmission
     }
 }

}

void loop() {

 delay(1000);

}

Minimal Arduino Code

/* Original Attribution*/ /* send.ino Example sketch for IRLib2

*  Illustrates how to send a code.
*/

/* Just made the sketch respond to I2C from a table of commands */

  1. include <Wire.h>
  2. include <IRLibSendBase.h> // First include the send base

//Now include only the protocols you wish to actually use. //The lowest numbered protocol should be first but remainder //can be any order.

  1. include <IRLib_P04_RC6.h>
  2. include <IRLibCombo.h> // After all protocols, include this

// All of the above automatically creates a universal sending // class called "IRsend" containing only the protocols you want. // Now declare an instance of that sender.

IRsend mySender; // Variable to store received data byte receivedData;

// Event handler for receiving data void receiveEvent(int numBytes) {

 while (Wire.available()) 
   {
     receivedData = Wire.read(); // Read received byte
     Serial.print("Received: ");
     process_command(receivedData);
     Serial.println(receivedData);
   }

}

void setup() {

 // Initialize as I2C slave with address 0x55
 Wire.begin(0x55);
 // Define a handler for receiving data
 Wire.onReceive(receiveEvent);
 Serial.begin(9600);
 delay(2000); while (!Serial); //delay for Leonardo
 Serial.println(F("Every time you press a key is a serial monitor we will send."));

}


uint32_t irCommands[] = {

 0x800F646E, // Command 0 - play/pause
 0x800F6420, // Command 1 - skip<<
 0x800F6421, // Command 2 - skip>>
 0x800F6410, // Command 3 - vol +
 0x800F6411  // Command 4 – vol -

};

void process_command(int index) {

 if (index >= 0 && index < sizeof(irCommands)) 
   {
     uint32_t command = irCommands[index];
     Serial.print("Sending IR command: ");
     Serial.println(command, HEX);
     // Send RC6 command (you can change protocol if needed)
     mySender.send(RC6, command, 32); // 32 = number of bits
   } 
  else 
   {
     Serial.println("Invalid index");
   }

}

void loop() {

 if (Serial.available()) 
   {
     int index = Serial.read() - '0'; // Read ASCII digit and convert to int
     process_command(index);
   }

}


My inspiring Bluetooth Speaker

Inspiring