增加GSM07驱动,拆分AT指令集。

编译通过,未测试
This commit is contained in:
大石头X2 2017-03-08 17:31:45 +08:00
parent 017035f573
commit 217618d848
9 changed files with 869 additions and 61 deletions

397
App/AT.cpp Normal file
View File

@ -0,0 +1,397 @@
#include "Kernel\Sys.h"
#include "Kernel\Task.h"
#include "Kernel\TTime.h"
#include "Kernel\WaitHandle.h"
#include "Device\SerialPort.h"
#include "AT.h"
#include "Config.h"
#include "App\FlushPort.h"
#define NET_DEBUG DEBUG
//#define NET_DEBUG 0
#if NET_DEBUG
#define net_printf debug_printf
#else
#define net_printf(format, ...)
#endif
struct CmdState
{
const String* Command = nullptr;
String* Result = nullptr;
cstring Key1 = nullptr;
cstring Key2 = nullptr;
cstring Key3 = nullptr;
bool Capture = true; // 是否捕获所有
uint Parse(const Buffer& bs, WaitHandle& handle);
uint FindKey(const String& str);
};
void LoadStationTask(void* param);
/*
1AT+CWMODE需要重启后生效AT+RST
2AP模式下查询本机IP无效
3server需要多连接作为基础AT+CIPMUX=1
4
*/
/******************************** AT ********************************/
AT::AT()
{
Port = nullptr;
DataKey = nullptr;
_Expect = nullptr;
}
AT::~AT()
{
delete Port;
}
void AT::Init(COM idx, int baudrate)
{
auto srp = new SerialPort(idx, baudrate);
srp->Tx.SetCapacity(0x200);
srp->Rx.SetCapacity(0x200);
srp->MaxSize = 512;
Init(srp);
}
void AT::Init(ITransport* port)
{
Port = port;
if (Port) Port->Register(OnPortReceive, this);
}
bool AT::Open()
{
if (!Port->Open()) return false;
/*if (!CheckReady())
{
net_printf("AT::Open 打开失败!");
return false;
}*/
return true;
}
void AT::Close()
{
Port->Close();
}
// 发送指令,在超时时间内等待返回期望字符串,然后返回内容
String AT::Send(const String& cmd, cstring expect, cstring expect2, uint msTimeout)
{
TS("AT::Send");
String rs;
auto& task = Task::Current();
// 判断是否正在发送其它指令
if (_Expect)
{
#if NET_DEBUG
auto h = (WaitHandle*)_Expect;
auto w = (CmdState*)h->State;
net_printf("AT::Send %d 正在发送 ", h->TaskID);
if (w->Command)
w->Command->Trim().Show(false);
else
net_printf("数据");
net_printf(" %d 无法发送 ", task.ID);
cmd.Trim().Show(true);
#endif
return rs;
}
// 在接收事件中拦截
CmdState we;
// 数据不显示Command没有打开NET_DEBUG时也不显示
//we.Command = &cmd;
we.Result = &rs;
we.Key1 = expect;
we.Key2 = expect2;
we.Key3 = "busy ";
WaitHandle handle;
handle.State = &we;
_Expect = &handle;
#if NET_DEBUG
bool EnableLog = true;
#endif
if (cmd)
{
Port->Write(cmd);
#if NET_DEBUG
// 只有AT指令显示日志
if (!cmd.StartsWith("AT") || (expect && expect[0] == '>')) EnableLog = false;
if (EnableLog)
{
we.Command = &cmd;
//net_printf("%d=> ", task.ID);
cmd.Trim().Show(true);
}
#endif
}
handle.WaitOne(msTimeout);
if (_Expect == &handle) _Expect = nullptr;
//if(rs.Length() > 4) rs.Trim();
#if NET_DEBUG
if (EnableLog && rs)
{
net_printf("%d<= ", task.ID);
// 太长时不要去尾,避免产生重新分配
if (rs.Length() < 0x40)
rs.Trim().Show(true);
else
rs.Show(true);
}
#endif
return rs;
}
// 发送命令,自动检测并加上\r\n等待响应OK
bool AT::SendCmd(const String& cmd, uint msTimeout)
{
TS("AT::SendCmd");
static const cstring ok = "OK";
static const cstring err = "ERROR";
String cmd2;
// 只有AT指令需要检查结尾其它指令不检查避免产生拷贝
auto p = &cmd;
if (cmd.StartsWith("AT") && !cmd.EndsWith("\r\n"))
{
cmd2 = cmd;
cmd2 += "\r\n";
p = &cmd2;
}
// 二级拦截。遇到错误也马上结束
auto rt = Send(*p, ok, err, msTimeout);
return rt.Contains(ok);
}
bool AT::WaitForCmd(cstring expect, uint msTimeout)
{
String rs;
// 在接收事件中拦截
CmdState we;
we.Result = &rs;
we.Key1 = expect;
we.Capture = false;
WaitHandle handle;
handle.State = &we;
_Expect = &handle;
// 等待收到数据
bool rt = handle.WaitOne(msTimeout);
_Expect = nullptr;
return rt;
}
void ParseFail(cstring name, const Buffer& bs)
{
#if NET_DEBUG
if (bs.Length() == 0) return;
int p = 0;
if (p < bs.Length() && bs[p] == ' ') p++;
if (p < bs.Length() && bs[p] == '\r') p++;
if (p < bs.Length() && bs[p] == '\n') p++;
// 无法识别的数据可能是空格前缀,需要特殊处理
auto str = bs.Sub(p, -1).AsString();
if (str)
{
net_printf("AT:%s 无法识别[%d]", name, bs.Length());
//if(bs.Length() == 2) net_printf("%02X %02X ", bs[0], bs[1]);
str.Show(true);
}
#endif
}
uint AT::OnPortReceive(ITransport* sender, Buffer& bs, void* param, void* param2)
{
auto esp = (AT*)param;
return esp->OnReceive(bs, param2);
}
// 引发数据到达事件
uint AT::OnReceive(Buffer& bs, void* param)
{
if (bs.Length() == 0) return 0;
//!!! 分析数据和命令返回,特别要注意粘包
int s = 0;
int p = 0;
auto str = bs.AsString();
while (p >= 0 && p < bs.Length())
{
s = p;
p = str.IndexOf(DataKey, s);
// +IPD之前之后的数据留给命令分析
int size = p >= 0 ? p - s : bs.Length() - s;
if (size > 0)
{
if (_Expect)
{
ParseReply(bs.Sub(s, size));
#if NET_DEBUG
// 如果没有吃完,剩下部分报未识别
//if(rs < size) ParseFail("ParseReply", bs.Sub(s + rs, size - rs));
// 不要报未识别了,反正内部会全部吃掉
#endif
}
else
{
#if NET_DEBUG
ParseFail("NoExpect", bs.Sub(s, size));
#endif
}
}
// +IPD开头的数据作为收到数据
if (p >= 0)
{
if (p + 5 >= bs.Length())
{
#if NET_DEBUG
ParseFail("+IPD<=5", bs.Sub(p, -1));
#endif
break;
}
else
{
auto bs2 = bs.Sub(p, -1);
Received(bs2);
int rs = bs2.Length();
if (rs <= 0)
{
#if NET_DEBUG
ParseFail("ParseReceive", bs.Sub(p + rs, -1));
#endif
break;
}
// 游标移到下一组数据
p += rs;
}
}
}
return 0;
}
// 分析关键字。返回被用掉的字节数
uint AT::ParseReply(const Buffer& bs)
{
if (!_Expect) return 0;
// 拦截给同步方法
auto handle = (WaitHandle*)_Expect;
auto we = (CmdState*)handle->State;
bool rs = we->Parse(bs, *handle);
// 如果内部已经适配,则清空
if (!we->Result) _Expect = nullptr;
return rs;
}
uint CmdState::Parse(const Buffer& bs, WaitHandle& handle)
{
if (bs.Length() == 0 || !Result) return 0;
TS("WaitExpect::Parse");
// 适配任意关键字后,也就是收到了成功或失败,通知业务层已结束
auto s_ = bs.AsString();
auto& s = (const String&)s_;
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;
// 设置事件,通知等待任务退出循环
handle.Set();
}
// 如果后面是换行,则跳过
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 CmdState::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(Key3);
if (p >= 0)
{
net_printf("适配第三关键字 %s \r\n", Key3);
return p + String(Key3).Length();
}
return 0;
}

43
App/AT.h Normal file
View File

@ -0,0 +1,43 @@
#ifndef __AT_H__
#define __AT_H__
// GPRS的AT指令集 GSM 07.07
class AT
{
public:
ITransport* Port; // 传输口
cstring DataKey; // 数据关键字
Delegate<Buffer&> Received;
AT();
~AT();
void Init(COM idx, int baudrate = 115200);
void Init(ITransport* port);
// 打开与关闭
bool Open();
void Close();
/******************************** 发送指令 ********************************/
// 发送指令,在超时时间内等待返回期望字符串,然后返回内容
String Send(const String& cmd, cstring expect, cstring expect2 = nullptr, uint msTimeout = 1000);
// 发送命令,自动检测并加上\r\n等待响应OK
bool SendCmd(const String& cmd, uint msTimeout = 1000);
// 等待命令返回
bool WaitForCmd(cstring expect, uint msTimeout);
private:
void* _Expect; // 等待内容
// 分析关键字。返回被用掉的字节数
uint ParseReply(const Buffer& bs);
// 引发数据到达事件
uint OnReceive(Buffer& bs, void* param);
static uint OnPortReceive(ITransport* sender, Buffer& bs, void* param, void* param2);
};
#endif

View File

@ -5,9 +5,7 @@
#include "Device\WatchDog.h"
#include "Config.h"
#include "Drivers\NRF24L01.h"
#include "Drivers\W5500.h"
#include "Drivers\Esp8266\Esp8266.h"
#include "Drivers\GSM07.h"
#include "TokenNet\TokenController.h"
#include "TokenNet\TokenConfig.h"
@ -124,52 +122,22 @@ void AP0803::InitButtons(const Delegate2<InputPort&, bool>& press)
}
}
NetworkInterface* AP0803::Create5500()
NetworkInterface* AP0803::CreateGPRS()
{
debug_printf("\r\nW5500::Create \r\n");
debug_printf("\r\nCreateGPRS::Create \r\n");
auto net = new W5500(Spi2, PE1, PD13);
auto net = new GSM07();
net->Init(COM4);
net->Set(P0, P0);
if(!net->Open())
{
delete net;
return nullptr;
}
net->EnableDNS();
net->EnableDHCP();
return net;
}
NetworkInterface* AP0803::Create8266(bool apOnly)
{
debug_printf("\r\nEsp8266::Create \r\n");
auto esp = new Esp8266(COM4, PE2, PD3);
// 初次需要指定模式 否则为 Wire
bool join = esp->SSID && *esp->SSID;
if (!join)
{
*esp->SSID = "WSWL";
*esp->Pass = "12345678";
esp->Mode = NetworkType::STA_AP;
esp->WorkMode = NetworkType::STA_AP;
}
if(!esp->Open())
{
delete esp;
return nullptr;
}
Client->Register("SetWiFi", &Esp8266::SetWiFi, esp);
Client->Register("GetWiFi", &Esp8266::GetWiFi, esp);
return esp;
}
/******************************** Token ********************************/
void AP0803::InitClient()
@ -209,8 +177,7 @@ static void OnInitNet(void* param)
{
auto& bsp = *(AP0803*)param;
bsp.Create5500();
bsp.Create8266(false);
bsp.CreateGPRS();
Client->Open();
}

View File

@ -40,12 +40,8 @@ public:
void InitLeds();
void InitButtons(const Delegate2<InputPort&, bool>& press);
// 打开以太网W5500
NetworkInterface* Create5500();
// 打开Esp8266作为主控或者纯AP
NetworkInterface* Create8266(bool apOnly);
// ITransport* Create2401();
// 打开GPRS
NetworkInterface* CreateGPRS();
void InitClient();
void InitNet();

View File

@ -18,21 +18,6 @@ bool WaitExpect::Wait(int msTimeout)
Sys.Sleep(40);
if(!Result) return true;
/*// 等待收到数据
TimeWheel tw(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;*/
return Handle.WaitOne(msTimeout);
}

324
Drivers/GSM07.cpp Normal file
View File

@ -0,0 +1,324 @@
#include "Kernel\Sys.h"
#include "Kernel\Task.h"
#include "Kernel\TTime.h"
#include "Kernel\WaitHandle.h"
#include "Device\SerialPort.h"
#include "GSM07.h"
#include "Config.h"
#include "App\FlushPort.h"
#define NET_DEBUG DEBUG
//#define NET_DEBUG 0
#if NET_DEBUG
#define net_printf debug_printf
#else
#define net_printf(format, ...)
#endif
struct CmdState
{
String* Command = nullptr;
String* Result = nullptr;
cstring Key1 = nullptr;
cstring Key2 = nullptr;
bool Capture = true; // 是否捕获所有
};
/*
1AT+CWMODE需要重启后生效AT+RST
2AP模式下查询本机IP无效
3server需要多连接作为基础AT+CIPMUX=1
4
*/
/******************************** GSM07 ********************************/
GSM07::GSM07()
{
Name = "GSM07";
Speed = 10;
APN = "CMNET";
Led = nullptr;
Buffer(_sockets, 5 * 4).Clear();
Mode = NetworkType::STA_AP;
InitConfig();
LoadConfig();
}
GSM07::~GSM07()
{
RemoveLed();
}
void GSM07::Init(COM idx, int baudrate)
{
At.Init(idx, baudrate);
}
void GSM07::Init(ITransport* port)
{
At.Init(port);
}
void GSM07::Set(Pin power, Pin rst)
{
if (power != P0) _Power.Init(power, false);
if (rst != P0) _Reset.Init(rst, true);
}
void GSM07::SetLed(Pin led)
{
if (led != P0)
{
auto port = new OutputPort(led);
SetLed(*port);
}
}
void GSM07::SetLed(OutputPort& led)
{
auto fp = new FlushPort();
fp->Port = &led;
fp->Start();
Led = fp;
}
void GSM07::RemoveLed()
{
if (Led)delete (FlushPort*)Led;
Led = nullptr;
}
bool GSM07::OnOpen()
{
if (!At.Open()) return false;
if (!CheckReady())
{
net_printf("GSM07::Open 打开失败!");
return false;
}
At.Received.Bind(&GSM07::OnReceive, this);
// 开回显
Echo(true);
#if NET_DEBUG
// 获取版本
GetVersion();
//auto ver = GetVersion();
//net_printf("版本:");
//ver.Show(true);
#endif
Config();
return true;
}
bool GSM07::CheckReady()
{
// 先关一会电,然后再上电,让它来一个完整的冷启动
if (!_Power.Empty())
{
_Power.Open(); // 使用前必须Open
_Power.Down(20);
}
if (!_Reset.Empty()) _Reset.Open(); // 使用前必须Open
// 每两次启动会有一次打开失败,交替
if (!_Reset.Empty())
Reset(false); // 硬重启
else
Reset(true); // 软件重启命令
// 等待模块启动进入就绪状态
if (!Test())
{
net_printf("GSM07::Open 打开失败!");
return false;
}
return true;
}
void GSM07::OnClose()
{
// 先断开已有连接
At.SendCmd("AT+CIPSHUT\r");
At.Close();
_Power.Close();
_Reset.Close();
}
bool GSM07::OnLink(uint retry)
{
//if(Linked) return true;
debug_printf("GSM07::OnLink\r\n");
/*bool join = SSID && *SSID;
// 等待WiFi自动连接
if (!WaitForCmd("WIFI CONNECTED", 3000))
{
auto mode = WorkMode;
// 默认Both
if (mode == NetworkType::Wire) mode = NetworkType::STA_AP;
if (!join || mode == NetworkType::STA_AP) OpenAP();
if (join)
{
if (!JoinAP(*SSID, *Pass)) return false;
ShowConfig();
SaveConfig();
}
}*/
return true;
}
// 配置网络参数
void GSM07::Config()
{
// ATE0 关闭回显
// ATE1 开启回显
At.SendCmd("ATE0\r");
At.SendCmd("AT+CIPSHUT\r");
At.SendCmd("AT+CGCLASS=\"B\"\r");
SetAPN(APN, false);
At.SendCmd("AT+CGATT=1\r");
// 先断开已有连接
//At.SendCmd("AT+CIPSHUT\r");
//设置APN
SetAPN(APN, true);
At.SendCmd("AT+CLPORT=\"UDP\",\"3399\"\r");
// IP设置方式
//At.SendCmd("AT+CIPSTART=\"UDP\",\"183.63.213.113\",\"3388\"\r");
// 域名设置方式
At.SendCmd("AT+CIPMUX=0\r");
At.SendCmd("AT+CIPRXGET=1\r");
At.SendCmd("AT+CIPQRCLOSE=1\r");
At.SendCmd("AT+CIPMODE=0\r");
}
Socket* GSM07::CreateSocket(NetType type)
{
/*auto es = (EspSocket**)_sockets;
int i = 0;
for (i = 0; i < 5; i++)
{
if (es[i] == nullptr) break;
}
if (i >= 5)
{
net_printf("没有空余的Socket可用了 !\r\n");
return nullptr;
}*/
switch (type)
{
/*case NetType::Tcp:
return es[i] = new EspTcp(*this, i);
case NetType::Udp:
return es[i] = new EspUdp(*this, i);*/
default:
return nullptr;
}
}
// 数据到达
void GSM07::OnReceive(Buffer& bs)
{
Received(bs);
}
/******************************** 基础AT指令 ********************************/
// 基础AT指令
bool GSM07::Test()
{
// 避免在循环内部频繁构造和析构
String cmd = "AT";
for (int i = 0; i < 10; i++)
{
if (At.SendCmd(cmd, 500)) return true;
Reset(false);
}
return false;
}
bool GSM07::Reset(bool soft)
{
if (soft) return At.SendCmd("AT+RST");
_Reset.Up(100);
return true;
}
/*
AT
SDK版本信息
*/
String GSM07::GetVersion()
{
return At.Send("AT+GMR\r\n", "OK");
}
bool GSM07::Sleep(uint ms)
{
String cmd = "AT+GSLP=";
cmd += ms;
return At.SendCmd(cmd);
}
bool GSM07::Echo(bool open)
{
String cmd = "ATE";
cmd = cmd + (open ? '1' : '0');
return At.SendCmd(cmd);
}
// 恢复出厂设置将擦写所有保存到Flash的参数恢复为默认参数。会导致模块重启
bool GSM07::Restore()
{
return At.SendCmd("AT+RESTORE");
}
// 00:CMNET 10:CMHK 01:CHKT 11:HKCSL
void GSM07::SetAPN(cstring apn, bool issgp)
{
String str;
if(issgp)
str = "AT+CIPCSGP=1";
else
str = "AT+CGDCONT=1,\"IP\"";
str = str + ",\"" + apn + "\"\r";
At.SendCmd(str);
}
/******************************** TCP/IP ********************************/

84
Drivers/GSM07.h Normal file
View File

@ -0,0 +1,84 @@
#ifndef __GSM07_H__
#define __GSM07_H__
#include "Device\Port.h"
#include "App\AT.h"
#include "Net\Socket.h"
#include "Net\NetworkInterface.h"
#include "Net\ITransport.h"
#include "Message\DataStore.h"
#include "Message\Pair.h"
// GPRS的AT指令集 GSM 07.07
class GSM07 : public NetworkInterface
{
public:
AT At; // AT操作对象
cstring APN;
IDataPort* Led; // 指示灯
OutputPort _Power;
OutputPort _Reset;
OutputPort _LowPower;
Delegate<Buffer&> Received;
GSM07();
virtual ~GSM07();
void Init(ITransport* port);
void Init(COM idx, int baudrate = 115200);
void Set(Pin power = P0, Pin rst = P0);
virtual void Config();
void SetLed(Pin led);
void SetLed(OutputPort& led);
void RemoveLed();
virtual Socket* CreateSocket(NetType type);
/******************************** 基础AT指令 ********************************/
bool Test();
bool Reset(bool soft);
String GetVersion();
bool Sleep(uint ms);
bool Echo(bool open);
// 恢复出厂设置将擦写所有保存到Flash的参数恢复为默认参数。会导致模块重启
bool Restore();
void SetAPN(cstring apn, bool issgp);
/******************************** 功能指令 ********************************/
IPAddress GetIP(bool sta);
/******************************** TCP/IP ********************************/
/******************************** 发送指令 ********************************/
private:
IPEndPoint _Remote; // 当前数据包远程地址
// 打开与关闭
virtual bool OnOpen();
virtual void OnClose();
// 检测连接
virtual bool OnLink(uint retry);
bool CheckReady();
// 多个硬件socket
int* _sockets[5];
// 分析+IPD接收数据。返回被用掉的字节数
uint ParseReceive(const Buffer& bs);
// 分析关键字。返回被用掉的字节数
uint ParseReply(const Buffer& bs);
// 数据到达
void OnReceive(Buffer& bs);
};
#endif

View File

@ -13,6 +13,7 @@
<ItemGroup>
<ClCompile Include="..\App\ACZero.cpp" />
<ClCompile Include="..\App\Alarm.cpp" />
<ClCompile Include="..\App\AT.cpp" />
<ClCompile Include="..\App\BlinkPort.cpp" />
<ClCompile Include="..\App\Button.cpp" />
<ClCompile Include="..\App\Button_GrayLevel.cpp" />
@ -31,6 +32,7 @@
<ClCompile Include="..\Board\AP0104.cpp" />
<ClCompile Include="..\Board\AP0801.cpp" />
<ClCompile Include="..\Board\AP0802.cpp" />
<ClCompile Include="..\Board\AP0803.cpp" />
<ClCompile Include="..\Board\BaseBoard.cpp" />
<ClCompile Include="..\Board\IOK026X.cpp" />
<ClCompile Include="..\Board\IOK027X.cpp" />
@ -84,6 +86,7 @@
<ClCompile Include="..\Drivers\Esp8266\EspTcp.cpp" />
<ClCompile Include="..\Drivers\Esp8266\EspUdp.cpp" />
<ClCompile Include="..\Drivers\Esp8266\WaitExpect.cpp" />
<ClCompile Include="..\Drivers\GSM07.cpp" />
<ClCompile Include="..\Drivers\HX711.cpp" />
<ClCompile Include="..\Drivers\JTW8953.cpp" />
<ClCompile Include="..\Drivers\NRF24L01.cpp" />

View File

@ -560,5 +560,14 @@
<ClCompile Include="..\Drivers\Esp8266\WaitExpect.cpp">
<Filter>Drivers\Esp8266</Filter>
</ClCompile>
<ClCompile Include="..\Board\AP0803.cpp">
<Filter>Board</Filter>
</ClCompile>
<ClCompile Include="..\Drivers\GSM07.cpp">
<Filter>Drivers</Filter>
</ClCompile>
<ClCompile Include="..\App\AT.cpp">
<Filter>App</Filter>
</ClCompile>
</ItemGroup>
</Project>