diff --git a/examples/raspi_blink.go b/examples/raspi_blink.go new file mode 100644 index 00000000..95e8b71a --- /dev/null +++ b/examples/raspi_blink.go @@ -0,0 +1,32 @@ +package main + +import ( + "time" + + "github.com/hybridgroup/gobot" + "github.com/hybridgroup/gobot/platforms/gpio" + "github.com/hybridgroup/gobot/platforms/raspi" +) + +func main() { + gbot := gobot.NewGobot() + + r := raspi.NewRaspiAdaptor("raspi") + led := gpio.NewLedDriver(r, "led", "7") + + work := func() { + gobot.Every(1*time.Second, func() { + led.Toggle() + }) + } + + robot := gobot.NewRobot("blinkBot", + []gobot.Connection{r}, + []gobot.Device{led}, + work, + ) + + gbot.AddRobot(robot) + + gbot.Start() +} diff --git a/examples/raspi_blinkm.go b/examples/raspi_blinkm.go new file mode 100644 index 00000000..0c1fa6c3 --- /dev/null +++ b/examples/raspi_blinkm.go @@ -0,0 +1,36 @@ +package main + +import ( + "fmt" + "time" + + "github.com/hybridgroup/gobot" + "github.com/hybridgroup/gobot/platforms/i2c" + "github.com/hybridgroup/gobot/platforms/raspi" +) + +func main() { + gbot := gobot.NewGobot() + r := raspi.NewRaspiAdaptor("raspi") + blinkm := i2c.NewBlinkMDriver(r, "blinkm") + + work := func() { + gobot.Every(3*time.Second, func() { + r := byte(gobot.Rand(255)) + g := byte(gobot.Rand(255)) + b := byte(gobot.Rand(255)) + blinkm.Rgb(r, g, b) + fmt.Println("color", blinkm.Color()) + }) + } + + robot := gobot.NewRobot("blinkmBot", + []gobot.Connection{r}, + []gobot.Device{blinkm}, + work, + ) + + gbot.AddRobot(robot) + + gbot.Start() +} diff --git a/examples/raspi_button.go b/examples/raspi_button.go new file mode 100644 index 00000000..6f196a20 --- /dev/null +++ b/examples/raspi_button.go @@ -0,0 +1,37 @@ +package main + +import ( + "fmt" + + "github.com/hybridgroup/gobot" + "github.com/hybridgroup/gobot/platforms/gpio" + "github.com/hybridgroup/gobot/platforms/raspi" +) + +func main() { + gbot := gobot.NewGobot() + + r := raspi.NewRaspiAdaptor("raspi") + button := gpio.NewButtonDriver(r, "button", "11") + led := gpio.NewLedDriver(r, "led", "7") + + work := func() { + gobot.On(button.Event("push"), func(data interface{}) { + fmt.Println("button pressed") + led.On() + }) + + gobot.On(button.Event("release"), func(data interface{}) { + fmt.Println("button released") + led.Off() + }) + } + + robot := gobot.NewRobot("buttonBot", + []gobot.Connection{r}, + []gobot.Device{button, led}, + work, + ) + gbot.AddRobot(robot) + gbot.Start() +} diff --git a/platforms/raspi/README.md b/platforms/raspi/README.md new file mode 100644 index 00000000..8f3c988d --- /dev/null +++ b/platforms/raspi/README.md @@ -0,0 +1,72 @@ +# Raspi + +This package contains the Gobot adaptor for the [Raspberry Pi](http://www.raspberrypi.org/). + +## Getting Started + +First you must install the appropriate Go packages + +``` +go get -d -u github.com/hybridgroup/gobot/... && go install github.com/hybridgroup/gobot/platforms/raspi +``` + +#### Cross compiling for the Raspberry Pi +You must first configure your Go environment for linux cross compiling + +```bash +$ cd $GOROOT/src +$ GOOS=linux GOARCH=arm ./make.bash --no-clean + +``` + +Then compile your Gobot program with +```bash +$ GOARM=6 GOARCH=arm GOOS=linux examples/raspi_blink.go +``` + +Then you can simply upload your program over the network from your host computer to the Raspi +``` bash +$ scp raspi_blink pi@192.168.1.xxx:/home/pi/ +``` + +and execute it on your Raspberry Pi with +```bash +$ ./raspi_blink +``` + +## Example + +```go +package main + +import ( + "time" + + "github.com/hybridgroup/gobot" + "github.com/hybridgroup/gobot/platforms/gpio" + "github.com/hybridgroup/gobot/platforms/raspi" +) + +func main() { + gbot := gobot.NewGobot() + + r := raspi.NewRaspiAdaptor("raspi") + led := gpio.NewLedDriver(r, "led", "7") + + work := func() { + gobot.Every(1*time.Second, func() { + led.Toggle() + }) + } + + robot := gobot.NewRobot("blinkBot", + []gobot.Connection{r}, + []gobot.Device{led}, + work, + ) + + gbot.AddRobot(robot) + + gbot.Start() +} +``` diff --git a/platforms/raspi/doc.go b/platforms/raspi/doc.go new file mode 100644 index 00000000..b74ef677 --- /dev/null +++ b/platforms/raspi/doc.go @@ -0,0 +1,7 @@ +/* +Package raspi contains the Gobot adaptor for the Raspberry Pi. + +For further information refer to raspi README: +https://github.com/hybridgroup/gobot/blob/master/platforms/raspi/README.md +*/ +package raspi diff --git a/platforms/raspi/raspi_adaptor.go b/platforms/raspi/raspi_adaptor.go new file mode 100644 index 00000000..e4b0e8a4 --- /dev/null +++ b/platforms/raspi/raspi_adaptor.go @@ -0,0 +1,226 @@ +package raspi + +import ( + "fmt" + "io" + "io/ioutil" + "os/exec" + "strconv" + "strings" + + "github.com/hybridgroup/gobot" + "github.com/hybridgroup/gobot/sysfs" +) + +var i2cLocationFor = func(rev string) string { + if rev == "1" { + return "/dev/i2c-0" + } + return "/dev/i2c-1" +} + +var boardRevision = func() string { + cat, _ := exec.Command("cat", "/proc/cpuinfo").Output() + grep := exec.Command("grep", "Revision") + in, _ := grep.StdinPipe() + out, _ := grep.StdoutPipe() + grep.Start() + in.Write([]byte(string(cat))) + in.Close() + buf, _ := ioutil.ReadAll(out) + grep.Wait() + + s := strings.Split(string(buf), " ") + a := fmt.Sprintf("0x%v", strings.TrimSuffix(s[len(s)-1], "\n")) + d, _ := strconv.ParseInt(a, 0, 64) + + rev := "" + if d <= 3 { + rev = "1" + } else if d <= 15 { + rev = "2" + } else { + rev = "3" + } + return rev +} + +type RaspiAdaptor struct { + gobot.Adaptor + revision string + digitalPins map[int]sysfs.DigitalPin + i2cDevice io.ReadWriteCloser +} + +var pins = map[string]map[string]int{ + "3": map[string]int{ + "1": 0, + "2": 2, + "3": 2, + }, + "5": map[string]int{ + "1": 1, + "2": 3, + "3": 3, + }, + "7": map[string]int{ + "*": 4, + }, + "8": map[string]int{ + "*": 14, + }, + "10": map[string]int{ + "*": 15, + }, + "11": map[string]int{ + "*": 17, + }, + "12": map[string]int{ + "*": 18, + }, + "13": map[string]int{ + "1": 21, + "2": 27, + "3": 27, + }, + "15": map[string]int{ + "*": 22, + }, + "16": map[string]int{ + "*": 23, + }, + "18": map[string]int{ + "*": 24, + }, + "19": map[string]int{ + "*": 10, + }, + "21": map[string]int{ + "*": 9, + }, + "22": map[string]int{ + "*": 25, + }, + "23": map[string]int{ + "*": 11, + }, + "24": map[string]int{ + "*": 8, + }, + "26": map[string]int{ + "*": 7, + }, + "29": map[string]int{ + "3": 5, + }, + "31": map[string]int{ + "3": 6, + }, + "32": map[string]int{ + "3": 12, + }, + "33": map[string]int{ + "3": 13, + }, + "35": map[string]int{ + "3": 19, + }, + "36": map[string]int{ + "3": 16, + }, + "37": map[string]int{ + "3": 26, + }, + "38": map[string]int{ + "3": 20, + }, + "40": map[string]int{ + "3": 21, + }, +} + +// NewRaspiAdaptor creates a RaspiAdaptor with specified name and +func NewRaspiAdaptor(name string) *RaspiAdaptor { + return &RaspiAdaptor{ + Adaptor: *gobot.NewAdaptor( + name, + "RaspiAdaptor", + ), + revision: boardRevision(), + digitalPins: make(map[int]sysfs.DigitalPin), + } +} + +// Connect starts conection with board and creates +// digitalPins and pwmPins adaptor maps +func (r *RaspiAdaptor) Connect() bool { + return true +} + +// Finalize closes connection to board and pins +func (r *RaspiAdaptor) Finalize() bool { + for _, pin := range r.digitalPins { + if pin != nil { + pin.Unexport() + } + } + if r.i2cDevice != nil { + r.i2cDevice.Close() + } + return true +} + +// digitalPin returns matched digitalPin for specified values +func (r *RaspiAdaptor) digitalPin(pin string, dir string) sysfs.DigitalPin { + var i int + + if val, ok := pins[pin][r.revision]; ok { + i = val + } else if val, ok := pins[pin]["*"]; ok { + i = val + } else { + panic("not valid pin") + } + + if r.digitalPins[i] == nil { + r.digitalPins[i] = sysfs.NewDigitalPin(i) + r.digitalPins[i].Export() + } + + r.digitalPins[i].Direction(dir) + + return r.digitalPins[i] +} + +// DigitalRead reads digital value from pin +func (r *RaspiAdaptor) DigitalRead(pin string) (i int) { + i, _ = r.digitalPin(pin, sysfs.IN).Read() + return +} + +// DigitalWrite writes digital value to specified pin +func (r *RaspiAdaptor) DigitalWrite(pin string, val byte) { + r.digitalPin(pin, sysfs.OUT).Write(int(val)) +} + +// PwmWrite Not Implemented +func (r *RaspiAdaptor) PwmWrite(pin string, val byte) { + fmt.Println("PwmWrite Is Not Implemented") +} + +// I2cStart starts a i2c device in specified address +func (r *RaspiAdaptor) I2cStart(address byte) { + r.i2cDevice, _ = sysfs.NewI2cDevice(i2cLocationFor(r.revision), address) +} + +// I2CWrite writes data to i2c device +func (r *RaspiAdaptor) I2cWrite(data []byte) { + r.i2cDevice.Write(data) +} + +// I2cRead returns value from i2c device using specified size +func (r *RaspiAdaptor) I2cRead(size uint) []byte { + buf := make([]byte, size) + r.i2cDevice.Read(buf) + return buf +} diff --git a/platforms/raspi/raspi_adaptor_test.go b/platforms/raspi/raspi_adaptor_test.go new file mode 100644 index 00000000..cb6d92f7 --- /dev/null +++ b/platforms/raspi/raspi_adaptor_test.go @@ -0,0 +1,67 @@ +package raspi + +import ( + "io" + "os" + "testing" + + "github.com/hybridgroup/gobot" + "github.com/hybridgroup/gobot/sysfs" +) + +func initTestRaspiAdaptor() *RaspiAdaptor { + i2cLocationFor = func(rev string) string { + return os.DevNull + } + boardRevision = func() string { + return "3" + } + sysfs.WriteFile = func(path string, data []byte) (i int, err error) { + return + } + a := NewRaspiAdaptor("myAdaptor") + a.Connect() + return a +} + +func TestRaspiAdaptorFinalize(t *testing.T) { + a := initTestRaspiAdaptor() + a.DigitalWrite("3", 1) + a.i2cDevice = new(gobot.NullReadWriteCloser) + gobot.Assert(t, a.Finalize(), true) +} + +func TestRaspiAdaptorDigitalIO(t *testing.T) { + a := initTestRaspiAdaptor() + lastWritePath := "" + lastReadPath := "" + lastWriteData := []byte{} + + sysfs.WriteFile = func(path string, data []byte) (i int, err error) { + lastWritePath = path + lastWriteData = data + return + } + sysfs.ReadFile = func(path string) (b []byte, err error) { + lastReadPath = path + return []byte("1"), nil + } + + a.DigitalWrite("7", 1) + gobot.Assert(t, lastWritePath, "/sys/class/gpio/gpio4/value") + gobot.Assert(t, lastWriteData, []byte{49}) + + i := a.DigitalRead("13") + gobot.Assert(t, lastReadPath, "/sys/class/gpio/gpio27/value") + gobot.Assert(t, i, 1) +} + +func TestRaspiAdaptorI2c(t *testing.T) { + a := initTestRaspiAdaptor() + a.I2cStart(0xff) + var _ io.ReadWriteCloser = a.i2cDevice + + a.i2cDevice = new(gobot.NullReadWriteCloser) + a.I2cWrite([]byte{0x00, 0x01}) + gobot.Assert(t, a.I2cRead(2), make([]byte, 2)) +}