Main

Сеть беспроводных устройств на Arduino

Микро-контроллер на платформе Arduino -- отличная платформа для хобби-проектов различной степени сложности и полезности. Не буду утверждать, что платформа Arduino есть наилучший выбор для профессиональных решений (скорее соглашусь с обратным), но для моих любительских "поделок" в области домашней автоматики это оптимальный вариант. Т.е. контроллер хорош сам по себе, но, если к тому же он перестанет быть "самим по себе", а будет уметь "общаться" с себе подобными, при этом не обрастая дополнительными проводами, то его полезность и применимость может многократно вырасти. Итак, начнём строить наш домашний SkyNet...

Бюджетные радио-модули

За основу нашей сети возьмём бюджетные радио-модули, работающие на частоте 433.90MHz. Стоимость одного такого модуля составляет порядка $2.5, так что невелики затраты для того, чтобы организовать связь с внешним миром. Конечно, для связи можно использовать и готовые ethernet-модули и даже делать симбиоз с беспрводными роутерами на базе альтернативных прошивок, но во многих случаях проще и дешевле всё сделать на вот таких радио-модулях.

Передатчик:

Приёмник:

Качество работы и дальность связи этих модулей оставляет желать лучшего, и я бы не стал верить оптимистичным заявлениям продавцов о радиусе действия ">500m". В лучшем случае 100 метров на открытой местности, ну и гораздо меньше при наличии бетонных перегородок. Тем не менее, для квартиры или небольшого загородного участка их хватит. Можно использовать и более качественные (соответственно, более дорогие) радио-модули, поэтому статья может рассматриваться как идейная концепция, применимая к многим возможным вариантам реализации.

Важный момент: в данном руководстве я не буду рассматривать вариант создания сети с контролем качества передачи данных. Если сравнение с ethernet-протоколами можно считать уместным, то мы не будем строить сеть передачи TCP-пакетов, а, скорее, UDP.

Каждый из модулей подключается к контроллеру элементарно -- питание через Vcc/Gnd и вывод Data подключается к свободному цифровому входу на микро-контроллере. Для повышения качества приёма/передачи рекомендуется дополнительно подключить антенну в виде провода размером 10-15 см. Кстати, дальность связи зависит ещё и от подающегося на модуль питания -- если их запитать от 12В, то дальность и надёжность связи значительно возрастает.

Приёмник и передатчик подключенный к микро-контроллеру Arduino UNO R3:

Таким образом, мы сделали два устройства: первое -- передатчик, который будет "вещать" в эфир какую-то информацию; второе -- приёмник, который, соответственно, будет эфир "слушать". Далее, дело за тем, что бы и передача и приём были осмысленными и полезными для нас.

Библиотека VirtualWire

Чем хороша платформа Arduino, так это наличием огромного количества готовых библиотек для работы с всевозможными устройствами. Можно, конечно, с радио-модулями работать и без каких-либо библиотек, но тогда нужно разрабатывать свой протокол связи с контрольными суммами и прочими вещами. К счастью, есть замечательная библиотека VirtualWire, поддерживающая данные (и им подобные) радио-модули. С помощью этой библиотеки очень легко организовать передачу и приём небольших пакетов информации.

Принцип использования: на передатчике формируем набор данных для пересылки (в виде строки символов либо байт-кодов), а на приёмнике, при получении "корректного" пакета данных, их отображаем. Проще всего это увидеть на примерах, которые идут с самой библиотекой.

Код передатчика с использованием VirtualWire (из примеров использования библиотеки):

Нажмите для отображения кода

// transmitter.pde
//
// Simple example of how to use VirtualWire to transmit messages
// Implements a simplex (one-way) transmitter with an TX-C1 module
#include <VirtualWire.h>

void setup()
{
    Serial.begin(9600);   // Debugging only
    Serial.println("setup");
    // Initialise the IO and ISR
    vw_set_ptt_inverted(true); // Required for DR3100
    vw_setup(2000);      // Bits per sec
}

void loop()
{
    const char *msg = "hello";

    digitalWrite(13, true); // Flash a light to show transmitting
    vw_send((uint8_t *)msg, strlen(msg));
    vw_wait_tx(); // Wait until the whole message is gone
    digitalWrite(13, false);
    delay(200);
}

Код приёмника

Нажмите для отображения кода

// receiver.pde
//
// Simple example of how to use VirtualWire to receive messages
// Implements a simplex (one-way) receiver with an Rx-B1 module
#include <VirtualWire.h>

void setup()
{
    Serial.begin(9600); // Debugging only
    Serial.println("setup");

    // Initialise the IO and ISR
    vw_set_ptt_inverted(true); // Required for DR3100
    vw_setup(2000);      // Bits per sec

    vw_rx_start();       // Start the receiver PLL running
}

void loop()
{
    uint8_t buf[VW_MAX_MESSAGE_LEN];
    uint8_t buflen = VW_MAX_MESSAGE_LEN;

    if (vw_get_message(buf, &buflen)) // Non-blocking
    {
        int i;
        digitalWrite(13, true); // Flash a light to show received good message
        // Message with a good checksum received, dump it.
        Serial.print("Got: ");       
        for (i = 0; i < buflen; i++)
        {
            Serial.print(buf[i], HEX);
            Serial.print(" ");
        }
        Serial.println("");
        digitalWrite(13, false);
    }
}

Протокол обмена данными

Следующим шагом у нас будет выход на новый уровень абстракции, а именно разработка типовой структуры пакета, которыми будут обмениваться все наши устройства. Это позволит в будущем подключать к нашей сети новое оборудование, которое сможет использовать сигналы от уже имеющихся устройств.

Я приведу ту структуру данных, которая показалась мне оптимальной при имеющихся возможностях оборудования. Итак, ниже по тексту список основных параметров, которые отправляются в эфир с каждым пакетом:

device_id -- идентификатор устройства, отправившего пакет. Тип данных: unsigned int (длина 2 байта, диапазон значений от 0 до 65535) -- как мне кажется, вполне достаточно для домашней сети.

destination_id -- идентификатор устройства, кому предназначен пакет. Тип данных тот же, что и у device_id. Важно отметить, что пакеты всё равно будут получаться всеми приёмниками, но уже программой на самом приёмнике можно "отсекать" пакеты, которые устройству не предназначены. Так же можно принять в качестве правила то, что значение "0" в данном поле означает широковещательный пакет.

packet_id -- идентификатор пакета. Тип тот же unsigned int. По замыслу, при отправке пакет "помечается" случайным числом, что может быть использовано для повторной отправки одного и того же пакета несколько раз с каким-то интервалом -- ввиду ненадёжности протокола это имеет смысл, но принимающее устройство должно фильтровать повторные команды дабы не выполнять одно и то же действие в качестве реакции на пакет данных.

command -- тип команды. Тип данных byte (длина 1 байт, диапазон значений от 0 до 255). Это так называемый "класс команды", а по сути информация о том, что за данные мы посылаем. Например, мы можем составить собственную таблицу команд, отведя для команды управления открытием/закрытием номер 10, а для команды передачи данных о температуре номер 15. Главное, чтобы эта таблица у нас была постоянной. А можно поступить ещё хитрее -- подсмотреть возможные команды в том же протоколе ZWave и использовать у себя их таблицу, чтобы было всё "как у взрослых", и не нужно было заботиться о сохранности этих ценных сведений.

data -- собственно данные. Тип данных int (длина 2 байта, диапазон значений от -32,768 до 32,767. В этом поле мы передаём непосредственно данные в виде одного числа. Мало? Ну мне показалось достаточным. Температуру можно передать (например условившись, что она умножена на 100), статус датчика движения -- легко, команду для приёмника с реле -- проще простого. Текстовые данные на внешний дисплей, конечно, не отправишь, но такой цели и не ставилось, а для моих настоящих и будущих устройств хватит за глаза и пары десятков чисел, чтобы описать все возможные команды.

В итоге мы имеем длину пакета равную 9 байт. Короткий пакет, на самом деле, очень хорошо -- во-первых, меньше вероятность, что он по дороге "сломается"; во-вторых, меньше время на пересылку, что уменьшает вероятность совместного использования эфира несколькими устройствами. Кстати, последнее обстоятельства потребует "экономно", т.е. не часто слать информацию. При этом желательно, чтобы при переодической отправке показаний промежуток между сеансами несколько варьировался. Но это всё надо уже предусматривать при интеграции конкретного устройства. Как бы то ни было, я бы не советывал слишком налегать на универсальность структуры в ущерб минимального размера пакета передаваемых данных.

Итак, мы определились со структурой пакета, теперь нужно реализовать обмен. Тут нам на помощь приходит ещё одна полезная библиотека под названием EasyTransfer. Собственно, она работает "поверх" VirtualWire, позволяя описать структуру данных на приёмнике и передатчике и вести обмен уже не набором байт-кодов, а указанной структурой целиком.

В нашем случае структура данных будет иметь следующий вид:

struct SEND_DATA_STRUCTURE{
  unsigned int device_id;
  unsigned int destination_id;    
  unsigned int packet_id;
  byte command;
  int data;
};

Крайне важно, чтобы структура на приёмнике и передачтике была один-в-один, иначе мы будем получать некорректную информацию. Собственно, поэтому и важно заранее определиться со структурой пакета.

Пару слов про поле device_id. Его можно задавать вручную для каждого устройства, но я пошёл по более простому пути -- при первом запуске генерирую это значений случайным образом и записываю в энерго-независимую область памяти EEPROM. Вероятность того, что разные устройства получают одинаковые идентификаторы из дипазона значений поля unsigned int крайне мала и, опять же, в моём случае риск вполне оправдан.

Применим полученные знания, для написания примера реализации на нашем протоколе обмена. Передатчик у нас будет отправлять значение внутреннего счётчика, а приёмник его отображать.

Код передатчика:

Нажмите для отображения кода

#include <VirtualWire.h>
#include <EasyTransferVirtualWire.h>
#include <EEPROM.h> // эта библиотека нужна для работы с энерго-независимой памятью

const int led_pin = 13;
const int transmit_pin = 2;
unsigned int unique_device_id = 0;
unsigned int count = 1;

//create object
EasyTransferVirtualWire ET; 

struct SEND_DATA_STRUCTURE{
  //наша структура данны. она должна быть определена одинаково на приёмнике и передатчике
  //кроме того, размер структуры не должен превышать 26 байт (ограничение VirtualWire)
  unsigned int device_id;
  unsigned int destination_id;    
  unsigned int packet_id;
  byte command;
  int data;
};

//переменная с данными нашей структуры
SEND_DATA_STRUCTURE mydata;

//ниже пару функций для записи данных типа unsigned int в EEPROM
void EEPROMWriteInt(int p_address, unsigned int p_value)
      {
      byte lowByte = ((p_value >> 0) & 0xFF);
      byte highByte = ((p_value >> 8) & 0xFF);

      EEPROM.write(p_address, lowByte);
      EEPROM.write(p_address + 1, highByte);
      }

unsigned int EEPROMReadInt(int p_address)
      {
      byte lowByte = EEPROM.read(p_address);
      byte highByte = EEPROM.read(p_address + 1);

      return ((lowByte << 0) & 0xFF) + ((highByte << 8) & 0xFF00);
      }

void setup()
{
  // блок инициализации
  pinMode(led_pin, OUTPUT);

  ET.begin(details(mydata));
  vw_set_tx_pin(transmit_pin); //установка пина, к которому подключен data-вход передатчика
  vw_setup(2000);        //скорость передачи
  Serial.begin(9600);
  randomSeed(analogRead(0));


  // Читаем/записываем Device ID
  Serial.print("Getting Device ID... "); 
  unique_device_id=EEPROMReadInt(0);
  if (unique_device_id<10000 || unique_device_id>60000) {
   Serial.print("N/A, updating... "); 
   unique_device_id=random(10000, 60000);
   EEPROMWriteInt(0, unique_device_id);
  }
  Serial.println(unique_device_id);

}

void loop()
{
  mydata.device_id = unique_device_id;
  mydata.destination_id = 0;
  mydata.packet_id = random(65535);
  mydata.command = 0;
  mydata.data = count;

  digitalWrite(led_pin, HIGH); // включаем светодиод для отображения процесса передачи
  Serial.print("Transmitting packet ");   
  Serial.print(mydata.packet_id); 
  Serial.print(" device id ");   
  Serial.print(mydata.device_id);   
  Serial.print(" data: "); 
  Serial.print(mydata.data);
  Serial.print(" ... "); 
  ET.sendData(); // отправка данных

  digitalWrite(led_pin, LOW);
  Serial.println("DONE");
  delay(1000);
  count = count + 1;
}

Приёмник в нашем случае будет просто слушать эфир и показывать все команды, отправленные передатчиками. Под каждое устройство, принимающее команды, код нужно будет модифицировать, добавив, при необходимости, фильтры по устройству назначения и классу команд.

Код приёмника:

Нажмите для отображения кода

#include <VirtualWire.h>
#include <EasyTransferVirtualWire.h>
#include <EEPROM.h>

const int led_pin = 13;
const int receive_pin = 2;
unsigned int unique_device_id = 0;

//create object
EasyTransferVirtualWire ET; 
char buf[120];

struct SEND_DATA_STRUCTURE{
  //наша структура данны. она должна быть определена одинаково на приёмнике и передатчике
  //кроме того, размер структуры не должен превышать 26 байт (ограничение VirtualWire)
  unsigned int device_id;
  unsigned int destination_id;  
  unsigned int packet_id;
  byte command;
  int data;
};

//переменная с данными нашей структуры
SEND_DATA_STRUCTURE mydata;

//ниже пару функций для записи данных типа unsigned int в EEPROM
void EEPROMWriteInt(int p_address, unsigned int p_value)
      {
      byte lowByte = ((p_value >> 0) & 0xFF);
      byte highByte = ((p_value >> 8) & 0xFF);

      EEPROM.write(p_address, lowByte);
      EEPROM.write(p_address + 1, highByte);
      }

unsigned int EEPROMReadInt(int p_address)
      {
      byte lowByte = EEPROM.read(p_address);
      byte highByte = EEPROM.read(p_address + 1);

      return ((lowByte << 0) & 0xFF) + ((highByte << 8) & 0xFF00);
      }

void setup()
{
    pinMode(led_pin, OUTPUT);
    Serial.begin(9600); // Debugging only

    ET.begin(details(mydata));
    // Initialise the IO and ISR
    vw_set_rx_pin(receive_pin);
    vw_setup(2000);      // Скорость приёма
    vw_rx_start();       // Запуск режима приёма

  // Device ID
  Serial.print("Getting Device ID... "); 
  unique_device_id=EEPROMReadInt(0);
  if (unique_device_id<10000 || unique_device_id>60000) {
   Serial.print("N/A, updating... "); 
   unique_device_id=random(10000, 60000);
   EEPROMWriteInt(0, unique_device_id);
  }
  Serial.println(unique_device_id);

}

void loop()
{
    if(ET.receiveData()) // получили пакет данных, обрабатываем
    {
        digitalWrite(led_pin, HIGH);
        Serial.print("Got: ");
        Serial.print("Device ID: ");
        Serial.print(mydata.device_id);
        Serial.print(" Destination ID: ");
        Serial.print(mydata.destination_id);        
        Serial.print(" Packet ID: ");
        Serial.print(mydata.packet_id);
        Serial.print(" Command: ");
        Serial.print(mydata.command);
        Serial.print(" Data: ");
        Serial.print(mydata.data);
        Serial.println();
        digitalWrite(led_pin, LOW);
    }
}

Ура! Наш Skynet в эфире! Уже можно делать много полезного, но нет предела совершенству... Двигаемся дальше.

Интеграция в MajorDoMo

Следующий этап -- встраивание всего нашего "хозяйства" в более сложную среду управления Умным Домом. В данном случае, используется платформа MajorDoMo, но аналогичным образом может быть организована интеграция с любой другой системой.

Собственно, принцип интеграции в организации "моста" между компьютером и нашей радио-сетью. Ниже я привожу пример создания "слушающего моста", задача которого в том, чтобы "слушать" эфир и транслировать все принятые пакеты в среду MajorDoMo. Последняя, в свою очередь, уже будет заниматься их обработкой -- реагировать какими-то действиями или же просто выводить получаемые данные в различных интерфейсах.

В панели управления сценариями создадим скрипт приёма сообщений под названием easyRF. Код сценария:

$device_id=$params['did'];
$destination_id=$params['dest'];
$packet_id=$params['pid'];
$command_id=$params['c'];
$data=$params['d'];
say("От устройства $device_id пришёл пакет $packet_id с командой $command_id и данными $data");
После добавления данный код сразу можно вызвать по http-ссылке:
http://192.168.0.17/objects/?script=easyRF

(вместо 192.168.0.17 адрес вашего сервера)

Следующий шаг, это трансляция принимаемых данных от Arduino в систему MajorDoMo. Тут есть варианты -- можно добавить на Arduino-приёмник модуль ethernet и сразу посылать http-запросы по сети, а можно подключить микро-контроллер по USB и воспользоваться программой ArduinoGW, которая "слушает" COM-порт и при наличии ключевой последовательности, соответствующей отправки http-запроса, сама переадресует его в сеть.

Воспользуемся вторым способом, т.к. он не требует дополнительного оборудования. В таком случае, код приёмника будет выглядеть вот так:

Нажмите для отображения кода

#include <VirtualWire.h>
#include <EasyTransferVirtualWire.h>
#include <EEPROM.h>

const int led_pin = 13;
const int receive_pin = 2;
unsigned int unique_device_id = 0;

//create object
EasyTransferVirtualWire ET; 
char buf[120];

struct SEND_DATA_STRUCTURE{
  //наша структура данны. она должна быть определена одинаково на приёмнике и передатчике
  //кроме того, размер структуры не должен превышать 26 байт (ограничение VirtualWire)
  unsigned int device_id;
  unsigned int destination_id;  
  unsigned int packet_id;
  byte command;
  int data;
};

//переменная с данными нашей структуры
SEND_DATA_STRUCTURE mydata;

//ниже пару функций для записи данных типа unsigned int в EEPROM
void EEPROMWriteInt(int p_address, unsigned int p_value)
      {
      byte lowByte = ((p_value >> 0) & 0xFF);
      byte highByte = ((p_value >> 8) & 0xFF);

      EEPROM.write(p_address, lowByte);
      EEPROM.write(p_address + 1, highByte);
      }

unsigned int EEPROMReadInt(int p_address)
      {
      byte lowByte = EEPROM.read(p_address);
      byte highByte = EEPROM.read(p_address + 1);

      return ((lowByte << 0) & 0xFF) + ((highByte << 8) & 0xFF00);
      }

void setup()
{
    pinMode(led_pin, OUTPUT);
    Serial.begin(9600); // Debugging only

    ET.begin(details(mydata));
    // Initialise the IO and ISR
    vw_set_rx_pin(receive_pin);
    vw_setup(2000);      // Bits per sec
    vw_rx_start();       // Start the receiver PLL running

  // Device ID
  Serial.print("Getting Device ID... "); 
  unique_device_id=EEPROMReadInt(0);
  if (unique_device_id<10000 || unique_device_id>60000) {
   Serial.print("N/A, updating... "); 
   unique_device_id=random(10000, 60000);
   EEPROMWriteInt(0, unique_device_id);
  }
  Serial.println(unique_device_id);

}

void loop()
{
    if(ET.receiveData())  // получили пакет данных, обрабатываем
    {
        digitalWrite(led_pin, HIGH);
        Serial.print("Got: ");
        Serial.print("Device ID: ");
        Serial.print(mydata.device_id);
        Serial.print(" Destination ID: ");
        Serial.print(mydata.destination_id);        
        Serial.print(" Packet ID: ");
        Serial.print(mydata.packet_id);
        Serial.print(" Command: ");
        Serial.print(mydata.command);
        Serial.print(" Data: ");
        Serial.print(mydata.data);
        Serial.println();
        digitalWrite(led_pin, LOW);
        sprintf(buf, "GET /objects/?script=easyRF&did=%u&dest=%u&pid=%u&c=%u&d=%i HTTP/1.0", (int)mydata.device_id, (int)mydata.destination_id, (int)mydata.packet_id, (int)mydata.command, (int)mydata.data);
        Serial.println(buf); // выводим строку со ссылкой для HTTP-запроса (здесь может быть добавлено использование ethernet-shield-а
        Serial.println();        
    }
}

Вот и всё! Создаём устройства, добавляем радио-модули и настраиваем взаимодействия всего и вся.

Дальнейшее развитие

Как я писал выше, данную статью можно рассматривать как концепцию идеи, развивать которую можно во многих направлениях, причём даже не меняя исходную структуру пакета.

Приведу несколько мыслей, пришедших в голову:

  • Создание узлов "надёжного" обмена (используем на одном устройстве и приёмник и передатчик и организуем обмен пакетами с контролем доставки)
  • Используем более дорогие и надёжные радио-модули
  • Реализуем процедуру "привязки" одного устройства к другому без необходимости изменения кода (перевод в режим "привязки" двух устройств и запись в EEPROM парного устройства)

На этом совсем всё. Спасибо за внимание!

UPDATE Пример кода для передатчика с подключенным датчиком температуры на базе DS18B20 и датчиком влажности DHT11

Нажмите для отображения кода

#include <OneWire.h>
#include <DallasTemperature.h>
#include <dht11.h>
#include <VirtualWire.h>
#include <EasyTransferVirtualWire.h>
#include <EEPROM.h> //Needed to access the eeprom read write functions

#define PIN_LED 13
#define PIN_LED_T 12
#define PIN_DHT 3
#define PIN_TEMP 4  
#define PIN_RF 6

#define PERIOD_READ_TEMP 20 // seconds
#define PERIOD_SEND_TEMP 10 // minutes
#define PERIOD_READ_HUM 30 // seconds
#define PERIOD_SEND_HUM 15 // minutes


float sent_temperature=0;
int sent_temperature_h=0;
int sent_humidity=0;
//int sent_pir=0;
int led_status=0;

unsigned int unique_device_id = 0;

dht11 DHT11;

int old_status=0;
int delay_value=1;

long int timeCheckedTemp=10000000;
long int timeCheckedHum=10000000;
long int timesentTemp=0;
long int timesentHum=0;
long int timeChangedLed=0;


// Setup a oneWire instance to communicate with any OneWire devices 
// (not just Maxim/Dallas temperature ICs)
OneWire oneWire(PIN_TEMP);

// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);

//create object
EasyTransferVirtualWire ET; 

struct SEND_DATA_STRUCTURE{
  //put your variable definitions here for the data you want to send
  //THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO
  //Struct can'e be bigger then 26 bytes for VirtualWire version
  unsigned int device_id;
  unsigned int destination_id;    
  unsigned int packet_id;
  byte command;
  int data;
};

//give a name to the group of data
SEND_DATA_STRUCTURE mydata;

//This function will write a 2 byte integer to the eeprom at the specified address and address + 1
void EEPROMWriteInt(int p_address, unsigned int p_value)
{
  byte lowByte = ((p_value >> 0) & 0xFF);
  byte highByte = ((p_value >> 8) & 0xFF);

  EEPROM.write(p_address, lowByte);
  EEPROM.write(p_address + 1, highByte);
}

//This function will read a 2 byte integer from the eeprom at the specified address and address + 1
unsigned int EEPROMReadInt(int p_address)
{
  byte lowByte = EEPROM.read(p_address);
  byte highByte = EEPROM.read(p_address + 1);

  return ((lowByte << 0) & 0xFF) + ((highByte << 8) & 0xFF00);
}

void blinking(int count) {
 for(int i=0;i<count;i++) {
  digitalWrite(PIN_LED, HIGH); 
  delay(500);
  digitalWrite(PIN_LED, LOW);
  delay(500);
 }
}

void setup()
{

  sensors.begin();
  pinMode(PIN_LED, OUTPUT);
  pinMode(PIN_LED_T, OUTPUT); 
  //pinMode(PIN_PIR, INPUT);   
  Serial.begin(9600); 

  ET.begin(details(mydata));
  vw_set_tx_pin(PIN_RF);
  vw_setup(2000);        // Bits per sec
  randomSeed(analogRead(0));


  // Device ID
  Serial.print("Getting Device ID... "); 
  unique_device_id=EEPROMReadInt(0);
  if (unique_device_id<10000 || unique_device_id>60000) {
    Serial.print("N/A, updating... "); 
    unique_device_id=random(10000, 60000);
    EEPROMWriteInt(0, unique_device_id);
  }
  Serial.println(unique_device_id); 

  mydata.device_id = unique_device_id;
  mydata.destination_id = 0;  

}

void loop()
{


 if (timeCheckedTemp>PERIOD_READ_TEMP) {
  // TEMP SENSOR 1
  float current_temp=0;
  // Соединяемся
  sensors.requestTemperatures();
  current_temp=sensors.getTempCByIndex(0); // получаем температуру
  if (current_temp == 0.00) { // иногда датчик почему-то зависает и показывае 0.00, сбрасываем тогда
    Serial.println("Temp sensor error");
    blinking(3);
    sensors.begin();
  } else {
   timeCheckedTemp=0;
   Serial.print("T: "); 
   Serial.println(current_temp);   
   if ((current_temp<50) && (current_temp>-5) && ((abs(sent_temperature-current_temp)>=0.3) || (timesentTemp>(PERIOD_SEND_TEMP*60)))) {
    Serial.print(millis()/1000);
    //Serial.print(" sent: ");
    //Serial.print(timesentTemp);
    //Serial.print(" compare: ");
    //Serial.print((PERIOD_SEND_TEMP*60));     
    //Serial.print(" diff ");    
    //Serial.print(abs(sent_temperature-current_temp));  
    mydata.packet_id = random(65535);
    mydata.command = 10; //temp
    mydata.data = (int)(current_temp*100);
    Serial.print("Transmitting packet ... "); 
    digitalWrite(PIN_LED_T, HIGH); 
    ET.sendData();
    digitalWrite(PIN_LED_T, LOW);
    Serial.println("DONE");
    timesentTemp=0;   
    sent_temperature=(float)current_temp;   
   }
  }
 }

 if (timeCheckedHum>PERIOD_READ_HUM) {
  int chk = DHT11.read(PIN_DHT);
  switch (chk)
  {
  case 0: 
    timeCheckedHum=0;
    break;
  case -1: 
    Serial.println("DHT11 Checksum error"); 
	blinking(4);
    break;
  case -2: 
    Serial.println("DHT11 Time out error"); 
	blinking(5);
    break;
  default: 
    Serial.println("DHT11 Unknown error"); 
    break;
  }

  int current_humidity=(int)DHT11.humidity;
  Serial.print("H: ");
  Serial.println(current_humidity);   
  if ((chk==0) && (current_humidity>0) && (current_humidity<=100) && (abs(current_humidity-sent_humidity)>=1 || (timesentHum>(PERIOD_SEND_HUM*60))))  { 
    //Serial.print(millis()/1000); 
    //Serial.print(" sent: ");
    //Serial.print(timesentHum);
    //Serial.print(" compare: ");
    //Serial.print((PERIOD_SEND_HUM*60));    
    //Serial.print(" diff ");    
     //Serial.print(abs(current_humidity-sent_humidity));
    mydata.packet_id = random(65535);
    mydata.command = 11; //humidity
    mydata.data = (int)current_humidity;
    Serial.print("Transmitting packet ... "); 
    digitalWrite(PIN_LED_T, HIGH); 
    ET.sendData();
    digitalWrite(PIN_LED_T, LOW);
    Serial.println("DONE");
    timesentHum=0;  
    sent_humidity=(int)current_humidity;
  }
 }

  if (timeChangedLed>=2) {
    timeChangedLed=0;
    if (led_status==1) {
     led_status=0;
     digitalWrite(PIN_LED, LOW);
    } else {
      led_status=1;
      digitalWrite(PIN_LED, HIGH);
    }
  }

  timesentTemp+=delay_value;
  timeCheckedTemp+=delay_value;  
  timesentHum+=delay_value;
  timeCheckedHum+=delay_value;  
  timeChangedLed+=delay_value;
  delay(delay_value*1000); // задержка. 

}

Пример кода приёмника с подключенным ethernet-shield-ом

Нажмите для отображения кода

#include <VirtualWire.h>
#include <EasyTransferVirtualWire.h>
#include <EEPROM.h> //Needed to access the eeprom read write functions
#include <Ethernet.h>
#include <SPI.h>

const int led_pin = 13;
const int receive_pin = 2;
unsigned int unique_device_id = 0;

//create object
EasyTransferVirtualWire ET; 
char buf[120];

byte mac[] = { 0x00, 0x2A, 0xB6, 0x14, 0x38, 0xAC };
IPAddress server(192,168,0,17);
//IPAddress ip(192, 168, 0, 72);
EthernetClient client;

int ethernetAvailable=0;
unsigned long lastConnectionTime = 0;          // last time you connected to the server, in milliseconds
boolean lastConnected = false;                 // state of the connection last time through the main loop


struct SEND_DATA_STRUCTURE{
  //put your variable definitions here for the data you want to send
  //THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO
  //Struct can'e be bigger then 26 bytes for VirtualWire version
  unsigned int device_id;
  unsigned int destination_id;  
  unsigned int packet_id;
  byte command;
  int data;
};

//give a name to the group of data
SEND_DATA_STRUCTURE mydata;

//This function will write a 2 byte integer to the eeprom at the specified address and address + 1
void EEPROMWriteInt(int p_address, unsigned int p_value)
      {
      byte lowByte = ((p_value >> 0) & 0xFF);
      byte highByte = ((p_value >> 8) & 0xFF);

      EEPROM.write(p_address, lowByte);
      EEPROM.write(p_address + 1, highByte);
      }

//This function will read a 2 byte integer from the eeprom at the specified address and address + 1
unsigned int EEPROMReadInt(int p_address)
      {
      byte lowByte = EEPROM.read(p_address);
      byte highByte = EEPROM.read(p_address + 1);

      return ((lowByte << 0) & 0xFF) + ((highByte << 8) & 0xFF00);
      }

void sendHTTPRequest() {
  Serial.println(buf);   
  if (ethernetAvailable==1) {
   if (client.connected()) {
     client.stop();
   }
   if (client.connect(server, 80)) {
     Serial.println("connected");
     // Make a HTTP request:
     client.println(buf);
     client.println("User-Agent: arduino-ethernet");     
     client.println("Connection: close");     
     client.println();
     lastConnectionTime = millis();     
   } 
   else {
    Serial.println("connection failed");
    Serial.println("disconnecting.");    
    client.stop();
   } 
  }
}


void setup()
{
    pinMode(led_pin, OUTPUT);
    Serial.begin(9600); // Debugging only

  // start the Ethernet connection:
  if (Ethernet.begin(mac) == 0) {
    Serial.println("Failed to configure Ethernet");
    ethernetAvailable=0;
  } else {
   ethernetAvailable=1;
   delay(1000);
   Serial.print("My IP address: ");
   Serial.println(Ethernet.localIP());   
  }

    ET.begin(details(mydata));
    // Initialise the IO and ISR
    vw_set_rx_pin(receive_pin);
    vw_setup(2000);      // Bits per sec
    vw_rx_start();       // Start the receiver PLL running

  // Device ID
  Serial.print("Getting Device ID... "); 
  unique_device_id=EEPROMReadInt(0);
  if (unique_device_id<10000 || unique_device_id>60000) {
   Serial.print("N/A, updating... "); 
   unique_device_id=random(10000, 60000);
   EEPROMWriteInt(0, unique_device_id);
  }
  Serial.println(unique_device_id);

}

void loop()
{

    if (client.available()) {
    char c = client.read();
    Serial.print(c);
  }  
  if (!client.connected() && lastConnected) {
    Serial.println();
    Serial.println("disconnecting.");
    client.stop();
  } 

    if(ET.receiveData())
    {
        digitalWrite(led_pin, HIGH);
        Serial.print("Got: ");
        Serial.print("Device ID: ");
        Serial.print(mydata.device_id);
        Serial.print(" Destination ID: ");
        Serial.print(mydata.destination_id);        
        Serial.print(" Packet ID: ");
        Serial.print(mydata.packet_id);
        Serial.print(" Command: ");
        Serial.print(mydata.command);
        Serial.print(" Data: ");
        Serial.print(mydata.data);
        Serial.println();
        digitalWrite(led_pin, LOW);
        sprintf(buf, "GET /objects/?script=easyRF&did=%u&dest=%u&pid=%u&c=%u&d=%i HTTP/1.0", (int)mydata.device_id, (int)mydata.destination_id, (int)mydata.packet_id, (int)mydata.command, (int)mydata.data);
        sendHTTPRequest();
        Serial.println();        
    }

   lastConnected = client.connected(); 

}
 
 

Обсуждение

 

sergejey: Обсуждение страницы Сеть беспроводных устройств на Arduino

Vadymkyr: если подать питанием в 12 вольт на приемник и передатчик, то значительно увеличится радиус приемо-передачи.

sergejey: если подать питанием в 12 вольт на приемник и передатчик, то значительно увеличится радиус приемо-передачи. Спасибо, добавлю в статью :)

Alex: Тема — супер. Очень актуально. Пара мыслей. Контроль доставки очень важен. Две большие разницы — послал и надеешься, что дошло и точно знаешь, что команда выполнена. Возникают вопросы с помехозащищённостью. Насколько это хозяйство может противостоять случайным/специальным помехам? Ну и вопрос с шифрованием, хотя бы примитивно-бытовым, чтобы отсечь 99% местных «хакеров», которым делать нечего.

sergejey: Тема — супер. Очень актуально. Пара мыслей. Контроль доставки очень важен. Две большие разницы — послал и надеешься, что дошло и точно знаешь, что команда выполнена. Возникают вопросы с помехозащищённостью. Насколько это хозяйство может противостоять случайным/специальным помехам? Ну и вопрос с шифрованием, хотя бы примитивно-бытовым, чтобы отсечь 99% местных «хакеров», которым делать нечего. Всё это можно реализовать уже в качестве развития концепции. Но для моих скромных нужд этого не тебуется :) Контроль доставки мне не критичен -- я планирую сделать для начала сеть температурных датчиков вещающих раз в несколько минут, так что если раз в полчаса будет пакет доходить, то и ладно -- мгновенная температура мне не обязательна. Для датчиков движения можно сделать отправку партии пакетов в 10 штук с промежутком в 10-15 секунд -- хоть один да дойдёт скорее всего. Ну или на самом деле поставить приёмник и использовать один из классов команд как подтверждение о доставке пакета. Про помехи и шифрование -- не думаю, что использовать радио-эфир стоит использовать для чего-то сколь-либо "секретного" или критически-важного. Помехи на частоту поставить очень легко и тогда вся сеть просто ляжет. Про безопасность аналогично -- не стоит делать открытие дверей через эту технологию. Это просто возможность легко и дёшево передавать в систему различную сервисную информацию -- те же данные по климату (температура, влажность и т.п.)

arturmon: может стоило вот на таких сделать сеть NRF24L01+?

sergejey: может стоило вот на таких сделать сеть NRF24L01+? На хабре уже несколько человек тоже предложили такое :) Вероятно, да. Но это статья о концепции -- она с большего не изменится, только немного доработается код для поддержки гарантированной доставки пакетов. Закажу себе десяток NRF24L01+ и може в самом деле всё на них сделаю.

Vadymkyr: будем ждать статью. С этими радиомодулями я давно разобрался и уже большее полугода они у меня работают. А вот про NRF24L01+ интересно бы было почитать

ser009: У меня 5 шт лежит + 5 мини артуино под них. Видел адаптер USB под эти модули

MaksMS82: Встречал несколько упоминаний ,что существует 2 версии NRF24L01 и NRF24L01+ ,только напомните в чем их отличие ?.Сам брал эти: http://www.buyincoins.com/new_en/detail ... -9036.html ,но пока не испытывал в работе..

sergejey: У меня 5 шт лежит + 5 мини артуино под них. Видел адаптер USB под эти модули а что за USB-адаптеры? я скорее буду делать ethernet-шлюз, но всё равно интересно.

ser009: Вот. http://www.aliexpress.com/item/NRF24L01 ... 87945.html NRF24L01 USB модуль Я видел и другой модификации

Vadymkyr: вот еще интересный комплект RF-2410U RF-2400x5 2.4G RF Module Kit и вот статья по их использованию Использование радио модулей RF-2400 от Inhaos

sergejey: Смущает, что многие жалуются на дальность работы 2.4GHz модулей, а так же помехи от WiFi. Никто не сталкивался? Всё-таки, если частота по-меньше, то с проходимостью сигнала проще, нет?

Vadymkyr: Если будет совсем плохо, думаю такой вариант поможет 2.4G NRF24L01+ Wireless Transceiver Module + SMA Antenna, но мне кажется, в пределах квартиры проблем возникнуть не должно. У себя дома для всем приемникам и передатчикам (433) напаял антенны 1/2 волны и ни каких проблем. Для меня главный минус в них, что может передавать одновременно только один передатчик.

arturmon: по моему самое интересное это mirf. http://habrahabr.ru/post/171613/ второй пост, про дальность этих радио модулей.

sergejey: Добавил в конце статьи два готовых скетча -- один для передатчика с подключенным DS18B20 (температура) и DHT11 (влажность); второй -- для приёмника с ethernet-shield-ом. Тестирую дома эту связку -- пока вроде работает.

sergejey: В этой ветке добавил скетч ethernet-передатчика команд в эфир: viewtopic.php?f=8&t=654

elik745i: Ребят, не пашет, пытаюсь вывести данные на сцену, вот код метода: $old_temp=$this->getProperty("temp"); $t=round($params*2)/2; $this->setProperty("temp",$t); if ($t>$old_temp) { $d=1; } elseif ($t<$old_temp) { $d=-1; } else { $d=0; } $this->setProperty("direction",$d); $this->setProperty("updated",time()); $this->setProperty("updatedTime",date("H:i",time())); что не так?

elik745i: Добавил в конце статьи два готовых скетча -- один для передатчика с подключенным DS18B20 (температура) и DHT11 (влажность); второй -- для приёмника с ethernet-shield-ом. Тестирую дома эту связку -- пока вроде работает. У меня не заработали, вот мой вариант: #include <VirtualWire.h> #include <EasyTransferVirtualWire.h> #include <EEPROM.h> // эта библиотека нужна для работы с энерго-независимой памятью #include <dht11.h> dht11 DHT11; #define DHT11PIN A0 const int led_pin = 13; const int transmit_pin = A1; unsigned int unique_device_id = 0; unsigned int count = 1; int predefined_device_id=51554; int predefined_destination_id=29249; int SensorReadDelay=0.5; //Delay between sensor read [Sec] //create object EasyTransferVirtualWire ET; struct SEND_DATA_STRUCTURE{ //наша структура данны. она должна быть определена одинаково на приёмнике и передатчике //кроме того, размер структуры не должен превышать 26 байт (ограничение VirtualWire) unsigned int device_id; unsigned int destination_id; unsigned int packet_id; byte command; unsigned int data; }; //переменная с данными нашей структуры SEND_DATA_STRUCTURE mydata; //ниже пару функций для записи данных типа unsigned int в EEPROM void EEPROMWriteInt(int p_address, unsigned int p_value) { byte lowByte = ((p_value >> 0) & 0xFF); byte highByte = ((p_value >> 8) & 0xFF); EEPROM.write(p_address, lowByte); EEPROM.write(p_address + 1, highByte); } unsigned int EEPROMReadInt(int p_address) { byte lowByte = EEPROM.read(p_address); byte highByte = EEPROM.read(p_address + 1); return ((lowByte << 0) & 0xFF) + ((highByte << 8) & 0xFF00); } void humidityRead(void) { Serial.println("\n"); int chk = DHT11.read(DHT11PIN); Serial.print("Read sensor: "); switch (chk) { case 0: Serial.println("OK"); break; case -1: Serial.println("Checksum error"); break; case -2: Serial.println("Time out error"); break; default: Serial.println("Unknown error"); break; } Serial.print("Humidity (%): "); Serial.println(DHT11.humidity); mydata.device_id = unique_device_id; mydata.destination_id = predefined_destination_id; mydata.packet_id = random(65535); mydata.command = 1; //Humidity mydata.data = DHT11.humidity; digitalWrite(led_pin, HIGH); // включаем светодиод для отображения процесса передачи Serial.print("Transmitting packet "); Serial.print(mydata.packet_id); Serial.print(" device id "); Serial.print(mydata.device_id); Serial.print(" data: "); Serial.print(mydata.data); Serial.print(" ... "); ET.sendData(); // отправка данных digitalWrite(led_pin, LOW); Serial.println("DONE"); } void temperatureRead(void) { Serial.println("\n"); int chk = DHT11.read(DHT11PIN); Serial.print("Read sensor: "); switch (chk) { case 0: Serial.println("OK"); break; case -1: Serial.println("Checksum error"); break; case -2: Serial.println("Time out error"); break; default: Serial.println("Unknown error"); break; } Serial.print("Temperature (oC): "); Serial.println(DHT11.temperature); mydata.device_id = unique_device_id; mydata.destination_id = predefined_destination_id; mydata.packet_id = random(65535); mydata.command = 2; //Temperature mydata.data = DHT11.temperature; digitalWrite(led_pin, HIGH); // включаем светодиод для отображения процесса передачи Serial.print("Transmitting packet "); Serial.print(mydata.packet_id); Serial.print(" device id "); Serial.print(mydata.device_id); Serial.print(" data: "); Serial.print(mydata.data); Serial.print(" ... "); ET.sendData(); // отправка данных digitalWrite(led_pin, LOW); Serial.println("DONE"); } void setup() { // блок инициализации pinMode(led_pin, OUTPUT); ET.begin(details(mydata)); vw_set_tx_pin(transmit_pin); //установка пина, к которому подключен data-вход передатчика vw_setup(2000); //скорость передачи Serial.begin(9600); randomSeed(analogRead(0)); // Читаем/записываем Device ID Serial.print("Setting Device ID... "); EEPROMWriteInt(0, predefined_device_id); unique_device_id=EEPROMReadInt(0); if (unique_device_id<10000 || unique_device_id>60000) { Serial.print("N/A, updating... "); unique_device_id=random(10000, 60000); EEPROMWriteInt(0, unique_device_id); } Serial.println(unique_device_id); Serial.println("DHT11 TEST PROGRAM "); Serial.print("LIBRARY VERSION: "); Serial.println(DHT11LIB_VERSION); Serial.println(); } void loop() { humidityRead(); delay(SensorReadDelay*1000); temperatureRead(); delay(SensorReadDelay*1000); } и для ресивера: #include <VirtualWire.h> #include <EasyTransferVirtualWire.h> #include <EEPROM.h> //Needed to access the eeprom read write functions //#include <Ethernet.h> #include <SPI.h> const int led_pin = 13; const int receive_pin = A0; unsigned int unique_device_id = 0; int predefined_device_id =29249; //create object EasyTransferVirtualWire ET; char buf[120]; /* byte mac[] = { 0x00, 0x2A, 0xB6, 0x14, 0x38, 0xAC }; IPAddress server(192,168,0,17); //IPAddress ip(192, 168, 0, 72); EthernetClient client; int ethernetAvailable=0; unsigned long lastConnectionTime = 0; // last time you connected to the server, in milliseconds boolean lastConnected = false; // state of the connection last time through the main loop */ struct SEND_DATA_STRUCTURE{ //put your variable definitions here for the data you want to send //THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO //Struct can'e be bigger then 26 bytes for VirtualWire version unsigned int device_id; unsigned int destination_id; unsigned int packet_id; byte command; int data; }; //give a name to the group of data SEND_DATA_STRUCTURE mydata; //This function will write a 2 byte integer to the eeprom at the specified address and address + 1 void EEPROMWriteInt(int p_address, unsigned int p_value) { byte lowByte = ((p_value >> 0) & 0xFF); byte highByte = ((p_value >> 8) & 0xFF); EEPROM.write(p_address, lowByte); EEPROM.write(p_address + 1, highByte); } //This function will read a 2 byte integer from the eeprom at the specified address and address + 1 unsigned int EEPROMReadInt(int p_address) { byte lowByte = EEPROM.read(p_address); byte highByte = EEPROM.read(p_address + 1); return ((lowByte << 0) & 0xFF) + ((highByte << 8) & 0xFF00); } /* void sendHTTPRequest() { Serial.println(buf); if (ethernetAvailable==1) { if (client.connected()) { client.stop(); } if (client.connect(server, 80)) { Serial.println("connected"); // Make a HTTP request: client.println(buf); client.println("User-Agent: arduino-ethernet"); client.println("Connection: close"); client.println(); lastConnectionTime = millis(); } else { Serial.println("connection failed"); Serial.println("disconnecting."); client.stop(); } } } */ void setup() { pinMode(led_pin, OUTPUT); Serial.begin(9600); // Debugging only /* // start the Ethernet connection: if (Ethernet.begin(mac) == 0) { Serial.println("Failed to configure Ethernet"); ethernetAvailable=0; } else { ethernetAvailable=1; delay(1000); Serial.print("My IP address: "); Serial.println(Ethernet.localIP()); } */ ET.begin(details(mydata)); // Initialise the IO and ISR vw_set_rx_pin(receive_pin); vw_setup(2000); // Bits per sec vw_rx_start(); // Start the receiver PLL running // Device ID Serial.print("Setting Device ID... "); EEPROMWriteInt(0, predefined_device_id); unique_device_id=EEPROMReadInt(0); if (unique_device_id<10000 || unique_device_id>60000) { Serial.print("N/A, updating... "); unique_device_id=random(10000, 60000); EEPROMWriteInt(0, unique_device_id); } Serial.println(unique_device_id); } void loop() { /* if (client.available()) { char c = client.read(); Serial.print(c); } if (!client.connected() && lastConnected) { Serial.println(); Serial.println("disconnecting."); client.stop(); } */ if(ET.receiveData()) { digitalWrite(led_pin, HIGH); Serial.print("Got: "); Serial.print("Device ID: "); Serial.print(mydata.device_id); Serial.print(" Destination ID: "); Serial.print(mydata.destination_id); Serial.print(" Packet ID: "); Serial.print(mydata.packet_id); Serial.print(" Command: "); Serial.print(mydata.command); Serial.print(" Data: "); Serial.print(mydata.data); Serial.println(); digitalWrite(led_pin, LOW); sprintf(buf, "GET /objects/?script=easyRF&did=%u&dest=%u&pid=%u&c=%u&d=%i HTTP/1.0", (int)mydata.device_id, (int)mydata.destination_id, (int)mydata.packet_id, (int)mydata.command, (int)mydata.data); Serial.println(buf); // выводим строку со ссылкой для HTTP-запроса (здесь может быть добавлено использование ethernet-shield-а //sendHTTPRequest(); Serial.println(); } //lastConnected = client.connected(); }

elik745i: Кстати ресивер не терпит более 5и вольтов питания, то есть не сгорит, но и стабильно не будет работать, а вот трансмиттер очень даже!!!

sergejey: Кстати ресивер не терпит более 5и вольтов питания, то есть не сгорит, но и стабильно не будет работать, а вот трансмиттер очень даже!!! У меня, кстати, другая ситуация -- ресивер вообще раз через два работал, если питание 5В было. При 9В и 12В всё отлично ловит. Трансмиттер, в свою очередь, от 5В стабильно работает.

elik745i: Серег, как обстоят дела с arduino gw? Заметил такой глюк: если долго передавать инфу, то в какойто момент начинаются глюки, окошки с ошибками выскакивать, притом совершенно хаотическом порядке, к примеру сегодня перезагрузил комп, и ошибок нет... Еще есть возможность добавить в прогу опцию, чтобы при возникновении ошибок, чтобы сама перезагружалась? То есть если дома нет никого, можно было положиться полностью на систему... У меня все через USB идет, лан карточки нет пока...

MaksMS82: А как Вам вариант использования nRF24L01-USB конвертора (на базе Atmega8 перепрограммируем http://www.ebay.com/itm/330809608976 ) и клиенты на ардуинах (или просто на Atmega`х) ? В принципе очень недорогой вариант получается.Вчера попробовал такую связку -получилось успешно передать и принять данные,но пока пробовал только один передатчик. Конечно есть сложности в создании своего протокола обмена данными между nRF24L01 и компьютером,но думаю решаемо . В итоге: железо - программатор USBasp перепрограммированный в данный переходник,там уже имеется стабилизатор на 3.3в программа - написана на avr-gcc используя библиотеку V-USB ,а так же плюс консольная программа на компьютере

sergejey: А как Вам вариант использования nRF24L01-USB конвертора (на базе Atmega8 перепрограммируем http://www.ebay.com/itm/330809608976 ) и клиенты на ардуинах (или просто на Atmega`х) ? В принципе очень недорогой вариант получается.Вчера попробовал такую связку -получилось успешно передать и принять данные,но пока пробовал только один передатчик. Конечно есть сложности в создании своего протокола обмена данными между nRF24L01 и компьютером,но думаю решаемо . В итоге: железо - программатор USBasp перепрограммированный в данный переходник,там уже имеется стабилизатор на 3.3в программа - написана на avr-gcc используя библиотеку V-USB ,а так же плюс консольная программа на компьютере Имеет право на жизнь, почему нет. Для себя я всё же решил, что "разорюсь" на полноценный Arduino+Ethernetshield в качестве приёмника сигналов от беспроводных датчиков, чтобы не нагружать сервер лишней периферией и сразу отправлять с этого устройства нужные HTTP-запросы. Работает достаточно надёжно -- уже три датчика на этой технологии использую: температура+влажность, расстояние, датчик движения. С последним, кстати, прикол вышел -- был этот датчик совмещён с датчиком температуры, но при отправке пакета радио-модуль как-то влиял на датчик движения и он всегда срабатывал при этом, т.е. отправляю температуру, а затем отправляется пакет сработки датчика движения. Пришлось считывание температуры убрать. На этом же протоколе сделал управление подъёмником римских штор по команде с компьютера -- та ещё конструкция, тоже опишу как-нибудь.

sergejey: Серег, как обстоят дела с arduino gw? Заметил такой глюк: если долго передавать инфу, то в какойто момент начинаются глюки, окошки с ошибками выскакивать, притом совершенно хаотическом порядке, к примеру сегодня перезагрузил комп, и ошибок нет... Еще есть возможность добавить в прогу опцию, чтобы при возникновении ошибок, чтобы сама перезагружалась? То есть если дома нет никого, можно было положиться полностью на систему... У меня все через USB идет, лан карточки нет пока... А можно как-нибудь скриншот с ошибкой подловить?

pdmitry2: На этом же протоколе сделал управление подъёмником римских штор по команде с компьютера -- та ещё конструкция, тоже опишу как-нибудь. Очень интересно будет почитать. Тоже планирую у себя нечто подобное. Какие моторчики использовали и как с ардуино сопрягали - драйвер какой-то или ещё как? И как узнаёте в каком положении штора находится?

elik745i: Серег, как обстоят дела с arduino gw? Заметил такой глюк: если долго передавать инфу, то в какойто момент начинаются глюки, окошки с ошибками выскакивать, притом совершенно хаотическом порядке, к примеру сегодня перезагрузил комп, и ошибок нет... Еще есть возможность добавить в прогу опцию, чтобы при возникновении ошибок, чтобы сама перезагружалась? То есть если дома нет никого, можно было положиться полностью на систему... У меня все через USB идет, лан карточки нет пока... А можно как-нибудь скриншот с ошибкой подловить? Вот скриншот одной из ошибок, выскочила после часа работы программы. http://s017.radikal.ru/i429/1311/9f/28381214f89a.jpg Ещё вот какой баг, тут про это уже писали, но решения не было вроде: если частота посылки с трансмиттера более 3х минут, то выскакивает такая же ошибка, Сергей нужна ваша помощь как родителя программы!!! Ещё, не знаю писалось ли об этом уже, не подскажете как реализовать управление освещением, к примеру через такую связку: радио передатчик 433мгц присоединенный через ардуино к компьютеру через ЮСБ, на другой стороне приемник через ардуино на реле, прога все та же ардуиноГВ (смешно даже продолжить название в созвучии, надо бы переименовать или дать полное название....так мысли в слух...) Заранее благодарен!

sergejey: Вот скриншот одной из ошибок, выскочила после часа работы программы. Ещё вот какой баг, тут про это уже писали, но решения не было вроде: если частота посылки с трансмиттера более 3х минут, то выскакивает такая же ошибка, Ага, вижу -- это попробую подправить. Т.е. ошибка соединения может и была, но исправлю проблему с сообщением -- оно не должно мешать дальнейшей работе программы. Сергей нужна ваша помощь как родителя программы!!! Ещё, не знаю писалось ли об этом уже, не подскажете как реализовать управление освещением, к примеру через такую связку: радио передатчик 433мгц присоединенный через ардуино к компьютеру через ЮСБ, на другой стороне приемник через ардуино на реле, прога все та же ардуиноГВ (смешно даже продолжить название в созвучии, надо бы переименовать или дать полное название....так мысли в слух...) Заранее благодарен! GW это от Gateway :) По поводу помощи -- тут от меня как от "родителя" ничего не требуется :) На самом деле идея нормальная вполне и всё что нужно уже есть -- и приёмник/передатчик и программа ArduinoGW. Только брать да использовать как описано в идее -- вполне себе рабочий вариант.

MaksMS82: Ещё, не знаю писалось ли об этом уже, не подскажете как реализовать управление освещением, к примеру через такую связку: радио передатчик 433мгц присоединенный через ардуино к компьютеру через ЮСБ, на другой стороне приемник через ардуино на реле, прога все та же ардуиноГВ (смешно даже продолжить название в созвучии, надо бы переименовать или дать полное название....так мысли в слух...) Заранее благодарен! Какой протокол будет использоваться для передачи данных ? Могу предложить вариант USB передатчика ,переделанного из USBasp за 3 бакса !!!! :) USBasp использую как макетную плату и уже много чего на нем собирал...Главное ,что вообще паять ничего не надо

sergejey: Вот скриншот одной из ошибок, выскочила после часа работы программы. Как оказалось, в дистрибутиве лежит не самая свежая версия ArduinoGateway -- более свежая была на сайте. В следующем дистрибутиве обновлю, а пока можно скачать по ссылке: http://smartliving.ru/download/arduino_gw_04.zip -- здесь проблема с окном ошибки уже исправлена по идее.

elik745i: Да конечно есть все, что надо, просто я пока все еще осваиваю языки программирования и мне не очень пока понятно по какому пути идти, все еще использую примерчики и методы описанные в нете. В общем реализовал я снятие сигнала с датчиков и высвечивание их на сцене, сцену сделал сам, правда я всегда иду по сложному пути и пришлось в 3Д (в максе) создать дом, и все остальное в нем, получилось вот так: фасад... http://s017.radikal.ru/i419/1311/5a/a2023968f078.jpg первый этаж.. http://s017.radikal.ru/i418/1311/e5/f1769275ae68.jpg второи.. http://s019.radikal.ru/i611/1311/e2/b9aa31d024fe.jpg пока еще работаю над мансардой и генератором... Далее хочу сделать управление резервным генератором по сигналу с сервера, который в данный момент в разработке (ардуино, большая печатная плата с релюшками и датчиками), плату генератора уже собрал, более или менее сносную менюшку написал, правда есть еще над чем поработать, доработать интерфейс.. вот он: печатная плата, использовал прогу для новичков Fritzing, заказал в китае (5раз дешевле, чен через прогу!!!): http://s020.radikal.ru/i709/1311/d5/1bb35986de5f.jpg сам сабж: http://s019.radikal.ru/i619/1311/9b/7611ce91cd5a.jpg a вот за что меня дома не любят :-): http://s020.radikal.ru/i716/1311/60/90c67e70ff6d.jpg вот сюда запихну сервер: http://s019.radikal.ru/i630/1311/31/0e1e83716076.jpg прикидываю размерчики...:

elik745i: Вот скриншот одной из ошибок, выскочила после часа работы программы. Как оказалось, в дистрибутиве лежит не самая свежая версия ArduinoGateway -- более свежая была на сайте. В следующем дистрибутиве обновлю, а пока можно скачать по ссылке: http://smartliving.ru/download/arduino_gw_04.zip -- здесь проблема с окном ошибки уже исправлена по идее. класс, тестирую!!!

elik745i: Вообщем вот как теперь: окна с ошибкой не появляется, но при долгом соединении ошибка всеже выходит, говорит сокет еррор, как я понимаю скорее проблема не в программке arduinoGW а скорее в MajorDomo, почему то при отсутствии передачи более чем на несколько секунд или же там где то буфер переполняется, вот скриншот, как видно MajorDomo перестала обновлять, то есть принимать данные вчера в 15:44: http://i019.radikal.ru/1311/13/568e1f03986c.jpg Вот, что получается при попытке остановить сервис и заново запустить: http://i011.radikal.ru/1311/7a/96ffe71e4929.jpg то есть ничего, помимо этого программка тормозит страшно и откликается очень медленно на команды, что говорит о пере заполнении ее буфера что ли... А вот после полной перезагрузки программы: http://s020.radikal.ru/i706/1311/29/816b0bc11946.jpg Как видно все заработало и прога начала передавать данные в MajorDomo. Сергей можно с этим что то сделать? как быстрое решение могу предложить сделать так, при появлении ошибки подать команду на перезагрузку программы, если конечно же это возможно? Благодарю...

sergejey: Видимо в самом деле какая-то накладка идёт при частом обращении к MajorDoMo из ArduinoGW. Попробую разобраться.

elik745i: Еще если есть возможность рабочий примерчик передачи данных с MajorDomo обратно на релюху к примеру через туже связку: MajorDomo->arduinoGW->ардуино->433Мгц радио модуль ------приемник->ардуино->реле может где обсуждалось, не нашел... Благодарю сударь!

Ze_side: Госопода, всё перерыл, но так и не нашёл ответа на свой глупый вопрос . почему на ресивере MX-05V 4 ножки?? не пойму как подключить, к сожалению гугл помочь мне не смог ( или из моря информации я не смог выделить нужное) . GND, Vcc понятно, интересуют именно 2 ножки для DATA

MaksMS82: Там же видно,что обе средние ножки вместе соединенны - можно подключать на любой. А 4 ноги скорее всего для надежности сделано чтобы сидел в разъеме лучше,т.к. модуль большой ,в отличии от передатчика,

Ze_side: MaksMS82, спасибо! Обратить на это внимание не позволил опыт, отдалённо помню универские курсы по электроники, по этому, можно сказать с 0 грызу)

azh122: Может кто знает протокол вот таких выключателей? - http://lanbon.cn/product.asp?bid=26 Формат команд для управления другими выключателями интересует. С остальным (инициализацией и пр. для A7102C) разобрался. Даже могу одиночными пощелкать. А дальше - затык. Или может есть альтернатива с обратной связью и известным протоколом? Готовые и не дорогие

Ivan: Вопрос к Сергею. Я так понимаю у Вас 2 приёмника. 1. Для датчиков на своём протоколе, через VirtualWire. 2. Для датчиков движения с китайских сигнализаций, через RCSwitch А нельзя это всё скрестить?

MaksMS82: Хоть я и не Сергей ,но отвечу :) 2 варианта можно реализовать ,если одни будут работать на 315 мгц ,а другие на 433 мгц . Но самый простой способ я уже разработал ,который работает как раз через RCSwitch http://homes-smart.ru/index.php/oborudo ... 33-315mgts . Беспроводные датчики могут быть как на ардуине ,так и на дешевом мелкомикроконтроллере attiny13 или attiny 85

shemnik69: Да. Вот ссылка в помощь. готовые проекты http://www.getchip.net/topics/upravlenie/

sergejey: Вопрос к Сергею. Я так понимаю у Вас 2 приёмника. 1. Для датчиков на своём протоколе, через VirtualWire. 2. Для датчиков движения с китайских сигнализаций, через RCSwitch А нельзя это всё скрестить? Как рекомендовали выше, всё можно сделать на RCSwitch, но т.к. у меня уже несколько передатчиков на VirtualWire, так что использую оба протокола. К сожалению, на одном приёмнике эти две библиотеки работать не хотят.

Ivan: Я почему спрашиваю. Не хочу лепить промежуточных модулей в виде ардуины и изернет шилда. Хочу зацепить прямо на Расбери. Передатчик и приёмник. Чтобы можно было слушать датчики и управлять релюхами. Но испробовав кучу библиотек пришёл к выводу что 60% поддерживают только передачу. Приём поддерживают только какой то 1 протокол. Причём сильно грузят процессор

MaksMS82: Для малинки есть либа RCSwitch , но да, она жрет ресурсов немерено . Если езернет городить нет желания ,то можно подключить ардуинку к малинке через UART..

sergejey: А ethernet-shield и не обязательно, можно одной ардуиной обойтись подключенной к распберри. Как напрямую датчики туда подключать я не знаю, сам никогда не сталкивался.

Vip: А может кто-то выложить пример схемы подключения этих дешёвых приёмо-передатчиков к 12в для уверенного приёма? 12в и на ресивер и на передатчик? Пробовал кто-то делать трансивер из ресивера и пердатчика?

sergejey: А может кто-то выложить пример схемы подключения этих дешёвых приёмо-передатчиков к 12в для уверенного приёма? 12в и на ресивер и на передатчик? Пробовал кто-то делать трансивер из ресивера и пердатчика? А в чём проблема с 12В? Я подавал 12В на Arduino и потом подключал "+" передатчика на пин Vin на Arduino, т.е. те же 12В. Насчёт трансивера не очень понял -- у меня на одном устройстве подключен и передатчик и приёмник, каждый выполняет свою функцию, так же никаких проблем.

Vip: Сергей, спасибо за ответ. Моя проблема заключается в том, что на макетной плате работало достаточно хорошо, а когда запаял всё дело и установил в подрозетник, то при 5 вольтах и 15 см внешней антенны дальше чем на 2 метра прямой видимости не "бъёт". Ещё и приёмник "базы" должен стоять в определённом положении для того что бы на 2м расстоянии получать пакеты. С вольтажом разобрался. Не мог "вкурить" как землю от бп 12В и ардуинные 5В правильно объединить, что б не спалить. Хотел ещё уточнить 12 вольтами и приёмник и передатчик усиливаются, или только передатчик? И можете тогда добавить объединённый скетч трансивера на страницу "Сеть беспроводных устройств на Arduino".

sergejey: Сергей, спасибо за ответ. Моя проблема заключается в том, что на макетной плате работало достаточно хорошо, а когда запаял всё дело и установил в подрозетник, то при 5 вольтах и 15 см внешней антенны дальше чем на 2 метра прямой видимости не "бъёт". Ещё и приёмник "базы" должен стоять в определённом положении для того что бы на 2м расстоянии получать пакеты. С вольтажом разобрался. Не мог "вкурить" как землю от бп 12В и ардуинные 5В правильно объединить, что б не спалить. Хотел ещё уточнить 12 вольтами и приёмник и передатчик усиливаются, или только передатчик? И можете тогда добавить объединённый скетч трансивера на страницу "Сеть беспроводных устройств на Arduino". Честно говоря, у меня у самого сейчас проблема с правильным питанием этих модулей. Собрал себе устройство из Arduino Mega, 16 реле, 10 включателей, приёмник и передатчик. Подключил и вроде всё работает, но жутко ненадёжно -- через какое-то время просто перестают работать то приёмник, то передатчик (сам контроллер работает, т.к. отрабатывает корректно нажатие кнопок замыканием нужных реле). Есть подозрение, что дело как раз в питании (использую блок питания 9В и 800mA). Очень уж капризные эти модули в плане питания -- надо питать и приёмник и передатчик. Даже приёмник почему-то более критично, т.к. у меня несколько штук датчиков работают от обычных телефонных USB-зарядок и всё отлично пробивает в том числе через стены, а вот приёмникам подавай питание по-лучше. Скетч от описанного устройства ниже:
#include <VirtualWire.h>
#include <EasyTransferVirtualWire.h>
#include <EEPROM.h> //Needed to access the eeprom read write functions

#define PIN_LED (13) // INDICATOR
#define PIN_RF_R (2) // Receiver pin
#define PIN_RF_T (3) // Transmitter pin
#define RF_SEND_COUNTER (3) // number of packets to send
#define CLASS_ON (20) // rf command ON
#define CLASS_OFF (21) // rf command OFF
#define CLASS_PULSE (22) //rf command pulse

unsigned int unique_device_id 0;
unsigned int packet_received_id 0;
   
long int uptime 0;
long int old_uptime 0;

String inData;

#define TOTAL_OUTPUTS (16)
#define TOTAL_INPUTS (10)
int outputPins[TOTAL_OUTPUTS] = {2224 2628303234 ,36 2325272931333537};
int inputPins[TOTAL_INPUTS] = {38394041434547495153};
int directLinkedPins[TOTAL_INPUTS] = {22,24,26,28,30,32,34,36,23,25};

int inputPinsStatus[TOTAL_OUTPUTS];

struct SEND_DATA_STRUCTURE {
  
//put your variable definitions here for the data you want to send
  //THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO
  //Struct can'e be bigger then 26 bytes for VirtualWire version
  
unsigned int device_id;
  
unsigned int destination_id;  
  
unsigned int packet_id;
  
byte command;
  
int data;
};

SEND_DATA_STRUCTURE mydata;
EasyTransferVirtualWire ET

//This function will write a 2 byte integer to the eeprom at the specified address and address + 1
void EEPROMWriteInt(int p_addressunsigned int p_value)
      {
      
byte lowByte = ((p_value >> 0) & 0xFF);
      
byte highByte = ((p_value >> 8) & 0xFF);

      
EEPROM.write(p_addresslowByte);
      
EEPROM.write(p_address 1highByte);
      }

//This function will read a 2 byte integer from the eeprom at the specified address and address + 1
unsigned int EEPROMReadInt(int p_address)
      {
      
byte lowByte EEPROM.read(p_address);
      
byte highByte EEPROM.read(p_address 1);

      return ((
lowByte << 0) & 0xFF) + ((highByte << 8) & 0xFF00);
      }


void setup()
{
    
randomSeed(analogRead(0));
    
pinMode(PIN_LED,OUTPUT);  
    
Serial.begin(9600);

    
ET.begin(details(mydata));
    
// Initialise the IO and ISR
    
pinMode(PIN_RF_T,OUTPUT);
    
pinMode(PIN_RF_R,INPUT);
    
vw_set_rx_pin(PIN_RF_R);
    
vw_set_tx_pin(PIN_RF_T);
    
//vw_set_ptt_pin(A2);         
    //vw_set_ptt_inverted(true); // Required for DR3100
    
vw_setup(2000);      // Bits per sec
    
vw_rx_start();       // Start the receiver PLL running  


 
Serial.print("Initializing inputs ... ");
 
int i;
 for (
0TOTAL_INPUTS1) {
   
pinMode(inputPins[i],INPUT);
   
inputPinsStatus[i]=digitalRead(inputPins[i]);
 }
 
Serial.println("DONE"); 
 
 
Serial.print("Initializing outputs ... ");
 for (
0TOTAL_OUTPUTS1) {
   
turnOffPin(outputPins[i]);
   
pinMode(outputPins[i],OUTPUT);
   
turnOffPin(outputPins[i]);
 } 
 
Serial.println("DONE");  

  
// Device ID
  
Serial.print("Getting Device ID... "); 
  
unique_device_id=EEPROMReadInt(0);
  if (
unique_device_id<10000 || unique_device_id>60000 || unique_device_id==26807) {
   
Serial.print("N/A, updating... "); 
   
unique_device_id=random(1000060000);
   
EEPROMWriteInt(0unique_device_id);
  }
  
Serial.println(unique_device_id);    

}

void sendRFData() {
 
//vw_rx_stop();
 
Serial.print("Transmitting packets ... ");   
 for(
int i=0;i<RF_SEND_COUNTER;i++) {
  if (
i>0) {
   
delay(200);    
  }
  
digitalWrite(PIN_LEDHIGH); 
  
ET.sendData();
  
digitalWrite(PIN_LEDLOW);
 }  
 
//vw_rx_start();
 
Serial.println("DONE");   
}


void blinking(int count) {
 for(
int i=0;i<count;i++) {
  
digitalWrite(PIN_LEDHIGH); 
  
delay(1000);
  
digitalWrite(PIN_LEDLOW);
  
delay(1000);
 }
}

void turnOnPin(int number) {
  
Serial.print("OUTPIN");
  
Serial.print(number);  
  
Serial.println(" set to 1");
  
digitalWrite(numberLOW);  
}

void turnOffPin(int number) {
  
Serial.print("OUTPIN");
  
Serial.print(number);  
  
Serial.println(" set to 0");  
  
digitalWrite(numberHIGH);  
}

void loop() {

  
  
 
int i;
 for (
0TOTAL_INPUTS1) {
   
int currentStatus=digitalRead(inputPins[i]);
   if (
currentStatus!=inputPinsStatus[i]) {
    
delay(100); // de-bounce delay
    
int currentStatusUpdated=digitalRead(inputPins[i]);
    if (
currentStatusUpdated==currentStatus) {
     
inputPinsStatus[i]=currentStatus;
     
Serial.print("IN");
     
Serial.print(i);
     
Serial.print(" set to ");      
     
Serial.println(currentStatus);
     if (
directLinkedPins[i]!=&& currentStatus==HIGH) {
      
turnOnPin(directLinkedPins[i]);
     }
     if (
directLinkedPins[i]!=&& currentStatus==LOW) {
      
turnOffPin(directLinkedPins[i]);      
     }    
    
     
mydata.device_id unique_device_id;
     
mydata.destination_id 0;  
     
mydata.packet_id random(65535);
     
packet_received_id=mydata.packet_id;
     if (
currentStatus==HIGH) {
       
mydata.command=CLASS_ON;
     } else {
       
mydata.command=CLASS_OFF;      
     }
     
mydata.data i;
     
sendRFData();
    }
   }
 }  
  
  
uptime=round(millis()/1000);
  if (
uptime!=old_uptime) {
    
Serial.print("Uptime: ");
    
Serial.println(uptime);
    
old_uptime=uptime;
  }
  
  
//begin easy transfer
  
if (ET.receiveData()) {
    
Serial.print("Got packet for device ");
    
Serial.print(mydata.destination_id);        
    
Serial.print(" packet ");
    
Serial.print(mydata.packet_id);
    
Serial.print(" command ");    
    
Serial.print(mydata.command);    
    
Serial.print(" data ");    
    
Serial.println(mydata.data);
  
    if ((
mydata.destination_id==unique_device_id) && (mydata.packet_id!=packet_received_id)) {      
      
Serial.print("Got RF command: ");
      
Serial.print(mydata.command);
      
Serial.print(" data: ");
      
Serial.println(mydata.data);
      
packet_received_id=(int)mydata.packet_id;    
      if (
mydata.command==CLASS_ON) {
        
turnOnPin(outputPins[mydata.data]);
      }
      if (
mydata.command==CLASS_OFF) {
        
turnOffPin(outputPins[mydata.data]);        
      }
      if (
mydata.command==CLASS_PULSE) {
        
turnOnPin(outputPins[mydata.data]);
        
delay(1000);
        
turnOffPin(outputPins[mydata.data]);        
      }      
   } else {
     
Serial.println("Ignoring packet.");
   }
   
  } 
  
  
  if (
Serial.available()) {
    
char c=Serial.read();
    if (
== '\n' || == ';')
        {
          
Serial.println(inData);
          
int commandProcessed=0;
          
          if (
inData.equals("blink")) {
           
commandProcessed=1;            
           
Serial.println("BLINKING!");
           
blinking(3);
          }
          
          if (
inData.startsWith("turnon")) {
            
commandProcessed=1;            
            
inData.replace("turnon","");
            
turnOnPin(outputPins[inData.toInt()]);
          }
          
          if (
inData.startsWith("turnoff")) {
            
commandProcessed=1;            
            
inData.replace("turnoff","");          
            
turnOffPin(outputPins[inData.toInt()]);            
          }          
          
          if (
inData.startsWith("status")) {
            
commandProcessed=1;
            
inData.replace("status","");            
            
int currentStatus=digitalRead(inData.toInt());     
            
Serial.println(currentStatus);
          }
          
          if (
commandProcessed==0) {
            
Serial.print("Unknown command: ");
            
Serial.println(inData);
          }
         
          
inData="";
          
Serial.flush();
        } else {
          
inData += (c);
        }    
  }  
  
//delay(100);
}
 

Denilkor: Доброго дня форумчане. Буквально вчера вечером наткнулся на этот любопытный проект и решил изучить для последующей реализации . С помощью 2 UNO собрал макет . 1 UNO выступает в качестве удаленной погодной станции , собирает данные с сенсоров и плюется ими по средством NRF24 на вторую UNO . Та в свою очередь заполняет структуру (как в примере) данными и отправляет их через COM --> ArduinoGW-->MajorDoMo . Я добавил скрипт как указано в примере и дальше не могу разобраться как добавить датчики и поместить их на рабочий стол . Помогите пожалуйста разобраться с интеграцией.

[ Обсудить на форуме ]

 

MajorDomo

Информация

Проекты

Партнёры

edit SideBar

 
 
 

 

Blix theme adapted by David Gilbert, powered by PmWiki