Система автоматизации на базе Arduino (умный дом, умная теплица, охранная сигнализация)

SmartRnd
Offline
Зарегистрирован: 13.09.2016

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

В данном случае необходимо разработать устройства с интерфейсом RS-485 и написать протокол обмена данными между ними. С этой задачей уже сталкивался или еще столкнется любой разработчик оборудования. Каждый проходит этот путь самостоятельно, наступая на грабли, набившие шишки сотням предшественников.

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

Протокол предназначен для построения централизованных систем с одним контроллером, который управляет всеми подчиненными модулями.

Какие плюсы дает это вам:

  • протокол уже написан, его не требуется сочинять;

  • уже есть базовый скетч, который работает на данном протоколе. Хотите сделать адресный датчик температуры? Возьмите Arduino, привяжите к нему датчик температуры и немного допишите код. Все готово!!!;

  • протокол уже проверен и отлажен, т. е. уже реально используется много лет. В нем есть всё необходимое;

  • в свободном доступе находится программа-контроллер, которая позволяет управлять этими модулями по любому алгоритму, который вы сами захотите написать;

  • если вы еще не разработали какие-то модули с нужным функционалам, вы можете покупать их у производителей, которые уже давно занимаются устройствами на протоколе ADNet. Разработали свое устройство, используйте его.

  •  

SmartRnd
Offline
Зарегистрирован: 13.09.2016

Описание протокола:

http://smart-elec.ru/manuals/alpha/ADNetProtocol.pdf

Базовый счетч для модуля, работающего на протоколе ADNet

/*
  Пример модуля, работающего по протоколу ADNet

  Этот пример является шаблоном для разработки новых модулей, работающих по протоколу ADNet

  Описание PIN
  0 - RX RS485 RO
  1 - TX RS485 DI
  2 - управление приёмом и передачей по RS485. На этой же ноге висит Led индикации передачи
  3 - Led индикации работы. Мигает 1 раз в секунду, если всё хорошо, иначе - ошибка
  4 - задание адреса модуля по шине ADNet
  5 - задание адреса модуля по шине ADNet
  6 - задание адреса модуля по шине ADNet
  7 - задание адреса модуля по шине ADNet
  8 - задание адреса модуля по шине ADNet
  9 - задание адреса модуля по шине ADNet  

  Created 14 сентября 2016г
  Быков Виктор Сергеевич

 */
#include <EEPROM.h>
 
unsigned char buff_in[8]; // буфер входящих символов
unsigned char rbit_num=0; // количество байт во входящем буфере

unsigned char RS485out = 2; // Порт управления прием/передача RS485
unsigned char ledWork = 3;  // Светодиод индикации
unsigned char jmp0 = 4;  // Джампер 0
unsigned char jmp1 = 5;  // Джампер 1
unsigned char jmp2 = 6;  // Джампер 2
unsigned char jmp3 = 7;  // Джампер 3
unsigned char jmp4 = 8;  // Джампер 4
unsigned char jmp5 = 9;  // Джампер 5
unsigned char addr; // Адрес модуля в шине
unsigned char temp_uch;
int temp_i;
int ledWork_freq; //время между переключениями светодиода: 1000 все хорошо, иначе плохо
unsigned long last_ledWork; //время последнего переключения светодиода

// Массив для загрузки
#define eeprom_size 64 // Размерность массива eeprom
unsigned char ee_params[eeprom_size]={ 2,251,11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
/*                                    00  01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
00 - версия модуля
01 - номер модуля
02 - тип модуля
Остальные параметры программист может использовать для нужд устройства
*/ 

#define read_only_params_count 3 // Количество параметров только для чтения
unsigned char read_only_params[read_only_params_count]={0,1,2};

void setup() {
  delay(3000);
  // initialize serial:
  Serial.begin(9600);
  ledWork_freq=1000; // Частот переключения LED работы 1000 - 1 переключение в секунду  
  pinMode(RS485out,OUTPUT);  
  pinMode(ledWork,OUTPUT);
  pinMode(jmp0,INPUT);
  pinMode(jmp1,INPUT);  
  pinMode(jmp2,INPUT);
  pinMode(jmp3,INPUT);
  pinMode(jmp4,INPUT);
  pinMode(jmp5,INPUT);    
  
  digitalWrite(jmp0,HIGH); // Включили подтягивающий резистор 20кОм на +
  digitalWrite(jmp1,HIGH); // Включили подтягивающий резистор 20кОм на +
  digitalWrite(jmp2,HIGH); // Включили подтягивающий резистор 20кОм на +
  digitalWrite(jmp3,HIGH); // Включили подтягивающий резистор 20кОм на +
  digitalWrite(jmp4,HIGH); // Включили подтягивающий резистор 20кОм на +
  digitalWrite(jmp5,HIGH); // Включили подтягивающий резистор 20кОм на +
  
  if (EEPROMread(0)!=ee_params[0]) // Если EEPROM не задан
  {
    //присваиваем начальные значения
    for (temp_i=0; temp_i<eeprom_size; temp_i++)
    {
      if (temp_i!=1)
        EEPROMwrite(temp_i,ee_params[temp_i]);
    };
  };
  EEPROMfix(); // Проверяем правильность значений в eeprom
  digitalWrite(RS485out,LOW);  // Переходим в прием сообщений
}
// ------------------Чтение EEPROM-----------------------
unsigned char EEPROMread(unsigned char num)
{
  // В EEPROM могут храниться данные только unsigned char
  unsigned char res_0, res_1, res_2, total_res;
  if (num>eeprom_size) return 255;
  res_0=EEPROM.read(num);
  res_1=EEPROM.read(eeprom_size+num);  
  res_2=EEPROM.read(eeprom_size*2+num);
  if (res_0==res_1) return res_0;
  if (res_0==res_2) return res_0;
  if (res_1==res_2) return res_1;
  return 255;
};
// ------------------Запись EEPROM-----------------------
void EEPROMwrite(unsigned char num, unsigned char value)
{
  // В EEPROM могут храниться данные только unsigned char
  if (num>eeprom_size) return;
  if (EEPROMread(num)==value) return;
  EEPROM.write(num,value);
  EEPROM.write(eeprom_size+num,value);
  EEPROM.write(eeprom_size*2+num,value);
};
// ------------------Ремонт EEPROM-----------------------
void EEPROMfix()
{
  int num;
  unsigned char res_0, res_1, res_2, total_res;
  for (int num=0;num<eeprom_size;num++)
  {
    res_0=EEPROM.read(num);
    res_1=EEPROM.read(eeprom_size+num);  
    res_2=EEPROM.read(eeprom_size*2+num);
    if ((res_0==res_1) && (res_1==res_2)) continue;
    if (res_0==res_1) {EEPROM.write(eeprom_size*2+num,res_0);continue;}
    if (res_0==res_2) {EEPROM.write(eeprom_size+num,res_0);continue;}
    if (res_1==res_2) {EEPROM.write(num,res_1);continue;}
    // Если все 3 значения разные
    EEPROM.write(num,0);
    EEPROM.write(eeprom_size+num,0);
    EEPROM.write(eeprom_size*2+num,0);
  }
};
// ------------------Основной цикл программы-----------------------
void loop() {
  addr=GetAddr();
  
  if (millis()-last_ledWork>ledWork_freq) // Мигание светодиода работы
  {
    last_ledWork=millis();
    digitalWrite(ledWork,!digitalRead(ledWork)); // Включаем светодиод
  };
}

// ------------------Функция передачи сообщений по RS-485-----------------------
void transmit()
{
  digitalWrite(RS485out,HIGH); // Переключаемся в передачу
  Serial.write(buff_in, 8); // Передаем ответ
  Serial.flush(); // Ожидаем отправку всей посылки
  digitalWrite(RS485out,LOW); // Переключаемся в приём  
}

// ------------------Вычисление адреса модуля--------------------------
unsigned char GetAddr()
{
  unsigned char addr=0;
  if (digitalRead(jmp0)==LOW)
    addr+=1;
  if (digitalRead(jmp1)==LOW)
    addr+=2;
  if (digitalRead(jmp2)==LOW)
    addr+=4;
  if (digitalRead(jmp3)==LOW)
    addr+=8;
  if (digitalRead(jmp4)==LOW)
    addr+=16;
  if (digitalRead(jmp5)==LOW)
    addr+=32;    
  if (addr==0) return 255;
  return addr;      
}

// ------------------ Вычисление контрольной суммы для модуля-----------
unsigned char  calc_ad_crc() // Вычисление crc для ADNet
{
  unsigned char i, crc[3];
  crc[1]=0;
  for(i=2;i<7;i++)
    crc[1]=crc[1]+buff_in[i];
  return crc[1];
}

// ------------------Приём данных-----------------------

void serialEvent() {

  while (Serial.available()) {
    // get the new byte:
    if (rbit_num<8)
    {
      buff_in[rbit_num]=(char)Serial.read();
      rbit_num++;
    };
    if (rbit_num==8) // Если в буфере 8 символов
    {
      if ((buff_in[0]==255)&&(buff_in[1]==255)) // Если есть 2 стартовых байта 255 и 255
      {
        temp_uch=calc_ad_crc();
        if (buff_in[7]==temp_uch) //Если crc совпадает
        {
          if (buff_in[3]==addr) // Если адрес модуля совпадает
          {
            func(); // выполняем команду, которую прислали на выполение
            rbit_num=0;
          };
        };
      };
      if (rbit_num==8) // Если во вхоящем буфере всё еще 8 симовлов
      {
        // смещаем во входящем буфере все на один символ влево
        for (int i=0;i<7;i++)
        {
          buff_in[i]=buff_in[i+1];
          rbit_num=7;
        };
      };
    };
  };
};

// Функция обработка сообщений RS-485
void func()
{
  unsigned char i;
  buff_in[3]=0;
  
  switch (buff_in[2])
  {

  case 0:
    buff_in[4]=EEPROMread(0);
    buff_in[5]=EEPROMread(2);
    buff_in[6]=30; //для SE6i5o
    //msg[6]=33; для secu 16
    buff_in[7]=calc_ad_crc();
    transmit();
    break;
    
  case 5: // вернуть значение параметра
    switch (buff_in[5])
    {
      case 1:
        buff_in[4]=addr;
        break;
      case 9:
        buff_in[4]=0;//curr_hum;
        break;
      case 10:
        buff_in[4]=0;//curr_temp;
        break;
      default:
        buff_in[4]=(buff_in[5]<eeprom_size)?EEPROMread(buff_in[5]):0; 
    }
    buff_in[7]=calc_ad_crc();
    transmit();
    break;

  case 6: // изменить значение параметра
    if(buff_in[5]>=eeprom_size) return;
    for (i=0;i<read_only_params_count;i++) //проверяем read only параметры
      if (buff_in[5]==read_only_params[i]) goto a;
    EEPROMwrite(buff_in[5],buff_in[4]);
    a:
    buff_in[7]=calc_ad_crc();
    transmit();
    break;

  case 10: // изменение выходов
    buff_in[7]=calc_ad_crc();    
    transmit();
    break;
  case 11: // запрос выходов
    buff_in[4]=0;//curr_hum;
    buff_in[6]=0;//curr_temp;
    buff_in[7]=calc_ad_crc();    
    transmit();
    break;
  default:
  break;
  }
}

 

SmartRnd
Offline
Зарегистрирован: 13.09.2016

Скачать бесплатную программу-контроллер, управляющую модулями по протоколу ADNet можно здесь:

http://alpha-se.ru/alpha.php?page1=406&page2=42&razdel=3&info=114

Посмотреть видео с обзором протокола и использованием программы контроллера можно здесь:

http://alpha-se.ru/alpha.php?page1=403&page2=124

На данный момент мне известны следующие производители оборудования для протокола ADNet:

Умная электроника http://alpha-se.ru

Разумный дом http://www.razumdom.ru

Умный дом http://ydom.ru/

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Спасибо.