Esp8266驱动独立目录,每个类一对源码文件

This commit is contained in:
Stone 2016-06-16 06:27:03 +00:00
parent a745365404
commit 323c04a457
10 changed files with 395 additions and 361 deletions

View File

@ -4,6 +4,10 @@
#include "Message\DataStore.h"
#include "Esp8266.h"
#include "EspTcp.h"
#include "EspUdp.h"
#include "WaitExpect.h"
#include "Config.h"
#define NET_DEBUG DEBUG
@ -14,23 +18,6 @@
#define net_printf(format, ...)
#endif
// 等待
class WaitExpect
{
public:
const String* Command = nullptr;
String* Result = nullptr;
cstring Key1 = nullptr;
cstring Key2 = nullptr;
bool Capture = true; // 是否捕获所有
//bool OK = false;
bool Wait(int msTimeout);
uint Parse(const Buffer& bs);
uint FindKey(const String& str);
};
/*
1AT+CWMODE需要重启后生效AT+RST
@ -39,61 +26,6 @@ public:
4
*/
/******************************** 内部Tcp/Udp ********************************/
class EspSocket : public Object, public ITransport, public ISocket
{
protected:
Esp8266& _Host;
byte _Index;
int _Error;
public:
EspSocket(Esp8266& host, ProtocolType protocol, byte idx);
virtual ~EspSocket();
// 打开Socket
virtual bool OnOpen();
virtual void OnClose();
//// 应用配置,修改远程地址和端口
//virtual bool Change(const String& remote, ushort port);
virtual bool OnWrite(const Buffer& bs);
virtual uint OnRead(Buffer& bs);
// 发送数据
virtual bool Send(const Buffer& bs);
// 接收数据
virtual uint Receive(Buffer& bs);
// 收到数据
virtual void OnProcess(const Buffer& bs, const IPEndPoint& remote);
protected:
bool SendData(const String& cmd, const Buffer& bs);
};
class EspTcp : public EspSocket
{
public:
EspTcp(Esp8266& host, byte idx);
virtual String& ToStr(String& str) const { return str + "Tcp_" + Local.Port; }
};
class EspUdp : public EspSocket
{
public:
EspUdp(Esp8266& host, byte idx);
virtual bool SendTo(const Buffer& bs, const IPEndPoint& remote);
virtual String& ToStr(String& str) const { return str + "Udp_" + Local.Port; }
private:
virtual bool OnWriteEx(const Buffer& bs, const void* opt);
};
/******************************** Esp8266 ********************************/
Esp8266::Esp8266(ITransport* port, Pin power, Pin rst)
@ -294,10 +226,6 @@ bool Esp8266::EnableDNS() { return true; }
// 启用DHCP
bool Esp8266::EnableDHCP() { Mode = SocketMode::STA_AP; return true;/* return SetDHCP(SocketMode::Both, true); */}
#if NET_DEBUG
static bool EnableLog = true;
#endif
// 发送指令,在超时时间内等待返回期望字符串,然后返回内容
String Esp8266::Send(const String& cmd, cstring expect, cstring expect2, uint msTimeout)
{
@ -337,6 +265,10 @@ String Esp8266::Send(const String& cmd, cstring expect, cstring expect2, uint ms
_Expect = &we;
#if NET_DEBUG
bool EnableLog = true;
#endif
if(cmd)
{
// 设定小灯快闪时间,单位毫秒
@ -346,7 +278,8 @@ String Esp8266::Send(const String& cmd, cstring expect, cstring expect2, uint ms
#if NET_DEBUG
// 只有AT指令显示日志
if(EnableLog && cmd.StartsWith("AT"))
if(!cmd.StartsWith("AT") || expect && expect[0] == '<') EnableLog = false;
if(EnableLog)
{
we.Command = &cmd;
net_printf("%d=> ", tid);
@ -946,283 +879,3 @@ bool Esp8266::SetIPD(bool enable)
String cmd = "AT+CIPDINFO=";
return SendCmd(cmd + (enable ? '1' : '0'));
}
/******************************** Socket ********************************/
EspSocket::EspSocket(Esp8266& host, ProtocolType protocol, byte idx)
: _Host(host)
{
_Index = idx;
_Error = 0;
Host = &host;
Protocol = protocol;
}
EspSocket::~EspSocket()
{
Close();
}
bool EspSocket::OnOpen()
{
// 确保宿主打开
if(!_Host.Open()) return false;
// 如果没有指定本地端口,则使用累加端口
if(!Local.Port)
{
// 累加端口
static ushort g_port = 1024;
if(g_port < 1024) g_port = 1024;
Local.Port = g_port++;
}
Local.Address = _Host.IP;
_Host.SetMux(true);
#if DEBUG
debug_printf("%s::Open ", Protocol == ProtocolType::Tcp ? "Tcp" : "Udp");
Local.Show(false);
debug_printf(" => ");
Server.Show(false);
debug_printf(" ");
Remote.Show(true);
#endif
String cmd = "AT+CIPSTART=";
cmd = cmd + _Index + ",";
if(Protocol == ProtocolType::Udp)
cmd += "\"UDP\"";
else if(Protocol == ProtocolType::Tcp)
cmd += "\"TCP\"";
auto rm = Server;
if(!rm) rm = Remote.Address.ToString();
// 设置端口目的(远程)IP地址和端口号
cmd = cmd + ",\"" + rm + "\"," + Remote.Port;
// 设置自己的端口号
if(Local.Port) cmd = cmd + ',' + Local.Port;
// UDP传输属性。0收到数据不改变远端目标1收到数据改变一次远端目标2收到数据改变远端目标
/*if(Remote.Address == IPAddress::Broadcast())
cmd += ",2";
else*/
cmd += ",0";
// 打开Socket。有OK/ERROR/ALREADY CONNECTED三种
auto rt = _Host.Send(cmd + "\r\n", "OK", "ERROR", 1600);
if(!rt.Contains("OK") && ! rt.Contains("ALREADY CONNECTED"))
{
debug_printf("协议 %d, %d 打开失败 \r\n", Protocol, Remote.Port);
return false;
}
// 清空一次缓冲区
/*cmd = "AT+CIPBUFRESET=";
_Host.SendCmd(cmd + _Index);*/
_Error = 0;
return true;
}
void EspSocket::OnClose()
{
String cmd = "AT+CIPCLOSE=";
cmd += _Index;
cmd += "\r\n";
_Host.SendCmd(cmd, 1600);
}
// 接收数据
uint EspSocket::Receive(Buffer& bs)
{
if(!Open()) return 0;
return 0;
}
// 发送数据
bool EspSocket::Send(const Buffer& bs)
{
if(!Open()) return false;
String cmd = "AT+CIPSEND=";
cmd = cmd + _Index + ',' + bs.Length() + "\r\n";
return SendData(cmd, bs);
}
bool EspSocket::SendData(const String& cmd, const Buffer& bs)
{
#if NET_DEBUG
EnableLog = false;
#endif
// 重发3次AT指令避免busy
int i = 0;
for(i=0; i<3; i++)
{
//auto rt = _Host.Send(cmd, ">", "OK", 1600);
// 不能等待OK而应该等待>,因为发送期间可能给别的指令碰撞
auto rt = _Host.Send(cmd, ">", "ERROR", 1600);
if(rt.Contains(">")) break;
}
if(i<3 && _Host.Send(bs.AsString(), "SEND OK", "ERROR", 1600).Contains("SEND OK"))
{
#if NET_DEBUG
EnableLog = true;
#endif
return true;
}
#if NET_DEBUG
EnableLog = true;
#endif
// 发送失败,关闭链接,下一次重新打开
if(++_Error >= 10)
{
_Error = 0;
Close();
}
return false;
}
bool EspSocket::OnWrite(const Buffer& bs) { return Send(bs); }
uint EspSocket::OnRead(Buffer& bs) { return Receive(bs); }
// 收到数据
void EspSocket::OnProcess(const Buffer& bs, const IPEndPoint& remote)
{
OnReceive((Buffer&)bs, (void*)&remote);
}
/******************************** Tcp ********************************/
EspTcp::EspTcp(Esp8266& host, byte idx)
: EspSocket(host, ProtocolType::Tcp, idx)
{
}
/******************************** Udp ********************************/
EspUdp::EspUdp(Esp8266& host, byte idx)
: EspSocket(host, ProtocolType::Udp, idx)
{
}
bool EspUdp::SendTo(const Buffer& bs, const IPEndPoint& remote)
{
//!!! ESP8266有BUG收到数据后远程地址还是乱了所以这里的远程地址跟实际可能不一致
if(remote == Remote) return Send(bs);
if(!Open()) return false;
String cmd = "AT+CIPSEND=";
cmd = cmd + _Index + ',' + bs.Length();
// 加上远程IP和端口
cmd = cmd + ",\"" + remote.Address + "\"";
cmd = cmd + ',' + remote.Port;
cmd += "\r\n";
return SendData(cmd, bs);
}
bool EspUdp::OnWriteEx(const Buffer& bs, const void* opt)
{
auto ep = (IPEndPoint*)opt;
if(!ep) return OnWrite(bs);
return SendTo(bs, *ep);
}
/******************************** WaitExpect ********************************/
bool WaitExpect::Wait(int msTimeout)
{
// 提前等待一会,再开始轮询,专门为了加快命中快速响应的指令
Sys.Sleep(40);
if(!Result) return true;
// 等待收到数据
TimeWheel tw(0, msTimeout - 40);
// 默认检查间隔200ms如果超时时间大于1000ms则以四分之一为检查间隔
// ESP8266串口任务平均时间为150ms左右为了避免接收指令任务里面发送指令时等不到OK需要加大检查间隔
tw.Sleep = 200;
if(msTimeout > 1000) tw.Sleep = msTimeout >> 2;
if(tw.Sleep > 1000) tw.Sleep = 1000;
while(Result)
{
if(tw.Expired()) return false;
}
return true;
}
uint WaitExpect::Parse(const Buffer& bs)
{
if(bs.Length() == 0 || !Result) return 0;
TS("WaitExpect::Parse");
// 适配任意关键字后,也就是收到了成功或失败,通知业务层已结束
auto s = (const String)bs.AsString();
int p = FindKey(s);
auto& rs= *Result;
// 捕获所有
if(Capture)
{
if(p > 0)
rs += bs.Sub(0, p).AsString();
else
rs += s;
}
else if(p > 0)
rs = bs.Sub(0, p).AsString();
// 匹配关键字,任务完成
if(p > 0) Result = nullptr;
// 如果后面是换行,则跳过
if(p < s.Length() && s[p] == ' ') p++;
if(p < s.Length() && s[p] == '\r') p++;
if(p < s.Length() && s[p] == '\n') p++;
return p;
}
uint WaitExpect::FindKey(const String& str)
{
// 适配第一关键字
int p = Key1 ? str.IndexOf(Key1) : -1;
if(p >= 0)
{
//net_printf("适配第一关键字 %s \r\n", Key1);
return p + String(Key1).Length();
}
// 适配第二关键字
p = Key2 ? str.IndexOf(Key2) : -1;
if(p >= 0)
{
net_printf("适配第二关键字 %s \r\n", Key2);
return p + String(Key2).Length();
}
// 适配busy
p = str.IndexOf("busy ");
if(p >= 0)
{
net_printf("适配 busy \r\n");
return p + 4 + 1 + 4;
}
return 0;
}

View File

@ -1,7 +1,6 @@
#ifndef __Esp8266_H__
#define __Esp8266_H__
#include "Sys.h"
#include "Port.h"
#include "Net\ITransport.h"
#include "Net\Socket.h"
@ -13,11 +12,8 @@ class Esp8266 : public PackPort, public ISocketHost
{
public:
bool AutoConn; // 是否自动连接WiFi默认false
//String SSID;
//String Pass;
IDataPort* Led; // 指示灯
//Action NetReady; // 网络准备就绪
Esp8266(ITransport* port, Pin power = P0, Pin rst = P0);
virtual ~Esp8266();

View File

@ -0,0 +1,154 @@
#include "EspSocket.h"
#define NET_DEBUG DEBUG
//#define NET_DEBUG 0
#if NET_DEBUG
#define net_printf debug_printf
#else
#define net_printf(format, ...)
#endif
/******************************** Socket ********************************/
EspSocket::EspSocket(Esp8266& host, ProtocolType protocol, byte idx)
: _Host(host)
{
_Index = idx;
_Error = 0;
Host = &host;
Protocol = protocol;
}
EspSocket::~EspSocket()
{
Close();
}
bool EspSocket::OnOpen()
{
// 确保宿主打开
if(!_Host.Open()) return false;
// 如果没有指定本地端口,则使用累加端口
if(!Local.Port)
{
// 累加端口
static ushort g_port = 1024;
if(g_port < 1024) g_port = 1024;
Local.Port = g_port++;
}
Local.Address = _Host.IP;
_Host.SetMux(true);
#if DEBUG
debug_printf("%s::Open ", Protocol == ProtocolType::Tcp ? "Tcp" : "Udp");
Local.Show(false);
debug_printf(" => ");
Server.Show(false);
debug_printf(" ");
Remote.Show(true);
#endif
String cmd = "AT+CIPSTART=";
cmd = cmd + _Index + ",";
if(Protocol == ProtocolType::Udp)
cmd += "\"UDP\"";
else if(Protocol == ProtocolType::Tcp)
cmd += "\"TCP\"";
auto rm = Server;
if(!rm) rm = Remote.Address.ToString();
// 设置端口目的(远程)IP地址和端口号
cmd = cmd + ",\"" + rm + "\"," + Remote.Port;
// 设置自己的端口号
if(Local.Port) cmd = cmd + ',' + Local.Port;
// UDP传输属性。0收到数据不改变远端目标1收到数据改变一次远端目标2收到数据改变远端目标
/*if(Remote.Address == IPAddress::Broadcast())
cmd += ",2";
else*/
cmd += ",0";
// 打开Socket。有OK/ERROR/ALREADY CONNECTED三种
auto rt = _Host.Send(cmd + "\r\n", "OK", "ERROR", 1600);
if(!rt.Contains("OK") && ! rt.Contains("ALREADY CONNECTED"))
{
debug_printf("协议 %d, %d 打开失败 \r\n", Protocol, Remote.Port);
return false;
}
// 清空一次缓冲区
/*cmd = "AT+CIPBUFRESET=";
_Host.SendCmd(cmd + _Index);*/
_Error = 0;
return true;
}
void EspSocket::OnClose()
{
String cmd = "AT+CIPCLOSE=";
cmd += _Index;
cmd += "\r\n";
_Host.SendCmd(cmd, 1600);
}
// 接收数据
uint EspSocket::Receive(Buffer& bs)
{
if(!Open()) return 0;
return 0;
}
// 发送数据
bool EspSocket::Send(const Buffer& bs)
{
if(!Open()) return false;
String cmd = "AT+CIPSEND=";
cmd = cmd + _Index + ',' + bs.Length() + "\r\n";
return SendData(cmd, bs);
}
bool EspSocket::SendData(const String& cmd, const Buffer& bs)
{
// 重发3次AT指令避免busy
int i = 0;
for(i=0; i<3; i++)
{
//auto rt = _Host.Send(cmd, ">", "OK", 1600);
// 不能等待OK而应该等待>,因为发送期间可能给别的指令碰撞
auto rt = _Host.Send(cmd, ">", "ERROR", 1600);
if(rt.Contains(">")) break;
}
if(i<3 && _Host.Send(bs.AsString(), "SEND OK", "ERROR", 1600).Contains("SEND OK"))
{
return true;
}
// 发送失败,关闭链接,下一次重新打开
if(++_Error >= 10)
{
_Error = 0;
Close();
}
return false;
}
bool EspSocket::OnWrite(const Buffer& bs) { return Send(bs); }
uint EspSocket::OnRead(Buffer& bs) { return Receive(bs); }
// 收到数据
void EspSocket::OnProcess(const Buffer& bs, const IPEndPoint& remote)
{
OnReceive((Buffer&)bs, (void*)&remote);
}

View File

@ -0,0 +1,39 @@
#ifndef __EspSocket_H__
#define __EspSocket_H__
#include "Esp8266.h"
class EspSocket : public Object, public ITransport, public ISocket
{
protected:
Esp8266& _Host;
byte _Index;
int _Error;
public:
EspSocket(Esp8266& host, ProtocolType protocol, byte idx);
virtual ~EspSocket();
// 打开Socket
virtual bool OnOpen();
virtual void OnClose();
//// 应用配置,修改远程地址和端口
//virtual bool Change(const String& remote, ushort port);
virtual bool OnWrite(const Buffer& bs);
virtual uint OnRead(Buffer& bs);
// 发送数据
virtual bool Send(const Buffer& bs);
// 接收数据
virtual uint Receive(Buffer& bs);
// 收到数据
virtual void OnProcess(const Buffer& bs, const IPEndPoint& remote);
protected:
bool SendData(const String& cmd, const Buffer& bs);
};
#endif

View File

@ -0,0 +1,9 @@
#include "EspTcp.h"
/******************************** Tcp ********************************/
EspTcp::EspTcp(Esp8266& host, byte idx)
: EspSocket(host, ProtocolType::Tcp, idx)
{
}

14
Drivers/Esp8266/EspTcp.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef __EspTcp_H__
#define __EspTcp_H__
#include "EspSocket.h"
class EspTcp : public EspSocket
{
public:
EspTcp(Esp8266& host, byte idx);
virtual String& ToStr(String& str) const { return str + "Tcp_" + Local.Port; }
};
#endif

View File

@ -0,0 +1,35 @@
#include "EspUdp.h"
/******************************** Udp ********************************/
EspUdp::EspUdp(Esp8266& host, byte idx)
: EspSocket(host, ProtocolType::Udp, idx)
{
}
bool EspUdp::SendTo(const Buffer& bs, const IPEndPoint& remote)
{
//!!! ESP8266有BUG收到数据后远程地址还是乱了所以这里的远程地址跟实际可能不一致
if(remote == Remote) return Send(bs);
if(!Open()) return false;
String cmd = "AT+CIPSEND=";
cmd = cmd + _Index + ',' + bs.Length();
// 加上远程IP和端口
cmd = cmd + ",\"" + remote.Address + "\"";
cmd = cmd + ',' + remote.Port;
cmd += "\r\n";
return SendData(cmd, bs);
}
bool EspUdp::OnWriteEx(const Buffer& bs, const void* opt)
{
auto ep = (IPEndPoint*)opt;
if(!ep) return OnWrite(bs);
return SendTo(bs, *ep);
}

19
Drivers/Esp8266/EspUdp.h Normal file
View File

@ -0,0 +1,19 @@
#ifndef __EspUdp_H__
#define __EspUdp_H__
#include "EspSocket.h"
class EspUdp : public EspSocket
{
public:
EspUdp(Esp8266& host, byte idx);
virtual bool SendTo(const Buffer& bs, const IPEndPoint& remote);
virtual String& ToStr(String& str) const { return str + "Udp_" + Local.Port; }
private:
virtual bool OnWriteEx(const Buffer& bs, const void* opt);
};
#endif

View File

@ -0,0 +1,95 @@
#include "Time.h"
#include "Sys.h"
#include "WaitExpect.h"
#define NET_DEBUG DEBUG
//#define NET_DEBUG 0
#if NET_DEBUG
#define net_printf debug_printf
#else
#define net_printf(format, ...)
#endif
/******************************** WaitExpect ********************************/
bool WaitExpect::Wait(int msTimeout)
{
// 提前等待一会,再开始轮询,专门为了加快命中快速响应的指令
Sys.Sleep(40);
if(!Result) return true;
// 等待收到数据
TimeWheel tw(0, msTimeout - 40);
// 默认检查间隔200ms如果超时时间大于1000ms则以四分之一为检查间隔
// ESP8266串口任务平均时间为150ms左右为了避免接收指令任务里面发送指令时等不到OK需要加大检查间隔
tw.Sleep = 200;
if(msTimeout > 1000) tw.Sleep = msTimeout >> 2;
if(tw.Sleep > 1000) tw.Sleep = 1000;
while(Result)
{
if(tw.Expired()) return false;
}
return true;
}
uint WaitExpect::Parse(const Buffer& bs)
{
if(bs.Length() == 0 || !Result) return 0;
TS("WaitExpect::Parse");
// 适配任意关键字后,也就是收到了成功或失败,通知业务层已结束
auto s = (const String)bs.AsString();
int p = FindKey(s);
auto& rs= *Result;
// 捕获所有
if(Capture)
{
if(p > 0)
rs += bs.Sub(0, p).AsString();
else
rs += s;
}
else if(p > 0)
rs = bs.Sub(0, p).AsString();
// 匹配关键字,任务完成
if(p > 0) Result = nullptr;
// 如果后面是换行,则跳过
if(p < s.Length() && s[p] == ' ') p++;
if(p < s.Length() && s[p] == '\r') p++;
if(p < s.Length() && s[p] == '\n') p++;
return p;
}
uint WaitExpect::FindKey(const String& str)
{
// 适配第一关键字
int p = Key1 ? str.IndexOf(Key1) : -1;
if(p >= 0)
{
//net_printf("适配第一关键字 %s \r\n", Key1);
return p + String(Key1).Length();
}
// 适配第二关键字
p = Key2 ? str.IndexOf(Key2) : -1;
if(p >= 0)
{
net_printf("适配第二关键字 %s \r\n", Key2);
return p + String(Key2).Length();
}
// 适配busy
p = str.IndexOf("busy ");
if(p >= 0)
{
net_printf("适配 busy \r\n");
return p + 4 + 1 + 4;
}
return 0;
}

View File

@ -0,0 +1,20 @@
#ifndef __WaitExpect_H__
#define __WaitExpect_H__
// 等待
class WaitExpect
{
public:
const String* Command = nullptr;
String* Result = nullptr;
cstring Key1 = nullptr;
cstring Key2 = nullptr;
bool Capture = true; // 是否捕获所有
bool Wait(int msTimeout);
uint Parse(const Buffer& bs);
uint FindKey(const String& str);
};
#endif