update xbee
This commit is contained in:
parent
7c5bfb8e7b
commit
f58938ea6b
|
@ -8,8 +8,10 @@ package xbee
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
// the frames have an outer shell - we will make a function that takes
|
// the frames have an outer shell - we will make a function that takes
|
||||||
|
@ -25,6 +27,10 @@ type Frameable interface {
|
||||||
Bytes() ([]byte, error)
|
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.
|
// now we can describe our function that takes a framable and contains it + calculates checksums.
|
||||||
func calculateChecksum(data []byte) byte {
|
func calculateChecksum(data []byte) byte {
|
||||||
var sum byte
|
var sum byte
|
||||||
|
@ -84,104 +90,6 @@ const (
|
||||||
|
|
||||||
// AT commands are hard, so let's write out all the major ones here
|
// 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.
|
// 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.
|
// we first need to make a thing that produces frames from a stream using a scanner.
|
||||||
|
|
|
@ -1,3 +1,73 @@
|
||||||
package xbee
|
package xbee
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"encoding/binary"
|
||||||
|
)
|
||||||
|
|
||||||
// this file contains some AT command constants and
|
// 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