Подключение датчиков к Raspberry Pi по радиоканалу.
В процессе создания умного дома, каждый рано или поздно сталкивается с размещением датчиков, показатели которых нужно выводить на экран, а то и обрабатывать для чего-то более умного.
Большинство датчиков легче всего подключаются к Arduino, за счет наличия библиотек, безгеморройных PWM, ADC и прочих вкусностей.
Но слишком умную логику на Arduino не построишь, и волей-неволей возникает необходимость использования чего-то более быстрого, универсального, и с простой индикацией\управлением. Например, почти ставший стандартом де-факто,
Raspberry Pi.
У него есть HDMI\AV выход. Есть флешка, сеть, память, USB и SSH.
Все что нам нужно.
Но напрямую датчики к Raspberry Pi подключить весьма непросто, в силу отсутствия большого количества библиотек, примеров, и просто потому что она не совсем к этому приспособлена. Да и держать эти датчики тогда придется рядом, а значит либо Малинке придется жить на балконе, либо датчику влажности воздуха у вас в шкафу.
Разумеется, тянуть два-три провода от датчиков к Малине — тоже не вариант, ибо тогда умный дом станет сложным домом.
Интересно?
Для этих целей была придумана радиосвязь.
В качестве самой дешевой, понятной и доступной радиосвязи, я выбрал радиомодуль-трансивер nrf24l01, и вот почему:
1) Дальность. В пределах дома — до ста метров. То что нам подходит.
2) Скорость передачи данных — до 2 Мбит. Хоть потоковое видео передавай.
3) Возможности подключения точка-точка, точки-хаб, хаб-хаб, хаб-точка.
4) Толерантны к 5В, хотя могут принимать и 3.3В.
5) Эти контроллеры имеют свою прошивку, которая позволяет им становиться независимым девайсом.
6) Автоматически шлет идентификатор сигнала, избавляя нас от необходимости заморачиваться с индивидуальной настройкой каналов для каждого трансивера.
7) Дуплекс.
8) Дешевизна. Кстати взял я их не дешево, решил не ждать три недели ради экономии 10 баксов.
Вот так выглядит этот девайс.
Топология связи очень проста, дешева и понятна.
1) Набор датчиков в какой-либо локации, подключается к Arduino. Таких локаций может быть несколько.
2) Она (Arduino) играет роль предварительного буфера, который собирает данные с датчиков, и комбинирует их для последующей отправки.
Ну и выполняет какую-то базовую логику, например включает на балконе свет по датчику движения (без участия центрального сервера). В общем согласно наши потребностям.
3) Raspberry Pi через такой же самый модуль, принимает радиосигнал.
В интернете очень много примеров связи Arduino-Raspberry Pi, используя эти трансиверы nrf24l01.
Однако почему-то все эти примеры рассматривают Arduino как центральный узел (хаб), а Raspberry Pi как придаток (клиент). И мало что из примеров работоспособно с первого раза.
Методом проб и ошибок, мне удалось сделать обратное — один Raspberry Pi-блок, и несколько узлов с Ардуинами, которые настраиваются абсолютно одинаково, не нуждаются в выборе каналов, частот и прочего. Поменяли циферку в строке — получили новое устройство.
Несколько фоток из центра событий.
Малиновый сервер. Не удобно подключаться по сети, WiFi рулит.
Первый подопечный. Arduino Nano с датчиком движения.
Второй подопечный. Arduino Mini и USB-TTL. Где-то там отстает проводок.
Все вместе.
Итак, не буду вдаваться в подробности установок IDE, библиотек, а выложу сразу работающий код для двух устройств.
Arduino-часть.
#include <SPI.h>
#include <RF24.h>
// CE,CSN пины
RF24 radio(9,10);
// init counter
unsigned long count = 0;
int sensor1 = 2;
void setup(void)
{
// Инициализируем канал связи и не только.
radio.begin();
radio.setPALevel(RF24_PA_MAX);
radio.setChannel(0x4c);
radio.openWritingPipe(0xF0F0F0F0E1LL);
radio.enableDynamicPayloads();
radio.powerUp();
pinMode(sensor1, INPUT);
pinMode(3, OUTPUT);
}
void loop(void)
{
char outBuffer[32]= "";
int pin1 = digitalRead(sensor1);
// Следующая строка изменяется по нашему желанию. Я шлю на сервер три значения: ID блока (ардуины), ID пина, и его значение. Больше ничего изменять не нужно.
String out = "dev1:p1:"+pin1;
out.toCharArray(outBuffer, 32);
radio.write(outBuffer, 32);
delay(50);
// Не знаю почему, но периодически трансивер зависает. Помогает повторная инициализация.
radio.begin();
radio.setPALevel(RF24_PA_MAX);
radio.setChannel(0x4c);
radio.openWritingPipe(0xF0F0F0F0E1LL);
radio.enableDynamicPayloads();
radio.powerUp();
delay(50);
// Следующий костыль включает свет по датчику движения на 10 секунд, не замораживая при этом основной цикл loop.
if (pin1=HIGH)
{
digitalWrite(3, HIGH);
count=0;
}
count++;
if (count>100)
{
digitalWrite(3, LOW);
count=0;
}
}
На Малине нам понадобится библиотека, установить которую можно так:
git clone https://github.com/stanleyseow/RF24.git
cd RF24
cd librf24-rpi/librf24
make
sudo make install
C++ код для Raspberry Pi. В инклуде нужно указать путь к файлу хидера.
#include <cstdlib>
#include <iostream>
#include "../RF24.h"
#include <fstream>
using namespace std;
// spi device, spi speed, ce gpio pin
RF24 radio("/dev/spidev0.0",8000000,25);
void setup(void)
{
// init radio for reading
radio.begin();
radio.enableDynamicPayloads();
radio.setAutoAck(1);
radio.setRetries(15,15);
radio.setDataRate(RF24_1MBPS);
radio.setPALevel(RF24_PA_MAX);
radio.setChannel(76);
radio.setCRCLength(RF24_CRC_16);
radio.openReadingPipe(1,0xF0F0F0F0E1LL);
radio.startListening();
}
void loop(void)
{
char receivePayload[64];
while (radio.available())
{
uint8_t len = radio.getDynamicPayloadSize();
radio.read(receivePayload, len);
// Костыль для создания промежуточного файла.
ofstream out("/dev/nrf24");
out << receivePayload << "\n";
out.close ();
delay(200);
}
}
int main(int argc, char** argv)
{
cout << "Driver initialized, please check values of /dev/nrf24" << endl;
setup();
while(1)
loop();
return 0;
}
Скомпилировать на Малинке мы его можем командой
g++ -Wall -Ofast -mfpu=vfp -mfloat-abi=hard -march=armv6zk -mtune=arm1176jzf-s -L../librf24/ -lrf24 receiver.cpp -o rpi
Не забываем, где мы положили librf24.
Запустив это поделие в фоне, мы получим файл /dev/nrf24, который будет содержать строку, пришедшую к нам по радиоканалу.
Его мы можем читать и парсить как нам заблагорассудится. Хоть read, хоть cat, хоть tail.
Внимание: костыль с этим файлом был введен ТОЛЬКО для «удобства» совместной работы приложений на разных языках: bash, PHP. По феншую все делать только на С++.
На закуску, видео работы двух Arduino с одной Raspberry Pi.
Ради интереса попробовал сформировать строку в JSON, это видно на видео. Неэффективно, если датчиков много. Лимит на 32 символа, а разбираться глубже не было времени. Просто поменять циферку 32 на 64 — не помогает.
Все элементы использую только для наглядности примера. Реальная реализация выглядит по-другому.
комментарии (58)