gobot.io/x/gobot@v1.16.0/platforms/tinkerboard/adaptor.go (about) 1 package tinkerboard 2 3 import ( 4 "errors" 5 "fmt" 6 "sync" 7 8 multierror "github.com/hashicorp/go-multierror" 9 "gobot.io/x/gobot" 10 "gobot.io/x/gobot/drivers/i2c" 11 "gobot.io/x/gobot/sysfs" 12 ) 13 14 type sysfsPin struct { 15 pin int 16 pwmPin int 17 } 18 19 // Adaptor represents a Gobot Adaptor for the ASUS Tinker Board 20 type Adaptor struct { 21 name string 22 pinmap map[string]sysfsPin 23 digitalPins map[int]*sysfs.DigitalPin 24 pwmPins map[int]*sysfs.PWMPin 25 i2cBuses [5]i2c.I2cDevice 26 mutex *sync.Mutex 27 } 28 29 // NewAdaptor creates a Tinkerboard Adaptor 30 func NewAdaptor() *Adaptor { 31 c := &Adaptor{ 32 name: gobot.DefaultName("Tinker Board"), 33 mutex: &sync.Mutex{}, 34 } 35 36 c.setPins() 37 return c 38 } 39 40 // Name returns the name of the Adaptor 41 func (c *Adaptor) Name() string { return c.name } 42 43 // SetName sets the name of the Adaptor 44 func (c *Adaptor) SetName(n string) { c.name = n } 45 46 // Connect initializes the board 47 func (c *Adaptor) Connect() (err error) { 48 return nil 49 } 50 51 // Finalize closes connection to board and pins 52 func (c *Adaptor) Finalize() (err error) { 53 c.mutex.Lock() 54 defer c.mutex.Unlock() 55 56 for _, pin := range c.digitalPins { 57 if pin != nil { 58 if e := pin.Unexport(); e != nil { 59 err = multierror.Append(err, e) 60 } 61 } 62 } 63 for _, pin := range c.pwmPins { 64 if pin != nil { 65 if errs := pin.Enable(false); errs != nil { 66 err = multierror.Append(err, errs) 67 } 68 if errs := pin.Unexport(); errs != nil { 69 err = multierror.Append(err, errs) 70 } 71 } 72 } 73 for _, bus := range c.i2cBuses { 74 if bus != nil { 75 if e := bus.Close(); e != nil { 76 err = multierror.Append(err, e) 77 } 78 } 79 } 80 return 81 } 82 83 // DigitalRead reads digital value from the specified pin. 84 func (c *Adaptor) DigitalRead(pin string) (val int, err error) { 85 sysfsPin, err := c.DigitalPin(pin, sysfs.IN) 86 if err != nil { 87 return 88 } 89 return sysfsPin.Read() 90 } 91 92 // DigitalWrite writes digital value to the specified pin. 93 func (c *Adaptor) DigitalWrite(pin string, val byte) (err error) { 94 sysfsPin, err := c.DigitalPin(pin, sysfs.OUT) 95 if err != nil { 96 return err 97 } 98 return sysfsPin.Write(int(val)) 99 } 100 101 // PwmWrite writes a PWM signal to the specified pin 102 func (c *Adaptor) PwmWrite(pin string, val byte) (err error) { 103 pwmPin, err := c.PWMPin(pin) 104 if err != nil { 105 return 106 } 107 period, err := pwmPin.Period() 108 if err != nil { 109 return err 110 } 111 duty := gobot.FromScale(float64(val), 0, 255.0) 112 return pwmPin.SetDutyCycle(uint32(float64(period) * duty)) 113 } 114 115 // TODO: take into account the actual period setting, not just assume default 116 const pwmPeriod = 10000000 117 118 // ServoWrite writes a servo signal to the specified pin 119 func (c *Adaptor) ServoWrite(pin string, angle byte) (err error) { 120 pwmPin, err := c.PWMPin(pin) 121 if err != nil { 122 return 123 } 124 125 // 0.5 ms => -90 126 // 1.5 ms => 0 127 // 2.0 ms => 90 128 const minDuty = 100 * 0.0005 * pwmPeriod 129 const maxDuty = 100 * 0.0020 * pwmPeriod 130 duty := uint32(gobot.ToScale(gobot.FromScale(float64(angle), 0, 180), minDuty, maxDuty)) 131 return pwmPin.SetDutyCycle(duty) 132 } 133 134 // DigitalPin returns matched digitalPin for specified values 135 func (c *Adaptor) DigitalPin(pin string, dir string) (sysfsPin sysfs.DigitalPinner, err error) { 136 c.mutex.Lock() 137 defer c.mutex.Unlock() 138 139 i, err := c.translatePin(pin) 140 141 if err != nil { 142 return 143 } 144 145 if c.digitalPins[i] == nil { 146 c.digitalPins[i] = sysfs.NewDigitalPin(i) 147 if err = c.digitalPins[i].Export(); err != nil { 148 return 149 } 150 } 151 152 if err = c.digitalPins[i].Direction(dir); err != nil { 153 return 154 } 155 156 return c.digitalPins[i], nil 157 } 158 159 // PWMPin returns matched pwmPin for specified pin number 160 func (c *Adaptor) PWMPin(pin string) (sysfsPin sysfs.PWMPinner, err error) { 161 c.mutex.Lock() 162 defer c.mutex.Unlock() 163 164 i, err := c.translatePwmPin(pin) 165 if err != nil { 166 return nil, err 167 } 168 if i == -1 { 169 return nil, errors.New("Not a PWM pin") 170 } 171 172 if c.pwmPins[i] == nil { 173 newPin := sysfs.NewPWMPin(i) 174 if err = newPin.Export(); err != nil { 175 return 176 } 177 // Make sure pwm is disabled when setting polarity 178 if err = newPin.Enable(false); err != nil { 179 return 180 } 181 if err = newPin.InvertPolarity(false); err != nil { 182 return 183 } 184 if err = newPin.Enable(true); err != nil { 185 return 186 } 187 if err = newPin.SetPeriod(10000000); err != nil { 188 return 189 } 190 c.pwmPins[i] = newPin 191 } 192 193 sysfsPin = c.pwmPins[i] 194 return 195 } 196 197 // GetConnection returns a connection to a device on a specified bus. 198 // Valid bus number is [0..4] which corresponds to /dev/i2c-0 through /dev/i2c-4. 199 // We don't support "/dev/i2c-6 DesignWare HDMI". 200 func (c *Adaptor) GetConnection(address int, bus int) (connection i2c.Connection, err error) { 201 c.mutex.Lock() 202 defer c.mutex.Unlock() 203 204 if (bus < 0) || (bus > 4) { 205 return nil, fmt.Errorf("Bus number %d out of range", bus) 206 } 207 if c.i2cBuses[bus] == nil { 208 c.i2cBuses[bus], err = sysfs.NewI2cDevice(fmt.Sprintf("/dev/i2c-%d", bus)) 209 } 210 return i2c.NewConnection(c.i2cBuses[bus], address), err 211 } 212 213 // GetDefaultBus returns the default i2c bus for this platform 214 func (c *Adaptor) GetDefaultBus() int { 215 return 1 216 } 217 218 func (c *Adaptor) setPins() { 219 c.digitalPins = make(map[int]*sysfs.DigitalPin) 220 c.pwmPins = make(map[int]*sysfs.PWMPin) 221 c.pinmap = fixedPins 222 } 223 224 func (c *Adaptor) translatePin(pin string) (i int, err error) { 225 if val, ok := c.pinmap[pin]; ok { 226 i = val.pin 227 } else { 228 err = errors.New("Not a valid pin") 229 } 230 return 231 } 232 233 func (c *Adaptor) translatePwmPin(pin string) (i int, err error) { 234 if val, ok := c.pinmap[pin]; ok { 235 i = val.pwmPin 236 } else { 237 err = errors.New("Not a valid pin") 238 } 239 return 240 }