Arduino i Modbus

Masz problem, z którym nie możesz sobie poradzić? Pisz śmiało!
ODPOWIEDZ
Merki
Młodszy majsterkowicz
Posty: 10
Rejestracja: 10 lut 2013, 10:32

Arduino i Modbus

Post autor: Merki » 10 lut 2013, 11:00

Witam serdecznie na Forum, jest to mój pierwszy post i jak większość z Was zapewne sprowadził mnie tu ciężki do rozwiązania dla mnie problem ;)

Otóż od kilku dni próbuję prawidłowo odczytywać i zapisywać rejestry protokołu Modbus za pomocą Arduino oraz oprogramowania Scada o nazwie Promotic (dostępna wersja freeware). Niestety, nie mogę umieszczać linków, wybaczcie ;p

W internecie znalazłem kilka różniących się od siebie implementacji Modbus dla Arduino. Przyznam szczerze, że większość jest dla mnie totalnie niezrozumiała w tym chyba najpopularniejsza odmiana jpmzometa (wystarczy wpisać w google jpmzometa arduino modbus)

Tym niemniej znalazłem dwa dość przyjazne przykłady dla początkującego. Z pomocą jednego z nich udało mi się skomunikować ze SCADA oraz odczytać wartość z wejścia analogowego, drugim niestety tylko się połączyłem i nie mam zielonego pojęcia jak odczytać wartość.

Poniżej wklejam pierwszy program (mogę odczytać z niego wartość wejścia analogowego):

Kod: Zaznacz cały

#include <SimpleModbusSlave.h>

#define  ledPin  13 // onboard led 
#define  buttonPin  7 // push button

/* This example code has 9 holding registers. 6 analogue inputs, 1 button, 1 digital output
   and 1 register to indicate errors encountered since started.
   Function 5 (write single coil) is not implemented so I'm using a whole register
   and function 16 to set the onboard Led on the Atmega328P.
   
   The modbus_update() method updates the holdingRegs register array and checks communication.

   Note:  
   The Arduino serial ring buffer is 128 bytes or 64 registers.
   Most of the time you will connect the arduino to a master via serial
   using a MAX485 or similar.
 
   In a function 3 request the master will attempt to read from your
   slave and since 5 bytes is already used for ID, FUNCTION, NO OF BYTES
   and two BYTES CRC the master can only request 122 bytes or 61 registers.
 
   In a function 16 request the master will attempt to write to your 
   slave and since a 9 bytes is already used for ID, FUNCTION, ADDRESS, 
   NO OF REGISTERS, NO OF BYTES and two BYTES CRC the master can only write
   118 bytes or 59 registers.
 
   Using the FTDI USB to Serial converter the maximum bytes you can send is limited 
   to its internal buffer which is 60 bytes or 30 unsigned int registers. 
 
   Thus:
 
   In a function 3 request the master will attempt to read from your
   slave and since 5 bytes is already used for ID, FUNCTION, NO OF BYTES
   and two BYTES CRC the master can only request 54 bytes or 27 registers.
 
   In a function 16 request the master will attempt to write to your 
   slave and since a 9 bytes is already used for ID, FUNCTION, ADDRESS, 
   NO OF REGISTERS, NO OF BYTES and two BYTES CRC the master can only write
   50 bytes or 25 registers.
 
   Since it is assumed that you will mostly use the Arduino to connect to a 
   master without using a USB to Serial converter the internal buffer is set
   the same as the Arduino Serial ring buffer which is 128 bytes.
*/
 

// Using the enum instruction allows for an easy method for adding and 
// removing registers. Doing it this way saves you #defining the size 
// of your slaves register array each time you want to add more registers
// and at a glimpse informs you of your slaves register layout.

//////////////// registers of your slave ///////////////////
enum 
{     
  // just add or remove registers and your good to go...
  // The first register starts at address 0
  ADC0,     
  ADC1,        
  ADC2,
  ADC3,
  ADC4,
  ADC5,  
  LED_STATE,
  BUTTON_STATE,
  TOTAL_ERRORS,
  // leave this one
  TOTAL_REGS_SIZE 
  // total number of registers for function 3 and 16 share the same register array
};

unsigned int holdingRegs[TOTAL_REGS_SIZE]; // function 3 and 16 register array
////////////////////////////////////////////////////////////

void setup()
{
  /* parameters(long baudrate, 
                unsigned char ID, 
                unsigned char transmit enable pin, 
                unsigned int holding registers size)
                
     The transmit enable pin is used in half duplex communication to activate a MAX485 or similar
     to deactivate this mode use any value < 2 because 0 & 1 is reserved for Rx & Tx
  */
  
  modbus_configure(9600, 1, 2, TOTAL_REGS_SIZE);
  pinMode(ledPin, OUTPUT);
  pinMode(buttonPin, INPUT);
}

void loop()
{
  // modbus_update() is the only method used in loop(). It returns the total error
  // count since the slave started. You don't have to use it but it's useful
  // for fault finding by the modbus master.
  holdingRegs[TOTAL_ERRORS] = modbus_update(holdingRegs);
  for (byte i = 0; i < 6; i++)
  {
    holdingRegs[i] = analogRead(i);
    delayMicroseconds(500);	     
  }
  
  byte buttonState = digitalRead(buttonPin); // read button states
  
  // assign the buttonState value to the holding register
  holdingRegs[BUTTON_STATE] = buttonState; 
  
  // read the LED_STATE register value and set the onboard LED high or low with function 16
  byte ledState = holdingRegs[LED_STATE]; 
  
  if (ledState) // set led		  
    digitalWrite(ledPin, HIGH);
  if (ledState == 0 || buttonState) // reset led
  {
    digitalWrite(ledPin, LOW);
    holdingRegs[LED_STATE] = 0;
  }
}

Nie miałbym nic przeciwko powyższej implementacji tym niemniej mam kilka pytań i wątpliwości:

1. Rozumiem, że adresy rejestrów przypisywane są w kolejności wpisania w odpowiedniej sekcji powyższego programu? Problem w tym, że chcąc odczytać wartość jakiegoś wyjścia cyfrowego (dla przykładu pin 13 czyli dioda LED) nie uzyskuje innej wartości niż 0. Próbowałem różnych modyfikacji programu przesłanego mi przez pomoc techniczną PROMOTIC (zamieszczam w załączniku) i nic. Mogę za to bez problemu (prawie) odczytywać wartość wejścia analogowego. Prawie, gdyż na maksymalnym napięciu 5V podanym na wejście analogowe za pomocą diody, wyświetlana wartość waha się między ok. 900 a 1023. Z kolei po odłączeniu diody, wyświetlana wartość przyjmuje zakres od 0 do ok. 200. Nie wiem czy probrlm wynika z samego użycia diody, czy też potrzebny będzie po prostu jakiś filtr programowy, ale to w tej chwili jest mało istotny problem.

2. W ustawieniach komunikacji pisze, że domyślnie ustawiony jest tryb pracy wykorzystujący RS-485. Ja natomiast używam zwyczajnego kabla USB czyli w sumie coś na wzór RS-232. W związku z tym mam pytanie czy do prawidłowej komunikacji czyli zapisu i odczytu do/z Arduino wystarczy, że wklepię 0 lub 1 w powyższym kodzie przy ustawieniach komunikacji? Czy też 0 to tylko odczyt, a 1 to tylko zapis?

3. Czy powyższą implementację można tak ustawić, aby korzystała w pełni z możliwości Arduino Mega tzn interesuje mnie pełna obsługa wszystkich dostępnych portów?

Narazie nie zamieszczam drugiej implementacji, żeby nie zaciemniać całości sprawy. W załączniku przesyłam przykładowy plik Promotic odczytujący wartość wejścia analogowego jaki otrzymałem od pomocy technicznej oraz całość bibliotek do Modbusa.

Byłbym bardzo wdzięczny za pomoc, bardzo mi zależy na choćby drobnej podpowiedzi ;)
Nie masz wymaganych uprawnień, aby zobaczyć pliki załączone do tego posta.

Co miesiąc do wygrania nagrody o wartości ponad 1600 zł!


duduslaw
Młodszy majsterkowicz
Posty: 1
Rejestracja: 19 cze 2013, 13:54

Re: Arduino i Modbus

Post autor: duduslaw » 20 cze 2013, 06:03

Witam!

Może Kolega wyslac jeszcze raz ten plik arduino.rar, bo nie mogę go rozpakać błąd CRC
Goslawen
Młodszy majsterkowicz
Posty: 1
Rejestracja: 1 wrz 2013, 12:08

Re: Arduino i Modbus

Post autor: Goslawen » 1 wrz 2013, 13:30

Cześć,

Może i późno, ale przyda się pewnie na później innym, bo sam się z tym męczyłem.
Z tego co ja miałem doświadczenie z ModBusem, a wiele go nie mam :) biblioteka SimpleModbus obsługuje TYLKO 2 funkcje, funkcje 3 oraz funkcje 16.
Funkcja 3 służy do odczytu przez Mastera właśnie rejestrów, czyli np. wartości analogowych.
Natomiast funkcja 16 służy do wpisanie przez Mastera do Slave jakiś danych.
Do odczytu wyjść ("coil" - cewek) binarnych jest funkcja 1.Read Coils, której SimpleModbus nie obsługuje.
Do wymuszenia stanu na wyjściu binarnym służy funkcja 5. Write Coils.

Wojtek
ODPOWIEDZ

Strony partnerskie: