По некоторым причинам у меня не получается заменить его на новый электронный с каким-либо цифровым интерфейсом. Так же не очень хотелось сооружать конструкцию из двух счётчиков. Короче, было решено попробовать собирать информацию считая количество оборотов колеса на счётчике.
Принцип такой -- с помощью ИК приёмника/передатчика и контроллера считаем количество оборотов колеса и по радио-каналу отправляем на сервер количество оборотов и время, за которое они были сделаны. Таким образом, зная скорость вращения колеса мы можем с некоторым приближением получить текущую потребляемую мощность. Ну а т.к. мы знаем точное количество оборотов, то достаточно точно будем знать суммарное потребление за более продолжительный период.
На контроллере считаем каждый импульс, но на сервер отправляем значение счётчика, если количество оборотов более 30 или если время с отправки последних данных более 10 минут. Таким образом мы не будем захламлять эфир постоянной передачей данных. Потерять данные не очень страшно -- просто время от времени надо будет сравнивать рассчётное показание на компьютере с реальным показанием на счётчике. По опыту нескольких дней использования эта разбежка если и будет, то минимальная (пока один в один).
Для рассчёта мощности нам нужно знать как сопоставить оборт счётчика с реальным потреблением. У меня на самом счётчике написано, что 1Квт/ч = 150 оборотов. Так что исходя из этого всё можно рассчитать.
Используемые компоненты для устройства:
1. Контроллер Arduino
2. Блок ИК приёмника-передатчика:
(куплено здесь)
3. RF-передатчик на 433Mhz:
4. Светодиод (будет дублировать прохождение тёмной полосы при вращении круга на счётчике)
По схематике всё довольно просто. В моём случае я использовал такое подключение к контроллеру:
PIN 2 -- светодиод индикации импульсов
PIN 3 -- data-выход для радио-передачика (подробнее про организацию протокола передачи я писал здесь)
PIN 4 -- data-вход от IR-модуля (в этом модуле два выхода, но нам нужен только один)
PIN 13 -- светодиод индикации отправки данных на сервер (используется встроенный светодиод)
Ниже код скетча для прошивки Arduino:
Код: Выделить всё
#include <VirtualWire.h>
#include <EasyTransferVirtualWire.h>
#include <EEPROM.h> //Needed to access the eeprom read write functions
#define PIN_LED 2
#define PIN_LED_T 13
#define PIN_TEMP 7
#define PIN_RF 3
#define PIN_IR 4
#define PERIOD_SEND_POWER 600 // seconds
#define PERIOD_MAX_COUNTER 30 // circles
#define COMMAND_COUNTER 15 // command code for counter
#define RF_SEND_COUNTER 3 // number of packets to send with transmission
unsigned int unique_device_id = 0;
int old_status=0;
long int timeSentPower=0;
long int counterPower=0;
long int timePassedPower=0;
//create object
EasyTransferVirtualWire ET;
struct SEND_DATA_STRUCTURE{
//put your variable definitions here for the data you want to send
//THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO
//Struct can'e be bigger then 26 bytes for VirtualWire version
unsigned int device_id;
unsigned int destination_id;
unsigned int packet_id;
byte command;
int data;
};
//give a name to the group of data
SEND_DATA_STRUCTURE mydata;
//This function will write a 2 byte integer to the eeprom at the specified address and address + 1
void EEPROMWriteInt(int p_address, unsigned int p_value)
{
byte lowByte = ((p_value >> 0) & 0xFF);
byte highByte = ((p_value >> 8) & 0xFF);
EEPROM.write(p_address, lowByte);
EEPROM.write(p_address + 1, highByte);
}
//This function will read a 2 byte integer from the eeprom at the specified address and address + 1
unsigned int EEPROMReadInt(int p_address)
{
byte lowByte = EEPROM.read(p_address);
byte highByte = EEPROM.read(p_address + 1);
return ((lowByte << 0) & 0xFF) + ((highByte << 8) & 0xFF00);
}
void blinking(int count) {
for(int i=0;i<count;i++) {
digitalWrite(PIN_LED, HIGH);
delay(200);
digitalWrite(PIN_LED, LOW);
delay(200);
}
}
void sendRFData() {
Serial.print("Transmitting packets ... ");
for(int i=0;i<RF_SEND_COUNTER;i++) {
if (i>0) {
delay(200);
}
digitalWrite(PIN_LED_T, HIGH);
ET.sendData();
digitalWrite(PIN_LED_T, LOW);
}
Serial.println("DONE");
}
void setup()
{
pinMode(PIN_LED, OUTPUT);
pinMode(PIN_LED_T, OUTPUT);
pinMode(PIN_IR, INPUT);
Serial.begin(9600);
ET.begin(details(mydata));
vw_set_tx_pin(PIN_RF);
vw_setup(2000); // Bits per sec
randomSeed(analogRead(0));
// Device ID
Serial.print("Getting Device ID... ");
unique_device_id=EEPROMReadInt(0);
if (unique_device_id<10000 || unique_device_id>60000) {
Serial.print("N/A, updating... ");
unique_device_id=random(10000, 60000);
EEPROMWriteInt(0, unique_device_id);
}
Serial.println(unique_device_id);
mydata.device_id = unique_device_id;
mydata.destination_id = 0;
}
void loop()
{
int current_ir=digitalRead(PIN_IR);
digitalWrite(PIN_LED,current_ir);
if (current_ir!=old_status) {
if (current_ir==0) {
counterPower++;
Serial.print(" Power counter: ");
Serial.print(counterPower);
Serial.print(" time passed: ");
timePassedPower=millis()/1000-timeSentPower;
Serial.println(timePassedPower);
if ((counterPower>=PERIOD_MAX_COUNTER) || (timePassedPower>=PERIOD_SEND_POWER)) {
timeSentPower=millis()/1000;
mydata.packet_id = random(65535);
mydata.command = COMMAND_COUNTER; //power counter
Serial.print("Sending value: ");
mydata.data = (int)counterPower*1000+(int)timePassedPower;
Serial.println(mydata.data);
sendRFData();
counterPower=0;
}
}
old_status=(int)current_ir;
}
delay(100); // задержка.
}
Итак, добившись работы контроллера переходим к сбору данных на сервере.
Для начала я создал класс объектов Counters со следующими свойствами:
Так же в этот класс добавил метод onUpdate со следующим кодом:
Код: Выделить всё
$counter=$params['counter'];
$passed=$params['passed'];
$old_counter=$this->getProperty("counter");
if ($counter>0) {
$old_counter+=$counter;
$this->setProperty("counter",$old_counter); // показание счётчика (в оборотах)
$this->setProperty("power",round($old_counter/150)); // показание счётчика (в кВт/ч)
}
if ($passed>0) {
$speed=round((($counter*60*60/$passed)/150),2); // рассчитываем текущую потребляемую мощность в кВт/ч
$this->setProperty("speed",$speed);
}
$this->setProperty("updated",time());
$this->setProperty("updatedTime",date('H:i'));
Дальше нужно внести правки в сценарий, который запускается приёмником сообщений от устройств из эфира. Там надо добавить обработчик сигналов от нашего нового беспроводного модуля:
Вот сам код с комментариями:
Код: Выделить всё
if ($device_id=='23505') { // счётчик на электро-щитке (вместо 23505 может быть любой код, совпадающий с кодом устройства)
// радио-модуль передаёт в одном числе закодированное количество оборотов и время, за которое они были посчитаны
// раскодируем:
$counter=floor($data/1000); // количество оборотов
$passed=$data-$counter*1000; // прошедшее количество секунд
callMethod("ElectroCounter.onUpdate",array("counter"=>$counter,"passed"=>$passed)); // отправляем данные в метод объекта
}
В основном меню добавляем пункт с таким названием:
Код: Выделить всё
Электро-энергия (%ElectroCounter.speed% Квт/ч)
1. Расход:
Код: Выделить всё
Текущее потребление: %ElectroCounter.speed% Квт/ч<br/>
Показание счётчика: %ElectroCounter.power% Квт/ч
Код: Выделить всё
Потребление (24h)<br>
<img src="/pChart/?p=ElectroCounter.speed&type=24h&width=280>ype=curve&px=5&unit=kWh&%rand%">
Код: Выделить всё
Показание счётчика (месяц)<br>
<img src="/pChart/?p=ElectroCounter.power&type=30d&width=280>ype=curve&px=15&unit=kWh&%rand%">
(месячный график не совсем корректный, т.к. не было истории показаний)