at_client.go 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. package netAtSDK
  2. import (
  3. "errors"
  4. "fmt"
  5. "github.com/gogf/gf/encoding/gbinary"
  6. "github.com/gogf/gf/net/gtcp"
  7. "github.com/gogf/gf/os/glog"
  8. "github.com/gogf/gf/text/gstr"
  9. "time"
  10. )
  11. const timeOut = 5 * time.Second
  12. type ATClient struct {
  13. conn *gtcp.Conn
  14. password string
  15. }
  16. func NetATClient(conn *gtcp.Conn, password string) *ATClient {
  17. return &ATClient{
  18. conn: conn,
  19. password: password,
  20. }
  21. }
  22. // Save 保存配置
  23. func (a *ATClient) Save() error {
  24. _, err := a.send("AT&W")
  25. return err
  26. }
  27. // GetIMEI 查询串号
  28. // AT+GSN
  29. func (a *ATClient) GetIMEI() (imei string, err error) {
  30. buf, err := a.send("GSN")
  31. if err != nil {
  32. return
  33. }
  34. imei = buf[0]
  35. return
  36. }
  37. // GetICCID 查询ICCID
  38. // AT+ICCID
  39. func (a *ATClient) GetICCID() (iccid string, err error) {
  40. buf, err := a.send("ICCID")
  41. if err != nil {
  42. return
  43. }
  44. iccid = buf[0]
  45. iccid = gstr.Replace(iccid, "+ICCID:", "")
  46. return
  47. }
  48. // GetGPS 查询基站信息
  49. // AT+GPS:查询基站信息, +GPS: Lac:0x581b,CellId:0x0b8aa201
  50. func (a *ATClient) GetGPS() (gps string, err error) {
  51. buf, err := a.send("GPS")
  52. if err != nil {
  53. return
  54. }
  55. gps = buf[0]
  56. gps = gstr.Replace(gps, "+GPS: ", "")
  57. return
  58. }
  59. // SetDTUMode 设备工作模式
  60. // AT+DTUMODE:配置工作模式
  61. /*
  62. 第一个参数 0 不启用该通道; 1 TCP/UDP 透传; 2 MQTT 透传; 3 塔石 DTU 云连接; 4 塔石 IOT 云连接; 5 HTTP 传输模式; 6 阿里云直连
  63. B 取值范围 1~4,代表 4 个不同的 SOCKET 通道,省略时仅配置通道 1 默认值:+DTUMODE:1,0,0,0 默认第一路为 TCP/UDP 透传,其他几路为默认关闭
  64. */
  65. func (a *ATClient) SetDTUMode(mode int, channel int) error {
  66. _, err := a.send(fmt.Sprintf("DTUMODE=%d,%d", mode, channel))
  67. if err != nil {
  68. return err
  69. }
  70. return nil
  71. }
  72. // GetDTUMode 查询工作模式
  73. // 返回1,0,0,0格式
  74. func (a *ATClient) GetDTUMode() (mode string, err error) {
  75. buf, err := a.send("DTUMODE?")
  76. if err != nil {
  77. return
  78. }
  79. mode = buf[0]
  80. mode = gstr.Replace(mode, "+DTUMODE= ", "")
  81. return
  82. }
  83. // SetServerAddr 设置连接服务器地址
  84. /*
  85. 格式:AT+DSCADDR=A,"B","C",D
  86. A 取值范围 1-4,代表 4 个不同的 SOCKET
  87. 通道
  88. B 为 TCP 或者 UDP
  89. C 为服务器地址,可填域名或 IP,长度最大 255
  90. D 为端口号,范围 1~65535
  91. */
  92. func (a *ATClient) SetServerAddr(channel int, cType string, Ip string, port int) error {
  93. _, err := a.send(fmt.Sprintf("DSCADDR=%d, \"%s\", \"%s\", %d", channel, cType, Ip, port))
  94. if err != nil {
  95. return err
  96. }
  97. return nil
  98. }
  99. // GetServerAddr 查询连接服务器 地址
  100. // +DSCADDR:1,"tcp","cloud.tastek.cn",10067
  101. // +DSCADDR:2,"tcp","cloud.tastek.cn",10067
  102. func (a *ATClient) GetServerAddr() (ret []string, err error) {
  103. buf, err := a.send("DSCADDR?")
  104. if err != nil {
  105. return
  106. }
  107. for _, v := range buf {
  108. newV := gstr.Replace(v, "+DSCADDR:", "")
  109. ret = append(ret, newV)
  110. }
  111. return
  112. }
  113. // SetUARTParam AT+UARTCFG:串口参数设置
  114. /*
  115. 格式:AT+UARTCFG=A,B,C,D
  116. A 串口波特率,支持的波特率为 115200、57600、38400、19200、14400、9600、4800、 2400、1200
  117. B 数据位,取值范围 0~1 0 7 位数据位 1 8 位数据位
  118. C 校验位,取值范围 0~2 0 无校验 NONE 1 奇校验 ODD 2 偶校验 EVEN
  119. D 停止位,取值范围 0~1 0 1 位停止位 1 2 位停止位
  120. 默认值:+UARTCFG:9600,1,0,0(9600,8,N,1)
  121. */
  122. func (a *ATClient) SetUARTParam(rate, dFlag, cFlag, sFlag int) error {
  123. _, err := a.send(fmt.Sprintf("UARTCFG=%d,%d,%d,%d", rate, dFlag, cFlag, sFlag))
  124. return err
  125. }
  126. // GetUARTParam 获取串口配置
  127. // 返回: 115200,1,0,0
  128. func (a *ATClient) GetUARTParam() (ret string, err error) {
  129. buf, err := a.send("UARTCFG?")
  130. if err != nil {
  131. return
  132. }
  133. ret = gstr.Replace(buf[0], "+UARTCFG: ", "")
  134. return
  135. }
  136. // SetDTUID AT+DTUID:注册包设置
  137. /*
  138. 格式:AT+DTUID=A,B,C,"D"(,E)
  139. A 注册包模式,取值范围 0-3 0 不启用注册包 1 仅连接时上传 2 和数据一起上传,在数据前 3 包括 1,2
  140. B 注册包内容,取值范围 0-2 0 自定义注册包 1 IMEI(15 位模块对应的唯一识别码) 2 ICCID(20 位 SIM 卡对应编码)
  141. C 数据输入格式,取值范围 0-1 0 ASCII 格式 1 HEX 格式
  142. D 数据内容,最大长度为 512Byte
  143. E 可选参数,取值范围 1-4,分别代表 4 个 socket 通道,省略时仅配置通道 1 默认值:+DTUID: 0,0,0,"tas001"
  144. */
  145. func (a *ATClient) SetDTUID(mode, idType, format int, content string, channel ...int) error {
  146. cmd := fmt.Sprintf("DTUID=%d,%d,%d,\"%s\"", mode, idType, format, content)
  147. if len(channel) > 0 {
  148. cmd = cmd + fmt.Sprintf(",%d", channel[0])
  149. }
  150. _, err := a.send(cmd)
  151. return err
  152. }
  153. // GetDTUID 查询注册包配置
  154. /*
  155. 返回:1,0,0,"dtuid1",1
  156. 1,0,0,"dtuid1",1
  157. */
  158. func (a *ATClient) GetDTUID() (ret []string, err error) {
  159. buf, err := a.send("DTUID?")
  160. if err != nil {
  161. return
  162. }
  163. for _, v := range buf {
  164. newV := gstr.Replace(v, "+DTUID: ", "")
  165. ret = append(ret, newV)
  166. }
  167. return
  168. }
  169. // SetKeepAlive AT+KEEPALIVE:心跳包设置
  170. /*
  171. 格式:AT+KEEPALIVE=A,B,"C"(,D)
  172. A 心跳时间间隔,取值范围 0-3600 0 不启用 1-3600 固定时间间隔,单位秒
  173. B 数据输入格式 0 ASCII 模式 1 HEX 模式
  174. C 数据内容,最大长度 256 ,固件 3.4.0 之后版本支持特殊含义字段,例如$(IMEI)、 $(ICCID)、$(TIME)、$(CSQ)等
  175. D 可选参数,范围 1-4,分别代表 4 个 socket 通道,省略时仅配置通道 1 默认值:+KEEPALIVE: 0,0,"ping"
  176. */
  177. func (a *ATClient) SetKeepAlive(dur, format int, content string, channel ...int) error {
  178. cmd := fmt.Sprintf("KEEPALIVE=%d,%d,\"%s\"", dur, format, content)
  179. if len(channel) > 0 {
  180. cmd = cmd + fmt.Sprintf(",%d", channel[0])
  181. }
  182. _, err := a.send(cmd)
  183. return err
  184. }
  185. // GetKeepAlive 查询心跳包配置
  186. // +KEEPALIVE: 120,0,"keepalive",1
  187. func (a *ATClient) GetKeepAlive() (ret []string, err error) {
  188. buf, err := a.send("KEEPALIVE?")
  189. if err != nil {
  190. return
  191. }
  192. for _, v := range buf {
  193. newV := gstr.Replace(v, "+KEEPALIVE: ", "")
  194. ret = append(ret, newV)
  195. }
  196. return
  197. }
  198. // GetAll 查询所有参数
  199. // AT+DTUALL:查询所有参数
  200. /*
  201. +DTUMODE:1,0,0,0 +TCPMODBUS:0,0,0,0 +UARTCFG:9600,1,0,0 +WORKMODE:0 +REVNUM:"10086",4,0 +CSTT:"","","" +RELINKTIME:3 +DSCTIME:300 +ACKTIME: 0 +PORTTIME: 0 +DTUFILTER:0 ------------SOCKET_1------------ +DSCADDR:1,"tcp","122.231.164.87",10158
  202. +KEEPALIVE:0,0,"ping",1 +DTUID:0,0,0,"tas001",1 +DTUCLOUD:2,"866262040274796","42ee4b0449154f959be44fc242337599",1 ------------SOCKET_2------------ +DSCADDR:2,"tcp","cloud.tastek.cn",10067 +KEEPALIVE:0,0,"ping",2 +DTUID:0,0,0,"tas002",2 +DTUCLOUD:0,"cloudID","cloudPWD",2 ------------SOCKET_3------------ +DSCADDR:3,"tcp","cloud.tastek.cn",10067 +KEEPALIVE:0,0,"ping",3 +DTUID:0,0,0,"tas003",3 +DTUCLOUD:0,"20060059","123456",3 ------------SOCKET_4------------ +DSCADDR:4,"tcp","cloud.tastek.cn",10067 +KEEPALIVE:0,0,"ping",4 +DTUID:0,0,0,"tas003",4 +DTUCLOUD:0,"20060059","123456",4
  203. */
  204. func (a *ATClient) GetAll() (result []string, err error) {
  205. result, err = a.send("DTUALL?")
  206. if err != nil {
  207. return
  208. }
  209. return
  210. }
  211. // SetPOLL AT+POLL:轮询使能
  212. /*
  213. 格式:AT+POLL=A,B,C
  214. A 自定义轮询使能,取值范围 0-1 0 关闭自定义轮询功能 1 开启自定义轮询功能
  215. B 轮询时间间隔,取值范围 1-3600,表示每条启用指令间的时间间隔,单位秒
  216. C 轮询数据输入格式,取值范围 0-1 0 ASCII 格式,设置为 0 表示之后输入的轮询指令均为以 ASCII 形式轮询, 即输入什么字串就轮询什么字串 1 HEX 格式,设置为 1 表示之后输入的轮询指令需要满足 HEX 格式,轮 询时会自动转成 16 进制对应的 ASCII 字串 默认值:+POLL:0,10,1
  217. */
  218. func (a *ATClient) SetPOLL(sw, dur, format int) error {
  219. _, err := a.send(fmt.Sprintf("POLL=%d,%d,%d", sw, dur, format))
  220. return err
  221. }
  222. // GetPOLL 查询轮询使能
  223. // +POLL: 0,10,1
  224. func (a *ATClient) GetPOLL() (result string, err error) {
  225. buf, err := a.send("POLL?")
  226. result = gstr.Replace(buf[0], "+POLL: ", "")
  227. return
  228. }
  229. func (a *ATClient) send(cmd string) (ret []string, err error) {
  230. glog.Debugf("发送AT+%s", cmd)
  231. buf := a.prepareCommand(cmd)
  232. if _, err = a.conn.Write(buf); err != nil {
  233. return
  234. }
  235. timer := time.NewTimer(5 * time.Second)
  236. for {
  237. select {
  238. case <-timer.C:
  239. err = errors.New("读取AT响应超时")
  240. return
  241. default:
  242. buf, err = a.conn.RecvLine()
  243. if err != nil {
  244. return
  245. }
  246. str := string(buf)
  247. glog.Debugf("收到:%s, %2X", str, buf)
  248. if gstr.Contains(str, "OK") {
  249. return
  250. }
  251. ret = append(ret, str)
  252. }
  253. }
  254. }
  255. func (a *ATClient) prepareCommand(cmd string) []byte {
  256. cmdStr := fmt.Sprintf("@DTU:%s:%s", a.password, cmd)
  257. return gbinary.EncodeString(cmdStr)
  258. }