Программирование AVR SUNFOUNDER: быстрый старт для создания умного дома Thu, November 21 2024  

Поделиться

Нашли опечатку?

Пожалуйста, сообщите об этом - просто выделите ошибочное слово или фразу и нажмите Shift Enter.


SUNFOUNDER: быстрый старт для создания умного дома Печать
Добавил(а) microsin   

Не секрет, что современные системы типа "Умный дом" обычно очень сложные, либо очень дорогие, либо и то и другое одновременно. Приходится читать кучу скучной документации, что-то там программировать, настраивать... Но как быть, если лишних денег нет, программировать тоже как-то не сложилось (но руки растут из правильного места), но хочется иметь дома сигнализацию + что-то еще (например, управление отопительным котлом и контроль его состояния, контроль температуры в теплице, полив автоматический и по команде издалека)? Тут Вам на помощь может прийти дешевый и сердитый наборчик SUNFOUNDER [1]. 

У SUNFOUNDER есть несколько разновидностей китов и наборов для создания умного дома. Мне попался набор на основе Arduino Mega 2560 за $80, купленный на ebay.com [2]. Несмотря на наличие в названии Raspberry Pi, никакого Raspberry Pi в моем наборе не было, просто этот набор бывает в 2 модификациях - один подешевле на основе Arduino Mega 2560 (как у меня), другой подороже на основе Raspberry Pi. 

На картинке показано, что входит в набор на основе Arduino Mega 2560.

SUNFOUNDER Arduino kit inside

1. Плата с 4 реле. Можно программно управлять освещением, включать и выключать любые электрические приборы.
2. Фото-резистивный сенсор. Может определять уровень освещенности (например, насколько хорошо освещена теплица).
3. Датчик газа и задымления MQ-2. Обладает высокой чувствительностью к наличию в воздухе природного газа, углеводородов на основе пропана, бутана, и к различному задымлению.
4. Датчик атмосферного давления BMP180.
5a, 5b. 5 светодиодов и 5 токоограничительных резисторов для их подключения.
6. Датчик влажности и температуры HDT11.
7. Основная управляющая плата Arduino Mega 2560 [7].
8. Ethernet-шилд на основе чипа W5100, совмещенный со слотом карт microSD.
9. Ключ-брелок на основе метки RFID.
10. Считыватель меток RFID RC522.
11. Дополнительная платка Arduino Nano.
12. Пироэлектрический датчик присутствия (PIR).
13. 2 модуля для беспроводной связи NRF24L01. Это никакой не Wi-Fi, модули работают по стандарту промышленной связи ISM 2.4..2.5 ГГц.
14. Плата для макетирования (так называемая Solderless BreadBoard).
15. Набор соединительных кабелей.
16. Компакт-диск с примерами кода, видеороликами (чтобы показать - да, это работает!) и средой радиотехнического конструирования Fritzing. 

Удивительно, как такое богатство уложилось в цену $79.99. Китайцы, что и говорить, все у них не как у людей. У нас в России только платка с реле стоила бы столько же, если не дороже. Конечно, чтобы попробовать собрать систему для умного дома и поэкспериментировать с примерами кода, совсем необязательно покупать набор именно у SUNFOUNDER, все нужные комплектующие можно купить по отдельности на ebay.com, aliexpress.com или dex.com. Однако по стоимости Вы много не выиграете, разве что можете не покупать то, что Вам не нужно. Лично мне в наборе интересно все, разве что не нужна плата BreadBoard (она у меня уже есть), все остальное пригодится.

Набор от SUNFOUNDER предназначен для того, чтобы Вы попробовали простейшие примеры, обрадовались тому, как все просто и понятно, и уже на этой основе соорудили что-то своё (удаленное управление газовым котлом, контроллер теплицы, домашнюю сигнализацию и т. д.). 

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

SUNFOUNDER Smart Home example

Основное ядро этой системы - плата Arduino Mega 2560 с установленным Ethernet-шилдом на чипе WS5100, обеспечивающим связь с Интернет. Для упрощения создания удаленного управления SUNFOUNDER предлагает воспользоваться сервисом devicebit.com. Т. е. для того, чтобы у Вас была какая-то минимальная система, доступная для управления через Интернет, Вам нужна Arduino Mega 2560 с воткнутым в неё Ethernet-шилдом. В плату Arduino Mega 2560 нужно записать скетч Arduino, который сам будет связываться с сервером DeviceBit под Вашим аккаунтом. На плату Arduino Mega 2560 можно уже сверху навесить всякие датчики, релюшки, светодиодики, и замутить таким образом немеряно крутую систему. Также, если Вам этого хочется, можно с помощью модулей беспроводной связи подключить платку Arduino Nano, которая будет служить выносным модулем управления (она тоже может управлять реле, собирать информацию с датчиков и т. п.). 

[Готовые примеры проектов]

Чтобы быстро воткнуться, как это работает, нужно просмотреть видеоролики, записанные на компакт диске, и потом попробовать поиграться с примерами скетчей Arduino, которые записаны на этом же компакт диске: 

Это самый простой скетч. Аналоговый уровень напряжения считывается с выхода фотоэлемента (с помощью АЦП микроконтроллера), и выводится в Serial-консоль. Плата фотодатчика подключается к плате Arduino Mega 2560 с помощью 3 проводков (GND, сигнал и + питания).

// Фотоэлемент подключен к аналоговому порту A0
//  платы Arduino MEGA 2560:
#define photocellPin 0
 
void setup()
{
   Serial.begin(9600);
}
 
void loop()
{
   Serial.print("Photocell Value: ");
   Serial.println(analogRead(photocellPin));
   delay(500);
}

SUNFOUNDER pthotocell

Этот пример кода будет замечательно работать как с платой Arduino Mega 2560, так и с платой Arduino Nano (надо только перед компиляцией правильно выставить тип платы в системе разработки).

Принцип работы фотодатчика простой. По сути это резистор, у которого сопротивление зависит от направленного на него потока света. Если поверхность фоторезистора затемнена, то его сопротивление большое (тысячи килоом), но если на фоторезистор попал свет, то его сопротивление уменьшается. Чем ярче свет, тем сопротивление меньше. Изменение сопротивления преобразуется в плате фотодатчика в изменение уровня напряжения с помощью делителя напряжения, после чего это напряжение может быть прочитано узлом АЦП микроконтроллера.

[dht11Test.ino]

#include < dht11.h >
 
DHT11 DHT11;
 
// DHT11 подключен к цифровому порту 34 платы Arduino Mega 2560
// (для Arduino Nano нужно выбрать другой номер порта).
#define DHT11PIN 34
 
void setup()
{
   Serial.begin(9600);
}
 
void loop()
{
   // Чтение данных из датчика DHT11 и вывод их в Serial-консоль
   Serial.print("DHT11, \t");
   // Прочитать значение из датчика:
   int chk = DHT11.read(DHT11PIN);
   switch (chk)
   {
      case DHTLIB_OK:  
      Serial.print("OK,\t"); 
      break;
      case DHTLIB_ERROR_CHECKSUM: 
      Serial.print("Checksum error,\t"); 
      break;
      case DHTLIB_ERROR_TIMEOUT: 
      Serial.print("Time out error,\t"); 
      break;
      default: 
      Serial.print("Unknown error,\t"); 
      break;
    }
   Serial.println();
   // Вывести данные:
   Serial.print("Tem: ");
   Serial.print(DHT11.temperature); //вывод температуры
   Serial.println(" C");
   Serial.print("Hum: ");
   Serial.print(DHT11.humidity);    //вывод влажности
   Serial.println(" %"); 
   Serial.println();
   delay(500);                      //задержка на 0.5 секунды
}

Датчик DHT11 считывается с помощью модулей библиотеки (исходный код dht11.cpp, заголовочный файл dht11.h) - все это тоже есть на компакт-диске.

[dht11.cpp]

//    FILE: dht11.cpp
// VERSION: 0.4.1
// Назначение: Arduino-библиотека для DHT11 Temperature & Humidity Sensor
// LICENSE: GPL v3 (http://www.gnu.org/licenses/gpl.html)
//
// DATASHEET: http://www.micro4you.com/files/sensor/DHT11.pdf
//
// История создания:
// George Hadjikyriacou - оригинальная версия (??)
// Модифицировано SimKard - Version 0.2 (24/11/2010)
// Модифицировано Rob Tillaart - Version 0.3 (28/03/2011)
// + добавлены комментарии
// + удален весь код, не относящийся к DHT11
// + добавлены ссылки
// Модифицировано Rob Tillaart - Version 0.4 (17/03/2012)
// + добавлена поддержка 1.0
// Модифицировано Rob Tillaart - Version 0.4.1 (19/05/2012)
// + добавлены коды ошибок
#include "dht11.h"
 
// Возвращаемые значения:
// DHTLIB_OK
// DHTLIB_ERROR_CHECKSUM
// DHTLIB_ERROR_TIMEOUT
int DHT11::read(int pin)
{
   // Буфер для приема:
   uint8_t bits[5];
   uint8_t cnt = 7;
   uint8_t idx = 0;
 
   // Очистка буфера:
   for (int i=0; i< 5; i++) bits[i] = 0;
 
   // Запрос выборки:
   pinMode(pin, OUTPUT);
   digitalWrite(pin, LOW);
   delay(18);
   digitalWrite(pin, HIGH);
   delayMicroseconds(40);
   pinMode(pin, INPUT);
 
   // Подтверждение (ACKNOWLEDGE) или таймаут (TIMEOUT)
   unsigned int loopCnt = 10000;
   while(digitalRead(pin) == LOW)
      if (loopCnt-- == 0) return DHTLIB_ERROR_TIMEOUT;
 
   loopCnt = 10000;
   while(digitalRead(pin) == HIGH)
      if (loopCnt-- == 0) return DHTLIB_ERROR_TIMEOUT;
 
   // Чтение выходных данных - 40 бит => 5 байт, иначе таймаут
   for (int i=0; i<40; i++)
   {
      loopCnt = 10000;
      while(digitalRead(pin) == LOW)
         if (loopCnt-- == 0) return DHTLIB_ERROR_TIMEOUT;
 
      unsigned long t = micros();
 
      loopCnt = 10000;
      while(digitalRead(pin) == HIGH)
         if (loopCnt-- == 0) return DHTLIB_ERROR_TIMEOUT;
 
      if ((micros() - t) > 40) bits[idx] |= (1 << cnt);
      if (cnt == 0)   // следующий байт?
      {
         cnt = 7;    // перезапуск со старшего бита (MSB)
         idx++;      // перейти к следующему биту
      }
      else cnt--;
   }
 
   // Записи в переменные bits[1] и bits[3] всегда будут
   // нулями - они пропущены в формулах.
   humidity    = bits[0]; 
   temperature = bits[2]; 
 
   uint8_t sum = bits[0] + bits[2];  
   if (bits[4] != sum) return DHTLIB_ERROR_CHECKSUM;
   return DHTLIB_OK;
}

Схема подключения датчика DHT11 также очень простая - GND, питание, сигнал данных.

SUNFOUNDER DHT11

Пример кода будет работать как с платой Arduino Mega 2560, так и с платой Arduino Nano (надо только перед компиляцией правильно выставить тип платы в системе разработки, и поменять в скетче номер цифрового порта).

Код скетча для датчика задымления очень простой, и полностью повторяет код проекта с фотодатчиком.

// Модуль MQ-2 подключен к аналоговому входу (АЦП)
// платы Arduino MEGA 2560:
#define mq2Pin 1
 
void setup()
{
   Serial.begin(9600);
}
 
void loop()
{
   Serial.print("MQ-2 Value: ");
   Serial.println(analogRead(mq2Pin));
   delay(500);
}

SUNFOUNDER MQ 2

Пример кода будет работать как с платой Arduino Mega 2560, так и с платой Arduino Nano (надо только перед компиляцией правильно выставить тип платы в системе разработки).

#define pirPin 36  //к этому цифровому порту подключен PIR
#define ledPin 38  //к этому цифровому порту подключен светодиод LED
 
unsigned char pirValue = 0;  //переменная для состояния выхода PIR
 
void setup() 
{
   Serial.begin(9600);
   pinMode(pirPin, INPUT);
   pinMode(ledPin, OUTPUT);
}
 
void loop()
{
   pirValue = digitalRead(pirPin);
   Serial.println("Pir State: ");
   Serial.println(pirValue);
   digitalWrite(ledPin, pirValue);
   delay(500);
}

Это также очень простой пример кода, который может работать как Arduino MEGA 2560, так и с Arduino Nano (не забудьте только поменять номера портов и правильно выставить тип платы в среде разработки).

Также для этого примера на компакт-диске есть проект Fritzing.

SUNFOUNDER PIR

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

#include < Wire.h >
#include < Adafruit_Sensor.h >
#include < Adafruit_BMP085_U.h >
 /* Этот пример кода использует унифицированную библиотеку сенсоров от
   компании Adafruit (Adafruit_Sensor), которая предоставляет общий тип
   'type' для данных сенсора и некоторые вспомогательные функции.
 
   Для использования этого драйвера Вам также нужно загрузить библиотеку
   Adafruit_Sensor, и добавить её в папку libraries среды разработки
   (библиотека находится также на компакт-диске SUNFOUNDER).
 
   Вы также должны назначить уникальный идентификатор (ID) для этого
   сенсора при использовании совместно с Adafruit Sensor API, чтобы можно
   было идентифицировать этот отдельный сенсор в любых логах данных
   и т. п. Чтобы назначить уникальный ID, просто предоставьте соответствующее
   значение ниже в конструкторе (в этом примере используется идентификатор
   12345).
 
   Соединения
   ==========
   SCL подключите к аналоговому порту 5
   SDA подключите к аналоговому порту 4
   VDD подключите к постоянному напряжению питания 3.3V
   GROUND подключите к общему проводу (земле) платы (сигнал GND)
 
   История
   =======
   2013/JUN/17  - Обновлено вычисление высоты (KTOWN)
   2013/FEB/13  - Первая версия проекта (KTOWN)*/
   
Adafruit_BMP085_Unified bmp = Adafruit_BMP085_Unified(10085);
 
/**************************************************************************
    Процедура отображает некую базовую информацию, полученную от сенсора
    с помощью API-типа sensor_t (см. документацию и код Adafruit_Sensor
    для получения дополнительной информации).
**************************************************************************/
void displaySensorDetails(void)
{
  sensor_t sensor;
  bmp.getSensor(&sensor);
  Serial.println("------------------------------------");
  Serial.print  ("Sensor:       "); Serial.println(sensor.name);
  Serial.print  ("Driver Ver:   "); Serial.println(sensor.version);
  Serial.print  ("Unique ID:    "); Serial.println(sensor.sensor_id);
  Serial.print  ("Max Value:    "); Serial.print(sensor.max_value); Serial.println(" hPa");
  Serial.print  ("Min Value:    "); Serial.print(sensor.min_value); Serial.println(" hPa");
  Serial.print  ("Resolution:   "); Serial.print(sensor.resolution); Serial.println(" hPa");  
  Serial.println("------------------------------------");
  Serial.println("");
  delay(500);
}
 
/**************************************************************************
    Стандартная функция настройки Arduino (она автоматически
    вызывается при включении питания или сбросе системы).
**************************************************************************/
void setup(void) 
{
  Serial.begin(9600);
  Serial.println("Pressure Sensor Test"); Serial.println("");
  
  /* Initialise the sensor */
  if(!bmp.begin())
  {
    /* Обнаружена проблема с детектированием наличия BMP085. Если
       это произошло, проверьте соединения с датчиком. */
    Serial.print("Ooops, no BMP085 detected ... Check your wiring or I2C ADDR!");
    while(1);
  }
  
  /* Отображение информации, полученной от датчика. */
  displaySensorDetails();
}
 
/**************************************************************************
    Стандартная функция цикла Arduino, которая будет запущена после
    завершения функции setup (здесь традиционно должен находиться
    Ваш код алгоритма программы).
**************************************************************************/
void loop(void) 
{
  /* Получение нового события сенсора */ 
  sensors_event_t event;
  bmp.getEvent(&event);
 
  /* Отобразить результаты (барометрическое давление измеряется в hPa) */
  if (event.pressure)
  {
    Serial.print("Pressure:    ");
    Serial.print(event.pressure);
    Serial.println(" hPa");
    
    /* Вычисление высоты над уровнем моря с подходящей точностью требует  *
     * наличия величины атмосферного давления Вашего местоположения в     *
     * момент получения данных датчиком, а также температуры окружающего  *
     * воздуха в градусах Цельсия. Если у Вас нет этой информации, то     *
     * можно использовать 'стандартное' значение давления 1013.25 hPa     *
     * (определено макросом SENSORS_PRESSURE_SEALEVELHPA в файле          *
     * sensors.h), однако это не идеальный вариант, так что результаты    *
     * измерений будут не точными.                                        *
     *                                                                    *
     * Вы можете обычно узнать текущее значение SLP (Sea Level Pressure)  *
     * на сайтах погоды или от центров информационных прогнозов недалеко  *
     * от аэропорта.                                                      *
     *                                                                    *
     * Например, для Парижа можно проверить текущее давление и уровень    *
     * над поверхностью мирового океана по ссылке http://bit.ly/16Au8ol   */
     
    /* Сначала мы получим температуру от датчика BMP085 */
    float temperature;
    bmp.getTemperature(&temperature);
    Serial.print("Temperature: ");
    Serial.print(temperature);
    Serial.println(" C");
  
    /* Затем получим атмосферное давление, и по SLP и температуре получим
       высоту над уровнем моря. */
    /* Исправьте следующую строку, чтобы указать в ней корректное значение
       SLP: тогда результаты будут точнее. */
    float seaLevelPressure = SENSORS_PRESSURE_SEALEVELHPA;
    Serial.print("Altitude:    "); 
    Serial.print(bmp.pressureToAltitude(seaLevelPressure,
                                        event.pressure,
                                        temperature)); 
    Serial.println(" m");
    Serial.println("");
  }
  else
  {
    Serial.println("Sensor error");
  }
  delay(1000);
}

В тексте скетча постоянно упоминается датчик BMP085, но код работоспособен и для датчика BMP180. Чтобы не загромождать статью, код библиотек Adafruit_BMP085_U и Adafruit_Sensor здесь не приведен.

SUNFOUNDER BMP180

Код может работать не только на Arduino MEGA 2560, но и на Arduino Nano. Для чтения цифровых данных с датчика используется интерфейс I2C (TWI), при этом используется класс Wire от Arduino.

В этом проекте имеется 2 примера кода. Первый getId.ino показывает, как читать идентификатор с RFID-ключа/метки.

[getId.ino]

#include "rfid.h"
 
RFID rfid;
uchar serNum[5];  // массив для хранения Вашего ID
 
void setup()
{
  Serial.begin(9600);
  rfid.begin(2, 3, 4, 5, 6, 7);  
  delay(100);
  rfid.init();
}
 
void loop()
{
  uchar status;
  uchar str[MAX_LEN];
  status = rfid.request(PICC_REQIDL, str);
  if (status != MI_OK)
  {
    return;
  }
  
  rfid.showCardType(str);
  status = rfid.anticoll(str);
  
  if (status == MI_OK)
  {
    Serial.print("The card's number is: ");
    memcpy(serNum, str, 5);
    rfid.showCardID(serNum);
    Serial.println();
    Serial.println();
  }
  delay(500);
   
  rfid.halt(); //команда карте перейти в режим сна (sleep mode)
}

Второй пример rfidTest.ino не намного сложнее, он просто проверяет считанные с RFID-ключа идентификаторы, и на основе прочитанных значений предпринимает какие-то разные действия. Все тупо и просто. Используется библиотеки rfid и softspi, код которых опять не привожу для краткости (все библиотеки и коды примеров присутствуют на компакт-диске SUNFOUNDER).

[rfidTest.ino]

#include "rfid.h"
 
RFID rfid;
 
uchar serNum[5];
 
void setup()
{
  Serial.begin(9600);
  rfid.begin(2, 3, 4, 5, 6, 7);
  delay(100);
  rfid.init();
}
 
void loop()
{
  uchar status;
  uchar str[MAX_LEN];
  status = rfid.request(PICC_REQIDL, str);
  if (status != MI_OK)
  {
    return;
  }
  
  rfid.showCardType(str);
  status = rfid.anticoll(str);
  
  if (status == MI_OK)
  {
    Serial.print("The card's number is: ");
    memcpy(serNum, str, 5);
    rfid.showCardID(serNum);
    Serial.println();
    
    // Check people associated with card ID
    uchar* id = serNum;
    if( id[0]==0x4B && id[1]==0xE6 && id[2]==0xD1 && id[3]==0x3B ) 
    {
      Serial.println("Hello Mary!");
    } 
    else if(id[0]==0x3B && id[1]==0xE6 && id[2]==0xD1 && id[3]==0x3B) 
    {
      Serial.println("Hello Greg!");
    }
    else if(id[0] == 0x15 && id[1] == 0x6F && id[2] == 0x9F && id[3] == 0xAF)
    {
      Serial.println("Collect!");
      Serial.println();
    }
    else
    {
      Serial.println("Hello unkown guy!");
    }
  }
  delay(500); 
  rfid.halt(); //команда карте перейти в режим сна (sleep mode)
}

Примеры кода могут работать с любой платой Arduino, не только с Arduino MEGA 2560, которую Вы видите на фото.

SUNFOUNDER RFID

Этот проект выглядит на первый взгляд феерически сложным. Какие-то платы, кнопочки, модули, провода... Однако на самом деле ничего сложного тут нет, просто демонстрируется обмен данными по радио между двумя платами Arduino с помощью модулей NRF24L01 диапазона ISM 2.4 МГц. Одна плата является передатчиком команд по радио (Arduino MEGA 2560, скетч nrfTest_send.ino), другая приемником - она команды обрабатывает и выполняет (Arduino Nano, скетч nrfTest_receive.ino). Совершенно не принципиально, какую плату использовать в качестве передатчика или приемника - код одинаково хорошо будет работать на любой плате Arduino.

[nrfTest_send.ino]

Это код передающей стороны.

#include "NRF24L01.h"
 
// Вспомогательные определения для типов данных
#define uint8 unsigned char
#define uint16 unsigned int
#define uint32 unsigned long
 
//Выбранные ножки платы для портов GPIO:
#define sw1Pin 2
#define sw2Pin 3
#define sw3Pin 4
#define sw4Pin 5
#define sw5Pin 6
 
#define TX_ADR_WIDTH    5  // 5 байт адреса TX(RX)
#define TX_PLOAD_WIDTH  3  // 32 байт полезной нагрузки TX
 
unsigned char TX_ADDRESS[TX_ADR_WIDTH]  = 
{
  0x34,0x43,0x10,0x10,0x01
}; // здесь задается статический адрес TX
 
//Инициализация значений:
unsigned char rx_buf[TX_PLOAD_WIDTH] = {0};
uint8 tx_buf[TX_PLOAD_WIDTH] = {0};
 
void setup()
{
   Serial.begin(9600);
   pinMode(sw1Pin, INPUT);
   pinMode(sw2Pin, INPUT);
   pinMode(sw3Pin, INPUT);
   pinMode(sw4Pin, INPUT);
   pinMode(sw5Pin, INPUT);
   digitalWrite(sw1Pin, HIGH);
   digitalWrite(sw2Pin, HIGH);
   digitalWrite(sw3Pin, HIGH);
   digitalWrite(sw4Pin, HIGH);
   digitalWrite(sw5Pin, HIGH);
   
   pinMode(CE,  OUTPUT);
   pinMode(SCK_PIN, OUTPUT);
   pinMode(CSN, OUTPUT);
   pinMode(MOSI_PIN,  OUTPUT);
   pinMode(MISO_PIN, INPUT);
   pinMode(IRQ, INPUT);
   //Инициализация порта ввода/вывода
   init_io();
   unsigned char status=SPI_Read(STATUS);
   Serial.print("status = ");
   // Здесь читается регистр статуса режима, значение по умолчанию должно быть 'E'
   Serial.println(status,HEX);
   Serial.println("*******************TX_Mode Start****************************");
   TX_Mode();
}
 
static void send (void)
{
   // чтение регистра статуса
   unsigned char status = SPI_Read(STATUS);
   // если прерывание готовности данных передачи transmit data send (TX_DS)
   if(status&TX_DS)
   {
     SPI_RW_Reg(FLUSH_TX,0);
     // записать полезную нагрузку (данные) в TX_FIFO
     SPI_Write_Buf(WR_TX_PLOAD,tx_buf,TX_PLOAD_WIDTH);
   }
   // если прерывание готовности приема данных receive data ready (MAX_RT),
   // то повторная передача SETUP_RETR
   if(status&MAX_RT)                          
   {
     SPI_RW_Reg(FLUSH_TX,0);
     // запрет режима standy
     SPI_Write_Buf(WR_TX_PLOAD,tx_buf,TX_PLOAD_WIDTH);
   }
   // очистка флагов прерывания RX_DR, TX_DS или MAX_RT
   SPI_RW_Reg(WRITE_REG+STATUS,status);
}
 
void loop()
{
   if(digitalRead(sw1Pin) == 0)
   {
      Serial.println(1);
      tx_buf[0] = 0x55;
      tx_buf[1] = 0xAA;
      tx_buf[2] = 0x11;
      send();
   }
   if(digitalRead(sw2Pin) == 0)
   {
      Serial.println(2);
      tx_buf[0] = 0x55;
      tx_buf[1] = 0xAA;
      tx_buf[2] = 0x21;
      send();
   }
   if(digitalRead(sw3Pin) == 0)
   {
      Serial.println(3);
      tx_buf[0] = 0x55;
      tx_buf[1] = 0xAA;
      tx_buf[2] = 0x31;
      send();
   }
   if(digitalRead(sw4Pin) == 0)
   {
      Serial.println(4);
      tx_buf[0] = 0x55;
      tx_buf[1] = 0xAA;
      tx_buf[2] = 0x41;
      send();
   }
   if(digitalRead(sw5Pin) == 0)
   {
      tx_buf[0] = 0x55;
      tx_buf[1] = 0xAA;
      tx_buf[2] = 0x50;
      send();
   }
}
 
//**************************************************
// Функция: init_io();
// Описание: однократно мигает светодиодом, разрешает
// чип (для готовности режима TX или RX), запрещает
// SPI, на выходе тактовый сигнал SPI в уровне лог. 1.
//**************************************************
void init_io(void)
{
  digitalWrite(IRQ, 0);
  digitalWrite(CE, 0);  // разрешение чипа
  digitalWrite(CSN, 1); // запрет SPI
}
 
/**************************************************
 * Функция: SPI_RW();
 * Описание: записывает байт в nRF24L01 и возвращает
 * байт, прочитанный из nRF24L01 в процессе записи,
 * как это подразумевает протокол SPI.
 **************************************************/
unsigned char SPI_RW(unsigned char Byte)
{
  unsigned char i;
 
  //Цикл вывода 8 бит:
  for(i=0; i < 8; i++)
  {
    if(Byte&0x80)
    {
      digitalWrite(MOSI_PIN, 1);
    }
    else
    {
      digitalWrite(MOSI_PIN, 0);
    }
    digitalWrite(SCK_PIN, 1);
    // вдвинуть следующий бит в старший бит (MSB)
    Byte << = 1;                         
    if(digitalRead(MISO_PIN) == 1)
    {
      Byte |= 1;     // захват текущего значения бита MISO
    }
    digitalWrite(SCK_PIN, 0);
  }
  return(Byte);      // возврат прочитанного байта
}
 
/**************************************************
 * Функция: SPI_RW_Reg();
 * Описание: записывает значение value в регистр reg.
 **************************************************/
unsigned char SPI_RW_Reg(unsigned char reg, unsigned char value)
{
  unsigned char status;
 
  digitalWrite(CSN, 0);    // CSN==0, начало транзакции SPI
  status = SPI_RW(reg);    // выбор регистра
  SPI_RW(value);           // .. и запись в него значения ..
  digitalWrite(CSN, 1);    // затем перевод CSN обратно в лог. 1
  return(status);          // возврат байта состояния nRF24L01
}
 
/**************************************************
 * Функция: SPI_Read();
 * Описание: чтение одного байта из регистра reg
 * модуля nRF24L01.
 **************************************************/
unsigned char SPI_Read(unsigned char reg)
{
  unsigned char reg_val;
 
  digitalWrite(CSN, 0);    // CSN==0, инициализация обмена SPI
  SPI_RW(reg);             // выбор регистра для чтения ..
  reg_val = SPI_RW(0);     // .. и затем чтение его значения
  digitalWrite(CSN, 1);    // CSN==1, прервать обмен по SPI
  
  return(reg_val);         // возврат значения регистра
}
 
/**************************************************
 * Функция: SPI_Read_Buf();
 * Описание: чтение нужного количества байт из регистра
 * reg. Обычно эта функция используется для чтения
 * полезной нагрузки приема, адресов Rx/Tx.
 **************************************************/
unsigned char SPI_Read_Buf(unsigned char reg, unsigned char *pBuf, unsigned char bytes)
{
  unsigned char status,i;
 
  digitalWrite(CSN, 0);    // CSN==0, инициализация обмена SPI
  status = SPI_RW(reg);    // выбор регистра для записи и чтение байта из регистра статуса
 
  for(i=0; i < bytes; i++)
  {
    pBuf[i] = SPI_RW(0);   // выполнить SPI_RW для чтения байтов из nRF24L01
  }
 
  digitalWrite(CSN, 1);    // вернуть CSN в состояние лог. 1
  return(status);          // возврат байта состояния nRF24L01
}
 
/**************************************************
 * Функция: SPI_Write_Buf();
 * Описание: запись содержимого буфера по указателю
 * pBuf в nRF24L01. Обычно используется для записи
 * полезной нагрузки передачи, адресов Rx/Tx.
 **************************************************/
unsigned char SPI_Write_Buf(unsigned char reg, unsigned char *pBuf, unsigned char bytes)
{
  unsigned char status,i;
  
  digitalWrite(CSN, 0);    // CSN==0, инициализация обмена SPI
  status = SPI_RW(reg);    // выбор регистра для записи и чтение байта из регистра статуса
  for(i=0; i < bytes; i++)    // в цикле записать все значения байт из буфера (*pBuf)
  {
    SPI_RW(*pBuf++);
  }
  digitalWrite(CSN, 1);    // вернуть CSN в состояние лог. 1
  return(status);          // возврат байта состояния nRF24L01
}
 
/**************************************************
 * Функция: TX_Mode();
 * Описание: эта функция инициализирует одно устройство
 * nRF24L01 в режим передачи, устанавливает его адрес TX,
 * устанавливает адрес RX для auto.ack, заполняет полезную
 * нагрузку TX, выбирает канал RF, скорость передачи бит и
 * мощность передачи TX. PWR_UP установлен, CRC
 * (2 байта) разрешено, и PRIM:TX.
 *
 * Что доделать: один импульс (>10 мкс) на CE теперь отправит
 * этот пакет, после чего ожидается подтверждение от
 * противоположной принимающей стороны RX.
 **************************************************/
void TX_Mode(void)
{
  digitalWrite(CE, 0);
 
  // запись TX_Address в nRF24L01:
  SPI_Write_Buf(WRITE_REG + TX_ADDR, TX_ADDRESS, TX_ADR_WIDTH);
  // RX_Addr0 тот же, что и TX_Adr для функции Auto.Ack
  SPI_Write_Buf(WRITE_REG + RX_ADDR_P0, TX_ADDRESS, TX_ADR_WIDTH);
 
  SPI_RW_Reg(WRITE_REG + EN_AA, 0x01);      // разрешить Auto.Ack:Pipe0
  SPI_RW_Reg(WRITE_REG + EN_RXADDR, 0x01);  // разрешить Pipe0
  SPI_RW_Reg(WRITE_REG + SETUP_RETR, 0x1a); // 500 мкс + 86 мкс, 10 повторных передач...
  SPI_RW_Reg(WRITE_REG + RF_CH, 40);        // выбор RF-канала 40
  SPI_RW_Reg(WRITE_REG + RF_SETUP, 0x07);   // TX_PWR:0dBm, Datarate:2Mbps, LNA:HCURR
  SPI_RW_Reg(WRITE_REG + CONFIG, 0x0e);     // установка бита PWR_UP bit, разрешить CRC & Prim:TX,
                                            // разрешены прерывания MAX_RT & TX_DS.
  SPI_Write_Buf(WR_TX_PLOAD,tx_buf,TX_PLOAD_WIDTH);
  
  digitalWrite(CE, 1);
}

[nrfTest_receive.ino]

Это код на стороне приемника.

/*********************************************************************
**   SPI-совместимый интерфейс                                      **
**   CS   - подключить к цифровому порту 8                          **
**   CSN  -                              9  (ножка SS)              **
**   SCK  -                              10 (ножка SCK)             **
**   MOSI -                              11 (ножка MOSI)            **
**   MISO -                              12 (ножка MISO)            **
**   IRQ  -                              13                         **
*********************************************************************/#include "NRF24L01.h"
 #define TX_ADR_WIDTH    5  // 5 байт адреса TX(RX)
#define TX_PLOAD_WIDTH  3  // 32 байт полезной нагрузки TX
 
#define SW1_ON_Command     0x11
#define SW1_OFF_Command    0x10
#define SW2_ON_Command     0x21
#define SW2_OFF_Command    0x20
#define SW3_ON_Command     0x31
#define SW3_OFF_Command    0x30
#define SW4_ON_Command     0x41
#define SW4_OFF_Command    0x40
#define SWS_OFF_Command    0x50  
 
//Порты для управления реле:
#define SW1 3
#define SW2 4
#define SW3 5
#define SW4 6
 
unsigned char TX_ADDRESS[TX_ADR_WIDTH]  = 
{
  0x34,0x43,0x10,0x10,0x01
}; // здесь задается статический адрес TX
 
unsigned char rx_buf[TX_PLOAD_WIDTH];
unsigned char tx_buf[TX_PLOAD_WIDTH];
 
void setup()
{
   pinMode(CE,  OUTPUT);
   pinMode(SCK_PIN, OUTPUT);
   pinMode(CSN, OUTPUT);
   pinMode(MOSI_PIN,  OUTPUT);
   pinMode(MISO_PIN, INPUT);
   pinMode(IRQ, INPUT);
   Serial.begin(9600);
   //Инициализация порта ввода/вывода
   init_io();
   unsigned char status=SPI_Read(STATUS);
   Serial.print("status = ");
   // Здесь читается регистр статуса режима, значение по умолчанию должно быть 'E'
   Serial.println(status,HEX);
   Serial.println("*****************RX_Mode start******************************");
   RX_Mode();
   
   pinMode(SW1, OUTPUT); 
   pinMode(SW2, OUTPUT); 
   pinMode(SW3, OUTPUT); 
   pinMode(SW4, OUTPUT); 
   
   digitalWrite(SW1, HIGH);
   digitalWrite(SW2, HIGH);
   digitalWrite(SW3, HIGH);
   digitalWrite(SW4, HIGH);
}
 
void loop()
{
   // чтение регистра статуса
   unsigned char status = SPI_Read(STATUS);
   if(status&RX_DR)
   {
      Serial.println(1);
      // чтение полезной нагрузки в буфер rx_buf
      SPI_Read_Buf(RD_RX_PLOAD, rx_buf, TX_PLOAD_WIDTH);
      SPI_RW_Reg(FLUSH_RX,0);       // очистка RX_FIFO
      if(rx_buf[0] == 0x55)
      {
         if(rx_buf[1] == 0xAA)
         {
            switch(rx_buf[2])
            {
            case SW1_ON_Command:  
                digitalWrite(SW1, LOW);        
                break;
            case SW1_OFF_Command:  
                digitalWrite(SW1, HIGH);        
                break;
            case SW2_ON_Command:  
                digitalWrite(SW2, LOW);        
                break;
            case SW2_OFF_Command:
                digitalWrite(SW2, HIGH);        
                break;
            case SW3_ON_Command:  
                digitalWrite(SW3, LOW);        
                break;
            case SW3_OFF_Command:  
                digitalWrite(SW3, HIGH);        
                break;
            case SW4_ON_Command:  
                digitalWrite(SW4, LOW);        
                break;
            case SW4_OFF_Command:  
                digitalWrite(SW4, HIGH);        
                break;
            case SWS_OFF_Command:
                digitalWrite(SW1, HIGH);
                digitalWrite(SW2, HIGH);
                digitalWrite(SW3, HIGH);
                digitalWrite(SW4, HIGH);
                break;
            }
         }
         
      }
   }
   // очистка флагов прерывания RX_DR, TX_DS или MAX_RT:
   SPI_RW_Reg(WRITE_REG+STATUS,status);
   for(int i = 0; i < 3; i++)
   {
       rx_buf[i] = 0;
   }
   delay(1000);
}
 
//**************************************************
// Функция: init_io();
// Описание: однократно мигает светодиодом, разрешает
// чип (для готовности режима TX или RX), запрещает
// SPI, на выходе тактовый сигнал SPI в уровне лог. 1.
//**************************************************
void init_io(void)
{
  digitalWrite(IRQ, 0);
  digitalWrite(CE, 0);  // разрешение чипа
  digitalWrite(CSN, 1); // запрет SPI
}
 
/**************************************************
 * Функция: SPI_RW();
 * Описание: записывает байт в nRF24L01 и возвращает
 * байт, прочитанный из nRF24L01 в процессе записи,
 * как это подразумевает протокол SPI.
 **************************************************/
unsigned char SPI_RW(unsigned char Byte)
{
  unsigned char i;
  //Цикл вывода 8 бит:
  for(i=0; i < 8; i++)
  {
    if(Byte&0x80)
    {
      // вывод старшего бита байта (MSB) в MOSI
      digitalWrite(MOSI_PIN, 1);
    }
    else
    {
      digitalWrite(MOSI_PIN, 0);
    }
    digitalWrite(SCK_PIN, 1);       // SCK==1
    Byte << = 1;                    // сдвиг следующего бита в MSB
    if(digitalRead(MISO_PIN) == 1)
    {
      Byte |= 1;                    // захват текущего значения бита MISO
    }
    digitalWrite(SCK_PIN, 0);       // SCK==0
  }
  return(Byte);                     // возврат прочитанного байта
}
 
/**************************************************
 * Функция: SPI_RW_Reg();
 * Описание: записывает значение value в регистр reg.
 **************************************************/
unsigned char SPI_RW_Reg(unsigned char reg, unsigned char value)
{
  unsigned char status;
 
  digitalWrite(CSN, 0);    // CSN==0, начало транзакции SPI
  status = SPI_RW(reg);    // выбор регистра
  SPI_RW(value);           // .. и запись в него значения ..
  digitalWrite(CSN, 1);    // затем перевод CSN обратно в лог. 1
 
  return(status);          // возврат байта состояния nRF24L01
}
 
/**************************************************
 * Функция: SPI_Read();
 * Описание: чтение одного байта из регистра reg
 * модуля nRF24L01.
 **************************************************/
unsigned char SPI_Read(unsigned char reg)
{
  unsigned char reg_val;
 
  digitalWrite(CSN, 0);    // CSN==0, инициализация обмена SPI
  SPI_RW(reg);             // выбор регистра для чтения ..
  reg_val = SPI_RW(0);     // .. и затем чтение его значения
  digitalWrite(CSN, 1);    // CSN==1, прервать обмен по SPI
  
  return(reg_val);         // возврат значения регистра
}
 
/**************************************************
 * Функция: SPI_Read_Buf();
 * Описание: чтение нужного количества байт из регистра
 * reg. Обычно эта функция используется для чтения
 * полезной нагрузки приема, адресов Rx/Tx.
 **************************************************/
unsigned char SPI_Read_Buf(unsigned char reg, unsigned char *pBuf, unsigned char bytes)
{
  unsigned char status,i;
 
  digitalWrite(CSN, 0);    // CSN==0, инициализация обмена SPI
  status = SPI_RW(reg);    // выбор регистра для записи и чтение байта из регистра статуса
  for(i=0; i < bytes; i++)
  {
    pBuf[i] = SPI_RW(0);   // выполнить SPI_RW для чтения байтов из nRF24L01
  }
  digitalWrite(CSN, 1);    // вернуть CSN в состояние лог. 1
  return(status);          // возврат байта состояния nRF24L01
}
 
/**************************************************
 * Функция: SPI_Write_Buf();
 * Описание: запись содержимого буфера по указателю
 * pBuf в nRF24L01. Обычно используется для записи
 * полезной нагрузки передачи, адресов Rx/Tx.
 **************************************************/
unsigned char SPI_Write_Buf(unsigned char reg, unsigned char *pBuf, unsigned char bytes)
{
  unsigned char status,i;
 
  digitalWrite(CSN, 0);    // CSN==0, инициализация обмена SPI
  status = SPI_RW(reg);    // выбор регистра для записи и чтение байта из регистра статуса
  for(i=0; i < bytes; i++) // в цикле записать все значения байт из буфера (*pBuf)
  {
    SPI_RW(*pBuf++);
  }
  digitalWrite(CSN, 1);    // вернуть CSN в состояние лог. 1
  return(status);          // возврат байта состояния nRF24L01
}
 
/**************************************************
 * Функция: RX_Mode();
 * Описание: эта функция инициализирует один модуль
 * nRF24L01 в режиме приема RX Mode, устанавливает
 * адрес RX, записывает длину полезной нагрузки RX,
 * выбирает канал RF, скорость и LNA HCURR.
 * После инициализации CE переключается в состояние
 * лог. 1 - это означает, что устройство теперь
 * готово для приема пакета данных.
 **************************************************/
void RX_Mode(void)
{
  digitalWrite(CE, 0);
  // Для устройств RX и TX используется один и тот же адрес
  SPI_Write_Buf(WRITE_REG + RX_ADDR_P0, TX_ADDRESS, TX_ADR_WIDTH);
  SPI_RW_Reg(WRITE_REG + EN_AA, 0x01);      // разрешить Auto.Ack:Pipe0
  SPI_RW_Reg(WRITE_REG + EN_RXADDR, 0x01);  // разрешить Pipe0
  SPI_RW_Reg(WRITE_REG + RF_CH, 40);        // выбрать RF-канал 40
  SPI_RW_Reg(WRITE_REG + RX_PW_P0, TX_PLOAD_WIDTH); // выбрать длину полезной нагрузки RX
                                                    // такую же, как и для TX
  SPI_RW_Reg(WRITE_REG + RF_SETUP, 0x07);   // TX_PWR:0dBm, Datarate:2Mbps, LNA:HCURR
  SPI_RW_Reg(WRITE_REG + CONFIG, 0x0f);     // установить бит PWR_UP, разрешить CRC & Prim:RX,
                                            // разрешены прерывания РRX_DR.
  digitalWrite(CE, 1);                      // CE==1, чтобы разрешить работу устройства RX
  // Теперь это устройство готово к приему одного пакета из 16 байт полезной нагрузки
  // от устройства TX, которое делает отправку на адрес '3443101001' с автоподтверждением,
  // счетчиком повторных передач 10, на RF-канале 40 и на скорости данных = 2 Mbps.
}

SUNFOUNDER NRF24L01

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

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

Этот пример проекта самый сложный, потому что на него понавешано множество датчиков, и плата Arduino MEGA 2560 общается не только со всеми этими датчиками и с дочерней платой по радио, но еще и через Интернет обменивается данными с сервисом DeviceBit. Однако если Вы успешно справились с предыдущими примерами, и ознакомились с руководством, то и с этим проектом не будет никаких проблем.

[W5100_DeviceBit.ino]

/*********************************************************************
**   SPI-совместимый интерфейс                                      **
**   CS   - подключен к цифровому порту 22                          **
**   CSN  -                             24 (ножка SS)               **
**   SCK  -                             26 (ножка SCK)              **
**   MOSI -                             28 (ножка MOSI)             **
**   MISO -                             30 (ножка MISO)             **
**   IRQ  -                             32                          **
*********************************************************************/
#include < SPI.h >
#include < Ethernet.h >
#include < DeviceBitTcpClient.h >
#include < EEPROM.h >
#include < dht11.h >
#include "NRF24L01.h"
#include "rfid.h"
#include < Wire.h >
#include < Adafruit_Sensor.h >
#include < Adafruit_BMP085_U.h >
#include < avr/interrupt.h >
 
//Вспомогательные определения для типов данных
#define uint8 unsigned char
#define uint16 unsigned int
#define uint32 unsigned long
 
//Конфигурация библиотеки
DHT11 DHT11;
RFID rfid;
uchar serNum[5];
Adafruit_BMP085_Unified bmp = Adafruit_BMP085_Unified(10085);
 
#define TX_ADR_WIDTH    5   // 5 байт для адреса TX(RX)
#define TX_PLOAD_WIDTH  3   // 32 байт для полезной нагрузки TX
 
unsigned char TX_ADDRESS[TX_ADR_WIDTH]  = 
{
  0x34,0x43,0x10,0x10,0x01
}; // здесь задан статический адрес TX
 
// инициализация переменных:unsigned char rx_buf[TX_PLOAD_WIDTH] = {0};
uint8 tx_buf[TX_PLOAD_WIDTH] = {0};
 
// USEEKEY (пользовательский ключ, который выдают на DeviceBit
// после регистрации):
#define LW_USERKEY "611442bf3e1344ddac7302fe43187e91"
// Номера шлюза пользователя:
#define LW_GATEWAY "01"
 
// Параметры сетевого интерфейса, которые следует при необходимости
// раскомментировать и настроить в соответствии с конфигурацией
// локальной сети, к которой через Ethernet-шилд WS5100 подключена
// Ваша платка Arduino MEGA2560.
//byte mac[] = {0xD0,0xC7,0xC0,0xAC,0xC5,0x81};
//IPAddress ip(192,168,0,135);
//IPAddress mydns(223,5,5,5);
//IPAddress gw(192,168,0,1);
//IPAddress subnet(255,255,255,0);
 
DeviceBitTcpClient *client;
 
int postInterval = 30000;
 
// Последнее время, когда Вы были подключены к серверу, в миллисекундах:
unsigned long lastConnectionTime = 0;
// Последнее состояние соединение в главном цикле:
boolean lastConnected = false;
// Задержка между обновлениями на cosm.com.
const unsigned long postingInterval = 30*1000;
 
unsigned long duration;
unsigned long starttime;
unsigned long sampletime_ms = 10000;
unsigned long lowpulseoccupancy = 0;
float ratio = 0;
double concentration = 0;
 
#define p0 101325          // Давление на высоте уровня моря (Pa) p0 = 101325;
#define currentAltitude 8  // Текущая высота в метрах
// Ожидаемое давление (в Pa) на этой высоте:
const float epressure = p0 * pow((1-currentAltitude/44330), 5.255);
#define BMP085_ADDRESS 0x77   // адрес I2C для BMP085
const unsigned char OSS = 0;  // Настройка оверсамплинга
 
float pressure = 0;
float temperature = 0;
float altitude = 0;
char weather;
 
#define DELAYTIME 1000
#define F_CPU 16000000
#define F_DIV 1024
#define TIME 0.1
volatile uint8 count1,count2 = 0;
 
// Значения калибровки:
int ac1; int ac2; int ac3;
uint16 ac4; uint16 ac5; uint16 ac6;
int b1; int b2;
int mb; int mc; int md;
// b5 вычисляется в bmp085GetTemperature(...), эта переменная также используется
// в bmp085GetPressure(...), так что ...Temperature(...) должна быть вызвана
// перед ...Pressure(...).
long b5;
 
uint8 pirValue = 0;
boolean pirState = false;
uint8 rfidValue = 0;
boolean rfidState = false;
 
#define photocellPin 0
#define mq2Pin 1
#define light1Pin 8  //Установка операции управления порта I/O
#define light2Pin 9
#define pirPin 36
#define DHT11PIN 34
#define rfidLED 38
#define pirLED 40
 
void setup() 
{
   Serial.begin(9600);
   if (!bmp.begin()) 
   {
      Serial.println("Could not find a valid BMP085 sensor, check wiring!");
      while (1);
   }
   //Wire.begin();
   //calibration();
   rfid.begin(2, 3, 4, 5, 6, 7);
   delay(100);
   rfid.init();
   pinMode(pirPin, INPUT);
   pinMode(rfidLED, OUTPUT);
   pinMode(pirLED, OUTPUT);
   
   pinMode(CE,  OUTPUT);
   pinMode(SCK_PIN, OUTPUT);
   pinMode(CSN, OUTPUT);
   pinMode(MOSI_PIN,  OUTPUT);
   pinMode(MISO_PIN, INPUT);
   pinMode(IRQ, INPUT);
   //Инициализация порта ввода/вывода
   init_io();
   unsigned char status=SPI_Read(STATUS);
   Serial.print("status = ");    
   // Здесь читается регистр статуса режима, значение по умолчанию должно быть 'E'
   Serial.println(status,HEX);
   Serial.println("*******************TX_Mode Start****************************");
   TX_Mode();
   
   TIMER1_init();
   client = new DeviceBitTcpClient(LW_USERKEY, LW_GATEWAY);
   //client = new LeweiTcpClient(LW_USERKEY, LW_GATEWAY,mac,ip,mydns,gw,subnet);
   UserSwitch us1(switchLight1,"light1",1);
   client->addUserSwitch(us1);
   UserSwitch us2(switchLight2,"light2",1);
   client->addUserSwitch(us2);
   UserSwitch us3(switchLight3,"light3",1);
   client->addUserSwitch(us3);
   UserSwitch us4(switchLight4,"light4",1);
   client->addUserSwitch(us4);
   starttime = millis();
   pinMode(light1Pin,OUTPUT);
   pinMode(light2Pin,OUTPUT);
}
 
void loop() 
{
  //long pressure = 0;
  //float altitude = 0;
   
  client->keepOnline();
  if ((millis()-starttime) > postInterval)
  {
     //humiture
     dhtPreprocess();
     client -> appendSensorValue("Humidity", DHT11.humidity); 
     delay(DELAYTIME);
     client -> appendSensorValue("Temperature", DHT11.temperature);
     delay(DELAYTIME);
     //illumination intensity
     client -> appendSensorValue("Light",analogRead(photocellPin));    
     delay(DELAYTIME);
     //gas sensor
     client -> appendSensorValue("Gas",analogRead(mq2Pin));  
     delay(DELAYTIME);
     //long pressure = 0;
     //float altitude = 0;
     //getBmpData(&pressure, &altitude);
     //int pressureInt = (int)pressure;
     readBmp();
     client -> appendSensorValue("Pressure", pressure);
     delay(DELAYTIME);
     client -> appendSensorValue("Altitude",weather);
     delay(DELAYTIME);  
     client->appendSensorValue("PIR",pirValue);         
     delay(DELAYTIME);
     //RFID
     client->sendSensorValue("RFID",rfidValue); 
     delay(DELAYTIME);      
     Serial.println("repeat lwc->send  ...Completed.");
     starttime = millis();
  }
}
 
void stateChange1()
{
  if(digitalRead(light1Pin)==HIGH)
  {
    switchLight1("0");
  }
  else
  {
    switchLight1("1");
  }
}
 
void stateChange2()
{
  if(digitalRead(light2Pin)==HIGH)
  {
    switchLight2("0");
  }
  else
  {
    switchLight2("1");
  }
}
 
static void send (void)
{
   // чтение регистра статуса
   unsigned char status = SPI_Read(STATUS);
   // если прерывание готовности данных передачи tratsmit data send (TX_DS)
   if(status&TX_DS)
   {
     SPI_RW_Reg(FLUSH_TX,0);
     // записать полезную нагрузку (данные) в TX_FIFO
     SPI_Write_Buf(WR_TX_PLOAD,tx_buf,TX_PLOAD_WIDTH);
   }
   // если прерывание готовности приема данных receive data ready (MAX_RT),
   // то повторная передача SETUP_RETR
   if(status&MAX_RT)                          
   {
     // запрет режима standy
     SPI_RW_Reg(FLUSH_TX,0);
     // запись полезной нагрузки в TX_FIFO
     SPI_Write_Buf(WR_TX_PLOAD,tx_buf,TX_PLOAD_WIDTH);
   }
   // очистка флагов прерывания RX_DR, TX_DS или MAX_RT
   SPI_RW_Reg(WRITE_REG+STATUS,status);
}
 
void switchLight1(char* p1)
{
   Serial.println("switch1 test");  
   //stateChange1();
   if(String(p1).equals("0"))
   {
      digitalWrite(light1Pin,HIGH);
      tx_buf[0] = 0x55;
      tx_buf[1] = 0xAA;
      tx_buf[2] = 0x11;
      send();
      //delay(1000);
   }
   else
   {
      digitalWrite(light1Pin,LOW);
      tx_buf[0] = 0x55;
      tx_buf[1] = 0xAA;
      tx_buf[2] = 0x10;
      send();
      //delay(1000);
   }
   client->checkFreeMem();
}
 
void switchLight2(char* p1)
{
   Serial.println("switch2 test"); 
   //stateChange2();
   if(String(p1).equals("0"))
   {
      digitalWrite(light2Pin,HIGH);
      tx_buf[0] = 0x55;
      tx_buf[1] = 0xAA;
      tx_buf[2] = 0x21;
      send();
      //delay(1000);
   }
   else
   {
      digitalWrite(light2Pin,LOW);
      tx_buf[0] = 0x55;
      tx_buf[1] = 0xAA;
      tx_buf[2] = 0x20;
      send();
      //delay(1000);
   }
   client->checkFreeMem();
}
 
void switchLight3(char* p1)
{
   Serial.println("switch1 test");  
   //stateChange1();
   if(String(p1).equals("0"))
   {
      //digitalWrite(light3Pin,HIGH);
      tx_buf[0] = 0x55;
      tx_buf[1] = 0xAA;
      tx_buf[2] = 0x31;
      send();
   }
   else
   {
      //digitalWrite(light3Pin,LOW);
      tx_buf[0] = 0x55;
      tx_buf[1] = 0xAA;
      tx_buf[2] = 0x30;
      send();
    }
    client->checkFreeMem();
}
 
void switchLight4(char* p1)
{
   Serial.println("switch2 test"); 
   //stateChange2();
   if(String(p1).equals("0"))
   {
      //digitalWrite(light4Pin,HIGH);
      tx_buf[0] = 0x55;
      tx_buf[1] = 0xAA;
      tx_buf[2] = 0x41;
      send();
   }
   else
   {
      //digitalWrite(light4Pin,LOW);
      tx_buf[0] = 0x55;
      tx_buf[1] = 0xAA;
      tx_buf[2] = 0x40;
      send();
   }
   client->checkFreeMem();
}
 
//**************************************************
// Функция: init_io();
// Описание: однократно мигает светодиодом, разрешает
// чип (для готовности режима TX или RX), запрещает
// SPI, на выходе тактовый сигнал SPI в уровне лог. 1.
//**************************************************
void init_io(void)
{
  digitalWrite(IRQ, 0);
  digitalWrite(CE, 0);  // разрешение чипа
  digitalWrite(CSN, 1); // запрет SPI
}
 
/**************************************************
 * Функция: SPI_RW();
 * Описание: записывает байт в nRF24L01 и возвращает
 * байт, прочитанный из nRF24L01 в процессе записи,
 * как это подразумевает протокол SPI.
 **************************************************/
unsigned char SPI_RW(unsigned char Byte)
{
  unsigned char i;
  //Цикл вывода 8 бит:
  for(i=0; i < 8; i++)
  {
    if(Byte&0x80)
    {
      digitalWrite(MOSI_PIN, 1);
    }
    else
    {
      digitalWrite(MOSI_PIN, 0);
    }
    digitalWrite(SCK_PIN, 1);
    // вдвинуть следующий бит в старший бит (MSB)
    Byte << = 1;                         
    if(digitalRead(MISO_PIN) == 1)
    {
      Byte |= 1;     // захват текущего значения бита MISO
    }
    digitalWrite(SCK_PIN, 0);
  }
  return(Byte);      // возврат прочитанного байта
}
 
/**************************************************
 * Функция: SPI_RW_Reg();
 * Описание: записывает значение value в регистр reg.
 **************************************************/
unsigned char SPI_RW_Reg(unsigned char reg, unsigned char value)
{
  unsigned char status;
  digitalWrite(CSN, 0);    // CSN==0, начало транзакции SPI
  status = SPI_RW(reg);    // выбор регистра
  SPI_RW(value);           // .. и запись в него значения ..
  digitalWrite(CSN, 1);    // затем перевод CSN обратно в лог. 1
  return(status);          // возврат байта состояния nRF24L01
}
 
/**************************************************
 * Функция: SPI_Read();
 * Описание: чтение одного байта из регистра reg
 * модуля nRF24L01.
 **************************************************/
unsigned char SPI_Read(unsigned char reg)
{
  unsigned char reg_val;
  digitalWrite(CSN, 0);    // CSN==0, инициализация обмена SPI
  SPI_RW(reg);             // выбор регистра для чтения ..
  reg_val = SPI_RW(0);     // .. и затем чтение его значения
  digitalWrite(CSN, 1);    // CSN==1, прервать обмен по SPI
  
  return(reg_val);         // возврат значения регистра
}
 
/**************************************************
 * Функция: SPI_Read_Buf();
 * Описание: чтение нужного количества байт из регистра
 * reg. Обычно эта функция используется для чтения
 * полезной нагрузки приема, адресов Rx/Tx.
 **************************************************/
unsigned char SPI_Read_Buf(unsigned char reg, unsigned char *pBuf, unsigned char bytes)
{
  unsigned char status,i;
 
  digitalWrite(CSN, 0);    // CSN==0, инициализация обмена SPI
  status = SPI_RW(reg);    // выбор регистра для записи и чтение байта из регистра статуса
 
  for(i=0; i < bytes; i++)
  {
    pBuf[i] = SPI_RW(0);   // выполнить SPI_RW для чтения байтов из nRF24L01
  }
 
  digitalWrite(CSN, 1);    // вернуть CSN в состояние лог. 1
  return(status);          // возврат байта состояния nRF24L01
}
 
/**************************************************
 * Функция: SPI_Write_Buf();
 * Описание: запись содержимого буфера по указателю
 * pBuf в nRF24L01. Обычно используется для записи
 * полезной нагрузки передачи, адресов Rx/Tx.
 **************************************************/
unsigned char SPI_Write_Buf(unsigned char reg, unsigned char *pBuf, unsigned char bytes)
{
  unsigned char status,i;
  digitalWrite(CSN, 0);    // CSN==0, инициализация обмена SPI
  status = SPI_RW(reg);    // выбор регистра для записи и чтение байта из регистра статуса
  for(i=0; i < bytes; i++)    // в цикле записать все значения байт из буфера (*pBuf)
  {
    SPI_RW(*pBuf++);
  }
  digitalWrite(CSN, 1);    // вернуть CSN в состояние лог. 1
  return(status);          // возврат байта состояния nRF24L01
}
 
/**************************************************
 * Функция: TX_Mode();
 * Описание: эта функция инициализирует одно устройство
 * nRF24L01 в режим передачи, устанавливает его адрес TX,
 * устанавливает адрес RX для auto.ack, заполняет полезную
 * нагрузку TX, выбирает канал RF, скорость передачи бит и 
 * мощность передачи TX. PWR_UP установлен, CRC 
 * (2 байта) разрешено, и PRIM:TX.
 * 
 * Что доделать: один импульс (>10 мкс) на CE теперь отправит
 * этот пакет, после чего ожидается подтверждение от 
 * противоположной принимающей стороны RX.
 **************************************************/
void TX_Mode(void)
{
  digitalWrite(CE, 0);
 
  // запись TX_Address в nRF24L01:
  SPI_Write_Buf(WRITE_REG + TX_ADDR, TX_ADDRESS, TX_ADR_WIDTH);
  // RX_Addr0 тот же, что и TX_Adr для функции Auto.Ack
  SPI_Write_Buf(WRITE_REG + RX_ADDR_P0, TX_ADDRESS, TX_ADR_WIDTH);
 
  SPI_RW_Reg(WRITE_REG + EN_AA, 0x01);      // разрешить Auto.Ack:Pipe0
  SPI_RW_Reg(WRITE_REG + EN_RXADDR, 0x01);  // разрешить Pipe0
  SPI_RW_Reg(WRITE_REG + SETUP_RETR, 0x1a); // 500 мкс + 86 мкс, 10 повторных передач...
  SPI_RW_Reg(WRITE_REG + RF_CH, 40);        // выбор RF-канала 40
  SPI_RW_Reg(WRITE_REG + RF_SETUP, 0x07);   // TX_PWR:0dBm, Datarate:2Mbps, LNA:HCURR
  SPI_RW_Reg(WRITE_REG + CONFIG, 0x0e);     // установка бита PWR_UP bit, разрешить CRC & Prim:TX,
                                            // разрешены прерывания MAX_RT & TX_DS.
  SPI_Write_Buf(WR_TX_PLOAD,tx_buf,TX_PLOAD_WIDTH);
  
  digitalWrite(CE, 1);
}
 
// Препроцессор для сенсора температуры и влажности
void dhtPreprocess(void)
{
   int chk = DHT11.read(DHT11PIN);
   switch (chk)
   {
   case DHTLIB_OK:  
      break;
   case DHTLIB_ERROR_CHECKSUM: 
      break;
   case DHTLIB_ERROR_TIMEOUT:  
      break;
   default:  
      break;
   }
}
 
void getBmpData(long *pre, float *alt)
{
   *pre = getPressure(readUP());
   *alt = (float)44330 * (1 - pow(((float) (*pre)/p0), 0.190295));
}
 
long getPressure(uint32 up)
{
   long x1, x2, x3, b3, b6, p;
   uint32 b4, b7;
  
   b6 = b5 - 4000;
   // вычисление B3
   x1 = (b2 * (b6 * b6)>>12)>>11;
   x2 = (ac2 * b6)>>11;
   x3 = x1 + x2;
   b3 = (((((long)ac1)*4 + x3) << OSS) + 2)>>2;
  
   // вычисление B4
   x1 = (ac3 * b6)>>13;
   x2 = (b1 * ((b6 * b6)>>12))>>16;
   x3 = ((x1 + x2) + 2)>>2;
   b4 = (ac4 * (unsigned long)(x3 + 32768))>>15;
  
   b7 = ((unsigned long)(up - b3) * (50000>>OSS));
   if (b7 < 0x80000000)
      p = (b7<<1)/b4;
   else
      p = (b7/b4) << 1;
    
   x1 = (p>>8) * (p>>8);
   x1 = (x1 * 3038)>>16;
   x2 = (-7357 * p)>>16;
   p += (x1 + x2 + 3791)>>4;
  
   return p;
}
 
// Чтение некомпенсированного значения давления
unsigned long readUP(void)
{
  unsigned char msb, lsb, xlsb;
  unsigned long up = 0;
  
  // Запись 0x34+(OSS << 6) в регистр 0xF4 запрашивает
  // чтение давления с установкой оверсамплинга
  Wire.beginTransmission(BMP085_ADDRESS);
  Wire.write(0xF4);
  Wire.write(0x34 + (OSS << 6));
  Wire.endTransmission();
  
  // Ожидание преобразования, время задержки зависит от OSS
  delay(2 + (3 << OSS));
  
  // Чтение регистров 0xF6 (MSB), 0xF7 (LSB) и 0xF8 (XLSB)
  Wire.beginTransmission(BMP085_ADDRESS);
  Wire.write(0xF6);
  Wire.endTransmission();
  Wire.requestFrom(BMP085_ADDRESS, 3);
  
  // Ожидание доступности данных
  while(Wire.available() < 3)
    ;
  msb = Wire.read();
  lsb = Wire.read();
  xlsb = Wire.read();
  
  up = (((unsigned long) msb << 16) 
     | ((unsigned long) lsb << 8)
     | (unsigned long) xlsb) >> (8-OSS);
  
  return up;
}
 
void calibration(void)
{
   ac1 = readInt(0xAA);
   ac2 = readInt(0xAC);
   ac3 = readInt(0xAE);
   ac4 = readInt(0xB0);
   ac5 = readInt(0xB2);
   ac6 = readInt(0xB4);
   b1  = readInt(0xB6);
   b2  = readInt(0xB8);
   mb  = readInt(0xBA);
   mc  = readInt(0xBC);
   md  = readInt(0xBE);
}
 
// Чтение 2 байт из BMP085 (или BMP180)
// Первый байт будет из 'адреса'
// Второй байт будет из 'адреса'+1
int readInt(uint8 address)
{
  uint8 msb, lsb;
  
  Wire.beginTransmission(BMP085_ADDRESS);
  Wire.write(address);
  Wire.endTransmission();
  
  Wire.requestFrom(BMP085_ADDRESS, 2);
  while(Wire.available() < 2)
    ;
  msb = Wire.read();
  lsb = Wire.read();
  
  return (int) msb<<8 | lsb;
}
 
void TIMER1_init(void)
{
   //cli();
   TCCR1A=0;              //Обработчик вывода PWM на таймере 1
   TCCR1B = 0x05;
   TCNT1 = 65536 - F_CPU / F_DIV * TIME;  
   TIMSK1 |= _BV(TOIE1);  //открыть прерывание таймера 1
   SREG |= _BV(7);  
}
 
ISR(TIMER1_OVF_vect)
{
   TCNT1 = 65536 - F_CPU / F_DIV * TIME; 
   count1 ++;
   if(count1 == 5)
   {
      count1 = 0;   
      if(readRfid())
      {
         rfidValue ++;
         rfidState = ~rfidState;
         digitalWrite(rfidLED, rfidState);
      }
   }
   if(digitalRead(pirPin))
   {
      while(digitalRead(pirPin));         
      pirValue ++;
      pirState = ~pirState;
      digitalWrite(pirLED, pirState);
   }
}
 
uint8 readRfid(void)
{
   uint8 status;
   uint8 str[MAX_LEN];
   status = rfid.request(PICC_REQIDL, str);
   if (status != MI_OK)
   {
     return 0;
   }
   status = rfid.anticoll(str);
   
   if (status == MI_OK)
   {
     memcpy(serNum, str, 5);
     
     // Проверка человека, связанного с этим ID карты
     uchar* id = serNum;
  
     if(id[0] == 0x15 && id[1] == 0x6F && id[2] == 0x9F && id[3] == 0xAF)
     {
         return 1;
     }
   }
   return 0;
    
   rfid.halt(); //команда карте перейти в режим сна (sleep mode)
}
 
void readBmp(void)
{
   /* Получить новое событие от сенсора */ 
   sensors_event_t event;
   bmp.getEvent(&event);
   
   /* Отобразить результаты (барометрическое давление в hPa) */
   if (event.pressure)
   {
      pressure = event.pressure;
      bmp.getTemperature(&temperature);
      if((pressure - epressure) > 250)
      {
         weather = 1;
      }
      else if(((pressure - epressure) < = 250) && ((pressure - epressure) >= -250))
      {
         weather = 0;
      }
      else if((pressure - epressure) < -250)
      {
         weather = -1;
      }
      float seaLevelPressure = SENSORS_PRESSURE_SEALEVELHPA;
      altitude = bmp.pressureToAltitude(seaLevelPressure,event.pressure,temperature);
   }
   else
   {
      Serial.println("Sensor error");
   }
}

[userSwitch_revertControl.ino]

Пример "обратного" управления: с сервера DeviceBit могут быть отправлены команды на Вашу плату Arduino.

#include < SPI.h >
#include < Ethernet.h >
#include < DeviceBitTcpClient.h >
#include < EEPROM.h >
 
// Вместо your_api_key подставьте ключ, который Вам выдали на сайте
// DeviceBit при регистрации:
#define DB_USERKEY "your_api_key"
#define DB_GATEWAY "01"
 
int relayPin = 6;
 
// Параметры сети, которые при необходимости следует раскомментировать
// и настроить:
//byte mac[] = {0x74,0x69,0x69,0x2D,0x30,0x31};
//IPAddress ip(192,168,1, 15);
//IPAddress mydns(8,8,8,8);
//IPAddress gw(192,168,1,1);
//IPAddress subnet(255,255,255,0);
 
DeviceBitTcpClient *client;long starttime;int postInterval = 30000;
  
void setup()
{
  Serial.begin(9600);
  pinMode(relayPin, OUTPUT); 
  starttime = millis();
  // Вам доступно 2 способа инициализации сетевого интерфейса
  // 1. Самый простой:
  client = new DeviceBitTcpClient(DB_USERKEY, DB_GATEWAY);
  // 2. Полная настройка сетевого интерфейса:
  //client = new DeviceBitTcpClient(DB_USERKEY, DB_GATEWAY,mac,ip,mydns,gw,subnet);
  
  // switchTest является функцией, которую Вы напишете и вставите ниже, чтобы
  // работать с программным "relay", имя контроллера которого Вы установили 
  // на сайте http://www.devicebit.com/ в меню "control command manager".
  // Мы тестировали передачу с сайта в плату Arduino.
  
  UserSwitch us1 (switchTest,"relay",0);
  client->addUserSwitch(us1);
  //client->easySetupMode(true); 
}
 
void loop()
{
   client->keepOnline();
}
 
void switchTest(char* p1)
{
   if(String(p1).equals("0"))
   {
      digitalWrite(relayPin,LOW);
      Serial.println("off");
   }
   else if(String(p1).equals("1"))
   {
      digitalWrite(relayPin,HIGH);
      Serial.println("on");
   }
   //client->checkFreeMem();
}

Дополнительным приложением к примерам служит руководство SmartHome kit For Arduino.pdf, где подробно, с картинками и пояснениями разобраны все эти примеры, даны схемы коммутации - как и что с чем соединять. Все довольно просто и ясно написано, на хорошем английском языке.

Чтобы позапускать проекты, Вы можете воспользоваться либо системой разработки Fritzing [3] (она есть на компакт-диске с примерами, но лучше всего скачать свежую версию с сайта авторов Fritzing), либо системой разработки Arduino IDE [4] (её на компакт-диске нет, но можно свободно скачать с сайта arduino.cc). Если Вы достаточно опытны, то вместо Arduino-совместимых сред программирования можете использовать AVR Studio, IAR, Keil, CodeVision AVR или что-нибудь еще - выбор систем программирования для AVR большой.

[devicebit.com]

Платформа DeviceBit [5] является системой реального времени, которая является посредником между так называемым новомодным "Интернетом вещей" (Internet of Things, IOT) и людьми, которые хотят своими вещами удаленно управлять (под "вещами" подразумевается интеллектуальные домашние системы наподобие холодильников, сигнализаций, теплиц, газовых котлов и т. п.). Посредничество осуществляется через специальное API. Сервера DeviceBit дают быстрый и простой метод добавлять устройства (Devices) и приложения (Applications), к платформе DeviceBit. При этом предоставляется хранилище статистики, работающее в реальном времени, и возможность удаленного управления объектами. Платформа DeviceBit является не просто простым методом для экспериментирования с новыми датчиками, которые могут передавать данные через Интернет; это также сервис, который помогает компаниям вывести свои продукты на рынок. 

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

Вы можете реализовать свои собственные идеи и разработать свои собственные устройства, которые полагаются на возможности платформы DeviceBit. Вы можете сфокусироваться на аппаратуре вместо того, чтобы разрабатывать программную инфраструктуру. 

Также платформа DeviceBit обменивается данными с существующими социальные сетями, такими как Twitter и Facebook, что позволяет предоставлять в общий доступ Вашим друзьям свои устройства (для управления или получения информации), что может быть очень полезным в различных похожих областях деятельности.

Чтобы получить доступ к возможностям DeviceBit (это понадобится, чтобы запустить пример Union с компакт-диска SUNFOUNDER), нужно зарегистрироваться у них на сайте [5]. Это очень просто: введите логин, придумайте пароль, укажите свой реальный email и нажмите на кнопку Sign Up. На этом регистрация завершится.

DeviceBit register

После входа на сайт Вы увидите приглашение, и получите доступ к инструментарию DeviceBit. Можно добавлять свои устройства, подключать к ним сенсоры и т. п.

DeviceBit welcome screen

[Ссылки]

1. SUNFOUNDER site:sunfounder.com.
2. SunFounder DIY Smart Home System Internet of things Kit for Arduino Raspberry Pi site:ebay.com.
3. Fritzing: как перестать бояться электроники.
4. Arduino: что там внутри? (FAQ).
5. DeviceBit site:devicebit.com.
6. 150418SUNFOUNDER-Arduino.zip - примеры кода, документация, демонстрационные видеоролики.
7. Arduino MEGA 2560.

 

Добавить комментарий


Защитный код
Обновить

Top of Page