stas-sl / esphome-sound-level-meter

77 stars 14 forks source link

-256 or -inf db #19

Closed RaymiiOrg closed 8 months ago

RaymiiOrg commented 8 months ago

Using an Olimex esp32-c3 with an INMP441. Im not able to get any measurements:

Max always reports: -256.29 dBA Min and peak are on: -inf dBA

I've tried switching left and right as in #10 .

The hardware does work with this Arduino code: https://iotassistant.io/esp32/smart-door-bell-noise-meter-using-fft-esp32/

Any tips on how to debug further?

stas-sl commented 8 months ago

Can you show your ESPHome config? Which esp-idf versions are you using in both cases?

I advice to use minimal config without any filters at first. Maybe try different sample rates/bits per sample and adjust bits_shift accordingly: if bits_per_sample is 16 then bits_shift should be 0 (actually 16bit option should be safer one, so try to make it work first), but if bits_per_sample is 32 then bits_shift should be 8, as INMP441 has only 24bits resolution. Try to set update_interval to 1 second and see if data is changing or it stays constant.

In the worst case you might need to print raw audio data as it comes from i2s_read function as hex or decimal and try to interpret it as audio data - it shouldn't be constant (all zeros or all 0xff), and it should change if you make some noise, or if you're in a quite room, then those numbers should be closer to 0 (positive or negative).

I guess there could be small differences in initialization/config depending on chip/framework version. You can see all initialization parameters in i2s.cpp file. It looks quite similar to your working code, but again you can compile your Arduino code and ESPHome against different esp-idf versions, so there could be some differences.

RaymiiOrg commented 8 months ago

These are the versions in use:

CONFIGURATION: https://docs.platformio.org/page/boards/espressif32/esp32dev.html
PLATFORM: Espressif 32 (5.4.0) > Espressif ESP32 Dev Module
HARDWARE: ESP32C3 240MHz, 320KB RAM, 4MB Flash
DEBUG: Current (cmsis-dap) External (cmsis-dap, esp-bridge, esp-builtin, esp-prog, iot-bus-jtag, jlink, minimodule, olimex-arm-usb-ocd, olimex-arm-usb-ocd-h, olimex-arm-usb-tiny-h, olimex-jtag-tiny, tumpa)
PACKAGES:
 - framework-espidf @ 3.40406.240122 (4.4.6)
 - tool-cmake @ 3.16.4
 - tool-esptoolpy @ 1.40400.0 (4.4.0)
 - tool-idf @ 1.0.1
 - tool-mconf @ 1.4060000.20190628 (406.0.0)
 - tool-ninja @ 1.9.0
 - toolchain-esp32ulp @ 2.35.0-20220830
 - toolchain-riscv32-esp @ 8.4.0+2021r2-patch5

This is the esphome yaml file:

substitutions:
  display_name: micky

esphome:
  name: ${display_name}
  name_add_mac_suffix: true
  project:
    name: "raymii.dbm"
    version: "1.0.0"
  platformio_options:
    board_build.mcu: esp32c3
    board_build.variant: esp32c3  
  includes:
    # should contain single line: #include <esp_task_wdt.h>
    - wdt_include.h
  on_boot:
    then:
      - lambda: !lambda |-
          // increase watchdog timeout
          esp_task_wdt_init(300, false);

external_components:
  - source: github://raymiiorg/esphome-sound-level-meter  # add @tag if you want to use a specific version (e.g @v1.0.0)

esp32:
  variant: ESP32C3
  board: esp32dev
  framework:
    type: esp-idf
    sdkconfig_options:
      CONFIG_BT_BLE_50_FEATURES_SUPPORTED: y
      CONFIG_BT_BLE_42_FEATURES_SUPPORTED: y
      #CONFIG_COMPILER_OPTIMIZATION_PERF: y      
      CONFIG_ESP_TASK_WDT_TIMEOUT_S: "300"
      CONFIG_COMPILER_OPTIMIZATION_SIZE: y    

status_led:  
  pin: GPIO8

logger: 
  level: VERBOSE

api:

ota:
  safe_mode: true
  password: !secret ota_password  

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  fast_connect: true

  ap:
    ssid: !secret ap_ssid
    password: !secret ap_pass

i2s:
  bck_pin: 20
  ws_pin: 10
  din_pin: 7
  sample_rate: 48000            # default: 48000
  bits_per_sample: 16           # default: 32
  dma_buf_count: 8              # default: 8
  dma_buf_len: 256              # default: 256
  use_apll: true                # default: false

  # 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: 0                 # default: 0

sound_level_meter:
  id: sound_level_meter1

  # 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

  # 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 
  # any value set here will be ignored
  is_on: false                   # 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

  # ignore audio data at startup for this long
  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

  # 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
  # additional offset if needed
  offset: 0dB                   # default: empty

  groups:
    - 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: ${display_name} LZeq_1s
              id: ${display_name}_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: ${display_name}_LZeq_1min
              id: ${display_name}_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: ${display_name} LZmax_1s_1min
              id: ${display_name}_LZmax_1s_1min
              window_size: 1s
              unit_of_measurement: dBZ

            # same as 'max', but 'min'
            - type: min
              name: ${display_name} LZmin_1s_1min
              id: ${display_name}_LZmin_1s_1min
              window_size: 1s
              unit_of_measurement: dBZ

            # it finds max single sample over whole update_interval
            - type: peak
              name: ${display_name} LZpeak_1min
              id: ${display_name}_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: ${display_name} LAeq_1min
              id: ${display_name}_LAeq_1min
              unit_of_measurement: dBA
            - type: max
              name: ${display_name} LAmax_1s_1min
              id: ${display_name}_LAmax_1s_1min
              window_size: 1s
              unit_of_measurement: dBA
            - type: min
              name: ${display_name} LAmin_1s_1min
              id: ${display_name}_LAmin_1s_1min
              window_size: 1s
              unit_of_measurement: dBA
            - type: peak
              name: ${display_name} LApeak_1min
              id: ${display_name}_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: ${display_name} 4LCeq_1min
              id: ${display_name}_LCeq_1min
              unit_of_measurement: dBC
            - type: max
              name: ${display_name} LCmax_1s_1min
              id: ${display_name}_LCmax_1s_1min
              window_size: 1s
              unit_of_measurement: dBC
            - type: min
              name: ${display_name} LCmin_1s_1min
              id: ${display_name}_LCmin_1s_1min
              window_size: 1s
              unit_of_measurement: dBC
            - type: peak
              name: ${display_name} LCpeak_1min
              id: ${display_name}_LCpeak_1min
              unit_of_measurement: dBC

# automation
# available actions:
#   - sound_level_meter.turn_on
#   - sound_level_meter.turn_off
#   - sound_level_meter.toggle
switch:
  - platform: template
    name: "${display_name} Sound Level Meter Switch"
    # 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
    # be overriden by the switch
    restore_mode: DISABLED # ALWAYS_OFF | ALWAYS_ON | RESTORE_DEFAULT_OFF | RESTORE_DEFAULT_ON
    lambda: |-
      return id(sound_level_meter1).is_on();
    turn_on_action:
      - sound_level_meter.turn_on
    turn_off_action:
      - sound_level_meter.turn_off
  - platform: restart
    name: "${display_name} Restart"      

button:
  - platform: template
    name: "${display_name} Sound Level Meter Toggle Button"
    on_press:
      - sound_level_meter.toggle: sound_level_meter1
  - platform: safe_mode
    name: ${display_name} (Safe Mode)

web_server:
  local: true

Some relevant logging at startup

[20:12:40][C][i2s:128]: Setting up I2S 0 ...
[20:12:40][V][esp-idf:000]: W (577) I2S: APLL not supported on current chip, use I2S_CLK_D2CLK as default clock source

I'm now trying different configurations (without filters as suggested).

Can I print the raw data via esphome or should I add that in the C++ code (SoundLevelMeter::task like a ESP_LOGD)?

stas-sl commented 8 months ago

I would suggest to print it in I2SComponent::read right after i2s_read, casting bytes to int16_t (signed) (if you're using 16 bits per sample). Yes I would use ESP_LOGD for that. But you only need to print some subsample of data, as printing all 48000 bytes per second will blow your log.

Setting use_apll to true of false didn't have much effect for me, so you can try turning it off if it produces warnings.

RaymiiOrg commented 8 months ago

I would suggest to print it in I2SComponent::read right after i2s_read, casting bytes to int16_t (signed) (if you're using 16 bits per sample). Yes I would use ESP_LOGD for that. But you only need to print some subsample of data, as printing all 48000 bytes per second will blow your log.

I'm not getting anything, just zeroes:

Logging right before the return true:

  ESP_LOGD(TAG, "Bytes read: %zu", *bytes_read);
  for(size_t i = 0; i < *bytes_read; ++i)
    ESP_LOGD(TAG, "i: %i, data: %02X", i, static_cast<uint16_t>(data[i]))
[21:09:09][D][i2s:048]: Bytes read: 2048
[21:09:09][D][i2s:050]: i: 0, data: 00
[21:09:09][D][i2s:050]: i: 1, data: 00
[21:09:09][D][i2s:050]: i: 2, data: 00
[21:09:09][D][i2s:050]: i: 3, data: 00

Until 2048...

RaymiiOrg commented 8 months ago

It seems that using different pins helps, instead of GPIO 20, 10 and 7, using 4, 5 and 6 does give me data:

[21:23:00][D][i2s:048]: Bytes read: 2048
[21:23:00][D][i2s:050]: i: 0, data: C3
[21:23:00][D][i2s:050]: i: 1, data: FF
[21:23:00][D][i2s:050]: i: 2, data: C3
[21:23:00][D][i2s:050]: i: 3, data: FF
[21:23:00][D][i2s:050]: i: 4, data: AC
[21:23:00][D][i2s:050]: i: 5, data: FF
[21:23:00][D][i2s:050]: i: 6, data: AC
[21:23:00][D][i2s:050]: i: 7, data: FF
[21:23:00][D][i2s:050]: i: 8, data: 9F
[21:23:00][D][i2s:050]: i: 9, data: FF
[21:23:00][D][i2s:050]: i: 10, data: 9F

Might be something to do with the specific pin layout: afbeelding

afbeelding

afbeelding

Thank you for the help so far!

RaymiiOrg commented 8 months ago

The measurements do seem quite high (loud), my other meter registers about 44 dB. That's for tomorrow to figure out. This issue can be closed I think since I no longer get inf or 256 values.

Thanks again for the help!

stas-sl commented 8 months ago

Glad to hear that you're getting more reasonable values now, you might also want to apply A-weighting then, it should give lower dB values, and it better matches how humans perceive loudness at different frequencies.

There also potentially could be other sources of radio/electromagnetic noises that might affect readings. So you can experiment turning off wifi/bluetooth, moving wires/mic around and check whether it makes any difference.

I'm closing for now, but feel free to comment if you will have more questions.

stas-sl commented 8 months ago

However it seems suspicious that you have LZmin = LZeq = LZmax, usually it should be LZmin < LZeq < LZmax < LZpeak