Go и arduino
На ардуине много всего можно построить и, иногда, этим всем нужно как-то управлять. Для этого можно использовать последовательный порт, отправляя команды с помощью Go программы.
В ардуине есть класс Serial, который служит для связи устройства ардуино с компьютером или другими устройствами, поддерживающими последовательный интерфейс обмена данными. Все платы arduino имеют хотя бы один последовательный порт (UART, иногда называют USART). Для обмена данными Serial используют цифровые порты ввод/вывода 0 (RX) и 1 (TX), а также USB порт. Важно помнить, что если вы используете класс Serial, то нельзя одновременно с этим использовать порты 0 и 1 для других целей.
В Go работать с последовательным портом, проще всего используя, пакет sio. У этого пакета не очень большой функционал, но его вполне хватает для оправки и получения данных с устройства.
Эхо
Для примера, напишем небольшую программу, которая отправляет данные на устройство и получает их обратно. Начнем с Go части. Cначала нужно установить соединение:
port, err := sio.Open("/dev/ttyACM0", syscall.B9600)
if err != nil {
log.Fatal(err)
}
/dev/ttyACM0
- имя порта, которое можно посмотреть в arduino ide или используя плагин для sublime
syscall.B9600
- скорость передачи данных в бит/c (бод). Для наших экспериментов не имеет принципиального значения, на какой скорости будет происходить обмен, важно, чтоб этот параметр был одинаковым и на устройстве и в нашей Go-программе.
После этого можно отправлять данные в порт.
_, err = port.Write([]byte("test\n"))
if err != nil {
log.Fatal(err)
}
В устройстве первым делом устанавливаем соединение.
void setup() {
Serial.begin(9600);
}
Потом получаем данные и оправляем их обратно в порт.
if (Serial.available() > 0) {
// если есть доступные данные
// считываем байт
incomingByte = Serial.read();
Serial.print(incomingByte);
}
Теперь пришло время считывать данные на стороне Go программы.
reader := bufio.NewReader(port)
reply, err := reader.ReadBytes('\n')
if err != nil {
log.Fatal(err)
}
fmt.Printf("%q", reply)
Все вместе это выглядит так:
// echo.go
package main
import (
"bufio"
"fmt"
"github.com/schleibinger/sio"
"log"
"syscall"
)
func main() {
// устанавливаем соединение
port, err := sio.Open("/dev/ttyACM0", syscall.B9600)
if err != nil {
log.Fatal(err)
}
// отправляем данные
_, err = port.Write([]byte("test\n"))
if err != nil {
log.Fatal(err)
}
// получаем данные
reader := bufio.NewReader(port)
reply, err := reader.ReadBytes('\n')
if err != nil {
log.Fatal(err)
}
fmt.Printf("%q", reply)
}
// echo.ino
// переменная для хранения полученного байта
char incomingByte = 0;
void setup() {
// устанавливаем последовательное соединение
Serial.begin(9600);
}
void loop() {
if (Serial.available() > 0) {
// если есть доступные данные
// считываем байт
incomingByte = Serial.read();
Serial.print(incomingByte);
}
}
Последний твит
Для закрепления приобретенных знаний напишем небольшую программу, которая отображает последний твитт бегущей строкой на LCD экране.
Конечно, сначала нужно получить твиты. И для этого заюзаем пакет anaconda.
anaconda.SetConsumerKey(consumerKey)
anaconda.SetConsumerSecret(consumerSecret)
api := anaconda.NewTwitterApi(key, secretKey)
consumerKey
, consumerSecret
, key
, secretKey
- соответственно ключи взятые из настроек твиттер приложения
Получаем твиты по ключевому слову #golang:
searchResult, _ = api.GetSearch("#golang", nil)
twitt = " -- " + searchResult[0].Text
fmt.Println(twitt)
В searchResult
будет храниться список всех найденных твитов, но нам нужен только последний по времени.
Оправляем твит в последовательный порт:
var twitt string
var searchResult []anaconda.Tweet
for {
searchResult, _ = api.GetSearch("#golang", nil)
twitt = " -- " + searchResult[0].Text
fmt.Println(twitt)
// отправляем данные
_, err = port.Write([]byte(twitt))
if err != nil {
log.Fatal(err)
}
time.Sleep(120 * time.Second)
}
Таким образом, в порт будет оправляться новый твит каждые три минуты.
Ардуино-часть нашего маленького проекта начинается с объявления и инициализации:
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
int bytesCount = 0;
char data[140] = "";
char string[16] = "";
LiquidCrystal
- это наш LCD дисплей, data
- это полученные данные, bytesCount
- количество полученных байтов, string
- это строка информации, которая будет отображаться на дисплее.
Про установку соединения и считывание данных писалось выше. Единственное изменение, теперь будем получать не по одному байту, а все сразу:
if (Serial.available() > 0) {
memset(&data[0], 0, sizeof(data));
bytesCount = Serial.readBytes(data, 1000);
}
memset(&data[0], 0, sizeof(data));
- это очистка массива перед получением новых данных, так как следующий твит может оказаться короче предыдущего.
И последний шаг - отображение данных в виде бегущей строки:
for(int i = 0; i < bytesCount; i++){
lcd.setCursor(0, 1);
for(int j = 0; j < 16; j++){
if (data[i+j] == NULL) string[j] = data[(i+j) - bytesCount];
else string[j] = data[i+j];
}
lcd.print(string);
delay(300);
}
Полностью исходники можно посмотреть на гитхабе. Для управления зависимостями использовался gpm.
Жду предложений и замечаний.