Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP - import some of the changes from the fork #3

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
# Marantz ESP32 based remote control
# Marantz ESP8266 based remote control with mDNS.

Web based remote control for Matantz amplifier using ESP32.
Web based remote control for Matantz amplifier using ESP8266.
Codes for "optical" and "network" remote buttons on newer amplfiifers.

## About

This project implements web interface usable from mobile/tablet/desktop for my old Marantz amplifier.
It is using "Remote IN" socket and ESP32 GPIO output.
This project implements web interface usable from mobile/tablet/desktop for PM series of Marantz amplifiers but likely will work with any?.
It is using "Remote IN" socket and ESP8266 GPIO output.
I had to connect it via diode (i used **1N5408** but +- any should work) to avoid current from amplifier to the board when infrared RC is used.

You can find more about Marantz remote socket and wire protocol in my [blog post](https://smallhacks.wordpress.com/2021/07/07/controlling-marantz-amplifier-using-arduino-via-remote-socket/).
You can find more about Marantz remote socket and wire protocol in this [blog post](https://smallhacks.wordpress.com/2021/07/07/controlling-marantz-amplifier-using-arduino-via-remote-socket/).

## Some implementation notes

Project is using [ESPAsyncWebServer](https://github.com/me-no-dev/ESPAsyncWebServer)
to provide HTTP and implements 2 endpoints: `/` to output HTML web page (with all embedded images and simple js) and `/update?button=<id>` which allows to send commands to the device. Supported ID-s are "standby, phono, cd, tuner, aux1, aux2, dcc, tape, volume_up, volume_down". All SVG icons are embedded to the source code, you can use SPIFFS if you prefer them to be served as files.
to provide HTTP and implements 2 endpoints: `/` to output HTML web page (with all embedded images and simple js) and `/update?button=<id>` which allows to send commands to the device. Supported ID-s are "standby, phono, cd, tuner, optical, network, dcc, tape, volume_up, volume_down". All SVG icons are embedded to the source code, you can use SPIFFS if you prefer them to be served as files.

Initially device WIFI latency was very unstable, so power manager was disabled to fix that. In my code there is also bssid selection, to ensure that device always connects to the room AP. Remove it if you have only one AP or set according to your BSSID value.

HTML source could be found in the [html.h](html.h) header, it is using some rudimentary scripting (e.g. `onclick` instead of DOM listeners) to be compatible with my older iPad. Protocol is one-way, so its not possible to grab device status. HTTP was choosen as it is compatible with any web browser.

Expand Down
4 changes: 2 additions & 2 deletions html.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ const char index_html[] PROGMEM = R"rawliteral(
</div>
<h2>Inputs</h2>
<div class="flex-container">
<div onclick="presButton(event.currentTarget.id);" id="aux1"><img alt="Projector" height="40"
<div onclick="presButton(event.currentTarget.id);" id="network"><img alt="Projector" height="40"
src="data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMTQgMTQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0ibTAgMGgxNHYxNGgtMTR6IiBmaWxsPSJub25lIiB2aXNpYmlsaXR5PSJoaWRkZW4iLz48cGF0aCBkPSJtMSAxYy0uNTU0IDAtMSAuNDQ2LTEgMXYxMWMwIC41NTQuNDQ2IDEgMSAxaDEyYy41NTQgMCAxLS40NDYgMS0xdi0xMWMwLS41NTQtLjQ0Ni0xLTEtMXptLjI1IDFoLjVjLjEzODUgMCAuMjUuMTExNS4yNS4yNXYuNWMwIC4xMzg1LS4xMTE1LjI1LS4yNS4yNWgtLjVjLS4xMzg1IDAtLjI1LS4xMTE1LS4yNS0uMjV2LS41YzAtLjEzODUuMTExNS0uMjUuMjUtLjI1em0zLjc1IDMgNCAyLjUtNCAyLjV6bTcuMjUtM2guNWMuMTM4NSAwIC4yNS4xMTE1LjI1LjI1di41YzAgLjEzODUtLjExMTUuMjUtLjI1LjI1aC0uNWMtLjEzODUgMC0uMjUtLjExMTUtLjI1LS4yNXYtLjVjMC0uMTM4NS4xMTE1LS4yNS4yNS0uMjV6bS0xMSAyaC41Yy4xMzg1IDAgLjI1LjExMTUuMjUuMjV2LjVjMCAuMTM4NS0uMTExNS4yNS0uMjUuMjVoLS41Yy0uMTM4NSAwLS4yNS0uMTExNS0uMjUtLjI1di0uNWMwLS4xMzg1LjExMTUtLjI1LjI1LS4yNXptMTEgMGguNWMuMTM4NSAwIC4yNS4xMTE1LjI1LjI1di41YzAgLjEzODUtLjExMTUuMjUtLjI1LjI1aC0uNWMtLjEzODUgMC0uMjUtLjExMTUtLjI1LS4yNXYtLjVjMC0uMTM4NS4xMTE1LS4yNS4yNS0uMjV6bS0xMSAyaC41Yy4xMzg1IDAgLjI1LjExMTUuMjUuMjV2LjVjMCAuMTM4NS0uMTExNS4yNS0uMjUuMjVoLS41Yy0uMTM4NSAwLS4yNS0uMTExNS0uMjUtLjI1di0uNWMwLS4xMzg1LjExMTUtLjI1LjI1LS4yNXptMTEgMGguNWMuMTM4NSAwIC4yNS4xMTE1LjI1LjI1di41YzAgLjEzODUtLjExMTUuMjUtLjI1LjI1aC0uNWMtLjEzODUgMC0uMjUtLjExMTUtLjI1LS4yNXYtLjVjMC0uMTM4NS4xMTE1LS4yNS4yNS0uMjV6bS0xMSAyaC41Yy4xMzg1IDAgLjI1LjExMTUuMjUuMjV2LjVjMCAuMTM4NS0uMTExNS4yNS0uMjUuMjVoLS41Yy0uMTM4NSAwLS4yNS0uMTExNS0uMjUtLjI1di0uNWMwLS4xMzg1LjExMTUtLjI1LjI1LS4yNXptMTEgMGguNWMuMTM4NSAwIC4yNS4xMTE1LjI1LjI1di41YzAgLjEzODUtLjExMTUuMjUtLjI1LjI1aC0uNWMtLjEzODUgMC0uMjUtLjExMTUtLjI1LS4yNXYtLjVjMC0uMTM4NS4xMTE1LS4yNS4yNS0uMjV6bS0xMSAyaC41Yy4xMzg1IDAgLjI1LjExMTUuMjUuMjV2LjVjMCAuMTM4NS0uMTExNS4yNS0uMjUuMjVoLS41Yy0uMTM4NSAwLS4yNS0uMTExNS0uMjUtLjI1di0uNWMwLS4xMzg1LjExMTUtLjI1LjI1LS4yNXptMTEgMGguNWMuMTM4NSAwIC4yNS4xMTE1LjI1LjI1di41YzAgLjEzODUtLjExMTUuMjUtLjI1LjI1aC0uNWMtLjEzODUgMC0uMjUtLjExMTUtLjI1LS4yNXYtLjVjMC0uMTM4NS4xMTE1LS4yNS4yNS0uMjV6bS0xMSAyaC41Yy4xMzg1IDAgLjI1LjExMTUuMjUuMjV2LjVjMCAuMTM4NS0uMTExNS4yNS0uMjUuMjVoLS41Yy0uMTM4NSAwLS4yNS0uMTExNS0uMjUtLjI1di0uNWMwLS4xMzg1LjExMTUtLjI1LjI1LS4yNXptMTEgMGguNWMuMTM4NSAwIC4yNS4xMTE1LjI1LjI1di41YzAgLjEzODUtLjExMTUuMjUtLjI1LjI1aC0uNWMtLjEzODUgMC0uMjUtLjExMTUtLjI1LS4yNXYtLjVjMC0uMTM4NS4xMTE1LS4yNS4yNS0uMjV6Ii8+PC9zdmc+">
</div>
<div onclick="presButton(event.currentTarget.id);" id="tuner"><img alt="Radio" height="40"
Expand All @@ -90,7 +90,7 @@ const char index_html[] PROGMEM = R"rawliteral(
<div onclick="presButton(event.currentTarget.id);" id="phono"><img alt="Turntable" height="40"
src="data:image/svg+xml;base64,PHN2ZyBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCAzMDAgMzAwIiB2aWV3Qm94PSIwIDAgMzAwIDMwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJtLS41IDE1djI3MWgzMDB2LTI3MXoiLz48ZyBmaWxsPSIjYTZiZWM2Ij48cGF0aCBkPSJtMTM1LjUgMzMuOWMtNjQgMC0xMTYuMSA1Mi4xLTExNi4xIDExNi4xczUyLjEgMTE2LjEgMTE2LjEgMTE2LjEgMTE2LjEtNTIuMSAxMTYuMS0xMTYuMS01Mi4xLTExNi4xLTExNi4xLTExNi4xem0wIDIyMi42Yy01OC43IDAtMTA2LjUtNDcuOC0xMDYuNS0xMDYuNXM0Ny44LTEwNi41IDEwNi41LTEwNi41IDEwNi41IDQ3LjggMTA2LjUgMTA2LjUtNDcuOCAxMDYuNS0xMDYuNSAxMDYuNXoiLz48cGF0aCBkPSJtMTY5LjQgMTUwYzAtMTguNy0xNS4yLTMzLjktMzMuOS0zMy45cy0zMy45IDE1LjItMzMuOSAzMy45IDE1LjIgMzMuOSAzMy45IDMzLjkgMzMuOS0xNS4yIDMzLjktMzMuOXptLTU4LjEgMGMwLTEzLjMgMTAuOS0yNC4yIDI0LjItMjQuMnMyNC4yIDEwLjkgMjQuMiAyNC4yLTEwLjkgMjQuMi0yNC4yIDI0LjItMjQuMi0xMC45LTI0LjItMjQuMnoiLz48Y2lyY2xlIGN4PSIxMzUuNSIgY3k9IjE1MCIgcj0iNC44Ii8+PHBhdGggZD0ibTIxNy43IDE1MGMwIDQ1LjQtMzYuOSA4Mi4zLTgyLjMgODIuM3Y5LjdjNTAuNyAwIDkxLjktNDEuMiA5MS45LTkxLjloLTkuNnoiLz48cGF0aCBkPSJtMjUxLjYgNzIuNmg5Ljd2MTM4LjhjMCA4LjktMy40IDE3LjMtOS43IDIzLjdsLTEuNC0xLjRjLTQuNC00LjQtMTIuMS00LjQtMTYuNSAwbC00LjggNC44Yy00LjUgNC42LTQuNSAxMiAwIDE2LjUgMi4yIDIuMiA1LjEgMy40IDguMyAzLjRzNi4xLTEuMiA4LjMtMy40bDEyLjktMTIuOWM4LjItOC4yIDEyLjgtMTkuMiAxMi44LTMwLjh2LTEzOC43aDkuN3YtMzguN2gtMjl6bS0xMy4xIDE3NS42Yy0xIDEtMS44IDEtMi44IDAtLjgtLjgtLjgtMi4xIDAtMi44bDQuOC00LjhjLjUtLjUgMS4xLS42IDEuNC0uNnMuOS4xIDEuNC42bDEuNCAxLjR6bTIyLjgtMjA0LjdoOS43djE5LjRoLTkuN3oiLz48cGF0aCBkPSJtMzMuOSAyMzcuMWgtOS43djQuOGgtNC44djkuN2g0Ljh2MTQuNWg5Ljd2LTE0LjVoNC44di05LjdoLTQuOHoiLz48L2c+PC9zdmc+">
</div>
<div onclick="presButton(event.currentTarget.id);" id="aux2"><img alt="Audio IN" height="40"
<div onclick="presButton(event.currentTarget.id);" id="optical"><img alt="Optical 1/2" height="40"
src="data:image/svg+xml;base64,PHN2ZyBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCAzMTUuMzI3IDMxNS4zMjciIGhlaWdodD0iMzE1LjMyNyIgdmlld0JveD0iMCAwIDMxNS4zMjcgMzE1LjMyNyIgd2lkdGg9IjMxNS4zMjciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0ibTI1My40MzMgODQuMjQzLTM5Ljk3NSAzOS45NzUgMTguMDU2IDE4LjA1MS05NS40MjIgOTUuNDIyLTM4LjY4NiAxOC4wNDYtLjMyMy0uMzIxLTI1LjQ2OSAyNS40NjctOS4yOS05LjI5LTQyLjU1MSA0Mi44MjMtMTkuNzczLTE5Ljc3MSA0Mi41NTEtNDIuODE3LTkuNDA2LTkuNDA5IDI1Ljc2NC0yNS43NTYgMTcuODY1LTM4LjI3NiA5NS40Mi05NS40MyAxOS44MjcgMTkuODI1IDM5Ljk3NS0zOS45NzZ6bS0xOC44NTQtMjQuMDE5IDIxLjQzOCAyMS40MzggMjkuNjU1LTI5LjY1NS0yMS40MzgtMjEuNDM4em0zMy4wNDctMzMuMDM5IDIxLjQzOCAyMS40MzggNC45OTEtNC45OTQtMjEuNDM4LTIxLjQzOHptNDUuMjA1LTIzLjc3NS0yMS44NDItMi40OTgtMTAuMTU0IDIwLjQ2OCAxNC4wMjcgMTQuMDIyIDIwLjQ2NS0xMC4xNTR6Ii8+PC9zdmc+">
</div>
</div>
Expand Down
72 changes: 40 additions & 32 deletions marantz.ino
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
// marantz wired remote control web server
// inspired by https://github.com/Arduino-IRremote/Arduino-IRremote
// Ported code by samm-git for esp8266. Added mDNS capability, some code modifications.
// Module used here was a Wemos D1 Mini Pro.
// see https://smallhacks.wordpress.com/2021/07/07/controlling-marantz-amplifier-using-arduino-via-remote-socket/

#define IR_TX_PIN 12 // pin to use for tx, connect via diode to "remote in" socket
#define IR_TX_PIN 4 // pin to use for tx, connect via diode to "remote in" socket

// Replace with your network credentials
const char* ssid = "<wifi_ssid>";
const char* password = "<wifi_password>";
// bssid of the wifi AP if you want to connect to fixed base
byte bssid[] = {0x01,0x02,0x03,0x04,0x05,0x06};
// byte bssid[] = {0x01,0x02,0x03,0x04,0x05,0x06};

/* some definitions from the IRremote Arduino Library */
#define RC5_ADDRESS_BITS 5
Expand All @@ -22,13 +24,13 @@ byte bssid[] = {0x01,0x02,0x03,0x04,0x05,0x06};
#define RC5_DURATION (15L * RC5_UNIT) // 13335
#define RC5_REPEAT_PERIOD (128L *RC5_UNIT) // 113792
#define RC5_REPEAT_SPACE (RC5_REPEAT_PERIOD - RC5_DURATION) // 100 ms
#define MDNS_HOSTNAME "amp" // mDNS hostname http://amp.local

// Import required libraries
#include <Preferences.h>
#include <WiFi.h>
#include <esp_wifi.h>
#include <AsyncTCP.h>
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <ESP8266mDNS.h> // Include the mDNS library
#include "html.h"

// Create AsyncWebServer object on port 80
Expand Down Expand Up @@ -76,7 +78,7 @@ int sendRC5(uint8_t aAddress, uint8_t aCommand, uint_fast8_t aNumberOfRepeats)
sLastSendToggleValue = 0;
}

uint_fast8_t tNumberOfCommands = aNumberOfRepeats + 1;
uint_fast8_t tNumberOfCommands = aNumberOfRepeats;

while (tNumberOfCommands > 0)
{
Expand Down Expand Up @@ -158,6 +160,7 @@ int sendRC5_X(uint8_t aAddress, uint8_t aCommand, uint8_t aExt, uint_fast8_t aNu
Serial.print("<p>");
#endif
// space marker for marantz rc5 extension
digitalWrite(IR_TX_PIN, LOW);
delayMicroseconds(RC5_UNIT *2 *2);
}
}
Expand Down Expand Up @@ -228,50 +231,55 @@ void setup()
// GET input1 value on <ESP_IP>/update?output=<inputMessage1>&state=<inputMessage2>
if (request->hasParam("button")) {
inputMessage1 = request->getParam("button")->value();
if (strcmp ("standby", inputMessage1.c_str()) == 0) {
sendRC5(16, 12, 1);
if (strcmp("standby", inputMessage1.c_str()) == 0) {
sendRC5(16, 12, 1);
}
if (strcmp ("phono", inputMessage1.c_str()) == 0) {
sendRC5(21, 63, 1);
if (strcmp("phono", inputMessage1.c_str()) == 0) {
sendRC5(21, 63, 1);
}
if (strcmp ("cd", inputMessage1.c_str()) == 0) {
sendRC5(20, 63, 1);
if (strcmp("cd", inputMessage1.c_str()) == 0) {
sendRC5(20, 63, 1);
}
if (strcmp ("tuner", inputMessage1.c_str()) == 0) {
sendRC5(17, 63, 1);
if (strcmp("tuner", inputMessage1.c_str()) == 0) {
sendRC5(17, 63, 1);
}
if (strcmp ("aux1", inputMessage1.c_str()) == 0) {
sendRC5_X(16, 0, 6, 1);
if (strcmp("network", inputMessage1.c_str()) == 0) {
sendRC5_X(25, 63, 10, 1);
}
if (strcmp ("aux2", inputMessage1.c_str()) == 0) {
sendRC5_X(16, 0, 7, 1);
if (strcmp("optical", inputMessage1.c_str()) == 0) {
sendRC5_X(16, 1, 40, 1);
}
if (strcmp ("dcc", inputMessage1.c_str()) == 0) {
sendRC5(23, 63, 1);
if (strcmp("dcc", inputMessage1.c_str()) == 0) {
sendRC5(23, 63, 1);
}
if (strcmp ("tape", inputMessage1.c_str()) == 0) {
sendRC5(18, 63, 1);
if (strcmp("tape", inputMessage1.c_str()) == 0) {
sendRC5(18, 63, 1);
}
if (strcmp ("volume_up", inputMessage1.c_str()) == 0) {
sendRC5(16, 16, 1);
if (strcmp("volume_up", inputMessage1.c_str()) == 0) {
sendRC5(16, 16, 1);
}
if (strcmp ("volume_down", inputMessage1.c_str()) == 0) {
sendRC5(16, 17, 1);
if (strcmp("volume_down", inputMessage1.c_str()) == 0) {
sendRC5(16, 17, 1);
}
}
else {
} else {
inputMessage1 = "No message sent";
}
Serial.print("Button: ");
Serial.print(inputMessage1);
Serial.print("\n");
request->send(200, "text/plain", "OK\n");
});



if (!MDNS.begin(MDNS_HOSTNAME)) { // Start the mDNS responder for amp.local
Serial.println("Error setting up MDNS responder!");
}
Serial.println("mDNS responder started");
MDNS.addService("http", "tcp", 80);
// Start server
server.begin();
}

void loop()
{
void loop() {
MDNS.update();
}