package netAtSDK import ( "errors" "fmt" "github.com/gogf/gf/encoding/gbinary" "github.com/gogf/gf/net/gtcp" "github.com/gogf/gf/os/glog" "github.com/gogf/gf/text/gstr" "time" ) const timeOut = 5 * time.Second type ATClient struct { conn *gtcp.Conn password string } func NetATClient(conn *gtcp.Conn, password string) *ATClient { return &ATClient{ conn: conn, password: password, } } // Save 保存配置 func (a *ATClient) Save() error { _, err := a.send("AT&W") return err } // Restart 模块重启 // AT+CFUN=1,1 func (a *ATClient) Restart() error { _, err := a.send("CFUN=1,1") return err } // GetIMEI 查询串号 // AT+GSN func (a *ATClient) GetIMEI() (imei string, err error) { buf, err := a.send("GSN") if err != nil { return } imei = buf[0] return } // GetICCID 查询ICCID // AT+ICCID func (a *ATClient) GetICCID() (iccid string, err error) { buf, err := a.send("ICCID") if err != nil { return } iccid = buf[0] iccid = gstr.Replace(iccid, "+ICCID:", "") return } // GetGPS 查询基站信息 // AT+GPS:查询基站信息, +GPS: Lac:0x581b,CellId:0x0b8aa201 func (a *ATClient) GetGPS() (gps string, err error) { buf, err := a.send("GPS") if err != nil { return } gps = buf[0] gps = gstr.Replace(gps, "+GPS: ", "") return } // SetDTUMode 设备工作模式 // AT+DTUMODE:配置工作模式 /* 第一个参数 0 不启用该通道; 1 TCP/UDP 透传; 2 MQTT 透传; 3 塔石 DTU 云连接; 4 塔石 IOT 云连接; 5 HTTP 传输模式; 6 阿里云直连 B 取值范围 1~4,代表 4 个不同的 SOCKET 通道,省略时仅配置通道 1 默认值:+DTUMODE:1,0,0,0 默认第一路为 TCP/UDP 透传,其他几路为默认关闭 */ func (a *ATClient) SetDTUMode(mode int, channel int) error { _, err := a.send(fmt.Sprintf("DTUMODE=%d,%d", mode, channel)) if err != nil { return err } return nil } // GetDTUMode 查询工作模式 // 返回1,0,0,0格式 func (a *ATClient) GetDTUMode() (mode string, err error) { buf, err := a.send("DTUMODE?") if err != nil { return } mode = buf[0] mode = gstr.Replace(mode, "+DTUMODE= ", "") return } // SetServerAddr 设置连接服务器地址 /* 格式:AT+DSCADDR=A,"B","C",D A 取值范围 1-4,代表 4 个不同的 SOCKET 通道 B 为 TCP 或者 UDP C 为服务器地址,可填域名或 IP,长度最大 255 D 为端口号,范围 1~65535 */ func (a *ATClient) SetServerAddr(channel int, cType string, Ip string, port int) error { _, err := a.send(fmt.Sprintf("DSCADDR=%d, \"%s\", \"%s\", %d", channel, cType, Ip, port)) if err != nil { return err } return nil } // GetServerAddr 查询连接服务器 地址 // +DSCADDR:1,"tcp","cloud.tastek.cn",10067 // +DSCADDR:2,"tcp","cloud.tastek.cn",10067 func (a *ATClient) GetServerAddr() (ret []string, err error) { buf, err := a.send("DSCADDR?") if err != nil { return } for _, v := range buf { newV := gstr.Replace(v, "+DSCADDR:", "") ret = append(ret, newV) } return } // SetUARTParam AT+UARTCFG:串口参数设置 /* 格式:AT+UARTCFG=A,B,C,D A 串口波特率,支持的波特率为 115200、57600、38400、19200、14400、9600、4800、 2400、1200 B 数据位,取值范围 0~1 0 7 位数据位 1 8 位数据位 C 校验位,取值范围 0~2 0 无校验 NONE 1 奇校验 ODD 2 偶校验 EVEN D 停止位,取值范围 0~1 0 1 位停止位 1 2 位停止位 默认值:+UARTCFG:9600,1,0,0(9600,8,N,1) */ func (a *ATClient) SetUARTParam(rate, dFlag, cFlag, sFlag int) error { _, err := a.send(fmt.Sprintf("UARTCFG=%d,%d,%d,%d", rate, dFlag, cFlag, sFlag)) return err } // GetUARTParam 获取串口配置 // 返回: 115200,1,0,0 func (a *ATClient) GetUARTParam() (ret string, err error) { buf, err := a.send("UARTCFG?") if err != nil { return } ret = gstr.Replace(buf[0], "+UARTCFG: ", "") return } // SetDTUID AT+DTUID:注册包设置 /* 格式:AT+DTUID=A,B,C,"D"(,E) A 注册包模式,取值范围 0-3 0 不启用注册包 1 仅连接时上传 2 和数据一起上传,在数据前 3 包括 1,2 B 注册包内容,取值范围 0-2 0 自定义注册包 1 IMEI(15 位模块对应的唯一识别码) 2 ICCID(20 位 SIM 卡对应编码) C 数据输入格式,取值范围 0-1 0 ASCII 格式 1 HEX 格式 D 数据内容,最大长度为 512Byte E 可选参数,取值范围 1-4,分别代表 4 个 socket 通道,省略时仅配置通道 1 默认值:+DTUID: 0,0,0,"tas001" */ func (a *ATClient) SetDTUID(mode, idType, format int, content string, channel ...int) error { cmd := fmt.Sprintf("DTUID=%d,%d,%d,\"%s\"", mode, idType, format, content) if len(channel) > 0 { cmd = cmd + fmt.Sprintf(",%d", channel[0]) } _, err := a.send(cmd) return err } // GetDTUID 查询注册包配置 /* 返回:1,0,0,"dtuid1",1 1,0,0,"dtuid1",1 */ func (a *ATClient) GetDTUID() (ret []string, err error) { buf, err := a.send("DTUID?") if err != nil { return } for _, v := range buf { newV := gstr.Replace(v, "+DTUID: ", "") ret = append(ret, newV) } return } // SetKeepAlive AT+KEEPALIVE:心跳包设置 /* 格式:AT+KEEPALIVE=A,B,"C"(,D) A 心跳时间间隔,取值范围 0-3600 0 不启用 1-3600 固定时间间隔,单位秒 B 数据输入格式 0 ASCII 模式 1 HEX 模式 C 数据内容,最大长度 256 ,固件 3.4.0 之后版本支持特殊含义字段,例如$(IMEI)、 $(ICCID)、$(TIME)、$(CSQ)等 D 可选参数,范围 1-4,分别代表 4 个 socket 通道,省略时仅配置通道 1 默认值:+KEEPALIVE: 0,0,"ping" */ func (a *ATClient) SetKeepAlive(dur, format int, content string, channel ...int) error { cmd := fmt.Sprintf("KEEPALIVE=%d,%d,\"%s\"", dur, format, content) if len(channel) > 0 { cmd = cmd + fmt.Sprintf(",%d", channel[0]) } _, err := a.send(cmd) return err } // GetKeepAlive 查询心跳包配置 // +KEEPALIVE: 120,0,"keepalive",1 func (a *ATClient) GetKeepAlive() (ret []string, err error) { buf, err := a.send("KEEPALIVE?") if err != nil { return } for _, v := range buf { newV := gstr.Replace(v, "+KEEPALIVE: ", "") ret = append(ret, newV) } return } // GetAll 查询所有参数 // AT+DTUALL:查询所有参数 /* +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 +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 */ func (a *ATClient) GetAll() (result []string, err error) { result, err = a.send("DTUALL?") if err != nil { return } return } // SetPOLL AT+POLL:轮询使能 /* 格式:AT+POLL=A,B,C A 自定义轮询使能,取值范围 0-1 0 关闭自定义轮询功能 1 开启自定义轮询功能 B 轮询时间间隔,取值范围 1-3600,表示每条启用指令间的时间间隔,单位秒 C 轮询数据输入格式,取值范围 0-1 0 ASCII 格式,设置为 0 表示之后输入的轮询指令均为以 ASCII 形式轮询, 即输入什么字串就轮询什么字串 1 HEX 格式,设置为 1 表示之后输入的轮询指令需要满足 HEX 格式,轮 询时会自动转成 16 进制对应的 ASCII 字串 默认值:+POLL:0,10,1 */ func (a *ATClient) SetPOLL(sw, dur, format int) error { _, err := a.send(fmt.Sprintf("POLL=%d,%d,%d", sw, dur, format)) return err } // GetPOLL 查询轮询使能 // +POLL: 0,10,1 func (a *ATClient) GetPOLL() (result string, err error) { buf, err := a.send("POLL?") result = gstr.Replace(buf[0], "+POLL: ", "") return } // SetPOLLStr 设置轮询字符串 // AT+POLLSTR:轮询字串设置 /* 格式:AT+POLLSTR=A,B,C,"D" A 轮询字串号,取值范围 1-10 B 字串轮询使能,取值范围 0-1 0 禁用该条轮询 1 启用该条轮询 C 字串 CRC 使能,取值范围 0-1 0 无操作 1 对所输入字串进行 Modbus CRC 校验并在轮询时添加在字串末尾 D 轮询字串数据,如果在 AT+POLL 指令中设置了 HEX 标志位为 1,那么必须以 16 进制输入, 轮询时自动转换成 BIN 格式(例:所输入字串为"313233414243", 实际轮询的字串为"123ABC") 具体字符对应关系可以对照以下网址 http://ascii.911cha.com/ 默认值:+POLLSTR:1,0,0,"313233 */ func (a *ATClient) SetPOLLStr(index, sw, crc int, str string) error { _, err := a.send(fmt.Sprintf("POLLSTR=%d,%d,%d,%s", index, sw, crc, str)) return err } // GetGPSInfo AT+GPSINFO:查询经纬度(精度 100m), // 查询经纬度信息,仅注册上基站后生效,属于原始 GPS 坐标 // +GPSINFO: 030.1842195,120.2400433 func (a *ATClient) GetGPSInfo() (gps string, err error) { buf, err := a.send("GPSINFO") if err != nil { return } gps = gstr.Replace(buf[0], "+GPSINFO: ", "") return } func (a *ATClient) send(cmd string) (ret []string, err error) { glog.Debugf("发送AT+%s", cmd) buf := a.prepareCommand(cmd) if _, err = a.conn.Write(buf); err != nil { return } timer := time.NewTimer(5 * time.Second) for { select { case <-timer.C: err = errors.New("读取AT响应超时") return default: buf, err = a.conn.RecvLine() if err != nil { return } str := string(buf) glog.Debugf("收到:%s, %2X", str, buf) if gstr.Contains(str, "OK") { return } ret = append(ret, str) } } } func (a *ATClient) prepareCommand(cmd string) []byte { cmdStr := fmt.Sprintf("@DTU:%s:%s", a.password, cmd) return gbinary.EncodeString(cmdStr) }