Мониторинг электрической нагрузки офиса.

RN6LJK
Offline
Зарегистрирован: 24.03.2013

На сегодняшний день устройство снимает мгновенные текущие показания напряжения и тока на фазах A B C.  Индикация осуществляется на дисплей в разрезе фаз по цветам. Индицируются мгновенные значения напряжения, тока, мощность по фазе и суммарная. Результаты текущего измерения транслируются на сервер sparkfun.com, что позволяет накапливать и отслеживать поведения процесса электропотребления в офисе.

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

Обработка результатов выполняется на Arduino Mega2560. Трансляция на сервер sparkfun.com реализована на ардуино-совместимом модуле ESP8266.

 

 

Измеритель в железе.

Токовые трансформаторы

Плата контроллеров (пока в сыром варианте без фиксации WiFi модуля ESP8266).

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

Результаты измерений можно посмотреть в текстовом виде.

Также возможен анализ результатов в графическом представлении.

 

 

Скетч для Мега2560.

// Измерение
// Вывод на дисплей
// 
// EmonLibrary examples openenergymonitor.org, Licence GNU GPL V3
// 11.05.2016


#include "EmonLib.h"                   // Include Emon Library
EnergyMonitor emon1;                   // Create an instance
EnergyMonitor emon2;                   // Create an instance
EnergyMonitor emon3;                   // Create an instance



// библиотека для работы с дисплеем
#include <UTFT.h>
// создаём объект класса UTFT
// и передаём идентификатор модели дисплея и номера управляющих пинов
UTFT myGLCD(CTE32HR, 38, 39, 40, 41);
// объявления встроенного шрифта
extern uint8_t BigFont[];
extern uint8_t SevenSegNumFont[];


#include <SoftwareSerial.h>

//SoftwareSerial swSer(2, 3, false, 256);// RX, TX
SoftwareSerial swSer(6,7);// RX, TX


int a1;
int a2;
int a3;
byte  c=B00110000;
byte c1=B00110000;
byte c2=B00110000;
byte c3=B00110000;
int out[]={15,240,20,230,25,240};
int i_w=0;
int in=0;
int ik=5; // размерность массива out
//int kfa=22; // поправочный коэффциент - Dlink
//int kfb=26; // поправочный коэффциент - Dlink
//int kfc=26; // поправочный коэффциент - Dlink

int kfa=32; // поправочный коэффциент - внешний черный блок
int kfb=36; // поправочный коэффциент - внешний черный блок
int kfc=36; // поправочный коэффциент - внешний черный блок

void setup()
{   
  swSer.begin(115200);
  Serial.begin(9600);
  
  emon1.current(A3, 111.1);             // Current: input pin, calibration.
    emon2.current(A4, 111.1);             // Current: input pin, calibration.
      emon3.current(A5, 111.1);             // Current: input pin, calibration.


  // инициализируем дисплей с вертикальной ориентацией
  myGLCD.InitLCD();

//analogReference(DEFAULT);  
}



int val;
int val_min=1023;
int val_max=0;
int i=1;
float val_sr;
float volt;
float faza_A;
float faza_B;
float faza_C;
int port_A=A0;
int port_B=A1;
int port_C=A2;
//float kf=1;
//float kf=68.3; // устанавливать после 2-х часового прогрева, 59.5 холодный для D-Link
float kf=58.5;

// the loop routine runs over and over again forever:
void loop() {
volt_faza(port_A); // Желтый
faza_A=volt*kf;
volt_faza(port_B); // Зеленый
faza_B=volt*kf;
volt_faza(port_C); // Красный
faza_C=volt*kf;


//analogReference(DEFAULT);  
  double Irms1 = (emon1.calcIrms(1480))/2;  // Calculate Irms only
    double Irms2 = (emon2.calcIrms(1480))/2;  // Calculate Irms only
      double Irms3 = (emon3.calcIrms(1480))/2;  // Calculate Irms only
      
Serial.print ("phase A  ");
Serial.print (faza_A,2);
Serial.print ("V  ");
  Serial.print(Irms1,2);
  Serial.print ("A  ");
Serial.print ("|  phase B  ");
Serial.print (faza_B,2);
Serial.print ("V  ");
  Serial.print(Irms2,2);
  Serial.print ("A  ");
Serial.print ("|  phase C  ");
Serial.print (faza_C,2);
Serial.print ("V  ");
  Serial.print(Irms3,2);
  Serial.println ("A");

 

  // печатаем строку в указанной строке позиции
  int p=50;
  int p0=0+p;
  int p1=100+p;
  int p2=140+p;
  int p3=210+p;
  int p4=260+p;
  int p5=350+p;
  int s1=70;
  int s2=140;
  int s3=210;
  int fa=round(faza_A)+kfa;
  int i1=round(Irms1);
  int fb=round(faza_B)+kfb;
  int i2=round(Irms2);
  int fc=round(faza_C)+kfc;
  int i3=round(Irms3);
out[0]=i1;
out[1]=fa;
out[2]=i2;
out[3]=fb;
out[4]=i3;
out[5]=fc;
transmit();
  int kw1=round((faza_A*Irms1)/1000);
   int kw2=round((faza_B*Irms2)/1000);
    int kw3=round((faza_C*Irms3)/1000);
 
 int kw=kw1+kw2+kw3;

   // очищаем экран
    myGLCD.clrScr();  
   // фаза А

  myGLCD.setFont(SevenSegNumFont);
  myGLCD.setColor(VGA_YELLOW);

 myGLCD.printNumI (fa,p0,0);
 myGLCD.setFont(BigFont);
 myGLCD.print ("V",p1,0);
 myGLCD.setFont(SevenSegNumFont);
 myGLCD.printNumI(i1,p2,0);
 myGLCD.setFont(BigFont);
 myGLCD.print ("A",p3,0);
 myGLCD.setFont(SevenSegNumFont);
 myGLCD.printNumI(kw1,p4,0);
 myGLCD.setFont(BigFont);
 myGLCD.print ("kW",p5,0);
   // фаза В
     myGLCD.setColor(VGA_GREEN);
 myGLCD.setFont(SevenSegNumFont);  
 myGLCD.printNumI (fb,p0,s1);
 myGLCD.setFont(BigFont);
 myGLCD.print ("V",p1,s1);
 myGLCD.setFont(SevenSegNumFont);
 myGLCD.printNumI(i2,p2,s1);
 myGLCD.setFont(BigFont);
 myGLCD.print ("A",p3,s1);
 myGLCD.setFont(SevenSegNumFont);
 myGLCD.printNumI(kw2,p4,s1);
 myGLCD.setFont(BigFont);
 myGLCD.print ("kW",p5,s1);

    // фаза С
 myGLCD.setColor(VGA_RED);
 myGLCD.setFont(SevenSegNumFont);
 myGLCD.printNumI (fc,p0,s2);
 myGLCD.setFont(BigFont);
 myGLCD.print ("V",p1,s2);
 myGLCD.setFont(SevenSegNumFont);
 myGLCD.printNumI(i3,p2,s2);
 myGLCD.setFont(BigFont);
 myGLCD.print ("A",p3,s2);
 myGLCD.setFont(SevenSegNumFont);
 myGLCD.printNumI(kw3,p4,s2);
 myGLCD.setFont(BigFont);
 myGLCD.print ("kW",p5,s2);

// Итого киловатт
 myGLCD.setColor(VGA_AQUA);
 myGLCD.setFont(SevenSegNumFont);
 myGLCD.printNumI(kw,p4,s3);
 myGLCD.setFont(BigFont);
 myGLCD.print ("kW",p5,s3);
 


delay (5000);
}



void volt_faza (int port){
  while (i<=100){
  delay(20);
//  analogReference(DEFAULT);  
  val = analogRead(port);
  if (val<=val_min){
    val_min=val;
  }
  if (val>=val_max){
    val_max=val;
  }
  i=i+1;
}
val_sr=(val_min+val_max)/2;
volt=(5*val_sr)/1023;

i=1;
val_min=1023;
val_max=0;
return ;
}
//----------------------------------------------------------------------------------

// Функция передачи
void transmit() {
       for (int i=in; i <= ik; i++){
        c1=B00110000;
        c2=B00110000;
        c3=B00110000;

//                Serial.print(out[i]);  
//        Serial.println("   "); 
        expand(out[i]);  

      
      swSer.write(c1);              // sends one byte

         swSer.write(c2);              // sends one byte

            swSer.write(c3);              // sends one byte

      } 
}
// Функции конвертирования
// разложение числа на цифры
void expand(int a){
  if (a >= 0 && a<10)                    // если а > =0 and a<10
  {
            convert(a);
            c3=c;
  }



  if (a >= 10 && a<100)                    // если а > =10 and a<100
  {
    a1 = a / 10;
                convert(a1);
                c2=c;
    a2 = a - a1 * 10;
                convert(a2);
                c3=c;


  }


  if (a >= 100 && a<1000)                    // если а > =10 and a<100
  {
    a1 = a / 100;
                convert(a1);
                c1=c;
    a2 = (a - a1 * 100) / 10;
                convert(a2);
                c2=c;
    a3 = a - (a2 * 10 + a1 * 100);
                convert(a3);
                c3=c;
  }
  return;
}
//конвертирование из 10-тичного в 2-чное
void convert(int b){
    switch (b)
    {
    case 0:
      c = B00110000;
      break;
    case 1:
      c = B00110001;
      break;
    case 2:
      c = B00110010;
      break;
    case 3:
      c = B00110011;
      break;
    case 4:
      c = B00110100;
      break;
    case 5:
      c = B00110101;
      break;
    case 6:
      c = B00110110;
      break;
    case 7:
      c = B00110111;
      break;
    case 8:
      c = B00111000;
      break;
    case 9:
      c = B00111001;
      break;
    }

return;
}

 

Скетч для ESP8266.

 

// Wemos D1 R2
// Запись в  data.sparkfun.com
// NodeMCU 1.0 (ESP12-E Module)
//ESPino (ESP-12 Module)
// IDE 1.6.8
// 11.05.2016
#include <ESP8266WiFi.h>

const char* SSID     = "хххххххххх";
const char*  password = "хххххххххх";

const char* host = "data.sparkfun.com";
const char* streamId   = "ххххххххххх";
const char* privateKey = "ххххххххххх";


#include <SoftwareSerial.h>

SoftwareSerial swSer(D1, D2, false, 256);// RX, TX

int ret=0; // признак обрыва
byte y;
int i=0;
int k=0;
byte yy[3]={0,0,0};
int outint=0;
int ii[3]={0,0,0};
float out[6]={0.00,0.00,0.00,0.00,0.00,0.00};
int value1 = 00.00;
int value2 = 00.00;
int value3 = 00.00;
int value4 = 00.00;
int value5 = 00.00;
int value6 = 00.00;

void setup() {
  Serial.begin(115200);
  swSer.begin(115200);
  swSer.flush();
// delay(10);
    connection();
//    WiFiTransmitter();
}


void loop() {

  receiveSer();
// WiFiTransmitter();
}
//----------------------------------WiFiTransmitter
void WiFiTransmitter(){
  Serial.print("WiFiTransmitter - connecting to  ");
  Serial.println(host);
 
  // Use WiFiClient class to create TCP connections
  WiFiClient client;
const int httpPort = 80;
  if (!client.connect(host, httpPort)) {
    Serial.println("connection failed");// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    connection();
    ret=1;
    return;
  }
//        1                2                3               4                5                 6  
// a_phase_current, a_phase_voltage, b_phase_current, b_phase_voltage, c_phase_current, c_phase_voltage
  
  // We now create a URI for the request
  String url = "/input/";
  url += streamId;
  url += "?private_key=";
  url += privateKey;
  url += "&a_phase_current=";
  url += value1;
  url += "&a_phase_voltage=";
  url += value2;
  url += "&b_phase_current=";
  url += value3;
  url += "&b_phase_voltage=";
  url += value4;
    url += "&c_phase_current=";
  url += value5;
    url += "&c_phase_voltage=";
  url += value6;

    
  Serial.print("Requesting URL: ");
  Serial.println(url);
  
  // This will send the request to the server
  client.print(String("GET ") + url + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" + 
               "Connection: close\r\n\r\n");

 
                             
  unsigned long timeout = millis();
  while (client.available() == 0) {
    if (millis() - timeout > 5000) {
      Serial.println(">>> Client Timeout !");
      client.stop();
      return;
    }
  }
  
  // Read all the lines of the reply from server and print them to Serial
  while(client.available()){
    String line = client.readStringUntil('\r');
    Serial.print(line);
  }
  
  Serial.println();
  Serial.println("closing connection");
  
}


// -------------------------------- function receive
void receive(){
yy[i]=y;
//          Serial.print(yy[i],BIN); 
//                        Serial.print("  ");  
   i=i+1;
  if (i>2){
//                        Serial.println("  ");  
// Вывод результата в int
// 
    BinToInt();
//     Serial.println(outint); 
      out[k]=outint;
/*
                 Serial.print("out("); 
                  Serial.print(k);
                           Serial.print(")=");
                                  Serial.println(out[k],2); 
                                  */
      k=k+1;
      if(k>5){    // Готовый результат !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!1
                 Serial.print(out[0],2); 
        Serial.print("   "); 
                Serial.print(out[1],2); 
        Serial.print("   "); 
                Serial.print(out[2],2); 
        Serial.print("   "); 
                Serial.print(out[3],2); 
        Serial.print("   "); 
                Serial.print(out[4],2); 
        Serial.print("   "); 
                Serial.print(out[5],2);  
        Serial.println("   "); 
         value1 = out[0];
                  value2 = out[1];
                           value3 = out[2];
                                    value4 = out[3];
                                             value5 = out[4];
                                                      value6 = out[5];

        WiFiTransmitter(); // Отправка на SparkFun --------------------------------------------
//while (ret == 1) {
//   connection();
//   WiFiTransmitter();
//}

          swSer.flush();
        out[0]=0.0; out[1]=0.0; out[2]=0.0; out[3]=0.0; out[4]=0.0; out[5]=0.0; 
        k=0;
      }
      i=0; 
    ii[0]=0; ii[1]=0; ii[2]=0;   
  }          
}


// Прием данных от измерителя по последовательному порту  receiveSer()

void receiveSer() {
  
  while (swSer.available()) { // loop through all but the last
  
         byte x = swSer.read();   
//            Serial.print("receiveSer     "); 
//              Serial.println(x,BIN);  
                                    y=x;
                                     receive();
                      
  }
}
void BinToInt(){

for (int j=0; j <= 2; j++){

      switch (yy[j])
    {
          case B00110000:
      ii[j] = 0;
      break;
          case B00110001:
      ii[j] = 1;
      break;
          case B00110010:
      ii[j] = 2;
      break;
          case B00110011:
      ii[j] = 3;
      break;
    case B00110100:
      ii[j] = 4;
      break;
    case B00110101:
      ii[j] = 5;
      break;
    case B00110110:
      ii[j] = 6;
      break;
    case B00110111:
      ii[j] = 7;
      break;
    case B00111000:
      ii[j] = 8;
      break;
    case B00111001:
      ii[j] = 9;
      break;
    }
  }
 outint=ii[0]*100+ii[1]*10+ii[2]; 
     yy[0]=0; yy[1]=0; yy[2]=0;
}
//-------------------------------------------------------------- CONNECTION-------
void connection(){
   // We start by connecting to a WiFi network

//  Serial.println();
//  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(SSID);
  WiFi.mode(WIFI_STA);
 WiFi.printDiag(Serial);
  WiFi.begin(SSID, password);
 WiFi.printDiag(Serial);  
  while (WiFi.status() != WL_CONNECTED) {
    delay(5000);
      Serial.println("NO Connecting to ");
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("WiFi connected");  
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP()); 
}

 

axill
Offline
Зарегистрирован: 05.09.2011

Здорово! Зачетный проект

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

один момент увидел. У вас в цепях измерения тока и напряжения электролиты, значит замеры у вас идут не по RMS

При индуктивных нагрузках без RMS могут быть довольно большие ошибки в расчетах, офисная техника это как раз сплош и рядом индуктивные нагрузки

RN6LJK
Offline
Зарегистрирован: 24.03.2013

axill пишет:

При индуктивных нагрузках без RMS могут быть довольно большие ошибки в расчетах, офисная техника это как раз сплош и рядом индуктивные нагрузки

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

faeton
faeton аватар
Offline
Зарегистрирован: 21.03.2016

axill пишет:

офисная техника это как раз сплош и рядом индуктивные нагрузки

Разве?

faeton
faeton аватар
Offline
Зарегистрирован: 21.03.2016

Код написан кошмарнейшим образом...

RN6LJK
Offline
Зарегистрирован: 24.03.2013

как минимум 70% нагрузки в офисе это климатическое оборудование, а это разве не индуктивная нагрузка?

RN6LJK
Offline
Зарегистрирован: 24.03.2013

faeton пишет:

Код написан кошмарнейшим образом...

не на продажу, а для души

но такой отзыв бодрит

быдло код, согласен

Бил, который Гейтс тоже сказал мне, что с таким кодом в Майкрософт не примет, вот я и маюсь тут с Ардуинами :-))

axill
Offline
Зарегистрирован: 05.09.2011

RN6LJK пишет:

как минимум 70% нагрузки в офисе это климатическое оборудование, а это разве не индуктивная нагрузка?

самая что ни на есть индуктивная ибо в основном там потребляют электромоторы

axill
Offline
Зарегистрирован: 05.09.2011

faeton пишет:

Разве?

Не индуктивная нагрузка, а если правильнее говорить не реактивная, то есть активная это чайники, конвекторы, разного рода нагреватели и лампы накаливания. По большому счету этим заканчивается перечень чисто активных потребителей

RN6LJK
Offline
Зарегистрирован: 24.03.2013

Главное в том, что теперь я в любой момент и откуда угодно вижу электрическую картину в нашем офисе.

При условии надежного интернета с передающей стороны.