uniapp微信小程序:WIFI设备配网之TCP/UDP开发AP配网

一、AP配网技术原理

1.1 配网模式选择

AP配网(SoftAP模式)是IoT设备配网成功率最高的方案之一

1、其核心原理:

  • ​设备端​ :启动AP模式(如SSID格式YC3000_XXXX,默认IP192.168.4.1
  • ​手机端​:直连设备热点后建立TCP/UDP通信,传输目标路由器的SSID/Password

1.2 协议交互流程

复制代码
sequenceDiagram
小程序->>设备AP: 连接热点(SSID:YC3000_XXXX)
小程序->>设备AP: 建立TCP连接(IP:192.168.4.1:8266)
小程序->>设备AP: 发送AT+CJWAP指令
设备AP-->>小程序: 返回"Data Correct"或"Data Error"
小程序->>设备AP: 发送AT+RST指令重启
设备AP->>路由器: 连接目标WiFi
基础描述(大白话)

WiFi设备进入配网状态,实际是进入AP模式,设备开放一个Wifi热点出来。手机通过连接上设备Wifi模块的热点,将路由器名字和密码直接发送给Wifi设备,设备接收成功后,重启设备。

UDP配网逻辑是一样的,如果需要UDP通信,命令参考UDPSocket | 微信开放文档

二、操作过程

2.1 用户长按按钮,触发WiFi设备进入配网模式。设备在此模式下创建wifi热点(单纯AP模式),开启TCP服务(默认 IP 为192.168.4.1,端口为8266),使指示灯闪烁。
2.2 小程序按照提示依次获取 Wi-Fi 列表,输入目标路由器的 SSID/PSW,再选择设备 softAP 热点的 SSID/PSW。
2.3 手机连接设备 softAP 热点成功后,小程序作为 TCP 客户端会连接 Wi-Fi 设备上面的 TCP 服务。
2.4小程序给设备 TCP 服务,发送目标 Wi-Fi 路由器的 SSID/PSW 。

2.5设备接收成功后重启,配网成功

三、UniApp开发实现

3.1 开发环境配置

1.​​manifest.json声明权限​​:

javascript 复制代码
"mp-weixin" : {
        "appid" : "******",//微信小程序appid
        "setting" : {
            "urlCheck" : false,
            "minified" : true
        },
        "usingComponents" : true,
        "permission" : {//必要,获取WiFi配置必须申请使用位置权限
            "scope.userLocation" : {
                "desc" : "用户使用小程序关联物联设备时,需要获取用户的所在位置区域"
            }
        },
        "requiredPrivateInfos" : [ "getLocation", "chooseLocation" ],
    },

2.设备热点连接​​(需用户手动操作引导)

javascript 复制代码
uni.connectWifi({
    SSID: 'YC3000_XXXX', // 设备热点名称
    password: 'z111111',
    success: (res) => { console.log("连接成功,开始TCP通信") },
    fail: (err) => { console.log("连接失败", err) }
});

3.2 TCP核心通信模块

连接建立
javascript 复制代码
    this.tcp = wx.createTCPSocket();
    console.log('运行')
    this.tcp.connect({
        address: '192.168.4.1',//默认 IP 为192.168.4.1
        port: 8266,//默认端口为8266
    });

指令发送

javascript 复制代码
this.tcp.write('AT+CJWAP="WIFI账户名","WIFI密码"');

重启设备

javascript 复制代码
this.tcp.write('AT+RST');

四、Demo页面源码

javascript 复制代码
<template lang="html">
	<view class="sin-home">
		<view class="">
			设备配网
		</view>
		<input v-model="form.name"  type="text" placeholder="网络1名称" placeholder-class="placeholder" >
		<input v-model="form.pwd"  type="text" placeholder="密码" placeholder-class="placeholder" >
		<view class="">
			获取授权
		</view>
		<button @click="getconfirm()">getconfirm</button>
		<view class="">
			获取wifi
		</view>
		<button @click="getWIFI()">getWIFI</button>
		<view class="">
			测试配网
		</view>
		<button @click="startWifi()">startWifi</button>
		<button @click="connectWifi()">connectWifi</button>
		<button @click="connectTCP()">connectTCP</button>
		<button @click="sendDataTCP()">sendDataTCP</button>
		<button @click="sendRST()">sendRST</button>
		
		<view>
			{{tips}}
		</view>
		{{tips2}}
	</view>
</template>

<script>
	export default {
		name: 'home',
		data() {
			return {
				form: {
					name: '',
					result: '',
					pwd: ''
				},
				tips:'暂无数据',
				tips2:'未连接',
				tcp:'',
			}
		},
		computed: {
			
		},
		watch: {
			
		},
		onLoad() {
			
		},
		async onReady() {
			
		},
		onShow() {
			
		},
		// 下拉刷新
		onPullDownRefresh:function(){
			
		},
		onPageScroll(e) {
			
		},
		methods: {
			getconfirm(){
				// 获取授权状态
				uni.getSetting({
					success: (res) => {
						const hasLocationAuth = res.authSetting['scope.userLocation']
						const hasAgreePrivacy = uni.getStorageSync('hasAgreePrivacy')
						// 如果隐私和位置权限都已授权,直接返回
						if (hasAgreePrivacy && hasLocationAuth) {
							resolve(true)
							return
						}
		
						// 处理隐私协议
						if (!hasAgreePrivacy) {
							uni.showModal({
							  title: '位置信息授权',
							  content: '获取WiFi列表需要位置权限,是否前往设置?',
							  confirmText: '去设置',
							  cancelText: '取消',
							  success: (modalRes) => {
							    if (modalRes.confirm) {
							      // 打开设置页面
							      uni.openSetting({
							        success: (settingRes) => {
							          resolve(!!settingRes.authSetting['scope.userLocation'])
							        },
							        fail: () => {
							          resolve(false)
							        }
							      })
							    } else {
							      resolve(false)
							    }
							  },
							  fail: () => {
							    resolve(false)
							  }
							})
						} 
					},
					fail: () => {
						resolve(false)
					}
				})
			},
			getWIFI(){
				wx.getWifiList({
				success(res) {
					this.tips = JSON.stringify(res)
					wx.onGetWifiList(function(res) {
						console.log("获取wifi列表");
						console.log(res.wifiList,102); //在这里提取列表数据
						this.tips = JSON.stringify(res.wifiList)
					})
				},
				fail(res) {
					console.log(res)
					//报错的相关处理
				},
			})
			},
			// 开启wifi
				startWifi() {
					console.log('开始wifi接口');
					wx.startWifi({
						complete (res) {
							console.log(res)
						}
					})
				},
				// 链接wifi
				connectWifi() {
					uni.connectWifi({
					  SSID: 'YC3000_XXXX', // 设备热点名称
					  password: '',
					  success: (res) => { console.log("连接成功,开始TCP通信") },
					  fail: (err) => { console.log("连接失败", err) }
					});
					return
				},
				// 链接TCP
				connectTCP() {
					this.tcp = wx.createTCPSocket();
					console.log('运行')
					this.tcp.connect({
					  address: '192.168.4.1',
					  port: 8266,
					});
				},
				// 发送命令-账号密码
				sendDataTCP() {
					this.tcp.write('AT+CJWAP="WIFI账户名","WIFI密码"');
				},
				// 发送命令-重启
				sendRST(){
					this.tcp.write('AT+RST');
				},

		}
	}
</script>

<style lang="scss">
	.sin-home{
		padding: 50px 20px;
	}
</style>

demo页面展示

五、注意

1、想要获取WiFi列表,必须授权获取位置信息。

2、开发者工具暂时不支持 调试,必须使用真机进行开发

3、使用手机手动切换连接设备热点会出问题,请使用connectWifi()链接

相关推荐
G_G#6 分钟前
纯前端js插件实现同一浏览器控制只允许打开一个标签,处理session变更问题
前端·javascript·浏览器标签页通信·只允许一个标签页
@大迁世界21 分钟前
TypeScript 的本质并非类型,而是信任
开发语言·前端·javascript·typescript·ecmascript
GIS之路30 分钟前
GDAL 实现矢量裁剪
前端·python·信息可视化
是一个Bug33 分钟前
后端开发者视角的前端开发面试题清单(50道)
前端
Amumu1213835 分钟前
React面向组件编程
开发语言·前端·javascript
专业开发者1 小时前
借助安全返场方案提升智慧建筑能效的新机遇
物联网·安全
持续升级打怪中1 小时前
Vue3 中虚拟滚动与分页加载的实现原理与实践
前端·性能优化
GIS之路1 小时前
GDAL 实现矢量合并
前端
hxjhnct1 小时前
React useContext的缺陷
前端·react.js·前端框架
前端 贾公子1 小时前
从入门到实践:前端 Monorepo 工程化实战(4)
前端