资讯详情

小度音箱+esp8266做语音控制开关

可用:插板,esp-01s继电器模块,usb转串口ttl模块、https服务器,小音箱。

大概原理:

https开关状态查询接口提供在服务器上,esp-01s烧录编写的固件可以连接wifi后轮询这个接口,并根据接口返回的状态打开或关闭继电器。在小技能平台上创建开关技能https提供服务器oauth2.0接口和遵循dueros智能家具协议的开关控制接口。这样,通过小度真机测试模式,小度可以打开开关https服务器打开开关接口,修改开关状态,间接控制开关开关。

大概过程:

(1)插板改造,把esp-01s继电器模块的常开始连接到插板的一线上。

(2)esp-01s可以使用nodemcu固件,直接lua开发,简化过程。

关于nodemcu固件,可以去https://nodemcu-build.com/在线编译,通过填邮箱、选择需要的模块、点击开始构建后,过会会收到包含固件下载地址的邮件。之前在这里编译过,当时选择了模块(adc,enduser_setup,file,gpio,http,mqtt,net,node,tmr,uart,wifi,tls),选定的模块,在lua相关脚本可用于脚本api,api文档地址https://nodemcu.readthedocs.io/en/master/。

关于固件烧录,esp-01s供电为3.3v,usb转ttl供电为5v,这里烧录时做了一个转接板连接,用asm1117 3.3v稳压器3.3v(usb转ttl带了3.3v电源,但没用,有些数据显示可能会烧坏,没有尝试),RST与GND连接微动开关重启,GPIO0与GND拨动开关用于切换下载模式。连接后,打开NodeMCU-PyFlasher-4.0-x64.exe,115200选择串口和固件,Quad I/O,点击刷写,等待完成。连接后,打开NodeMCU-PyFlasher-4.0-x64.exe,115200选择串口和固件,Quad I/O,点击刷写,等待完成。如果不正常,试几次,建议短线连接。

(3)写入lua程序。

换用ESPlorer,选择串口,115200,打开串口,根据前转接板上的微开关重新启动,刷固件将初始化文件系统。命令线显示在软件的右侧,命令或脚本写在左侧。print("hello")并选择发送,发送到命令行,执行后显示结果hello。

这个时候可以写需要的。init.lua脚本,通过upload上传,写入esp-01s,按微动开关重启,nodemcu自动加载init.lua脚本执行,连接wifi,请求接口。

通过修改接口返回值,可以检测控制开关。

-- //init.lua print('hello')  -- wifi连接 wifi.setmode(wifi.STATION) station_cfg={} station_cfg.ssid="changeme" station_cfg.pwd="changeme" station_cfg.auto=true station_cfg.save=true wifi.sta.config(station_cfg) print(wifi.sta.getip()) -- //wifi.sta.disconnect()  -- ap模式,手机连接此wifi,到192.168.4.1配置wifi enduser_setup.start(     function()         print("enduser conn wifi as:" .. wifi.sta.getip())     end,     function(err, str)         print("enduser conn wifi err #" .. err .. ": " .. str)     end )  -- //print(uart.setup(0, 9600, 8, 0, 1, 1 ))  -- gpio0控制继电器,对应nodemc的pin 3,gpio2控制板载led,对应4 pin1 = 3 pin2 = 4 gpio.mode(pin1,gpio.OUTPUT) gpio.mode(pin2,gpio.OUTPUT) gpio.write(pin1,gpio.HIGH) gpio.write(pin2,gpio.HIGH)  myurl = "https://test.xxxxxxx.com/test/led.php"  mytimer = tmr.create() mytimer:alarm(5000, tmr.ALARM_AUTO, function()     ip = wifi.sta.getip()     if (ip == nil) then         print('wifi not connect...')         return     else         print(wifi.sta.getip())     end      print('http req led.php...')     http.get(myurl, nil, function(code, data)         if (code < 0) then             print("HTTP request failed...")         else             print("HTTP request succ...", code, data)             if (data == "00") then                 gpio.write(pin1,gpio.LOW)                 gpio.write(pin2,gpio.LOW)             elseif (data =="01") then                 gpio.write(pin1,gpio.LOW)                 gpio.write(pin2,gpio.HIGH)             elseif (data =="10") then                 gpio.write(pin1,gpio.HIGH)                 gpio.write(pin2,gpio.LOW)             elseif (data =="11") then                 gpio.write(pin1,gpio.HIGH)                 gpio.write(pin2,gpio.HIGH)             else                 print('led nop...')             end         end     end)     collectgarbage() end)  -- //mytimer.stop() 
<?php     # led.php  function getRedis($host = '127.0.0.1', $port = 6379, $db = 0) {   $redis = new Redis();   $redis->connect($host, $port);   $redis->select($db);   return $redis;  }    function getLed() {   $redis = getRedis('127.0.0.1', 6379, 0);   $led = $redis->get("led");   $redis->close();   return $led;  }    function setLed($led) {   $redis = getRedis('127.0.0.1', 6379, 0);   $redis->set("led", $led);   $redis->close();  }    $m = $_SERVER['REQUEST_METHOD'];  $t = isset($_REQUEST['t']) ? $_REQUEST['t'] : '';  if ($m == 'GET') {   if (empty($t)) {    $led = getLed();    if (!empty($led)) {     echo $led;    } else {     echo 'none';    }    exit;   }  }      if ($m == 'GET' && $t == 'ui') {     } else {   echo 'nop';   exit;  }    $led = isset($_REQUEST['led']) ? $_REQUEST['led'] : '';  $sw = isset($_REQUEST['sw']) ? $_REQUEST['led'] : '';  $sw = isset($_REQUEST['sw']) ? $_REQUEST['sw'] : '';  $msg = '';  if (!empty($led)) {   setLed($led);   $msg = 'set led = ' . $led . "\n";  } else if (!empty($sw)) {   if ($sw === 'on') {    setLed('01');    $msg = "开关打开\n";   } else {    setLed('11');    $msg = "开关关闭\n";   }  } else {   $led = getLed();   $msg = 'now led = ' . $led . "\n";  }    ?>   <!DOCTYPE html> <html lang="zh-cmn"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> <title>LED</title> <style>   </style> </head> <body> <pre>  <?php echo $msg; ?php echo $msg; ?> </pre>  <hr>  <a href='?t=ui&led=00'>[set 00]</a>  <a href='?t=ui&led=01'>[set 01]</a>  <a href='?t=ui&led=10'>[set 10]</a>  &l;a href='?t=ui&led=11'>[set 11]</a>
	<a href='?t=ui'>[get now]</a>
	<br>
	开关:<a href='?t=ui&sw=on'>[开]</a> <a href='?t=ui&sw=off'>[关]</a>
	
</body>
</html>

(4)在小度技能平台,创建我的开关,配置服务里配置各url地址和值。

授权信息配置部分配置oauth2.0权限,设备云信息配置开关接口,如:

授权地址:https://test.xxxxxx.com/oauth2.0/authorize.php

Client_Id:191223d5e1bcb5f9e51bca66113ce3a1

Token地址:https://test.xxxxxx.com/oauth2.0/token.php

ClientSecret:191223d5e1bcb5f9e51bca66113ce3a2

WebService:https://test.xxxxxx.com/iot/dueros.php

这里oauth2.0只简单实现功能,dueros.php按协议实现简单功能。

-- authorize.php -----------------------------------
<?php

$response_type = vvget("response_type", "");
$client_id = vvget("client_id", "");
$redirect_uri = vvget("redirect_uri", "");
$scope = vvget("scope", "default");
$state = vvget("state", "");

// 校验各个值非空
if (empty($response_type)) {
	$message = "'值不能为空'";
	Header("content-type: text/plain;charset=UTF-8");
	echo $message;
	return;
}

// 检测client_id被注册,检测与redirect_uri匹配,检测没禁用
if ('191223d5e1bcb5f9e51bca66113ce3a1' !== $client_id) {
	$message = "client_id不存在";
	Header("HTTP/1.1 400 Bad Request");
	Header("content-type: text/plain;charset=UTF-8");
	echo $message;
	return;
}
if ('https://xiaodu.baidu.com/saiya/auth/xxxxxxxxxxxxxxxxxx' !== $redirect_uri) {
	$message = "'redirect_uri不正确'";
	Header("HTTP/1.1 400 Bad Request");
	Header("content-type: text/plain;charset=UTF-8");
	echo $message;
	return;
}

// 检测当前是否有用户登录,没登录去登录


// 检测登录的用户是否授权,授权是否一致


// 也授权了,那跳转回去
if ($response_type === 'code') {
	$code = '191223d5e1bcb5f9e51bca66113ce3a3';
	$url = $redirect_uri;
	if (strpos($redirect_uri, '?') > 0) {
		$url .= "&";
	} else {
		$url .= "?";
	}
	$url .= "code=" . $code . "&state=" . $state;
	header('Location: ' . $url);
	return;
} else if ($response_type === 'token') {
	$access_token = '191223d5e1bcb5f9e51bca66113ce3a4';
	$expires_in = 60 * 60 * 2;
	$url = $redirect_uri;
	$url .= "#access_token=" . $access_token . "&state=" . $state . "&token_type=access_token&expires_in=" . $expires_in;
	header('Location: ' . $url);
	return;
} else {
	Header("HTTP/1.1 400 Bad Request");
	Header("content-type: text/plain;charset=UTF-8");
	$message = "不支持的response_type";
	echo $message;
	return;
}


-- token.php -----------------------------------

<?php

$client_id = vvget("client_id", "");
$client_secret = vvget("client_secret", "");
$redirect_uri = vvget("redirect_uri", "");
$grant_type = vvget("grant_type", "");
$code = vvget("code", "");
$refresh_token = vvget("refresh_token", "");

// 校验各个值非空

// 检测client_id被注册,检测没禁用,校验client_secret正确
if ('191223d5e1bcb5f9e51bca66113ce3a1' !== $client_id) {
	Header("HTTP/1.1 400 Bad Request");
	Header("content-type: text/plain;charset=UTF-8");
	$message = "'client_id不存在'";
	echo $message;
	return;
}
if ('191223d5e1bcb5f9e51bca66113ce3a2' !== $client_secret) {
	Header("HTTP/1.1 400 Bad Request");
	Header("content-type: text/plain;charset=UTF-8");
	$message = "'client_secret不正确'";
	echo $message;
	return;
}


// 检测与redirect_uri匹配
if ("authorization_code" === $grant_type) {
	// 校验redirect_uri一致
	if ('https://xiaodu.baidu.com/saiya/auth/xxxxxxxxxxxxxx' !== $redirect_uri) {
		Header("HTTP/1.1 400 Bad Request");
		Header("content-type: text/plain;charset=UTF-8");
		$message = "'redirect_uri不正确'";
		return;
	}
}

// token
if ("authorization_code" === $grant_type) {
	Header("HTTP/1.1 200 OK");
	Header("content-type: application/json;charset=UTF-8");
	echo json_encode(
		array(
			"access_token" => '191223d5e1bcb5f9e51bca66113ce3a4',
			"token_type" => 'access_token',
			"expires_in" => 60 * 60 *2,
			"refresh_token" => '191223d5e1bcb5f9e51bca66113ce3a5',
			"scpoe" => ''
		)
	);
	return;
} else if ("refresh_token" === $grant_type) {
	Header("HTTP/1.1 200 OK");
	Header("content-type: application/json;charset=UTF-8");
	echo json_encode(
		array(
			"access_token" => '191223d5e1bcb5f9e51bca66113ce3a4',
			"token_type" => 'access_token',
			"expires_in" => 60 * 60 *2,
			"refresh_token" => '191223d5e1bcb5f9e51bca66113ce3a5',
			"scpoe" => ''
		)
	);
	return;
} else {
	Header("HTTP/1.1 400 Bad Request");
	Header("content-type: text/plain;charset=UTF-8");
	echo '不支持的grant_type';
	return;
}


-- dueros.php ----------------------------------
<?php
	
	$body = @file_get_contents('php://input');
	
	$debug = isset($_GET["debug"]) ? $_GET["debug"] : 'false';
	$json = json_decode($body, true);
	
	$header = $json['header'];
	$payload = $json['payload'];
	
	$namespace = $header['namespace'];
	$name = $header['name'];
	$messageId = $header['messageId'];
	
	$accessToken = $payload['accessToken'];
	if ($accessToken !== '191223d5e1bcb5f9e51bca66113ce3a4') {
		//Header("content-type: application/json;charset=UTF-8");
		Header("content-type: text/plain;charset=UTF-8");
		echo "错误的accessToken";
		return;
	}
	// --------------------
	function getRedis($host = '127.0.0.1', $port = 6379, $db = 0) {
		$redis = new Redis();
		$redis->connect($host, $port);
		$redis->select($db);
		return $redis;
	}
	
	function getLed() {
		$redis = getRedis('127.0.0.1', 6379, 0);
		$led = $redis->get("led");
		$redis->close();
		return $led;
	}
	
	function setLed($led) {
		$redis = getRedis('127.0.0.1', 6379, 0);
		$redis->set("led", $led);
		$redis->close();
	}
	
	// https://www.xxxxxxxxxxxx.com/iot/dueros.php
	// --------------------
	$path = $namespace . '/' . $name;
	if ('DuerOS.ConnectedHome.Discovery/DiscoverAppliancesRequest' === $path) {
		//echo "discovery";
		$ret = array(
			'header' => array(
				"namespace" => "DuerOS.ConnectedHome.Discovery",
				"name" => "DiscoverAppliancesResponse",
				"messageId" => $messageId,
				"payloadVersion" => "1"
			),
			'payload' => array(
				"discoveredAppliances" => array(
					array(
						"applianceTypes" => "SWITCH",
						"applianceId" => "20191222220215",
						"friendlyDescription" => "我的开关开关。。",
						"friendlyName" => "我的开关",
						"isReachable" => true,
						"manufacturerName" => "zhanglc",
						"modelName" => "my sw",
						"version" => "0.0。1",
						"actions" => array(
							"turnOn", "turnOff"
						),
						"attributes" => array(
							"name" => "powerState",
							"value" => "ON",
							"scale" => "",
							"timestampOfSample" => time(),
							"uncertaintyInMilliseconds" => 0
						)
					)
				)
			)
		);
		
		echo json_encode($ret);
		return;
	}

	if ('DuerOS.ConnectedHome.Control/TurnOnRequest' === $path) {
		setLed('00');
		//echo "turn on";
		$ret = array(
			'header' => array(
				"namespace" => "DuerOS.ConnectedHome.Control",
				"name" => "TurnOnConfirmation",
				"messageId" => $messageId,
				"payloadVersion" => "1"
			),
			'payload' => array(
				"attributes" => array(
					"name" => "powerState",
					"value" => "ON",
					"scale" => "",
					"timestampOfSample" => time(),
					"uncertaintyInMilliseconds" => 0
				)
			)
		);
		
		echo json_encode($ret);
		return;
	}

	if ('DuerOS.ConnectedHome.Control/TurnOffRequest' === $path) {
		setLed('10');
		//echo "turn off";
		$ret = array(
			'header' => array(
				"namespace" => "DuerOS.ConnectedHome.Control",
				"name" => "TurnOffConfirmation",
				"messageId" => $messageId,
				"payloadVersion" => "1"
			),
			'payload' => array(
				"attributes" => array(
					"name" => "powerState",
					"value" => "ON",
					"scale" => "",
					"timestampOfSample" => time(),
					"uncertaintyInMilliseconds" => 0
				)
			)
		);
		
		echo json_encode($ret);
		return;
	}

	//Header("content-type: application/json;charset=UTF-8");
	Header("content-type: text/plain;charset=UTF-8");
	echo "不支持的请求: ".$path;
	return;

(5)真机测试,开启技能调试模式,手机小度app上看设备,应该是会显示出此设备(原先直接向小度发语言提示没找到设备),向小度发语言“打开开关”,开关被打开。

 

标签: 8pin插板连接器插板式pin连接器

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台