mirror of https://github.com/periph/devices
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
182 lines
4.0 KiB
Go
182 lines
4.0 KiB
Go
package firmata
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"fmt"
|
|
"math"
|
|
"sync"
|
|
|
|
"periph.io/x/conn/v3/gpio"
|
|
"periph.io/x/conn/v3/onewire"
|
|
)
|
|
|
|
type OneWireBus struct {
|
|
c ClientI
|
|
q *Pin
|
|
mu sync.Mutex
|
|
closer func() error
|
|
cid uint16
|
|
}
|
|
|
|
func newOneWireBus(c ClientI, q *Pin, closer func() error) *OneWireBus {
|
|
return &OneWireBus{
|
|
c: c,
|
|
q: q,
|
|
closer: closer,
|
|
}
|
|
}
|
|
|
|
func (b *OneWireBus) Close() error {
|
|
return b.closer()
|
|
}
|
|
|
|
func (b *OneWireBus) String() string {
|
|
panic("implement me")
|
|
}
|
|
|
|
func (b *OneWireBus) Search(alarmOnly bool) ([]onewire.Address, error) {
|
|
b.mu.Lock()
|
|
defer b.mu.Unlock()
|
|
|
|
ch := make(chan []byte)
|
|
defer close(ch)
|
|
|
|
release, err := b.c.SetOneWireListener(b.q.pin, ch)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer release()
|
|
|
|
if alarmOnly {
|
|
if _, err := b.c.SendSysEx(SysExOneWireData, byte(OneWireInstructionSearchAlarmed), b.q.pin); err != nil {
|
|
return nil, err
|
|
}
|
|
} else {
|
|
if _, err := b.c.SendSysEx(SysExOneWireData, byte(OneWireInstructionSearch), b.q.pin); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
data := <-ch
|
|
|
|
ins := OneWireInstruction(data[0])
|
|
pin := data[1]
|
|
|
|
if ins != OneWireInstructionSearchAlarmedReply && ins != OneWireInstructionSearchReply {
|
|
return nil, fmt.Errorf("%w: did not receive search reply", ErrUnhandledMessage)
|
|
}
|
|
if pin != b.q.pin {
|
|
return nil, fmt.Errorf("%w: received message from wrong bus", ErrInvalidOneWirePin)
|
|
}
|
|
|
|
data = Decoder7Bit(data[2:])
|
|
|
|
addresses := make([]onewire.Address, len(data)/8)
|
|
for i := range addresses {
|
|
addresses[i] = onewire.Address(binary.LittleEndian.Uint64(data[i*8:]))
|
|
}
|
|
return addresses, nil
|
|
}
|
|
|
|
func (b *OneWireBus) Tx(w, r []byte, power onewire.Pullup) error {
|
|
if len(w) == 0 && len(r) == 0 {
|
|
return nil
|
|
}
|
|
|
|
if len(r) > math.MaxUint16 {
|
|
return fmt.Errorf("%w: cannot reach more than %d bytes", ErrValueOutOfRange, math.MaxUint16)
|
|
}
|
|
|
|
b.mu.Lock()
|
|
defer b.mu.Unlock()
|
|
|
|
ch := make(chan []byte)
|
|
defer close(ch)
|
|
|
|
release, err := b.c.SetOneWireListener(b.q.pin, ch)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer release()
|
|
|
|
var powerVal byte = 0x00
|
|
if power {
|
|
powerVal = 0x01
|
|
}
|
|
|
|
if _, err := b.c.SendSysEx(SysExOneWireData, byte(OneWireInstructionConfigure), b.q.pin, powerVal); err != nil {
|
|
return err
|
|
}
|
|
|
|
cmd := OneWireCommandReset
|
|
|
|
if len(w) > 0 {
|
|
switch w[0] {
|
|
case 0xF0: // search rom
|
|
if _, err := b.c.SendSysEx(SysExOneWireData, byte(OneWireInstructionSearch), b.q.pin); err != nil {
|
|
return err
|
|
}
|
|
case 0xEC: // search rom (alarmed)
|
|
if _, err := b.c.SendSysEx(SysExOneWireData, byte(OneWireInstructionSearchAlarmed), b.q.pin); err != nil {
|
|
return err
|
|
}
|
|
case 0xCC: // skip rom
|
|
panic("not implemented")
|
|
case 0x33: // read rom
|
|
panic("not implemented")
|
|
case 0x55: // match rom
|
|
cmd |= OneWireCommandSelect
|
|
|
|
payload := w[1:9]
|
|
|
|
if len(w) > 9 { // command length (1) + address length (8)
|
|
cmd |= OneWireCommandWrite
|
|
}
|
|
if len(r) > 0 {
|
|
cmd |= OneWireCommandRead
|
|
// Write the amount of bytes to read.
|
|
payload = append(payload, byte(len(r)&0xFF), byte((len(r)>>8)&0xFF))
|
|
// Write the correlation id for sanity checking.
|
|
payload = append(payload, byte(b.cid&0xFF), byte((b.cid>>8)&0xFF))
|
|
b.cid++
|
|
}
|
|
|
|
cmd |= OneWireCommandDelay
|
|
payload = append(payload, 10, 0, 0, 0)
|
|
|
|
payload = append(payload, w[9:]...)
|
|
payload = append([]byte{byte(cmd), b.q.pin}, Encoder7Bit(payload)...)
|
|
if _, err := b.c.SendSysEx(SysExOneWireData, payload...); err != nil {
|
|
return err
|
|
}
|
|
default:
|
|
return fmt.Errorf("%w: no onewire command matching 0x%02X", ErrUnsupportedFeature, w[0])
|
|
}
|
|
}
|
|
|
|
if len(r) > 0 {
|
|
data := <-ch
|
|
|
|
ins := OneWireInstruction(data[0])
|
|
pin := data[1]
|
|
|
|
if ins != OneWireInstructionReadReply {
|
|
return fmt.Errorf("%w: did not receive read reply", ErrUnhandledMessage)
|
|
}
|
|
if pin != b.q.pin {
|
|
return fmt.Errorf("%w: received message from wrong bus", ErrInvalidOneWirePin)
|
|
}
|
|
|
|
data = Decoder7Bit(data[2:])
|
|
|
|
// Drop the correlation id when copying.
|
|
copy(r, data[2:])
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (b *OneWireBus) Q() gpio.PinIO {
|
|
return b.q
|
|
}
|