beechwoods-software / zephyr-cyw43-driver

Zephyr out of tree build for image with cyw43-driver
MIT License
16 stars 2 forks source link

I can not run AP and STA mode simultaneously. #2

Open munir-zin opened 8 months ago

munir-zin commented 8 months ago

Hi, I am facing issues setting both modes (AP and STA) of picow. When I try to setup AP mode 1st and then try a connection to router in STA mode, I get following error.

<err> zephyr_cyw43: Please disable access point mode before initiating a client connection.

Similarly, when I try to connect first and then enable AP mode, I get this error.

<err> zephyr_cyw43: Currently connected as a client. Please disconnect before enabling AP.

NOTE: I am trying to disconnect as well before enabling AP mode.

If I just enable AP mode, it works fine. But When I try to connect a client device with it, the client device does not get any IP address and no event for NET_EVENT_WIFI_AP_STA_CONNECTED is received.

drensber commented 8 months ago

You do have to disconnect before going into AP mode and disable AP mode before connecting. Not sure if that helps. If not, please provide detailed instructions for how to reproduce the problem. Thanks, —DaveOn Mar 21, 2024, at 4:29 AM, munir-zin @.***> wrote: Hi, I am facing issues setting both modes (AP and STA) of picow. When I try to setup AP mode 1st and then try a connection to router in STA mode, I get following error.

zephyr_cyw43: Please disable access point mode before initiating a client connection. Similarly, when I try to connect first and then enable AP mode, I get this error. zephyr_cyw43: Currently connected as a client. Please disconnect before enabling AP. NOTE: I am trying to disconnect as well before enabling AP mode. —Reply to this email directly, view it on GitHub, or unsubscribe.You are receiving this because you are subscribed to this thread.Message ID: ***@***.***>
munir-zin commented 8 months ago

H idrensber, Thank you for your response. I actually want to put the device in both modes simultaneously. I am trying to achieve following:

  1. Start device in STA mode and try to connect to the provided network.
  2. If connection is fail, start AP mode and also keep trying to reconnect.
  3. User can connect through this AP and a webpage should be displayed to the user with SSID and Password settings.
  4. User can update these fields and device can connect to these new credentials.

Can you please let me know if I can achieve this using these CYW43-driver API?

I have achieved this using pico_sdk link. Now I want to achieve this using Zephyr environment.

beechwoods-software commented 8 months ago

H idrensber, Thank you for your response. I actually want to put the device in both modes simultaneously. I am trying to achieve following:

  1. Start device in STA mode and try to connect to the provided network.
  2. If connection is fail, start AP mode and also keep trying to reconnect.
  3. User can connect through this AP and a webpage should be displayed to the user with SSID and Password settings.
  4. User can update these fields and device can connect to these new credentials.

Can you please let me know if I can achieve this using these CYW43-driver API?

I have achieved this using pico_sdk link. Now I want to achieve this using Zephyr environment.

The driver that our Zephyr binding depends on as a submodule (https://github.com/georgerobotics/cyw43-driver.git) does not support simultaneously being in STA and AP mode. This is the same driver that's provided with the Pico SDK. If it did support simultaneous AP and STA, I would have passed that ability along in the Zephyr binding. I thought that the reason for this is because the WiFi module itself does not support it.

If you believe that this is an artificial limitation, you can experiment with removing the check that results in those error messages.

munir-zin commented 8 months ago

Hi, The wifi module supports both modes simultaneously, as I have done work on it using pico_sdk. There are certain conditions to synchronise them, e.g., if the device is in a connecting state, you can not perform scanning at that particular moment, and vice versa. I will check your code by removing the conditions you suggested and updating you on the results.

munir-zin commented 8 months ago

I am also facing another issue related to AP mode. When I try to connect a client device to AP mode of PicoW, It does not get IP address and no event for "AP_STA_Connected" is triggered.

beechwoods-software commented 8 months ago

When I was writing the code that prevented simultaneous AP and STA modes, I do remember thinking that sometimes I had both working simultaneously, so you could be right (I'd need to check the module spec to be sure).

I believe that you are correct that no IP address is set up when it goes into AP mode. I suspect that most people would want the AP network to have a DHCP server, but some may want static addresses. That is really part of the example app, not the driver, though.

If you want to enhance the sample application so that it can set up the IP network on the AP-mode network, I'd be happy to accept those changes. If you can get the simultaneous AP and STA mode working reliably, I'd be happy to take that too.

munir-zin commented 8 months ago

Thanks for the update. I will try both of these for my application, and once tested, I will share them with you.

munir-zin commented 8 months ago

I have updated the code to make both modes work, and I did it. Following code sections are commented (in file: zephyr_cyw43_drv.c:

function name: zephyr_cyw43_mgmt_connect
 // if ((cyw43_state.itf_state >> CYW43_ITF_STA) & 1) {
        //         int link_status=cyw43_wifi_link_status(&cyw43_state, CYW43_ITF_STA);

        //         if (link_status == CYW43_LINK_JOIN || link_status == CYW43_LINK_NONET) {
        //                 LOG_ERR("Already connected.\n");
        //                 rv = -EAGAIN;
        //                 return rv;
        //         }
        // }

        // if ((cyw43_state.itf_state >> CYW43_ITF_AP) & 1) {
        //         LOG_ERR("Please disable access point mode before initiating a client connection.\n");
        //         rv = -EBUSY;
        //         return rv;
        // }
Function name:zephyr_cyw43_mgmt_ap_enable

 // if ((cyw43_state.itf_state >> CYW43_ITF_STA) & 1) {

        //         int link_status=cyw43_wifi_link_status(&cyw43_state, CYW43_ITF_STA);

        //         if (link_status == CYW43_LINK_JOIN || link_status == CYW43_LINK_NONET) {
        //                 LOG_ERR("Currently connected as a client. Please disconnect before enabling AP.\n");
        //                 rv = -EBUSY;
        //                 return rv;
        //         }
        // }

I also added two event raise to detect AP mode enabled or disabled.

case ZEPHYR_CYW43_REQ_ENABLE_AP:
                err = zephyr_cyw43_enable_ap(zephyr_cyw43_device);
                if (err) { LOG_ERR("Enable AP returned error %d\n", err); }
                wifi_mgmt_raise_ap_enable_result_event(zephyr_cyw43_device->iface, err);
                break;
        case ZEPHYR_CYW43_REQ_DISABLE_AP:
                err = zephyr_cyw43_disable_ap(zephyr_cyw43_device);
                if (err) { LOG_ERR("Disable AP returned error %d\n", err); }
                wifi_mgmt_raise_ap_disable_result_event(zephyr_cyw43_device->iface, err);
                break;

Now I am having issue of assigning IP address to client device when connecting to AP mode. I used below code from zephyr/subsys/net/lib/dhcpv4/dhcpv4_server.c, but it has no impact. I would appreciate if you can suggest me some tutorial or guide for this mechanism.

struct in_addr baseAddr = {{{192, 168, 4, 1}}}; 
    net_dhcpv4_server_start(iface, &baseAddr);
beechwoods-software commented 8 months ago

As you discovered, there is a DHCP server in the core codebase, but it seems to be really poorly documented (Google search didn't turn up anything at all, really).

It will start by putting "CONFIG_NET_DHCPV4_SERVER=y" in the prj.conf file, but beyond that, I'm not really sure. I think the best "documentation" may be to just look at the code, so do a search like this:

fgrep CONFIG_NET_DHCPV4_SERVER -r zephyrproject/zephyr

You'll also need to find a way to start and stop the server at runtime, since you'll only want it running when the AP is enabled. One of my colleagues recently implemented something using the server, so I'll see if I can find more info from him about how to integrate it into the application.

munir-zin commented 8 months ago

I am getting error for dhcp server <err> net_dhcpv4_server: Address pool does not belong to the interface subnet.

I am settingup like this

    struct net_if *iface = net_if_get_default();
    uint8_t gateway_addr[] = {192, 168, 4, 1};
    uint8_t netmask_addr[] = {255, 255, 255, 0};
    iface->config.ip.ipv4->gw.s_addr = htonl(*(uint32_t *)gateway_addr);
    iface->config.ip.ipv4->netmask.s_addr = htonl(*(uint32_t *)netmask_addr);

 struct in_addr baseAddr = {{{192, 168, 4, 1}}};
    net_dhcpv4_server_start(iface, &baseAddr);
munir-zin commented 8 months ago

I am getting "Network Interface is not configured". I have configured the network interface by setting up following configurations in prj.conf.

CONFIG_NETWORKING=y
CONFIG_NET_CONFIG_INIT_TIMEOUT=60
CONFIG_NET_CONFIG_AUTO_INIT=y
CONFIG_NET_CONFIG_NEED_IPV4=y

The error logs are:


*** Booting Zephyr OS build v3.6.0 ***
[00:00:02.584,000] <inf> net_config: Initializing network
[00:00:02.584,000] <inf> net_config: Waiting interface 1 (0x3ffb2508) to be up...
[00:00:02.585,000] <err> esp32_wifi: Failed to send packet
[00:00:02.586,000] <err> esp32_wifi: Failed to send packet
[00:00:02.586,000] <err> esp32_wifi: Failed to send packet
[00:00:02.586,000] <inf> net_config: Interface 1 (0x3ffb2508) coming up
[00:00:02.587,000] <err> esp32_wifi: Failed to send packet
[00:00:02.587,000] <inf> net_config: Running dhcpv4 client...
[00:00:03.587,000] <err> esp32_wifi: Failed to send packet
[00:00:04.588,000] <err> esp32_wifi: Failed to send packet
[00:00:11.588,000] <err> esp32_wifi: Failed to send packet
[00:00:16.588,000] <err> esp32_wifi: Failed to send packet
[00:00:24.589,000] <err> esp32_wifi: Failed to send packet
[00:00:41.589,000] <err> esp32_wifi: Failed to send packet
[00:00:50.588,000] <err> net_config: Timeout while waiting network setup
[00:00:50.588,000] <err> net_config: Network initialization failed (-116)

When I try to connect client device, then error logs are:

[00:02:49.293,000] <wrn> net_if: iface 0x3ffb2508 is down
[00:02:49.293,000] <err> net_dhcpv4_server: Failed to send ICMP probe
[00:02:51.209,000] <wrn> net_if: iface 0x3ffb2508 is down
[00:02:51.209,000] <err> net_dhcpv4_server: Failed to send ICMP probe
[00:02:53.224,000] <wrn> net_if: iface 0x3ffb2508 is down
[00:02:53.224,000] <err> net_dhcpv4_server: Failed to send ICMP probe
[00:02:58.027,000] <wrn> net_if: iface 0x3ffb2508 is down
[00:02:58.027,000] <err> net_dhcpv4_server: Failed to send ICMP probe
drensber commented 8 months ago

Hi,

I'm actually having problems with AP mode too. I'd honestly not tested it much before (I only really checked to see if it advertised an SSID and that another client could connect to it). I'm going to look into this further, as I'm doing something right now where the AP mode would be important. I'll cross-check what I do with an ESP32 module just to make sure that this isn't a generic problem with Zephyr and Zephyr configuration vs. a problem with the wifi module and driver.

munir-zin commented 8 months ago

This is the code I am trying to achieve the functionality of.

I am using PicoW and ESP32 devices to setup WiFIManager. I am starting device in AP and STA both modes simultaneously. When I try to connect a client device, I am getting these issue:

  1. AP mode is enabled, but no event for NET_EVENT_WIFI_AP_ENABLE_RESULT is triggered.
  2. When I connect a client device (Mobile) to AP mode, it is not getting any IP addresses and no event is triggered for NET_EVENT_WIFI_AP_STA_CONNECTED.
bool IS_AP_ENABLED = false;

void enableApMode(void)
{
    LOG_DBG("Enabling AP Mode.");
    struct net_if * netifInterface = net_if_get_default();
    struct in_addr gateway_addr = {192, 168, 4, 1};
    struct in_addr netmask_addr = {255, 255, 255, 0};
    struct in_addr baseAddr = gateway_addr;
    LOG_DBG("Gateway: %" PRIu32 ", Netmask: %" PRIu32 "", gateway_addr.s_addr, netmask_addr.s_addr);
    net_if_ipv4_set_netmask(netifInterface, &netmask_addr);
    net_if_ipv4_set_gw(netifInterface, &gateway_addr);
    // netifInterface->config.ip.ipv4->gw.s_addr = htonl(*(uint32_t *)gateway_addr);
    // netifInterface->config.ip.ipv4->netmask.s_addr = htonl(*(uint32_t *)netmask_addr);

    struct wifi_connect_req_params wifiConfig = {
        .ssid = (const uint8_t *)AP_SSID,
        .ssid_length = strlen(AP_SSID),
        .psk = (const uint8_t *)AP_PSK,
        .psk_length = strlen(AP_PSK),
        .band = WIFI_FREQ_BAND_2_4_GHZ,
        .channel = WIFI_CHANNEL_ANY,
    };

    if (strlen(AP_PSK) > 0)
    {
        wifiConfig.security = WIFI_SECURITY_TYPE_PSK;
    }
    else
    {
        wifiConfig.security = WIFI_SECURITY_TYPE_NONE;
    }

    if (net_mgmt(NET_REQUEST_WIFI_AP_ENABLE, netifInterface, &wifiConfig, sizeof(struct wifi_connect_req_params)))
    {
        LOG_ERR("AP Mode Request Failed\n");
    }

    netifInterface = net_if_get_default();

    if (!netifInterface)
    {
        printk("Error: No network interface found!\n");
        return;
    }

    printk("DHCPv4 server started...\n");
    net_dhcpv4_server_start(netifInterface, &baseAddr);
}

void connectWiFi(void)
{
   struct net_if * netifInterface = net_if_get_default();

    struct wifi_connect_req_params wifiConfig =
        {
            .ssid = (const uint8_t *)SSID,
            .ssid_length = strlen(SSID),
            .psk = (const uint8_t *)PSK,
            .psk_length = strlen(PSK),
            .band = WIFI_FREQ_BAND_2_4_GHZ,
            .channel = WIFI_CHANNEL_ANY,
        };

    if (strlen(PSK) > 0)
    {
        wifiConfig.security = WIFI_SECURITY_TYPE_PSK;
    }
    else
    {
        wifiConfig.security = WIFI_SECURITY_TYPE_NONE;
    }

    wifiConfig.band = WIFI_FREQ_BAND_2_4_GHZ;
    wifiConfig.mfp = WIFI_MFP_OPTIONAL;

    LOG_DBG("Connecting to SSID: %s\n", wifiConfig.ssid);
    if (net_mgmt(NET_REQUEST_WIFI_CONNECT, netifInterface, &wifiConfig, sizeof(struct wifi_connect_req_params)))
    {
        LOG_ERR("WiFi Connection Request Failed\n");
    }
}

void handleWiFiEvents(struct net_mgmt_event_callback *cb, uint32_t mgmt_event, struct net_if *iface)
{
    switch (mgmt_event)
    {
    case NET_EVENT_WIFI_CONNECT_RESULT:
       const struct wifi_status *status = (const struct wifi_status *)cb->info;
       LOG_DBG("NET_EVENT_WIFI_CONNECT_RESULT");

       if (status->status)
       {
            LOG_ERR("WiFi connection failed (%d).", status->status);
            if (!IS_AP_ENABLED)
                enableApMode();
            else
                connectWiFi();
        }
        break;

    case NET_EVENT_WIFI_DISCONNECT_RESULT:
        LOG_DBG("NET_EVENT_WIFI_DISCONNECT_RESULT");
       if (!IS_AP_ENABLED)
                enableApMode();
      else
                connectWiFi();
        break;

    case NET_EVENT_WIFI_AP_ENABLE_RESULT:
        LOG_DBG("NET_EVENT_WIFI_AP_ENABLE_RESULT");
         IS_AP_ENABLED = true;
        connectWiFi();
        break;

    case NET_EVENT_WIFI_AP_DISABLE_RESULT:
        LOG_DBG("NET_EVENT_WIFI_AP_DISABLE_RESULT");
        IS_AP_ENABLED = false;
        break;

    case NET_EVENT_WIFI_AP_STA_CONNECTED:
        LOG_DBG("NET_EVENT_WIFI_AP_STA_CONNECTED");
        break;

    case NET_EVENT_WIFI_AP_STA_DISCONNECTED:
        LOG_DBG("NET_EVENT_WIFI_AP_STA_DISCONNECTED");
        break;

    case NET_EVENT_IPV4_ADDR_ADD:
        LOG_DBG("NET_EVENT_IPV4_ADDR_ADD");

        break;

    default:
        break;
    }
}

void main()
{
    LOG_DBG("Setting up WiFi on %s\n", CONFIG_BOARD);

    net_mgmt_init_event_callback(&cbWiFi, handleWiFiEvents, WIFI_EVENT_MASK);
    net_mgmt_init_event_callback(&cbIPV4, handleWiFiEvents, IPV4_EVENT_MASK);

    net_mgmt_add_event_callback(&cbWiFi);
    net_mgmt_add_event_callback(&cbIPV4);

    // enableApMode();
    connectWiFi();

    LOG_DBG("WiFi Ready...\n\n");
}
drensber commented 8 months ago

Thanks for sharing your code. I'm actually having trouble getting AP mode to work correctly on the Pico W (nevermind the simultaneous STA mode). Good to see that you have an ESP32 to cross-check with as well. Wifi+Zephyr is obviously a lot more mature on the ESP32 (which I mistakenly called "STM32" in my previous message).

munir-zin commented 8 months ago

Please do share your results after testing it.

drensber commented 8 months ago

Hi, I'm still looking at this. I pushed some changes that allow the repository to be built for esp32 for comparison. I'm currently working on trying to get a feature in that will optionally allow one to start the DHCP server from the driver in AP mode. I'm also going to be adding the ability to make the DHCP client startup from the driver optional (I think that for both AP mode and STA mode, driver startup of the DHCP client/server should be optional, because some people may want to start those from the application instead).

One thing I noticed while doing this that may help what you're doing: The driver starts the interface up in "dormant" mode. You need to take it out of dormant mode to use it with: net_if_dormant_off(iface);

I was doing that for STA mode upon connect, but I was not doing it upon enablement of AP mode.

drensber commented 7 months ago

Hi, I just pushed some changes that may help. In addition to the item I mentioned above, I also noticed that the call to cyw43_send_ethernet() has an "interface type" parameter that needs to be set to CYW43_ITF_AP if you're in AP mode or else packet sends don't actually work. I also added an option to have the driver start up a DHCP server when AP mode is initiated.

munir-zin commented 6 months ago

Hi, I have integrated the DHCP server and trying to connect client. The server is assigning the correct IP address but the gateway or router information is missing. When a device is connected to hotspot/router, it get IP address, gateway and subnet. In this case IP address and subnet are assigned, but gateway is 0.0.0.0. What could be the possible reason?

beechwoods-software commented 6 months ago

Hi, I have integrated the DHCP server and trying to connect client. The server is assigning the correct IP address but the gateway or router information is missing. When a device is connected to hotspot/router, it get IP address, gateway and subnet. In this case IP address and subnet are assigned, but gateway is 0.0.0.0. What could be the possible reason?

The reason is that setting the gateway just hasn't been implemented yet. There was a PR (https://github.com/beechwoods-software/zephyr-cyw43-driver/pull/3) submitted regarding this, but the thing I didn't like about it was:

  1. It assumed that the gateway address would be the same as the STA mode IP address of the board. I don't think that's necessarily a good assumption, so I think there needs to be a separate CONFIG item to represent the gateway.
  2. It assumes that there will be a gateway address at all, which is also not a good thing to assume. One common application of AP mode on a small IoT device will be to just connect to the device via it's AP mode to set up the STA address (commonly called "captive portal mode"). Because of this, any gateway address setting should be optional.

If you'd like to take that PR and make those changes, I'll accept it. Otherwise, I'll eventually do the same.

munir-zin commented 6 months ago

Hi, I am trying to write own dhcp server based on lwip apis. I tried to set CYW43_LWIP variable to 1, so I wan getting a lot of lwip files not found, I also could not lwip files in this repository. Please guide.

beechwoods-software commented 6 months ago

Hi, I am trying to write own dhcp server based on lwip apis. I tried to set CYW43_LWIP variable to 1, so I wan getting a lot of lwip files not found, I also could not lwip files in this repository. Please guide.

I think you would need to raise that issue with the cyw43-driver project (https://github.com/georgerobotics/cyw43-driver). The purpose of this project is to integrate cyw43-driver with Zephyr for the IP stack, DHCP server, etc. /instead of/ LWIP.

zin-HXQ commented 5 months ago

The problem why one can't have AP+STA mode is because of zephyr's limitation. it creates only single netif instance for same network device. In AP+STA mode, One may desire to set IP for AP mode statically while for STA it should be assigned via DHCP. Because of single netif instance we can'yt have two set of IPs. There are some macros like NET_DEVICE_DT_INST_DEFINE_INSTANCE which docs says that it can be used to make multiple netif instances bound to same network device. I tried it and it did not allow me create multiple instances.