Форум обсуждения систем  

Вернуться   Форум обсуждения систем "Умный дом", проектов Ардуино, OpenWRT и других DIY устройств > Форум умного дома > Сделай сам > Открытый проект умного дома

 
 
Опции темы Поиск в этой теме Опции просмотра
Старый 24.02.2015, 20:16   #1
Kopylov
Junior Member
 
Регистрация: 12.02.2015
Сообщений: 18
Вес репутации: 0
Kopylov is an unknown quantity at this point
По умолчанию OpenWRT + modbus + arduino = учёт эл.энергии

Всем доброго времени суток!
Не совсем хорошо понимаю значение "Открытый проект умного дома" - должен ли я только опубликовать исходный код или есть ещё какие требования? Техподдержку оказывать по этому проекту не смогу ввиду того что являюсь программистом уровня "Хеллоу Ворлд!" и основная часть кода пишется по наитию. Но в любом случае - идею и основную часть кода позаимствовал у andr128 (за что ему огромное спасибо!) из темы http://cyber-place.ru/showthread.php?t=623, а раз этот проект является открытым - поэтому считаю нужным выложить и свой код.

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

Немного о железе:
Роутер: D-Link DIR-320 (hw B1) с прошивкой OpenWRT Attitude Adjustment 12.09
Контроллер: Ардуино Мега 2560
Датчики тока: ACS712-20A

Во время ремонта в квартире была заменена проводка и сейчас практически на каждую группу розеток идёт свой кабель что позволило установить все датчики в одном месте - в распред. щитке в квартире. В общем у меня 14 автоматов: 1 вводной и 13 - на группы потребителей. Поэтому были закуплены датчики тока ACS712-20A в кол-ве 15 штук (2-резерв). Выбор контроллера пал на Ардуино Мега 2560, т.к. у неё достаточное кол-во аналоговых входов и, тем более, она уже была в наличии.

Скетч для Меги:
Код:
#include <TimerOne.h> //использует Timer1
#include <SimpleModbusSlave.h>


//////////////////////// Modbus slave ///////////////////////
// Using the enum instruction allows for an easy method for adding and 
// removing registers. Doing it this way saves you #defining the size 
// of your slaves register array each time you want to add more registers
// and at a glimpse informs you of your slaves register layout.

//////////////// registers of your slave ///////////////////
enum 
{     
  // just add or remove registers and your good to go...
  // The first register starts at address 0
  mb_A0,     
  mb_A1,        
  mb_A2,        
  mb_A3,        
  mb_A4,        
  mb_A5,        
  mb_A6,        
  mb_A7,        
  mb_A8,        
  mb_A9,        
  mb_A10,        
  mb_A11,        
  mb_A12,        
  mb_A13,        
  mb_A14,        
  mb_A15,        
  HOLDING_REGS_SIZE // leave this one
  // total number of registers for function 3 and 16 share the same register array
  // i.e. the same address space
};

unsigned int holdingRegs[HOLDING_REGS_SIZE]; // function 3 and 16 register array
////////////////////////////////////////////////////////////


//================================================================
// http://habrahabr.ru/post/141442/
#define FASTADC 1

// defines for setting and clearing register bits
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif
//================================================================

const int k=100;
int cycles=15; // Кол-во обрабатываемых аналоговых входов
int ai_count=cycles, ai_mid_cnt=cycles; // Счетчики циклов 'for'

volatile long Counter=0; // счетчик периодов
volatile int CurA[16]; // Ток текущий
volatile int A[16]; // Аналоговый вход
volatile int CurMaxA[16]; // Ток маскимальный
volatile int CurMinA[16]; // Ток минимальный
volatile long CurMaxCalcA[16]; // Ток маскимальный за весь период
volatile long CurMinCalcA[16]; // Ток минимальный за весь период
float CurMidCalcA[16]; // Ток средний (среднеквадратичное от CurA0calcMax и CurA0calcMin)
           
volatile int c, v;

// ---------------------
float Current_A[16];

void setup()  
{
//  Serial.begin(57600);//поднимаем соединение для передачи на терминал   
    
////////////////////// Modbus slave ///////////////////////
  modbus_configure(&Serial, 57600, SERIAL_8N1, 1, 2, HOLDING_REGS_SIZE, holdingRegs);    
//////////////////////////////////////////////////////////

//================================================================
// http://habrahabr.ru/post/141442/
#if FASTADC
// set prescale to 16
sbi(ADCSRA,ADPS2) ;
cbi(ADCSRA,ADPS1) ;
cbi(ADCSRA,ADPS0) ;
#endif
//================================================================

    Timer1.initialize(400);              // Интервал срабатывания таймера в мкс 
    Timer1.attachInterrupt(current_meter);   //будет вызываться каждый раз при отсчете заданного времени
} 
//********************обработчики прерываний******************************* 
void current_meter()  //прерывания таймера 
{ 
  Counter++;  //счетчик периодов  

//  for (ai_count=0; ai_count < cycles; ai_count++)
//  {
//    CurA[ai_count] = analogRead(A[ai_count])-512;
//    CurMaxA[ai_count] = max(CurA[ai_count],CurMaxA[ai_count]);
//    CurMinA[ai_count] = min(CurA[ai_count],CurMinA[ai_count]);
//  }

    CurA[0] = analogRead(A0)-512;
    CurMaxA[0] = max(CurA[0],CurMaxA[0]);
    CurMinA[0] = min(CurA[0],CurMinA[0]);

    CurA[1] = analogRead(A1)-512;
    CurMaxA[1] = max(CurA[1],CurMaxA[1]);
    CurMinA[1] = min(CurA[1],CurMinA[1]);

    CurA[2] = analogRead(A2)-512;
    CurMaxA[2] = max(CurA[2],CurMaxA[2]);
    CurMinA[2] = min(CurA[2],CurMinA[2]);

    CurA[3] = analogRead(A3)-512;
    CurMaxA[3] = max(CurA[3],CurMaxA[3]);
    CurMinA[3] = min(CurA[3],CurMinA[3]);

    CurA[4] = analogRead(A4)-512;
    CurMaxA[4] = max(CurA[4],CurMaxA[4]);
    CurMinA[4] = min(CurA[4],CurMinA[4]);

    CurA[5] = analogRead(A5)-512;
    CurMaxA[5] = max(CurA[5],CurMaxA[5]);
    CurMinA[5] = min(CurA[5],CurMinA[5]);

    CurA[6] = analogRead(A6)-512;
    CurMaxA[6] = max(CurA[6],CurMaxA[6]);
    CurMinA[6] = min(CurA[6],CurMinA[6]);

    CurA[7] = analogRead(A7)-512;
    CurMaxA[7] = max(CurA[7],CurMaxA[7]);
    CurMinA[7] = min(CurA[7],CurMinA[7]);

    CurA[8] = analogRead(A8)-512;
    CurMaxA[8] = max(CurA[8],CurMaxA[8]);
    CurMinA[8] = min(CurA[8],CurMinA[8]);

    CurA[9] = analogRead(A9)-512;
    CurMaxA[9] = max(CurA[9],CurMaxA[9]);
    CurMinA[9] = min(CurA[9],CurMinA[9]);

    CurA[10] = analogRead(A10)-512;
    CurMaxA[10] = max(CurA[10],CurMaxA[10]);
    CurMinA[10] = min(CurA[10],CurMinA[10]);

    CurA[11] = analogRead(A11)-512;
    CurMaxA[11] = max(CurA[11],CurMaxA[11]);
    CurMinA[11] = min(CurA[11],CurMinA[11]);

    CurA[12] = analogRead(A12)-512;
    CurMaxA[12] = max(CurA[12],CurMaxA[12]);
    CurMinA[12] = min(CurA[12],CurMinA[12]);

    CurA[13] = analogRead(A13)-512;
    CurMaxA[13] = max(CurA[13],CurMaxA[13]);
    CurMinA[13] = min(CurA[13],CurMinA[13]);

//    CurA[14] = analogRead(A14)-512;
//    CurMaxA[14] = max(CurA[14],CurMaxA[14]);
//    CurMinA[14] = min(CurA[14],CurMinA[14]);


    if(Counter==k)
  { 
      Counter=0;  //обнуляем счетчик 

//    for (ai_count=0; ai_count < cycles; ai_count++)
//    {
//        CurMaxCalcA[ai_count]=CurMaxA[ai_count];
//        CurMinCalcA[ai_count]=CurMinA[ai_count];
//        CurMaxA[ai_count]=0;
//        CurMinA[ai_count]=0;
//    }

      CurMaxCalcA[0]=CurMaxA[0];
      CurMinCalcA[0]=CurMinA[0];
      CurMaxA[0]=0;
      CurMinA[0]=0;

      CurMaxCalcA[1]=CurMaxA[1];
      CurMinCalcA[1]=CurMinA[1];
      CurMaxA[1]=0;
      CurMinA[1]=0;

      CurMaxCalcA[2]=CurMaxA[2];
      CurMinCalcA[2]=CurMinA[2];
      CurMaxA[2]=0;
      CurMinA[2]=0;

      CurMaxCalcA[3]=CurMaxA[3];
      CurMinCalcA[3]=CurMinA[3];
      CurMaxA[3]=0;
      CurMinA[3]=0;

      CurMaxCalcA[4]=CurMaxA[4];
      CurMinCalcA[4]=CurMinA[4];
      CurMaxA[4]=0;
      CurMinA[4]=0;

      CurMaxCalcA[5]=CurMaxA[5];
      CurMinCalcA[5]=CurMinA[5];
      CurMaxA[5]=0;
      CurMinA[5]=0;

      CurMaxCalcA[6]=CurMaxA[6];
      CurMinCalcA[6]=CurMinA[6];
      CurMaxA[6]=0;
      CurMinA[6]=0;

      CurMaxCalcA[7]=CurMaxA[7];
      CurMinCalcA[7]=CurMinA[7];
      CurMaxA[7]=0;
      CurMinA[7]=0;

      CurMaxCalcA[8]=CurMaxA[8];
      CurMinCalcA[8]=CurMinA[8];
      CurMaxA[8]=0;
      CurMinA[8]=0;

      CurMaxCalcA[9]=CurMaxA[9];
      CurMinCalcA[9]=CurMinA[9];
      CurMaxA[9]=0;
      CurMinA[9]=0;

      CurMaxCalcA[10]=CurMaxA[10];
      CurMinCalcA[10]=CurMinA[10];
      CurMaxA[10]=0;
      CurMinA[10]=0;

      CurMaxCalcA[11]=CurMaxA[11];
      CurMinCalcA[11]=CurMinA[11];
      CurMaxA[11]=0;
      CurMinA[11]=0;

      CurMaxCalcA[12]=CurMaxA[12];
      CurMinCalcA[12]=CurMinA[12];
      CurMaxA[12]=0;
      CurMinA[12]=0;

      CurMaxCalcA[13]=CurMaxA[13];
      CurMinCalcA[13]=CurMinA[13];
      CurMaxA[13]=0;
      CurMinA[13]=0;

//      CurMaxCalcA[14]=CurMaxA[14];
//      CurMinCalcA[14]=CurMinA[14];
//      CurMaxA[14]=0;
//      CurMinA[14]=0;
  }
} 


//************************************************************************* 
void loop()  
{ 
//  for (ai_mid_cnt=0; ai_mid_cnt < cycles; ai_mid_cnt++)
//  {
//    CurMidCalcA[ai_mid_cnt] = sqrt((sq(CurMaxCalcA[ai_mid_cnt])+sq(CurMinCalcA[ai_mid_cnt]))/2);
    CurMidCalcA[0] = sqrt((sq(CurMaxCalcA[0])+sq(CurMinCalcA[0]))/2);
    CurMidCalcA[1] = sqrt((sq(CurMaxCalcA[1])+sq(CurMinCalcA[1]))/2);
    CurMidCalcA[2] = sqrt((sq(CurMaxCalcA[2])+sq(CurMinCalcA[2]))/2);
    CurMidCalcA[3] = sqrt((sq(CurMaxCalcA[3])+sq(CurMinCalcA[3]))/2);
    CurMidCalcA[4] = sqrt((sq(CurMaxCalcA[4])+sq(CurMinCalcA[4]))/2);
    CurMidCalcA[5] = sqrt((sq(CurMaxCalcA[5])+sq(CurMinCalcA[5]))/2);
    CurMidCalcA[6] = sqrt((sq(CurMaxCalcA[6])+sq(CurMinCalcA[6]))/2);
    CurMidCalcA[7] = sqrt((sq(CurMaxCalcA[7])+sq(CurMinCalcA[7]))/2);
    CurMidCalcA[8] = sqrt((sq(CurMaxCalcA[8])+sq(CurMinCalcA[8]))/2);
    CurMidCalcA[9] = sqrt((sq(CurMaxCalcA[9])+sq(CurMinCalcA[9]))/2);
    CurMidCalcA[10] = sqrt((sq(CurMaxCalcA[10])+sq(CurMinCalcA[10]))/2);
    CurMidCalcA[11] = sqrt((sq(CurMaxCalcA[11])+sq(CurMinCalcA[11]))/2);
    CurMidCalcA[12] = sqrt((sq(CurMaxCalcA[12])+sq(CurMinCalcA[12]))/2);
    CurMidCalcA[13] = sqrt((sq(CurMaxCalcA[13])+sq(CurMinCalcA[13]))/2);
//    CurMidCalcA[14] = sqrt((sq(CurMaxCalcA[14])+sq(CurMinCalcA[14]))/2);
  /*
  Измеряем максимальный и минимальный пики синусоиды. Затем находим их среднеквадратичное значение.
                  пик = sqrt( ( (пик мин)^2 + (пик макс)^2 )/2 )  [маш.ед.]
  Далее это значение (в машинных единицах - 0-1023) переводим в напряжение:
                  напряж. = (пик / 1023)*5  [Вольт]
  Чувствительность ACS712-20 равна 100 милиВольт/Ампер = 0,1В/А (см. даташит на ACS), отсюда:
                  ток ампл. = напряж. / Чувствит. = напряж / 0,1 = напряж * 10  [Ампер]
  Ток средний - это ток ампл., поделенный на корень из 2:
                  ток средний = ток ампл. / sqrt(2) = ток ампл. * 0,707  [Ампер]
  Итого:
                  Ток = 0,707*ток ампл. = 0,707 * напряж / чувствит. = 0,707 * напряж. * 10 = 7,07 * напряж. =
                      = 7,07 * пик * 5 / 1023 = 35,35 * пик / 1023
  */  
//  Current_A[ai_mid_cnt] = 35.35 * CurMidCalcA[ai_mid_cnt] / 1023.0;
  Current_A[0] = 35.35 * CurMidCalcA[0] / 1023.0;
  Current_A[1] = 35.35 * CurMidCalcA[1] / 1023.0;
  Current_A[2] = 35.35 * CurMidCalcA[2] / 1023.0;
  Current_A[3] = 35.35 * CurMidCalcA[3] / 1023.0;
  Current_A[4] = 35.35 * CurMidCalcA[4] / 1023.0;
  Current_A[5] = 35.35 * CurMidCalcA[5] / 1023.0;
  Current_A[6] = 35.35 * CurMidCalcA[6] / 1023.0;
  Current_A[7] = 35.35 * CurMidCalcA[7] / 1023.0;
  Current_A[8] = 35.35 * CurMidCalcA[8] / 1023.0;
  Current_A[9] = 35.35 * CurMidCalcA[9] / 1023.0;
  Current_A[10] = 35.35 * CurMidCalcA[10] / 1023.0;
  Current_A[11] = 35.35 * CurMidCalcA[11] / 1023.0;
  Current_A[12] = 35.35 * CurMidCalcA[12] / 1023.0;
  Current_A[13] = 35.35 * CurMidCalcA[13] / 1023.0;
//  Current_A[14] = 35.35 * CurMidCalcA[14] / 1023.0;

//  }

//////////////////////// Modbus slave ///////////////////////

  holdingRegs[mb_A0] = int(Current_A[1]*1000); // update data to be read by the master
  holdingRegs[mb_A1] = int(Current_A[2]*1000); // update data to be read by the master
  holdingRegs[mb_A2] = int(Current_A[3]*1000); // update data to be read by the master
  holdingRegs[mb_A3] = int(Current_A[4]*1000); // update data to be read by the master
  holdingRegs[mb_A4] = int(Current_A[5]*1000); // update data to be read by the master
  holdingRegs[mb_A5] = int(Current_A[6]*1000); // update data to be read by the master
  holdingRegs[mb_A6] = int(Current_A[7]*1000); // update data to be read by the master
  holdingRegs[mb_A7] = int(Current_A[8]*1000); // update data to be read by the master
  holdingRegs[mb_A8] = int(Current_A[9]*1000); // update data to be read by the master
  holdingRegs[mb_A9] = int(Current_A[10]*1000); // update data to be read by the master
  holdingRegs[mb_A10] = int(Current_A[11]*1000); // update data to be read by the master
  holdingRegs[mb_A11] = int(Current_A[12]*1000); // update data to be read by the master
  holdingRegs[mb_A12] = int(Current_A[13]*1000); // update data to be read by the master
//  holdingRegs[mb_A13] = int(Current_A[13]*1000); // update data to be read by the master
//  holdingRegs[mb_A14] = int(Current_A[14]*1000); // update data to be read by the master
  holdingRegs[mb_A15] = int(analogRead(A15)*1000.0*5.0/1023.0); // update data to be read by the master
//  holdingRegs[mb_A15] = analogRead(A15); // update data to be read by the master

  modbus_update();
////////////////////////////////////////////////////////////

}
Изначально вся программа была сделана на циклах, но в процессе отладки убрал временно циклы - и пока так и осталось

В ходе пусконаладки Меги столкнулся с недостатком аппаратных ресурсов - прерывания нужно вызывать достаточно часто и обработка 13-ти аналоговых входов также занимает время. Поэтому пришлось увеличить частоту работы АЦП понизив делитель.
Миниатюры
Нажмите на изображение для увеличения
Название: 111.png
Просмотров: 1213
Размер:	98.8 Кб
ID:	2286   Нажмите на изображение для увеличения
Название: 222.png
Просмотров: 843
Размер:	34.7 Кб
ID:	2287   Нажмите на изображение для увеличения
Название: 333.png
Просмотров: 752
Размер:	146.5 Кб
ID:	2288  
Вложения
Тип файла: rar mbrrd.rar (13.5 Кб, 604 просмотров)
Kopylov вне форума   Ответить с цитированием
 


Здесь присутствуют: 1 (пользователей: 0 , гостей: 1)
 

Ваши права в разделе
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.

Быстрый переход


Текущее время: 20:21. Часовой пояс GMT +3.


Powered by vBulletin® Version 3.8.5
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd. Перевод: zCarot
Яндекс.Метрика