From 4cde22ecdbdb0a75316eb253868f6cb046407e38 Mon Sep 17 00:00:00 2001 From: Neucrack Date: Mon, 5 Aug 2024 14:21:07 +0800 Subject: [PATCH] add wifi AP mode support --- components/network/include/maix_wifi.hpp | 78 +++++--- components/network/port/linux/maix_wifi.cpp | 201 ++++++++++++++++++-- 2 files changed, 237 insertions(+), 42 deletions(-) diff --git a/components/network/include/maix_wifi.hpp b/components/network/include/maix_wifi.hpp index 08490da3..4859273c 100644 --- a/components/network/include/maix_wifi.hpp +++ b/components/network/include/maix_wifi.hpp @@ -17,50 +17,50 @@ namespace maix::network::wifi /** * WiFi AP info * @maixpy maix.network.wifi.AP_Info - */ + */ class AP_Info { public: /** * WiFi AP info SSID * @maixpy maix.network.wifi.AP_Info.ssid - */ + */ std::vector ssid; /** * WiFi AP info BSSID * @maixpy maix.network.wifi.AP_Info.bssid - */ + */ std::string bssid; /** * WiFi AP info security * @maixpy maix.network.wifi.AP_Info.security - */ + */ std::string security; /** * WiFi AP info channel * @maixpy maix.network.wifi.AP_Info.channel - */ + */ int channel; /** * WiFi AP info frequency * @maixpy maix.network.wifi.AP_Info.frequency - */ + */ int frequency; /** * WiFi AP info rssi * @maixpy maix.network.wifi.AP_Info.rssi - */ + */ int rssi; /** * WiFi AP info ssid_str * @maixpy maix.network.wifi.AP_Info.ssid_str - */ + */ std::string ssid_str() { return std::string(ssid.begin(), ssid.end()); @@ -71,13 +71,13 @@ namespace maix::network::wifi * List WiFi interfaces * @return WiFi interface list, string type * @maixpy maix.network.wifi.list_devices - */ + */ std::vector list_devices(); /** * Wifi class * @maixpy maix.network.wifi.Wifi - */ + */ class Wifi { public: @@ -86,7 +86,7 @@ namespace maix::network::wifi * @param iface wifi interface name, default is wlan0 * @maixpy maix.network.wifi.Wifi.__init__ * @maixcdk maix.network.wifi.Wifi.Wifi - */ + */ Wifi(std::string iface = "wlan0"); ~Wifi(); @@ -94,14 +94,14 @@ namespace maix::network::wifi * Get current WiFi ip * @return ip, string type, if network not connected, will return empty string. * @maixpy maix.network.wifi.Wifi.get_ip - */ + */ std::string get_ip(); /** * Get current WiFi MAC address * @return ip, string type. * @maixpy maix.network.wifi.Wifi.get_mac - */ + */ std::string get_mac(); /** @@ -110,14 +110,14 @@ namespace maix::network::wifi * attention, first time call this method will auto matically read config from file, and if call connect method will set cache. * @return SSID, string type. * @maixpy maix.network.wifi.Wifi.get_ssid - */ + */ std::string get_ssid(bool from_cache = true); /** * Get current WiFi ip * @return ip, string type, if network not connected, will return empty string. * @maixpy maix.network.wifi.Wifi.get_gateway - */ + */ std::string get_gateway(); // std::string get_channel(); @@ -131,20 +131,20 @@ namespace maix::network::wifi * WiFi start scan AP info around in background. * @return If success, return err.Err.ERR_NONE, else means failed. * @maixpy maix.network.wifi.Wifi.start_scan - */ + */ err::Err start_scan(); /** * Get WiFi scan AP info. * @return wifi.AP_Info list. * @maixpy maix.network.wifi.Wifi.get_scan_result - */ + */ std::vector get_scan_result(); /** * Stop WiFi scan AP info. * @maixpy maix.network.wifi.Wifi.stop_scan - */ + */ void stop_scan(); /** @@ -155,41 +155,59 @@ namespace maix::network::wifi * @param timeout connect timeout internal, unit second. * @return If success, return err.Err.ERR_NONE, else means failed. * @maixpy maix.network.wifi.Wifi.connect - */ + */ err::Err connect(const std::string &ssid, const std::string &password, bool wait = true, int timeout = 60); /** * Disconnect from WiFi AP. * @return If success, return err.Err.ERR_NONE, else means failed. * @maixpy maix.network.wifi.Wifi.disconnect - */ + */ err::Err disconnect(); /** * See if WiFi is connected to AP. * @return If connected return true, else false. * @maixpy maix.network.wifi.Wifi.is_connected - */ + */ bool is_connected(); // std::string get_rssi(); - // AP mode + /** + * Start WiFi AP. + * @param ssid SSID of AP. + * @param password password of AP, if no password, leave it empty. + * @param ip ip address of hostap, default empty string means auto generated one according to hardware. + * @param netmask netmask, default 255.255.255.0, now only support 255.255.255.0 . + * @param mode WiFi mode, default g(IEEE 802.11g (2.4 GHz)), a = IEEE 802.11a (5 GHz), b = IEEE 802.11b (2.4 GHz). + * @param channel WiFi channel number, 0 means auto select. MaixCAM not support auto, will default channel 1. + * @param hidden hidden SSID or not. + * @return If success, return err.Err.ERR_NONE, else means failed. + * @maixpy maix.network.wifi.Wifi.start_ap + */ err::Err start_ap(const std::string &ssid, const std::string &password, - const std::string &ip = "192.168.66.1", const std::string &netmask = "255.255.255.0", - int channel = 1, bool hidden = false, - const std::string &ssid_5g = "", const std::string &password_5g = "", - int channel_5g = 36, bool hidden_5g = false, - int bandwidth = 20, int bandwidth_5g = 20 - ); + std::string mode = "g", int channel = 0, + const std::string &ip = "192.168.66.1", const std::string &netmask = "255.255.255.0", + bool hidden = false); + + /** + * Stop WiFi AP. + * @return If success, return err.Err.ERR_NONE, else means failed. + * @maixpy maix.network.wifi.Wifi.stop_ap + */ err::Err stop_ap(); + + /** + * Whether WiFi is AP mode + * @return True if AP mode now, or False. + * @maixpy maix.network.wifi.Wifi.is_ap_mode + */ bool is_ap_mode(); private: std::string _iface; - bool _ap_mode; std::string _ssid; bool _ssid_cached; }; } // namespace maix::wifi - diff --git a/components/network/port/linux/maix_wifi.cpp b/components/network/port/linux/maix_wifi.cpp index 1348fb40..8e8027ce 100644 --- a/components/network/port/linux/maix_wifi.cpp +++ b/components/network/port/linux/maix_wifi.cpp @@ -20,6 +20,22 @@ namespace maix::network::wifi { + static std::string split_ip3(const std::string &ip) + { + // Find the last dot in the string + size_t lastDot = ip.rfind('.'); + + // Check if a dot was found + if (lastDot != std::string::npos) + { + // Return the substring from the beginning to the last dot + return ip.substr(0, lastDot); + } + + // If no dot is found, return the original string (or handle it as an error) + return ip; + } + static int wifi_freq_to_channel(int freq) { if (freq >= 2412 && freq <= 2484) @@ -368,13 +384,13 @@ bssid / frequency / signal level / flags / ssid * Get current WiFi SSID * @return SSID, string type. * @maixpy maix.network.wifi.Wifi.get_ssid - */ + */ std::string Wifi::get_ssid(bool from_cache) { - if((!from_cache) || !_ssid_cached) + if ((!from_cache) || !_ssid_cached) { fs::File *f = fs::open("/boot/wifi.ssid", "r"); - if(f) + if (f) { std::string *ssid = f->readline(); _ssid = *ssid; @@ -391,10 +407,26 @@ bssid / frequency / signal level / flags / ssid return _ssid; } - std::string Wifi::get_gateway() { // get gateway ip + if (is_ap_mode()) + { + std::string cmd = "ip route | grep " + _iface + " | grep src | awk '{print $NF}'"; + FILE *fp = popen(cmd.c_str(), "r"); + if (fp == NULL) + { + return ""; + } + char buf[1024]; + if (fgets(buf, sizeof(buf), fp) == NULL) + { + pclose(fp); + return ""; + } + pclose(fp); + return buf; + } std::string cmd = "ip route | grep default | grep " + _iface + " | awk '{print $3}'"; FILE *fp = popen(cmd.c_str(), "r"); if (fp == NULL) @@ -417,8 +449,18 @@ bssid / frequency / signal level / flags / ssid _ssid_cached = true; #if PLATFORM_MAIXCAM // write ssid to /boot/wifi.ssid and password to /boot/wifi.pass + // write wifi.sta remove wifi.ap // then opoen /etc/init.d/S30wifi restart + // ensure wifi.sta + if (fs::exists("/boot/wifi.ap")) + { + fs::remove("/boot/wifi.ap"); + } + fs::File *f = fs::open("/boot/wifi.sta", "w"); + f->close(); + delete f; + // write ssid to /boot/wifi.ssid FILE *fp = fopen("/boot/wifi.ssid", "w"); if (fp == NULL) @@ -456,11 +498,27 @@ bssid / frequency / signal level / flags / ssid #else throw err::Exception(err::ERR_NOT_IMPL, "connect wifi not implemented in this platform"); #endif - while (wait && !is_connected() && time::ticks_s() - t < timeout) + uint64_t last_t = time::ticks_s(); + while (wait && !is_connected() && time::ticks_s() - t < timeout && !app::need_exit()) { + if (time::ticks_s() - last_t > 8) + { + log::info("wait connect %d/%d s", time::ticks_s() - t, timeout); + last_t = time::ticks_s(); + } time::sleep_ms(50); } - if(wait && !is_connected()) + last_t = time::ticks_s(); + while (wait && get_ip().empty() && time::ticks_s() - t < timeout && !app::need_exit()) + { + if (time::ticks_s() - last_t > 8) + { + log::info("wait get ip %d/%d s", time::ticks_s() - t, timeout); + last_t = time::ticks_s(); + } + time::sleep_ms(50); + } + if (wait && !is_connected()) { log::error("Connect failed, wait get ip timeout"); return err::Err::ERR_TIMEOUT; @@ -498,21 +556,140 @@ bssid / frequency / signal level / flags / ssid // AP mode err::Err Wifi::start_ap(const std::string &ssid, const std::string &password, + std::string mode, int channel, const std::string &ip, const std::string &netmask, - int channel, bool hidden, - const std::string &ssid_5g, const std::string &password_5g, - int channel_5g, bool hidden_5g, - int bandwidth, int bandwidth_5g) + bool hidden) { + _ssid = ssid; + _ssid_cached = true; +#if PLATFORM_MAIXCAM + // write ssid to /boot/wifi.ssid and password to /boot/wifi.pass + // write wifi.ap remove wifi.sta + // write /boot/hostapd.conf + // then opoen /etc/init.d/S30wifi restart + + if (channel <= 0) + channel = 1; + + // ensure wifi.ap + if (fs::exists("/boot/wifi.sta")) + { + fs::remove("/boot/wifi.sta"); + } + fs::File *f = fs::open("/boot/wifi.ap", "w"); + f->close(); + delete f; + + // write ssid to /boot/hostapd.conf + FILE *fp = fopen("/boot/hostapd.conf", "w"); + if (fp == NULL) + { + log::error("open /boot/hostapd.conf failed"); + return err::Err::ERR_IO; + } + // see https://w1.fi/cgit/hostap/plain/hostapd/hostapd.conf + std::string conf = "ctrl_interface=/var/run/hostapd\n\ +ctrl_interface_group=0\n\ +beacon_int=100\n\ +dtim_period=2\n\ +max_num_sta=255\n\ +rts_threshold=-1\n\ +fragm_threshold=-1\n\ +macaddr_acl=0\n\ +auth_algs=3\n\ +wpa=2\n\ +ieee80211n=1\n"; + conf += "ssid=" + ssid + "\n"; + conf += "hw_mode=" + mode + "\n"; + conf += "wpa_passphrase=" + password + "\n"; + conf += "channel=" + std::to_string(channel) + "\n"; + fwrite(conf.c_str(), 1, conf.size(), fp); + fclose(fp); + + // write dhcp config + fp = fopen("/etc/udhcpd.wlan0.conf", "w"); + if (fp == NULL) + { + log::error("open /etc/udhcpd.wlan0.conf failed"); + return err::Err::ERR_IO; + } + // see https://w1.fi/cgit/hostap/plain/hostapd/hostapd.conf + std::string ip_prefix = split_ip3(ip); + conf = "start " + ip_prefix + ".100\n" + + "end " + ip_prefix + ".200\n" + + "interface " + _iface + "\n\ +pidfile /var/run/udhcpd." + + _iface + ".pid\n\ +lease_file /var/lib/misc/udhcpd." + + _iface + ".leases\n\ +option subnet " + + netmask + "\n\ +option lease 864000\n"; + fwrite(conf.c_str(), 1, conf.size(), fp); + fclose(fp); + + // write ssid to /boot/wifi.ssid + fp = fopen("/boot/wifi.ssid", "w"); + if (fp == NULL) + { + log::error("open /boot/wifi.ssid failed"); + return err::Err::ERR_IO; + } + fwrite(ssid.c_str(), 1, ssid.size(), fp); + fclose(fp); + + // write ssid to /boot/wifi.ssid + fp = fopen("/boot/wifi.ipv4_prefix", "w"); + if (fp == NULL) + { + log::error("open /boot/wifi.ipv4_prefix failed"); + return err::Err::ERR_IO; + } + fwrite(ip_prefix.c_str(), 1, ip_prefix.size(), fp); + fclose(fp); + + sync(); + + // restart wifi + if (access("/etc/init.d/S30wifi", F_OK) == -1) + { + log::error("/etc/init.d/S30wifi not found"); + return err::Err::ERR_NOT_FOUND; + } + int ret = system("/etc/init.d/S30wifi restart"); + if (ret != 0) + { + log::error("restart wifi failed: %d", ret); + return err::Err::ERR_RUNTIME; + } +#else return err::Err::ERR_NOT_IMPL; +#endif + return err::ERR_NONE; } err::Err Wifi::stop_ap() { - return err::Err::ERR_NOT_IMPL; +#if PLATFORM_MAIXCAM + // opoen /etc/init.d/S30wifi stop + if (access("/etc/init.d/S30wifi", F_OK) == -1) + { + log::error("/etc/init.d/S30wifi not found"); + return err::Err::ERR_NOT_FOUND; + } + int ret = system("/etc/init.d/S30wifi stop"); + if (ret != 0) + { + log::error("stop wifi failed: %d", ret); + return err::Err::ERR_RUNTIME; + } + return err::Err::ERR_NONE; +#else + throw err::Exception(err::ERR_NOT_IMPL, "stop_ap wifi not implemented in this platform"); +#endif } bool Wifi::is_ap_mode() { - return false; + return fs::exists("/boot/wifi.ap"); } } // namespace maix::wifi