updates and testing of implementations

pull/20/head
imle 5 years ago
parent 050bc7a20a
commit 7b71c747f2

@ -20,6 +20,9 @@ import (
"periph.io/x/conn/v3/spi" "periph.io/x/conn/v3/spi"
) )
// I2CAddr i2c default address.
const I2CAddr uint16 = 0x77
// Oversampling affects how much time is taken to measure each of temperature, // Oversampling affects how much time is taken to measure each of temperature,
// pressure and humidity. // pressure and humidity.
// //

@ -10,6 +10,22 @@ type AnalogMappingResponse struct {
DigitalPinToAnalog map[uint8]uint8 DigitalPinToAnalog map[uint8]uint8
} }
func ParseAnalogMappingResponse(data []byte) AnalogMappingResponse {
var response = AnalogMappingResponse{
AnalogPinToDigital: []uint8{},
DigitalPinToAnalog: map[uint8]uint8{},
}
for i := 0; i < len(data); i++ {
if data[i] != CapabilityResponsePinDelimiter {
response.DigitalPinToAnalog[uint8(i)] = uint8(len(response.AnalogPinToDigital))
response.AnalogPinToDigital = append(response.AnalogPinToDigital, uint8(i))
}
}
return response
}
func (a AnalogMappingResponse) String() string { func (a AnalogMappingResponse) String() string {
str := bytes.Buffer{} str := bytes.Buffer{}
for analogPin, digitalPin := range a.AnalogPinToDigital { for analogPin, digitalPin := range a.AnalogPinToDigital {

@ -33,6 +33,30 @@ type CapabilityResponse struct {
SupportedPinModes [][]pin.Func SupportedPinModes [][]pin.Func
} }
func ParseCapabilityResponse(data []byte) CapabilityResponse {
var response = CapabilityResponse{
PinToModeToResolution: []map[pin.Func]uint8{{}},
SupportedPinModes: [][]pin.Func{{}},
}
var pindex = 0
for i := 0; i < len(data); {
if data[i] == CapabilityResponsePinDelimiter {
response.PinToModeToResolution = append(response.PinToModeToResolution, map[pin.Func]uint8{})
response.SupportedPinModes = append(response.SupportedPinModes, []pin.Func{})
i += 1
pindex++
} else {
pinFunc := pinModeToFuncMap[data[i]]
response.PinToModeToResolution[pindex][pinFunc] = data[i+1]
response.SupportedPinModes[pindex] = append(response.SupportedPinModes[pindex], pinFunc)
i += 2
}
}
return response
}
func (c CapabilityResponse) String() string { func (c CapabilityResponse) String() string {
str := bytes.Buffer{} str := bytes.Buffer{}
for p, modeMap := range c.PinToModeToResolution { for p, modeMap := range c.PinToModeToResolution {

@ -15,8 +15,10 @@ import (
// These max values are for data bytes as, within firmata, data is 7 bits long. // These max values are for data bytes as, within firmata, data is 7 bits long.
const ( const (
MaxUInt8 uint8 = (1<<8 - 1) >> 1 MaxUInt8 uint8 = (1<<8 - 1) >> (8 / 8)
MaxUInt16 uint16 = (1<<16 - 1) >> 2 MaxUInt16 uint16 = (1<<16 - 1) >> (16 / 8)
MaxUInt24 uint32 = (1<<24 - 1) >> (24 / 8)
MaxUInt32 uint32 = (1<<32 - 1) >> (32 / 8)
) )
var commandResponseMap = map[SysExCmd]SysExCmd{ var commandResponseMap = map[SysExCmd]SysExCmd{
@ -28,7 +30,7 @@ var commandResponseMap = map[SysExCmd]SysExCmd{
type ClientI interface { type ClientI interface {
SendSysEx(SysExCmd, ...byte) (chan []byte, error) SendSysEx(SysExCmd, ...byte) (chan []byte, error)
SendReset() error SendReset() error
ExtendedReportAnalogPin(uint8, int) error ExtendedReportAnalogPin(uint8, uint8) error
CapabilityQuery() (chan CapabilityResponse, error) CapabilityQuery() (chan CapabilityResponse, error)
PinStateQuery(uint8) (chan PinStateResponse, error) PinStateQuery(uint8) (chan PinStateResponse, error)
ReportFirmware() (chan FirmwareReport, error) ReportFirmware() (chan FirmwareReport, error)
@ -124,6 +126,10 @@ func (c *Client) Start() error {
c.started = true c.started = true
c.mu.Unlock() c.mu.Unlock()
if err := c.SendReset(); err != nil {
return err
}
if b, ok := c.board.(flusher); ok { if b, ok := c.board.(flusher); ok {
b.Flush() b.Flush()
} else if b, ok := c.board.(flusherErr); ok { } else if b, ok := c.board.(flusherErr); ok {
@ -132,12 +138,16 @@ func (c *Client) Start() error {
} }
} }
firmChannel := make(chan []byte, 1)
// Don't call ReportFirmware as it is automatic, but we want to register a listener for it. // Don't call ReportFirmware as it is automatic, but we want to register a listener for it.
firmChannel := make(chan []byte, 1)
c.mu.Lock() c.mu.Lock()
c.responseChannels[SysExReportFirmware] = []chan []byte{firmChannel} c.responseChannels[SysExReportFirmware] = []chan []byte{firmChannel}
c.mu.Unlock() c.mu.Unlock()
report := c.parseReportFirmware(firmChannel)
responseChannel := make(chan FirmwareReport, 1)
go func() {
responseChannel <- ParseFirmwareReport(<-firmChannel)
}()
go func() { go func() {
err := c.responseWatcher() err := c.responseWatcher()
@ -146,7 +156,22 @@ func (c *Client) Start() error {
} }
}() }()
fmt.Println("Firmware Info:", <-report) // Do not move on until we receive our firmware report. This is a signal the device is ready.
// Anything sent before we receive this message will not be read by the device.
fmt.Println("Firmware Info:", <-responseChannel)
analogMappingQuery, err := c.SendAnalogMappingQuery()
if err != nil {
return err
}
capabilityQuery, err := c.CapabilityQuery()
if err != nil {
return err
}
fmt.Println(<-analogMappingQuery)
fmt.Println(<-capabilityQuery)
return nil return nil
} }
@ -156,8 +181,6 @@ func (c *Client) write(payload []byte, withinMutex func()) error {
c.mu.Lock() c.mu.Lock()
defer c.mu.Unlock() defer c.mu.Unlock()
//fmt.Println(SprintHexArray(payload))
// Write to the board. // Write to the board.
_, err := c.board.Write(payload) _, err := c.board.Write(payload)
if err != nil { if err != nil {
@ -280,6 +303,8 @@ func (c *Client) responseWatcher() (err error) {
resp <- data resp <- data
close(resp) close(resp)
case cmd == SysExStringData: case cmd == SysExStringData:
// TODO: The Firmata spec defines this as mostly an error statement.
// Should we fail on receiving a string message?
fmt.Printf("device: [%s]\n", TwoByteString(data)) fmt.Printf("device: [%s]\n", TwoByteString(data))
default: default:
str := "" str := ""
@ -347,41 +372,18 @@ func (c *Client) CapabilityQuery() (chan CapabilityResponse, error) {
return nil, err return nil, err
} }
resp := c.parseCapabilityCommand(future)
return resp, nil
}
func (c *Client) parseCapabilityCommand(future chan []byte) chan CapabilityResponse {
resp := make(chan CapabilityResponse, 1) resp := make(chan CapabilityResponse, 1)
go func() { go func() {
data := <-future data := <-future
var response = CapabilityResponse{ var response = ParseCapabilityResponse(data)
PinToModeToResolution: []map[pin.Func]uint8{{}},
SupportedPinModes: [][]pin.Func{{}},
}
var pindex = 0
for i := 0; i < len(data); {
if data[i] == CapabilityResponsePinDelimiter {
response.PinToModeToResolution = append(response.PinToModeToResolution, map[pin.Func]uint8{})
response.SupportedPinModes = append(response.SupportedPinModes, []pin.Func{})
i += 1
pindex++
} else {
pinFunc := pinModeToFuncMap[data[i]]
response.PinToModeToResolution[pindex][pinFunc] = data[i+1]
response.SupportedPinModes[pindex] = append(response.SupportedPinModes[pindex], pinFunc)
i += 2
}
}
c.cr = response c.cr = response
resp <- response resp <- response
close(resp) close(resp)
}() }()
return resp return resp, nil
} }
func (c *Client) SendAnalogMappingQuery() (chan AnalogMappingResponse, error) { func (c *Client) SendAnalogMappingQuery() (chan AnalogMappingResponse, error) {
@ -390,46 +392,18 @@ func (c *Client) SendAnalogMappingQuery() (chan AnalogMappingResponse, error) {
return nil, err return nil, err
} }
resp := c.parseAnalogMappingQuery(future)
return resp, nil
}
func (c *Client) parseAnalogMappingQuery(future chan []byte) chan AnalogMappingResponse {
resp := make(chan AnalogMappingResponse, 1) resp := make(chan AnalogMappingResponse, 1)
go func() { go func() {
data := <-future data := <-future
var response = AnalogMappingResponse{ var response = ParseAnalogMappingResponse(data)
AnalogPinToDigital: []uint8{},
DigitalPinToAnalog: map[uint8]uint8{},
}
for i := 0; i < len(data); i++ {
if data[i] != CapabilityResponsePinDelimiter {
response.DigitalPinToAnalog[uint8(i)] = uint8(len(response.AnalogPinToDigital))
response.AnalogPinToDigital = append(response.AnalogPinToDigital, uint8(i))
}
}
c.amr = response c.amr = response
resp <- response resp <- response
close(resp) close(resp)
}() }()
return resp return resp, nil
}
func (c *Client) ExtendedReportAnalogPin(p uint8, value int) error {
if value > 0xFFFFFFFFFFFFFF {
return fmt.Errorf("%w: 0x0 - 0xFFFFFFFFFFFFFF", ErrValueOutOfRange)
}
_, err := c.SendSysEx(SysExExtendedAnalog, p, uint8(value), uint8(value>>7), uint8(value>>14))
if err != nil {
return err
}
return nil
} }
func (c *Client) PinStateQuery(p uint8) (chan PinStateResponse, error) { func (c *Client) PinStateQuery(p uint8) (chan PinStateResponse, error) {
@ -438,31 +412,17 @@ func (c *Client) PinStateQuery(p uint8) (chan PinStateResponse, error) {
return nil, err return nil, err
} }
resp := c.parsePinStateQuery(future)
return resp, nil
}
func (c *Client) parsePinStateQuery(future chan []byte) chan PinStateResponse {
resp := make(chan PinStateResponse, 1) resp := make(chan PinStateResponse, 1)
go func() { go func() {
data := <-future data := <-future
var ps = PinStateResponse{ var response = ParsePinStateResponse(data)
Pin: data[0],
Mode: pinModeToFuncMap[data[1]],
State: 0,
}
for i, b := range data[2:] { resp <- response
ps.State |= int(b << (i * 7))
}
resp <- ps
close(resp) close(resp)
}() }()
return resp return resp, nil
} }
func (c *Client) ReportFirmware() (chan FirmwareReport, error) { func (c *Client) ReportFirmware() (chan FirmwareReport, error) {
@ -471,27 +431,28 @@ func (c *Client) ReportFirmware() (chan FirmwareReport, error) {
return nil, err return nil, err
} }
resp := c.parseReportFirmware(future)
return resp, nil
}
func (c *Client) parseReportFirmware(future chan []byte) chan FirmwareReport {
resp := make(chan FirmwareReport, 1) resp := make(chan FirmwareReport, 1)
go func() { go func() {
data := <-future data := <-future
var rc = FirmwareReport{ var response = ParseFirmwareReport(data)
Major: data[0],
Minor: data[1],
Name: data[2:],
}
resp <- rc resp <- response
close(resp) close(resp)
}() }()
return resp return resp, nil
}
func (c *Client) ExtendedReportAnalogPin(p uint8, value uint8) error {
lsb, msb := ByteToTwoByte(value)
_, err := c.SendSysEx(SysExExtendedAnalog, p, lsb, msb)
if err != nil {
return err
}
return nil
} }
func (c *Client) SetAnalogPinReporting(analogPin uint8, report bool) error { func (c *Client) SetAnalogPinReporting(analogPin uint8, report bool) error {
@ -523,7 +484,7 @@ func (c *Client) SetSamplingInterval(ms uint16) error {
return c.write([]byte{byte(SysExSamplingInterval), byte(ms), byte(ms >> 7)}, nil) return c.write([]byte{byte(SysExSamplingInterval), byte(ms), byte(ms >> 7)}, nil)
} }
// This function only supports 7-bit I2C addresses // WriteI2CData only supports 7-bit I2C addresses
func (c *Client) WriteI2CData(address uint8, restart bool, data []uint8) error { func (c *Client) WriteI2CData(address uint8, restart bool, data []uint8) error {
if !c.i2cStarted { if !c.i2cStarted {
return ErrI2CNotEnabled return ErrI2CNotEnabled
@ -539,14 +500,14 @@ func (c *Client) WriteI2CData(address uint8, restart bool, data []uint8) error {
return err return err
} }
// This function only supports 7-bit I2C addresses // ReadI2CData only supports 7-bit I2C addresses
func (c *Client) ReadI2CData(address uint8, restart bool, length uint16) error { func (c *Client) ReadI2CData(address uint8, restart bool, length uint16) error {
if !c.i2cStarted { if !c.i2cStarted {
return ErrI2CNotEnabled return ErrI2CNotEnabled
} }
if length > MaxUInt16 { if length > MaxUInt16 {
return fmt.Errorf("%w: 0x0 - 0xFFFFFFFFFFFFFF", ErrValueOutOfRange) return fmt.Errorf("%w: 0x0 - 0x%X", ErrValueOutOfRange, MaxUInt16)
} }
byte2 := byte(I2CModeRead) byte2 := byte(I2CModeRead)
@ -560,14 +521,14 @@ func (c *Client) ReadI2CData(address uint8, restart bool, length uint16) error {
return err return err
} }
// This function only supports 7-bit I2C addresses // ReadI2CRegister only supports 7-bit I2C addresses
func (c *Client) ReadI2CRegister(address uint8, restart bool, register uint8, length uint16) error { func (c *Client) ReadI2CRegister(address uint8, restart bool, register uint8, length uint16) error {
if !c.i2cStarted { if !c.i2cStarted {
return ErrI2CNotEnabled return ErrI2CNotEnabled
} }
if length > MaxUInt16 { if length > MaxUInt16 {
return fmt.Errorf("%w: 0x0 - 0xFFFFFFFFFFFFFF", ErrValueOutOfRange) return fmt.Errorf("%w: 0x0 - 0x%X", ErrValueOutOfRange, MaxUInt16)
} }
byte2 := byte(I2CModeRead) byte2 := byte(I2CModeRead)
@ -599,7 +560,7 @@ func (c *Client) releaseI2CAddressListener(addr uint8) {
delete(c.i2cListeners, addr) delete(c.i2cListeners, addr)
} }
// This function only supports 7-bit I2C addresses // SetI2CAddressListener only supports 7-bit I2C addresses
func (c *Client) SetI2CAddressListener(addr uint8, ch chan I2CPacket) (release func(), err error) { func (c *Client) SetI2CAddressListener(addr uint8, ch chan I2CPacket) (release func(), err error) {
c.i2cMU.Lock() c.i2cMU.Lock()
defer c.i2cMU.Unlock() defer c.i2cMU.Unlock()

@ -1,216 +0,0 @@
package firmatareg
import (
"errors"
"strconv"
"strings"
"sync"
"periph.io/x/devices/v3/firmata"
)
// Opener opens a handle to a firmata device.
//
// It is provided by the actual device driver.
type Opener func() (firmata.ClientI, error)
// Ref references a Firmata device.
//
// It is returned by All() to enumerate all registered devices.
type Ref struct {
// Name of the device.
//
// It must be unique across the host.
Name string
// Aliases are the alternative names that can be used to reference this bus.
Aliases []string
// Open is the factory to open a handle to this Firmata device.
Open Opener
}
// Open opens a firmata device by its name, an alias or its file path and returns
// a handle to it.
//
// Specify the empty string "" to get the first available device. This is the
// recommended default value unless an application knows the exact device to use.
//
// Each device can register multiple aliases, each leading to the same device handle.
//
// "file path" is a generic concept that is highly dependent on the platform
// and OS. Depending on the OS, a serial port can be `/dev/tty*` or `COM*`.
func Open(name string) (firmata.ClientI, error) {
var r *Ref
var err error
func() {
mu.Lock()
defer mu.Unlock()
if len(byName) == 0 {
err = errors.New("firmatareg: no device found; did you forget to call Init()")
return
}
if len(name) == 0 {
r = getDefault()
return
}
// Try by name, by alias, by number.
if r = byName[name]; r == nil {
r = byAlias[name]
}
}()
if err != nil {
return nil, err
}
if r == nil {
return nil, errors.New("firmatareg: can't open unknown device: " + strconv.Quote(name))
}
return r.Open()
}
// All returns a copy of all the registered references to all know firmata devices
// available on this host.
//
// The list is sorted by the bus name.
func All() []*Ref {
mu.Lock()
defer mu.Unlock()
out := make([]*Ref, 0, len(byName))
for _, v := range byName {
r := &Ref{Name: v.Name, Aliases: make([]string, len(v.Aliases)), Open: v.Open}
copy(r.Aliases, v.Aliases)
out = insertRef(out, r)
}
return out
}
// Register registers a firmata device.
//
// Registering the same device name twice is an error, e.g. o.Name().
func Register(name string, aliases []string, o Opener) error {
if len(name) == 0 {
return errors.New("firmatareg: can't register a bus with no name")
}
if o == nil {
return errors.New("firmatareg: can't register bus " + strconv.Quote(name) + " with nil Opener")
}
if _, err := strconv.Atoi(name); err == nil {
return errors.New("firmatareg: can't register bus " + strconv.Quote(name) + " with name being only a number")
}
if strings.Contains(name, ":") {
return errors.New("firmatareg: can't register bus " + strconv.Quote(name) + " with name containing ':'")
}
for _, alias := range aliases {
if len(alias) == 0 {
return errors.New("firmatareg: can't register bus " + strconv.Quote(name) + " with an empty alias")
}
if name == alias {
return errors.New("firmatareg: can't register bus " + strconv.Quote(name) + " with an alias the same as the bus name")
}
if _, err := strconv.Atoi(alias); err == nil {
return errors.New("firmatareg: can't register bus " + strconv.Quote(name) + " with an alias that is a number: " + strconv.Quote(alias))
}
if strings.Contains(alias, ":") {
return errors.New("firmatareg: can't register bus " + strconv.Quote(name) + " with an alias containing ':': " + strconv.Quote(alias))
}
}
mu.Lock()
defer mu.Unlock()
if _, ok := byName[name]; ok {
return errors.New("firmatareg: can't register bus " + strconv.Quote(name) + " twice")
}
if _, ok := byAlias[name]; ok {
return errors.New("firmatareg: can't register bus " + strconv.Quote(name) + " twice; it is already an alias")
}
for _, alias := range aliases {
if _, ok := byName[alias]; ok {
return errors.New("firmatareg: can't register bus " + strconv.Quote(name) + " twice; alias " + strconv.Quote(alias) + " is already a bus")
}
if _, ok := byAlias[alias]; ok {
return errors.New("firmatareg: can't register bus " + strconv.Quote(name) + " twice; alias " + strconv.Quote(alias) + " is already an alias")
}
}
r := &Ref{Name: name, Aliases: make([]string, len(aliases)), Open: o}
copy(r.Aliases, aliases)
byName[name] = r
for _, alias := range aliases {
byAlias[alias] = r
}
return nil
}
// Unregister removes a previously registered firmata device.
//
// This can happen when a firmata device is exposed via a USB device and the device
// is unplugged.
func Unregister(name string) error {
mu.Lock()
defer mu.Unlock()
r := byName[name]
if r == nil {
return errors.New("firmatareg: can't unregister unknown bus name " + strconv.Quote(name))
}
delete(byName, name)
for _, alias := range r.Aliases {
delete(byAlias, alias)
}
return nil
}
//
var (
mu sync.Mutex
byName = map[string]*Ref{}
// Caches
byNumber = map[int]*Ref{}
byAlias = map[string]*Ref{}
)
// getDefault returns the Ref that should be used as the default bus.
func getDefault() *Ref {
var o *Ref
if len(byNumber) == 0 {
// Fallback to use byName using a lexical sort.
name := ""
for n, o2 := range byName {
if len(name) == 0 || n < name {
o = o2
name = n
}
}
return o
}
number := int((^uint(0)) >> 1)
for n, o2 := range byNumber {
if number > n {
number = n
o = o2
}
}
return o
}
func insertRef(l []*Ref, r *Ref) []*Ref {
n := r.Name
i := search(len(l), func(i int) bool { return l[i].Name > n })
l = append(l, nil)
copy(l[i+1:], l[i:])
l[i] = r
return l
}
// search implements the same algorithm as sort.Search().
//
// It was extracted to not depend on sort, which depends on reflect.
func search(n int, f func(int) bool) int {
lo := 0
for hi := n; lo < hi; {
if i := int(uint(lo+hi) >> 1); !f(i) {
lo = i + 1
} else {
hi = i
}
}
return lo
}

@ -10,6 +10,14 @@ type FirmwareReport struct {
Name []byte Name []byte
} }
func ParseFirmwareReport(data []byte) FirmwareReport {
return FirmwareReport{
Major: data[0],
Minor: data[1],
Name: data[2:],
}
}
func (r FirmwareReport) String() string { func (r FirmwareReport) String() string {
return fmt.Sprintf("%s [%d.%d]", TwoByteString(r.Name), r.Major, r.Minor) return fmt.Sprintf("%s [%d.%d]", TwoByteString(r.Name), r.Major, r.Minor)
} }

@ -173,13 +173,17 @@ func (p *Pin) DefaultPull() gpio.Pull {
} }
func (p *Pin) Out(l gpio.Level) error { func (p *Pin) Out(l gpio.Level) error {
// TODO: // No need to set mode as firmata does so automatically
panic("implement me") return p.c.SetDigitalPinValue(p.pin, l)
} }
const dutyMax gpio.Duty = 1<<8 - 1
// PWM ignores physic.Frequency as there is no way to set it through firmata
func (p *Pin) PWM(duty gpio.Duty, f physic.Frequency) error { func (p *Pin) PWM(duty gpio.Duty, f physic.Frequency) error {
// TODO: // No need to set mode as firmata does so automatically
panic("implement me") // PWM duty scaled down from 24 to 8 bits
return p.c.ExtendedReportAnalogPin(p.pin, uint8((duty>>16)&0xFF))
} }
func (p *Pin) Func() pin.Func { func (p *Pin) Func() pin.Func {

@ -12,6 +12,20 @@ type PinStateResponse struct {
State int State int
} }
func ParsePinStateResponse(data []byte) PinStateResponse {
var response = PinStateResponse{
Pin: data[0],
Mode: pinModeToFuncMap[data[1]],
State: 0,
}
for i, b := range data[2:] {
response.State |= int(b << (i * 7))
}
return response
}
func (p PinStateResponse) String() string { func (p PinStateResponse) String() string {
return fmt.Sprintf("pin(%d) mode(%s) state(%d)", p.Pin, p.Mode, p.State) return fmt.Sprintf("pin(%d) mode(%s) state(%d)", p.Pin, p.Mode, p.State)
} }

@ -96,7 +96,7 @@ func (p *pin) Read() gpio.Level {
} }
func (p *pin) WaitForEdge(timeout time.Duration) bool { func (p *pin) WaitForEdge(timeout time.Duration) bool {
return false return gpio.INVALID.WaitForEdge(timeout)
} }
func (p *pin) Pull() gpio.Pull { func (p *pin) Pull() gpio.Pull {

Loading…
Cancel
Save