Arduino Polska Forum

Pełna wersja: Problem z komunikacja via RS485 ModbusRTU
Aktualnie przeglądasz uproszczoną wersję forum. Kliknij tutaj, by zobaczyć wersję z pełnym formatowaniem.
Witam, mam nadzieje, ze pisze w dobrym dziale Smile
Jakis czas temu szukałem na tym forum rekomendacji dotyczącej dwukierunkowego licznika z protokołem ModbusRTU, koniec końców zakupiłem cos takiego: https://www.fif.com.pl/pl/index.php?cont...hment=1093 (pełna dokumentacja, również z adresami poszczególnych rejestrów).

Docelowo, chcę zczytywać dane z licznika za pomocą NodeMCU v2 i konwertera UART -> RS485 by potem można było wysyłać dane na serwer. Układ połączyłem nastepujaco:

NodeMCU:        Konwerter:
3.3V           ->  3.3V
GND           ->  GND
D7              ->  RO
D8              ->  DI
D6              ->  RSE

Oraz złącze A konwertera ze złączem A licznika, analogicznie z B.


Do obsługi protokołu ModbusRTU użyłem biblioteki https://github.com/Trialcommand/ESP8266-...RTU-Master, jest ona odpowiednio zmodyfikowana by działała z ESP8266, jednak pojawiły się dwa problemy. Program nie kompilował się z racji na nadpisywanie nazw funkcji z oryginalnej biblioteki SoftwareSerial, dla świętego spokoju pozmieniałem nazwy plików nagłówkowych; oprocz tego, zdałem sobie sprawę, że musze jakoś obsluzyc konwerter z którego korzystam - wymaga on podania stanu wysokiego na pin RST by wysylac dane oraz stanu niskiego by nasłuchiwać. Poczyniłem zatem parę modyfikacji w bibliotece, są one opisane i oznaczone w plikach.


Teoretycznie wszystko powinno być ok, jednak dalej nie jestem wstanie skomunikować się z licznikiem. Biblioteka ma pewną obsługę błędów i po wgraniu następującego programu:
Kod:
#include "ModbusMaster232.h"
#include "SoftwareSerialForESP.h"

#define SLAVE_ADDRESS 1
#define RX_SOFTWARE_PIN D7
#define TX_SOFTWARE_PIN D8

#define EXAMPLE_REGISTER 0
#define REGISTER_SIZE 1

ModbusMaster232 node(SLAVE_ADDRESS);

void setup() {
 Serial.begin(9600);
 delay(100);
 node.begin(9600, D6);

 //pinMode(D6, HIGH); //
}

void loop() {
 Serial.print("Response status: ");
 String hexResponseStatus(node.readDiscreteInputs(EXAMPLE_REGISTER, REGISTER_SIZE), HEX);
 Serial.println(hexResponseStatus);
 Serial.print("Response: ");
 Serial.println(node.getResponseBuffer(0));
 node.clearResponseBuffer();
 delay(5000);
}
otrzymuje status odpowiedzi 0xE2 który odpowiada ResponseTimedOut. Co zatem może być jeszcze nie tak? Wiem, że zagadnienie jest dość obszerne i możliwe, że to o co pytam to błachostka, ale zaznaczam, że mam bardzo małe doświadczenie jeśli chodzi o obsługę protokołów. Niestety nie posiadam też analizatora stanów logicznych (dotrze do mnie dopiero w przyszłym tygodniu).
Nie będę Ci wymyślał koła na nowo, w załączniku działający przykład, ale dla innego licznika OR-WE-504. Zasada działania jest taka sama.
Parę wskazówek.
Do testów użyj sprzętowego UART, jak konwerter jest na logice 5V to użyj na początek typowego Arduino UNO/NANO, a najlepiej Leonardo/Micro i Serial1 gadasz z licznikiem, Serial drukujesz na PC.  Oprócz UART mam podłączony tylko pin DE z konwertera, a UART to TX-DI, RX-RO. No i musisz mieć tę bibliotekę https://github.com/4-20ma/ModbusMaster.
W Node MCU serial nie jest potrzebny, do programowania użyj OTA, do podglądu danych użyj przykładu z TELNET i Putty jako klienta.
Ja używam RS485 w NANO na tym samym serialu co UART USB, tak że ten niezalecany sposób również działa. Przez ten licznik zacząłem zabawę z Arduino, więc to było już dawno temu.

Kod:
/*

 RS485_HalfDuplex.pde - example using ModbusMaster library to communicate
 with EPSolar LS2024B controller using a half-duplex RS485 transceiver.

 This example is tested against an EPSolar LS2024B solar charge controller.
 See here for protocol specs:
 http://www.solar-elektro.cz/data/dokumenty/1733_modbus_protocol.pdf

 Library:: ModbusMaster
 Author:: Marius Kintel <marius at kintel dot net>

 Copyright:: 2009-2016 Doc Walker

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.

*/

#include <ModbusMaster.h>

/*!
 We're using a MAX485-compatible RS485 Transceiver.
 Rx/Tx is hooked up to the hardware serial port at 'Serial'.
 The Data Enable and Receiver Enable pins are hooked up as follows:
*/
#define MAX485_DE      4
//#define MAX485_RE_NEG  6 //podłączony do MAX485_DE

// instantiate ModbusMaster object
ModbusMaster node;

void preTransmission()
{
 //digitalWrite(MAX485_RE_NEG, 1);
 digitalWrite(MAX485_DE, 1);
}

void postTransmission()
{
// digitalWrite(MAX485_RE_NEG, 0);
 digitalWrite(MAX485_DE, 0);
}

void setup()
{
// pinMode(MAX485_RE_NEG, OUTPUT);
 pinMode(MAX485_DE, OUTPUT);
 // Init in receive mode
 //digitalWrite(MAX485_RE_NEG, 0);
 digitalWrite(MAX485_DE, 0);

 // Modbus communication runs at 115200 baud
 Serial.begin(9600);

 // Modbus slave ID 2
 node.begin(2, Serial);
 // Callbacks allow us to configure the RS485 transceiver correctly
 node.preTransmission(preTransmission);
 node.postTransmission(postTransmission);
}

bool state = true;

void loop()
{
 uint8_t result;
 uint16_t data[16];
 uint32_t kwh;
 // Toggle the coil at address 0x0002 (Manual Load Control)
 //result = node.writeSingleCoil(0x0002, state);
 //state = !state;
Serial.begin(9600);
 // Read 16 registers starting at 0x3100)
//  result = node.readHoldingRegisters(0x40000, 10);
 //OR
 result = node.readHoldingRegisters(0, 10);
 if (result == node.ku8MBSuccess)
 {
  Serial.begin(115200);  
 Serial.write(27);       // ESC command
 Serial.print("[2J");    // clear screen command
 Serial.write(27);
 Serial.print("[H");     // cursor to home command
     Serial.print("Voltage: \t\t");
   Serial.println(node.getResponseBuffer(0x00)/10);
   Serial.print("Current: \t\t");
   Serial.println(node.getResponseBuffer(0x01)/10);
   Serial.print("Freq: \t\t\t");
   Serial.println(node.getResponseBuffer(0x02)/10);
   Serial.print("Ac. Power: \t\t");
   Serial.println(node.getResponseBuffer(0x03));
   Serial.print("Reac. Power: \t\t");
   Serial.println(node.getResponseBuffer(0x04));
   Serial.print("App. Power: \t\t");
   Serial.println(node.getResponseBuffer(0x05));
   Serial.print("Power fact: \t\t");
   Serial.println(node.getResponseBuffer(0x06)/1000);
/*        
   Serial.print("x06: ");
       Serial.println(node.getResponseBuffer(0x06));
   Serial.print("x07: ");
       Serial.println(node.getResponseBuffer(0x07));
*/

kwh=((uint32_t) node.getResponseBuffer(0x07))<<16;
//kwh=kwh<<16; Albo tak
kwh=kwh|(node.getResponseBuffer(0x08));
    Serial.print("x07|08 Energy Act: \t");
       Serial.println( kwh/1000);

kwh=((uint32_t) node.getResponseBuffer(0x09))<<16;

kwh=kwh|(node.getResponseBuffer(0x0A));
    Serial.print("x09|0A Energy Reac: \t");
       Serial.println( kwh/1000);
       
/*    Serial.print("x08: ");
       Serial.println(node.getResponseBuffer(0x08));
   Serial.print("x09: ");
       Serial.println(node.getResponseBuffer(0x09));
   Serial.print("x0A: ");
       Serial.println(node.getResponseBuffer(0x0A));
  */
/*  
   kwh=(node.getResponseBuffer(0x06));
   kwh=kwh<<16;
   
  uint32_t temp=(node.getResponseBuffer(0x07));
  kwh=(kwh|temp);
   Serial.println(kwh);*/
   
 }

 delay(5000);
}

Wynik działania w Putty:
[attachment=838]
Dzięki za szybką odpowiedz, potestuje i dam znać czy coś ruszyło Smile
Testowałem pod różnymi kątami i w koncu konfiguracja z NodeMcu i konwerterem podpietym pod sprzetowe RX i TX ruszyła, haczykiem było podpiecie zewnetrznego zasilacza. To znaczy, tak jakby ruszyła - nie mam juz teraz błędu "ResponseTimedOut" tylko "InvalidFunction". 

Oto kod:
Kod:
#include <ModbusMaster.h>

#define SLAVE_ADDRESS 0
#define RST_PIN D8
#define CONTROLL_LED_PIN D5

#define EXAMPLE_REGISTER 0
#define REGISTER_SIZE 1

ModbusMaster node;

void preTransmission() {
 digitalWrite(RST_PIN, HIGH);
}

void postTransmission() {
 digitalWrite(RST_PIN, LOW);
}

void setup() {
 pinMode(RST_PIN, OUTPUT);
 digitalWrite(RST_PIN, LOW);
 pinMode(CONTROLL_LED_PIN, OUTPUT);
 digitalWrite(CONTROLL_LED_PIN, LOW);
 
 Serial.begin(9600);

 delay(100);
}

void loop() {
 uint8_t responseStatus;
 Serial.begin(9600);

 node.begin(SLAVE_ADDRESS, Serial);
 node.preTransmission(preTransmission);
 node.postTransmission(postTransmission);

 digitalWrite(CONTROLL_LED_PIN, LOW);
 node.clearResponseBuffer();
 responseStatus = node.readHoldingRegisters(EXAMPLE_REGISTER, REGISTER_SIZE);

 Serial.begin(115200);
 if(responseStatus == node.ku8MBSuccess) {
   digitalWrite(CONTROLL_LED_PIN, HIGH);
   
   Serial.print("Response status: ");
   String hexResponseStatus(responseStatus, HEX);
   Serial.println(hexResponseStatus);
   Serial.print("Response: ");
   Serial.println(node.getResponseBuffer(0));
 } else {
   Serial.print("Error occured, response status: ");
   String hexResponseStatus(responseStatus, HEX);
   Serial.println(hexResponseStatus);
 }
 delay(1000);
}

Tym razem probowałem dostać się do licznika https://www.fif.com.pl/pl/liczniki-zuzyc...3m-ct.html, ponieważ jest to drugi licznik który muszę obsłużyć a mam go zamontowany w garażu i łatwiej mi jest się tam dostać. W każdym razie, wywołując funkcje "readHoldingRegisters" dostaje błąd niewłaściwej funkcji i jest to zrozumiałe, ponieważ, o ile wiem dla odczytywania wartości int powinienem wywoływać "readDiscreteInputs". Jednak gdy wywołuje jakakolwiek funkcje inna niż "readHoldingRegisters" dostaje ponownie odpowiedz "ResponseTimedOut".

EDIT: Właśnie sobie uświadomiłem, że może być to spowodowane załączeniem biblioteki "ModbusMaster.h", zamiast "ModbusMaster232.h" która jest przystosowana do ESP8266. Kiedy wrócę do domu w piatek, spróbuje to jeszcze przetestować Smile
Z ESP ruszyło mi to dopiero po użyciu soft serial i translatora poziomów logicznych (translator to była pierwsza próba rozwiązania problemu i potem nie testowałem bez). Z Arduino NANO bez problemu działało przełączanie prędkości UART, raz gadałem z licznikiem, potem szybciej z PC na tym samym UART. Może NodeMCU jest nieco inne, ja użyłem Wemos, oba jednak mają UART podłączone do mostka USB. W planach mam jeszcze odpalenie tego na ESP-01, zobaczę jaki to ma wpływ. W ORNO to co odczytuję też jest w 16 bitowych rejestrach, energia jest sklejana z dwóch sąsiednich, w NANO używałem INT (tu jest 16 bitów), w ESP też to działało bez zmian, pomimo, że tu INT jest 32 bity.