WIFI channel sensor for ESP-IDF framework

Hello I have had a WIFI channel sensor running under the arduino framework for about 6 months (I use this to monitor if ESP devices are swapping WAPs). The code for the sensor using the arduino framework is:

sensor:
  - platform: template
    name: Wifi Channel
    id: wifi_channel
    icon: "mdi:wifi-marker"
    lambda: return WiFi.channel();
    accuracy_decimals: 0
    update_interval: 60s

However, I have just switched to the ESP-IDF framework to use the bluetooth proxy, but when compiling it says “WiFi is out of scope” and fails.

I have tried several other ideas from google on how to read the wifi channel but none seem to work/compile with the ESP-IDF framework.

Does anyone know a working solution to get the WIFI channel when using the ESP-IDF framework?

Might be easier to use the Wifi Info Text Sensor

# Example configuration entry
text_sensor:
  - platform: wifi_info
    ip_address:
      name: ESP IP Address
      address_0:
        name: ESP IP Address 0
      address_1:
        name: ESP IP Address 1
      address_2:
        name: ESP IP Address 2
      address_3:
        name: ESP IP Address 3
      address_4:
        name: ESP IP Address 4
    ssid:
      name: ESP Connected SSID
    bssid:
      name: ESP Connected BSSID
    mac_address:
      name: ESP Mac Wifi Address
    scan_results:
      name: ESP Latest Scan Results
    dns_address:
      name: ESP DNS Address

The BSSID will tell you which AP you’re connected to.

By WiFi “channel”, I read OP as meaning like this?

I feel like the answer may be in here somewhere, possibly around line 314.

https://esphome.io/api/wifi__component__esp__idf_8cpp_source

Yes I would like the actual WIFI channel. I do have the BSSID but I don’t find it very user friendly. I could convert the BSSID to the channel but I would prefer not to do that as if I the channels change on the routers I would need to remember to do it in the conversion.

Yesterday I spent quite sometime looking at Mahko_Mahko’s above reference from line 314 and this would be an ideal solution that would be framework independent. Through the use of get_channel() on the WiFiAP object for a client connection.

Unfortunately it seems it is impossible to obtain the required WiFiAP object it operates on as it is protected and no function of WIFIComponent allows you to get the correct WiFiAP object. WIFIComponent does have a function get_ap() but this only returns the WiFiAP object when the controller is operating in access point mode rather than client mode. There is a second WiFiAP object in WIFIComponent that is inaccessible which is used for the client connection.

I can see only two solutions add a function to WIFIComponent that allows you to get the protected WiFiAP object, or somehow override the C++ protection (I dont know how as I am a lot happier with C than I am C++)

I think it is in principle possible to go directly to the underlying ESP-IDF framework to extract the channel, with code something like the following. but this fails to compile as I am pretty sure I need to include the framework headers and I cant work it out how to do this.

    lambda: |-
      wifi_ap_record_t wifidata;
      esp_wifi_sta_get_ap_info(&wifidata);
      return wifidata.primary;
1 Like

I would also post your question and findings so far on the ESPHome Discord.

Quite a few developers hang out over there and they may have more of a clue about the lower level stuff.

Mahko thanks I will look into discord as its not something I have used very much although I do have an account.

1 Like

any good news ?

I just posted it to Discord, I have also started to read up on how to install an ESPhome development environment, I just have a lot more to take in for that option.

C files listed under “esphome: / includes:” get compiled and linked automatically. They can #include esp-idf framework header files. Also, header files listed under “esphome: / includes:” get included by the generated main.cpp and can declare functions so that they can be called from lambdas. For example, this can be placed in the esphome: section:

  includes:
    - idfWifi.h
    - idfWifi.c

and when idfWifi.h contains this:

extern "C"
    {
    int idfWifiGetChannelNum (void);
    }

and idfWifi.c contains this:

#include "esp_wifi.h"

int idfWifiGetChannelNum (void)
    {
    wifi_ap_record_t ap_info;

    if (esp_wifi_sta_get_ap_info (&ap_info) != ESP_OK)
        return (-1);

    return (ap_info.primary);
    }

then esphome yaml code can define a text_sensor using code like this:

text_sensor:
  - platform: template
    id: wifi_channel
    name: "Net WiFi Channel"
    lambda: |-
      static char str[3]; // two digits (or -1) plus NULL
      sprintf(str, "%2d", idfWifiGetChannelNum());
      return (std::string) str;

I tested this with idf 5.2.1 and platformio 6.7.0 and it worked. YMMV, Maarten

2 Likes

hi @maartenwrs can you convert text_sensor template to sensor template ? as the end in HA be as Numer not Text

Thank you @maartenwrs that is what I was looking for, I didnt know how to include c and header files, very helpful.

@Control this works as a sensor rather than text sensor:

sensor:
  - platform: template
    name: Wifi Channel
    id: wifi_channel
    icon: "mdi:wifi-marker"
    lambda: |-
      return idfWifiGetChannelNum(); 
    accuracy_decimals: 0
    update_interval: 60s
2 Likes

thanks @iotola :grinning:

error: ‘idfWifiGetChannelNum’ was not declared in this scope

You need to include the steps that maartenwrs wrote on Jun 26 as well as the sensor template that I wrote. (I am still using this on ESPhome version 2024.8.0 so it should still work)

1 Like

Thanks @iotola :blush:

I forget it :sweat_smile: