Введение
Про метеостанции на Arduino писали и не раз. В своё оправдание скажу, что был хакатон — а нашей команде (в составе меня и хабраюзера ViArt) хотелось попробовать работу с Arduino. Кроме того к нашей метеостанции прикручена визуализация данных. Если хотите узнать, какая база данных может получать данные по com-порту без промежуточных звеньев в виде web-сервера, файлов или ещё каких-то ухищрений, добро пожаловать под кат.
Работа с устройствами
В InterSystems Caché можно работать напрямую с большим количеством физических и логических типов устройств. Вот они:
- Диски
- Магнитные ленты
- Файлы
- Терминалы
- TCP порты
- COM порты
- и другие
Работа происходит в 5 этапов:
- Сначала с помощью команды OPEN регистрируется тот факт что текущий процесс получает доступ к устройству
- Затем, когда нужно работать с устройством, оно делается текущим с помощью команды USE
- Работа с устройством. Получение данных с устройства командой READ, отправка данных на устройство командой WRITE
- Переключение на другое устройство ввода-вывода (опять USE)
- Закрытие устройства командой CLOSE
Как это выглядит на практике?
Мигаем лампочкой из Caché
Итак, соберём на Arduino схему, которая читает данные из COM порта и включает светодиод на указанное число миллисекунд.
/* Led.ino * * Пример получения данных по COM порту * Подключите светодиод к выводу ledPin * */ // Вывод светодиода (цифровой) #define ledpin 8 // "Буфер" поступающих данных String inString = ""; void setup() { Serial.begin(9600); pinMode(ledpin, OUTPUT); digitalWrite(ledpin, LOW); } void loop() { // Получаем данные из COM порта while (Serial.available() > 0) { int inChar = Serial.read(); if (isDigit(inChar)) { // Получаем 1 символ, // Прибавляем его к строке inString += (char)inChar; } // Доходим до новой строки if (inChar == '\n') { // Включаем светодиод digitalWrite(ledpin, HIGH); int time = inString.toInt(); delay(time); digitalWrite(ledpin, LOW); // Обнуляем полученную строку inString = ""; } } }
В Caché напишем метод, подключающийся к com порту и отправляющий строку 1000\n:
/// Отправляем на порт строку 1000\n ClassMethod SendSerial() { set port = "COM1" open port:(:::" 0801n0":/BAUD=9600) // Открываем устройство set old = $IO // Записываем текущее устройство ввода-вывода use port // Переключаемся на com порт write $Char(10) // Отправка пробного пакета данных hang 1 write 1000 _ $Char(10) // Передаём строку 1000\n use old // Переключаем вывод на терминал close port // Закрываем устройство }
Строка «0801n0» это список параметров подключения к Com порту, подробно расписана в документации. А /BAUD=9600 — это скорость подключения в бодах.
В результате, если вызвать этот метод в терминале:
do ##class(Arduino.Habr).SendSerial()
То он ничего не выведет, а вот светодиод загорится на секунду.
Получаем данные в Caché
Теперь подключим клавиатуру (keypad) к Caché и будем передавать данные. Это может быть использовано как, например, дополнительная аутентификация пользователя с помощью делегации авторизации и рутины ZAUTHENTICATE.mac
/* Keypadtest.ino * * Пример использования библиотеки Keypad * Подключите Keypad к выводам Arduino указанным в * rowPins[] and colPins[]. * */ // Репозиторий библиотеки: // https://github.com/Chris--A/Keypad #include <Keypad.h> const byte ROWS = 4; // Четыре строки const byte COLS = 4; // Три столбцы // Карта соответствия кнопок и символов char keys[ROWS][COLS] = { {'1','2','3','A'}, {'4','5','6','B'}, {'7','8','9','C'}, {'*','0','#','D'} }; // Подключите разьёмы keypad 1-8 (сверху-вниз) к Arduino разьёмам 11-4. 1->11, 2->10, ... , 8->4 // Подключите keypad ROW0, ROW1, ROW2 и ROW3 к этим выводам Arduino byte rowPins[ROWS] = { 7, 6, 5, 4 }; // Подключите keypad COL0, COL1 and COL2 к этим выводам Arduino byte colPins[COLS] = { 8, 9, 10, 11 }; // Инициализация Keypad Keypad kpd = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS ); #define ledpin 13 void setup() { Serial.begin(9600); } void loop() { char key = kpd.getKey(); // Поолучаем нажатую кнопку if(key) { switch (key) { case '#': Serial.println(); default: Serial.print(key); } } }
В Caché напишем метод, подключающийся к com порту и получающий строку данных:
/// Получение одной строки данных (до конца строки) ClassMethod ReceiveOneLine() As %String { port = "COM1" set str="" try { open port:(:::" 0801n0":/BAUD=9600) set old = $io // Запоминаем текущее устройство ввода-вывода use port read str // Читаем, пока не встретим символ конца строки use old close port } catch ex { close port } return str }
В терминале выполним:
write ##class(Arduino.Habr).ReceiveOneLine()
И он перейдёт в режим ожидания, пока на keypad не нажмём "#" (по нажатию на который будет передан конец строки), после чего в терминале будет выведена введённая строка.
Итак, это были основы взаимодействия с устройством, теперь перейдём к метеостанции.
Метеостанция
Для сборки метеостанции использовали фоторезистор и датчик DHT11 (температура и влажность). Схема подключения:
/* Meteo.ino * * Программа, регистрирующая влажность, температуру и яркость * Отправляет результаты на COM port * Формат вывода: H=1.0;T=1.0;LL=1; */ //Пин фоторезистора (аналоговый) int lightPin = 0; // Пин DHT-11 (цифровой) int DHpin = 8; // Массив, хранящий данные DHT-11 byte dat[5]; // Первоначальная настройка void setup() { Serial.begin(9600); pinMode(DHpin,OUTPUT); } /* * Выполняется после setup() * Основной бесконечный цикл */ void loop() { delay(1000); // Замер примерно 1 раз в секунду int lightLevel = analogRead(lightPin); //Получаем уровень освещённости temp_hum(); // Получаем температуру и влажность в переменную dat // И выводим результат Serial.print("H="); Serial.print(dat[0], DEC); Serial.print('.'); Serial.print(dat[1],DEC); Serial.print(";T="); Serial.print(dat[2], DEC); Serial.print('.'); Serial.print(dat[3],DEC); Serial.print(";LL="); Serial.print(lightLevel); Serial.println(";"); } // Получить данные от DHT-11 в dat void temp_hum() { digitalWrite(DHpin,LOW); delay(30); digitalWrite(DHpin,HIGH); delayMicroseconds(40); pinMode(DHpin,INPUT); while(digitalRead(DHpin) == HIGH); delayMicroseconds(80); if(digitalRead(DHpin) == LOW); delayMicroseconds(80); for(int i=0;i<4;i++) { dat[i] = read_data(); } pinMode(DHpin,OUTPUT); digitalWrite(DHpin,HIGH); } // Получить часть данных от DHT-11 byte read_data() { byte data; for(int i=0; i<8; i++) { if(digitalRead(DHpin) == LOW) { while(digitalRead(DHpin) == LOW); delayMicroseconds(30); if(digitalRead(DHpin) == HIGH) { data |= (1<<(7-i)); } while(digitalRead(DHpin) == HIGH); } } return data; }
После загрузки кода на Arduino она начинает посылать данные на COM порт в следующем формате:
H=34.0;T=24.0;LL=605;
Где:
- H — влажность (от 0 до 100 процентов)
- T — температура в градусах Цельсия
- LL — освещённость (от 0 до 1023)
Надо как-то его хранить в Caché. Для этого напишем хранимый класс Arduino.Info:
Class Arduino.Info Extends %Persistent { Parameter SerialPort As %String = "com1"; Property DateTime As %DateTime; Property Temperature As %Double; Property Humidity As %Double(MAXVAL = 100, MINVAL = 0); Property Brightness As %Double(MAXVAL = 100, MINVAL = 0); Property Volume As %Double(MAXVAL = 100, MINVAL = 0); ClassMethod AddNew(Temperature = 0, Humidity = 0, Brightness = 0, Volume = 0) { set obj = ..%New() set obj.DateTime=$ZDT($H,3,1) set obj.Temperature=Temperature set obj.Humidity=Humidity set obj.Brightness=Brightness/1023*100 set obj.Volume=Volume write $SYSTEM.Status.DisplayError(obj.%Save()) }
И добавим туда метод, который будет принимать данные в формате Arduino, и преобразовывать их объекты класса Arduino.Info:
/// Получаем поток данных в формате H=34.0;T=24.0;LL=605;\n /// И преобразуем их в объекты класса Arduino.Info ClassMethod ReceiveSerial(port = {..#SerialPort}) { try { open port:(:::" 0801n0":/BAUD=9600) set old = $IO use port for { read x //Читаем одну строку set Humidity = $Piece($Piece(x,";",1),"=",2) set Temperature = $Piece($Piece(x,";",2),"=",2) set Brightness = $Piece($Piece(x,";",3),"=",2) if (x '= "") { do ..AddNew(Temperature,Humidity,Brightness) // Добавляем данные } } } catch anyError { close port } }
После этого нам нужно запустить Arduino и выполнить в терминале метод ReceiveSerial:
write ##class(Arduino.Info).ReceiveSerial()
Этот метод в бесконечном цикле будет собирать и сохранять данные, приходящие от Arduino.
Визуализация данных
После того, как метеостанция была собрана, наша команда запустила её на улице (была уже ночь) и оставила собирать данные до утра.
К утру данные накопились (~36000 записей) и мы визуализировали их в BI DeepSee, вот что получилось.
График яркости. Явно виден рассвет в районе 5:50:
Графики температуры и влажности.
Не стоило ставить датчик температуры под прямые солнечные лучи, но всё явно явно прослеживается взаимосвязь между температурой и влажностью.
Демо
Доступно тут.
Выводы
InterSystems Caché позволяет организовать взаимодействие с большим числом устройств напрямую. Возможна быстрая разработка решений по сбору и визуализации данных.
Ссылки
» Документация
» Репозитоий с кодом
ссылка на оригинал статьи https://habrahabr.ru/post/273749/
Добавить комментарий