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

Вернуться   Форум обсуждения систем "Умный дом", проектов Ардуино, 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 вне форума   Ответить с цитированием
Старый 24.02.2015, 20:19   #2
Kopylov
Junior Member
 
Регистрация: 12.02.2015
Сообщений: 18
Вес репутации: 0
Kopylov is an unknown quantity at this point
По умолчанию Re: OpenWRT + modbus + arduino = учёт эл.энергии

Много буков получилось
Цитата:
При отправке были допущены следующие ошибки:

Вы ввели слишком длинный текст (63567 символов). Пожалуйста, сократите его до 35000 символов.
Далее:
Для связи между роутером и Мегой выбрал протокол Модбас-RTU, т.к. уже приходилось немного сталкиваться с ним и ещё прийдется, поэтому опыт использования модбаса пригодится в жизни и в работе.
Физический канал связи - UART.

Для поутера пришлось придумывать свою программулину, т.к. предложенная andr128 отправляла данные по запросу веб-страницы, а мне такой способ не понравился. Мне хотелось чтобы роутер постоянно собирал данные, складывал их в кольцевую базу данных, а при необходимости - можно было бы зайти на веб-морду и посмотреть статистику. В качестве кольцевой базы данных выбрал RRDtool. Она ко всему прочему ещё и графики сама строит - что было для меня большой находкой. (изначально планировал связку modbus + sqlite, но после того как нашел rrdtool - отказался от sqlite. Также пробовал поставить и полный mysql с вордпрессом на роутере. Поставил, запустил - поржал, глядя как стартовая страница минут 10 грузится и снёс это всё )

В самописной программульке использовал библиотеки libmodbus и librrd. Пришлось освоить кросс-компиляцию

Начальные требования были такие (планировал прогу назвать mbsql):
Цитата:
mbsql - это modbus-клиент, запускаемый на роутере, собирающий информацию с модбас-серверов (Ардуино) и записывающий эту информацию в базу данных sqlite.

Для реализации этой функции необходимо:
1. полная совместимость с протоколом модбас а для этого:
- реализовать все виды чтения/записи регистров, дискретных входов/выходов и последовательность регистров
- обеспечить настройку скорости приёма/передачи данных
2. чтение/запись данных в базу sqlite
3. Универсальность программы:
- реализовать передачу аргументов программе
- возможность как циклической, так и единичной обработки данных
Хотелось универсальности и чтобы можно было создать какой-нибудь INI-файл с параметрами, скормить его проге и она его обрабатывала, но потом понял что моих способностей программирования недостаточно (либо потребуется очень много времени), поэтому от INI-файлов отказался.

В результате получилось что-то как-то работающее, с названием mbrrd:
Код:
//gcc mbrrd.c -o mbrrd `pkg-config --libs --cflags libmodbus librrd`

/*
 *     Gateway-server Modbus-RTU to RRD for Arduino
 *
 *             Copyright (C) 2015 Kopylov Sergey 
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 */


#include <string.h>
#include <stdlib.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>

#include <unistd.h>    /* UNIX standard function definitions */
#include <fcntl.h>     /* File control definitions */
#include <errno.h>     /* Error number definitions */
#include <termios.h>   /* POSIX terminal control definitions */

#include <modbus.h>   // https://github.com/stephane/libmodbus
                      // https://github.com/stephane/libmodbus/archive/master.zip


//-------------------- Взято из теста libmodbus - unit-test-client.c и unit-test.h
const uint8_t UT_BITS_TAB[] = { 0xCD, 0x6B, 0xB2, 0x0E, 0x1B };
const uint16_t UT_REGISTERS_TAB[] = { 0x022B, 0x0001, 0x0064 };
uint8_t *tab_rp_bits;
uint8_t value;
uint16_t *tab_rp_registers;
//---------------------

int IDX  = 1;					// id modbus slave
int RW = 0;						// режим чтения/записи регистров
int ADDR = 0;					// нач. адрес табл. регистров
int cycle = 1;					// кол-во итераций опроса регистров. на каждой итерации адрес регистра увелич. на 1
int VAL = 0;					// значение регистра
int NB = 1;						

char DEV [80] = "/dev/ttyS1";  // UART seial порт
int  BND = 57600;                // скорость serial порта
int  T_ANSWER = 100;		// таймаут ожидания ответа
int  T_BYTE = 1;		// таймаут ожидания следующего байта
int  DEB = 0;                    // флаг отладки
char SQL [80] = "/home/base/arduino.db";  // база данных sqlite
char RRD [80] = "/home/base/rrd_currents.rrd";
char rrd_req [200] = "";                        // RRD-запрос
char INI [80] = "/home/mbsql.ini";  // INI-файл с конфигурацией mbsql
const char * sql_req = "";			// SQL-запрос
int nb_points;					// Allocate and initialize the memory to store the bits and the registers
int rc;
int ret;

// Входы/выходы ардуино:
float A[15], D[53];


//==============================================
// device -"/dev/ttyS1"
// baud - 57600
// tr - 500 ms
// t -  500 ms
//==============================================



// создаем контекст устройства модбас:
modbus_t *ctx;              // объект модбас



int ModbusInit(const char *device, int baud, int tr, int t) 
{
  int rc;
  struct timeval response_timeout;
  struct timeval byte_timeout;

	ctx = modbus_new_rtu(device, baud, 'N', 8, 1);
	if (ctx == NULL) { fprintf(stderr, "Unable to create the modbus context\n"); return -1; }
	else {
		if (DEB==1) {printf("Modbus context is created - OK!.\n");}
	}	

	// Взято из тестовой программы libmodbus - unit-test-client.c
	modbus_set_debug(ctx, TRUE);
	modbus_set_error_recovery(ctx, 
										MODBUS_ERROR_RECOVERY_LINK |
                              MODBUS_ERROR_RECOVERY_PROTOCOL);

//	modbus_set_slave(ctx, IDX);

	/* Allocate and initialize the memory to store the bits */
	// Тернарная условная операция - O1 ? O2 : O3 ==> if O1 then O2 else O3
	nb_points = NB;
	tab_rp_bits = (uint8_t *) malloc(nb_points * sizeof(uint8_t));
	memset(tab_rp_bits, 0, nb_points * sizeof(uint8_t));

	/* Allocate and initialize the memory to store the registers */
	nb_points = NB;
	tab_rp_registers = (uint16_t *) malloc(nb_points * sizeof(uint16_t));
	memset(tab_rp_registers, 0, nb_points * sizeof(uint16_t));

  // инициализация тайм-аута и вр. ожидания
/*
  функция устанавливает временной интервал для ожидания ответа. 
  Если ожидание до получения ответа больше, чем заданный таймаут,
  будет сгенерирована ошибка.
*/

  response_timeout.tv_sec  = 0;
  response_timeout.tv_usec = tr * 1000;
  modbus_set_response_timeout(ctx, &response_timeout);

/*
  Modbus_set_byte_timeout() функция устанавливает временной 
  интервал между двумя последовательными байтами в том же сообщении. 
  Если задержка между ними длиннее данного тайм-аута, 
  будет сгенерирована ошибка.

  Если по истечении заданного tv_sec из -1,
  то это время ожидания не будет использоваться вообще. 
  Это приводит к modbus_set_response_timeout регулирующие 
  весь период тайм-аут операции.
*/

  byte_timeout.tv_sec  = 0;
  byte_timeout.tv_usec = t * 1000;
  modbus_set_byte_timeout(ctx, &byte_timeout);

  //Modbus Connect
  rc = modbus_connect(ctx);
  if (rc == -1 ) 
    { fprintf(stderr, "Connection failed: %s\n", modbus_strerror(errno));
      modbus_free(ctx); } 
  return rc;
}



//=============================================
ModbusClose() 
{
  if (ctx != NULL) { modbus_close(ctx); modbus_free(ctx); }
}


/*
 "id"   - ID slave
 "addr" - нач. адрес табл. регистров
 "regs" - кол-во используемых регистров
 "rw"   - 0 - read, 1-write/read
 "r"    - значение регистра для записи
*/

//=======================
//      wait (ms) 	
//=======================
WaitMs(int t)
{
struct timeval timeout;

timeout.tv_sec =  0;
timeout.tv_usec = t * 1000;

// используем как таймер
select (0, NULL, NULL, NULL, &timeout); //ждать

}


//=======================================
//     разбор строки параметров
//=======================================
int param(int argc, char * argv[]) {
int b;
   if (argc > 1) {
		for (b = 1 ; b < argc; b++) {
			if( !strcmp(argv[b], "-d") && argv[b+1] != NULL)	{ strcpy(DEV, argv[b+1]);			}
			if( !strcmp(argv[b], "-b") && argv[b+1] != NULL)	{ BND = atoi(argv[b+1]);			}
                        if( !strcmp(argv[b], "-ta") && argv[b+1] != NULL)        { T_ANSWER = atoi(argv[b+1]);                  }
                        if( !strcmp(argv[b], "-tb") && argv[b+1] != NULL)        { T_BYTE = atoi(argv[b+1]);                    }
  			if( !strcmp(argv[b], "-id") && argv[b+1] != NULL)	{ IDX = atoi(argv[b+1]);			}
			if( !strcmp(argv[b], "-a") && argv[b+1] != NULL)	{ ADDR = atoi(argv[b+1]);			}
			if( !strcmp(argv[b], "-n") && argv[b+1] != NULL)	{ NB = atoi(argv[b+1]);				}
			if( !strcmp(argv[b], "-v") && argv[b+1] != NULL)	{ VAL = atoi(argv[b+1]);			}
			if( !strcmp(argv[b], "-rw") && argv[b+1] != NULL)	{ RW = atoi(argv[b+1]);				}
			if( !strcmp(argv[b], "-c") && argv[b+1] != NULL)	{ cycle = atoi(argv[b+1]);			}
			if( !strcmp(argv[b], "-sql") && argv[b+1] != NULL)	{ strcpy(SQL, argv[b+1]);			}
                        if( !strcmp(argv[b], "-rrd") && argv[b+1] != NULL)      { strcpy(RRD, argv[b+1]);                       }
			if( !strcmp(argv[b], "-ini") && argv[b+1] != NULL)	{ strcpy(INI, argv[b+1]);			}
			if( !strcmp(argv[b], "-deb")) { DEB = 1; }
			if( !strcmp(argv[b], "-h") || !strcmp(argv[b], "-?")) {  
				printf("Usage: mbsql [-p] [-d] [-b] [-id] [-a] [-rw] [-sql] [-ini] [-deb] [-h or -?]\n");
				printf("Example: mbsql -p 3425 -d /dev/ttyATH0 -b 9600 -deb\n");
				printf("       -d		serial device [/dev/ttyS1]\n");
				printf("       -b		baud rate [57600]\n");
                                printf("       -ta              frame time out [100 ms]\n");
                                printf("       -tb              inter character time out [1 ms]\n");
				printf("       -id		modbus slave address [1]\n");
				printf("       -a		register start address [0]\n");
				printf("       -n		quantity of the registers/coils [1]\n");
				printf("       -v		value of the registers (decimal) [0]\n");
				printf("       -rw		register read/write function mode (in decimal) [0]\n");
				printf("       			01 Read Coil Status\n");
				printf("       			02 Read Input Status\n");
				printf("       			03 Read Holding Registers\n");
				printf("       			04 Read Input Registers\n");
				printf("       			05 Force Single Coil\n");
				printf("       			06 Preset Single Register\n");
//				printf(" 	      		07 Read Exception Status\n");
//				printf("    	   		11 Fetch Comm Event Ctr\n");
//				printf("       			12 Fetch Comm Event Log\n");
				printf("       			15 Force Multiple Coils\n");
				printf("       			16 Preset Multiple Regs\n");
//				printf("       			17 Report Slave ID\n");
//				printf("    	   		20 Read General Reference\n");
//				printf("       			21 Write General Reference\n");
//				printf("       			22 Mask Write 4X Register\n");
//				printf("       			23 Read/Write 4X Registers\n");
//				printf("       			24 Read FIFO Queue\n");
				printf("       -c		cycles (0=unlimited) [1]\n");
				printf("       -sql		sqlite base path [/home/base/smart.db]\n");
                                printf("       -rrd             RRD base path [/home/base/rrd_currents.rrd]\n");
				printf("       -ini		configuration INI-file [/home/mbsql.ini]\n");
				printf("       -deb		debug [0]\n");
				printf("       -h or -?		this page\n");
				exit(0);
      	}
		}
	}
}

////////////////////////////////////////////////////////////
//                                                        //
//                  M A I N    P R O G R A M M            //
//                                                        //
////////////////////////////////////////////////////////////
int main(int argc, char *argv[])
{
	printf("\n=======================================================\n");
	printf("     Gatawey-server Modbus RTU to sqlite for Arduino\n");
	printf("          Copyright (C) 2015 by Kopylov Sergey\n");
	printf("=======================================================\n");
	printf("  Usage argument: -h or -? for help \n\n");
	param(argc, argv );

//	sqlite3 *db;
//	sqlite3_stmt *stmt;
	char *zErrMsg = 0;
	int rc_sql = 0;
	int col, cols;

	if ( ModbusInit(DEV, BND, T_ANSWER, T_BYTE) == -1 ) {
		if (DEB==1) {printf("ModbusInit = -1.\n");}
		exit(-1);
	}

	int i = 0;
	int k = 0;

// ------------------------ Взято из SimpleModbusMaster.cpp:	
	// Modbus states that a baud rate higher than 19200 must use a fixed 750 us 
	// for inter character time out and 1.75 ms for a frame delay for baud rates
	// below 19200 the timing is more critical and has to be calculated.
	// E.g. 9600 baud in a 11 bit packet is 9600/11 = 872 characters per second
	// In milliseconds this will be 872 characters per 1000ms. So for 1 character
	// 1000ms/872 characters is 1.14583ms per character and finally modbus states
	// an inter-character must be 1.5T or 1.5 times longer than a character. Thus
	// 1.5T = 1.14583ms * 1.5 = 1.71875ms. A frame delay is 3.5T.
	// Thus the formula is T1.5(us) = (1000ms * 1000(us) * 1.5 * 11bits)/baud
	// 1000ms * 1000(us) * 1.5 * 11bits = 16500000 can be calculated as a constant
	
	//	if (baud > 19200)
	//		T1_5 = 750; 
	//	else 
	//		T1_5 = 16500000/baud; // 1T * 1.5 = T1.5
// ------------------------


// ------------------------ Взято из Modicon Modbus Protocol Reference Guide (PI–MBUS–300 Rev. J):	
	//#01 Read Coil Status
	//#02 Read Input Status
	//#03 Read Holding Registers
	//#04 Read Input Registers
	//#05 Force Single Coil
	//#06 Preset Single Register
	// 07 Read Exception Status
	// 11 (0B Hex) Fetch Comm Event Ctr
	// 12 (0C Hex) Fetch Comm Event Log
	//#15 (0F Hex) Force Multiple Coils
	//#16 (10 Hex) Preset Multiple Regs
	// 17 (11 Hex) Report Slave ID
	// 20 (14Hex) Read General Reference
	// 21 (15Hex) Write General Reference
	// 22 (16Hex) Mask Write 4X Register
	// 23 (17Hex) Read/Write 4X Registers
	// 24 (18Hex) Read FIFO Queue


// -------------------------------------------------------
// -                 01 Read Coil Status                 -
// -------------------------------------------------------

	if ( RW == 1 ) {
		
		if (DEB==1) {
			printf("01 Read Coil Status: device = %s, baud rate = %d,\n", DEV, BND);
			printf("slave address = %d, RW = %d, Coil start address = %d.\n", IDX, RW, ADDR);
		}

		modbus_flush(ctx); // очистить буфер 
		modbus_set_slave(ctx, IDX);
		while ((i < cycle) || (cycle == 0)) {
			if (DEB==1) { printf("\ncycle №: %d\n", i); }

			if (NB == 1) {
				rc = modbus_read_bits(ctx, ADDR+i, 1, tab_rp_bits);

				// Если прочитать статус не удалось - выход из программы:
				if (rc != 1) {
					printf("Single Coile status reading is FAILED (nb points %d)\n", rc);
					goto close;
				}
	
				printf("Single Coil address '%d' status: ", ADDR+i);
				if (tab_rp_bits[0] == 1) {
					printf("ON\n\n", rc);
				} else {
					printf("OFF\n\n", rc);
				}
			}



			if (NB > 1) {
				rc = modbus_read_bits(ctx, ADDR+i, NB, tab_rp_bits);

				// Если прочитать статус не удалось - выход из программы:
				if (rc != NB) {
					printf("Multiple Coiles status reading is FAILED (nb points %d)\n", rc);
					goto close;
				}

				k = 0;
				nb_points = NB;
				while (nb_points > 0) {
					// Тернарная условная операция - O1 ? O2 : O3 ==> if O1 then O2 else O3
					int nb_bits = (nb_points > 8) ? 8 : nb_points;

					value = modbus_get_byte_from_bits(tab_rp_bits, k*8, nb_bits);
					printf("Multiple Coiles start address [%d] status: Byte [%d] has value: %02X\n", ADDR+i*NB, k, value);

					nb_points -= nb_bits;
					k++;
				}
			}
			if (cycle != 0) {	i++; }
		}
		goto close;
	}
//===============================================================


// -------------------------------------------------------
// -                 02 Read Input Status                -
// -------------------------------------------------------

	if ( RW == 2 ) {
		
		if (DEB==1) {
			printf("02 Read Input Status: device = %s, baud rate = %d,\n", DEV, BND);
			printf("slave address = %d, RW = %d, Input start address = %d.\n", IDX, RW, ADDR);
		}

		modbus_flush(ctx); // очистить буфер 
		modbus_set_slave(ctx, IDX);
		while ((i < cycle) || (cycle == 0)) {
			if (DEB==1) { printf("\ncycle №: %d\n", i); }

			if (NB > 1) {
				rc = modbus_read_input_bits(ctx, ADDR+i, NB, tab_rp_bits);

				// Если прочитать статус не удалось - выход из программы:
				if (rc != NB) {
					printf("Input status reading is FAILED (nb points %d)\n", rc);
					goto close;
				}

				k = 0;
				nb_points = NB;
				while (nb_points > 0) {
					// Тернарная условная операция - O1 ? O2 : O3 ==> if O1 then O2 else O3
					int nb_bits = (nb_points > 8) ? 8 : nb_points;

					value = modbus_get_byte_from_bits(tab_rp_bits, k*8, nb_bits);
					printf("Input address [%d] status: Byte [%d] has value: %02X\n", ADDR+i, k, value);

					nb_points -= nb_bits;
					k++;
				}
			}
			if (cycle != 0) {	i++; }
		}
		goto close;
	}
//===============================================================


// -------------------------------------------------------
// -              03 Read Holding Registers              -
// -------------------------------------------------------

	if ( RW == 3 ) {
		
		if (DEB==1) {
			printf("03 Read Holding Registers: device = %s, baud rate = %d,\n", DEV, BND);
			printf("slave address = %d, RW = %d, Register start address = %d.\n", IDX, RW, ADDR);
		}

		modbus_flush(ctx); // очистить буфер 
		modbus_set_slave(ctx, IDX);
		while ((i < cycle) || (cycle == 0)) {
			if (DEB==1) { printf("\ncycle №: %d\n", i); }

			rc = modbus_read_registers(ctx, ADDR+i*NB, NB, tab_rp_registers);

			// Если прочитать статус не удалось - выход из программы:
			if (rc != NB) {
				printf("Holding Registers reading is FAILED (nb points %d)\n", rc);
				goto close;
			}

			for (k=0; k < NB; k++) {
				printf("Holding Register address [%d] value: %d dec (0x%02X)\n",
							ADDR+i*NB+k, tab_rp_registers[k], tab_rp_registers[k]);
			}
			if (cycle != 0) {	i++; }
		}
		goto close;
	}
//===============================================================


// -------------------------------------------------------
// -               04 Read Input Registers               -
// -------------------------------------------------------

	if ( RW == 4 ) {
		
		if (DEB==1) {
			printf("04 Read Input Registers: device = %s, baud rate = %d,\n", DEV, BND);
			printf("slave address = %d, RW = %d, Register start address = %d.\n", IDX, RW, ADDR);
		}

		modbus_flush(ctx); // очистить буфер 
		modbus_set_slave(ctx, IDX);
		while ((i < cycle) || (cycle == 0)) {
			if (DEB==1) { printf("\ncycle №: %d\n", i); }

			rc = modbus_read_input_registers(ctx, ADDR+i*NB, NB, tab_rp_registers);

			// Если прочитать статус не удалось - выход из программы:
			if (rc != NB) {
				printf("Input Registers reading is FAILED (nb points %d)\n", rc);
				goto close;
			}

			for (k=0; k < NB; k++) {
				printf("Input Registers address [%d] value: %d dec (0x%02X)\n",
							ADDR+i*NB+k, tab_rp_registers[k], tab_rp_registers[k]);
			}


			if (cycle != 0) {	i++; }
		}
		goto close;
	}
//===============================================================


// -------------------------------------------------------
// -               05 Force Single Coil                  -
// -------------------------------------------------------
	if ( RW == 5 ) {
		
		// Если введённое значение регистра не равно 0 или 1 - выход из программы
		if ( (VAL != 0) && (VAL != 1) ) {
			printf("\nArgument [-v] can be '0' or '1' only!\n");
			goto close;
		}

		if (DEB==1) {
			printf("05 Force Single Coil: device = %s, baud rate = %d,\n", DEV, BND);
			printf("slave address = %d,RW = %d, Coil start address = %d.\n", IDX, RW, ADDR);
		}

		modbus_flush(ctx); // очистить буфер 
		modbus_set_slave(ctx, IDX);
		while ((i < cycle) || (cycle == 0)) {
			if (DEB==1) { printf("\ncycle №: %d\n", i); }

			rc = modbus_write_bit(ctx, ADDR+i, VAL);

			// Если записать статус не удалось - выход из программы:
			if (rc != 1) {
				printf("\nSingle Coil Forcing is FAILED (nb points %d)\n", rc);
				goto close;
			}

			printf("Single Coil address '%d' written: ", ADDR+i);
			if (VAL == 1) {
				printf("ON\n\n");
			} else {
				printf("OFF\n\n");
			}
			if (cycle != 0) {	i++; }
		}
		goto close;
	}
//===============================================================



// -------------------------------------------------------
// -            06 Preset Single Register                -
// -------------------------------------------------------
	if ( RW == 6 ) {
		
		// Если введённое значение регистра не равно 0 или 1 - выход из программы
		if (VAL > 65535) {
			printf("\nArgument [-v] for Single Register Presetting can be up to '0xFFFF' (65535 dec)!\n");
			goto close;
		}

		if (DEB==1) {
			printf("06 Preset Single Register: device = %s, baud rate = %d,\n", DEV, BND);
			printf("slave address = %d,RW = %d, register start address = %d.\n", IDX, RW, ADDR);
		}

		modbus_flush(ctx); // очистить буфер 
		modbus_set_slave(ctx, IDX);
		while ((i < cycle) || (cycle == 0)) {
			if (DEB==1) { printf("\ncycle №: %d\n", i); }

			rc = modbus_write_register(ctx, ADDR+i, VAL);

			// Если записать статус не удалось - выход из программы:
			if (rc != 1) {
				printf("\nSingle Register Presetting is FAILED (nb points %d)\n", rc);
				goto close;
			}

			printf("Single Register address [%d] presetted: %d (0x%02X)\n", ADDR+i, VAL, VAL);
			if (cycle != 0) {	i++; }
		}
		goto close;
	}
//===============================================================



// -------------------------------------------------------
// -              15 Force Multiple Coils                -
// -------------------------------------------------------
	if ( RW == 15 ) {
		
		// Если введённое значение регистра не равно 0 или 1 - выход из программы
//		if ( (VAL != 0) && (VAL != 1) ) { goto close; }

		if (DEB==1) {
			printf("15 Force Multiple Coils: device = %s, baud rate = %d,\n", DEV, BND);
			printf("slave address = %d,RW = %d, Coils start address = %d.\n", IDX, RW, ADDR);
		}

		modbus_flush(ctx); // очистить буфер 
		modbus_set_slave(ctx, IDX);
		while ((i < cycle) || (cycle == 0)) {
			if (DEB==1) { printf("\ncycle №: %d\n", i); }

			uint8_t tab_value[NB];

			modbus_set_bits_from_bytes(tab_value, 0, NB, UT_BITS_TAB);
			rc = modbus_write_bits(ctx, ADDR+i,
										NB, tab_value);

			// Если записать статус не удалось - выход из программы:
			if (rc != NB) {
				printf("\nMultiple Coils Forcing is FAILED (nb points %d)\n", rc);
				goto close;
			}

			printf("Multiple Coils with start address '%d' are written: OK\n\n", ADDR+i);
			if (cycle != 0) {	i++; }
		}
		goto close;
	}
//===============================================================



// -------------------------------------------------------
// -            16 Preset Multiple Registers             -
// -------------------------------------------------------
	if ( RW == 16 ) {
		
		if (DEB==1) {
			printf("06 Preset Multiple Registers: device = %s, baud rate = %d,\n", DEV, BND);
			printf("slave address = %d,RW = %d, registers start address = %d.\n", IDX, RW, ADDR);
		}

		modbus_flush(ctx); // очистить буфер 
		modbus_set_slave(ctx, IDX);
		while ((i < cycle) || (cycle == 0)) {
			if (DEB==1) { printf("\ncycle №: %d\n", i); }

			rc = modbus_write_registers(ctx, ADDR+i, NB, UT_REGISTERS_TAB);

			// Если записать статус не удалось - выход из программы:
			if (rc != NB) {
				printf("\nMultiple Registers Presetting is FAILED (nb points %d)\n", rc);
				goto close;
			}

			for (k=0; k < NB; k++) {
				printf("Register address [%d] presetted: %d (0x%02X)\n",
							ADDR+i*NB+k, UT_REGISTERS_TAB[k], UT_REGISTERS_TAB[k]);
			}
			if (cycle != 0) {	i++; }
		}
		goto close;
	}
//===============================================================


//---------------------------------------------------
//|               Обработка RRD-запросов            |
//---------------------------------------------------

	if (( RRD != NULL ) && ( RW != 99 )) {

                if (DEB==1) {printf("RRD request started");}

		char *updateparams[] = {
					"rrdupdate",
					"/home/base/rrd_currents.rrd",
					"N:0.1:1.1:2.2:3:4:5:6:7:8:9:10:11:12:13:14:15",
					NULL
		};

//		for (i=0; i < 17; i++)
//		{
//			Bytecounter[i]=flow1.bytes;
//			rrd_update(3,updateparams);
//		}

		rrd_update(3,updateparams);

                goto close;

	}

//------------------------------------------------------------------------



// -------------------------------------------------------
// -                    99 My Program                    -
// -------------------------------------------------------

        if ( RW == 99 ) {
                if (DEB==1) {
			printf("My Program started\n");
                        printf("03 Read Holding Registers: device = %s, baud rate = %d,\n", DEV, BND);
                        printf("slave address = %d, RW = %d, Register start address = %d.\n", IDX, RW, ADDR);
                }
		int mb_err_nb = 0, rrd_err_nb=0;
		
again:

// -------------------------------------------------------
// -              03 Read Holding Registers              -
// -------------------------------------------------------

                modbus_flush(ctx); // очистить буфер 
                modbus_set_slave(ctx, IDX);

                        rc = modbus_read_registers(ctx, ADDR, NB, tab_rp_registers);

                        // Если прочитать статус не удалось - выход из программы:
                        if (rc != NB) {
				mb_err_nb++;
				if (mb_err_nb >= 32768) mb_err_nb=0;
                                printf("Holding Registers reading is FAILED (%d errors)\n", mb_err_nb);
                                WaitMs(2000);
                                goto again;
                        }

                        for (k=0; k < NB; k++) {
                                if (DEB==1) {
	                                printf("Holding Register address [%d] value: %d dec (0x%02X)\n",
                                                        ADDR+k, tab_rp_registers[k], tab_rp_registers[k]);
				}
				A[k] = tab_rp_registers[k]/1000.0;
                        }

//---------------------------------------------------
//|               Обработка RRD-запросов            |
//---------------------------------------------------


		// TODO: Сделать цикл на основании кол-ва переменных, указанных в аргументе NB
                sprintf(rrd_req,
                                        "N:%f:%f:%f:%f:%f:%f:%f:%f:%f:%f:%f:%f:%f:%f:%f:%f",
                                        A[0], A[1], A[2], A[3], A[4], A[5], A[6], A[7], A[8], A[9], A[10], A[11], A[12], A[13], A[14], A[15]);

                if (DEB==1) { printf("\nRRD-request: %s %s\n", RRD, rrd_req); }




                char *updateparams[] = {
                                        "rrdupdate",
                                        "/home/base/rrd_currents.rrd",
//                                        "N:0.1:1.1:2.2:3:4:5:6:7:8:9:10:11:12:13:14:15",
					rrd_req,
                                        NULL
                };

//              for (i=0; i < 17; i++)
//              {
//                      Bytecounter[i]=flow1.bytes;
//                      rrd_update(3,updateparams);
//              }

		rrd_clear_error();  
                ret = rrd_update(3,updateparams);
		if( ret == -1 )
		{
                        rrd_err_nb++;
                        if (rrd_err_nb >= 32768) rrd_err_nb=0;
			printf ("rrd_update %d errors. Current error: %s\n", rrd_err_nb, rrd_get_error());
			rrd_clear_error();
		}
		WaitMs(1000);
		goto again;
        }

//------------------------------------------------------------------------

close:
	/* Free the memory */
	free(tab_rp_bits);
	free(tab_rp_registers);

	/* Close the connection */
	modbus_close(ctx);
	modbus_free(ctx);

    
	return 0;
}
В итоге реализовал функции:
01 Read Coil Status
02 Read Input Status
03 Read Holding Registers
04 Read Input Registers
05 Force Single Coil
06 Preset Single Register
15 (0F Hex) Force Multiple Coils
16 (10 Hex) Preset Multiple Regs
потестил их, но основная рабочая часть вызывается по аргументу -rw 99 и уже она опрашивает ардуину по модбасу и кладёт значения в базу RRD.

Исходник и IPK (для архитектуры роутера DIR-320) - во вложении (mbrrd.rar).
Вложение 2285
Kopylov вне форума   Ответить с цитированием
Старый 24.02.2015, 20:21   #3
Kopylov
Junior Member
 
Регистрация: 12.02.2015
Сообщений: 18
Вес репутации: 0
Kopylov is an unknown quantity at this point
По умолчанию Re: OpenWRT + modbus + arduino = учёт эл.энергии

Для создания графиков мне не удалось подружить rrdtool graph с php, поэтому написал скрипт, который выполняет cron каждую минуту и обновляет все тренды. Как показала практика - здесь я опять столкнулся с нехваткой аппаратных ресурсов, на этот раз - роутера, и выполнение скрипта занимает 2-3 минуты. Но в будущем планирую убрать формирование части краткосрочных трендов и обновлять в кроне не каждую минуту, а каждые 5 минут минимум.
Вот этот скрипт (rrdgraph.sh):
Код:
#!/bin/sh

rrdtool graph '/home/www/rrd/A0_1min.png' --start -60s --width '650' --height '250' --upper-limit '1' 'DEF:A0=/home/base/rrd_currents.rrd:A0:AVERAGE' 'LINE2:A0#FF00FF'
rrdtool graph '/home/www/rrd/A0_30m.png' --start -30minute --width '650' --height '250' --upper-limit '1' 'DEF:A0=/home/base/rrd_currents.rrd:A0:AVERAGE' 'LINE2:A0#FF00FF'
rrdtool graph '/home/www/rrd/A0_2h.png' --start -2h --width '650' --height '250' --upper-limit '1' 'DEF:A0=/home/base/rrd_currents.rrd:A0:AVERAGE' 'LINE2:A0#FF00FF'
rrdtool graph '/home/www/rrd/A0_1d.png' --start -1d --width '650' --height '250' --upper-limit '1' 'DEF:A0=/home/base/rrd_currents.rrd:A0:AVERAGE' 'LINE2:A0#FF00FF'
rrdtool graph '/home/www/rrd/A0_1w.png' --start -1w --width '650' --height '250' --upper-limit '1' 'DEF:A0=/home/base/rrd_currents.rrd:A0:AVERAGE' 'LINE2:A0#FF00FF'
rrdtool graph '/home/www/rrd/A0_1month.png' --start -1month --width '650' --height '250' --upper-limit '1' 'DEF:A0=/home/base/rrd_currents.rrd:A0:AVERAGE' 'LINE2:A0#FF00FF'
rrdtool graph '/home/www/rrd/A0_1y.png' --start -1y --width '650' --height '250' --upper-limit '1' 'DEF:A0=/home/base/rrd_currents.rrd:A0:AVERAGE' 'LINE2:A0#FF00FF'

rrdtool graph '/home/www/rrd/A1_1min.png' --start -60s --width '650' --height '250' --upper-limit '1' 'DEF:A1=/home/base/rrd_currents.rrd:A1:AVERAGE' 'LINE2:A1#FF00FF'
rrdtool graph '/home/www/rrd/A1_30m.png' --start -30minute --width '650' --height '250' --upper-limit '1' 'DEF:A1=/home/base/rrd_currents.rrd:A1:AVERAGE' 'LINE2:A1#FF00FF'
rrdtool graph '/home/www/rrd/A1_2h.png' --start -2h --width '650' --height '250' --upper-limit '1' 'DEF:A1=/home/base/rrd_currents.rrd:A1:AVERAGE' 'LINE2:A1#FF00FF'
rrdtool graph '/home/www/rrd/A1_1d.png' --start -1d --width '650' --height '250' --upper-limit '1' 'DEF:A1=/home/base/rrd_currents.rrd:A1:AVERAGE' 'LINE2:A1#FF00FF'
rrdtool graph '/home/www/rrd/A1_1w.png' --start -1w --width '650' --height '250' --upper-limit '1' 'DEF:A1=/home/base/rrd_currents.rrd:A1:AVERAGE' 'LINE2:A1#FF00FF'
rrdtool graph '/home/www/rrd/A1_1month.png' --start -1month --width '650' --height '250' --upper-limit '1' 'DEF:A1=/home/base/rrd_currents.rrd:A1:AVERAGE' 'LINE2:A1#FF00FF'
rrdtool graph '/home/www/rrd/A1_1y.png' --start -1y --width '650' --height '250' --upper-limit '1' 'DEF:A1=/home/base/rrd_currents.rrd:A1:AVERAGE' 'LINE2:A1#FF00FF'

rrdtool graph '/home/www/rrd/A2_1min.png' --start -60s --width '650' --height '250' --upper-limit '1' 'DEF:A2=/home/base/rrd_currents.rrd:A2:AVERAGE' 'LINE2:A2#FF00FF'
rrdtool graph '/home/www/rrd/A2_30m.png' --start -30minute --width '650' --height '250' --upper-limit '1' 'DEF:A2=/home/base/rrd_currents.rrd:A2:AVERAGE' 'LINE2:A2#FF00FF'
rrdtool graph '/home/www/rrd/A2_2h.png' --start -2h --width '650' --height '250' --upper-limit '1' 'DEF:A2=/home/base/rrd_currents.rrd:A2:AVERAGE' 'LINE2:A2#FF00FF'
rrdtool graph '/home/www/rrd/A2_1d.png' --start -1d --width '650' --height '250' --upper-limit '1' 'DEF:A2=/home/base/rrd_currents.rrd:A2:AVERAGE' 'LINE2:A2#FF00FF'
rrdtool graph '/home/www/rrd/A2_1w.png' --start -1w --width '650' --height '250' --upper-limit '1' 'DEF:A2=/home/base/rrd_currents.rrd:A2:AVERAGE' 'LINE2:A2#FF00FF'
rrdtool graph '/home/www/rrd/A2_1month.png' --start -1month --width '650' --height '250' --upper-limit '1' 'DEF:A2=/home/base/rrd_currents.rrd:A2:AVERAGE' 'LINE2:A2#FF00FF'
rrdtool graph '/home/www/rrd/A2_1y.png' --start -1y --width '650' --height '250' --upper-limit '1' 'DEF:A2=/home/base/rrd_currents.rrd:A2:AVERAGE' 'LINE2:A2#FF00FF'

rrdtool graph '/home/www/rrd/A3_1min.png' --start -60s --width '650' --height '250' --upper-limit '1' 'DEF:A3=/home/base/rrd_currents.rrd:A3:AVERAGE' 'LINE2:A3#FF00FF'
rrdtool graph '/home/www/rrd/A3_30m.png' --start -30minute --width '650' --height '250' --upper-limit '1' 'DEF:A3=/home/base/rrd_currents.rrd:A3:AVERAGE' 'LINE2:A3#FF00FF'
rrdtool graph '/home/www/rrd/A3_2h.png' --start -2h --width '650' --height '250' --upper-limit '1' 'DEF:A3=/home/base/rrd_currents.rrd:A3:AVERAGE' 'LINE2:A3#FF00FF'
rrdtool graph '/home/www/rrd/A3_1d.png' --start -1d --width '650' --height '250' --upper-limit '1' 'DEF:A3=/home/base/rrd_currents.rrd:A3:AVERAGE' 'LINE2:A3#FF00FF'
rrdtool graph '/home/www/rrd/A3_1w.png' --start -1w --width '650' --height '250' --upper-limit '1' 'DEF:A3=/home/base/rrd_currents.rrd:A3:AVERAGE' 'LINE2:A3#FF00FF'
rrdtool graph '/home/www/rrd/A3_1month.png' --start -1month --width '650' --height '250' --upper-limit '1' 'DEF:A3=/home/base/rrd_currents.rrd:A3:AVERAGE' 'LINE2:A3#FF00FF'
rrdtool graph '/home/www/rrd/A3_1y.png' --start -1y --width '650' --height '250' --upper-limit '1' 'DEF:A3=/home/base/rrd_currents.rrd:A3:AVERAGE' 'LINE2:A3#FF00FF'

rrdtool graph '/home/www/rrd/A4_1min.png' --start -60s --width '650' --height '250' --upper-limit '1' 'DEF:A4=/home/base/rrd_currents.rrd:A4:AVERAGE' 'LINE2:A4#FF00FF'
rrdtool graph '/home/www/rrd/A4_30m.png' --start -30minute --width '650' --height '250' --upper-limit '1' 'DEF:A4=/home/base/rrd_currents.rrd:A4:AVERAGE' 'LINE2:A4#FF00FF'
rrdtool graph '/home/www/rrd/A4_2h.png' --start -2h --width '650' --height '250' --upper-limit '1' 'DEF:A4=/home/base/rrd_currents.rrd:A4:AVERAGE' 'LINE2:A4#FF00FF'
rrdtool graph '/home/www/rrd/A4_1d.png' --start -1d --width '650' --height '250' --upper-limit '1' 'DEF:A4=/home/base/rrd_currents.rrd:A4:AVERAGE' 'LINE2:A4#FF00FF'
rrdtool graph '/home/www/rrd/A4_1w.png' --start -1w --width '650' --height '250' --upper-limit '1' 'DEF:A4=/home/base/rrd_currents.rrd:A4:AVERAGE' 'LINE2:A4#FF00FF'
rrdtool graph '/home/www/rrd/A4_1month.png' --start -1month --width '650' --height '250' --upper-limit '1' 'DEF:A4=/home/base/rrd_currents.rrd:A4:AVERAGE' 'LINE2:A4#FF00FF'
rrdtool graph '/home/www/rrd/A4_1y.png' --start -1y --width '650' --height '250' --upper-limit '1' 'DEF:A4=/home/base/rrd_currents.rrd:A4:AVERAGE' 'LINE2:A4#FF00FF'

rrdtool graph '/home/www/rrd/A5_1min.png' --start -60s --width '650' --height '250' --upper-limit '1' 'DEF:A5=/home/base/rrd_currents.rrd:A5:AVERAGE' 'LINE2:A5#FF00FF'
rrdtool graph '/home/www/rrd/A5_30m.png' --start -30minute --width '650' --height '250' --upper-limit '1' 'DEF:A5=/home/base/rrd_currents.rrd:A5:AVERAGE' 'LINE2:A5#FF00FF'
rrdtool graph '/home/www/rrd/A5_2h.png' --start -2h --width '650' --height '250' --upper-limit '1' 'DEF:A5=/home/base/rrd_currents.rrd:A5:AVERAGE' 'LINE2:A5#FF00FF'
rrdtool graph '/home/www/rrd/A5_1d.png' --start -1d --width '650' --height '250' --upper-limit '1' 'DEF:A5=/home/base/rrd_currents.rrd:A5:AVERAGE' 'LINE2:A5#FF00FF'
rrdtool graph '/home/www/rrd/A5_1w.png' --start -1w --width '650' --height '250' --upper-limit '1' 'DEF:A5=/home/base/rrd_currents.rrd:A5:AVERAGE' 'LINE2:A5#FF00FF'
rrdtool graph '/home/www/rrd/A5_1month.png' --start -1month --width '650' --height '250' --upper-limit '1' 'DEF:A5=/home/base/rrd_currents.rrd:A5:AVERAGE' 'LINE2:A5#FF00FF'
rrdtool graph '/home/www/rrd/A5_1y.png' --start -1y --width '650' --height '250' --upper-limit '1' 'DEF:A5=/home/base/rrd_currents.rrd:A5:AVERAGE' 'LINE2:A5#FF00FF'

rrdtool graph '/home/www/rrd/A6_1min.png' --start -60s --width '650' --height '250' --upper-limit '1' 'DEF:A6=/home/base/rrd_currents.rrd:A6:AVERAGE' 'LINE2:A6#FF00FF'
rrdtool graph '/home/www/rrd/A6_30m.png' --start -30minute --width '650' --height '250' --upper-limit '1' 'DEF:A6=/home/base/rrd_currents.rrd:A6:AVERAGE' 'LINE2:A6#FF00FF'
rrdtool graph '/home/www/rrd/A6_2h.png' --start -2h --width '650' --height '250' --upper-limit '1' 'DEF:A6=/home/base/rrd_currents.rrd:A6:AVERAGE' 'LINE2:A6#FF00FF'
rrdtool graph '/home/www/rrd/A6_1d.png' --start -1d --width '650' --height '250' --upper-limit '1' 'DEF:A6=/home/base/rrd_currents.rrd:A6:AVERAGE' 'LINE2:A6#FF00FF'
rrdtool graph '/home/www/rrd/A6_1w.png' --start -1w --width '650' --height '250' --upper-limit '1' 'DEF:A6=/home/base/rrd_currents.rrd:A6:AVERAGE' 'LINE2:A6#FF00FF'
rrdtool graph '/home/www/rrd/A6_1month.png' --start -1month --width '650' --height '250' --upper-limit '1' 'DEF:A6=/home/base/rrd_currents.rrd:A6:AVERAGE' 'LINE2:A6#FF00FF'
rrdtool graph '/home/www/rrd/A6_1y.png' --start -1y --width '650' --height '250' --upper-limit '1' 'DEF:A6=/home/base/rrd_currents.rrd:A6:AVERAGE' 'LINE2:A6#FF00FF'

rrdtool graph '/home/www/rrd/A7_1min.png' --start -60s --width '650' --height '250' --upper-limit '1' 'DEF:A7=/home/base/rrd_currents.rrd:A7:AVERAGE' 'LINE2:A7#FF00FF'
rrdtool graph '/home/www/rrd/A7_30m.png' --start -30minute --width '650' --height '250' --upper-limit '1' 'DEF:A7=/home/base/rrd_currents.rrd:A7:AVERAGE' 'LINE2:A7#FF00FF'
rrdtool graph '/home/www/rrd/A7_2h.png' --start -2h --width '650' --height '250' --upper-limit '1' 'DEF:A7=/home/base/rrd_currents.rrd:A7:AVERAGE' 'LINE2:A7#FF00FF'
rrdtool graph '/home/www/rrd/A7_1d.png' --start -1d --width '650' --height '250' --upper-limit '1' 'DEF:A7=/home/base/rrd_currents.rrd:A7:AVERAGE' 'LINE2:A7#FF00FF'
rrdtool graph '/home/www/rrd/A7_1w.png' --start -1w --width '650' --height '250' --upper-limit '1' 'DEF:A7=/home/base/rrd_currents.rrd:A7:AVERAGE' 'LINE2:A7#FF00FF'
rrdtool graph '/home/www/rrd/A7_1month.png' --start -1month --width '650' --height '250' --upper-limit '1' 'DEF:A7=/home/base/rrd_currents.rrd:A7:AVERAGE' 'LINE2:A7#FF00FF'
rrdtool graph '/home/www/rrd/A7_1y.png' --start -1y --width '650' --height '250' --upper-limit '1' 'DEF:A7=/home/base/rrd_currents.rrd:A7:AVERAGE' 'LINE2:A7#FF00FF'

rrdtool graph '/home/www/rrd/A8_1min.png' --start -60s --width '650' --height '250' --upper-limit '1' 'DEF:A8=/home/base/rrd_currents.rrd:A8:AVERAGE' 'LINE2:A8#FF00FF'
rrdtool graph '/home/www/rrd/A8_30m.png' --start -30minute --width '650' --height '250' --upper-limit '1' 'DEF:A8=/home/base/rrd_currents.rrd:A8:AVERAGE' 'LINE2:A8#FF00FF'
rrdtool graph '/home/www/rrd/A8_2h.png' --start -2h --width '650' --height '250' --upper-limit '1' 'DEF:A8=/home/base/rrd_currents.rrd:A8:AVERAGE' 'LINE2:A8#FF00FF'
rrdtool graph '/home/www/rrd/A8_1d.png' --start -1d --width '650' --height '250' --upper-limit '1' 'DEF:A8=/home/base/rrd_currents.rrd:A8:AVERAGE' 'LINE2:A8#FF00FF'
rrdtool graph '/home/www/rrd/A8_1w.png' --start -1w --width '650' --height '250' --upper-limit '1' 'DEF:A8=/home/base/rrd_currents.rrd:A8:AVERAGE' 'LINE2:A8#FF00FF'
rrdtool graph '/home/www/rrd/A8_1month.png' --start -1month --width '650' --height '250' --upper-limit '1' 'DEF:A8=/home/base/rrd_currents.rrd:A8:AVERAGE' 'LINE2:A8#FF00FF'
rrdtool graph '/home/www/rrd/A8_1y.png' --start -1y --width '650' --height '250' --upper-limit '1' 'DEF:A8=/home/base/rrd_currents.rrd:A8:AVERAGE' 'LINE2:A8#FF00FF'

rrdtool graph '/home/www/rrd/A9_1min.png' --start -60s --width '650' --height '250' --upper-limit '1' 'DEF:A9=/home/base/rrd_currents.rrd:A9:AVERAGE' 'LINE2:A9#FF00FF'
rrdtool graph '/home/www/rrd/A9_30m.png' --start -30minute --width '650' --height '250' --upper-limit '1' 'DEF:A9=/home/base/rrd_currents.rrd:A9:AVERAGE' 'LINE2:A9#FF00FF'
rrdtool graph '/home/www/rrd/A9_2h.png' --start -2h --width '650' --height '250' --upper-limit '1' 'DEF:A9=/home/base/rrd_currents.rrd:A9:AVERAGE' 'LINE2:A9#FF00FF'
rrdtool graph '/home/www/rrd/A9_1d.png' --start -1d --width '650' --height '250' --upper-limit '1' 'DEF:A9=/home/base/rrd_currents.rrd:A9:AVERAGE' 'LINE2:A9#FF00FF'
rrdtool graph '/home/www/rrd/A9_1w.png' --start -1w --width '650' --height '250' --upper-limit '1' 'DEF:A9=/home/base/rrd_currents.rrd:A9:AVERAGE' 'LINE2:A9#FF00FF'
rrdtool graph '/home/www/rrd/A9_1month.png' --start -1month --width '650' --height '250' --upper-limit '1' 'DEF:A9=/home/base/rrd_currents.rrd:A9:AVERAGE' 'LINE2:A9#FF00FF'
rrdtool graph '/home/www/rrd/A9_1y.png' --start -1y --width '650' --height '250' --upper-limit '1' 'DEF:A9=/home/base/rrd_currents.rrd:A9:AVERAGE' 'LINE2:A9#FF00FF'

rrdtool graph '/home/www/rrd/A10_1min.png' --start -60s --width '650' --height '250' --upper-limit '1' 'DEF:A10=/home/base/rrd_currents.rrd:A10:AVERAGE' 'LINE2:A10#FF00FF'
rrdtool graph '/home/www/rrd/A10_30m.png' --start -30minute --width '650' --height '250' --upper-limit '1' 'DEF:A10=/home/base/rrd_currents.rrd:A10:AVERAGE' 'LINE2:A10#FF00FF'
rrdtool graph '/home/www/rrd/A10_2h.png' --start -2h --width '650' --height '250' --upper-limit '1' 'DEF:A10=/home/base/rrd_currents.rrd:A10:AVERAGE' 'LINE2:A10#FF00FF'
rrdtool graph '/home/www/rrd/A10_1d.png' --start -1d --width '650' --height '250' --upper-limit '1' 'DEF:A10=/home/base/rrd_currents.rrd:A10:AVERAGE' 'LINE2:A10#FF00FF'
rrdtool graph '/home/www/rrd/A10_1w.png' --start -1w --width '650' --height '250' --upper-limit '1' 'DEF:A10=/home/base/rrd_currents.rrd:A10:AVERAGE' 'LINE2:A10#FF00FF'
rrdtool graph '/home/www/rrd/A10_1month.png' --start -1month --width '650' --height '250' --upper-limit '1' 'DEF:A10=/home/base/rrd_currents.rrd:A10:AVERAGE' 'LINE2:A10#FF00FF'
rrdtool graph '/home/www/rrd/A10_1y.png' --start -1y --width '650' --height '250' --upper-limit '1' 'DEF:A10=/home/base/rrd_currents.rrd:A10:AVERAGE' 'LINE2:A10#FF00FF'

rrdtool graph '/home/www/rrd/A11_1min.png' --start -60s --width '650' --height '250' --upper-limit '1' 'DEF:A11=/home/base/rrd_currents.rrd:A11:AVERAGE' 'LINE2:A11#FF00FF'
rrdtool graph '/home/www/rrd/A11_30m.png' --start -30minute --width '650' --height '250' --upper-limit '1' 'DEF:A11=/home/base/rrd_currents.rrd:A11:AVERAGE' 'LINE2:A11#FF00FF'
rrdtool graph '/home/www/rrd/A11_2h.png' --start -2h --width '650' --height '250' --upper-limit '1' 'DEF:A11=/home/base/rrd_currents.rrd:A11:AVERAGE' 'LINE2:A11#FF00FF'
rrdtool graph '/home/www/rrd/A11_1d.png' --start -1d --width '650' --height '250' --upper-limit '1' 'DEF:A11=/home/base/rrd_currents.rrd:A11:AVERAGE' 'LINE2:A11#FF00FF'
rrdtool graph '/home/www/rrd/A11_1w.png' --start -1w --width '650' --height '250' --upper-limit '1' 'DEF:A11=/home/base/rrd_currents.rrd:A11:AVERAGE' 'LINE2:A11#FF00FF'
rrdtool graph '/home/www/rrd/A11_1month.png' --start -1month --width '650' --height '250' --upper-limit '1' 'DEF:A11=/home/base/rrd_currents.rrd:A11:AVERAGE' 'LINE2:A11#FF00FF'
rrdtool graph '/home/www/rrd/A11_1y.png' --start -1y --width '650' --height '250' --upper-limit '1' 'DEF:A11=/home/base/rrd_currents.rrd:A11:AVERAGE' 'LINE2:A11#FF00FF'

rrdtool graph '/home/www/rrd/A12_1min.png' --start -60s --width '650' --height '250' --upper-limit '1' 'DEF:A12=/home/base/rrd_currents.rrd:A12:AVERAGE' 'LINE2:A12#FF00FF'
rrdtool graph '/home/www/rrd/A12_30m.png' --start -30minute --width '650' --height '250' --upper-limit '1' 'DEF:A12=/home/base/rrd_currents.rrd:A12:AVERAGE' 'LINE2:A12#FF00FF'
rrdtool graph '/home/www/rrd/A12_2h.png' --start -2h --width '650' --height '250' --upper-limit '1' 'DEF:A12=/home/base/rrd_currents.rrd:A12:AVERAGE' 'LINE2:A12#FF00FF'
rrdtool graph '/home/www/rrd/A12_1d.png' --start -1d --width '650' --height '250' --upper-limit '1' 'DEF:A12=/home/base/rrd_currents.rrd:A12:AVERAGE' 'LINE2:A12#FF00FF'
rrdtool graph '/home/www/rrd/A12_1w.png' --start -1w --width '650' --height '250' --upper-limit '1' 'DEF:A12=/home/base/rrd_currents.rrd:A12:AVERAGE' 'LINE2:A12#FF00FF'
rrdtool graph '/home/www/rrd/A12_1month.png' --start -1month --width '650' --height '250' --upper-limit '1' 'DEF:A12=/home/base/rrd_currents.rrd:A12:AVERAGE' 'LINE2:A12#FF00FF'
rrdtool graph '/home/www/rrd/A12_1y.png' --start -1y --width '650' --height '250' --upper-limit '1' 'DEF:A12=/home/base/rrd_currents.rrd:A12:AVERAGE' 'LINE2:A12#FF00FF'

rrdtool graph '/home/www/rrd/A13_1min.png' --start -60s --width '650' --height '250' --upper-limit '1' 'DEF:A13=/home/base/rrd_currents.rrd:A13:AVERAGE' 'LINE2:A13#FF00FF'
rrdtool graph '/home/www/rrd/A13_30m.png' --start -30minute --width '650' --height '250' --upper-limit '1' 'DEF:A13=/home/base/rrd_currents.rrd:A13:AVERAGE' 'LINE2:A13#FF00FF'
rrdtool graph '/home/www/rrd/A13_2h.png' --start -2h --width '650' --height '250' --upper-limit '1' 'DEF:A13=/home/base/rrd_currents.rrd:A13:AVERAGE' 'LINE2:A13#FF00FF'
rrdtool graph '/home/www/rrd/A13_1d.png' --start -1d --width '650' --height '250' --upper-limit '1' 'DEF:A13=/home/base/rrd_currents.rrd:A13:AVERAGE' 'LINE2:A13#FF00FF'
rrdtool graph '/home/www/rrd/A13_1w.png' --start -1w --width '650' --height '250' --upper-limit '1' 'DEF:A13=/home/base/rrd_currents.rrd:A13:AVERAGE' 'LINE2:A13#FF00FF'
rrdtool graph '/home/www/rrd/A13_1month.png' --start -1month --width '650' --height '250' --upper-limit '1' 'DEF:A13=/home/base/rrd_currents.rrd:A13:AVERAGE' 'LINE2:A13#FF00FF'
rrdtool graph '/home/www/rrd/A13_1y.png' --start -1y --width '650' --height '250' --upper-limit '1' 'DEF:A13=/home/base/rrd_currents.rrd:A13:AVERAGE' 'LINE2:A13#FF00FF'

rrdtool graph '/home/www/rrd/A14_1min.png' --start -60s --width '650' --height '250' --upper-limit '1' 'DEF:A14=/home/base/rrd_currents.rrd:A14:AVERAGE' 'LINE2:A14#FF00FF'
rrdtool graph '/home/www/rrd/A14_30m.png' --start -30minute --width '650' --height '250' --upper-limit '1' 'DEF:A14=/home/base/rrd_currents.rrd:A14:AVERAGE' 'LINE2:A14#FF00FF'
rrdtool graph '/home/www/rrd/A14_2h.png' --start -2h --width '650' --height '250' --upper-limit '1' 'DEF:A14=/home/base/rrd_currents.rrd:A14:AVERAGE' 'LINE2:A14#FF00FF'
rrdtool graph '/home/www/rrd/A14_1d.png' --start -1d --width '650' --height '250' --upper-limit '1' 'DEF:A14=/home/base/rrd_currents.rrd:A14:AVERAGE' 'LINE2:A14#FF00FF'
rrdtool graph '/home/www/rrd/A14_1w.png' --start -1w --width '650' --height '250' --upper-limit '1' 'DEF:A14=/home/base/rrd_currents.rrd:A14:AVERAGE' 'LINE2:A14#FF00FF'
rrdtool graph '/home/www/rrd/A14_1month.png' --start -1month --width '650' --height '250' --upper-limit '1' 'DEF:A14=/home/base/rrd_currents.rrd:A14:AVERAGE' 'LINE2:A14#FF00FF'
rrdtool graph '/home/www/rrd/A14_1y.png' --start -1y --width '650' --height '250' --upper-limit '1' 'DEF:A14=/home/base/rrd_currents.rrd:A14:AVERAGE' 'LINE2:A14#FF00FF'

rrdtool graph '/home/www/rrd/A15_1min.png' --start -60s --width '650' --height '250' --upper-limit '1' 'DEF:A15=/home/base/rrd_currents.rrd:A15:AVERAGE' 'LINE2:A15#FF00FF'
rrdtool graph '/home/www/rrd/A15_30m.png' --start -30minute --width '650' --height '250' --upper-limit '1' 'DEF:A15=/home/base/rrd_currents.rrd:A15:AVERAGE' 'LINE2:A15#FF00FF'
rrdtool graph '/home/www/rrd/A15_2h.png' --start -2h --width '650' --height '250' --upper-limit '1' 'DEF:A15=/home/base/rrd_currents.rrd:A15:AVERAGE' 'LINE2:A15#FF00FF'
rrdtool graph '/home/www/rrd/A15_1d.png' --start -1d --width '650' --height '250' --upper-limit '1' 'DEF:A15=/home/base/rrd_currents.rrd:A15:AVERAGE' 'LINE2:A15#FF00FF'
rrdtool graph '/home/www/rrd/A15_1w.png' --start -1w --width '650' --height '250' --upper-limit '1' 'DEF:A15=/home/base/rrd_currents.rrd:A15:AVERAGE' 'LINE2:A15#FF00FF'
rrdtool graph '/home/www/rrd/A15_1month.png' --start -1month --width '650' --height '250' --upper-limit '1' 'DEF:A15=/home/base/rrd_currents.rrd:A15:AVERAGE' 'LINE2:A15#FF00FF'
rrdtool graph '/home/www/rrd/A15_1y.png' --start -1y --width '650' --height '250' --upper-limit '1' 'DEF:A15=/home/base/rrd_currents.rrd:A15:AVERAGE' 'LINE2:A15#FF00FF'
Тоже никак руки не дойдут допилить его нормально и в цикле всё организовать, но уже хотелось результатов и получилось то, что получилось

Подводя итоги: в целом система работает неплохо (для моих целей) и выполняет свою задачу - сбор и анализ потребления эл.энергии. Несомненно - она будет допиливаться, но это уже по результатам накопленной статистики и по наличию свободного времени. Датчики тока работают неплохо на средних значениях, но на значениях близких к нулю - большой шум до 100-150 мА, так что коммерческого учёта электроэнергии не получится
Ну и напоследок - несколько графиков:
F11(A0) - XS11:холодильник; XS12:духовка, вытяжка [C16]
Вложение 2286
F23(A4) - R21:тёплый пол [C16]
Вложение 2287
F22(A5) - XS22:водонагреватель; XS23:розетка у раковины [C16]
Вложение 2288
Kopylov вне форума   Ответить с цитированием
Старый 24.02.2015, 20:24   #4
Kopylov
Junior Member
 
Регистрация: 12.02.2015
Сообщений: 18
Вес репутации: 0
Kopylov is an unknown quantity at this point
По умолчанию Re: OpenWRT + modbus + arduino = учёт эл.энергии

Со вложениями не разобрался - как подправить так, чтобы они не в первом сообщении были, а сразу в тексте были, сорри.
Kopylov вне форума   Ответить с цитированием
Старый 24.02.2015, 21:14   #5
solalex
Senior Member
 
Регистрация: 20.01.2015
Сообщений: 312
Вес репутации: 443
solalex has much to be proud ofsolalex has much to be proud ofsolalex has much to be proud ofsolalex has much to be proud ofsolalex has much to be proud ofsolalex has much to be proud ofsolalex has much to be proud ofsolalex has much to be proud ofsolalex has much to be proud of
По умолчанию Re: OpenWRT + modbus + arduino = учёт эл.энергии

Цитата:
Сообщение от Kopylov Посмотреть сообщение
В последнее время по счетам за электроэнергию стали приходить большие счета и решил я создать небольшую систему для сбора данных по количеству потребляемой электроэнергии для того чтобы проанализировать: куда идёт бОльшая часть эл.энергии и как можно этот расход оптимизировать.
Оптимизировали расходы на электричество?
solalex вне форума   Ответить с цитированием
Старый 24.02.2015, 21:23   #6
Kopylov
Junior Member
 
Регистрация: 12.02.2015
Сообщений: 18
Вес репутации: 0
Kopylov is an unknown quantity at this point
По умолчанию Re: OpenWRT + modbus + arduino = учёт эл.энергии

Статистика собирается только вторые сутки, но уже есть много информации для размышления. Например: я грешил на тёплые полы в ванной - думал очень много кушают они - ан нет! Гроши потребляют в сумме. Сейчас увеличил температуру полов - посмотрю сколько они в максимуме выжгут. Также полезно проверить - правильно ли автоматы выбраны - проверяю потихоньку - где постоянно одни и те же электроприборы подключены - проверяю макимальную нагрузку и смотрю - нужно ли менять автомат.
Kopylov вне форума   Ответить с цитированием
Старый 24.02.2015, 21:34   #7
solalex
Senior Member
 
Регистрация: 20.01.2015
Сообщений: 312
Вес репутации: 443
solalex has much to be proud ofsolalex has much to be proud ofsolalex has much to be proud ofsolalex has much to be proud ofsolalex has much to be proud ofsolalex has much to be proud ofsolalex has much to be proud ofsolalex has much to be proud ofsolalex has much to be proud of
По умолчанию Re: OpenWRT + modbus + arduino = учёт эл.энергии

УЗО и УЗИП ставили?
solalex вне форума   Ответить с цитированием
Старый 24.02.2015, 21:40   #8
Kopylov
Junior Member
 
Регистрация: 12.02.2015
Сообщений: 18
Вес репутации: 0
Kopylov is an unknown quantity at this point
По умолчанию Re: OpenWRT + modbus + arduino = учёт эл.энергии

УЗО стоят 2 штуки - только на водонагреватель и на теплые полы.

Упс.. Поправка - на водонагреватель и на стиралку УЗО подключены. Теплые полы электрики без УЗО пустили.

Последний раз редактировалось Kopylov; 24.02.2015 в 21:55.
Kopylov вне форума   Ответить с цитированием
Старый 15.04.2015, 13:56   #9
Prana
Junior Member
 
Регистрация: 17.03.2015
Сообщений: 27
Вес репутации: 0
Prana is an unknown quantity at this point
По умолчанию Re: OpenWRT + modbus + arduino = учёт эл.энергии

а какие ACS712-20A вы покупали? смотрел на готовые китайские модули, и меня смущает сечение подводки/дорожек на платах, подходящих к датчикам - не отгорают ли они при регулярно возникающей нагрузке индуктивного характера более 10А?
Prana вне форума   Ответить с цитированием
Старый 15.04.2015, 14:58   #10
Kopylov
Junior Member
 
Регистрация: 12.02.2015
Сообщений: 18
Вес репутации: 0
Kopylov is an unknown quantity at this point
По умолчанию Re: OpenWRT + modbus + arduino = учёт эл.энергии

Брал вот эти: http://www.ebay.com/itm/1pcs-new-20A...item51c48706bb
Насчет индуктивной нагрузки 10А не могу сказать. У меня в квартире в основном активная нагрузка и частота включений маленькая. Из постоянно включающейся индуктивной нагрузки - это холодильник - включается на 6 минут каждые 18 минут. Но там ток всего-то 0,7А.
Kopylov вне форума   Ответить с цитированием
Ответ


Здесь присутствуют: 2 (пользователей: 0 , гостей: 2)
 
Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск
Опции просмотра

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

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

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


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


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