Sign in   |  Join   |  Help
Untitled Page

ARCHIVED FORUM -- March 2012 to February 2022
READ ONLY FORUM

This is the second Archived Forum which was active between 1st March 2012 and 23rd February 2022

 

Turn Beosound Ouverture On/Off Using WiFi?

rated by 0 users
This post has 15 Replies | 3 Followers

soloAudio
Not Ranked
Posts 5
OFFLINE
Bronze Member
soloAudio Posted: Thu, Jan 16 2020 11:15 PM

Hello,

I have a Beosound Ouverture and Beolab 1 speakers. I use the AUX output to use my loudspeakers for the TV, streaming, sending my iTunes music from my iMac, etc. Works great.

The problem is, I have no remote way to turn the Ouverture on and off other than using the Beo4 remote control. I tried a universal remote control hub, but sadly discovered it is unable to learn the Beo4 IR codes (and read somewhere on this forum about how different B&O is concerning the RC). I contacted B&O support, and they were less than helpful.

So is there any way to easily do this with commercially available products using an iOS app? If not, is there a DIY way to do this? Could MasterLink be used to do this in some way? Etc.

Thanks for your help.

Tony

Kromer
Top 500 Contributor
Randers, Denmark
Posts 129
OFFLINE
Bronze Member
Kromer replied on Fri, Jan 17 2020 9:35 AM

You can use a blc nl/ml connected via master link to ouverture. The blc needs a wired connection to your network though.

Then it's just using the b&o app.

matador43
Top 75 Contributor
Posts 1,373
OFFLINE
Bronze Member
matador43 replied on Fri, Jan 17 2020 12:11 PM

Guy:

 

That's the guy who makes the Beo4 to remote plugs converter! A trustful seller.

 

trackbeo
Top 150 Contributor
Posts 644
OFFLINE
Bronze Member
trackbeo replied on Fri, Jan 17 2020 4:06 PM

soloAudio:
I tried a universal remote control hub, but sadly discovered it is unable to learn the Beo4 IR codes
I have used a Logitech Harmony with a BeoSystem 6500.  It cannot learn Beo4 button-presses, but it comes pre-programmed with most of the buttons, once you configure it to add a BeoWhatever to its list of your components.  However, not sure if you can "drive it" with any commands except from Logitech's own iOS App.

soloAudio
Not Ranked
Posts 5
OFFLINE
Bronze Member

After checking around, I decided to do this myself using a very inexpensive ESP32. With a Vishay TSAL4400 emitter, and the RMT peripheral driver and a WiFi server. I can now successfully control my Beosound Ouverture using power on and off, and raise the volume up and down, from my iPhone.

Thanks for everyone's help.

Tony

klbu
Not Ranked
Posts 3
OFFLINE
Bronze Member
klbu replied on Thu, Apr 30 2020 12:37 PM

Hi,

 

Seems interesting to me - I'm in need for a controller that can turn on a Beolab 3500 without using the Beo4 - would it be possible for you to elaborate further to how you made the setup and the code needed for the ESP?

klbu
Not Ranked
Posts 3
OFFLINE
Bronze Member
klbu replied on Thu, Apr 30 2020 12:37 PM

Hi,

 

Seems interesting to me - I'm in need for a controller that can turn on a Beolab 3500 without using the Beo4 - would it be possible for you to elaborate further to how you made the setup and the code needed for the ESP?

soloAudio
Not Ranked
Posts 5
OFFLINE
Bronze Member
soloAudio replied on Mon, May 4 2020 11:37 PM

Hi klbu,

Yes, I can provide details on how I did this. I'll need some time to gather this up and will post.

Thanks

klbu
Not Ranked
Posts 3
OFFLINE
Bronze Member
klbu replied on Tue, May 5 2020 8:39 AM

Hi,

Thanks for your answer - that would be really great if you can provide me with the details.

/Klaus

soloAudio
Not Ranked
Posts 5
OFFLINE
Bronze Member
Hi Klaus,

Here are the details of how I solved this. I hope this is helpful for you.

Thanks,
Tony

Hardware

Espressif ESP32-WROOM-32D ($14.95+tax)

But I see it now at a lower cost ($10+tax) at Mouser:

There are also many other variations of ESP32, and other choices might be simpler to use (includes JTAG interface, etc.).

I used a Vishay TSAL4400 emitter (940nm, 15ns rise time). You can get them at a variety of places, such as:

The circuit is a simple voltage divider for an LED. Per the specification, I aimed for the forward voltage of 1.35V (forward current of 100mA, 20ms), which calculates out to a 19.5ohm resistor (1/4W). But as I only had a 15ohm resistor, I used a 1/2W (130mA). Note that the radiant intensity is directly correlated to the forward current, and that the transmitter is only on for 200us max (see below), so if you want to extend the range, you certainly can. I found this circuit worked at around 15 feet (4.6m), so this was fine for my application.

Here’s what it looks like (using GPIO pin 2):



Software

I used the Arduino IDE and Espressif Arduino library for faster development:

But you can also do it with just Espressif’s library, and any IDE you want (Eclipse, PlatformIO, etc.):

For setting up your computer installation, connection to device, etc. I point you to the Espressif documentation, of which there is plenty and which I found to be surprisingly good.

Over the Air Protocol

It took a fair amount of research to figure out the codes for the Beo4, and how they are supposed to be properly transmitted. Here are the details I have gathered and how I used the ESP32 RMT functionality to generate the IR signals. Many of the details can be seen from the code itself, but I’ll provide an overview here. I should stress that I came across disparate descriptions of some of what I describe below. For example, one reference (from B&O) clearly indicated four start bits which included the link bit, but other references from various sources included the link bit with the data bits (but it basically amounts to the same thing). Also, some sources indicated the number of data bits could be either 16 (or 17 if you include the link bit) or 19 (or 20 if you include the link bit). I had decided to simplify things by including the link bit with the start bits, and only using 16 data bits. 

The Beo4 transmitter modulates via Pulse Distance Coding using a 455kHz carrier, thus a bit value is defined by the time between the pulses. The carrier pulse (CP) duration is always 200us, and itself consists of a series of on and off pulses of wavelength 2.197802198 us. The remaining time (with the CP duration included) is defined as one of five values:

t1: 3125us = 0 bit                      (a bit transition from 1 to 0)
t2: 6250us = same as last bit (a bit transition from 0 to 0 or 1 to 1)
t3: 9375us = 1 bit                      (a bit transition from 0 to 1)
t4: 12500us = stop bit
t5: 15625us = start bit

Codes (such as "Power All Off") are transmitted in a Frame, comprised of 4 start bits, 16 data bits, 1 stop bit, and 1 trailer bit. There are 16 data bits (8 address bits, 8 command bits). The trailer bit is 200us (i.e. just the carrier pulse, I assume to delineate the stop bit).

The first two starter bits are for AGC, and are each t1; I do not know what would happen if other bit value combinations are used. They are followed by the start bit t5, and then the link bit t1 (as I always used zero for the link bit). So the first four bits would look like:
Starter Bits: 200us CP + 2925us, 200us CP + 2925us, 200us CP + 15425us, 200us CP + 2925us (i.e. t1, t1, t5, t1).

Next comes the data bits. The upper 8 data bits (called the address bits) indicates the device code. For example, the device code “Source All” is 0x0F. The lower 8 bits are the key command. For example, the key command for “Power SysOff” is 0x0C. So the code to turn everything off is 0x0F0C. This would be followed by the Stop bit and the Trailer bit. For example, the entire transmission for "Power SysOff" would be:

t1, t1, t5, t1, t2, t2, t2, t2, t3, t2, t2, t2, t1, t2, t2, t2, t3, t2, t1, t2, t4, T (where T is the last carrier pulse for the Trailer bit)

Using the ESP32 Remote Control (RMT) Feature

The reason I picked the ESP32 is it had the RMT feature which allowed me to define the carrier frequency, as this is not a standard frequency used by nearly every other IR device. You can see this setup in rmt_tx_init (using config.tx_config.carrier_freq_hz). The RMT feature also allowed me to clock divide the clock used for transmission time. Since the default clock to RMT is 80MHz, I set the clock divide to 80 (using config.clk_div) so that data in RAM representing the signal could be indicated in nice units of microseconds. Note too that the RMT can access memory in one of two ways; either dedicated channels, or via direct writes from any RAM location to the transmitter (all handled by the provided driver). I chose the latter (using rmt_write_items) so I didn’t have to worry about long commands, or combining channel memories, etc. I did not find any issue with the corresponding increase in latency.

So in effect, the signals to transmit are laid out in memory as a sequence of 32 bit words. Each 32 bit word consists of two 16 bit operations. Each operation consists of a 15 bit duration (in units of 1us based on my clock divide), and one bit of either a value of 0 or 1. For example, if I wanted to turn the carrier on for 200us, and off for 200us, I would use two paired 16 bit values of (200, 1)(200, 0). The driver provides a data structure (rmt_item32_t) for setting this up. 

So, to send the “Power SysOff” command, the memory would look like this:

static const rmt_item32_t beo4_cmds[] = {
    // All Off (0x0F0C)
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T1, 0 }}}, // Start Bit 1 - AGC
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T1, 0 }}}, // Start Bit 2 - AGC
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T5, 0 }}}, // Start Bit 3 - Start
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T1, 0 }}}, // Start Bit 4 - Link Bit
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}}, // Data Start
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T3, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T1, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T3, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T1, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}}, // Data End
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T4, 0 }}}, // Stop Bit
    {{{ PULSE_ON_T1, 1, 0, 0 }}},                           // Trailer Pulse, End RMT
<snip>

In this way, different commands can be sent using different offsets into the array beo4_cmds. I think the code is self explanatory.

Application

I used the WiFi Server driver to easily do this. I experimented with making the ESP32 an Access Point and using the RemoteXY application, but I found this to be too cumbersome from the iPhone. Instead, the ESP32 connects to my LAN, and I use very basic HTML to send the request (ON, OFF, VOL UP,  VOL DOWN) and “GET” on the server to process the command and transmit the proper signal. While I found this to be a tad sluggish, it does work. When you boot and run the code, you can see the IP address displayed in the serial monitor. Then you can open a browser on your phone, and type that IP address as the URL. At this point, you should see the ON/OFF/VOL UP/VOL DOWN buttons displayed. Note that when using DHCP, I usually would get the same IP address assigned to the ESP32, but it could potentially change. So instead, I assigned my device using a static IP address (using WiFi.config).

Here’s what the browser looks like when you connect to the ESP32. The Beo4 Power button toggles between ON and OFF when pressed.



On the iPhone, I created a icon for easy access (to save the trouble of typing the IP address for the URL), and used a tools called CallMeIcons to jam an image onto  it. Here’s what that looks like on my iPhone:



Caveats/Issues

For my Beo4, I need to have it set to AUX ahead of time. That is, if I leave the Beo4 set to CD or something else, then use my application to turn on/off, it won’t work. I can manually press List then Aux on the Beo4, but if I try sending that sequence of commands via my device, it doesn’t seem to work. I am not entirely sure why this is the case. More experimentation is needed.

Future Additions

- Been experimenting with using WiFi power management to limit energy usage as this device is on all the time, and mostly doing very little. I haven’t had time to work on this yet.
- I want to write my own Apple application to see how this would work (and to learn).
- The ESP32 device is crazy powerful, with two cores! Surely there are other things that this device can be doing while sitting there as an RC emulator.

Code

Disclaimer: Please note that I borrowed heavily from other sources to build this code. There are many examples in the Espressif repo, and of course elsewhere on the internet.

Note: When you cut/paste this into Arduino, if you get any compiler errors, check the error lines for bad single quotes (‘) or double quotes (“) that were corrupted when pasted.

Note: Let me know if you have any issues. I couldn’t re-test the code before posting as I uninstalled the port drivers to work on another project, and I didn’t want to redo all that just yet.

/*
 * WiFiServer + ESP32 Remote Control Test Project for Beo4
 * soloAudio for Beoworld (https://archivedforum2.beoworld.org/)
 * February 22, 2020
 */

#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "driver/rmt.h"
#include <WiFi.h>
#include <WiFiClient.h>
#include <WiFiServer.h>
#include <ESPmDNS.h>

// set static IP address
IPAddress local_IP(W, X, Y, Z);      // e.g. W=192, X=168, W=1, Z=300 
IPAddress gateway(W, X, Y, Z);
IPAddress subnet(W, X, Y, Z);        // e.g. W=255, X=255, Y=255, Z=0
IPAddress primaryDNS(W, X, Y, Z);    // optional
IPAddress secondaryDNS(W, X, Y, Z);  // optional, could be W=0, X=0, Y=0, Z=0

// network credentials
const char *ssid = “<Your SSID>";
const char *password = “<Your Password>";

// Set web server port number to 80
WiFiServer server(80);

// Variable to store the HTTP request
String header;

// state variable for power on/off
String beo4PowerState = "off";

#define RMT_TX_CHANNEL RMT_CHANNEL_0
#define RMT_TX_GPIO    2

#define PULSE_ON_T1  200
#define PULSE_OFF_T1 2925    // 3125
#define PULSE_OFF_T2 6050    // 6250
#define PULSE_OFF_T3 9175    // 9375
#define PULSE_OFF_T4 12300   // 12500
#define PULSE_OFF_T5 15425   // 15625

#define BEO4_CMD_SIZE        22                     // sizeof(beo4_cmds) / sizeof(beo4_cmds[0])
#define ALL_OFF_OFFSET       (BEO4_CMD_SIZE * 0)
#define AUDIO_GO_OFFSET      (BEO4_CMD_SIZE * 1)
#define AUDIO_LIST_OFFSET    (BEO4_CMD_SIZE * 2)
#define AUDIO_VUP_OFFSET     (BEO4_CMD_SIZE * 3)
#define AUDIO_VDOWN_OFFSET   (BEO4_CMD_SIZE * 4)

// Remote Control Commands Using 1MHz clock
static const rmt_item32_t beo4_cmds[] = {
    // All Off (0x0F0C)
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T1, 0 }}}, // Start Bit 1 - AGC
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T1, 0 }}}, // Start Bit 2 - AGC
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T5, 0 }}}, // Start Bit 3 - Start
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T1, 0 }}}, // Start Bit 4 - Link Bit
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}}, // Data Start
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T3, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T1, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T3, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T1, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}}, // Data End
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T4, 0 }}}, // Stop Bit
    {{{ PULSE_ON_T1, 1, 0, 0 }}},            // Trailer Pulse, End RMT
    // Audio-Go (0x0135)
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T1, 0 }}}, // Start Bit 1 - AGC
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T1, 0 }}}, // Start Bit 2 - AGC
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T5, 0 }}}, // Start Bit 3 - Start
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T1, 0 }}}, // Start Bit 4 - Link Bit
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}}, // Data Start
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T3, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T1, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T3, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T1, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T3, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T1, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T3, 0 }}}, // Data End
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T4, 0 }}}, // Stop Bit
    {{{ PULSE_ON_T1, 1, 0, 0 }}},            // Trailer Pulse, End RMT
    // Audio-List (0x0158)
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T1, 0 }}}, // Start Bit 1 - AGC
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T1, 0 }}}, // Start Bit 2 - AGC
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T5, 0 }}}, // Start Bit 3 - Start
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T1, 0 }}}, // Start Bit 4 - Link Bit
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}}, // Data Start
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T3, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T1, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T3, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T1, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T3, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T1, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}}, // Data End
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T4, 0 }}}, // Stop Bit
    {{{ PULSE_ON_T1, 1, 0, 0 }}},            // Trailer Pulse, End RMT
    // Audio-Volume Up (0x0160)
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T1, 0 }}}, // Start Bit 1 - AGC
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T1, 0 }}}, // Start Bit 2 - AGC
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T5, 0 }}}, // Start Bit 3 - Start
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T1, 0 }}}, // Start Bit 4 - Link Bit
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}}, // Data Start
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T3, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T1, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T3, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T1, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}}, // Data End
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T4, 0 }}}, // Stop Bit
    {{{ PULSE_ON_T1, 1, 0, 0 }}},            // Trailer Pulse, End RMT
    // Audio-Volume Down (0x0164)
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T1, 0 }}}, // Start Bit 1 - AGC
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T1, 0 }}}, // Start Bit 2 - AGC
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T5, 0 }}}, // Start Bit 3 - Start
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T1, 0 }}}, // Start Bit 4 - Link Bit
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}}, // Data Start
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T3, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T1, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T3, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T1, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T3, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T1, 0 }}},
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T2, 0 }}}, // Data End
    {{{ PULSE_ON_T1, 1, PULSE_OFF_T4, 0 }}}, // Stop Bit
    {{{ PULSE_ON_T1, 1, 0, 0 }}}             // Trailer Pulse, End RMT
};

// RMT Tx Channel Initialization
static void rmt_tx_init(void)
{
    rmt_config_t config;

    config.rmt_mode = RMT_MODE_TX;
    config.channel  = RMT_TX_CHANNEL;
    config.gpio_num = (gpio_num_t)RMT_TX_GPIO;
    config.mem_block_num = 1;
    config.tx_config.carrier_level = RMT_CARRIER_LEVEL_HIGH;
    config.tx_config.idle_level = RMT_IDLE_LEVEL_LOW;
    config.tx_config.loop_en = false;
    config.tx_config.idle_output_en = true;
    config.tx_config.carrier_en = true;
    config.tx_config.carrier_duty_percent = 50;
    config.tx_config.carrier_freq_hz = 455000;
    config.clk_div = 80;

    ESP_ERROR_CHECK(rmt_config(&config));
    
    ESP_ERROR_CHECK(rmt_driver_install(config.channel, 0, 0));
}

// Arduino Setup
void setup() 
{
  periph_module_enable(PERIPH_RMT_MODULE);

  ESP_LOGI(TAG, "Configuring transmitter");
    
  rmt_tx_init();

  rmt_set_source_clk(RMT_TX_CHANNEL, RMT_BASECLK_APB); // RMT_BASECLK_APB or RMT_BASECLK_MAX or RMT_BASECLK_REF

  WiFi.config(local_IP, gateway, subnet, primaryDNS, secondaryDNS);

  Serial.begin(115200);

  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  Serial.println("");
  
  while (WiFi.status() != WL_CONNECTED) 
  {
    delay(500);
    Serial.print(".");
  }
  
  // Print local IP address and start web server
  Serial.println("");
  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  if (MDNS.begin("esp32")) 
  {
    Serial.println("MDNS responder started");
  }

  // force B&O off
  ESP_ERROR_CHECK(rmt_write_items(RMT_TX_CHANNEL, &beo4_cmds[ALL_OFF_OFFSET], BEO4_CMD_SIZE, true));
  
  server.begin();
  Serial.println("WiFi server started");
}

// Arduino Loop
void loop() 
{
  WiFiClient client;

  // listen for incoming clients
  client = server.available();
  
  if (client) 
  { 
    // if a new client connects,
    Serial.println("New Client.");          // print a message out in the serial port
    String currentLine = "";                // make a String to hold incoming data from the client
    
    while (client.connected()) 
    { 
      // loop while the client's connected
      if (client.available()) 
      { 
        // if there's bytes to read from the client,
        char c = client.read();             // read a byte, then
        Serial.write(c);                    // print it out the serial monitor
        header += c;
        if (c == '\n') 
        { 
          // if the byte is a newline character
          // if the current line is blank, you got two newline characters in a row.
          // that's the end of the client HTTP request, so send a response:
          if (currentLine.length() == 0) 
          {
            // HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
            // and a content-type so the client knows what's coming, then a blank line:
            client.println("HTTP/1.1 200 OK");
            client.println("Content-type:text/html");
            client.println("Connection: close");
            client.println();
            
            // check the command received
            if (header.indexOf("GET /Beo4_ON") >= 0) 
            {
              beo4PowerState = "on";

              ESP_ERROR_CHECK(rmt_write_items(RMT_TX_CHANNEL, &beo4_cmds[AUDIO_LIST_OFFSET], BEO4_CMD_SIZE, true));
              vTaskDelay(20 / portTICK_PERIOD_MS);
              ESP_ERROR_CHECK(rmt_write_items(RMT_TX_CHANNEL, &beo4_cmds[AUDIO_GO_OFFSET], BEO4_CMD_SIZE, true));
              
              ESP_LOGI(TAG, "Transmission complete");
              Serial.println("B&O ON\n");
            } 
            else if (header.indexOf("GET /Beo4_OFF") >= 0) 
            {
              beo4PowerState = "off";

              ESP_ERROR_CHECK(rmt_write_items(RMT_TX_CHANNEL, &beo4_cmds[ALL_OFF_OFFSET], BEO4_CMD_SIZE, true));
              
              ESP_LOGI(TAG, "Transmission complete");
              Serial.println("B&O OFF\n");
            }
            else if (header.indexOf("GET /Beo4_VOL_UP") >= 0)
            {
              ESP_ERROR_CHECK(rmt_write_items(RMT_TX_CHANNEL, &beo4_cmds[AUDIO_VUP_OFFSET], BEO4_CMD_SIZE, true));
            }
            else if (header.indexOf("GET /Beo4_VOL_DOWN") >= 0)
            {
              ESP_ERROR_CHECK(rmt_write_items(RMT_TX_CHANNEL, &beo4_cmds[AUDIO_VDOWN_OFFSET], BEO4_CMD_SIZE, true));
            }
            
            // display the HTML web page
            client.println("<!DOCTYPE html><html>");
            client.println("<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
            client.println("<link rel=\"icon\" href=\"data:,\">");
            // CSS to style the on/off buttons 
            // Feel free to change the background-color and font-size attributes to fit your preferences
            client.println("<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}");
            client.println(".button { background-color: #4CAF50; border: none; color: white; padding: 16px 40px;");
            client.println("text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}");
            client.println("</style></head>");
            
            // Web Page Heading
            client.println("<body><h1>Bang & Olufsen Beo4 Remote Control</h1>");
            
            // display current state, and ON/OFF button 
            client.println("<p>Beo4 Power - State " + beo4PowerState + "</p>");
            
            // if the beo4PowerState is off, it displays the ON button       
            if (beo4PowerState=="off") 
            {
              client.println("<p><a href=\"/Beo4_ON\"><button class=\"button\">ON</button></a></p>");
            } 
            else // if the beo4PowerState is on, it displays the OFF button
            {
              client.println("<p><a href=\"/Beo4_OFF\"><button class=\"button\">OFF</button></a></p>");
            }

            client.println("<p>Beo4 Volume Up</p>");
            client.println("<p><a href=\"/Beo4_VOL_UP\"><button class=\"button\">VOL UP</button></a></p>");
            client.println("<p>Beo4 Volume Down</p>");
            client.println("<p><a href=\"/Beo4_VOL_DOWN\"><button class=\"button\">VOL DOWN</button></a></p>");
               
            client.println("</body></html>");
            
            // the HTTP response ends with another blank line
            client.println();

            // break out of the while loop
            break;
          } 
          else  // if you got a newline, then clear currentLine
          {
            currentLine = "";
          }
        } 
        else if (c != '\r') 
        {  
          // if you got anything else but a carriage return character,
          currentLine += c;      // add it to the end of the currentLine
        }
      }
    }
    
    // clear the header variable
    header = "";

    // close the connection
    client.stop();
    Serial.println("Client disconnected.");
    Serial.println("");
  }
}


matador43
Top 75 Contributor
Posts 1,373
OFFLINE
Bronze Member
matador43 replied on Fri, May 8 2020 10:06 AM

soloAudio:
Here are the details of how I solved this. I hope this is helpful for you.

Hi Tony,

Please confirm I've understood you well:

What you did is an IR blaster sending Beo4 streams, running a little web server you can control over network?
If it is so, I got a couple of questions for you:

- Could you add more controls. Like source selection, CD/track selection, Channel Selection etc… Would this be easy or complicated.
- From where do you take (lear) the IR controls? (related to the previous question).
- Could this work with non B&O equipment? And mixed brand (like Samsung TV with B&O sound systemWink)?

I feel like they could be very funny and useful things to do with this, dont know precisely what just now, but it opens possibilities.

Thank you. 

soloAudio
Not Ranked
Posts 5
OFFLINE
Bronze Member

Hi Tony,

Please confirm I've understood you well:

What you did is an IR blaster sending Beo4 streams, running a little web server you can control over network?

[Tony]

Yes, that is correct (as originally stated when this thread was started). It is just a WiFi controlled Beo4 emulator.

If it is so, I got a couple of questions for you:

- Could you add more controls. Like source selection, CD/track selection, Channel Selection etc… Would this be easy or complicated.

[Tony]

This would be “easy", given you have the right control sequences by extending the beo4_cmds table. I did have some problems (see Caveats/Issues), so more experimentation is needed.

- From where do you take (lear) the IR controls? (related to the previous question).

[Tony]

From various places. Anywhere I could actually. It was hard as there isn’t just one released reference. B&O doesn’t seem to want this information distributed. Many build receivers, and reverse engineer their RCs. In any case, my only goal was to turn the Ouverture on and off remotely, and wasn’t interested in a complete Beo4 replacement (at least not initially, but I do think it would be fun to keep extending it).

If you just search, you’ll find all kinds of references, and similar work done long ago. Here is just a few that I found:

- Could this work with non B&O equipment? And mixed brand (like Samsung TV with B&O sound systemWink)?

[Tony]

I don’t see why not. It would be a matter of re-configuring the RMT feature to use a different carrier frequency, and probably a different representation of pulse code modulation (bi-phasic coding, pulse distance coding, etc.). A different emitter would be needed (or one that can handle a range of frequencies, depending on how many other non-beo devices you wanted to control). An app button that switches the display to a different device would be used to initiate the reconfiguration, or each app button could uniquely indicate the device, etc. For me, everything I have now already comes with an app and already has WiFi support, so only my antiquated B&O Ouverture was an issue for me. But yes, I think such other projects are totally doable.

I feel like they could be very funny and useful things to do with this, dont know precisely what just now, but it opens possibilities.

Thank you. 

[Tony]

Yes, I totally agree. The device also includes Bluetooth and BLE, as well as a variety of physical buses, so we are only limited by our imaginations! ;)  Good luck!
matador43
Top 75 Contributor
Posts 1,373
OFFLINE
Bronze Member

Thank you Tony for your comprehensive answer,

It sounds like beyond my reach right now (specifically the part with mixed IR protocols) and at the same time very exciting. But anyway the door is open and i'll keep that somewhere in a corner of my mind for later.

Thanks again for your work and the knowledge you shared.

Frankston
Not Ranked
Posts 2
OFFLINE
Bronze Member
Frankston replied on Fri, Sep 24 2021 5:57 PM

Hi Tony,

thanx for your great work! Is there a way to send hex or raw code ?

 

I got this code (Bemooster 7000)

Radio:
1908FF000001001605003502DF00350F10003505EA003508F800350C05000001000202020202020
20302000202020202030404

CD:
1908FF000001001605003602DE00360F0F003605E9003608F600360C03000001000202020202020
20302000203000203000404

Phono:
19080082000500130C00320061004C008E004C005E0072002600260026004C01100026008800320
1C700370026003200C1009C00440076390D00010201030402050504060703080801090A0B

AUX:
190800820005000E0B005C003D00310058005C00F3005C009300310098004F0026005C006100260
0A800310076002600260031390E000102030405050607010809050A

Laut:
1908FF000001005506003002DD00300F11003005E9003008F700300C03003021990000010002020
2020202020300030200020202020405000001000202020202020203000302000202020204000001
000202020202020203000302000202020204000001000202020202020203000302000202020202

Leise:
1908FF000001005506003002DE00300F11003005E9003008F700300C03003021990000010002020
2020202020300030200020300020405000001000202020202020203000302000203000204000001
000202020202020203000302000203000204000001000202020202020203000302000203000202

Mute:
1908FF000001001605003502DF00350F10003505EA003508F800350C05000001000202020202020
20300020202030200030404


Input Phono: 
190800820005001A0E00350046003500CD00260053006B003D00350027002600270026003B00260
0F1006B006B006B00A40035005C006BFFFB0035007A003539080001000002030400040506070803
040904080A0B0C030105030D

Input AUX:
19080082000500130E00320065004B009C004B0062007000260026002600D40026009600E000320
09500570026007200D10032004A003D00260072009F003239060001020103040005060707080109
0A0B0C030D

Input V AUX: 
19080082000500110B002F004F002F00D4002F00760045008900570026002F002E0045005B00260
05600A701E200A700B4002F3902000102030000040401050605070108090A

Power OFF: 
1908FF000001001605003402DE00340F11003405EB003408F800340C05000001000202020202020
20300020202030200020404

Input Tape: 
1908FF000001001605003502DE00350F10003505EA003508F700350C04000001000202020202020
20302000203000202030404

Input Tape 2:
1908FF000001001605003602DD00360F0F003605E9003608F600360C04000001000202020202020
20302000203000300020404

0:
1908FF000001001605003502DE00350F10003505EA003508F800350C04000001000202020202020
20300020202020202020404

1:
1908FF000001001605003502E000350F10003505EA003508F700350C05000001000202020202020
20300020202020202030404

2:
1908FF000001001605003502DF00350F10003505EA003508F800350C05000001000202020202020
20300020202020203000404

3:
1908FF000001001605003502E000350F10003505EA003508F800350C05000001000202020202020
20300020202020203020404

4:
1908FF000001001605003502DE00350F10003505EA003508F600350C04000001000202020202020
20300020202020300020404

5:
1908FF000001001605003502DE00350F10003505EA003508F700350C04000001000202020202020
20300020202020300030404

6:
1908FF000001001605003502DF00350F12003505EA003508F800350C05000001000202020202020
20300020202020302000404

7:
1908FF000001001605003502DD00350F10003505EA003508F700350C05000001000202020202020
20300020202020302020404

8:
1908FF000001001605003502DF00350F10003505EA003508F600350C05000001000202020202020
20300020202030002020404

9:
1908FF000001001605003502DE00350F10003505EA003508F700350C05000001000202020202020 

20300020202030002030404

 

 

Frankston
Not Ranked
Posts 2
OFFLINE
Bronze Member
Frankston replied on Fri, Sep 24 2021 5:57 PM

Hi Tony,

thanx for your great work! Is there a way to send hex or raw code ?

 

I got this code (Bemooster 7000)

Radio:
1908FF000001001605003502DF00350F10003505EA003508F800350C05000001000202020202020
20302000202020202030404

CD:
1908FF000001001605003602DE00360F0F003605E9003608F600360C03000001000202020202020
20302000203000203000404

Phono:
19080082000500130C00320061004C008E004C005E0072002600260026004C01100026008800320
1C700370026003200C1009C00440076390D00010201030402050504060703080801090A0B

AUX:
190800820005000E0B005C003D00310058005C00F3005C009300310098004F0026005C006100260
0A800310076002600260031390E000102030405050607010809050A

Laut:
1908FF000001005506003002DD00300F11003005E9003008F700300C03003021990000010002020
2020202020300030200020202020405000001000202020202020203000302000202020204000001
000202020202020203000302000202020204000001000202020202020203000302000202020202

Leise:
1908FF000001005506003002DE00300F11003005E9003008F700300C03003021990000010002020
2020202020300030200020300020405000001000202020202020203000302000203000204000001
000202020202020203000302000203000204000001000202020202020203000302000203000202

Mute:
1908FF000001001605003502DF00350F10003505EA003508F800350C05000001000202020202020
20300020202030200030404


Input Phono: 
190800820005001A0E00350046003500CD00260053006B003D00350027002600270026003B00260
0F1006B006B006B00A40035005C006BFFFB0035007A003539080001000002030400040506070803
040904080A0B0C030105030D

Input AUX:
19080082000500130E00320065004B009C004B0062007000260026002600D40026009600E000320
09500570026007200D10032004A003D00260072009F003239060001020103040005060707080109
0A0B0C030D

Input V AUX: 
19080082000500110B002F004F002F00D4002F00760045008900570026002F002E0045005B00260
05600A701E200A700B4002F3902000102030000040401050605070108090A

Power OFF: 
1908FF000001001605003402DE00340F11003405EB003408F800340C05000001000202020202020
20300020202030200020404

Input Tape: 
1908FF000001001605003502DE00350F10003505EA003508F700350C04000001000202020202020
20302000203000202030404

Input Tape 2:
1908FF000001001605003602DD00360F0F003605E9003608F600360C04000001000202020202020
20302000203000300020404

0:
1908FF000001001605003502DE00350F10003505EA003508F800350C04000001000202020202020
20300020202020202020404

1:
1908FF000001001605003502E000350F10003505EA003508F700350C05000001000202020202020
20300020202020202030404

2:
1908FF000001001605003502DF00350F10003505EA003508F800350C05000001000202020202020
20300020202020203000404

3:
1908FF000001001605003502E000350F10003505EA003508F800350C05000001000202020202020
20300020202020203020404

4:
1908FF000001001605003502DE00350F10003505EA003508F600350C04000001000202020202020
20300020202020300020404

5:
1908FF000001001605003502DE00350F10003505EA003508F700350C04000001000202020202020
20300020202020300030404

6:
1908FF000001001605003502DF00350F12003505EA003508F800350C05000001000202020202020
20300020202020302000404

7:
1908FF000001001605003502DD00350F10003505EA003508F700350C05000001000202020202020
20300020202020302020404

8:
1908FF000001001605003502DF00350F10003505EA003508F600350C05000001000202020202020
20300020202030002020404

9:
1908FF000001001605003502DE00350F10003505EA003508F700350C05000001000202020202020 

20300020202030002030404

 

 

Page 1 of 1 (16 items) | RSS