Open NonaSuomy opened 1 year ago
Give me a couple of days and I'll work on it. I think there are some better ways to do this now
Did they integrate your light stuff as well to master or just the DAC stuff, would I still need to use your code base to get the LED's working? Does the microphone work, and do you know if there is a manual way to input the LMS server's IP address into the esp32 so it knows where it is? My taudio on wifi and LMS docker are on two different subnets
The led control is still an open PR, so you would need my code base for that. I'm a few releases behind. If you have an issue ,I can look at remerging.
Never tried it, but check out the squeezelite command-line options for setting the LMS IP address (Accessible in the Audio/Optional settings). From a quick google, try -s
I don't believe the project supports audio-in or the microphone. As a LMS client, there is no real requirement. I think I did look at one point, but the quality pretty much sucked.
Ok, that would be great if you could do a remerge. Was there anything stopping them from merging your PR? can you link to it for me?
Thank you -s worked like a charm.
I just thought it would be cool if you could pass the mic into Home Assistant for audio controls (Alexa / Goog / or the open source projects) understandable though if LMS doesn't support that.
I've updated the configuration.yaml file. It is only an example, and some of the entries may need to be pasted into other files (see notes).
As mentioned, I wasn't too impressed with the mic on this device. I too, was looking at voice control. In the end, I concluded it did not make sense. If your going to use google or alexa, then you might as well use one of their devices. If you're going down the open source solutions, then you'll need something more powerful than a ESP32. I played around with Rhasspy and the 4-mic array, but again was not impressed.
I'll look at remerging when I have time, although there is not much new, and none relates to the t-audio platform. I did up a compiled release in my builds folder if you want to give it a try.
You’re awesome! Thanks will wait for that re-merge do you have a link to the past PR I can’t seem to find it. Wanted to see if they said anything about it etc? Maybe if resubmitted I could beg them to merge it.
I currently use the Alexa integration with HA and a Vector robot he has a 4 mic array on his back I believe not sure what they do differently but seems to work. https://www.digitaldreamlabs.com/products/vector-robot Got him for $80 on Amazon when the prior company was going under but too expensive now.
Is your binary compiling against the 4.3 branch?
Hope this helps, automations section was missing "trigger:" at the top and if you put input_text in its own yaml file you need to put quotes around the message: "{{ states('input_text.alert_trigger') }}"
configuration.yaml
# Loads default set of integrations. Do not remove.
default_config:
automation: !include automations.yaml
script: !include scripts.yaml
scene: !include scenes.yaml
input_text: !include input_text.yaml
input_number: !include input_number.yaml
notify: !include notify.yaml
light: !include light.yaml
light.yaml
# Squeezelite-esp32 T-Audio LED strip
- platform: template
lights:
taudio_good:
friendly_name: "Notify Good"
turn_on:
service: squeezebox.call_method
data:
entity_id: media_player.squeezelite_taudio
command: dmx
parameters:
- '0,80,0,0,80,0,0,80,0,0,80,0,0,80,0,0,80,0'
turn_off:
service: squeezebox.call_method
data:
entity_id: media_player.squeezelite_taudio
command: dmx
parameters:
- '0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0'
taudio_warn:
friendly_name: "Notify Warn"
turn_on:
service: squeezebox.call_method
data:
entity_id: media_player.squeezelite_taudio
command: dmx
parameters:
- '60,20,0,60,20,0,60,20,0,60,20,0,60,20,0'
- '7'
turn_off:
service: squeezebox.call_method
data:
entity_id: media_player.squeezelite_taudio
command: dmx
parameters:
- '0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0'
- '7'
taudio_alert:
friendly_name: "Notify Alert"
turn_on:
service: squeezebox.call_method
data:
entity_id: media_player.squeezelite_taudio
command: dmx
parameters:
- '80,0,0,80,0,0,80,0,0,80,0,0,80,0,0,80,0,0,80,0,0'
- '12'
turn_off:
service: squeezebox.call_method
data:
entity_id: media_player.squeezelite_taudio
command: dmx
parameters:
- '0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0'
- '12'
taudio_all:
friendly_name: "TAudio Color"
level_template: "{{states('input_number.taudio_l') | int(0)}}"
value_template: "{{states('input_number.taudio_l') | int(0) > 0}}"
color_template: "({{states('input_number.taudio_h') | int(0)}}, {{states('input_number.taudio_s') | int(0)}})"
turn_on:
- service: input_number.set_value
data:
entity_id: input_number.taudio_l
value: '{{ state_attr("input_number.taudio_l","initial")|float(0) }}'
- service: script.taudio_set
turn_off:
- service: input_number.set_value
data:
entity_id: input_number.taudio_l
value: "0"
- service: script.taudio_set
set_level:
- service: input_number.set_value
data:
value: "{{ brightness| float(0) }}"
entity_id: input_number.taudio_l
- service: script.taudio_set
set_color:
- service: input_number.set_value
data:
value: "{{ h }}"
entity_id: input_number.taudio_h
- service: input_number.set_value
data:
value: "{{ s }}"
entity_id: input_number.taudio_s
- service: script.taudio_set
automations.yaml
# Squeezelite-esp32 T-Audio Example TTS notify using an input text helper
- id: announce_to_speaker
alias: TAudio Announce
description: ''
trigger:
- platform: state
entity_id: input_text.alert_trigger
action:
- service: squeezebox.call_method
data:
command: show
parameters:
- 'line1: HA Announce'
- 'line2: {{ states("input_text.alert_trigger") }}'
- 'font: light'
- 'duration: 30'
entity_id: media_player.squeezelite_taudio
- service: notify.tts_announce
data:
message: "{{ states('input_text.alert_trigger') }}"
mode: single
# Squeezelite-esp32 T-Audio the following automation uses text helpers to search and play music
- id: taudio_load_albums
alias: taudio_load_albums
description: ''
trigger:
- platform: state
entity_id: input_text.taudio_play_albums
condition: []
action:
- service: squeezebox.call_method
data:
entity_id: media_player.squeezelite_taudio
command: playlist
parameters:
- loadtracks
- 'album.titlesearch={{ states(''input_text.taudio_play_albums'') }}'
mode: single
- id: taudio_load_artists
alias: taudio_load_artists
description: ''
trigger:
- platform: state
entity_id: input_text.taudio_play_artists
condition: []
action:
- service: squeezebox.call_method
data:
entity_id: media_player.squeezelite_taudio
command: playlist
parameters:
- loadtracks
- 'contributor.namesearch={{ states(''input_text.taudio_play_artists'') }}'
mode: single
- id: taudio_load_songs
alias: taudio_load_songs
description: ''
trigger:
- platform: state
entity_id: input_text.taudio_play_songs
condition: []
action:
- service: squeezebox.call_method
data:
entity_id: media_player.squeezelite_taudio
command: playlist
parameters:
- loadtracks
- 'track.titlesearch={{ states(''input_text.taudio_play_songs'') }}'
mode: single
scripts.yaml
# Squeezelite-ESP32 T-Audio Shared function to simplify template lights
taudio_set:
alias: TAudio Set RGB Color
sequence:
- service: squeezebox.call_method
data:
entity_id: media_player.squeezelite_taudio
command: dmx
parameters:
- '{% set gain = state_attr("light.taudio_all", "brightness")|int(0) %}{% for n in range(19) %}{% if gain == 0 %}0,0,0,{% else %}{% set rgb = state_attr("light.taudio_all", "rgb_color") %}{{ (rgb[0]|int(0) * gain / 255)|round }},{{ (rgb[1]|int(0) * gain / 255)|round(0) }},{{ (rgb[2]|int(0) * gain / 255)|round(0) }},{% endif %}{% endfor %}'
- "0"
# Squeezelite-esp32 T-Audio used to flash lights in service calls
taudio_alert_flash:
alias: taudio_alert_flash
sequence:
repeat:
count: 10
sequence:
- service: light.turn_on
data:
entity_id: light.taudio_alert
- delay: '00:00:01'
- service: light.turn_off
data:
entity_id: light.taudio_alert
- delay: '00:00:03'
input_text.yaml
# Squeezelite-ESP32 T-Audio input_text
taudio_play_songs:
name: "Play Songs"
taudio_play_artists:
name: "Play Artist"
taudio_play_albums:
name: "Play Album"
alert_trigger:
name: "Alert Trigger Source"
input_number.yaml
# Squeezelite-ESP32 T-Audio input_number
taudio_h:
name: TAudio Hue
min: 0
max: 360
taudio_s:
name: TAudio Saturation
min: 0
max: 100
taudio_l:
name: TAudio Level
initial: 30
min: 0
max: 255
step: 1
notify.yaml
# Squeezelite-ESP32 T-Audio a Text-To-Speech platform is required for the announcement to speaker example
- platform: tts
name: tts_announce
tts_service: tts.google_say
media_player: media_player.squeezelite_taudio
Sorry - missed the last post. Just updated merged with the master-cmake branch. Have not looked at the 4.3 as yet,. If it does get released as the default branch, I can taker a look, although there is no immediate need on the t-audio.
I just moved my server to the linux docker. It was a pain to get the plugin updated to enable the LED control features. I ended up having to manually update the files on the server. (which I think I had to do on the Windows integration too).
Thanks for the feedback. I'll update the examples.
Even though you said the mic is not of great quality any chance you can add the wm8978 to the new audio code for home assistant - esphome? Seems to support media stuff a lot nicer than the squeeze stuff with HA.
https://github.com/esphome/media-players/blob/main/m5stack-atom-echo.yaml
They were first using the arduino framework then switched to esp-idf framework for esphome. https://github.com/espressif/esp-adf/pull/33/files
https://github.com/esphome/esphome/tree/dev/esphome/components/i2s_audio
https://esphome.io/components/media_player/i2s_audio.html
https://rc.home-assistant.io/projects/thirteen-usd-voice-remote/
Example of another dac with i2c control for esphome. https://github.com/esphome/esphome/pull/3552
Example YAML setup for muse-luxe
https://github.com/esphome/media-players/blob/main/raspiaudio-muse-luxe.yaml
Microphone example YAML
https://github.com/esphome/media-players/blob/main/m5stack-atom-echo.yaml
This is the echo YAML but pins swapped for the T-Audio 1.6 board. https://github.com/LilyGO/TTGO-TAudio
Original Atom Echo YAML:
YAML could look something like this:
# Substitute these words into the YAML below.
substitutions:
name: "t-audio-001"
friendly_name: "T-Audio-001"
# ESPHome Setup.
esphome:
name: "${name}"
friendly_name: "${friendly_name}"
min_version: 2023.5.0
# Board and Framework Setup.
esp32:
board: esp-wrover-kit
framework:
type: arduino
# Enable logging.
logger:
# Enable and set up the Home Assistant API.
api:
encryption:
key: !secret encryption_key005
# Enable OTA updates.
ota:
password: !secret ota_pass005
# Enable and set up WiFi.
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
use_address: !secret use_address_wifi005
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "${friendly_name} Fallback Hotspot"
password: !secret fallbackhotspot005
# Enable captive portal if WiFi fails to connect.
captive_portal:
# Enable WebUI for direct control.
web_server:
# Configure the switch for restarting ESP32 from the WebUI.
switch:
- platform: restart
name: "${friendly_name} Restart"
# The improv_serial component in ESPHome implements the open Improv standard
# for configuring Wi-Fi on an ESPHome device by using a serial connection to
# the device, eg. USB.
improv_serial:
# Setup I2C pins for WM8978 communication.
i2c:
sda: GPIO19
scl: GPIO18
# Enable and set up the I2S audio pins.
i2s_audio:
# Left / Right Clock or Word Select or Frame Sync.
i2s_lrclk_pin: GPIO25
# Bit Clock or Continuous Serial Clock [SCK].
i2s_bclk_pin: GPIO33
# Missing this pin setup?
# Master Clock (typically 256 x LRCLK).
#i2s_mclk_pin: 0
# Enable and set up the I2S microphone.
microphone:
- platform: i2s_audio
id: wm8978_microphone
# Digital IN for the microphone.
i2s_din_pin: GPIO27
adc_type: external
pdm: false
# Enable and set up the media player for TTS files to play.
media_player:
- platform: i2s_audio
name: "${friendly_name} I2SAudio"
id: wm8978_audio
dac_type: external
# I2S Digital Out for speaker(s).
i2s_dout_pin: GPIO26
mode: stereo
mute_pin:
number: GPIO21
inverted: true
# Enable and set up the WM8978 DAC.
# Need a wm8978 component.
#wm8978:
# Enable voice assistant.
voice_assistant:
microphone: wm8978_microphone
on_start:
- light.turn_on:
id: led
blue: 100%
red: 0%
green: 0%
effect: none
on_tts_start:
- light.turn_on:
id: led
blue: 0%
red: 0%
green: 100%
effect: none
on_tts_end:
- media_player.play_media: !lambda return x;
- light.turn_on:
id: led
blue: 0%
red: 0%
green: 100%
effect: none
- media_player.play_media: !lambda return x;
on_end:
- delay: 1s
- wait_until:
not:
media_player.is_playing: wm8978_audio
- light.turn_off: led
on_error:
- light.turn_on:
id: led
blue: 0%
red: 100%
green: 0%
effect: none
- delay: 1s
- light.turn_off: led
# The T-Audio Battery IC IP5306 is I2C so this code won't work.
#sensor:
# - platform: adc
# pin: GPIO??
# name: Battery
# icon: "mdi:battery-outline"
# device_class: voltage
# state_class: measurement
# entity_category: diagnostic
# unit_of_measurement: V
# update_interval: 15s
# accuracy_decimals: 3
# attenuation: 11db
# raw: true
# filters:
# - multiply: 0.00173913 # 2300 -> 4, for attenuation 11db, based on Olivier's code
# - exponential_moving_average:
# alpha: 0.2
# send_every: 2
# - delta: 0.002
# Enable and set up the volume and microphone capture buttons.
binary_sensor:
- platform: gpio
pin:
number: GPIO34
inverted: true
mode:
input: true
pullup: true
name: Volume Up
on_click:
- media_player.volume_up: wm8978_audio
- platform: gpio
pin:
number: GPIO36
inverted: true
mode:
input: true
pullup: true
name: Volume Down
on_click:
- media_player.volume_down: wm8978_audio
- platform: gpio
pin:
number: GPIO39
inverted: true
mode:
input: true
pullup: true
name: Action
on_multi_click:
- timing:
- ON FOR AT MOST 350ms
- OFF FOR AT LEAST 10ms
then:
- media_player.toggle: wm8978_audio
- timing:
- ON FOR AT LEAST 350ms
then:
- voice_assistant.start:
- timing:
- ON FOR AT LEAST 350ms
- OFF FOR AT LEAST 10ms
then:
- voice_assistant.stop:
# Enable and set up the 19 addressable LED's on the T-Audio 1.6.
light:
- platform: fastled_clockless
id: led
name: "${friendly_name} LED(s)"
disabled_by_default: true
entity_category: config
pin: GPIO22
default_transition_length: 0s
chipset: WS2812B
num_leds: 19
rgb_order: GRB
rmt_channel: 0
effects:
- pulse:
transition_length: 250ms
update_interval: 250ms
It basically works (Button and LED haha) besides the wm8978 initialization code missing to turn off SOFTMUTE and enable the right and left audio channels.
Testing the button press (Doesn't detect audio from the microphone):
ESPHome version 2023.4.2 compiled on May 1 2023, 23:03:49
[23:04:23][D][api.connection:961]: Home Assistant 2023.5.0b5 (10.0.1.42): Connected successfully
[23:04:44][D][binary_sensor:036]: 'T-Audio-001 Button': Sending state ON
[23:04:44][D][voice_assistant:065]: Requesting start...
[23:04:44][D][voice_assistant:045]: Starting...
[23:04:44][D][voice_assistant:083]: Assist Pipeline running
[23:04:45][D][binary_sensor:036]: 'T-Audio-001 Button': Sending state OFF
[23:04:45][D][voice_assistant:073]: Signaling stop...
[23:04:53][E][voice_assistant:145]: Error: stt-no-text-recognized - No text recognized
Test sending a TTS wav file from Piper Docker in Home Assistant Docker with Wyoming Protocol (Doesn't output any sound).
[23:25:31][D][media_player:059]: 'T-Audio-001' - Setting
[23:25:31][D][media_player:066]: Media URL: http://10.0.1.42:8123/api/tts_proxy/7b031c3d673c84477529b838252c3609e5be34b8_en-us_a877e2b3bf_tts.piper.wav
A bunch of docker-compose YAML I use for Home Assistant, Faster-Whisper, Piper, etc. https://github.com/NonaSuomy/nonasuomy.github.io/blob/master/_posts/2022-10-15-docker.md
Seems they hardcode GPIO0 for the clock.
https://github.com/esphome/esphome/pull/3552/commits/b1ad48c8240707eae18e4b7b9266c3c3ee01b239
namespace es8388 {
void ES8388Component::setup() {
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_CLK_OUT1);
WRITE_PERI_REG(PIN_CTRL, READ_PERI_REG(PIN_CTRL) & 0xFFFFFFF0);
ESPHome i2s_audio code was based on arduino i2s library
Not sure when I'll have a chance to look at it, but it may get on the todo list at sometime.
If anyone else wanted to give it a try, the main challenge is that the 2-byte I2s commands on the WM are non standard, so standard setup script interfaces don't typically work.
Ok, I attempted it with putting these files under esphome/config/custom_components/wm8978/ and the above yamls How would you go about getting this to actually work with 2-byte I2S commands? What would you add from the references wm code at the bottom pasted below?
This compiles but I'm not really sure that I even got the WM initialized properly with what I modified.
init.py
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import i2c
from esphome.const import CONF_ID
wm8978_ns = cg.esphome_ns.namespace("wm8978")
wm8978Component = wm8978_ns.class_("wm8978Component", cg.Component, i2c.I2CDevice)
CONFIG_SCHEMA = (
cv.Schema({cv.GenerateID(): cv.declare_id(wm8978Component)})
.extend(i2c.i2c_device_schema(0x1A))
.extend(cv.COMPONENT_SCHEMA)
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await i2c.register_i2c_device(var, config)
wm8978_component.cpp
#include "wm8978_component.h"
#include "esphome/core/hal.h"
#include <soc/io_mux_reg.h>
namespace esphome {
namespace wm8978 {
void wm8978Component::setup() {
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_CLK_OUT1);
WRITE_PERI_REG(PIN_CTRL, READ_PERI_REG(PIN_CTRL) & 0xFFFFFFF0);
// reset
//this->write_byte(0x00, {0x80},);
//this->write_byte(0x00, {0x00}, 1);
// R1, MIC enable
this->write_byte(1, 27);
// R2, ROUT1, LOUT1 enable headphone
this->write_byte(2, 27);
// R3, LOUT2, LOUT2 enable speaker
this->write_byte(3, 108);
// R6, MCLK is provided externally
this->write_byte(6, 0);
// R43, INVROUT2 reverse, drive the speaker
this->write_byte(43, 16);
// R47, setting, PGABOOSTL, left channel MIC gets 20 times gain
this->write_byte(47, 256);
// R48, setting, PGABOOSTR, right channel MIC gets 20 times gain
this->write_byte(48, 256);
// R49, TSDEN, enable overheating protection
this->write_byte(49, 2);
// R10, SOFTMUTE off, 128x sampling, best SNR
this->write_byte(10, 8);
// R14, ADC 128x sampling rate and enable high pass filter (3.7Hz cut-off)
this->write_byte(14, 8 | 256);
// mute
//this->write_byte(0x19, 0x04);
// powerup
//this->write_byte(0x01, 0x50);
//this->write_byte(0x02, 0x00);
// worker mode
//this->write_byte(0x08, 0x00);
// DAC powerdown
//this->write_byte(0x04, 0xC0);
// vmidsel/500k ADC/DAC idem
//this->write_byte(0x00, 0x12);
// i2s 16 bits
//this->write_byte(0x17, 0x18);
// sample freq 256
//this->write_byte(0x18, 0x02);
// LIN2/RIN2 for mixer
//this->write_byte(0x26, 0x00);
// left DAC to left mixer
//this->write_byte(0x27, 0x90);
// right DAC to right mixer
//this->write_byte(0x2A, 0x90);
// DACLRC ADCLRC idem
//this->write_byte(0x2B, 0x80);
//this->write_byte(0x2D, 0x00);
// DAC volume max
//this->write_byte(0x1B, 0x00);
//this->write_byte(0x1A, 0x00);
// ADC poweroff
//this->write_byte(0x03, 0xFF);
// ADC amp 24dB
//this->write_byte(0x09, 0x88);
// LINPUT1/RINPUT1
//this->write_byte(0x0A, 0x00);
// ADC mono left
//this->write_byte(0x0B, 0x02);
// i2S 16b
//this->write_byte(0x0C, 0x0C);
// MCLK 256
//this->write_byte(0x0D, 0x02);
// ADC Volume
//this->write_byte(0x10, 0x00);
//this->write_byte(0x11, 0x00);
// ALC OFF
//this->write_byte(0x03, 0x09);
//this->write_byte(0x2B, 0x80);
//this->write_byte(0x02, 0xF0);
//delay(1);
//this->write_byte(0x02, 0x00);
// DAC power-up LOUT1/ROUT1 enabled
//this->write_byte(0x04, 0x30);
//this->write_byte(0x03, 0x00);
// DAC volume max
//this->write_byte(0x2E, 0x1C);
//this->write_byte(0x2F, 0x1C);
// unmute
//this->write_byte(0x19, 0x00);
}
} // namespace wm8978
} // namespace esphome
wm8978_component.h
#pragma once
#include "esphome/components/i2c/i2c.h"
#include "esphome/core/component.h"
namespace esphome {
namespace wm8978 {
class wm8978Component : public Component, public i2c::I2CDevice {
public:
void setup() override;
float get_setup_priority() const override { return setup_priority::LATE - 1; }
};
} // namespace wm8978
} // namespace esphome
Files used for ref
WM8978.cpp
#include <stdio.h>
#include <Arduino.h>
#include <Wire.h>
#include "WM8978.h"
// WM8978 register value buffer zone (total 58 registers 0 to 57), occupies 116 bytes of memory
// Because the IIC WM8978 operation does not support read operations, so save all the register values in the local
// Write WM8978 register, synchronized to the local register values, register read, register directly back locally stored value.
// Note: WM8978 register value is 9, so use uint16_t storage.
static uint16_t REGVAL_TBL[58] =
{
0X0000, 0X0000, 0X0000, 0X0000, 0X0050, 0X0000, 0X0140, 0X0000,
0X0000, 0X0000, 0X0000, 0X00FF, 0X00FF, 0X0000, 0X0100, 0X00FF,
0X00FF, 0X0000, 0X012C, 0X002C, 0X002C, 0X002C, 0X002C, 0X0000,
0X0032, 0X0000, 0X0000, 0X0000, 0X0000, 0X0000, 0X0000, 0X0000,
0X0038, 0X000B, 0X0032, 0X0000, 0X0008, 0X000C, 0X0093, 0X00E9,
0X0000, 0X0000, 0X0000, 0X0000, 0X0003, 0X0010, 0X0010, 0X0100,
0X0100, 0X0002, 0X0001, 0X0001, 0X0039, 0X0039, 0X0039, 0X0039,
0X0001, 0X0001
};
//WM8978 write register.
//reg: register address.
//val: the value to be written to the register.
//Return value:0, success;
//other, error code.
uint8_t WM8978::Write_Reg(uint8_t reg, uint16_t val)
{
char buf[2];
buf[0] = (reg << 1) | ((val >> 8) & 0X01);
buf[1] = val & 0XFF;
Wire.beginTransmission(WM8978_ADDR); // Send data to the slave with device number 4
Wire.write((const uint8_t*)buf, 2);
Wire.endTransmission(); // Stop sending.
REGVAL_TBL[reg] = val; // Save register value to local
return 0;
}
//WM8978 init
// Return value: 0, initialization is normal.
// other, error code
uint8_t WM8978::Init(void)
{
uint8_t res;
res = Write_Reg(0, 0); // Soft reset WM8978.
if (res)return 1; // Failed to send command, WM8978 is abnormal
// The following are general settings.
Write_Reg(1, 0X1B); //R1,MICEN is set to 1 (MIC enable), BIASEN is set to 1 (simulator work), VMIDSEL[1:0] is set to: 11 (5K)
Write_Reg(2, 0X1B0); //R2,ROUT1,LOUT1 output enable (headphone can work), BOOSTENR, BOOSTENL enable
Write_Reg(3, 0X6C); //R3,LOUT2,ROUT2 output enable (speaker work), RMIX, LMIX enable
Write_Reg(6, 0); //R6,MCLK is provided externally
Write_Reg(43, 1 << 4); //R43, INVROUT2 reverse, drive the speaker
Write_Reg(47, 1 << 8); //R47 setting, PGABOOSTL, left channel MIC gets 20 times gain
Write_Reg(48, 1 << 8); //R48 setting, PGABOOSTR, right channel MIC 20 times gain
Write_Reg(49, 1 << 1); //R49,TSDEN, enable overheating protection
Write_Reg(10, 1 << 3); //R10,SOFTMUTE off, 128x sampling,best SNR
Write_Reg(14, 1 << 3 | 1 << 8); //R14,ADC 128x sampling rate and enable high pass filter (3.7Hz cut-off)
return 0;
}
// WM8978 read register
// Reads the value of the local register buffer zone
// reg: Register Address
// Return Value: Register value
uint16_t WM8978::Read_Reg(uint8_t reg)
{
return REGVAL_TBL[reg];
}
// WM8978 DAC/ADC configuration
// adcen:adc enable(1)/disable(0)
// dacen:dac enable(1)/disable(0)
void WM8978::cfgADDA(uint8_t dacen, uint8_t adcen)
{
uint16_t regval;
regval = WM8978::Read_Reg(3); // Read R3
if (dacen)regval |= 3 << 0; // Set the lowest 2 bits of R3 to 1, enable DACR&DACL
else regval &= ~(3 << 0); // Clear the lowest 2 bits of R3, close DACR&DACL.
Write_Reg(3, regval); // Set R3
regval = WM8978::Read_Reg(2); // Read R2
if (adcen)regval |= 3 << 0; // Set the lower 2 bits of R2 to 1, enable ADCR&ADCL
else regval &= ~(3 << 0); // Clear the lowest 2 bits of R2 to zero, turn off ADCR&ADCL.
Write_Reg(2, regval); // Set R2
}
// WM8978 input channel configuration
// micen: MIC enable(1)/disable(0)
// lineinen: Line In enable(1)/disable(0)
// auxen: Aux enable(1)/disable(0)
void WM8978::cfgInput(uint8_t micen, uint8_t lineinen, uint8_t auxen)
{
uint16_t regval;
regval = WM8978::Read_Reg(2); // Read R2
if (micen)regval |= 3 << 2; // Enable INPPGAENR, INPPGAENL (PGA amplification of MIC)
else regval &= ~(3 << 2); // Close INPPGAENR, INPPGAENL.
Write_Reg(2, regval); // Set R2
regval = WM8978::Read_Reg(44); // Read R44
if (micen)regval |= 3 << 4 | 3 << 0; // Enable LIN2INPPGA, LIP2INPGA, RIN2INPPGA, RIP2INPGA.
else regval &= ~(3 << 4 | 3 << 0); // Close LIN2INPPGA, LIP2INPGA, RIN2INPPGA, RIP2INPGA.
Write_Reg(44, regval); // Set R44
if (lineinen)WM8978::setLINEINgain(5); // LINE-IN 0dB gain
else WM8978::setLINEINgain(0); // Close LINE-IN
if (auxen)WM8978::setAUXgain(7); // AUX 6dB gain
else WM8978::setAUXgain(0); // Close AUX input
}
// WM8978 output configuration
// dacen: DAC output (playback) enable(1)/disable(0)
// bpsen: Bypass output (recording, including MIC, LINE-IN, AUX, etc) enable(1)/disable(0)
void WM8978::cfgOutput(uint8_t dacen, uint8_t bpsen)
{
uint16_t regval = 0;
if (dacen) regval |= 1 << 0; // DAC output enable
if (bpsen)
{
regval |= 1 << 1; // BYPASS enabled
regval |= 5 << 2; // 0dB gain
}
Write_Reg(50, regval); // R50 setting
Write_Reg(51, regval); // R51 setting
}
// WM8978 MIC gain setting (excluding BOOST's 20dB, MIC-->ADC input gain)
// Gain: 0~63, corresponding to -12dB~35.25dB, 0.75dB/Step
void WM8978::setMICgain(uint8_t gain)
{
gain &= 0X3F;
Write_Reg(45, gain); // R45, left channel PGA setting
Write_Reg(46, gain | 1 << 8); // R46, right channel PGA setting
}
// WM8978 L2/R2 (that is, Line-In) gain setting (L2/R2-->ADC input part gain)
// Gain: 0~7,0 means the channel is prohibited, 1~7, corresponding to -12dB~6dB, 3dB/Step
void WM8978::setLINEINgain(uint8_t gain)
{
uint16_t regval;
gain &= 0X07;
regval = WM8978::Read_Reg(47); // Read R47
regval &= ~(7 << 4); // Clear the original setting
Write_Reg(47, regval | gain << 4); // Set R47
regval = WM8978::Read_Reg(48); // Read R48
regval &= ~(7 << 4); // Clear the original setting
Write_Reg(48, regval | gain << 4); // Set R48
}
// WM8978 AUXR, AUXL (PWM audio part) gain setting (AUXR/L-->ADC input part gain)
// Gain: 0~7, 0 means the channel is prohibited, 1~7, corresponding to -12dB~6dB,3dB/Step
void WM8978::setAUXgain(uint8_t gain)
{
uint16_t regval;
gain &= 0X07;
regval = WM8978::Read_Reg(47); // Read R47
regval &= ~(7 << 0); // Clear the original setting
Write_Reg(47, regval | gain << 0); // Set R47
regval = WM8978::Read_Reg(48); // Read R48
regval &= ~(7 << 0); // Clear the original setting
Write_Reg(48, regval | gain << 0); // Set R48
}
// Set I2S working mode
// fmt: 0, LSB (right alignment); 1, MSB (left alignment); 2, Philips standard I2S; 3, PCM/DSP;
// len: 0, 16 bits; 1, 20 bits; 2, 24 bits;3, 32 bits;
void WM8978::cfgI2S(uint8_t fmt, uint8_t len)
{
fmt &= 0X03;
len &= 0X03; // Limited range
Write_Reg(4, (fmt << 3) | (len << 5)); // R4, WM8978 working mode setting
}
// Set the left and right channel volume of the earphone
// voll: left channel volume (0~63)
// volr: right channel volume (0~63)
void WM8978::setHPvol(uint8_t voll, uint8_t volr)
{
voll &= 0X3F;
volr &= 0X3F; // Limited range
if (voll == 0)voll |= 1 << 6; // When the volume is 0, directly mute
if (volr == 0)volr |= 1 << 6; // When the volume is 0, directly mute
Write_Reg(52, voll); // R52, the volume setting of the left channel of the earphone
Write_Reg(53, volr | (1 << 8)); // R53, the volume setting of the right channel of the earphone, updated synchronously (HPVU=1)
}
// Set speaker volume
// voll: left channel volume (0~63)
void WM8978::setSPKvol(uint8_t volx)
{
volx &= 0X3F; // Limited range
if (volx == 0)volx |= 1 << 6; // When the volume is 0, directly mute
Write_Reg(54, volx); // R54, speaker left channel volume setting
Write_Reg(55, volx | (1 << 8)); // R55, speaker right channel volume setting, update synchronously (SPKVU=1)
}
// Set 3D surround sound
// depth: 0~15 (3D strength, 0 is the weakest, 15 is the strongest)
void WM8978::set3D(uint8_t depth)
{
depth&=0XF; // Limited range
Write_Reg(41,depth); // R41, 3D surround setting
}
// Set EQ/3D action direction
// dir: 0, works in ADC
// 1, works on DAC (default)
void WM8978::set3Ddir(uint8_t dir)
{
uint16_t regval;
regval = WM8978::Read_Reg(0X12);
if (dir)regval |= 1 << 8;
else regval &= ~(1 << 8);
Write_Reg(18, regval); // R18, the 9th bit of EQ1 controls EQ/3D direction
}
// Set EQ1
// cfreq: cut-off frequency, 0~3, respectively corresponding to: 80/105/135/175Hz
// gain: gain, 0~24, corresponding to -12~+12dB
void WM8978::setEQ1(uint8_t cfreq, uint8_t gain)
{
uint16_t regval;
cfreq &= 0X3; // Limited range
if (gain > 24)gain = 24;
gain = 24 - gain;
regval = WM8978::Read_Reg(18);
regval &= 0X100;
regval |= cfreq << 5; // Set the cutoff frequency
regval |= gain; // Set gain
Write_Reg(18, regval); // R18, EQ1 setting
}
// Set EQ2
// cfreq: center frequency, 0~3, respectively corresponding to: 230/300/385/500Hz
// gain: gain, 0~24, corresponding to -12~+12dB
void WM8978::setEQ2(uint8_t cfreq, uint8_t gain)
{
uint16_t regval = 0;
cfreq &= 0X3; // Limited range
if (gain > 24)gain = 24;
gain = 24 - gain;
regval |= cfreq << 5; // Set the cutoff frequency
regval |= gain; // Set gain
Write_Reg(19, regval); // R19, EQ2 setting
}
// Set EQ3
// cfreq: center frequency, 0~3, respectively corresponding to :650/850/1100/1400Hz
// gain: gain, 0~24, corresponding to -12~+12dB
void WM8978::setEQ3(uint8_t cfreq, uint8_t gain)
{
uint16_t regval = 0;
cfreq &= 0X3; // Limited range
if (gain > 24)gain = 24;
gain = 24 - gain;
regval |= cfreq << 5; // Set the cutoff frequency
regval |= gain; // Set gain
Write_Reg(20, regval); // R20, EQ3 setting
}
// Set EQ4
// cfreq: center frequency, 0~3, respectively corresponding to :1800/2400/3200/4100Hz
// gain: gain, 0~24, corresponding to -12~+12dB
void WM8978::setEQ4(uint8_t cfreq, uint8_t gain)
{
uint16_t regval = 0;
cfreq &= 0X3; // Limited range
if (gain > 24)gain = 24;
gain = 24 - gain;
regval |= cfreq << 5; // Set the cutoff frequency
regval |= gain; // Set gain
Write_Reg(21, regval); // R21, EQ4 settings
}
// Set EQ5
// cfreq: center frequency, 0~3, respectively corresponding to: 5300/6900/9000/11700Hz
// gain: gain, 0~24, corresponding to -12~+12dB
void WM8978::setEQ5(uint8_t cfreq, uint8_t gain)
{
uint16_t regval = 0;
cfreq &= 0X3; // Limited range
if (gain > 24)gain = 24;
gain = 24 - gain;
regval |= cfreq << 5; // Set the cutoff frequency
regval |= gain; // Set gain
Write_Reg(22, regval); // R22, EQ5 settings
}
void WM8978::setALC(uint8_t enable, uint8_t maxgain, uint8_t mingain)
{
uint16_t regval;
if (maxgain > 7) maxgain = 7;
if (mingain > 7) mingain = 7;
regval = WM8978::Read_Reg(32);
if (enable)
regval |= (3 << 7);
regval |= (maxgain << 3) | (mingain << 0);
Write_Reg(32, regval);
}
void WM8978::setNoise(uint8_t enable, uint8_t gain)
{
uint16_t regval;
if (gain > 7) gain = 7;
regval = WM8978::Read_Reg(35);
regval = (enable << 3);
regval |= gain; // Set gain
Write_Reg(35, regval); // R18, EQ1 setting
}
void WM8978::setHPF(uint8_t enable)
{
uint16_t regval;
regval = WM8978::Read_Reg(14);
regval &= ~(1 << 8);
regval |= (enable << 8);
Write_Reg(14, regval); // R14, high pass filter
}
bool WM8978::begin() {
Wire.beginTransmission(WM8978_ADDR);
const uint8_t error = Wire.endTransmission();
if (error) {
log_e("No WM8978 dac @ i2c address: 0x%X", WM8978_ADDR);
return false;
}
const int err = Init();
if (err) {
log_e("WM8978 init err: 0x%X", err);
return false;
}
cfgI2S(2, 0); // Philips 16bit
cfgADDA(1, 1); // Enable ADC DAC
cfgInput(0, 0, 0); // Mic, linein, aux - Note: M5Stack node has only internal microphones connected
setMICgain(0);
setAUXgain(0);
setLINEINgain(0);
setSPKvol(0); // 0-63
setHPvol(0, 0); // 0-63
set3Ddir(0);
setEQ1(0, 24);
setEQ2(0, 24);
setEQ3(0, 24);
setEQ4(0, 24);
setEQ5(0, 24);
cfgOutput(1, 0); // Output enabled, bypass disabled
return true;
}
bool WM8978::begin(const uint8_t sda, const uint8_t scl, const uint32_t frequency) {
if (!Wire.begin(sda, scl, frequency)) {
log_e("Wire setup error sda=%i scl=%i frequency=%i", sda, scl, frequency);
return false;
}
return begin();
}
WM8978.h
#ifndef __WM8978_H
#define __WM8978_H
#include <stdio.h>
#define WM8978_ADDR 0X1A //WM8978 Default Address
#define EQ1_80Hz 0X00
#define EQ1_105Hz 0X01
#define EQ1_135Hz 0X02
#define EQ1_175Hz 0X03
#define EQ2_230Hz 0X00
#define EQ2_300Hz 0X01
#define EQ2_385Hz 0X02
#define EQ2_500Hz 0X03
#define EQ3_650Hz 0X00
#define EQ3_850Hz 0X01
#define EQ3_1100Hz 0X02
#define EQ3_14000Hz 0X03
#define EQ4_1800Hz 0X00
#define EQ4_2400Hz 0X01
#define EQ4_3200Hz 0X02
#define EQ4_4100Hz 0X03
#define EQ5_5300Hz 0X00
#define EQ5_6900Hz 0X01
#define EQ5_9000Hz 0X02
#define EQ5_11700Hz 0X03
class WM8978
{
public:
WM8978() {}
~WM8978() {}
bool begin(); /* use this function if you want to setup i2c before */
bool begin(const uint8_t sda, const uint8_t scl, const uint32_t frequency = 100000);
void cfgADDA(uint8_t dacen, uint8_t adcen);
void cfgInput(uint8_t micen, uint8_t lineinen, uint8_t auxen);
void cfgOutput(uint8_t dacen, uint8_t bpsen);
void cfgI2S(uint8_t fmt, uint8_t len);
void setMICgain(uint8_t gain);
void setLINEINgain(uint8_t gain);
void setAUXgain(uint8_t gain);
void setHPvol(uint8_t voll, uint8_t volr);
void setSPKvol(uint8_t volx);
void set3D(uint8_t depth);
void set3Ddir(uint8_t dir);
void setEQ1(uint8_t cfreq, uint8_t gain);
void setEQ2(uint8_t cfreq, uint8_t gain);
void setEQ3(uint8_t cfreq, uint8_t gain);
void setEQ4(uint8_t cfreq, uint8_t gain);
void setEQ5(uint8_t cfreq, uint8_t gain);
void setNoise(uint8_t enable, uint8_t gain);
void setALC(uint8_t enable, uint8_t maxgain, uint8_t mingain);
void setHPF(uint8_t enable);
private:
uint8_t Init(void);
uint8_t Write_Reg(uint8_t reg, uint16_t val);
uint16_t Read_Reg(uint8_t reg);
};
#endif
Hi is there a working version of the configuration.yaml for the current HomeAssistant?