diff --git a/platforms/firmata/firmata.go b/platforms/firmata/firmata.go index 5954ad45..9f3a3bde 100644 --- a/platforms/firmata/firmata.go +++ b/platforms/firmata/firmata.go @@ -6,142 +6,130 @@ import ( "io" "math" "time" + + "github.com/hybridgroup/gobot" ) const ( - Open byte = 1 - Close byte = 0 - Input byte = 0x00 - Output byte = 0x01 - Analog byte = 0x02 - PWM byte = 0x03 - Servo byte = 0x04 - Low byte = 0 - High byte = 1 - ReportVersion byte = 0xF9 - SystemReset byte = 0xFF - DigitalMessage byte = 0x90 - DigitalMessageRangeStart byte = 0x90 - DigitalMessageRangeEnd byte = 0x9F - AnalogMessage byte = 0xE0 - AnalogMessageRangeStart byte = 0xE0 - AnalogMessageRangeEnd byte = 0xEF - ReportAnalog byte = 0xC0 - ReportDigital byte = 0xD0 - PinMode byte = 0xF4 - StartSysex byte = 0xF0 - EndSysex byte = 0xF7 - CapabilityQuery byte = 0x6B - CapabilityResponse byte = 0x6C - PinStateQuery byte = 0x6D - PinStateResponse byte = 0x6E - AnalogMappingQuery byte = 0x69 - AnalogMappingResponse byte = 0x6A - StringData byte = 0x71 - I2CRequest byte = 0x76 - I2CReply byte = 0x77 - I2CConfig byte = 0x78 - FirmwareQuery byte = 0x79 - I2CModeWrite byte = 0x00 - I2CModeRead byte = 0x01 - I2CmodeContinuousRead byte = 0x02 - I2CModeStopReading byte = 0x03 + open byte = 1 + close byte = 0 + input byte = 0x00 + output byte = 0x01 + analog byte = 0x02 + pwm byte = 0x03 + servo byte = 0x04 + low byte = 0 + high byte = 1 + reportVersion byte = 0xF9 + systemReset byte = 0xFF + digitalMessage byte = 0x90 + digitalMessageRangeStart byte = 0x90 + digitalMessageRangeEnd byte = 0x9F + analogMessage byte = 0xE0 + analogMessageRangeStart byte = 0xE0 + analogMessageRangeEnd byte = 0xEF + reportAnalog byte = 0xC0 + reportDigital byte = 0xD0 + pinMode byte = 0xF4 + startSysex byte = 0xF0 + endSysex byte = 0xF7 + capabilityQuery byte = 0x6B + capabilityResponse byte = 0x6C + pinStateQuery byte = 0x6D + pinStateResponse byte = 0x6E + analogMappingQuery byte = 0x69 + analogMappingResponse byte = 0x6A + stringData byte = 0x71 + i2CRequest byte = 0x76 + i2CReply byte = 0x77 + i2CConfig byte = 0x78 + firmwareQuery byte = 0x79 + i2CModeWrite byte = 0x00 + i2CModeRead byte = 0x01 + i2CmodeContinuousRead byte = 0x02 + i2CModeStopReading byte = 0x03 ) type board struct { - Serial io.ReadWriteCloser - Pins []pin - AnalogPins []byte - FirmwareName string - MajorVersion byte - MinorVersion byte - Events []event - Connected bool + serial io.ReadWriteCloser + pins []pin + analogPins []byte + firmwareName string + majorVersion byte + minorVersion byte + connected bool + events map[string]*gobot.Event } type pin struct { - SupportedModes []byte - Mode byte - Value int - AnalogChannel byte -} - -type event struct { - Name string - Data []byte - I2cReply map[string][]byte + supportedModes []byte + mode byte + value int + analogChannel byte } func newBoard(sp io.ReadWriteCloser) *board { - board := new(board) - board.MajorVersion = 0 - board.MinorVersion = 0 - board.Serial = sp - board.FirmwareName = "" - board.Pins = []pin{} - board.AnalogPins = []byte{} - board.Connected = false - board.Events = []event{} + board := &board{ + majorVersion: 0, + minorVersion: 0, + serial: sp, + firmwareName: "", + pins: []pin{}, + analogPins: []byte{}, + connected: false, + events: make(map[string]*gobot.Event), + } + + for _, s := range []string{ + "firmware_query", + "capability_query", + "analog_mapping_query", + "report_version", + "i2c_reply", + "analog_mapping_query", + "string_data", + "firmware_query", + } { + board.events[s] = gobot.NewEvent() + } + return board } func (b *board) connect() { - if b.Connected == false { + if b.connected == false { b.initBoard() - b.Connected = true - go func() { - for { - b.queryReportVersion() - time.Sleep(50 * time.Millisecond) - b.readAndProcess() + for { + b.queryReportVersion() + <-time.After(1 * time.Second) + b.readAndProcess() + if b.connected == true { + break } - }() + } } } func (b *board) initBoard() { - for { - b.queryFirmware() - time.Sleep(50 * time.Millisecond) - b.readAndProcess() - if len(b.findEvents("firmware_query")) > 0 { - break - } - } - for { + gobot.On(b.events["firmware_query"], func(data interface{}) { + b.events["firmware_query"] = gobot.NewEvent() b.queryCapabilities() - time.Sleep(50 * time.Millisecond) - b.readAndProcess() - if len(b.findEvents("capability_query")) > 0 { - break - } - } - for { - b.queryAnalogMapping() - time.Sleep(50 * time.Millisecond) - b.readAndProcess() - if len(b.findEvents("analog_mapping_query")) > 0 { - break - } - } - b.togglePinReporting(0, High, ReportDigital) - time.Sleep(50 * time.Millisecond) - b.togglePinReporting(1, High, ReportDigital) - time.Sleep(50 * time.Millisecond) -} + }) -func (b *board) findEvents(name string) []event { - ret := []event{} - for key, val := range b.Events { - if val.Name == name { - ret = append(ret, val) - if len(b.Events) > key+1 { - b.Events = append(b.Events[:key], b.Events[key+1:]...) - } - } - } - return ret + gobot.On(b.events["capability_query"], func(data interface{}) { + b.events["capability_query"] = gobot.NewEvent() + b.queryAnalogMapping() + }) + + gobot.On(b.events["analog_mapping_query"], func(data interface{}) { + b.events["analog_mapping_query"] = gobot.NewEvent() + b.togglePinReporting(0, high, reportDigital) + <-time.After(50 * time.Millisecond) + b.togglePinReporting(1, high, reportDigital) + <-time.After(50 * time.Millisecond) + b.connected = true + }) } func (b *board) readAndProcess() { @@ -149,59 +137,59 @@ func (b *board) readAndProcess() { } func (b *board) reset() { - b.write([]byte{SystemReset}) + b.write([]byte{systemReset}) } func (b *board) setPinMode(pin byte, mode byte) { - b.Pins[pin].Mode = mode - b.write([]byte{PinMode, pin, mode}) + b.pins[pin].mode = mode + b.write([]byte{pinMode, pin, mode}) } func (b *board) digitalWrite(pin byte, value byte) { port := byte(math.Floor(float64(pin) / 8)) portValue := byte(0) - b.Pins[pin].Value = int(value) + b.pins[pin].value = int(value) for i := byte(0); i < 8; i++ { - if b.Pins[8*port+i].Value != 0 { + if b.pins[8*port+i].value != 0 { portValue = portValue | (1 << i) } } - b.write([]byte{DigitalMessage | port, portValue & 0x7F, (portValue >> 7) & 0x7F}) + b.write([]byte{digitalMessage | port, portValue & 0x7F, (portValue >> 7) & 0x7F}) } func (b *board) analogWrite(pin byte, value byte) { - b.Pins[pin].Value = int(value) - b.write([]byte{AnalogMessage | pin, value & 0x7F, (value >> 7) & 0x7F}) + b.pins[pin].value = int(value) + b.write([]byte{analogMessage | pin, value & 0x7F, (value >> 7) & 0x7F}) } func (b *board) version() string { - return fmt.Sprintf("%v.%v", b.MajorVersion, b.MinorVersion) + return fmt.Sprintf("%v.%v", b.majorVersion, b.minorVersion) } func (b *board) reportVersion() { - b.write([]byte{ReportVersion}) + b.write([]byte{reportVersion}) } func (b *board) queryFirmware() { - b.write([]byte{StartSysex, FirmwareQuery, EndSysex}) + b.write([]byte{startSysex, firmwareQuery, endSysex}) } func (b *board) queryPinState(pin byte) { - b.write([]byte{StartSysex, PinStateQuery, pin, EndSysex}) + b.write([]byte{startSysex, pinStateQuery, pin, endSysex}) } func (b *board) queryReportVersion() { - b.write([]byte{ReportVersion}) + b.write([]byte{reportVersion}) } func (b *board) queryCapabilities() { - b.write([]byte{StartSysex, CapabilityQuery, EndSysex}) + b.write([]byte{startSysex, capabilityQuery, endSysex}) } func (b *board) queryAnalogMapping() { - b.write([]byte{StartSysex, AnalogMappingQuery, EndSysex}) + b.write([]byte{startSysex, analogMappingQuery, endSysex}) } func (b *board) togglePinReporting(pin byte, state byte, mode byte) { @@ -209,37 +197,37 @@ func (b *board) togglePinReporting(pin byte, state byte, mode byte) { } func (b *board) i2cReadRequest(slaveAddress byte, numBytes uint) { - b.write([]byte{StartSysex, I2CRequest, slaveAddress, (I2CModeRead << 3), - byte(numBytes & 0x7F), byte(((numBytes >> 7) & 0x7F)), EndSysex}) + b.write([]byte{startSysex, i2CRequest, slaveAddress, (i2CModeRead << 3), + byte(numBytes & 0x7F), byte(((numBytes >> 7) & 0x7F)), endSysex}) } func (b *board) i2cWriteRequest(slaveAddress byte, data []byte) { - ret := []byte{StartSysex, I2CRequest, slaveAddress, (I2CModeWrite << 3)} + ret := []byte{startSysex, i2CRequest, slaveAddress, (i2CModeWrite << 3)} for _, val := range data { ret = append(ret, byte(val&0x7F)) ret = append(ret, byte((val>>7)&0x7F)) } - ret = append(ret, EndSysex) + ret = append(ret, endSysex) b.write(ret) } func (b *board) i2cConfig(data []byte) { - ret := []byte{StartSysex, I2CConfig} + ret := []byte{startSysex, i2CConfig} for _, val := range data { ret = append(ret, byte(val&0xFF)) ret = append(ret, byte((val>>8)&0xFF)) } - ret = append(ret, EndSysex) + ret = append(ret, endSysex) b.write(ret) } func (b *board) write(commands []byte) { - b.Serial.Write(commands[:]) + b.serial.Write(commands[:]) } func (b *board) read() []byte { buf := make([]byte, 1024) - b.Serial.Read(buf) + b.serial.Read(buf) return buf } @@ -251,27 +239,27 @@ func (b *board) process(data []byte) { break } switch { - case ReportVersion == messageType: - b.MajorVersion, _ = buf.ReadByte() - b.MinorVersion, _ = buf.ReadByte() - b.Events = append(b.Events, event{Name: "report_version"}) - case AnalogMessageRangeStart <= messageType && AnalogMessageRangeEnd >= messageType: + case reportVersion == messageType: + b.majorVersion, _ = buf.ReadByte() + b.minorVersion, _ = buf.ReadByte() + gobot.Publish(b.events["report_version"], b.version()) + case analogMessageRangeStart <= messageType && analogMessageRangeEnd >= messageType: leastSignificantByte, _ := buf.ReadByte() mostSignificantByte, _ := buf.ReadByte() value := uint(leastSignificantByte) | uint(mostSignificantByte)<<7 pin := (messageType & 0x0F) - b.Pins[b.AnalogPins[pin]].Value = int(value) - b.Events = append(b.Events, - event{Name: fmt.Sprintf("analog_read_%v", pin), - Data: []byte{ - byte(value >> 24), byte(value >> 16), byte(value >> 8), byte(value & 0xff), - }, + b.pins[b.analogPins[pin]].value = int(value) + gobot.Publish(b.events[fmt.Sprintf("analog_read_%v", pin)], + []byte{ + byte(value >> 24), + byte(value >> 16), + byte(value >> 8), + byte(value & 0xff), }, ) - - case DigitalMessageRangeStart <= messageType && DigitalMessageRangeEnd >= messageType: + case digitalMessageRangeStart <= messageType && digitalMessageRangeEnd >= messageType: port := messageType & 0x0F firstBitmask, _ := buf.ReadByte() secondBitmask, _ := buf.ReadByte() @@ -279,18 +267,14 @@ func (b *board) process(data []byte) { for i := 0; i < 8; i++ { pinNumber := (8*byte(port) + byte(i)) - pin := b.Pins[pinNumber] - if byte(pin.Mode) == Input { - pin.Value = int((portValue >> (byte(i) & 0x07)) & 0x01) - b.Events = append(b.Events, - event{Name: fmt.Sprintf("digital_read_%v", pinNumber), - Data: []byte{byte(pin.Value & 0xff)}, - }, - ) + pin := b.pins[pinNumber] + if byte(pin.mode) == input { + pin.value = int((portValue >> (byte(i) & 0x07)) & 0x01) + gobot.Publish(fmt.Sprintf("digital_read_%v", pinNumber), + []byte{byte(pin.value & 0xff)}) } } - - case StartSysex == messageType: + case startSysex == messageType: currentBuffer := []byte{messageType} for { b, err := buf.ReadByte() @@ -298,25 +282,26 @@ func (b *board) process(data []byte) { break } currentBuffer = append(currentBuffer, b) - if currentBuffer[len(currentBuffer)-1] == EndSysex { + if currentBuffer[len(currentBuffer)-1] == endSysex { break } } command := currentBuffer[1] switch command { - case CapabilityResponse: + case capabilityResponse: supportedModes := 0 n := 0 for _, val := range currentBuffer[2:(len(currentBuffer) - 5)] { if val == 127 { modes := []byte{} - for _, mode := range []byte{Input, Output, Analog, PWM, Servo} { + for _, mode := range []byte{input, output, analog, pwm, servo} { if (supportedModes & (1 << mode)) != 0 { modes = append(modes, mode) } } - b.Pins = append(b.Pins, pin{modes, Output, 0, 0}) + b.pins = append(b.pins, pin{modes, output, 0, 0}) + b.Events[fmt.Sprintf("digital_read_%v", len(b.pins)-1)] = gobot.NewEvent() supportedModes = 0 n = 0 continue @@ -327,42 +312,38 @@ func (b *board) process(data []byte) { } n ^= 1 } - b.Events = append(b.Events, event{Name: "capability_query"}) - - case AnalogMappingResponse: + gobot.Publish(b.events["capability_query"], nil) + case analogMappingResponse: pinIndex := byte(0) - for _, val := range currentBuffer[2 : len(b.Pins)-1] { + for _, val := range currentBuffer[2 : len(b.pins)-1] { - b.Pins[pinIndex].AnalogChannel = val + b.pins[pinIndex].analogChannel = val if val != 127 { - b.AnalogPins = append(b.AnalogPins, pinIndex) + b.analogPins = append(b.analogPins, pinIndex) } - + b.events[fmt.Sprintf("analog_read_%v", pinIndex)] = gobot.NewEvent() pinIndex++ } - b.Events = append(b.Events, event{Name: "analog_mapping_query"}) - - case PinStateResponse: - pin := b.Pins[currentBuffer[2]] - pin.Mode = currentBuffer[3] - pin.Value = int(currentBuffer[4]) + gobot.Publish(b.events["analog_mapping_query"], nil) + case pinStateResponse: + pin := b.pins[currentBuffer[2]] + pin.mode = currentBuffer[3] + pin.value = int(currentBuffer[4]) if len(currentBuffer) > 6 { - pin.Value = int(uint(pin.Value) | uint(currentBuffer[5])<<7) + pin.value = int(uint(pin.value) | uint(currentBuffer[5])<<7) } if len(currentBuffer) > 7 { - pin.Value = int(uint(pin.Value) | uint(currentBuffer[6])<<14) + pin.value = int(uint(pin.value) | uint(currentBuffer[6])<<14) } - b.Events = append(b.Events, - event{Name: fmt.Sprintf("pin_%v_state", currentBuffer[2]), - Data: []byte{byte(pin.Value & 0xff)}, - }, + gobot.Publish(b.events[fmt.Sprintf("pin_%v_state", currentBuffer[2])], + []byte{byte(pin.value & 0xff)}, ) - case I2CReply: + case i2CReply: i2cReply := map[string][]byte{ "slave_address": []byte{byte(currentBuffer[2]) | byte(currentBuffer[3])<<7}, "register": []byte{byte(currentBuffer[4]) | byte(currentBuffer[5])<<7}, @@ -379,21 +360,20 @@ func (b *board) process(data []byte) { byte(currentBuffer[i])|byte(currentBuffer[i+1])<<7, ) } - b.Events = append(b.Events, event{Name: "i2c_reply", I2cReply: i2cReply}) - - case FirmwareQuery: + gobo.Publish(b.events["i2c_reply"], i2cReply) + case firmwareQuery: name := []byte{} for _, val := range currentBuffer[4:(len(currentBuffer) - 1)] { if val != 0 { name = append(name, val) } } - b.FirmwareName = string(name[:]) - b.Events = append(b.Events, event{Name: "firmware_query"}) - case StringData: + b.firmwareName = string(name[:]) + gobot.Publish(b.events["firmware_query"], b.firmwareName) + case stringData: str := currentBuffer[2 : len(currentBuffer)-1] fmt.Println(string(str[:len(str)])) - b.Events = append(b.Events, event{Name: "string_data", Data: str}) + gobot.Publish(b.events["string_data"], str) default: fmt.Println("bad byte", fmt.Sprintf("0x%x", command)) } diff --git a/platforms/firmata/firmata_adaptor.go b/platforms/firmata/firmata_adaptor.go index 22b5b4cf..3e140bb7 100644 --- a/platforms/firmata/firmata_adaptor.go +++ b/platforms/firmata/firmata_adaptor.go @@ -10,7 +10,7 @@ import ( type FirmataAdaptor struct { gobot.Adaptor - Board *board + board *board i2cAddress byte connect func(*FirmataAdaptor) } @@ -27,20 +27,20 @@ func NewFirmataAdaptor(name, port string) *FirmataAdaptor { if err != nil { panic(err) } - f.Board = newBoard(sp) + f.board = newBoard(sp) }, } } func (f *FirmataAdaptor) Connect() bool { f.connect(f) - f.Board.connect() + f.board.connect() f.SetConnected(true) return true } func (f *FirmataAdaptor) Disconnect() bool { - err := f.Board.Serial.Close() + err := f.board.serial.Close() if err != nil { fmt.Println(err) } @@ -52,50 +52,54 @@ func (f *FirmataAdaptor) InitServo() {} func (f *FirmataAdaptor) ServoWrite(pin string, angle byte) { p, _ := strconv.Atoi(pin) - f.Board.setPinMode(byte(p), Servo) - f.Board.analogWrite(byte(p), angle) + f.board.setPinMode(byte(p), servo) + f.board.analogWrite(byte(p), angle) } func (f *FirmataAdaptor) PwmWrite(pin string, level byte) { p, _ := strconv.Atoi(pin) - f.Board.setPinMode(byte(p), PWM) - f.Board.analogWrite(byte(p), level) + f.board.setPinMode(byte(p), pwm) + f.board.analogWrite(byte(p), level) } func (f *FirmataAdaptor) DigitalWrite(pin string, level byte) { p, _ := strconv.Atoi(pin) - f.Board.setPinMode(byte(p), Output) - f.Board.digitalWrite(byte(p), level) + f.board.setPinMode(byte(p), output) + f.board.digitalWrite(byte(p), level) } func (f *FirmataAdaptor) DigitalRead(pin string) int { + ret := make(chan int) p, _ := strconv.Atoi(pin) - f.Board.setPinMode(byte(p), Input) - f.Board.togglePinReporting(byte(p), High, ReportDigital) - events := f.Board.findEvents(fmt.Sprintf("digital_read_%v", pin)) - if len(events) > 0 { - return int(events[len(events)-1].Data[0]) - } - return -1 + f.board.setPinMode(byte(p), input) + f.board.togglePinReporting(byte(p), high, reportDigital) + f.board.readAndProcess() + + gobot.On(f.board.events[fmt.Sprintf("digital_read_%v", pin)], func(data interface{}) { + ret <- int(data.([]byte)[0]) + }) + + return <-ret } // NOTE pins are numbered A0-A5, which translate to digital pins 14-19 func (f *FirmataAdaptor) AnalogRead(pin string) int { + ret := make(chan int) + p, _ := strconv.Atoi(pin) p = f.digitalPin(p) - f.Board.setPinMode(byte(p), Analog) - f.Board.togglePinReporting(byte(p), High, ReportAnalog) - events := f.Board.findEvents(fmt.Sprintf("analog_read_%v", pin)) - if len(events) > 0 { - event := events[len(events)-1] - return int(uint(event.Data[0])<<24 | - uint(event.Data[1])<<16 | - uint(event.Data[2])<<8 | - uint(event.Data[3])) - } - return -1 + f.board.setPinMode(byte(p), analog) + f.board.togglePinReporting(byte(p), high, reportAnalog) + f.board.readAndProcess() + + gobot.On(f.board.events[fmt.Sprintf("analog_read_%v", pin)], func(data interface{}) { + b := data.([]byte) + ret <- int(uint(b[0])<<24 | uint(b[1])<<16 | uint(b[2])<<8 | uint(b[3])) + }) + + return <-ret } func (f *FirmataAdaptor) AnalogWrite(pin string, level byte) { @@ -108,19 +112,22 @@ func (f *FirmataAdaptor) digitalPin(pin int) int { func (f *FirmataAdaptor) I2cStart(address byte) { f.i2cAddress = address - f.Board.i2cConfig([]byte{0}) + f.board.i2cConfig([]byte{0}) } func (f *FirmataAdaptor) I2cRead(size uint) []byte { - f.Board.i2cReadRequest(f.i2cAddress, size) + ret := make(chan []byte) + f.board.i2cReadRequest(f.i2cAddress, size) - events := f.Board.findEvents("i2c_reply") - if len(events) > 0 { - return events[len(events)-1].I2cReply["data"] - } - return make([]byte, 0) + f.board.readAndProcess() + + gobot.On(f.board.events["i2c_reply"], func(data interface{}) { + ret <- data.(i2CReply)["data"] + }) + + return <-ret } func (f *FirmataAdaptor) I2cWrite(data []byte) { - f.Board.i2cWriteRequest(f.i2cAddress, data) + f.board.i2cWriteRequest(f.i2cAddress, data) }