{"id":17799961,"url":"https://github.com/chatziko/audioengine-a5-controller","last_synced_at":"2025-03-17T07:31:08.696Z","repository":{"id":44475774,"uuid":"411769147","full_name":"chatziko/audioengine-a5-controller","owner":"chatziko","description":"Embedded controller for Audioengine A5+","archived":false,"fork":false,"pushed_at":"2024-03-11T09:10:02.000Z","size":1441,"stargazers_count":21,"open_issues_count":1,"forks_count":0,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-02-27T19:36:19.762Z","etag":null,"topics":["esp32","esphome","home-assistant"],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/chatziko.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-09-29T17:29:08.000Z","updated_at":"2025-01-26T13:31:33.000Z","dependencies_parsed_at":"2023-02-14T23:05:23.352Z","dependency_job_id":"e9faf428-f7c4-4b2f-9320-47254ba8e06f","html_url":"https://github.com/chatziko/audioengine-a5-controller","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chatziko%2Faudioengine-a5-controller","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chatziko%2Faudioengine-a5-controller/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chatziko%2Faudioengine-a5-controller/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chatziko%2Faudioengine-a5-controller/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/chatziko","download_url":"https://codeload.github.com/chatziko/audioengine-a5-controller/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243848120,"owners_count":20357494,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["esp32","esphome","home-assistant"],"created_at":"2024-10-27T12:13:31.273Z","updated_at":"2025-03-17T07:31:08.322Z","avatar_url":"https://github.com/chatziko.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"## Embedded controller for Audioengine A5+\n\n![](images/home-assistant-ui.png)\n\nThe [Audioengine A5+](https://audioengineusa.com/shop/poweredspeakers/a5-plus-classic-speakers/)\n(classic model, no bluetooth) is a fantastic set of powered speakers. However,\nit might be annoying to have an essential part of your living room controlled\nonly by a physical button and IR remote.\n\nThis project builds an ESP32 controller (based on [ESPHome](https://esphome.io/))\nembedded in Audioengine A5+, with two main features:\n\n1. Remote control and automation\n    - The speaker's Power (sleep), Volume and Mute can be controlled via [Home Assistant](https://home-assistant.io/) and used in automations.\n    - Built-in controls (IR, rotary, button) are still usable and update the values\n        in Home Assistant.\n    - Some simple automations can run in the controller itself, eg a timer to\n      turn the speaker off at night if it has been idle for too long.\n\n2. Full customization of built-in controls. For example:\n    - The LED brightness indicates the volume level.\n    - The LED turns off (instead of blinking) when the speaker is off.\n    - Long pressing the physical button sets the volume to a preset level.\n    - Smoother rotary control.\n    - The IR codes can be modified, eg to replace the original remote with an arbitrary\n      one, use multiple remotes simultaneously, use unutilized buttons from the TV remote\n      to control the speakers, etc.\n    - An IR command can be added to set the volume to a preset level.\n    - Or even add IR commands that trigger any Home Assistant automation.\n\n\n## Idea\n\nPowered speakers are often automated by sending IR commands from some hub\nor Arduino/ESP device. However this has several drawbacks:\n\n- Requires an IR transmitter within line-of-sight from the speaker.\n- IR is often unreliable.\n- Most importantly, Home Assistant is completely unaware of any manual inputs to\nthe speaker. This is particularly annoying for speakers with a single \"toggle\npower\"  command (instead of separate \"on\" and \"off\"), such as the Audioengine\nA5+.  We might want to turn the speaker on and end up doing the exact opposite,\nbecause the speaker was already manually turned on!\n\nSo we opt for a more advanced solution:\n- We add an ESP32 device __embedded inside__ the speaker, in-between the\nbuilt-in controls and the speaker's MCU.\n- All built-in controls are handled by the ESP32 based on our code.\n- The ESP32 communicates changes to the speaker by simulating\n    outputs from the built-in controls.\n\nThis way everything happens inside the speaker, and we can fully control\nits state.\n\n## Hardware\n\n__WARNING:__ I'm just a hobbyist, I don't pretend I really know what I'm doing. Proceed at your own risk.\n\n#### Audioengine A5+ built-in controls\n\nThe A5 has 3 controls located at the front of the speaker: a rotary encoder (with button), an IR receiver and an LED. All three are connected to the\nA5's board via a cable, with a 9-pin JST connector\nso that it can be easily removed.\nThe image below shows the cable and the connector on the A5 side\n(the following [youtube video](https://www.youtube.com/watch?v=RtI95P0j_7A)\nalso shows the internals of A5):\n\n![](images/speaker-board.svg)\n\nAll three controls are standard, so a bit of reverse engineering reveals the\npurpose of each pin (counting from left to right in the A5 connector pictured\nabove):\n\n```\nRotary encoder (pins 1-4)\n1. Rotary output A\n2. Rotary output B\n3. Button output\n4. GND\n\nIR receiver (pins 5-7)\n5. Output\n6. GND\n7. VCC\n\nLED (pins 8-9)\n8. + side\n9. - side\n```\nNote that the A5 uses 5V logic, and all input pins have pullups.\n\n\n#### Components we need\n\n- An ESP32 development board (I used [this one](https://www.mischianti.org/wp-content/uploads/2020/11/ESP32-DOIT-DEV-KIT-v1-pinout-mischianti.png), ESP8266 might also be sufficient).\n- A Logic Level Shifter with at least 4 channels (like [this one](https://github.com/sparkfun/Logic_Level_Bidirectional)).\n- Two capacitors (0.1 μF).\n- Two resistors (100Ω and 1KΩ).\n- JST XH2.54 9-pin male and female connectors.\n- An LED (optional for debugging).\n- A 4cm x 6cm prototype board and spacers.\n\nThe plan is to connect our ESP32 between the controls and the A5.\n\n\n#### Connecting the controls to the ESP32\n\nThe connections between the controls and the ESP32 are shown below.\nThe GPIOs can vary of course, depending on the exact board we use.\n```\nRotary\n1. Pin A   \u003c-\u003e  GPIO05    \u003c-\u003e  0.1 μF cap.  \u003c-\u003e  GND\n2. Pin B   \u003c-\u003e  GPIO18    \u003c-\u003e  0.1 μF cap.  \u003c-\u003e  GND\n3. Button  \u003c-\u003e  GPIO19\n4. GND     \u003c-\u003e  GND\n\nIR\n5. Signal  \u003c-\u003e  GPIO03\n6. GND     \u003c-\u003e  GND\n7. VCC     \u003c-\u003e  3V\n\nLED\n8. + side  \u003c-\u003e  100Ω res.  \u003c-\u003e  GPIO23\n9. - side  \u003c-\u003e  GND\n```\n\nThe capacitors are useful for [cleaning up](https://www.candrian.gr/index.php/3-pin-rotary-encoder-how-to/) the signal from the rotary\nencoder. The resulting behavior is much better that the A5's original\none, no more turning the knob one way and have the volume jump the other way.\n\nNote also that the IR receiver works happily with the ESP32's 3V logic.\n\n\n#### Connecting the A5 board to the ESP32\n\nOn the other side, we need to connect ESP32 to A5 so that we can output data to\nit. Since A5's inputs have 5V pullups, we need all communication to pass through\nthe logic level shifter, to avoid frying the ESP32.  The connections are shown\nbelow:\n\n```\nRotary\n1 Output A  \u003c-\u003e  HV1/LV1   \u003c-\u003e  GPIO04\n2 Output B  \u003c-\u003e  HV4/LV4   \u003c-\u003e  GPIO16\n3 Button    \u003c-\u003e  HV3/LV3   \u003c-\u003e  GPIO17\n4. GND      discon. (we already have common GND from pin 6 below)\n\nIR\n5. Output   \u003c-\u003e  HV2/LV2   \u003c-\u003e  GPIO15\n6. GND      \u003c-\u003e                 GND\n7. VCC      discon. (ESP32 has its own power)\n\nLED\n8. + side   \u003c-\u003e  1ΚΩ res.  \u003c-\u003e  Debug LED +\n9. - side   \u003c-\u003e                 Debug LED -\n```\nNote that the front LED is controlled by the ESP32, so the A5's LED output\nis not needed. Still, it's useful to add an LED to our board and connect\nit to A5's output, to be able to see the speaker's original light while\ndebugging.\n\n\n#### Powering the ESP32\n\nThe A5 has a USB plug in the back which can be used to charge a smartphone\nwhile playing music. The USB is connected to the 5V power supply shown\nin the image above, so we can very conveniently use it to power our ESP32.\n\nSince I don't use the USB (and it's more useful to repurpose it anyway, see below), I chose to \"steal\" the nice cable with the\nprotective foam that connected the USB to the 5V, so I desoldered it and\nadded it to my controller board. This also freed some space for\nmounting our board. But in principle there should be enough power for\nboth the ESP32 and the USB, if one wishes to keep it.\n\n\n#### Assembling and mounting the board\n\nThe board will go inside the speaker, so we need a clean job.  We put all the\npieces together, solder them to a 4cm x 6cm prototype board, add JST\nterminals for easy connection and the foamy cable for power. The result:\n\n![](images/controller.jpg)\n\nFinally, next to the A5's main board there is a smaller board\nholding some of the speaker's terminals in the back. Using some spacers,\nwe can very conveniently mount our board on top of it. There is\nenough space so that it does not interfere with any other component\nof the speaker. The result looks like it came straight from the factory:\n\n![](images/controller-installed.jpg)\n\n\n#### Repurposing the USB plug\n\nA downside of having the ESP32 board inside the speaker is that, if we lose\nwireless connectivity for any reason, we need to unmount and open the speaker\nto debug the issue and flash new firmware. This happened to me\nonce when experimenting with a buggy bluetooth component.\n\nA neat solution: connect the female USB plug in the back of the speaker to the devboard's\nUSB! The four pins of the plug are clearly visible at\nits mounting location on the board. We just need to connect a micro USB cable to the\ndevboard, cut the other end, and solder the wires directly on the four pins (make sure\nto respect the order).\nThen, with the speaker fully mounted, we can simply switch it off, connect the\nUSB plug to a laptop, and we\nget a serial connection for viewing logs, flashing firmware, etc.\n\n\n\n## Software\n\nTo program our controller we can use ESPHome. It provides all the components\nwe need as well as excellent integration with Home Assistant.\nThe controller code is available in\n[audioengine-a5-controller.yaml](audioengine-a5-controller.yaml), while\n[audioengine-a5.yaml](audioengine-a5.yaml) contains the generic node\nconfiguration (wifi, passwords, etc). To setup the board simply\nrun\n```\nesphome run audioengine-a5.yaml\n```\n\nHandling the input controls is easy, we can directly use ESPHome's\n[`rotary_encoder`](https://esphome.io/components/sensor/rotary_encoder.html) and\n[`remote_receiver`](https://esphome.io/components/remote_receiver.html)\ncomponents.\n\nCommunicating with the A5 board is a little bit more involved, since we need to\nsimulate the control outputs and send them to the A5's input pins:\n- For IR, we can use the\n[`remote_transmitter`](https://esphome.io/components/remote_transmitter.html)\ncomponent, and pretend that pin 5 of the A5 is an IR transmitter.\nSince it's not really a transmitter, we just need to set\n`carrier_duty_percent: 100%` to keep only the \"real\" (low frequency) IR signal\nwithout the (high frequency) modulation of an IR LED.\n\n- For the rotary encoder, we manually generate the encoder's signal\nby bit-banging, its format is quite simple. (I also tried to do it\nvia PWM, but it didn't seem to work and it wasn't worth the effort.)\n\nNote that the Volume Up/Down and Power commands can be sent either via\nIR or via the rotary encoder. It turns out that the rotary is both faster\nand more reliable, so I opt for using that, keeping IR only for the Mute\ncommand.\n\nThe communication with Home Assistant is done via the\n[`switch`](https://esphome.io/components/switch/gpio.html) (for Power/Mute)\nand [`number`](https://esphome.io/components/number/template.html) (for Volume)\ncomponents.\nChanges to these components trigger the `update_speaker_state` script which\ntransmits the changes to the speaker (serializing them in case of multiple changes).\nNote that the Volume is displayed in Home Assistant as a textbox by default;\nto display it as a slider use the following [lovelace\nplugin](https://github.com/thomasloven/lovelace-slider-entity-row).\n\nThe speaker's current state is stored in the `power_cur, mute_cur, volume_cur`\nglobal variables, which should always match the speaker's real state.\nIn case these variables become out of sync with the speaker for any reason,\nthe `sync_speaker_state` script is executed on every boot to restore the synchronization.\nThis is achieved by forcing the volume to 0 (by sending lots of \"Volume Down\" commands),\nand then increasing it again to the desired value.\n\n\n\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchatziko%2Faudioengine-a5-controller","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchatziko%2Faudioengine-a5-controller","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchatziko%2Faudioengine-a5-controller/lists"}