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"
)
// I2CAddr i2c default address.
const I2CAddr uint16 = 0x77
// Oversampling affects how much time is taken to measure each of temperature,
// pressure and humidity.
//

@ -10,6 +10,22 @@ type AnalogMappingResponse struct {
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 {
str := bytes.Buffer{}
for analogPin, digitalPin := range a.AnalogPinToDigital {

@ -33,6 +33,30 @@ type CapabilityResponse struct {
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 {
str := bytes.Buffer{}
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.
const (
MaxUInt8 uint8 = (1<<8 - 1) >> 1
MaxUInt16 uint16 = (1<<16 - 1) >> 2
MaxUInt8 uint8 = (1<<8 - 1) >> (8 / 8)
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{
@ -28,7 +30,7 @@ var commandResponseMap = map[SysExCmd]SysExCmd{
type ClientI interface {
SendSysEx(SysExCmd, ...byte) (chan []byte, error)
SendReset() error
ExtendedReportAnalogPin(uint8, int) error
ExtendedReportAnalogPin(uint8, uint8) error
CapabilityQuery() (chan CapabilityResponse, error)
PinStateQuery(uint8) (chan PinStateResponse, error)
ReportFirmware() (chan FirmwareReport, error)
@ -124,6 +126,10 @@ func (c *Client) Start() error {
c.started = true
c.mu.Unlock()
if err := c.SendReset(); err != nil {
return err
}
if b, ok := c.board.(flusher); ok {
b.Flush()
} 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.
firmChannel := make(chan []byte, 1)
c.mu.Lock()
c.responseChannels[SysExReportFirmware] = []chan []byte{firmChannel}
c.mu.Unlock()
report := c.parseReportFirmware(firmChannel)
responseChannel := make(chan FirmwareReport, 1)
go func() {
responseChannel <- ParseFirmwareReport(<-firmChannel)
}()
go func() {
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
}
@ -156,8 +181,6 @@ func (c *Client) write(payload []byte, withinMutex func()) error {
c.mu.Lock()
defer c.mu.Unlock()
//fmt.Println(SprintHexArray(payload))
// Write to the board.
_, err := c.board.Write(payload)
if err != nil {
@ -280,6 +303,8 @@ func (c *Client) responseWatcher() (err error) {
resp <- data
close(resp)
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))
default:
str := ""
@ -347,41 +372,18 @@ func (c *Client) CapabilityQuery() (chan CapabilityResponse, error) {
return nil, err
}
resp := c.parseCapabilityCommand(future)
return resp, nil
}
func (c *Client) parseCapabilityCommand(future chan []byte) chan CapabilityResponse {
resp := make(chan CapabilityResponse, 1)
go func() {
data := <-future
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
}
}
var response = ParseCapabilityResponse(data)
c.cr = response
resp <- response
close(resp)
}()
return resp
return resp, nil
}
func (c *Client) SendAnalogMappingQuery() (chan AnalogMappingResponse, error) {
@ -390,46 +392,18 @@ func (c *Client) SendAnalogMappingQuery() (chan AnalogMappingResponse, error) {
return nil, err
}
resp := c.parseAnalogMappingQuery(future)
return resp, nil
}
func (c *Client) parseAnalogMappingQuery(future chan []byte) chan AnalogMappingResponse {
resp := make(chan AnalogMappingResponse, 1)
go func() {
data := <-future
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))
}
}
var response = ParseAnalogMappingResponse(data)
c.amr = response
resp <- response
close(resp)
}()
return resp
}
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
return resp, nil
}
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
}
resp := c.parsePinStateQuery(future)
return resp, nil
}
func (c *Client) parsePinStateQuery(future chan []byte) chan PinStateResponse {
resp := make(chan PinStateResponse, 1)
go func() {
data := <-future
var ps = PinStateResponse{
Pin: data[0],
Mode: pinModeToFuncMap[data[1]],
State: 0,
}
var response = ParsePinStateResponse(data)
for i, b := range data[2:] {
ps.State |= int(b << (i * 7))
}
resp <- ps
resp <- response
close(resp)
}()
return resp
return resp, nil
}
func (c *Client) ReportFirmware() (chan FirmwareReport, error) {
@ -471,27 +431,28 @@ func (c *Client) ReportFirmware() (chan FirmwareReport, error) {
return nil, err
}
resp := c.parseReportFirmware(future)
return resp, nil
}
func (c *Client) parseReportFirmware(future chan []byte) chan FirmwareReport {
resp := make(chan FirmwareReport, 1)
go func() {
data := <-future
var rc = FirmwareReport{
Major: data[0],
Minor: data[1],
Name: data[2:],
}
var response = ParseFirmwareReport(data)
resp <- rc
resp <- response
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 {
@ -523,7 +484,7 @@ func (c *Client) SetSamplingInterval(ms uint16) error {
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 {
if !c.i2cStarted {
return ErrI2CNotEnabled
@ -539,14 +500,14 @@ func (c *Client) WriteI2CData(address uint8, restart bool, data []uint8) error {
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 {
if !c.i2cStarted {
return ErrI2CNotEnabled
}
if length > MaxUInt16 {
return fmt.Errorf("%w: 0x0 - 0xFFFFFFFFFFFFFF", ErrValueOutOfRange)
return fmt.Errorf("%w: 0x0 - 0x%X", ErrValueOutOfRange, MaxUInt16)
}
byte2 := byte(I2CModeRead)
@ -560,14 +521,14 @@ func (c *Client) ReadI2CData(address uint8, restart bool, length uint16) error {
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 {
if !c.i2cStarted {
return ErrI2CNotEnabled
}
if length > MaxUInt16 {
return fmt.Errorf("%w: 0x0 - 0xFFFFFFFFFFFFFF", ErrValueOutOfRange)
return fmt.Errorf("%w: 0x0 - 0x%X", ErrValueOutOfRange, MaxUInt16)
}
byte2 := byte(I2CModeRead)
@ -599,7 +560,7 @@ func (c *Client) releaseI2CAddressListener(addr uint8) {
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) {
c.i2cMU.Lock()
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
}
func ParseFirmwareReport(data []byte) FirmwareReport {
return FirmwareReport{
Major: data[0],
Minor: data[1],
Name: data[2:],
}
}
func (r FirmwareReport) String() string {
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 {
// TODO:
panic("implement me")
// No need to set mode as firmata does so automatically
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 {
// TODO:
panic("implement me")
// No need to set mode as firmata does so automatically
// 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 {

@ -12,6 +12,20 @@ type PinStateResponse struct {
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 {
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 {
return false
return gpio.INVALID.WaitForEdge(timeout)
}
func (p *pin) Pull() gpio.Pull {

Loading…
Cancel
Save