update xbee
This commit is contained in:
parent
7c5bfb8e7b
commit
f58938ea6b
|
@ -8,8 +8,10 @@ package xbee
|
|||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// the frames have an outer shell - we will make a function that takes
|
||||
|
@ -25,6 +27,10 @@ type Frameable interface {
|
|||
Bytes() ([]byte, error)
|
||||
}
|
||||
|
||||
// we also need a way of tracking frame IDs - this is a uint8
|
||||
// it uses a sync.Map
|
||||
|
||||
|
||||
// now we can describe our function that takes a framable and contains it + calculates checksums.
|
||||
func calculateChecksum(data []byte) byte {
|
||||
var sum byte
|
||||
|
@ -84,104 +90,6 @@ const (
|
|||
|
||||
// AT commands are hard, so let's write out all the major ones here
|
||||
|
||||
type ATCmdFrame struct {
|
||||
Id byte
|
||||
Cmd string
|
||||
Param []byte
|
||||
Queued bool
|
||||
}
|
||||
|
||||
// implement the frame stuff for us.
|
||||
func (atFrame *ATCmdFrame) Bytes() ([]byte, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
if atFrame.Queued {
|
||||
// queued (batched) at comamnds have different Frame type
|
||||
buf.WriteByte(byte(ATCmdQueue))
|
||||
|
||||
} else {
|
||||
// normal frame type
|
||||
buf.WriteByte(byte(ATCmd))
|
||||
|
||||
}
|
||||
|
||||
buf.WriteByte(atFrame.Id)
|
||||
|
||||
// write cmd, if it's the right length.
|
||||
if cmdLen := len(atFrame.Cmd); cmdLen != 2 {
|
||||
return nil, fmt.Errorf("AT command incorrect length: %d", cmdLen)
|
||||
}
|
||||
buf.Write([]byte(atFrame.Cmd))
|
||||
|
||||
// write param.
|
||||
buf.Write(atFrame.Param)
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// transmissions to this address are instead broadcast
|
||||
const BroadcastAddr = 0xFFFF
|
||||
|
||||
type TxFrame struct {
|
||||
Id byte
|
||||
Destination uint64
|
||||
BCastRadius uint8
|
||||
Options uint8
|
||||
Payload []byte
|
||||
}
|
||||
|
||||
func (txFrame *TxFrame) Bytes() ([]byte, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
buf.WriteByte(byte(TxReq))
|
||||
|
||||
buf.WriteByte(txFrame.Id)
|
||||
|
||||
a := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(a, txFrame.Destination)
|
||||
buf.Write(a)
|
||||
|
||||
// write the reserved part.
|
||||
buf.Write([]byte{0xFF, 0xFE})
|
||||
|
||||
// write the radius
|
||||
buf.WriteByte(txFrame.BCastRadius)
|
||||
|
||||
buf.WriteByte(txFrame.Options)
|
||||
|
||||
buf.Write(txFrame.Payload)
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
type RemoteATCmdReq struct {
|
||||
ATCmdFrame
|
||||
Destination uint64
|
||||
Options uint8
|
||||
}
|
||||
|
||||
func (remoteAT *RemoteATCmdReq) Bytes() ([]byte, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
buf.WriteByte(byte(RemoteCmdReq))
|
||||
|
||||
buf.WriteByte(remoteAT.Id)
|
||||
|
||||
a := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(a, remoteAT.Destination)
|
||||
buf.Write(a)
|
||||
|
||||
// write the reserved part.
|
||||
buf.Write([]byte{0xFF, 0xFE})
|
||||
// write options
|
||||
buf.WriteByte(remoteAT.Options)
|
||||
|
||||
// now, write the AT command and the data.
|
||||
buf.Write([]byte(remoteAT.Cmd))
|
||||
|
||||
buf.Write(remoteAT.Param)
|
||||
|
||||
return buf.Bytes(), nil
|
||||
|
||||
}
|
||||
|
||||
// Now we will implement receiving packets from a character stream.
|
||||
// we first need to make a thing that produces frames from a stream using a scanner.
|
||||
|
|
|
@ -1,3 +1,73 @@
|
|||
package xbee
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"encoding/binary"
|
||||
)
|
||||
|
||||
// this file contains some AT command constants and
|
||||
type ATCmdFrame struct {
|
||||
Id byte
|
||||
Cmd string
|
||||
Param []byte
|
||||
Queued bool
|
||||
}
|
||||
|
||||
// implement the frame stuff for us.
|
||||
func (atFrame *ATCmdFrame) Bytes() ([]byte, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
if atFrame.Queued {
|
||||
// queued (batched) at comamnds have different Frame type
|
||||
buf.WriteByte(byte(ATCmdQueue))
|
||||
|
||||
} else {
|
||||
// normal frame type
|
||||
buf.WriteByte(byte(ATCmd))
|
||||
|
||||
}
|
||||
|
||||
buf.WriteByte(atFrame.Id)
|
||||
|
||||
// write cmd, if it's the right length.
|
||||
if cmdLen := len(atFrame.Cmd); cmdLen != 2 {
|
||||
return nil, fmt.Errorf("AT command incorrect length: %d", cmdLen)
|
||||
}
|
||||
buf.Write([]byte(atFrame.Cmd))
|
||||
|
||||
// write param.
|
||||
buf.Write(atFrame.Param)
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
|
||||
type RemoteATCmdReq struct {
|
||||
ATCmdFrame
|
||||
Destination uint64
|
||||
Options uint8
|
||||
}
|
||||
|
||||
func (remoteAT *RemoteATCmdReq) Bytes() ([]byte, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
buf.WriteByte(byte(RemoteCmdReq))
|
||||
|
||||
buf.WriteByte(remoteAT.Id)
|
||||
|
||||
a := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(a, remoteAT.Destination)
|
||||
buf.Write(a)
|
||||
|
||||
// write the reserved part.
|
||||
buf.Write([]byte{0xFF, 0xFE})
|
||||
// write options
|
||||
buf.WriteByte(remoteAT.Options)
|
||||
|
||||
// now, write the AT command and the data.
|
||||
buf.Write([]byte(remoteAT.Cmd))
|
||||
|
||||
buf.Write(remoteAT.Param)
|
||||
|
||||
return buf.Bytes(), nil
|
||||
|
||||
}
|
||||
|
|
54
internal/xbee/conntrack.go
Normal file
54
internal/xbee/conntrack.go
Normal file
|
@ -0,0 +1,54 @@
|
|||
package xbee
|
||||
import (
|
||||
"sync"
|
||||
"errors"
|
||||
)
|
||||
|
||||
// A connTrack is a simple frame mark utility for xbee packets. The xbee api frame
|
||||
// takes a mark that is used when sending the response - this allows to coordinate
|
||||
// the sent packet and the response, since there may be other packets emitted
|
||||
// between them.
|
||||
type connTrack struct {
|
||||
mu sync.RWMutex // use RW mutex to allow for multiple readers
|
||||
internal map[uint8]bool // map frame tag to if it's been used.
|
||||
// the map is set when writing a frame, and deleted when recieving a matching frame.
|
||||
}
|
||||
|
||||
// GetMark finds the next available marker and takes it, returning the value of
|
||||
// the mark. If no mark can be acquired, it returns an error.
|
||||
func (ct *connTrack) GetMark() (uint8, error) {
|
||||
// get a read lock.
|
||||
ct.mu.RLock()
|
||||
|
||||
// NOTE: we start at one. This is because 0 will not return a frame ever - it's
|
||||
// the "silent mode" mark
|
||||
for i := 1; i < 256; i++ {
|
||||
if !ct.internal[uint8(i)] {
|
||||
// it's free.
|
||||
// discard our read lock and lock for write.
|
||||
ct.mu.RUnlock()
|
||||
|
||||
ct.mu.Lock()
|
||||
// update the value to true.
|
||||
ct.internal[uint8(i)] = true
|
||||
ct.mu.Unlock()
|
||||
return uint8(i), nil
|
||||
}
|
||||
}
|
||||
ct.mu.RUnlock()
|
||||
return 0, errors.New("no available marks")
|
||||
}
|
||||
// ClearMark removes a given mark from the set if it exists, or returns an error.
|
||||
func (ct *connTrack) ClearMark(mark uint8) error {
|
||||
ct.mu.RLock()
|
||||
// FIXME: should this be the other way around (swap if and normal execution
|
||||
if ct.internal[mark] {
|
||||
ct.mu.RUnlock()
|
||||
ct.mu.Lock()
|
||||
delete(ct.internal, mark)
|
||||
ct.mu.Unlock()
|
||||
return nil
|
||||
}
|
||||
ct.mu.RUnlock()
|
||||
return errors.New("mark was not set")
|
||||
}
|
0
internal/xbee/rxframe.go
Normal file
0
internal/xbee/rxframe.go
Normal file
40
internal/xbee/txframe.go
Normal file
40
internal/xbee/txframe.go
Normal file
|
@ -0,0 +1,40 @@
|
|||
package xbee
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
)
|
||||
// transmissions to this address are instead broadcast
|
||||
const BroadcastAddr = 0xFFFF
|
||||
|
||||
type TxFrame struct {
|
||||
Id byte
|
||||
Destination uint64
|
||||
BCastRadius uint8
|
||||
Options uint8
|
||||
Payload []byte
|
||||
}
|
||||
|
||||
func (txFrame *TxFrame) Bytes() ([]byte, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
buf.WriteByte(byte(TxReq))
|
||||
|
||||
buf.WriteByte(txFrame.Id)
|
||||
|
||||
a := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(a, txFrame.Destination)
|
||||
buf.Write(a)
|
||||
|
||||
// write the reserved part.
|
||||
buf.Write([]byte{0xFF, 0xFE})
|
||||
|
||||
// write the radius
|
||||
buf.WriteByte(txFrame.BCastRadius)
|
||||
|
||||
buf.WriteByte(txFrame.Options)
|
||||
|
||||
buf.Write(txFrame.Payload)
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
Loading…
Reference in a new issue