Page 1 of 1

【iOS】BLE-WiFi 双模设备先扫描支持的WiFi列表后配网

Posted: 2024年 Nov 27日 14:19
by yuguo

背景

设备无法联网的一个主要原因是它们无法连接到路由器。而设备连不上路由器的最常见原因,是设备不兼容当前路由器发出的WiFi。这种情况可能是因为路由器的待配网WiFi是2.4G的,也可能是因为路由器使用了较新的WiFi6或WiFi7标准,而设备无法适应这些新的WiFi技术。

如果能通过设备扫描当前环境下可以进行配网的WiFi,并且返回WiFi的信号,则可以规避一大部分以上问题。

优势

用户体验提升:通过扫描WiFi列表,设备可以展示周围可用的网络,用户无需手动输入SSID。这使得连接过程更加直观和简便,尤其对于不熟悉技术的用户来说。

减少错误连接:扫描WiFi可以确保用户选择的网络确实是当前环境中的有效网络,从而减少了手动输入SSID时可能出现的拼写错误或连接错误的可能性。

提高兼容性识别:设备在扫描后可以预先识别出哪些网络是不兼容的(例如,WiFi 5设备无法连接到仅支持WiFi 6的网络),从而避免用户尝试连接不支持的网络。

信号强度判断:通过扫描,设备可以提供每个网络的信号强度信息,帮助用户选择信号最强、最稳定的网络连接,以提高连接质量和稳定性。

支持网络选择:如果用户环境中有多个WiFi网络,扫描结果可以帮助用户在多个选项中选择最佳网络,比如一个信号更强或使用更少的网络。

安全性增强:通过显示加密类型,用户可以选择配备较强加密措施的安全WiFi,避免连接到不安全的网络。

代码示例

本功能仅适用于Tuya OS 3.8.0版本以上的设备,需要设备支持扫描WiFi列表才可使用。

查询WiFi列表API & 开始配网API

Code: Select all

@interface ThingSmartBLEWifiActivator : NSObject

/// 查询WiFi列表
/// @param UUID  设备uuid
/// @param success 连接成功回调
/// @param failure 连接失败回调
- (void)connectAndQueryWifiListWithUUID:(NSString *)UUID
                                success:(ThingSuccessHandler)success
                                failure:(ThingFailureError)failure;

/// 开始配网 (查询完WiFi列表后会保持蓝牙连接,通过此API进行配网)
/// @param UUID 设备uuid
/// @param token 配网token
/// @param ssid 待配网ssid
/// @param pwd 密码
/// @param timeout 配网超时时间
- (void)pairDeviceWithUUID:(NSString *)UUID token:(NSString *)token ssid:(NSString *)ssid pwd:(NSString *)pwd timeout:(long)timeout;

@end

查询WiFi列表回调

Code: Select all


@protocol ThingSmartBLEWifiActivatorDelegate <NSObject>

- (void)bleWifiActivator:(ThingSmartBLEWifiActivator *)activator didReceiveBLEWifiConfigDevice:(nullable ThingSmartDeviceModel *)deviceModel error:(nullable NSError *)error;

@optional

/// 设备配网状态回调 (如果不是待配网状态时触发)
- (void)bleWifiActivator:(ThingSmartBLEWifiActivator *)activator notConfigStateWithError:(NSError *)error;

/// WiFi列表扫描回调
- (void)bleWifiActivator:(ThingSmartBLEWifiActivator *)activator didScanWifiList:(NSArray *)wifiList uuid:(NSString *)uuid error:(NSError *)error;

@end

完整示例

Code: Select all

class OptDualModeViewController: UIViewController {
    
var startScanBtn = UIButton(type: .system) var stopScanBtn = UIButton(type: .system) var tableView: UITableView! var bleMgr = ThingSmartBLEManager.sharedInstance() var stashUuids = [String]() var deviceList = [(String, ThingBLEAdvModel)]() var req = ThingSmartActivatorDiscoveryRequest() var bleWifiMultier = ThingSmartBLEWifiMultier() override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. startScanBtn.frame = CGRect(x: 100, y: 150, width: 100, height: 20) startScanBtn.setTitle("Start Scan", for: .normal) startScanBtn.addTarget(self, action: #selector(startScan), for: .touchUpInside) view.addSubview(startScanBtn) stopScanBtn.frame = CGRect(x: 200, y: 150, width: 100, height: 20) stopScanBtn.setTitle("Stop Scan", for: .normal) stopScanBtn.addTarget(self, action: #selector(stopScan), for: .touchUpInside) view.addSubview(stopScanBtn) tableView = UITableView(frame: CGRect(x: 0, y: 220, width: view.frame.width, height: view.frame.height - 250), style: .plain) tableView.delegate = self tableView.dataSource = self tableView.register(UITableViewCell.classForCoder(), forCellReuseIdentifier: "cell") view.addSubview(tableView) } @objc func startScan() { bleMgr.delegate = self bleMgr.startListening(false) } @objc func stopScan() { bleMgr.stopListening(false) } func stopConfigWifi() { bleWifiMultier.stopConfigWifi() } } extension OptDualModeViewController: ThingSmartBLEManagerDelegate { func didDiscoveryDevice(withDeviceInfo deviceInfo: ThingBLEAdvModel) { let bleType = deviceInfo.bleType if bleType == ThingSmartBLETypeUnknow || bleType == ThingSmartBLETypeBLE || bleType == ThingSmartBLETypeBLESecurity || bleType == ThingSmartBLETypeBLEPlus || bleType == ThingSmartBLETypeBLEZigbee || bleType == ThingSmartBLETypeBLEBeacon { print("Please use BLE to pair: %@", deviceInfo.uuid ?? "") return }
if (!deviceInfo.isSupportQueryWifiList) { print("Device does not support wifi list query: %@", deviceInfo.uuid ?? "") return; } if (!stashUuids.contains(deviceInfo.uuid)) { stashUuids.append(deviceInfo.uuid) bleMgr.queryDeviceInfo(withUUID: deviceInfo.uuid, productId: deviceInfo.productId) { result in if let r = result, let name = r["name"] as? String { self.deviceList.append((name, deviceInfo)) self.tableView.reloadData() } } } } } extension OptDualModeViewController: UITableViewDelegate, UITableViewDataSource { func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "cell")! let (name, _) = deviceList[indexPath.row] cell.textLabel?.text = name return cell }
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return deviceList.count } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { let (name, deviceInfo) = deviceList[indexPath.row] let vc = ChooseWifiViewController(uuid: deviceInfo.uuid) vc.title = name navigationController?.pushViewController(vc, animated: true) } } struct WifiModel: Codable { let ssid: String let rssi: Int let sec: Int } class ChooseWifiViewController: UIViewController, ThingSmartBLEWifiActivatorDelegate { var uuid: String? var tableView: UITableView! var wifiList = Array<WifiModel>() let wifiActer = ThingSmartBLEWifiActivator.sharedInstance()
init(uuid: String? = nil) { self.uuid = uuid super.init(nibName: nil, bundle: nil) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func viewDidLoad() { view.backgroundColor = .white tableView = UITableView(frame: CGRect(x: 0, y: 120, width: view.frame.width, height: view.frame.height - 140), style: .plain) tableView.delegate = self tableView.dataSource = self tableView.register(UITableViewCell.classForCoder(), forCellReuseIdentifier: "cell") view.addSubview(tableView) if let uuid = uuid { SVProgressHUD.show() wifiActer.bleWifiDelegate = self wifiActer.connectAndQueryWifiList(withUUID: uuid) { } failure: { _ in } } else { SVProgressHUD.showError(withStatus: "uuid is invalid") } } func startActiveDevice(ssid: String, pwd: String) { guard let uuid = uuid else {return} ThingSmartActivator.sharedInstance()?.getTokenWithHomeId(Home.current!.homeId) { token in guard let t = token else {return} self.wifiActer.pairDevice(withUUID: uuid, token: t, ssid: ssid, pwd: pwd, timeout: 120) SVProgressHUD.show() } failure: { _ in } } func bleWifiActivator(_ activator: ThingSmartBLEWifiActivator, didReceiveBLEWifiConfigDevice deviceModel: ThingSmartDeviceModel?, error: Error?) { SVProgressHUD.dismiss() if error == nil && deviceModel != nil { SVProgressHUD.showSuccess(withStatus: "Device active success!!!") self.navigationController?.popToRootViewController(animated: true) } } func bleWifiActivator(_ activator: ThingSmartBLEWifiActivator, didScanWifiList wifiList: [Any], uuid: String, error: Error) { SVProgressHUD.dismiss() if (error != nil) { print("\(error)") } if let wifis = wifiList as? [[String:Any]] { for w in wifis { do { let jsonData = try JSONSerialization.data(withJSONObject: w, options: []) let decoder = JSONDecoder() let wifiModel = try decoder.decode(WifiModel.self, from: jsonData) self.wifiList.append(wifiModel) self.tableView.reloadData() } catch { print("Error decoding JSON: \(error)") } } self.tableView.reloadData() } } } extension ChooseWifiViewController: UITableViewDelegate, UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return wifiList.count }
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "cell")! let wifiModel = self.wifiList[indexPath.row] cell.textLabel?.text = wifiModel.ssid return cell } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { let wifiModel = self.wifiList[indexPath.row] let alertController = UIAlertController(title: wifiModel.ssid, message: nil, preferredStyle: .alert) alertController.addTextField { textField in textField.placeholder = "Enter your Password" } let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil) alertController.addAction(cancelAction) let okAction = UIAlertAction(title: "OK", style: .default) { [weak self] _ in if let text = alertController.textFields?.first?.text { print("Entered Password: \(text)") self?.startActiveDevice(ssid: wifiModel.ssid, pwd: text) } } alertController.addAction(okAction) present(alertController, animated: true, completion: nil) } }

小结

通过设备扫描的WiFi列表进行配网,可以更精准地识别当前WiFi是否适用于配网设备。这不仅提升了配网体验,还显著提高了配网的成功率,从而减少用户的困扰和烦恼。