update xbee

This commit is contained in:
saji 2023-04-25 16:29:49 -05:00
parent 7c5bfb8e7b
commit f58938ea6b
5 changed files with 170 additions and 98 deletions

View file

@ -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.

View file

@ -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
}

View 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
View file

40
internal/xbee/txframe.go Normal file
View 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
}