Arduino Mega + Меркурий 203.2Т RS485

santi90
Offline
Зарегистрирован: 25.09.2019

Добрый день! Пытаюсь считать данные со счетчика Меркурий посредством Arduino Mega
Для обмена использую модуль troyka.
Пока пробую взять статичный пакет запроса, заведомо рабочий вариант, который опробован для связи со счетчиком. Не могу понять, почему при считывании я получаю тот же пакет, что и отправил. Точнее  логически понимаю, ведь я по сути сам же отправляю этот набор в буфер и потом пытаюсь считать что то из буфера, а там отправленный висит. Только вопрос в том, как же считать ответные данные от счетчика? 

 

Вот скетч:

byte inComing [30];

byte  byteReceived; 

void setup() {
  // put your setup code here, to run once:
Serial3.begin(9600);
Serial.begin(9600);
}

void loop() {
  // put your main code here, to run repeatedly:
byte buf[7]={0x02,0x54,0x3F,0xF5,0x27,0x7A,0xB6};

for (int i = 0;i<7;i++)
{
  Serial3.write(buf[i]);
}

byte i =0;
delay(500);
 if (Serial3.available() > 0) {  //если есть доступные данные
       while (Serial3.available()) {
      
       Serial.print(Serial3.read());
       
       delay(100);
      }
      }
delay(1000);
 

 

b707
Offline
Зарегистрирован: 26.05.2017

что за модуль тройка вы используете для обмена? модулей тройка существует десятки

santi90
Offline
Зарегистрирован: 25.09.2019
santi90
Offline
Зарегистрирован: 25.09.2019

на данном модуле мигает только индикация Tx, Rx молчит! Подключение к счетчику верное, проверено на usb конверторах. 

b707
Offline
Зарегистрирован: 26.05.2017

надеюсь, подключали не по этой картинке ? :)

 

а если серьезно - от того, что вы записали пакет в буфер - при чтении он считываться не должен. Раз у вас считывается - похоже данные реально возвращаются обратно. Толи этот "тройка-модуль" так настроен, толи счетчик Меркуоий пакеты футболит

 

 

santi90
Offline
Зарегистрирован: 25.09.2019

Похоже что по этой картинке) это плохо?

Вообще сам меркурий не должен футболить посылки, ибо такую же ровно посылку от проги принимает на ура и отвечает что надо.

b707
Offline
Зарегистрирован: 26.05.2017

santi90 пишет:

Похоже что по этой картинке) это плохо?

ну там какая-то откровенная фигня нарисована. На левой колодке провода красный и черный не имеют ни малейшего отношения к VCC и GND. Средний контакт зачем-то заведен на пин ардуино, когда он на модуле не используется.

но вообще я с этим модулем дела не имел, может кто другой подскажет

Цитата:
Вообще сам меркурий не должен футболить посылки, ибо такую же ровно посылку от проги принимает на ура и отвечает что надо.

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

 

santi90
Offline
Зарегистрирован: 25.09.2019

b707 пишет:

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

Интересная идея! Подключил. с компа отправляю пакет, на тройке мигает Rx, правда в мониторе порта ничего не видно...

santi90
Offline
Зарегистрирован: 25.09.2019

похоже что то не так с подключением!

MaksVV
Offline
Зарегистрирован: 06.08.2015

имхо, если говорить про шину rs485, то это нормальное явление - видеть в приемнике,  то что отправил. Как вариант, сразу вычитывать буфер после каждого отправленного байта, чтобы на приёмнике это эхо не мешало. 

byte inComing [30];

byte  byteReceived; 

void setup() {
  // put your setup code here, to run once:
Serial3.begin(9600);
Serial.begin(9600);
}

void loop() {
  // put your main code here, to run repeatedly:
byte buf[7]={0x02,0x54,0x3F,0xF5,0x27,0x7A,0xB6};

for (int i = 0;i<7;i++)
{
  Serial3.write(buf[i]);
  delayMicroseconds(250);
  Serial3.read();
}

byte i =0;
delay(500);
 if (Serial3.available() > 0) {  //если есть доступные данные
       while (Serial3.available()) {
      
       Serial.print(Serial3.read());
       
       delay(100);
      }
      }
delay(1000);

 

Joog
Offline
Зарегистрирован: 03.01.2020

Опирался на эту статью: https://habr.com/ru/post/339868/  Чем для меня плоха связь по485- 1)он под пломбой, 2)если есть система, то её нужно временно отключать для своей поделки, 3) надо знать сетевой номер. Я по оптике сначала опрашиваю прибор по 0 адресу и узнаю его сетевой, закрываю сессию по 0 и открываю по сетевому, потом уже опрос. Читаю Меркурий234 по оптопорту: 9600 8N1, протокол обмена практически одинаков для всех меркуриев (сейчас этим же устройством опрашиваю и СЭТ4ТМ, разница- 9600 8O1, и в старых версиях по 40 "AA" и 40 "66" отправлять, команды практически те же самые). Самая большая сложность -разместить соосно свето/фотодиоды и настроить их чувствительность. На порт-3 последовательно: 1светодиод для контроля передачи, резистор 100 Ом, IR светодиод от старого ТВ пульта и всё на +5v. На порт-2: фототранзистор от компьютерной мышки и эмиттерный повторитель BC547 с контрольным светодиодом (проверял пультом от телевизора по морганию). 3 кнопки управления:1-Наложить маску отображаемых параметров(мне не нужны тарифы, а счётчиков порядка 40шт); 2-показания Актив/Реактив- вчера 0 часов, сегодня 0 часов, сейчас; 3-параметры электросети пофазно (правда всё это можно посмотреть и на экране счётчика): напряжения В, токи мА, углы между напряжениями, угол между током и напряжением (cos F). В планах подключить цветной дисплей и выводить векторной диаграммой.

#include <PCD8544.h>                           //библа 5110 переназначить выводы в хедере
#include <SoftwareSerial.h>                    //софтовый СОМ

static PCD8544 lcd;                            //Инициализация

//-------- порты передачи
#define SSerialRx        2                      // приём софтовый СОМ
#define SSerialTx        3                      // передача софтовый СОМ
SoftwareSerial irSerial(SSerialRx, SSerialTx);  // инициализация софтовый СОМ

const int buttonPin_1 = 5;                      //Кнопка КН1 на РD5 (на любой)
int buttonState_1 = 0;                          //установка статуса кнопки
const int buttonPin_2 = 4;                      //Кнопка КН2 на РD4 (на любой)
int buttonState_2 = 0;                          //установка статуса кнопки
const int buttonPin_3 = 6;                      //Кнопка КН3 на РD6 (на любой)
int buttonState_3 = 0;                          //установка статуса кнопки

///////////////////// команды//////////////////////////////////////////////////////
byte testConnect[] = { 0x00, 0x00 };             //тест связи
byte Sn[]          = { 0x00, 0x08, 0x05 };       //запрос сетевого адреса
byte Open[]        = { 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}; //открыть канал с паролеи
byte Close[]       = { 0x00, 0x02 };             // закрыть канал (а то ждать 4 минуты)
byte activPower[]  = { 0x00, 0x05, 0x00, 0x00 }; //суммарная энергия прямая + обратная + активная + реактивная
byte ind[]         = { 0x00, 0x03, 0x01, 0x01, 0x00, 0x01, 0x00, 0x1F, 0x00, 0x1F, 0x00 }; //маска индикации работает только для автоматического режима
byte Angle[]       = { 0x00, 0x08, 0x16, 0x51 }; // углы между фазами 1и2, 1и3, 2и3

byte response[19];                               // приняные байты
int byteReceived;                                //переменная принятый байт
int netAdr;                                      //переменная хранения сетевого адреса устройства
char tmp[32];

void setup() {  
lcd.begin(84, 48);                               // Задаём размеры экрана. 
pinMode(buttonPin_1, INPUT_PULLUP);              //инициалиация пина под кнопку
pinMode(buttonPin_2, INPUT_PULLUP);              //инициалиация пина под кнопку
pinMode(buttonPin_3, INPUT_PULLUP);              //инициалиация пина под кнопку

irSerial.begin(9600);
  lcd.clear();                                   //полная очистка дисплея
  lcd.setCursor(55, 0);
  lcd.print("Mask-");
  lcd.setCursor(55, 1);
  lcd.print("DATE-"); 
  lcd.setCursor(49, 2);
  lcd.print("PARAM-"); 
}

void loop()////////////////// основной цикл ////////////////////////
{  
  int Vop= ((1.1*15320)/Vbg())*10 ;          //измерение напр. питания
  lcd.setCursor(0, 5);                      //Установка курсора
  sprintf (tmp, "%u.%uv", Vop/10, Vop%10);   //Вывод напряжения батареи
  lcd.print(tmp); 
  
  
  buttonState_1 = digitalRead(buttonPin_1);  //установка для чтения кнопки
  if (buttonState_1 == LOW)                  //если кнпка нажата, то...
  {
    lcd.clear();                            //полная очистка дисплея
    readSN();                               //подпрограмма чтения серийника и открытия канала
    dateSN(0xD0,1);                         // показания на начало вчерашних суток и строка экрана
    dateSN(0xC0,2);                         // показания на начало сегодняшних суток и строка экрана
    dateSN(0x00,3);                         // показания на сейчас и строка экрана
    closeSN();                              //закрытие канала
  }
  
  buttonState_2 = digitalRead(buttonPin_2);  //установка для чтения кнопки
  if (buttonState_2 == LOW)                  //если кнпка нажата, то...
  {
    lcd.clear();                            //полная очистка дисплея
    readSN();                               //подпрограмма чтения серийника и открытия канала
    ind[0] = netAdr;                        //подставить в 0 байт посылки сетевой адрес
    response[0] = 0;                        //обнуляем ответный байт для проверки связи  
    send(ind, sizeof(ind), response);       //Записываем маску индикации
    if(response[0] == netAdr)
      {
        lcd.setCursor(0, 1);
        lcd.print(" Mask-ok");
      }       
    delay(100); //настроечная пауза  
    closeSN();                              //закрытие канала
  } 
  
  buttonState_3 = digitalRead(buttonPin_3);  //установка для чтения кнопки
  if (buttonState_3 == LOW)                  //если кнпка нажата, то...
  {
    lcd.clear();                            //полная очистка дисплея
    readSN();                               //подпрограмма чтения серийника и открытия канала
    paramSN(0x11, 1, 100, 0);               //0x11 напряжения пофазно,1строка,100делитель, 0-есть знак после запятой
    paramSN(0x21, 2, 1, 1);                //0x21 токи пофазно 
    paramSN(0x51, 3, 100, 1);              //0x51 углы между напряжениями фаз
    cosSN();                               //0x31 углы между напряжениями фазы и током COSf   
    closeSN();                              //закрытие канала
  } 
  
  
}
///////////подпрограмма чтения серийного номера и открытия по нему канала//////////////////////////
void readSN()
 {
  ReLoad:
     response[0] = 0;
     send(Open, sizeof(Open), response);     //открываем канал связи по 0 адресу
     if(response[0] == 0)
      {
        send(Sn, sizeof(Sn), response);        //читаем сетевой адрес счётчика
        lcd.setCursor(0, 0);
        lcd.print("Sn: ");
        netAdr = (response[2]);
        lcd.print(netAdr);        
      }
     else
      {
        lcd.setCursor(0, 0);
        lcd.print("FAIL");
        delay(100);
        goto ReLoad;                           //повтор открытия по 0 и чтения адреса
      }    
   delay(100); 
   send(Close, sizeof(Close), response);       //закрыть канал связи по адресу 0
   delay(500);                                 //настроечная пауза

  Open[0] = netAdr;                             //подставить 0 байтом сетевой адрес
  response[0] = 0;                              //обнуляем ответный байт для проверки связи
  send(Open, sizeof(Open), response);
  if(response[0] == netAdr && response[0] != 0) //Если равен адресу И не равен 0
      {
        lcd.print(" OK");
      }     
  delay(30);                                    //настроечная пауза 
 }
 
////////////////////////подпрограмма чтения показаний счётчика//////////////////////////////////// 
int dateSN(byte paramA, byte paramB)
{
  response[0]=0;
  activPower[0] = netAdr;
  activPower[2] = paramA;                         // показания на начало вчерашних суток 0xD0
  send(activPower, sizeof(activPower),response);
  if(response[0] == netAdr && response[0] != 0) //Если равен адресу И не равен 0
   {  
    unsigned long r = 0;
    r |= (unsigned long)response[2]<<24;
    r |= (unsigned long)response[1]<<16;
    r |= (unsigned long)response[4]<<8;
    r |= (unsigned long)response[3];
    /*  
    писать сюда если response[0] не равен 0 тогда записывать в память
    */
    lcd.setCursor(0, paramB);
    sprintf (tmp, "%lu.%lu", r/1000, r%100); //А вчерашний
    lcd.print(tmp);  
    r = 0;
    r |= (unsigned long)response[10]<<24;
    r |= (unsigned long)response[9]<<16;
    r |= (unsigned long)response[12]<<8;
    r |= (unsigned long)response[11];
    lcd.setCursor(42, paramB);
    sprintf (tmp, "%lu.%lu", r/1000, r%100); //R вчерашний
    lcd.print(tmp);
   }; 
  }   
/////////////////////////углы, напряжения токи по фазам////////////////////////////////////////////////// 
int paramSN(byte paramC, byte paramD, byte paramE, byte paramF)  //paramC-0x11 напряжение,0x21 ток,0x51 межфазные углы
{                                      //paramD смещение по горизонтале на дисплее
  response[0]=0;                       //paramE и paramE делитель числа для правильного отображения
  Angle[0] = netAdr;
  Angle[3] = paramC;
  send(Angle, sizeof(Angle),response);
  long r = 0;
  r |= (long)response[1]<<16;
  r |= (long)response[3]<<8;
  r |= (long)response[2];
    lcd.setCursor(0,paramD);
    if(paramF==0){
      sprintf (tmp, "%lu.%lu", r/100, r%10); //А фаза напряжение
      lcd.print(tmp);
    }
    else{
      sprintf (tmp, "%lu", r/paramE); //А фаза ток и угол
      lcd.print(tmp);
    }
  r = 0;
  r |= (long)response[4]<<16;
  r |= (long)response[6]<<8;
  r |= (long)response[5];
   lcd.setCursor(32,paramD);
    if(paramF==0){
      sprintf (tmp, "%lu.%lu", r/100, r%10); //В фаза напряжение
      lcd.print(tmp);
    }
    else{
      sprintf (tmp, "%lu", r/paramE); //В фаза ток и угол
      lcd.print(tmp);
    }
  r=0;
  r |= (long)response[7]<<16;
  r |= (long)response[9]<<8;
  r |= (long)response[8];
   lcd.setCursor(60,paramD);
    if(paramF==0){
      sprintf (tmp, "%lu.%lu", r/100, r%10); //С фаза напряжение
      lcd.print(tmp);
    }
    else{
      sprintf (tmp, "%lu", r/paramE); //С фаза ток и угол
      lcd.print(tmp);
    }
}
/////////////////////////COSf по фазам////////////////////////////////////////////////// 
int cosSN()  //paramC-0x11 напряжение,0x21 ток,0x51 межфазные углы
{                                      //paramD смещение по горизонтале на дисплее
  response[0]=0;                       //paramE и paramE делитель числа для правильного отображения
  Angle[0] = netAdr;
  Angle[3] = 0x30;
  send(Angle, sizeof(Angle),response);
  long r = 0;
  r |= (long)response[4]<<16;
  r |= (long)response[6]<<8;
  r |= (long)response[5];
    lcd.setCursor(0,4);
   sprintf (tmp, "%lu.%lu", r/1000, r%100); //С фаза напряжение
   lcd.print(tmp);
  r = 0;
  r |= (long)response[7]<<16;
  r |= (long)response[9]<<8;
  r |= (long)response[8];
   lcd.setCursor(32,4);
   sprintf (tmp, "%lu.%lu", r/1000, r%100); //С фаза напряжение
   lcd.print(tmp);
  r=0;
  r |= (long)response[10]<<16;
  r |= (long)response[12]<<8;
  r |= (long)response[11];
   lcd.setCursor(60,4);
   sprintf (tmp, "%lu.%lu", r/1000, r%100); //С фаза напряжение
   lcd.print(tmp);
}

  
///////////////////////подпрограмма закрытия канала///////////////////////////////
void closeSN()
{
  Close[0] = netAdr;                    //подставить 0 байтом сетевой адрес
  send(Close, sizeof(Close), response); //закрыть канал связи
  //netAdr = 0;
  delay(500);                           //настроечная пауза
}

///////////////////подпрограмма чтения напряжения питания///////////////////////
int Vbg() 
{  
ADMUX = (1<<REFS0)|(0<<REFS1)|(1<<MUX3)|(1<<MUX2)|(1<<MUX1)|(0<<MUX0);
long buffersamp=0;
for (int n=0x0; n<=0xff; n++ ) {
ADCSRA |= (1<<ADSC)|(1<<ADEN);  //Starts a new conversion
while (bit_is_set(ADCSRA,ADSC));
buffersamp += ADC; }
buffersamp >>=4; //16368 full scale 14bit
ADCSRA &= ~(1 << ADEN);  // отключаем АЦП
return buffersamp;
 }


/////////////////////подпрограмма посылки и приёма ///////////////////////////
void send(byte *cmd, int s, byte *response)// посылка: запрос
{
  unsigned int crc = crc16MODBUS(cmd, s);
  unsigned int crc1 = crc & 0xFF;
  unsigned int crc2 = (crc>>8) & 0xFF;
  delay(10);
       for(int i=0; i<s; i++) 
       {
              irSerial.write(cmd[i]); // посылка побайтно
       }
  irSerial.write(crc1);               //отправка мл.байта CRC
  irSerial.write(crc2);               //отправка ст.байта CRC
  byte i = 0;

  delay(20);
         if (irSerial.available()) //пришло в сом, начинаем собирать
           {
             while (irSerial.available()) 
               {
                byteReceived= irSerial.read();    // Read received byte 
                 //delay(10); 
                response[i++] = byteReceived;
                }   
           } 
}
/////////////////////////////////////////////////////////////////////////


////////////////////// вычисление кортрольной суммы /////////////////////
unsigned int crc16MODBUS(byte *s, int count) {
  unsigned int crcTable[] = {
        0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
        0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
        0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
        0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
        0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
        0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
        0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
        0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
        0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
        0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
        0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
        0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
        0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
        0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
        0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
        0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
        0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
        0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
        0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
        0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
        0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
        0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
        0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
        0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
        0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
        0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
        0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
        0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
        0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
        0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
        0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
        0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040
    };    
    unsigned int crc = 0xFFFF;
    for(int i = 0; i < count; i++)
    {
        crc = ((crc >> 8) ^ crcTable[(crc ^ s[i]) & 0xFF]);
    }
    return crc;
}