package tinkerboard import ( "errors" "fmt" multierror "github.com/hashicorp/go-multierror" "gobot.io/x/gobot" "gobot.io/x/gobot/drivers/i2c" "gobot.io/x/gobot/sysfs" ) type sysfsPin struct { pin int pwmPin int } // Adaptor represents a Gobot Adaptor for the ASUS Tinker Board type Adaptor struct { name string pinmap map[string]sysfsPin digitalPins map[int]sysfs.DigitalPin pwmPins map[int]*sysfs.PWMPin i2cBuses [2]sysfs.I2cDevice } // NewAdaptor creates a Tinkerboard Adaptor func NewAdaptor() *Adaptor { c := &Adaptor{ name: gobot.DefaultName("Tinker Board"), digitalPins: make(map[int]sysfs.DigitalPin), } c.setPins() return c } // Name returns the name of the Adaptor func (c *Adaptor) Name() string { return c.name } // SetName sets the name of the Adaptor func (c *Adaptor) SetName(n string) { c.name = n } // Connect initializes the board func (c *Adaptor) Connect() (err error) { c.pwmPins = make(map[int]*sysfs.PWMPin) return nil } // Finalize closes connection to board and pins func (c *Adaptor) Finalize() (err error) { for _, pin := range c.digitalPins { if pin != nil { if e := pin.Unexport(); e != nil { err = multierror.Append(err, e) } } } for _, pin := range c.pwmPins { if pin != nil { if errs := pin.Enable(false); errs != nil { err = multierror.Append(err, errs) } if errs := pin.Unexport(); errs != nil { err = multierror.Append(err, errs) } } } for _, bus := range c.i2cBuses { if bus != nil { if e := bus.Close(); e != nil { err = multierror.Append(err, e) } } } return } // DigitalRead reads digital value from the specified pin. func (c *Adaptor) DigitalRead(pin string) (val int, err error) { sysfsPin, err := c.digitalPin(pin, sysfs.IN) if err != nil { return } return sysfsPin.Read() } // DigitalWrite writes digital value to the specified pin. func (c *Adaptor) DigitalWrite(pin string, val byte) (err error) { sysfsPin, err := c.digitalPin(pin, sysfs.OUT) if err != nil { return err } return sysfsPin.Write(int(val)) } // PwmWrite writes a PWM signal to the specified pin func (c *Adaptor) PwmWrite(pin string, val byte) (err error) { pwmPin, err := c.pwmPin(pin) if err != nil { return } period, err := pwmPin.Period() if err != nil { return err } duty := gobot.FromScale(float64(val), 0, 255.0) return pwmPin.SetDutyCycle(uint32(float64(period) * duty)) } // TODO: take into account the actual period setting, not just assume default const pwmPeriod = 10000000 // ServoWrite writes a servo signal to the specified pin func (c *Adaptor) ServoWrite(pin string, angle byte) (err error) { pwmPin, err := c.pwmPin(pin) if err != nil { return } // 0.5 ms => -90 // 1.5 ms => 0 // 2.0 ms => 90 const minDuty = 100 * 0.0005 * pwmPeriod const maxDuty = 100 * 0.0020 * pwmPeriod duty := uint32(gobot.ToScale(gobot.FromScale(float64(angle), 0, 180), minDuty, maxDuty)) return pwmPin.SetDutyCycle(duty) } // GetConnection returns a connection to a device on a specified bus. // Valid bus number is [0..1] which corresponds to /dev/i2c-0 through /dev/i2c-1. func (c *Adaptor) GetConnection(address int, bus int) (connection i2c.Connection, err error) { if (bus < 0) || (bus > 1) { return nil, fmt.Errorf("Bus number %d out of range", bus) } if c.i2cBuses[bus] == nil { c.i2cBuses[bus], err = sysfs.NewI2cDevice(fmt.Sprintf("/dev/i2c-%d", bus)) } return i2c.NewConnection(c.i2cBuses[bus], address), err } // GetDefaultBus returns the default i2c bus for this platform func (c *Adaptor) GetDefaultBus() int { return 1 } func (c *Adaptor) setPins() { c.pinmap = fixedPins } func (c *Adaptor) translatePin(pin string) (i int, err error) { if val, ok := c.pinmap[pin]; ok { i = val.pin } else { err = errors.New("Not a valid pin") } return } // digitalPin returns matched digitalPin for specified values func (c *Adaptor) digitalPin(pin string, dir string) (sysfsPin sysfs.DigitalPin, err error) { i, err := c.translatePin(pin) if err != nil { return } if c.digitalPins[i] == nil { c.digitalPins[i] = sysfs.NewDigitalPin(i) if err = c.digitalPins[i].Export(); err != nil { return } } if err = c.digitalPins[i].Direction(dir); err != nil { return } return c.digitalPins[i], nil } // pwmPin returns matched pwmPin for specified pin number func (c *Adaptor) pwmPin(pin string) (sysfsPin *sysfs.PWMPin, err error) { sysPin := c.pinmap[pin] if sysPin.pwmPin != -1 { if c.pwmPins[sysPin.pwmPin] == nil { newPin := sysfs.NewPWMPin(sysPin.pwmPin) if err = newPin.Export(); err != nil { return } if err = newPin.Enable(true); err != nil { return } if err = newPin.SetPeriod(10000000); err != nil { return } if err = newPin.InvertPolarity(false); err != nil { return } c.pwmPins[sysPin.pwmPin] = newPin } sysfsPin = c.pwmPins[sysPin.pwmPin] return } err = errors.New("Not a PWM pin") return }