diff --git a/README.md b/README.md index a7af3d9..a2cb20c 100644 --- a/README.md +++ b/README.md @@ -30,8 +30,8 @@ i2s: # according to datasheet when L/R pin is connected to GND, # the mic should output its signal in the left channel, - # however in my experience it's the opposite: when I connect - # L/R to GND then the signal is in the right channel + # but on some boards I've encountered the opposite, + # so you can try both channel: right # default: right # right shift samples. diff --git a/components/sound_level_meter/sound_level_meter.cpp b/components/sound_level_meter/sound_level_meter.cpp index 59b86a5..5287473 100644 --- a/components/sound_level_meter/sound_level_meter.cpp +++ b/components/sound_level_meter/sound_level_meter.cpp @@ -116,7 +116,7 @@ void SoundLevelMeter::task(void *param) { if (process_count >= sr * (this_->update_interval_ / 1000.f)) { auto t = uint32_t(float(process_time) / process_count * (sr / 1000.f)); auto cpu_util = float(process_time) / 1000 / this_->update_interval_; - ESP_LOGE(TAG, "CPU (Core %u) Utilization: %.1f %%", xPortGetCoreID(), cpu_util * 100); + ESP_LOGD(TAG, "CPU (Core %u) Utilization: %.1f %%", xPortGetCoreID(), cpu_util * 100); process_time = process_count = 0; } } diff --git a/configs/advanced-example-config.yaml b/configs/advanced-example-config.yaml index 5020ffc..4f75c91 100644 --- a/configs/advanced-example-config.yaml +++ b/configs/advanced-example-config.yaml @@ -18,23 +18,23 @@ i2s: bck_pin: 23 ws_pin: 18 din_pin: 19 - sample_rate: 48000 # default: 48000 - bits_per_sample: 32 # default: 32 - dma_buf_count: 8 # default: 8 - dma_buf_len: 256 # default: 256 - use_apll: true # default: false + sample_rate: 48000 # default: 48000 + bits_per_sample: 32 # default: 32 + dma_buf_count: 8 # default: 8 + dma_buf_len: 256 # default: 256 + use_apll: true # default: false # according to datasheet when L/R pin is connected to GND, # the mic should output its signal in the left channel, - # however in my experience it's the opposite: when I connect - # L/R to GND then the signal is in the right channel - channel: right # default: right + # but on some boards I've encountered the opposite, + # so you can try both + channel: right # default: right # right shift samples. # for example if mic has 24 bit resolution, and # i2s configured as 32 bits, then audio data will be aligned left (MSB) # and LSB will be padded with zeros, so you might want to shift them right by 8 bits - bits_shift: 8 # default: 0 + bits_shift: 8 # default: 0 sound_level_meter: id: sound_level_meter1 @@ -42,166 +42,163 @@ sound_level_meter: # update_interval specifies over which interval to aggregate audio data # you can specify default update_interval on top level, but you can also override # it further by specifying it on sensor level - update_interval: 60s # default: 60s + update_interval: 60s # default: 60s # you can disable (turn off) component by default (on boot) # and turn it on later when needed via sound_level_meter.turn_on/toggle actions; # when used with switch it might conflict/being overriden by # switch state restoration logic, so you have to either disable it in - # switch config and then is_on property here will have effect, - # or completely rely on switch state restoration/initialization and + # switch config and then is_on property here will have effect, + # or completely rely on switch state restoration/initialization and # any value set here will be ignored - is_on: true # default: true + is_on: true # default: true # buffer_size is in samples (not bytes), so for float data type # number of bytes will be buffer_size * 4 - buffer_size: 1024 # default: 1024 + buffer_size: 1024 # default: 1024 # ignore audio data at startup for this long - warmup_interval: 500ms # default: 500ms + warmup_interval: 500ms # default: 500ms # audio processing runs in a separate task, you can change its settings below - task_stack_size: 4096 # default: 4096 - task_priority: 2 # default: 2 - task_core: 1 # default: 1 + task_stack_size: 4096 # default: 4096 + + # idle task priority is 0, + # main esphome loop priority is 1, + # critical system tasks have priorities 18, 19, 20... + # I set the priority to 2, slightly higher than the main loop, + # because audio processing is highly sensitive to timing. + # Large delays from certain components could cause the + # DMA buffers to overflow, resulting in lost audio data. + task_priority: 2 # default: 2 + task_core: 1 # default: 1 # see your mic datasheet to find sensitivity and reference SPL. # those are used to convert dB FS to db SPL - mic_sensitivity: -26dB # default: empty - mic_sensitivity_ref: 94dB # default: empty + mic_sensitivity: -26dB # default: empty + mic_sensitivity_ref: 94dB # default: empty # additional offset if needed - offset: 0dB # default: empty - - # for flexibility sensors are organized hierarchically into groups. each group - # could have any number of filters, sensors and nested groups. - # for examples if there is a top level group A with filter A and nested group B - # with filter B, then for sensors inside group B filters A and then B will be - # applied: - # groups: - # # group A - # - filters: - # - filter A - # groups: - # # group B - # - filters: - # - filter B - # sensors: - # - sensor X - groups: - # group 1 (mic eq) - - filters: - # for now only SOS filter type is supported, see math/filter-design.ipynb - # to learn how to create or convert other filter types to SOS - - type: sos - coeffs: - # INMP441: - # b0 b1 b2 a1 a2 - - [ 1.0019784 , -1.9908513 , 0.9889158 , -1.9951786 , 0.99518436] - - # nested groups - groups: - # group 1.1 (no weighting) - - sensors: - # 'eq' type sensor calculates Leq (average) sound level over specified period - - type: eq - name: LZeq_1s - id: LZeq_1s - # you can override updated_interval specified on top level - # individually per each sensor - update_interval: 1s - - # you can have as many sensors of same type, but with different - # other parameters (e.g. update_interval) as needed - - type: eq - name: LZeq_1min - id: LZeq_1min - unit_of_measurement: dBZ - - # 'max' sensor type calculates Lmax with specified window_size. - # for example, if update_interval is 60s and window_size is 1s - # then it will calculate 60 Leq values for each second of audio data - # and the result will be max of them - - type: max - name: LZmax_1s_1min - id: LZmax_1s_1min - window_size: 1s - unit_of_measurement: dBZ - - # same as 'max', but 'min' - - type: min - name: LZmin_1s_1min - id: LZmin_1s_1min - window_size: 1s - unit_of_measurement: dBZ - - # it finds max single sample over whole update_interval - - type: peak - name: LZpeak_1min - id: LZpeak_1min - unit_of_measurement: dBZ - - # group 1.2 (A-weighting) - - filters: - # for now only SOS filter type is supported, see math/filter-design.ipynb - # to learn how to create or convert other filter types to SOS - - type: sos - coeffs: - # A-weighting: - # b0 b1 b2 a1 a2 - - [ 0.16999495 , 0.741029 , 0.52548885 , -0.11321865 , -0.056549273] - - [ 1. , -2.00027 , 1.0002706 , -0.03433284 , -0.79215795 ] - - [ 1. , -0.709303 , -0.29071867 , -1.9822421 , 0.9822986 ] - sensors: - - type: eq - name: LAeq_1min - id: LAeq_1min - unit_of_measurement: dBA - - type: max - name: LAmax_1s_1min - id: LAmax_1s_1min - window_size: 1s - unit_of_measurement: dBA - - type: min - name: LAmin_1s_1min - id: LAmin_1s_1min - window_size: 1s - unit_of_measurement: dBA - - type: peak - name: LApeak_1min - id: LApeak_1min - unit_of_measurement: dBA - - # group 1.3 (C-weighting) - - filters: - # for now only SOS filter type is supported, see math/filter-design.ipynb - # to learn how to create or convert other filter types to SOS - - type: sos - coeffs: - # C-weighting: - # b0 b1 b2 a1 a2 - - [-0.49651518 , -0.12296628 , -0.0076134163, -0.37165618 , 0.03453208 ] - - [ 1. , 1.3294908 , 0.44188643 , 1.2312505 , 0.37899444 ] - - [ 1. , -2. , 1. , -1.9946145 , 0.9946217 ] - sensors: - - type: eq - name: LCeq_1min - id: LCeq_1min - unit_of_measurement: dBC - - type: max - name: LCmax_1s_1min - id: LCmax_1s_1min - window_size: 1s - unit_of_measurement: dBC - - type: min - name: LCmin_1s_1min - id: LCmin_1s_1min - window_size: 1s - unit_of_measurement: dBC - - type: peak - name: LCpeak_1min - id: LCpeak_1min - unit_of_measurement: dBC - + offset: 0dB # default: empty + + # under dsp_filters section you can define multiple filters, + # which can be referenced later by each sensor + + # for now only SOS filter type is supported, see math/filter-design.ipynb + # to learn how to create or convert other filter types to SOS + dsp_filters: + - id: f_inmp441 # INMP441 mic eq @ 48kHz + type: sos + coeffs: + # b0 b1 b2 a1 a2 + - [1.0019784, -1.9908513, 0.9889158, -1.9951786, 0.99518436] + - id: f_a # A weighting @ 48kHz + type: sos + coeffs: + # b0 b1 b2 a1 a2 + - [0.16999495, 0.741029, 0.52548885, -0.11321865, -0.056549273] + - [1., -2.00027, 1.0002706, -0.03433284, -0.79215795] + - [1., -0.709303, -0.29071867, -1.9822421, 0.9822986] + - id: f_c # C weighting @ 48kHz + type: sos + coeffs: + # b0 b1 b2 a1 a2 + - [-0.49651518, -0.12296628, -0.0076134163, -0.37165618, 0.03453208] + - [1., 1.3294908, 0.44188643, 1.2312505, 0.37899444] + - [1., -2., 1., -1.9946145, 0.9946217] + + sensors: + # 'eq' type sensor calculates Leq (average) sound level over specified period + - type: eq + name: LZeq_1s + id: LZeq_1s + # you can override updated_interval specified on top level + # individually per each sensor + update_interval: 1s + + # you can have as many sensors of same type, but with different + # other parameters (e.g. update_interval) as needed + - type: eq + name: LZeq_1min + id: LZeq_1min + unit_of_measurement: dBZ + + # 'max' sensor type calculates Lmax with specified window_size. + # for example, if update_interval is 60s and window_size is 1s + # then it will calculate 60 Leq values for each second of audio data + # and the result will be max of them + - type: max + name: LZmax_1s_1min + id: LZmax_1s_1min + window_size: 1s + unit_of_measurement: dBZ + + # same as 'max', but 'min' + - type: min + name: LZmin_1s_1min + id: LZmin_1s_1min + window_size: 1s + unit_of_measurement: dBZ + + # it finds max single sample over whole update_interval + - type: peak + name: LZpeak_1min + id: LZpeak_1min + unit_of_measurement: dBZ + + - type: eq + name: LAeq_1min + id: LAeq_1min + unit_of_measurement: dBA + + # The dsp_filters field is a list of filter IDs defined + # in the main component's corresponding section. + # If you're only using a single filter, you can omit the brackets. + # Alternatively, instead of referencing an existing filter, + # you can define a filter directly here in the same format + # as in the main component. However, this filter won't be reusable + # across other sensors, so it’s best to use this approach only when + # each sensor requires its own unique filters that don't overlap with others. + dsp_filters: [f_inmp441, f_a] + - type: max + name: LAmax_1s_1min + id: LAmax_1s_1min + window_size: 1s + unit_of_measurement: dBA + dsp_filters: [f_inmp441, f_a] + - type: min + name: LAmin_1s_1min + id: LAmin_1s_1min + window_size: 1s + unit_of_measurement: dBA + dsp_filters: [f_inmp441, f_a] + - type: peak + name: LApeak_1min + id: LApeak_1min + unit_of_measurement: dBA + dsp_filters: [f_inmp441, f_a] + + - type: eq + name: LCeq_1min + id: LCeq_1min + unit_of_measurement: dBC + dsp_filters: [f_inmp441, f_c] + - type: max + name: LCmax_1s_1min + id: LCmax_1s_1min + window_size: 1s + unit_of_measurement: dBC + dsp_filters: [f_inmp441, f_c] + - type: min + name: LCmin_1s_1min + id: LCmin_1s_1min + window_size: 1s + unit_of_measurement: dBC + dsp_filters: [f_inmp441, f_c] + - type: peak + name: LCpeak_1min + id: LCpeak_1min + unit_of_measurement: dBC + dsp_filters: [f_inmp441, f_c] # automation # available actions: @@ -211,6 +208,7 @@ sound_level_meter: switch: - platform: template name: "Sound Level Meter Switch" + icon: mdi:power # if you want is_on property on component to have effect, then set # restore_mode to DISABLED, or alternatively you can use other modes # (more on them in esphome docs), then is_on property on the component will diff --git a/configs/minimal-example-config.yaml b/configs/minimal-example-config.yaml index 298bf34..97dbf72 100644 --- a/configs/minimal-example-config.yaml +++ b/configs/minimal-example-config.yaml @@ -17,46 +17,16 @@ i2s: bck_pin: 23 ws_pin: 18 din_pin: 19 - sample_rate: 48000 # default: 48000 - bits_per_sample: 32 # default: 32 - - # right shift samples. - # for example if mic has 24 bit resolution, and i2s configured as 32 bits, - # then audio data will be aligned left (MSB) and LSB will be padded with - # zeros, so you might want to shift them right by 8 bits - bits_shift: 8 # default: 0 + sample_rate: 48000 + bits_per_sample: 32 + bits_shift: 8 + channel: right sound_level_meter: - # update_interval specifies over which interval to aggregate audio data - # you can specify default update_interval on top level, but you can also - # override it further by specifying it on sensor level - update_interval: 1s # default: 60s - - # buffer_size is in samples (not bytes), so for float data type - # number of bytes will be buffer_size * 4 - buffer_size: 1024 # default: 1024 - - # see your mic datasheet to find sensitivity and reference SPL. - # those are used to convert dB FS to db SPL - mic_sensitivity: -26dB # default: empty - mic_sensitivity_ref: 94dB # default: empty + update_interval: 1s + mic_sensitivity: -26dB + mic_sensitivity_ref: 94dB - # for flexibility sensors are organized hierarchically into groups. - # each group can have any number of filters, sensors and nested groups. - # for examples if there is a top level group A with filter A and nested - # group B with filter B, then for sensors inside group B filters A - # and then B will be applied: - # groups: - # # group A - # - filters: - # - filter A - # groups: - # # group B - # - filters: - # - filter B - # sensors: - # - sensor X - groups: - - sensors: - - type: eq - name: Leq_1s + sensors: + - type: eq + name: Leq_1s diff --git a/configs/sensor-community-example-config.yaml b/configs/sensor-community-example-config.yaml index e445629..24fdfb7 100644 --- a/configs/sensor-community-example-config.yaml +++ b/configs/sensor-community-example-config.yaml @@ -51,32 +51,36 @@ sound_level_meter: warmup_interval: 500ms mic_sensitivity: -26dB mic_sensitivity_ref: 94dB - groups: - - filters: - - type: sos - coeffs: - # A-weighting: - # b0 b1 b2 a1 a2 - - [0.16999495, 0.741029, 0.52548885, -0.11321865, -0.056549273] - - [1., -2.00027, 1.0002706, -0.03433284, -0.79215795] - - [1., -0.709303, -0.29071867, -1.9822421, 0.9822986] - sensors: - - type: eq - name: LAeq_1min - id: LAeq_1min - unit_of_measurement: dBA - - type: max - name: LAmax_125ms_1min - id: LAmax_125ms_1min - # I believe, previously 35ms was used in DNMS FW, - # but later it was changed to 125ms - window_size: 125ms - unit_of_measurement: dBA - - type: min - name: LAmin_125ms_1min - id: LAmin_125ms_1min - window_size: 125ms - unit_of_measurement: dBA + + dsp_filters: + - id: f_a # A weighting @ 48kHz + type: sos + coeffs: + # b0 b1 b2 a1 a2 + - [ 0.16999495 , 0.741029 , 0.52548885 , -0.11321865 , -0.056549273] + - [ 1. , -2.00027 , 1.0002706 , -0.03433284 , -0.79215795 ] + - [ 1. , -0.709303 , -0.29071867 , -1.9822421 , 0.9822986 ] + + sensors: + - type: eq + name: LAeq_1min + id: LAeq_1min + dsp_filters: [f_a] + unit_of_measurement: dBA + - type: max + name: LAmax_125ms_1min + id: LAmax_125ms_1min + dsp_filters: [f_a] + # I believe, previously 35ms was used in DNMS FW, + # but later it was changed to 125ms + window_size: 125ms + unit_of_measurement: dBA + - type: min + name: LAmin_125ms_1min + id: LAmin_125ms_1min + dsp_filters: [f_a] + window_size: 125ms + unit_of_measurement: dBA interval: - interval: 150s