Arduino Mega + Меркурий 203.2Т RS485
- Войдите на сайт для отправки комментариев
Ср, 25/09/2019 - 17:37
Добрый день! Пытаюсь считать данные со счетчика Меркурий посредством 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);
что за модуль тройка вы используете для обмена? модулей тройка существует десятки
RS-485 troyka
http://wiki.amperka.ru/%D0%BF%D1%80%D0%BE%D0%B4%D1%83%D0%BA%D1%82%D1%8B:troyka-rs485
на данном модуле мигает только индикация Tx, Rx молчит! Подключение к счетчику верное, проверено на usb конверторах.
надеюсь, подключали не по этой картинке ? :)
а если серьезно - от того, что вы записали пакет в буфер - при чтении он считываться не должен. Раз у вас считывается - похоже данные реально возвращаются обратно. Толи этот "тройка-модуль" так настроен, толи счетчик Меркуоий пакеты футболит
Похоже что по этой картинке) это плохо?
Вообще сам меркурий не должен футболить посылки, ибо такую же ровно посылку от проги принимает на ура и отвечает что надо.
Похоже что по этой картинке) это плохо?
ну там какая-то откровенная фигня нарисована. На левой колодке провода красный и черный не имеют ни малейшего отношения к VCC и GND. Средний контакт зачем-то заведен на пин ардуино, когда он на модуле не используется.
но вообще я с этим модулем дела не имел, может кто другой подскажет
а попробуйте подключить модуль тройка к проге и принять тот пакет, что посылает программа
а попробуйте подключить модуль тройка к проге и принять тот пакет, что посылает программа
Интересная идея! Подключил. с компа отправляю пакет, на тройке мигает Rx, правда в мониторе порта ничего не видно...
похоже что то не так с подключением!
имхо, если говорить про шину 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);Опирался на эту статью: 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; }