Измерение качества электроэнергии в садовом товариществе на основе ABB dmtme-i-485-96

Тема в разделе "Глядите, что я сделал", создана пользователем FigaroVL, 21 июл 2017.

Метки:
  1. FigaroVL

    FigaroVL Нуб

    Добрый день всем. Хочу попробовать себя в области контроллеров и реализовать довольно сложный с моей точки зрения проект. Это мой первый проект на Arduino, надеюсь он завершиться успешно, а не будет заброшен. Планирую сюда размещать результаты по ходу проекта, свои мысли и проблемы с которыми столкнулся (Надеюсь модераторы не будут против). Я обладаю некоторыми навыками работы с паяльником и знаниями языков программирования.

    Итак приступим, сначала опишу задачу:
    В СНТ есть КТП - трансформаторная подстанция, которая нас питает, там помимо счетчика (который управляется мосэнергосбытом по GSM модему), я планирую установить цифровой мультиметр
    ABB dmtme-i-485-96, который обладает интерфейсом RS485 и может передавать напряжения, токи, мощности и ряд других параметров. Мой участок с компьютером, очевидно, является самым дальним от КТП:) - порядка 1км. В результате родился список функций:
    1. Ежесекундно читать информацию с мультиметра ABB и записывать их на флешку в CSV файл с указанием даты/времени и набора показателей качества сети.
    2. Организовать мониторинг текущих значений на переносном устройстве по радиоканалу.
    3. Обеспечить скачивание CSV файлов с флешки в КТП на компьютер (FTP или SMB) по радиоканалу через переносное устройство.
    4. Периодически корректировать часы на устройстве в КТП по радиоканалу, информацию брать от NTP сервера.

    Вчера закупил следующее оборудование, сегодня надеюсь привезут.
    Для устройства в КТП:
    1. Iskra Mini (без ног)
    2. Приёмопередатчик RS-485 (Troyka-модуль)
    3. SD картридер (Troyka-модуль)
    4. Радиомодуль NRF24L01+ с антенной (1100 м)
    5. Забыл - еще нужны - Часы реального времени (Troyka-модуль)

    Для переносного устройства:
    1. Arduino Uno R3 совместимая
    2. LCD keypad shield
    3. Ethernet W5100
    4. Радиомодуль NRF24L01+ с антенной (1100 м)

    Думаю, еще что забыл, но докупить не проблема.

    Чуть позже размещу пост с планом действий.
     
  2. koteika

    koteika Гик

    Качеств электроэнергии около 12 штук. Начиная от всяких пределов отклонений напряжений и частоты, заканчивая гармониками и дозой фликера.
    Вот гост на это http://www.energometrika.ru/product_files/512/GOST_R_54149-2010.pdf Если интересно почитайте)
     
  3. Tomasina

    Tomasina Сушитель лампочек Модератор

    ТС, проблема в том, что ты делаешь показометр различных величин, показания которого не имеют юридической силы при разборках. Для личных нужд сойдет, а при необходимости предъявить претензии в инстанциях потребуют показания сертифицированного прибора.
     
  4. FigaroVL

    FigaroVL Нуб

    Спасибо, про качество почитаю.

    Для юридических разборок система и не планировалась. Планировалось, посмотреть график суточного потребления в разные периоды года (лето, зима, весна/осень) - вдруг что интересное покажет, со счетчика можно снять только двухтарифный режим и средние показания за месяц, а мне интересно пиковые. Ну и в большей степени это проект для себя - разобраться с микроконтроллерами. Вдруг потом прикручу к ним еще запуск бензинового генератора и управление АВР'ом у себя.

    Я основываюсь на возможностях мультиметра ABB, вот его описание протокола ModbusRTU, там перечислены что он может отдавать http://www.symmetron.gr/uploads/dmtme_modbus_protocol_1.2b.pdf
    Интересны только основные "дилетантские": Напряжения, Токи, Мощность активная, суммарно, в разрезе фаз и пиковые значения.

    Решил посчитать какой объем данных получиться на флешке, строка CSV файла будет содержать 10 байт даты/времени, 35 параметров по 2 байта каждый, 36 разделителей "," и два байта перевод каретки/новая строка - итого 118 байт на строчку. Если каждую секунду записывать, то за сутки объем файла составит 60х60х24х118= 9 956 Кбт или 9,7 Мбт. Хммммм, что-то многовато для одного файла. Понятно, что файлы придется дробить на почасовые, иначе по радиоканалу не пропихну на сервер - в "идеальных" условиях 256 Кбит канал, передача файла займет около 350 секунд, что мне кажется многовато. На флешку 8Гбт влезет файлов примерно на 2 года, только по каталогам надо распихивать.
     
  5. FigaroVL

    FigaroVL Нуб

    Железочки приехали. Подключил Arduino, проверил работу монитора и SD карточки на модуле Ethernet W5100.
    Обнаружил одну неприятность, красивого пирога из коробки: Arduino Uno сверху Ethernet W5100 и еще выше LCD keypad shield не получается. длины штекеров на LCD keypad shield не хватает, плата упирается в разъем RJ45. Их придется удлинять, плюс есть подозрение что они будут "конфликтовать" по пинам. Буду смотреть, в понедельник привезут ABB мультиметр.
    Так и есть, 4й пин данных задействован для дисплея и включения SD карты.
    Все железочки:
    [​IMG]
    Пирог - видно как контактами дисплей цепляет за RJ45
    [​IMG]

    [​IMG]
     
    Последнее редактирование: 21 июл 2017
  6. FigaroVL

    FigaroVL Нуб

    Сегодня во второй половине дня попробовал оживить первый тестовый стенд: Arduino UNO + RS485
    Что сделано (пока в не потребном виде, поэтому фотки и код опубликую позже):
    1. Подключил RS485 к 5,6,7 пину. т.к. не купил отдельный USB-Serial для мониторинга и отладки программы. Придется использовать программый последовательный порт.
    2. Поставил библиотеки SoftSerial + ModubusRTU. Обнаружил, что штатный ModbusRTU штатно не живет в режиме RS485 на SoftSerial - пришлось немного подправить библиотеку:rolleyes:, теперь живет:) Опыт программиста помагает сильно.
    3. Написал тестовый скетч, немного помучался. В ModbusRTU вызывал просто метод Begin, а надо было вызываеть его переопределение для SoftSerial. Все глючило, пока создание виртуального порта не перенес в setup, а переменную указателя на класс не сделал static. Теперь она совершенно безболезненно что "одновременно" шлет и в отладочное окно и на RS485.
    4. Подключил ABB мультиметр (у меня на даче в электрощите стоит для себя) по RS485. Пытаюсь послать ему "телеграмму", а он в ответ молчит. Электрически, вроде как, сигналы доходят - на мультиметре на портах AB в момент передачи напряжение меняется. Жалею, что не купил переходник USB-RS485 для компа - понять в чем дело было бы значительно легче. Но светодиод RX на модуле Тройка RS485 не моргает, и на АББ также нет индикации приема/передачи по RS485. Повозился немного и решил что хватит на сегодня - пойду спать.

    P.S. Есть глубокае подозрение, что дело в четности и стоп-битах на RS485. Эх монитор для RS485 нужен или осцилограф записывающий....
     
  7. FigaroVL

    FigaroVL Нуб

    После обеда привезли ABB DMTME-I-485. Собрал стенд - сразу на тестовом примере заработала передача данных по RS485. Вечер потратил на отлаживание тестового примера, передачи висла через некоторое время, не сразу разобрался с асинхронным получением ответных данных. Подбирал минимальные задержки и ожидания ответов от ABB, при которых программа не затыкается, кол-во регистров ABB которые можно прочитать за раз. Но все получилось. Оставил стенд на ночь для тестирования устойчивости.
    Вот такой стенд:
    [​IMG]

    Это изменения в бибилиотеке ModbusRtu.h:
    1. В раздел private класса Modbus добавляем новое переопределение процедуры init с 2мя параметрами
    Код (C++):

        void init(uint8_t u8id, uint8_t u8serno, uint8_t u8txenpin);
        void init(uint8_t u8id);
        //FigaroVL 22/07/2017
        void init(uint8_t u8id, uint8_t u8txenpin);
     
    2. Меняем конструктор класса, так что-бы при указании номера компорта более 3 он обрабатывал новую функцию
    Код (C++):

    Modbus::Modbus(uint8_t u8id, uint8_t u8serno, uint8_t u8txenpin)
    {
        if (u8serno > 3) { init(u8id, u8txenpin); } else { init(u8id, u8serno, u8txenpin); }
    }
     
    3. Ну и сама функция
    Код (C++):

    void Modbus::init(uint8_t u8id, uint8_t u8txenpin)
    {
        this->u8id = u8id;
        this->u8serno = 4;
        this->u8txenpin = u8txenpin;
        this->u16timeOut = 1000;
    }
     

    Ну и сам скретч:
    Код (C++):

    #include <SoftwareSerial.h>
    #include <ModbusRtu.h>

    #define stlPin 13
    #define RS485_TX_Pin 6  //RX RS485
    #define RS485_RX_Pin 7  //TX RS485
    #define RS485_Mode_Pin 5 //RS485 Direction Send/Recive

    #define ModbusWaitAnswerTimeOut 200

    #define ModbusBoud 9600
    #define ModbusParity "n"
    #define ModbusStopBit 1
    #define ModbusClientAddress 31

    enum  //Command
    {
        NO_COMMAND = 0,
        EXC_SEND_TELEGRAM = 1,
        EXC_WAIT_ANSWER = 2,
    };


    #define Debug 2  //Debug Level: 0 - Off, 1 - Minimal, 2 - Normal, 3 - Detail
    static SoftwareSerial* RS485Serial;
    Modbus  Master(0, 100, 5);  //Create RS485 On Sofware SerialPort - Library pacthed

    uint16_t au16data[64];
    uint8_t CurrCommand;
    unsigned long LastTimeSendTelegram;
    byte IsTelegramSend;

    modbus_t telegram;

    void setup() {
      uint8_t tempus;
    //  pinMode(RS485_Mode_Pin, OUTPUT);
      pinMode(stlPin, OUTPUT);
      pinMode(RS485_RX_Pin, OUTPUT);
      pinMode(RS485_TX_Pin, INPUT);
      if (Debug>0) Serial.begin( 57600 );
      digitalWrite(stlPin, HIGH);
      RS485Serial = new SoftwareSerial(RS485_TX_Pin, RS485_RX_Pin); // RX, TX Arduino
      Master.begin(RS485Serial, ModbusBoud);
      Master.setTimeOut( ModbusWaitAnswerTimeOut-50 ); // Кривовато - таймаут у библиотеки должен меньше чем мы ждем ответа в цикле
      CurrCommand = NO_COMMAND;
      LastTimeSendTelegram = 0;
      digitalWrite(stlPin, LOW );

      if (Debug>0) Serial.println("Initialization Done");  
    }

    void loop() {
      int qRes;
      int i;
      switch( CurrCommand ) {
        case NO_COMMAND:
          if (Debug>50) {
             Serial.print("NO_COMMAND - WAITING: "); Serial.print(millis()); Serial.print(" - "); Serial.print(LastTimeSendTelegram); Serial.print(" - "); Serial.println(IsTelegramSend);
          }
          if ((millis() > (LastTimeSendTelegram + ModbusWaitAnswerTimeOut + 50)) && (IsTelegramSend == 0)) { CurrCommand = EXC_SEND_TELEGRAM;
    //        Serial.print("NO_COMMAND - START SEND: "); Serial.print(millis()); Serial.print(" - "); Serial.print(LastTimeSendTelegram); Serial.print(" - "); Serial.println(IsTelegramSend);
          break; }//
          if ((millis() > LastTimeSendTelegram) && (millis() < (LastTimeSendTelegram + ModbusWaitAnswerTimeOut)) && (IsTelegramSend == 1)) { CurrCommand = EXC_WAIT_ANSWER; break; }
          if ((millis() > (LastTimeSendTelegram + ModbusWaitAnswerTimeOut)) && (IsTelegramSend == 1)) { IsTelegramSend = 0; break; }
          break;
        case EXC_SEND_TELEGRAM:
          if (Debug>1) { Serial.print(millis()); Serial.print(": Send Telegramm - ");}
          IsTelegramSend = 0;
          if (Master.getState() == COM_IDLE) {
            telegram.u8id = ModbusClientAddress; // slave address
            telegram.u8fct = 0x3; // function code (this one is registers read)
            telegram.u16RegAdd = 0x1024; // start address in slave
            telegram.u16CoilsNo = 0x0012; // number of elements (coils or registers) to read
            telegram.au16reg = au16data; // pointer to a memory array in the Arduino
            qRes = Master.query( telegram ); // send query (only once)
            if (Master.getState() == COM_WAITING) {
              LastTimeSendTelegram = millis();
              IsTelegramSend = 1;
              if (Debug>2) { Serial.print(IsTelegramSend); Serial.print(" - ");  Serial.print(qRes); Serial.println(" - Telegramm Send"); }        
            }
            CurrCommand = EXC_WAIT_ANSWER;
          } else {Serial.print("Telegramm NOT Send - "); Serial.println(Master.getState()); CurrCommand = NO_COMMAND;}
          break;
        case EXC_WAIT_ANSWER:
          if (Debug>2) {Serial.print(IsTelegramSend); Serial.println("Wait answer of Telegramm");}
          qRes = Master.poll(); // check incoming messages
          if (Debug>3) { Serial.print(qRes); i = Master.getState(); Serial.print(i); Serial.println(" - Answer Wait Result"); }
          if ((Master.getState() == COM_IDLE) && (qRes > 0)) {
            if (Debug>2) Serial.println("Recive answer");
            if (Debug>1) Serial.print("Result Buffer: ");
            i = 0;
            while (i<=qRes-1) {
              if (Debug>1) { Serial.print(au16data[i]); }
              i++;
            }
            if (Debug>1) Serial.print(" - "); Serial.print(i); Serial.print(" - "); Serial.print(qRes); Serial.println();
            LastTimeSendTelegram = millis();
            IsTelegramSend = 0;
          } else {
            if (millis() > LastTimeSendTelegram + ModbusWaitAnswerTimeOut-50) { IsTelegramSend = 0; Serial.println("Answer Wait TimeOut");}
         
          }
          if (Debug>2) { Serial.print(millis()); Serial.print(" - "); Serial.print(LastTimeSendTelegram); Serial.print(" - "); Serial.println(IsTelegramSend); }
          CurrCommand = NO_COMMAND;
          break;
      }
    }

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

    P.S. USB-RS485 не потребовался, а вот часы реального времени нужны. Хотя пока напишу заглушку.
     
    Последнее редактирование: 24 июл 2017
  8. FigaroVL

    FigaroVL Нуб

    Сегодня провозился почти весь день. Модуль проработал всю ночь и не повис, что радует. Отключил отладку (вывод в Serial) и обмен данными прекратился. Нашел еще место в библиотеке, которое надо исправить, при отправке буфера, библиотека ждет очистки буфера в реальном компорте в функции sendTxBuffer(), а для SoftSerial этого делать не надо.

    Код (C++):

    #if defined(UBRR3H)
            case 3:
                while (!(UCSR3A & (1 << TXC3)));
                break;
    #endif
    //FigaroVL 25.07.2017
            case 4:
                break;
    //End FigaroVL 25.07.2017
            case 0:
            default:
                while (!(UCSR0A & (1 << TXC0)));
                break;
            }

            // return RS485 transceiver to receive mode
            digitalWrite( u8txenpin, LOW );
     
    Зацепил физически модуль SD карты, но задействовать не успел. Дописывал скретч для получения всех регистров с ABB, напихал пачку отладочной инфы - и память закончилась:)
    Вспомнил, что такое оптимизация кода.
    Завтра буду продолжать.
     
  9. b707

    b707 Гуру

    Чисто практическое соображение - для данных за месяцы. а тем более годы - частота выборки раз в 1 сек очевидно избыточна. Графики будут содежать мегабайты точек , в основном "белого шума", который никто и никогда не станет анализировать. На мой взгляд, читать данные можно каждую секунду или каждые 10 сек, а вот передавать по радио и писать на карту с усреднением - для графиков будет более чем достаточно иметь среднее значение за период в 5-10 минут(а может и еще реже)
     
  10. FigaroVL

    FigaroVL Нуб

    В части избыточности данных для анализа за длительный период я - согласен, их будет слишком много, по факту надо регистрировать минимальные/максимальные значения за минуту или за 5 минут и их уже записывать. Как вариант потом возможно так и переделаю. Просто усредненные значения не интересны, т.к. вожно понимать "просадку" трансформатора по напряжению и перекосы для последующего аналаза и разбрасывания абонентов по фазам или их переключение на 3х фазное подключение. А вот передавать по радиоканалу текущие значения интересно в ситуации, когда ты приходить к абоненту тестором смотришь у него на вводе в щиток напряжение и одновременно на трансформаторе - можно косвенно понять хороший ли контакт от линии до его щитка. Хотя может я фантазирую - это все от лукавого.
     
  11. FigaroVL

    FigaroVL Нуб

    На выходных вернулся к проекту. Закупил на неделе DC-DC преобразователь TME 1209S с гальванической развязкой для своего домашнего ABB DMTME, но пока еще не получил. Я год назад переделывал его питание (убрал трансформатор и запитал от ИБП на 12вольт) и при этом отключил питание блока RS485 за ненадобностью, теперь надо восстановить. Для проекта купил преобразователь DC-DC 3.3В, 5В на основе AMS1117, часы реального времени на DS3231. Подключил и протестировал NRF24L01+ в режиме сканирования каналов (конечно с конденсаторами по питанию). Собрал "котлету" (бутербродом это назвать сложно) из Iskra Mini, приёмопередатчик RS-485, SD картридер, часы реального времени на DS3231,
    радиомодуль NRF24L01+ - отдельно на проводках с "выносом", что-бы в последствии экранировать от основной платы. Подал питание 12в на вход преобразователя - задымилась сначала AMS1117-05. Что-то пошло не так:(, стал подключать модули по отдельности к блоку питания с ограничением по току, и как результат задымился логический элемент И на плате RS485 - потребление более ампера. Как я умудрился его спалить не совсем понятно, он отвечает за моргание светодиода TX. Остальное надеюсь осталось целым, как минимум контроллер работает, а вот целы ли его входа/выходы еще предстоит определить. Вторая AMS на 3,3 вольта также сдохла. Сегодня заберу и поменяю AMS на плате преобразователя и элемент И. Потом продолжу эксперименты.
     
  12. FigaroVL

    FigaroVL Нуб

    Восстановил RS485, сгорел светодиод и элемент И, остальное оказалось цело. А модуль не работал из-за ошибки в скретче при инициализации Modbus
    Код (C++):
    static SoftwareSerial* RS485Serial;
    Modbus  Master(0, 100, 5); // Тут вместо 5 надо было указать RS485_Mode_Pin
    Номер пина (5) был прописан жестко в коде, а не как "положено" через #define RS485_Mode_Pin, вот при смене пинов и перестало работать.

    Всю неделю занимался переписыванием скретча для дальнейшего его развития. Разбил loop на отдельные функции, добавил отладочную информацию с возможностью записывать лог на SD карту или выводить в Serial, при этом сообщения для лога читаются из файла на SD карте. Завтра выложу обновленный скретч.

    Сегодня "порадовался" количеству памяти, которое съедается библиотекой "SD.h" - 786 байт просто при ее подключении - это 39%, что крайне не эффективно. Нашел две альтернативные библиотеки для работы с SD - завтра проверю, достаточно ли функционала в них и как там с потреблением памяти. У остальных библиотек потребление при подключении поменьше в районе 130-170 байт, завтра приведу результаты тестирования.
     
  13. FigaroVL

    FigaroVL Нуб

    В выходные поставил себе Visual Studio 2017 и Arduino IDE for VS, экспериментировал с различными библиотеками для SD карточки. Перепробовал:
    1. родную от производителя SD
    2. SDFS от Bill Greiman - https://github.com/greiman/SdFs
    3. PetitFS
    4. FatFS от Jean-Michel Gallego
    Все печально, на UNO памяти катастрофически не хватает, "пустая" SD занимает 786 байт, SDFS занимает 636 байт, PetitFS - всего 57, но функциональности для ведения логов никакая (в интернете много написано), FatFS собралась с горем пополам - потом обнаружил, что она для DUO и после компиляции 3127 байт занято под глобальные переменные. Бросил пока копаться. Занялся отладкой переписанного скретча, целый час тренировался с прерываниями от часов, а оказалось перепутал пин 2 аналоговый и цифровой :) Отладил чтение с SD и форматирование строчек лога. На днях доделаю запись лога на SD и причешу код по чтению Modbus из RS485 и запись данных в csv файл. Следующий этап начало мучений с nRF для передачи данных по радиоканалу. Пока прикладываю текущую версию для ознакомления.
     

    Вложения:

    • Modbus_base.ino
      Размер файла:
      27,4 КБ
      Просмотров:
      628