1 串口服务器
ESP8266工作在TCP服务器模式时,可以借助IOTGATEWAY很轻松地将传统设备接入物联网,实现多设备实时监控
2 参数配置
串口服务器就像是一个路由器一样,用户可以通过浏览器访问后台来配置模块的相关参数
2.1串口参数
2.2 WiFi参数
esp8266的wifi可以工作在三种模式下,AP模式、STA模式、AP+STA模式,作为串口服务器时,一般工作在AP+STA模式下
2.2.1 AP模式
在该模式下可以产生一个WIFI,供外部设备连接
2.2.2 STA模式
在该模式下会连接到路由器的WIFI上,需要填写路由器WIFI的相关信息
2.3 TCP通信参数
无论是服务器还是客户端,原理都是将串口收到的数据透传输出,将TCP收到的数据通过串口再发送出去
2.4 固件升级
3 实现方法
esp8266通过响应来网页端的get、post请求来实现参数的存储,数据的读取,官方有现成的http服务器的例子可以参考一下
3.1 网页端代码
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link href="favicon.png" rel="shortcut icon">
<title>
串口服务器
title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=0">
<script stype="text/javascript">
var work_mode_ap_sta = '0';
var work_mode_sta = '1';
var mode_current = '0';
var mode_last = '0';
var port_remote = '8081';
var port_tcp = '8080';
var xmlobj = null;
var xmlHttp = null;
function CreateXMLHttpRequest() {
xmlobj = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHttp")
}
function CreateXMLHttpRequest_ap() {
if (null == xmlHttp) xmlHttp = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHttp")
}
function processRequest() {
if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {
var res = xmlHttp.responseText;
if (res.length > 0) {
document.getElementById("ap_list").options.length = 0;
var a_list = new Array();
a_list = res.split("^$&");
for (i = 0; i < a_list.length; i++) {
if (a_list[i].length > 0) {
var t = a_list[i];
document.getElementById("ap_list").options.add(new Option(t, t))
}
}
}
}
}
function doPostResp() {
if (xmlobj.readyState == 4 && xmlobj.status == 200) {
var res = xmlobj.responseText;
if (res.length > 0) {
alert(res)
}
}
}
function submit_post(parm) {
console.log('submit_post' + parm);
CreateXMLHttpRequest();
xmlobj.open("POST", "/setpara", true);
xmlobj.onreadystatechange = doPostResp;
xmlobj.send(parm)
}
function submit_postDefault(parm) {
CreateXMLHttpRequest();
xmlobj.open("POST", "/default", true);
xmlobj.onreadystatechange = doPostResp;
xmlobj.send(parm)
}
function submit_post_mode(parm) {
CreateXMLHttpRequest();
xmlobj.open("POST", "/setmode", true);
xmlobj.onreadystatechange = doPostResp;
xmlobj.send(parm)
}
function getVByName(name) {
var tt = document.getElementsByName(name);
for (var iIndex = 0; iIndex < tt.length; iIndex++) {
if (tt[iIndex].checked) {
return tt[iIndex].value
}
}
}
function isValidIP(ip) {
var reg = /^(\\d{1,2}|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d{1,2}|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d{1,2}|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d{1,2}|1\\d\\d|2[0-4]\\d|25[0-5])$/;
return reg.test(ip)
}
function isValidMask(mask) {
var reg = /^(254|252|248|240|224|192|128|0)\\.0\\.0\\.0|255\\.(254|252|248|240|224|192|128|0)\\.0\\.0|255\\.255\\.(254|252|248|240|224|192|128|0)\\.0|255\\.255\\.255\\.(254|252|248|240|224|192|128|0)$/;
return reg.test(mask)
}
function IsPortValid(str) {
var RegUrl = new RegExp();
RegUrl.compile("^[1-9]$|(^[1-9][0-9]$)|(^[1-9][0-9][0-9]$)|(^[1-9][0-9][0-9][0-9]$)|(^[1-6][0-5][0-5][0-3][0-5]$)");
if (!RegUrl.test(str)) {
return false
}
return true
}
function IsTimeoutValid(time_out) {
if ((time_out < 1) || (time_out > 7200)) {
return false
}
return true
}
function submitForm() {
var work_mode = document.getElementById('work_mode').value;
var baud = document.getElementById('baud').value;
var bits = document.getElementById('bits').value;
var parity = document.getElementById('parity').value;
var stop = document.getElementById('stop').value;
var ap_enc_mode = document.getElementById('ap_enc_mode').value;
var ap_hide = getVByName('ap_hide');
var ap_name = document.getElementById('ap_name').value;
var ap_paswd = document.getElementById('ap_paswd').value;
var ip_ap = document.getElementById('ip_ap').value;
var gateway_ap = document.getElementById('gateway_ap').value;
var sta_name = document.getElementById('sta_name').value;
var sta_paswd = document.getElementById('sta_paswd').value;
var sta_dhcp_en = getVByName('sta_dhcp_en');
var ip_sta = document.getElementById('ip_sta').value;
var gateway_sta = document.getElementById('gateway_sta').value;
var tcp_type = getVByName('tcp_type');
var ip_tcp = document.getElementById('ip_tcp').value;
var tcp_port = document.getElementById('tcp_port').value;
var time_out = document.getElementById('time_out').value;
if ((IsPortValid(tcp_port) == false) || (80 == tcp_port) || (0 == tcp_port) || (0xFFFF < tcp_port)) {
alert('port is invalid');
return
}
if (false == isValidIP(ip_tcp)) {
alert('tcp ip error');
return
}
if (IsTimeoutValid(time_out) == false) {
alert('time_out is invalid');
return
}
if (false == isValidIP(ip_ap)) {
alert('sta ip error');
return
}
if (false == isValidIP(gateway_ap)) {
alert('gateway error');
return
}
if (0 == sta_dhcp_en) {
if (false == isValidIP(ip_sta)) {
alert('sta ip error');
return
}
if (false == isValidIP(gateway_sta)) {
alert('gateway error');
return
}
}
var post_work_mode = 'work_mode=' + work_mode + '&';
var uart = 'baud=' + baud + '&bits=' + bits + '&parity=' + parity + '&stop=' + stop + '&';
var post_ap_wifi = 'ap_enc_mode=' + ap_enc_mode + '&ap_hide=' + ap_hide + '&ap_name=' + encodeURI(ap_name) + '&ap_paswd=' + ap_paswd + '&';
var post_ap_ip = 'ip_ap=' + ip_ap + '&gateway_ap=' + gateway_ap + '&';
var post_sta_wifi = 'sta_name=' + sta_name + "" + '&sta_paswd=' + sta_paswd + '&';
var post_sta_ip = 'sta_dhcp_en=' + sta_dhcp_en + '&';
if (0 == sta_dhcp_en) {
post_sta_ip += 'ip_sta=' + ip_sta + '&gateway_sta=' + gateway_sta + '&'
}
var post_tcp = 'tcp_type=' + tcp_type + '&ip_tcp=' + ip_tcp + '&time_out=' + time_out + '&';
if (2 == tcp_type) {
post_tcp = post_tcp + 'remote_port=' + tcp_port + '&'
} else {
post_tcp = post_tcp + 'tcp_port=' + tcp_port + '&'
}
var obj = document.getElementById('work_mode');
var mode = obj.value;
console.log('mode=' + mode);
if (mode == work_mode_sta) {
if (sta_name == "" || sta_name == null) {
alert('enter ssid');
return
}
if (sta_paswd == "" || sta_paswd == null) {
alert('enter password');
return
}
if (sta_name.length > 16) {
alert('ssid len error');
return
}
if ((sta_paswd.length > 16) || (sta_paswd.length < 8)) {
alert('password len error');
return
}
} else if (mode == work_mode_ap_sta) {
if (ap_name == "" || ap_name == null) {
alert('enter apssid');
return
}
if (ap_paswd == "" || ap_paswd == null) {
alert('enter appassword');
return
}
if (sta_name == "" || sta_name == null) {
alert('enter stassid');
return
}
if (sta_paswd == "" || sta_paswd == null) {
alert('enter stapassword');
return
}
if (sta_name.length > 16) {
alert('stassid len error');
return
}
if ((sta_paswd.length > 16) || (sta_paswd.length < 8)) {
alert('stapassword len error');
return
}
if (ap_name.length > 16) {
alert('apssid len error');
return
}
if ((ap_paswd.length > 16) || (ap_paswd.length < 8)) {
alert('appassword len error');
return
}
}
submit_post(post_work_mode + uart + post_ap_wifi + post_ap_ip + post_sta_wifi + post_sta_ip + post_tcp)
}
function change_port() {
var tcp_type = getVByName('tcp_type');
var obj = document.getElementById("tcp_port");
if (2 == tcp_type) {
port_remote = obj.value;
console.log("port_remote=" + port_remote)
} else {
port_tcp = obj.value;
console.log("port_tcp=" + port_tcp)
}
}
function sockttypeClicked(_mode) {
if (_mode == 2) {
var obj = document.getElementById("label_port");
obj.innerText = "远程端口:";
obj = document.getElementById("ip_tcp");
obj.disabled = false;
document.getElementById("tcp_port").value = port_remote;
console.log("(_mode == 2) ");
console.log("obj.val=" + port_remote)
} else {
var obj = document.getElementById("label_port");
obj.innerText = "本地端口:";
obj = document.getElementById("ip_tcp");
obj.disabled = true;
document.getElementById("tcp_port").value = port_tcp;
console.log("_mode=" + _mode);
console.log("obj.val=" + port_tcp)
}
}
function setParaRadioChecked(name, id) {
var obj = document.getElementsByName(name);
for (var i = 0; i <= obj.length; i++) {
if (obj[i].id == id) {
obj[i].checked = 1;
if (name == "tcp_type") sockttypeClicked(id);
break
}
}
}
function setParaSelectOption(id, value) {
var obj = document.getElementById(id);
for (var i = 0; i <= obj.length; i++) {
if (obj.options[i].value == value) {
obj.options[i].selected = true;
if (id == 'ap_enc_mode' && value == 0) document.getElementById('ap_paswd').disabled = true;
break
}
}
}
function ap_enc_mode_check() {
var obj = document.getElementById('ap_enc_mode');
if (obj.value == 0) document.getElementById('ap_paswd').disabled = true;
else document.getElementById('ap_paswd').disabled = false
}
function work_mode_check() {
var obj = document.getElementById('work_mode');
mode_current = obj.value;
console.log('mode_current=' + mode_current);
if (mode_current == work_mode_sta) {
console.log('hide ap show sta');
var nodes = document.getElementById("ap").getElementsByTagName('*');
for (var i = 0; i < nodes.length; i++) {
nodes[i].disabled = true
}
} else if (mode_current == work_mode_ap_sta) {
console.log('show ap and sta');
var nodes = document.getElementById("ap").getElementsByTagName('*');
for (var i = 0; i < nodes.length; i++) {
nodes[i].disabled = false
}
}
if (mode_last != mode_current) {
mode_last = mode_current
}
}
function set_ip_en(mode) {
console.log("set_ip_en mode=" + mode);
if (mode == '1') {
console.log("disable ip info");
var nodes = document.getElementById("ip_en").getElementsByTagName('*');
for (var i = 0; i < nodes.length; i++) {
nodes[i].disabled = true
}
} else {
console.log("enable ip info");
var nodes = document.getElementById("ip_en").getElementsByTagName('*');
for (var i = 0; i < nodes.length; i++) {
nodes[i].disabled = false
}
}
}
script>
<style>
body {
background:#e2e2e2;
max-width: 642px;
font-family: 'Roboto', sans-serif;
margin: auto auto;
box-shadow: 0 0 10px 10px rgba(0,0,0,.5);
border-radius: 10px;
}
.mytabs {
display: flex;
flex-wrap: wrap;
margin: 1em auto;
line-height: 2em;
}
.mytabs input[type="radio"] {
display: none;
}
.mytabs label {
padding: 25px;
font-weight: bold;
border-radius: 10px 10px 0 0 ;
}
.mytabs .tab {
width: 100%;
background: white;
order: 1;
display: none;
border-radius: 0 0 10px 10px;
min-height:270px;
}
.mytabs input[type='radio']:checked + label + .tab {
display: block;
}
.mytabs input[type="radio"]:checked + label {
background: white;
}
.control_group
{
width:100%;
}
.control_group label
{
padding:unset;
font-size: 14px;
width:40%;
display:inline-block;
text-align:right;
padding-right:5%;
background-color:transparent;
}
.control_group .title
{
margin-top:5%;
}
.control_group input[type="radio"]{
display:unset;
}
button {
border:none;
border-radius:4px;
line-height:2em;
color:#fff;
margin-right:1em
}
button.green {
background:#45c01a
}
button.button.blue {
background:#466
}
.gray {
background:gray
}
style>
head>
<body>
<form name="hotform" id="hotform" action='' method="post">
<div class="mytabs">
<input type="radio" id="tab_basic_config" name="mytabs" checked="checked">
<label for="tab_basic_config">基础参数label>
<div class="tab">
<div id='uart'>
<div class="control_group">
<label class="title">
工作模式:
label>
<select class="select" id="work_mode" name="mode" onchange="work_mode_check()">
<option value="0">
AP+STA
option>
<option value="1">
STA
option>
select>
<label>
波特率:
label>
<select id="baud" name="baud">
<option value="9600">
9600
option>
<option value="19200">
19200
option>
<option value="38400">
38400
option>
<option value="57600">
57600
option>
<option value="74880">
74880
option>
<option value="115200">
115200
option>
select>
div>
<div class="control_group">
<label>
数据位:
label>
<select id="bits" name="bits">
<option value=2>
7
option>
<option value=3>
8
option>
select>
div>
<div class="control_group">
<label>
奇偶校验:
label>
<select id="parity" name="parity">
<option value=0>
NONE
option>
<option value=3>
ODD
option>
<option value=2>
EVEN
option>
select>
div>
<div class="control_group">
<label>
停止位:
label>
<select id="stop" name="stop">
<option value=1>
1
option>
<option value=2>
1.5
option>
<option value=3>
2
option>
select>
div>
div>
div>
<input type="radio" id="tab_ap_config" name="mytabs">
<label for="tab_ap_config">热点模式label>
<div class="tab">
<div id='ap'>
<div class="control_group">
<label class="title">
热点名:
label>
<input name="ap_name" id="ap_name" type="text" maxlength=32>
div>
<div class="control_group">
<label>
密码:
label>
<input Name="ap_paswd" id="ap_paswd" type="text" minlength=8 maxlength=16>
div>
<div class="control_group">
<label>
加密方式:
label>
<select id="ap_enc_mode" name="ap_enc_mode" onchange="ap_enc_mode_check()">
<option value=0>
OPEN
option>
<option value=2>
WPA_PSK
option>
<option value=3>
WPA_PSK2
option>
<option value=4>
WPA_WPA2_PSK
option>
select>
div>
<div class="control_group" id='ap_hide'>
<label>
隐藏热点:
label>
<input name="ap_hide" id="1" value="1" type="radio">
是
<input name="ap_hide" id="0" value="0" type="radio">
否
div>
<div>
<div class="control_group">
<label>
IP:
label>
<input name="ip_ap" id="ip_ap" type="text" maxlength=15>
div>
<div class="control_group">
<label>
网关:
label>
<input name="gateway_ap" id="gateway_ap" type="text" maxlength=15>
div>
div>
div>
div>
<input type="radio" id="tab_sta_config" name="mytabs">
<label for="tab_sta_config">客户端模式label>
<div class="tab">
<div id='station'>
<div class="control_group">
<label class="title">
WIFI名:
label>
<input name="sta_name" id="sta_name" type="text" maxlength=32>
div>
<div class="control_group">
<label>
连接状态:
label>
<input name="sta_status" id="sta_status" type="text" maxlength=32>
div>
<div class="control_group">
<label>
WIFI列表:
label>
<select name="ap_list" id="ap_list" maxlength=20 onchange="document.getElementById('sta_name').value=this.value;">
select>
<button class="green" style="width: 48px;" onclick="get_ap_list();return false;">
刷新
button>
div>
<div class="control_group">
<label>
WIFI密码:
label>
<input name="sta_paswd" id="sta_paswd" type="text" maxlength=16>
div>
<div class="control_group">
<label>
动态获取IP:
label>
<input name="sta_dhcp_en" id="1" value="1" type="radio" onclick="set_ip_en('1')">
是
<input name="sta_dhcp_en" id="0" value="0" type="radio" onclick="set_ip_en('0')">
否
div>
<div id='ip_en'>
<div class="control_group">
<label>
IP:
label>
<input name="ip_sta" id="ip_sta" type="text" maxlength=15>
div>
<div class="control_group">
<label>
网关:
label>
<input name="gateway_sta" id="gateway_sta" type="text" maxlength=15>
div>
div>
div>
div>
<input type="radio" id="tab_net_config" name="mytabs">
<label for="tab_net_config">TCP通信label>
<div class="tab">
<div id='network'>
<div class="control_group">
<label class="title">
Socket类型:
label>
<input name="tcp_type" id="1" value="1" checked="checked" type="radio"
onclick="sockttypeClicked(1)">
服务器
<input name="tcp_type" id="2" value="2" type="radio" onclick="sockttypeClicked(2)">
客户端
div>
<div class="control_group">
<label>
远端IP:
label>
<input name="ip_tcp" id="ip_tcp" type="text" maxlength=64>
div>
<div class="control_group">
<label id="label_port">
本地端口:
label>
<input name="tcp_port" id="tcp_port" type="text" maxlength=5 onchange="change_port()"
onKeyUp="value=value.replace(/\\D/g,'')" onafterpaste="value=value.replace(/\\D/g,'')">
div>
<div class="control_group">
<label>
超时时间(s):
label>
<input name="time_out" id="time_out" type="text" maxlength=4>
div>
div>
div>
<input type="radio" id="tab_ota" name="mytabs">
<label for="tab_ota">固件升级label>
<div class="tab">
<div class='ota'>
<label class="title">
label>
<div class="control_group">
<input id="file-name" readonly type="text" style="width: 80px; margin-left: 200px; "/>
<input type="file" onchange="fileChange(this)" id="file-info" name="file">
<button class="green" style="margin-left: -85px; " onclick="postOta();return false;">updatebutton>
div>
<div class="control_group">
<label style="margin:0 0;">
version:
label>
<input id="ver" readonly type="text" />
div>
div>
div>
div>
<div style="width:100%; text-align: center; line-height: 2em;">
<div class="button_group">
<button class="green" style="width: 120px; margin-bottom: 10px;" onclick="submitForm();return false;">
保存参数
button>
div>
<div class="button_group">
<button class="green" style="width: 120px; margin-bottom: 10px;" onclick="set_confirm();return false;">
恢复默认参数
button>
div>
<img src="logo.png">img>
div>
form>
<script stype="text/javascript">
var timeoutNum = 0;
var Ajax = {
get: function(e, d) {
var f;
if (window.XMLHttpRequest) {
f = new XMLHttpRequest()
} else {
if (window.ActiveObject) {
f = new ActiveXobject("Microsoft.XMLHTTP")
}
}
f.open("GET", e, true);
f.onreadystatechange = function() {
if (f.readyState == 4) {
console.log(f.status);
if (f.status == 200 || f.status == 304) {
d.call(this, f.responseText)
} else {}
}
};
f.send()
},
post: function(e, g, h) {
showLoading();
var f;
if (window.XMLHttpRequest) {
f = new XMLHttpRequest()
} else {
if (window.ActiveObject) {
f = new ActiveXobject("Microsoft.XMLHTTP")
}
}
f.open("POST", e, true);
f.setRequestHeader("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
f.onreadystatechange = function() {
if (f.readyState == 4) {
if (f.status == 200 || f.status == 304) {
h.call(this, f.responseText)
} else {}
}
};
f.send(g)
},
postFile: function(e, g, h) {
var f;
if (window.XMLHttpRequest) {
f = new XMLHttpRequest()
} else {
if (window.ActiveObject) {
f = new ActiveXobject("Microsoft.XMLHTTP")
}
}
f.open("POST", e, true);
f.setRequestHeader("Content-Type", "application/octet-stream;charset=UTF-8");
f.onreadystatechange = function() {
if (f.readyState == 4) {
if (f.status == 200 || f.status == 304) {
h.call(this, f.responseText)
} else {}
}
};
f.send(g)
}
};
function postOta() {
timeoutNum = 0;
var b = document.getElementById("file-info").files;
if (b && b.length > 0) {
b = b[0];
Ajax.postFile("/setotadata", b,
function(a) {
a = JSON.parse(a);
console.log(a);
if (a.state == 0) {
console.log("otaSuc")
} else {
console.log("otaFailed")
}
})
} else {}
}
function fileChange(e) {
var c = e.files;
if (c && c.length > 0) {
c = c[0];
var d = c.name.toLowerCase();
if (d.indexOf(".bin") != (d.length - 4)) {
document.getElementById("file-info").value = "";
return
}
if (c.size >= 2097152) {
document.getElementById("file-info").value = "";
return
}
document.getElementById("file-name").value = d;
console.log(document.getElementById("file-info").files)
}
}
function check_valid_bits(val) {
if (!/^([01])$/.test(val)) {
alert("手机号码有误,请重填");
return false
}
return true
}
function init_parameters() {
Ajax.get("/getpara",
function(c) {
c = JSON.parse(c);
try {
console.log('workmode=' + c.work_mode);
console.log('ap_enc_mode=' + c.ap_enc_mode);
console.log('ap_hide=' + c.ap_hide);
console.log('sta_dhcp_en=' + c.sta_dhcp_en);
c.work_mode = c.work_mode + '';
c.ap_enc_mode = c.ap_enc_mode + '';
c.ap_hide = c.ap_hide + '';
c.sta_dhcp_en = c.sta_dhcp_en + '';
console.log('after');
console.log('workmode=' + c.work_mode);
console.log('ap_enc_mode=' + c.ap_enc_mode);
console.log('ap_hide=' + c.ap_hide);
console.log('sta_dhcp_en=' + c.sta_dhcp_en);
console.log("res=" + check_valid_bits(c.work_mode));
if ((c.work_mode != work_mode_ap_sta) && (c.work_mode != work_mode_sta)) {
c.work_mode = work_mode_ap_sta
}
document.getElementById("work_mode").value = c.work_mode;
setParaRadioChecked('ap_hide', c.ap_hide);
setParaSelectOption('ap_enc_mode', c.ap_enc_mode);
document.getElementById("ap_name").value = c.ap_name;
document.getElementById("ap_paswd").value = c.ap_paswd;
document.getElementById("sta_name").value = c.sta_name;
document.getElementById("sta_paswd").value = c.sta_paswd;
setParaRadioChecked('sta_dhcp_en', c.sta_dhcp_en);
set_ip_en(c.sta_dhcp_en);
if (0 == c.sta_dhcp_en) {
document.getElementById("ip_sta").value = c.ip_sta;
document.getElementById("gateway_sta").value = c.gateway_sta;
} else {
get_sta_ip_info();
}
get_sta_status();
document.getElementById("sta_status").disabled = true;
var obj = document.getElementById('work_mode');
var mode = obj.value;
console.log('mode=' + mode);
work_mode_check();
get_ap_list();
document.getElementById("ip_ap").value = c.ip_ap;
document.getElementById("gateway_ap").value = c.gateway_ap;
setParaSelectOption('baud', c.baud);
setParaSelectOption('bits', c.bits);
setParaSelectOption('parity', c.parity);
setParaSelectOption('stop', c.stop);
setParaRadioChecked('tcp_type', c.tcp_type);
document.getElementById('ip_tcp').value = c.ip_tcp;
document.getElementById('tcp_port').value = c.tcp_port;
port_remote = c.remote_port;
port_tcp = c.tcp_port;
console.log("c.tcp_type=" + c.tcp_type);
if (2 == c.tcp_type) {
document.getElementById('tcp_port').value = c.remote_port
}
document.getElementById('time_out').value = c.time_out
} catch(d) {}
})
}
function get_ap_list() {
Ajax.get("/getaplist",
function(c) {
if (!isEmpty(c)) {
console.log(c);
var res = c;
if (res.length > 0) {
document.getElementById("ap_list").options.length = 0;
var a_list = new Array();
a_list = res.split("^$&");
for (i = 0; i < a_list.length; i++) {
if (a_list[i].length > 0) {
var t = a_list[i];
document.getElementById("ap_list").options.add(new Option(t, t))
}
}
}
}
})
}
function isEmpty(str) {
if (typeof str == null || str == "" || str == "undefined") {
return true
} else {
return false
}
}
function get_sta_ip_info() {
Ajax.get("/getip",
function(c) {
if (!isEmpty(c)) {
c = JSON.parse(c);
console.log(c);
try {
if (c.ip_sta) {
document.getElementById("ip_sta").value = c.ip_sta;
}
if (c.gateway_sta) {
document.getElementById("gateway_sta").value = c.gateway_sta;
}
} catch(d) {}
}
})
}
function get_sta_status() {
Ajax.get("/getstatus",
function(c) {
if (!isEmpty(c)) {
c = JSON.parse(c);
console.log(c);
try {
if (c.status) {
document.getElementById("sta_status").value = c.status
}
} catch(d) {}
}
})
}
function get_ver() {
Ajax.get("/getotainfo",
function(c) {
if (!isEmpty(c))
{
console.log(c);
var res = JSON.parse(c);
try {
if (res.fw_version) {
document.getElementById("ver").value = res.fw_version
}
} catch(d) {}
}
})
}
function set_confirm() {
var se = confirm("restore the module?");
if (se == true) {
submit_postDefault();
}
}
init_parameters();
get_ver();
script>
body>
html>
3.2 http服务器端
3.2.1 基本请求
工作在AP模式下,在浏览器打开192.168.4.1后,相当于发起了一个HTTP GET请求,路径是/,那么服务器端会发送一个html的网页内容到浏览器上,图片也可以通过这种方式发出去
httpd_uri_t basic_handlers[] =
{
{"/", HTTP_GET, hello_get_handler,NULL},
{"/logo.png", HTTP_GET, logo_png_get_handler,NULL},
{"/favicon.png", HTTP_GET, favicon_png_get_handler,NULL},
{"/getpara", HTTP_GET, config_wifi_get_handler, NULL},
{"/getaplist", HTTP_GET, ap_record_get_handler, NULL},
{"/setpara", HTTP_POST, config_wifi_post_handler, NULL},
{"/default", HTTP_POST, config_wifi_default_handler, NULL},
{"/getip", HTTP_GET, config_ip_get_handler, NULL},
{"/getstatus", HTTP_GET, sta_get_status, NULL},
};
3.2.2 固件升级
esp8266已经有封装好的api,可以很简单的实现OTA功能,只需要简单的配置一下就好了。
-选择分区表为Factory app,two OTA definitions
-请求响应
主要是获取当前版本号,发送APP文件
httpd_uri_t basic_handlers[] =
{
{"/getotainfo", HTTP_GET, ota_info_get_handler, NULL},
{"/setotadata", HTTP_POST, ota_data_post_handler, NULL},
};
处理客户端获取版本信息
static esp_err_t ota_info_get_handler(httpd_req_t *req)
{
int32_t json_len = 0;
char *temp_json_str = ((web_server_context_t*) (req->user_ctx))->scratch;
memset(temp_json_str, '\\0', ESP_AT_WEB_SCRATCH_BUFSIZE * sizeof(char));
httpd_resp_set_type(req, "application/json");
json_len += sprintf(temp_json_str + json_len, "{\\"fw_version\\":\\"%s\\"}", CONFIG_VERSION_AT); // it means http context OK
ESP_LOGD(TAG, "now ota get json str is %s\\n", temp_json_str);
httpd_resp_send(req, temp_json_str, strlen(temp_json_str));
return ESP_OK;
}
接收OTA文件
static esp_err_t ota_data_post_handler(httpd_req_t *req)
{
char *buf = ((web_server_context_t*) (req->user_ctx))->scratch;
int total_len = req->content_len;
int remaining_len = req->content_len;
int received_len = 0;
esp_err_t err = ESP_FAIL;
esp_ota_handle_t update_handle = 0;
const esp_partition_t *update_partition = at_web_get_ota_update_partition();
// check post data size
if (update_partition->size < total_len) {
ESP_LOGE(TAG, "ota data too long, partition size is %u, bin size is %d", update_partition->size, total_len);
goto err_handler;
}
ESP_LOGI(TAG, "bin size is %d", total_len);
memset(buf, 0x0, ESP_AT_WEB_SCRATCH_BUFSIZE * sizeof(char));
// Send a message to MCU.
ESP_LOGI(TAG, "%s", s_ota_start_response);
// start ota
err = esp_ota_begin(update_partition, OTA_SIZE_UNKNOWN, &update_handle);
if (err != ESP_OK) {
ESP_LOGE(TAG, "ota begin failed (%s)", esp_err_to_name(err));
goto err_handler;
}
// receive ota data
while (remaining_len > 0) {
received_len = httpd_req_recv(req, buf, MIN(remaining_len, ESP_AT_WEB_SCRATCH_BUFSIZE)); // Receive the file part by part into a buffer
if (received_len <= 0) { // received error
if (received_len == HTTPD_SOCK_ERR_TIMEOUT) {
/* Retry if timeout occurred */
continue;
}
ESP_LOGE(TAG, "Failed to receive post ota data, err = %d", received_len);
esp_ota_end(update_handle);
goto err_handler;
}else { // received successfully
err = esp_ota_write(update_handle, buf, received_len);
if (err != ESP_OK) {
ESP_LOGE(TAG, "ota write failed (%s)", esp_err_to_name(err));
esp_ota_end(update_handle);
goto err_handler;
}
remaining_len -= received_len;
}
}
err = at_web_ota_end(update_handle, update_partition);
if (err != ESP_OK) {
goto err_handler;
}
at_web_response_ok(req);
//esp_at_port_write_data((uint8_t*)s_ota_receive_success_response, strlen(s_ota_receive_success_response));
ESP_LOGI(TAG, "%s", s_ota_receive_success_response);
ESP_LOGI(TAG, "ota end successfully, please restart");
const char *temp_str = CONFIG_MSG_REMIND;
httpd_resp_set_type(req, HTTPD_TYPE_TEXT);
httpd_resp_set_status(req, HTTPD_200);
httpd_resp_send(req, temp_str, strlen(temp_str));
sleep(2);
esp_restart();
return ESP_OK;
err_handler:
at_web_response_error(req, HTTPD_500);
//esp_at_port_write_data((uint8_t*)s_ota_receive_fail_response, strlen(s_ota_receive_fail_response));
ESP_LOGI(TAG, "%s", s_ota_receive_fail_response);
return ESP_FAIL;
}
4 TCP通信
#define CONFIG_S_TIMEOUT_CLIENT 1
#define BACKLOG 5
extern EventGroupHandle_t wifi_event_group_check;
static const char *TAG="APP_TCP";
static void client_remove_all(tcp_multiclient_t *p_clients);
static void client_remove(tcp_multiclient_t *p_clients,int8_t idx);
#define TCP_RX_BUF_SIZE 2048
char tcp_rx_buffer[TCP_RX_BUF_SIZE];
rt_slist_t tcp_clients_list;
tcp_multiclient_t s_clients[BACKLOG];
volatile int socket_tcp_client = 0;
int8_t online_client = 0;
static fd_set fdsr_tcp;
static void client_remove(tcp_multiclient_t *p_clients,int8_t idx)
{
if ((p_clients) && (idx >= 0))
{
ESP_LOGE(TAG,"client[%d] close fd[%d]", idx ,p_clients[idx].fd);
close(p_clients[idx].fd);
FD_CLR(p_clients[idx].fd, &fdsr_tcp);
p_clients[idx].fd = -1;
if (online_client > 0)
{
online_client--;
}
ESP_LOGE(TAG,"online_client[%d]", online_client);
}
}
static void client_remove_all(tcp_multiclient_t *p_clients)
{
uint8_t idx = 0;
if (p_clients)
{
ESP_LOGE(TAG, "remove all client");
for (idx = 0; idx < BACKLOG; idx++)
{
if (p_clients[idx].fd != -1)
{
shutdown(p_clients[idx].fd, 0);
close(p_clients[idx].fd);
p_clients[idx].fd = -1;
s_clients[idx].idx = -1;
p_clients[idx].timeout_tick = 0;
}
}
}
online_client = 0;
}
uint32_t xTaskGetTickCount_new(void)
{
struct timeval tv;
struct timezone tz;
if (gettimeofday(&tv, &tz) == 0)
{
return (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
}
return 0;
}
void showclient(void)
{
int i;
ESP_LOGI(TAG,"client amount: %d", online_client);
for (i = 0; i < BACKLOG; i++)
{
ESP_LOGI(TAG,"[%d]:%d ", i, s_clients[i].fd);
}
ESP_LOGI(TAG,"\\r\\n");
}
void tcp_server_task(void *pvParameters)
{
char addr_str[128];
int addr_family;
int ip_protocol;
struct timeval tv;
int i;
int yes = 1;
int listen_sock = -1;
tcp_server_parameter_t get_config = {CONFIG_PORT_TCP_SERVER,CONFIG_S_TIMEOUT_TCP_SERVER};
struct sockaddr_in client_addr;
int maxsockfd;
if (pvParameters)
{
memcpy(&get_config,pvParameters,sizeof(tcp_server_parameter_t));
}
ESP_LOGI(TAG, "server port[%d] timeout[%d]",get_config.port,get_config.timeout);
ESP_LOGI(TAG, "tcp_server_task waiting for ip...");
xEventGroupWaitBits(wifi_event_group_check, WIFI_CONNECTED_BIT, false, true, portMAX_DELAY);
while (1)
{
struct sockaddr_in destAddr;
destAddr.sin_addr.s_addr = htonl(INADDR_ANY);
destAddr.sin_family = AF_INET;
destAddr.sin_port = htons(get_config.port);
addr_family = AF_INET;
ip_protocol = IPPROTO_IP;
inet_ntoa_r(destAddr.sin_addr, addr_str, sizeof(addr_str) - 1);
listen_sock = socket(addr_family, SOCK_STREAM, ip_protocol);
if (listen_sock < 0)
{
ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
break;
}
ESP_LOGI(TAG, "Socket created");
//allow socket address reuse
if (setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1)
{
perror("setsockopt");
//exit(1);
}
ESP_LOGI(TAG, "address reuse ok");
int err = bind(listen_sock, (struct sockaddr *)&destAddr, sizeof(destAddr));
if (err != 0)
{
ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
break;
}
ESP_LOGI(TAG, "Socket binded");
err = listen(listen_sock, BACKLOG);
if (err != 0)
{
ESP_LOGE(TAG, "Error occured during listen: errno %d", errno);
break;
}
ESP_LOGI(TAG, "Socket listening");
#ifdef CONFIG_EXAMPLE_IPV6
struct sockaddr_in6 sourceAddr; // Large enough for both IPv4 or IPv6
#else
struct sockaddr_in sourceAddr;
#endif
uint addrLen = sizeof(sourceAddr);
maxsockfd = listen_sock;//use for select() to check the readable fd
ESP_LOGI(TAG, "maxsockfd [%d]",maxsockfd);
for (i = 0; i < BACKLOG; i++)
{
s_clients[i].fd = -1;
s_clients[i].idx = -1;
s_clients[i].timeout_tick = xTaskGetTickCount_new();//tick_count
}
online_client = 0;
rt_slist_init(&tcp_clients_list);
memset(tcp_rx_buffer,0,TCP_RX_BUF_SIZE);
for (;;)
{
uint32_t get_val = 0;
if( pdTRUE == xTaskNotifyWait( 0,//在函数开始出将消息对应的位清零
0,//在函数退出时将消息对应的位清零,只有在接收到通知才有效
&get_val,//接收到的消息值
0 )) //阻塞的时间
{
ESP_LOGI(TAG, "server recv notify get_val:%d",get_val);
goto free_task;
}
// initialize file descriptor set
FD_ZERO(&fdsr_tcp);
FD_SET(listen_sock, &fdsr_tcp);
// timeout setting
// add active connection to fd set
tcp_multiclient_t* p_client_cur = NULL;
tcp_multiclient_t* p_client_check = NULL;
rt_list_t* node_check;
rt_slist_for_each(node_check, &tcp_clients_list)
{
p_client_check = rt_slist_entry(node_check, tcp_multiclient_t, list);
if (p_client_check)
{
FD_CLR(p_client_check->fd, &fdsr_tcp);
FD_SET(p_client_check->fd, &fdsr_tcp);
p_client_cur = p_client_check;
}
}
tv.tv_sec = 1;
tv.tv_usec = 0;
//block here until listen_sock can read
int ret = select(maxsockfd+1, &fdsr_tcp, NULL, NULL, &tv);
if (ret < 0)
{
perror("select");
break;
}
else if (ret == 0)
{
//ESP_LOGI(TAG, "timeout_s[%d]",CONFIG_S_TIMEOUT_CLIENT*get_config.timeout);
if (p_client_cur)
{
int check_tick = (xTaskGetTickCount_new() - p_client_cur->timeout_tick);
//ESP_LOGI(TAG, "check_tick[%d]",check_tick);
if (check_tick >= ((CONFIG_S_TIMEOUT_CLIENT*get_config.timeout)*1000))
{
ESP_LOGI(TAG, "timeout with idx[%d] fd[%d]",p_client_cur->fd,p_client_cur->idx);
p_client_cur->timeout_tick = xTaskGetTickCount_new();
rt_slist_remove(&tcp_clients_list, &(p_client_cur->list));
client_remove(s_clients,p_client_cur->idx);
}
}
continue;
}
// check whether a new connection comes and handler the new connection
if (FD_ISSET(listen_sock, &fdsr_tcp))
{
int new_connect = accept(listen_sock, (struct sockaddr *)&client_addr, &addrLen);
if (new_connect <= 0)
{
perror("accept");
continue;
}
ESP_LOGI(TAG, "Socket accepted");
// add to fd queue
if (online_client < BACKLOG)
{
if (s_clients[online_client].fd != -1)
{
ESP_LOGI(TAG,"replace fd[%d]",s_clients[online_client].fd);
close(s_clients[online_client].fd);
FD_CLR(s_clients[online_client].fd, &fdsr_tcp);
s_clients[online_client].fd = -1;
if (online_client> 0)
{
online_client--;
ESP_LOGI(TAG,"reset online_client[%d]",online_client);
}
}
s_clients[online_client].timeout_tick = xTaskGetTickCount_new();
s_clients[online_client].fd = new_connect;
s_clients[online_client].idx = online_client;
ESP_LOGI(TAG,"new client timeout_tick=%d",s_clients[online_client].timeout_tick);
ESP_LOGI(TAG,"new connection client[%d] %s:%d", online_client,
inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
rt_slist_append(&tcp_clients_list, &(s_clients[online_client].list));
if (new_connect > maxsockfd)
maxsockfd = new_connect;
online_client++;
ESP_LOGI(TAG,"online_client=[%d]",online_client);
uint32_t get_len = rt_slist_len(&tcp_clients_list);
ESP_LOGI(TAG,"list len=[%d]",get_len);
if (get_len > 1)
{
tcp_multiclient_t *check_client=NULL;
tcp_multiclient_t *new_client = rt_slist_tail_entry(&tcp_clients_list,tcp_multiclient_t,list);
rt_list_t* node;
ESP_LOGI(TAG,"new_client id[%d],fd[%d]",new_client->idx,new_client->fd);
rt_slist_for_each(node, &tcp_clients_list)
{
check_client = rt_slist_entry(node, tcp_multiclient_t, list);
ESP_LOGI(TAG,"check_client id[%d],fd[%d]",check_client->idx,check_client->fd);
if (check_client->fd == -1)
{
ESP_LOGI(TAG,"remove id[%d],fd[%d]",check_client->idx,check_client->fd);
rt_slist_remove(&tcp_clients_list, &(check_client->list));
client_remove(s_clients,check_client->idx);
}
else if (check_client->fd != new_client->fd)
{
ESP_LOGI(TAG,"remove id[%d],fd[%d]",check_client->idx,check_client->fd);
rt_slist_remove(&tcp_clients_list, &(check_client->list));
client_remove(s_clients,check_client->idx);
}
}
}
}
else
{
ESP_LOGI(TAG,"max connections arrive, exit");
send(new_connect, "bye", 4, 0);
close(new_connect);
continue;
}
}
if (p_client_cur)
{
int get_fd = -1;
if( lock_tcp() == pdTRUE )
{
get_fd = p_client_cur->fd;
unlock_tcp();
}
else
{
ESP_LOGI(TAG, "take uart mux error");
}
uint32_t get_tick = p_client_cur->timeout_tick;
int8_t idx_client = p_client_cur->idx;
if (FD_ISSET(get_fd, &fdsr_tcp))
{
p_client_cur->timeout_tick = xTaskGetTickCount_new();
//ESP_LOGI(TAG,"s_clients[%d],idx=[%d]", get_fd, idx_client);
int ret = recv(get_fd, tcp_rx_buffer, TCP_RX_BUF_SIZE - 1, 0);
if (ret < 0)
{
ESP_LOGE(TAG, "recv failed : errno %d,ret=%d,s_clients.fd=%d", errno,ret,get_fd);
break;
}
else if (0 == ret)
{
ESP_LOGE(TAG,"close fd[%d] idx[%d]" ,get_fd, idx_client);
client_remove(s_clients,idx_client);
rt_slist_remove(&tcp_clients_list, &(p_client_cur->list));
continue;
}
else
{
#if 0
if (ret < TCP_RX_BUF_SIZE)
{
memset(&tcp_rx_buffer[ret],0, 1);
}
#endif
if( lock_uart() == pdTRUE )
{
uart_write_bytes(EX_UART_NUM, (const char *) tcp_rx_buffer, ret);
unlock_uart();
}
else
{
ESP_LOGI(TAG, "take uart mux error");
}
#if 0
uint8_t idx = 0;
ESP_LOGI(TAG,"uwrite");
for (idx=0; idxESP_LOGE(TAG,"%02x " ,tcp_rx_buffer[idx]);
}
ESP_LOGI(TAG,"\\n");
#endif
}
}
}
//showclient();
}
// when select() return error, close other connections
ESP_LOGE(TAG, "Shutting down socket and restarting...");
client_remove_all(s_clients);
shutdown(listen_sock,0);
close(listen_sock);
usleep(5000);
}
free_task:
client_remove_all(s_clients);
rt_slist_init(&tcp_clients_list);
shutdown(listen_sock,0);
close(listen_sock);
xEventGroupSetBits(wifi_event_group_check,TCP_TASK_FREE_BIT);
vTaskDelete(NULL);
}
void tcp_client_task(void *pvParameters)
{
char addr_str[128];
int addr_family;
int ip_protocol;
int get_fd = -1;
tcp_client_parameter_t get_config = {CONFIG_IP_TCP_SERVER,CONFIG_PORT_REMOTE};
if (pvParameters)
{
memcpy(&get_config,pvParameters,sizeof(tcp_client_parameter_t));
}
ESP_LOGI(TAG, "tcp_client_task waiting for ip...");
ESP_LOGI(TAG, "client port[%d] host[%s]",get_config.port_remote,get_config.ip_remote);
xEventGroupWaitBits(wifi_event_group_check, WIFI_CONNECTED_BIT, false, true, portMAX_DELAY);
while (1)
{
struct sockaddr_in destAddr;
destAddr.sin_addr.s_addr = inet_addr(get_config.ip_remote);
destAddr.sin_family = AF_INET;
destAddr.sin_port = htons(get_config.port_remote);
addr_family = AF_INET;
ip_protocol = IPPROTO_IP;
inet_ntoa_r(destAddr.sin_addr, addr_str, sizeof(addr_str) - 1);
if( lock_tcp() == pdTRUE )
{
socket_tcp_client = socket(addr_family, SOCK_STREAM, ip_protocol);
if (socket_tcp_client < 0)
{
ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
break;
}
get_fd = socket_tcp_client;
unlock_tcp();
}
else
{
ESP_LOGI(TAG, "take uart mux error");
}
ESP_LOGI(TAG, "Socket created");
int err = connect(get_fd, (struct sockaddr *)&destAddr, sizeof(destAddr));
if (err != 0)
{
ESP_LOGE(TAG, "Socket unable to connect: errno %d", errno);
}
ESP_LOGI(TAG, "Successfully connected");
while (1)
{
uint32_t get_val = 0;
if( pdTRUE == xTaskNotifyWait( 0,//在函数开始出将消息对应的位清零
0,//在函数退出时将消息对应的位清零,只有在接收到通知才有效
&get_val,//接收到的消息值
0 )) //阻塞的时间
{
ESP_LOGI(TAG, "client recv notify get_val:%d",get_val);
goto free_task;
}
ESP_LOGI(TAG, "recv");
int len = recv(get_fd, tcp_rx_buffer, sizeof(tcp_rx_buffer) - 1, 0);
// Error occured during receiving
if (len <= 0)
{
ESP_LOGE(TAG, "recv failed: errno %d", errno);
break;
}
// Data received
else //if (len > 0)
{
if( lock_uart() == pdTRUE )
{
uart_write_bytes(EX_UART_NUM, (const char *) tcp_rx_buffer, len);
unlock_uart();
}
else
{
ESP_LOGI(TAG, "take uart mux error");
}
//tcp_rx_buffer[len] = 0; // Null-terminate whatever we received and treat like a string
ESP_LOGI(TAG, "Received %d bytes from %s:", len, addr_str);
//ESP_LOGI(TAG, "%s", tcp_rx_buffer);
}
//ESP_LOGI(TAG, "vTaskDelay");
//vTaskDelay(100 / portTICK_PERIOD_MS);//200/10=20
}
if (get_fd != -1)
{
ESP_LOGE(TAG, "Shutting down socket and restarting...");
shutdown(get_fd, 0);
close(get_fd);
}
}
free_task:
if (get_fd != -1)
{
shutdown(get_fd, 0);
close(get_fd);
get_fd = -1;
}
xEventGroupSetBits(wifi_event_group_check,TCP_TASK_FREE_BIT);
vTaskDelete(NULL);
}
5 演示效果
6 总结
我们可以自己实现一套固件升级的协议,也可用通用的文件传输协议(例如ymodem),通过串口把固件分包再转发给其他MCU,实现具体产品的固件升级,通过无线来升级固件是非常方便的,可以节省拆卸外壳的时间,减少维护成本
全部0条评论
快来发表一下你的评论吧 !