ModbusRTU, ModbusTCP Arduino и OWEN PLC

cilentlekx
Offline
Зарегистрирован: 14.12.2014

вот предварительный скетч для опроса устройств 

#include<EEPROM.h>  
#include <SPI.h>
#include <Adafruit_GFX.h>
//#include <TFT_ILI9163C.h>
#include <Adafruit_ST7735.h>


#define TFT_CS     10
#define TFT_RST    12  // you can also connect this to the Arduino reset
#define TFT_DC     8
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS,  TFT_DC, TFT_RST);

// Option 2: use any pins but a little slower!
#define TFT_SCLK 13   // set these to be whatever pins you like!
#define TFT_MOSI 11

#define  BLACK           0x0000
#define BLUE            0x001F
#define RED             0xF800
#define GREEN           0x07E0
#define CYAN            0x07FF
#define MAGENTA         0xF81F
#define YELLOW          0xFFE0
#define WHITE           0xFFFF


/*
   Пример будет использовать packet1, чтобы считать регистр из адреса 0 (значение adc ch0)
   от arduino раба (id=1). Это будет тогда использовать это значение, чтобы скорректировать яркость
   из вовлеченного контакт 9 использований PWM.
   Это будет тогда использовать packet2, чтобы записать регистр (его собственное значение adc ch0), чтобы адресоваться 1 
   на arduino рабе (id=1) корректировка яркости вовлеченного контакт 9 использований PWM.
*/
#include <SimpleModbusMaster.h>
//////////////////// Макроопределения портов и настройки программы  ///////////////////
long sped = 9600;
#define baud        sped // скоростьобмена по последовательному интерфейсу. (UART)
#define timeout     1000 // Длительность ожидание ответа (таймаут modbus)
#define polling     200  // скорость опроса по modbus
#define retry_count 10   // количесво запросов modbus до ошибки и останова обмена 
#define TxEnablePin 2    // Tx/Rx пин RS485
#define LED1        9    // светодиод 1
#define LED2        13   // светодиод 2

// Общая сумма доступной памяти на master устройстве, для хранения данных
// не забудьте изменить макроопределение TOTAL_NO_OF_REGISTERS. Если из слейва 
// запрашиваешь 4 регистра, то тогда в массиве reg должно быть не меньше 4х ячеек 
// для хранения полученных данных.
#define TOTAL_NO_OF_REGISTERS 8

// Масив пакетов modbus
// Для добавления новых пакетов просто добавте ихсюда
// сколько вам нужно.
enum
{
  PACKET1,
  PACKET2,
  PACKET3,
  PACKET4,
  PACKET5,
  PACKET6,
  PACKET7,
  PACKET8,
  TOTAL_NO_OF_PACKETS // эту строку неменять
};

// Масив пакетов модбус
Packet packets[TOTAL_NO_OF_PACKETS];

// Массив хранения содержимого принятых и передающихся регистров
unsigned int regs[TOTAL_NO_OF_REGISTERS];

unsigned int var1=0;
unsigned int var2=0;
unsigned int var3=0;
unsigned int var4=0;
unsigned char id1=1;

const int but_1 = 7;                                  //Кнопки управления по меню
const int but_2 = A4;                                 
const int but_3 = A5; 
byte varius = 0;
unsigned int addr1 =1;  // хранение адресов регистров чтобы не записывать каждый раз после включения
unsigned int addr2 =2;
unsigned int addr3 = 3;
unsigned int addr4 = 4;
unsigned int addr5 = 5;

void setup()
{
  tft.initR(INITR_BLACKTAB);
  tft.fillScreen(ST7735_BLACK);
  tft.setRotation(1);
  pinMode(but_1, INPUT);  //входы кнопок
  pinMode(but_2, INPUT); //входы кнопок
  pinMode(but_3, INPUT);  //входы кнопок
  digitalWrite(but_1, HIGH);
  digitalWrite(but_2, HIGH);
  digitalWrite(but_3, HIGH);
  pinMode(LED1, OUTPUT);
 pinMode(LED2, OUTPUT);
    
 // Настраиваем пакеты
 // Шестой параметр - это индекс ячейки в массиве, размещенном в памяти ведущего устройства, в которую будет 
 // помещен результат или из которой будут браться данные для передачи в подчиненное устройство. В нашем коде - это массив reg

/*
void modbus_construct(Packet *_packet, 
unsigned char id, //адрес слейва
unsigned char function, //функция регистра
unsigned int address, // адрес переменной
unsigned int data,// из скольки регистров читаем
unsigned _local_start_address); // локальный адрес по порядку
*/
  
 
} // конец void setup()

void loop()
{

  // Пакет,SLAVE адрес,функция модбус,адрес регистра,количесво запрашиваемых регистров,локальный адрес регистра.
 modbus_construct(&packets[PACKET1], id1, READ_HOLDING_REGISTERS,var1, 1, 0); // чтение данных slave-master (slave адрес 1, регистр 0) 
 modbus_construct(&packets[PACKET2], id1, READ_HOLDING_REGISTERS,var2, 1, 1); // чтение данных slave-master (slave адрес 1, регистр 1)
 modbus_construct(&packets[PACKET3], id1, READ_HOLDING_REGISTERS,var3, 1, 2);
 modbus_construct(&packets[PACKET4], id1, READ_HOLDING_REGISTERS,var4, 1, 3);
 // Пакет,SLAVE адрес,функция модбус,адрес регистра,данные,локальный адрес регистра.
 modbus_construct(&packets[PACKET5], id1, PRESET_MULTIPLE_REGISTERS,var1, 1, 0); // запись данных master-slave (slave адрес 1, регистр 2)
 modbus_construct(&packets[PACKET6], id1, PRESET_MULTIPLE_REGISTERS,var2, 1, 1); // запись данных master-slave (slave адрес 1, регистр 3)
 modbus_construct(&packets[PACKET7], id1, PRESET_MULTIPLE_REGISTERS,var3, 1, 2);
 modbus_construct(&packets[PACKET8], id1, PRESET_MULTIPLE_REGISTERS,var4, 1, 3);
 // инициализируем протокол модбус
 modbus_configure(&Serial, baud, SERIAL_8N1, timeout, polling, retry_count, TxEnablePin, packets, TOTAL_NO_OF_PACKETS, regs);

 
    var1 = EEPROM.read(addr1);
    //var1=var1*6;
     var2 = EEPROM.read(addr2);
      var3 = EEPROM.read(addr3);
       var4 = EEPROM.read(addr4);
     id1 = EEPROM.read(addr5);
 // tft.fillRect (0, 0, 160, 160, BLACK);
  //tft.fillScreen(BLACK);
    if(varius==0){  
  tft.setTextSize(1);
     tft.setCursor(30,5);
    tft.setTextColor(GREEN); // 'inverted' text
    tft.print("ID-slave-");
     tft.setTextColor(CYAN);
    tft.print(id1);
      tft.setCursor(6,25);
    tft.setTextColor(CYAN); // 'inverted' text
    tft.print("var1-");
     tft.setTextColor(CYAN);
    tft.print(var1);
    tft.setCursor(6,40);
    tft.setTextColor(CYAN); // 'inverted' text
    tft.print("var2-");
    tft.print(var2);
    tft.setCursor(6,55);
    tft.setTextColor(CYAN); // 'inverted' text
    tft.print("var3-");
    tft.print(var3);
    tft.setCursor(6,70);
    tft.setTextColor(CYAN); // 'inverted' text
    tft.print("var4-");
    tft.print(var4);
    
    if(varius==0&&(digitalRead(but_1)==LOW)){
  delay(50);
  tft.fillRect (84,5,30,10, BLACK);
  id1=id1+1;
  if(id1>=251){id1=0;}
}
if(varius==0&&(digitalRead(but_3)==LOW)){
  delay(50);
  tft.fillRect (84,5,30,10, BLACK);
  id1=id1-1;
   if(id1==-1){id1=250;}
}
  }

    if(varius==1){  
  tft.setTextSize(1);
  tft.setCursor(30,5);
    tft.setTextColor(CYAN); // 'inverted' text
    tft.print("ID-slave-");
     tft.setTextColor(CYAN);
    tft.print(id1);
      tft.setCursor(6,25);
    tft.setTextColor(GREEN); // 'inverted' text
    tft.print("var1-");
     tft.setTextColor(CYAN);
    tft.print(var1);
    tft.setCursor(6,40);
    tft.setTextColor(CYAN); // 'inverted' text
    tft.print("var2-");
    tft.print(var2);
    tft.setCursor(6,55);
    tft.setTextColor(CYAN); // 'inverted' text
    tft.print("var3-");
    tft.print(var3);
    tft.setCursor(6,70);
    tft.setTextColor(CYAN); // 'inverted' text
    tft.print("var4-");
    tft.print(var4);
    
    if(varius==1&&(digitalRead(but_1)==LOW)){
  delay(50);
  tft.fillRect (35,25,30,10, BLACK);
  var1=var1+1;
  if(var1>=256){var1=0;}
}
if(varius==1&&(digitalRead(but_3)==LOW)){
  delay(50);
  tft.fillRect (35,25,30,10, BLACK);
  var1=var1-1;
   if(var1==-1){var1=255;}
}
  }
//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
     if(varius==2){  
  tft.setTextSize(1);
  tft.setCursor(30,5);
    tft.setTextColor(CYAN); // 'inverted' text
    tft.print("ID-slave-");
     tft.setTextColor(CYAN);
    tft.print(id1);
      tft.setCursor(6,25);
    tft.setTextColor(CYAN); // 'inverted' text
    tft.print("var1-");
      tft.print(var1);
    tft.setCursor(6,40);
    tft.setTextColor(GREEN); // 'inverted' text
    tft.print("var2-");
     tft.setTextColor(CYAN);
      tft.print(var2);
    tft.setCursor(6,55);
    tft.setTextColor(CYAN); // 'inverted' text
    tft.print("var3-");
      tft.print(var3);
    tft.setCursor(6,70);
    tft.setTextColor(CYAN); // 'inverted' text
    tft.print("var4-");
      tft.print(var4);

if(varius==2&&(digitalRead(but_1)==LOW)){
   delay(50);
  tft.fillRect (35,40,30,10, BLACK);
  var2=var2+1;
  if(var2>=256){var2=0;}
}
if(varius==2&&(digitalRead(but_3)==LOW)){
  delay(50);
  tft.fillRect (35,40,30,10, BLACK);
  var2=var2-1;
   if(var2==-1){var2=255;}
}
  }
//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
     if(varius==3){  
  tft.setTextSize(1);
  tft.setCursor(30,5);
    tft.setTextColor(CYAN); // 'inverted' text
    tft.print("ID-slave-");
     tft.setTextColor(CYAN);
    tft.print(id1);
      tft.setCursor(6,25);
    tft.setTextColor(CYAN); // 'inverted' text
    tft.print("var1-"); 
    tft.print(var1);
    tft.setCursor(6,40);
    tft.setTextColor(CYAN); // 'inverted' text
    tft.print("var2-");
    tft.print(var2);
    tft.setCursor(6,55);
    tft.setTextColor(GREEN); // 'inverted' text
    tft.print("var3-");
    tft.setTextColor(CYAN);
    tft.print(var3);
    tft.setCursor(6,70);
    tft.setTextColor(CYAN); // 'inverted' text
    tft.print("var4-");
    tft.print(var4);

if(varius==3&&(digitalRead(but_1)==LOW)){
  delay(50);
  tft.fillRect (35,55,30,10, BLACK);
  var3=var3+1;
  if(var3>=256){var3=0;}
}
if(varius==3&&(digitalRead(but_3)==LOW)){
   delay(50);
  tft.fillRect (35,55,30,10, BLACK);
  var3=var3-1;
   if(var3==-1){var3=255;}
}
  }
  //xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
     if(varius==4){  
  tft.setTextSize(1);
  tft.setCursor(30,5);
    tft.setTextColor(CYAN); // 'inverted' text
    tft.print("ID-slave-");
     tft.setTextColor(CYAN);
    tft.print(id1);
    tft.setCursor(6,25);
    tft.setTextColor(CYAN); // 'inverted' text
    tft.print("var1-");
    tft.print(var1);
    tft.setCursor(6,40);
    tft.setTextColor(CYAN); // 'inverted' text
    tft.print("var2-");
    tft.print(var2);
    tft.setCursor(6,55);
    tft.setTextColor(CYAN); // 'inverted' text
    tft.print("var3-");
    tft.print(var3);
    tft.setCursor(6,70);
    tft.setTextColor(GREEN); // 'inverted' text
    tft.print("var4-");
    tft.setTextColor(CYAN);
    tft.print(var4);

    if(varius==4&&(digitalRead(but_1)==LOW)){
  delay(50);
  tft.fillRect (35,70,30,10, BLACK);  
  var4=var4+1;
  if(var4>=256){var4=0;}
}
if(varius==4&&(digitalRead(but_3)==LOW)){
   delay(50);
  tft.fillRect (35,70,30,10, BLACK);
  var4=var4-1;
   if(var4==-1){var4=255;}
}
  }
 //xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 

if(digitalRead(but_2)==LOW){delay(100);
varius=varius+1;
if(varius==5)varius=0;
delay(100);
       }

//}

//if(digitalRead(but_3)==LOW){
///// tft.fillScreen(BLACK);
//}
        
  modbus_update(); // запуск обмена по Modbus

  // если пришло 255 зажигаем светодиод 
  analogWrite(LED1, regs[0]>>2);  // чтение данных slave-master (slave адрес 1, регистр 0) 
                                  // (ограничеть количесво бит данных числом 255), прочитаное значение выводим шимом на аналоговый выход pin9
  if (regs[1] == 255){digitalWrite(LED2, HIGH);}else{digitalWrite(LED2, LOW);} // чтение данных slave-master (slave адрес 1, регистр 1)
  
  regs[2] = 255;           // запись данных master-slave (slave адрес 1, регистр 2), запись константы
  regs[3] = analogRead(0); // запись данных master-slave (slave адрес 1, регистр 3), значение из аналогового входа 0

  tft.setTextColor(GREEN);
       //if(( millis()- overd) >=5000) { 
       //overd = millis(); 
  //tft.fillScreen(BLACK);
  //delay(500);
       //}
       tft.setRotation(1); 
  tft.setTextSize(1);
   tft.fillRect (98,25,30,10, BLACK);
   tft.setCursor(70,25);
    tft.print("reg1-");
   tft.print(regs[0]);
    tft.fillRect (98,40,30,10, BLACK);
    tft.setCursor(70,40);
    tft.print("reg2-");
   tft.print(regs[1]);
    tft.fillRect (98,55,30,10, BLACK);
    tft.setCursor(70,55);
    tft.print("reg3-");
   tft.print(regs[2]);
    tft.fillRect (98,70,30,10, BLACK);
    tft.setCursor(70,70);
    tft.print("reg4-");
   tft.print(regs[3]);

  //var1=var1/6;
   EEPROM.write(addr1, var1);
   EEPROM.write(addr2, var2);
   EEPROM.write(addr3, var3);
   EEPROM.write(addr4, var4);
    EEPROM.write(addr5, id1);
    delay(10);
    //addr++;


} // конец void loop()

 

cilentlekx
Offline
Зарегистрирован: 14.12.2014

в принципе должен работать ,под рукой нет модуля пока.Может что дополните и подскажите! Буду очень признателен! Фото экрана позже выложу

 

alexval2007
Offline
Зарегистрирован: 10.11.2012

на вид вроде нормально но проверять тоже некогда после работы собираю один проэкт на заказ и сроки до сентября так что пока занят

cilentlekx
Offline
Зарегистрирован: 14.12.2014

конфигурацию переменных вытащил в loop а потом функцией modbus_update(); он обновляет регистры и опрашивает. есть уменя плк-150 .время будет сконфигурирую и попробую .о результатах сообщю. вообще у меня есть сами микросхемы max485,хочу прямо через нее пробовать а не заказывать отдельный шилд. Делал в свое время  модуль вводы вывода на ардуино slave и дискретные и аналоговые входа. все работало и с этой микросхемой,там обвязка минимальная! 

alexval2007
Offline
Зарегистрирован: 10.11.2012

на микросхемах можно и работать будет просто шилды для ленивых

cilentlekx
Offline
Зарегистрирован: 14.12.2014

 

фото мастера rs485 на ардуино

Dr_grizzly
Dr_grizzly аватар
Offline
Зарегистрирован: 07.12.2015

Делюсь своим рабочим примером. ПОкажу только несколько строк касаемых модбас:

#include <ModbusRtu.h>
....
uint16_t au16data[20] // массив для хранения полученных данных от устройств
modbus_t ModRead[16]; // cтруктурный пакет modbus на 16 устройств
bool fullsend=false; // если отправлен весь массив данных
....
Modbus master(0,2,TXEN); // this is master and RS-232 or USB-FTDI 2 порт Сериал

void setup() {
master.begin( 19200 ); // baud-rate at 19200
master.setTimeOut( 10 ); // if there is no answer in 5000 ms, roll over
}

bool mobus_send(int pIndex, int pId,int pFun,int pReg,int pAns,int pDat)
{
   if (pFun==6) {
  au16data[pAns]=pDat;
  ModRead[pIndex].u8id = pId; // slave address
  ModRead[pIndex].u8fct = pFun; // function code (this one is registers read)
  ModRead[pIndex].u16RegAdd = pReg; // start address in slave
  ModRead[pIndex].au16reg =au16data+pAns; // ячейка в массиве куда будет записан полученный ответ
  }
  else
   {
    au16data[pAns]=pDat;
    ModRead[pIndex].u8id = pId; // slave address
    ModRead[pIndex].u8fct = pFun; // function code (this one is registers read)
    ModRead[pIndex].u16RegAdd = pReg; // start address in slave
    ModRead[pIndex].u16CoilsNo = 1; // читаем один байт
    ModRead[pIndex].au16reg =au16data+pAns; // ячейка в массиве куда будет записан полученный ответ 
   }
  
  master.query( ModRead[pIndex] ); 
  return true;  
 }

 return false;
}
void loop() {
.... // что-то делаем, а потом отправляем пакет:
ans=mobus_send(5,9,6,8,10,2); // 5-номер пакета, 9 - адрес устройства, 6-функция записи, 8 - регистр записи, 10 - поместить ответ в 10ю ячейку, 2 - значение регистра
// вот так будет выглядеть пакет 090600080002[CRC] где CRC контрольная сумма пакета.
...
//гдето на таймере:
master.poll(); // check incoming messages
  if (master.getState() == COM_IDLE) {
//например включили светодиод который сказал что данные приняты/переданы
}
}

Я реализовал Slave и Master в одной арудино. Как Slave я с ней общаюсь с компа, как мастер - я общаюсь с устройствами от ардуино

Библиотеку ModbusRTU я малость модернизировал, чтобы реализовать мастер и слейв в одном устройстве.

cilentlekx
Offline
Зарегистрирован: 14.12.2014

Здравствуте! Вобщем запустил опросник с дисплеем,но корректно прочитать переменную типа float с плк не получается! В плк температура с датчика 28,34  а на дисплее 49408. В чем причина может быть? подскажите? адрес регистра 0. В модбас универсал сервере все корректно,на ардуино нет. То что тип данных разный или еще что.

cilentlekx
Offline
Зарегистрирован: 14.12.2014

все получилось, когда в плк преобразоал в UINT и вывело корректно! вывод --пременные получается опрашивать можно с определенным типом данных

cilentlekx
Offline
Зарегистрирован: 14.12.2014

Здравствуйте! Всем доброго времени! Подскажите пожалуйста-столкнулся спроблеммой при реализации модбус мастер на атмега328р. вообщем на ардуино уно все прекрасно работает ,код выкладывал выше. но когда извлек hex файл и прошил микроконтроллер и собрал то наотрез отказывается работать! собираю все тоже самое на уно то все прекрасно работает.в чем может быть проблемма ? или на отдельном микроконтроллере порт этот не работает или еще что. замерял напряжение на rx tx  то оказалось почему то наоборот тоесть +5в постоянно а при переключениии прием передача то проскальзывает минус . парни голову уже сломал. в чем причина такого поведения? Всем за ранее спасибо кто откликнется!

cilentlekx
Offline
Зарегистрирован: 14.12.2014

вот нашел по моему вопросу но тоже неясно на сколько это подключение работоспособно и реально.

http://deadlock.org.ua/kit/habr/post/729

и в чем смысл

Dr_grizzly
Dr_grizzly аватар
Offline
Зарегистрирован: 07.12.2015

Мой код управляет 16-ю частотниками Mistubishi и 4-мя ПИД-регуляторами температуры Autonics TM. Опрос оптических датчиков и отправка задачи контроллеру происходит в основном цикле программы(не в setup). Создаем что-то типа того:

void loop() {
....
switch (nextpak) 
{
case 1: {
  nextpak=100;
         }

case 2: {
mobus_send(6,10,6,8,33,2);
nextpak=100;
}

case 3: {
mobus_send(7,10,6,8,33,2);
nextpak=100;
}


case 100:
 {
  nextpak=1;
 }

} // switch
...
// где-то здесь обрабатываем порты ввода/вывода

если кнопка нажата тогда nextpak=2; 

если другая кнопка нажата тогда nextpak=3; 

}

 

harbor
Offline
Зарегистрирован: 31.05.2016

>>код выкладывал выше. но когда извлек hex файл и прошил микроконтроллер и собрал то наотрез отказывается работать!

при отсутствии приема/передачи на RS постояннный потенциал между линиями в 2-5 вольт это нормально

а во время передачи соответственно побегут байтики. можно посмотреть осцилограммы.

по ссылке ниже автор ставил инвертор на Tx, значит там почему-то был перевернутый сигнал, все от бибилиотеки зависит наверное/

кстати, как заливали хекс от ардуино в 328р ? а то тоже хотел попробовать

harbor
Offline
Зарегистрирован: 31.05.2016

Dr_grizzly, у вас своя библиотека какая-то ? откуда функция mobus_send ?

Dr_grizzly
Dr_grizzly аватар
Offline
Зарегистрирован: 07.12.2015

Смотри пост #57, эту библиотеку нашел здесь на форуме

#include <ModbusRtu.h>

 

bool mobus_send(int pIndex, int pId,int pFun,int pReg,int pAns,int pDat)

15 {
16    if (pFun==6) {
17   au16data[pAns]=pDat;
18   ModRead[pIndex].u8id = pId; // slave address
19   ModRead[pIndex].u8fct = pFun; // function code (this one is registers read)
20   ModRead[pIndex].u16RegAdd = pReg; // start address in slave
21   ModRead[pIndex].au16reg =au16data+pAns; // ячейка в массиве куда будет записан полученный ответ
22   }
23   else
24    {
25     au16data[pAns]=pDat;
26     ModRead[pIndex].u8id = pId; // slave address
27     ModRead[pIndex].u8fct = pFun; // function code (this one is registers read)
28     ModRead[pIndex].u16RegAdd = pReg; // start address in slave
29     ModRead[pIndex].u16CoilsNo = 1; // читаем один байт
30     ModRead[pIndex].au16reg =au16data+pAns; // ячейка в массиве куда будет записан полученный ответ
31    }
32   
33   master.query( ModRead[pIndex] );
34   return true
35  }
36  
37  return false;
38 }

 

cilentlekx
Offline
Зарегистрирован: 14.12.2014

Harbor,Здравствуйте! HEX так извлекал- скетч на ардуино иде компилируешь,

 
Дальше, открываем проводник и пишем там %temp%\ и нажимаем Enter:
Находим там папки с именами buildXXXXXXXXXXXXXXX.tmp:
В каждой из этих папок будет куча файлов, нас интересует файл Blink.cpp.hex  это я к примеру там найдете и свой 
harbor
Offline
Зарегистрирован: 31.05.2016

cilentlekx, я гдето читал, что для заливки хекса от ардуино нужно в атмегу загрузчик ардуиновский зашить сначала

Dr_grizzly
Dr_grizzly аватар
Offline
Зарегистрирован: 07.12.2015

По поводу hex прошивки - CodeSys 3.х  отлично все заливает ))) попробуйте им

alexval2007
Offline
Зарегистрирован: 10.11.2012

Загрузчик только если это будет плата ардуино тоесть загрузка скетча по usb а если програматором шить hex то ненужно ничего только правильно установить fuse биты их можно посмотреть в файле boards.txt который лежит в папке arduino/hardvare ауть точно непомню найдете. А для извлечения hex из скетча в настройкас среды арлуино поставить галочку отображать компиляцию или чтото в этом духе скрмпилировать и внизу в окне сообщений скопировать путь к файлу с расширением hex обычно он в самом конце сообщения ну почти в самом конце да незабудте на контролер прицепить кварц 16мгц и два кондера 22пф на его ноги и на общий

cilentlekx
Offline
Зарегистрирован: 14.12.2014

в том то и дело я посмотрел этот файл board.txt  и фьюзы выставил как на ардуиновскую атмегу и все равно все работает и экран и меню ничего не глючит а обмен по rs 485 не идет! Тут же снова перебираю на ардуино уно и блин все работает. Где засада не пойму

cilentlekx
Offline
Зарегистрирован: 14.12.2014

а может быть что я на микроконтроллер кварц поставил на 12мгц а на УНО 16мгц? может скорости теперь не совпадают

alexval2007
Offline
Зарегистрирован: 10.11.2012

Так 16 ставить надо

cilentlekx
Offline
Зарегистрирован: 14.12.2014

Да вообщем мучения закончились,незнаю- но пропаял еще раз ноги микросхем и поменял кварц на 16мгц и чудо все заработало .Всем говорю спасибо за поддержку и участие. скетч выложу ниже и фото что получилось

cilentlekx
Offline
Зарегистрирован: 14.12.2014

вот фото опросника

SIR
SIR аватар
Offline
Зарегистрирован: 07.11.2016

Доброго Здравия!

Такой вопрос. Нужна библиотека ModbusTCP Мастер + пример какой-нибудь.

Задача, Arduino в режиме Мастера по GPRS связывается с OPC сервером, который Slave, по Modbus протоколу и передает на него данные.  Таких точек в режиме Мастера может быть до сотни. Так как статического IP у них не будет,  то для них нужен режим Мастера.

Идентификация Ардуин либо через порт, либо через идентификатор.

Так как я в Ардуинах начинающий, хотя более 15 лет автоматизации за плечами, и есть опыт программирования, нужен какой-нибудь пример скретча.

Заранее Благодарю за помощь.

harbor
Offline
Зарегистрирован: 31.05.2016

в самом первом сообщении есть ссылка на библиотеку ModbusTCP и пример.

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

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

Если вы понимаете как работает протокол modbus то особо сложно не должно быть разобраться

SIR
SIR аватар
Offline
Зарегистрирован: 07.11.2016

Как Slave по протоколу Modbus TCP я уже соединился с OPC сервером. Все отлично управляется со Скады.

Сейчас задача в режиме Master подключиться к OPC, который переводим в режим Slave.

Пример в первом сообщении (ModbusTCP и библиотекой Mudbus) не понятен отсутствием рабочих примеров с библиотекой mudbus, начиная со строки if (Mb.Fc_rtu == 3).

Не подскажите программу - сканер Модбас TCP в режиме Slave.

harbor
Offline
Зарегистрирован: 31.05.2016

SIR пишет:

Пример в первом сообщении (ModbusTCP и библиотекой Mudbus) не понятен отсутствием рабочих примеров с библиотекой mudbus, начиная со строки if (Mb.Fc_rtu == 3).

Не подскажите программу - сканер Модбас TCP в режиме Slave.

в этой строке задается функция модбас 1,3,5 или 6, выше в примере они описаны

вернее это условие проверки.. 

в теле цикла я так понимаю надо вместо DEC выставить цифровые значения адреса, начального регистра, функцию и число читаемых регистров(от 1 до..)

единственно мне не понятно, почему в примере в сериал номер регистра посылается прежде  чем код функции, ведь должно быть наоборот. может быть ошибка, а может нет. так что если не заработает то возможно надо поменять местами строчки кода

44   Serial.print(Mb.Start_rtu, DEC);          // Start address адресc регистра
 
46   Serial.print(Mb.Fc_rtu, DEC);             // Function code код функции модбас

 

тут программка Модбас в режиме Slave, только кажется работает без регистрации 10 минут, но есть в сети крякнутые версии.

https://yadi.sk/d/Q_wDJjX2yPLa5

SIR
SIR аватар
Offline
Зарегистрирован: 07.11.2016

Уважаемый harbor, БлагоДарю за программу.

Если я отправляю-получаю данные разных типов то и код функции будет разным, и наверно стоит функцию отправки-получения вынести в подпрограмму, задавая необходимые условия перед отправкой.

Цикл, как я понимаю, организовывать не нужно, просто задавая количество опрашиваемых регистров. Регистры при этом должны быть одного типа и идти по порядку.

Таким образом мы присваиваем начальные условия, например отправки, и вход в подпрограмму, далее условия получения и вход в подпрограмму.

kristow
kristow аватар
Offline
Зарегистрирован: 08.08.2013

Подскажите читаю переменную с прибора 

modbus_construct(&packets[PACKET1], 16, READ_HOLDING_REGISTERS,    8,  1, 0);

В описании написано что переменная имеет топ word_16 с диапазоном от -200 до +200

При чтении от 0 до +200 проблеме нет.

А вот когда значение например -11, то получаю 65255 (0xfff5)

По запросам вижу:

21:26:22.854 [11564] (COM1)Tx: [8] 10 03 00 08 00 01 06 89
21:26:22.856 [11564] (COM1)Rx: [7] 10 03 02 00 05 84 44       = 5

а тут должно быть -11:

21:22:40.005 [11564] (COM1)Tx: [8] 10 03 00 08 00 01 06 89
21:22:40.007 [11564] (COM1)Rx: [7] 10 03 02 FF F5 C5 F0       = -11

Подскажите как привести к правильному виду.

 

harbor
Offline
Зарегистрирован: 31.05.2016

почитайте про типы данных, знаковые целые в частности

например тут http://arduino.ua/ru/prog/UnsignedInt

kristow
kristow аватар
Offline
Зарегистрирован: 08.08.2013

harbor пишет:

почитайте про типы данных, знаковые целые в частности

например тут http://arduino.ua/ru/prog/UnsignedInt

я про это уже понял.

Но не пойму как мне привести к нормальному виду.

 

Используя калькулятор вижу что если в hex добавить FFFF то получу -11. 

Если не сложно, подскажите как это сделать программно.

Dr_grizzly
Dr_grizzly аватар
Offline
Зарегистрирован: 07.12.2015

Ну если по логике то получаем следующее: разложим FFF5 на байты и получим "1111 1111 1111 0101" самый старший бит несет информацию о знаке. 1 - т.е отрицательное число. Тогда можно сделать так - FFFF минус FFF5 и сдвинуть на один бит влево. Результат такой - FFFF - FFF5 = A, A+1 = B. Bxh = 11(dec) но мы знаем что оно отрицательное, т.к. старший бит был равен 1.

harbor
Offline
Зарегистрирован: 31.05.2016

странно, я считал, что если создать переменную sign int и присовить ей ваши FFF5, то при отображении оно автоматически покажет нормальное знаковое число

покажите как вы в программе делаете, интересно просто

nik182
Онлайн
Зарегистрирован: 04.05.2015

kristow пишет:

Подскажите читаю переменную с прибора 

modbus_construct(&packets[PACKET1], 16, READ_HOLDING_REGISTERS,    8,  1, 0);

В описании написано что переменная имеет топ word_16 с диапазоном от -200 до +200

При чтении от 0 до +200 проблеме нет.

А вот когда значение например -11, то получаю 65255 (0xfff5)

По запросам вижу:

21:26:22.854 [11564] (COM1)Tx: [8] 10 03 00 08 00 01 06 89
21:26:22.856 [11564] (COM1)Rx: [7] 10 03 02 00 05 84 44       = 5

а тут должно быть -11:

21:22:40.005 [11564] (COM1)Tx: [8] 10 03 00 08 00 01 06 89
21:22:40.007 [11564] (COM1)Rx: [7] 10 03 02 FF F5 C5 F0       = -11

Подскажите как привести к правильному виду.

 

(int16_t)packets[i] 

SIR
SIR аватар
Offline
Зарегистрирован: 07.11.2016

Всем доброго времени суток!

Такой вопрос. Кто-нибудь использовал протокол передачи ModbusTCP в связке с шилдом SIM900 + Mega2560?

Подскажите рабочие библиотеки, которые можно использовать для такой связки. SIM900 соединяю с сервером без библиотек, через команды AT, даже могу опять же в ручном режиме закинуть заранее сформированный пакет ModbusTCP и сервер его обработает, но получить данные с сервера никак не получается. Например запрос по состоянию дискретных переменных на сервере присылает непонятную кучу непонятной длины байт, хотя тот же запрос через Ethernet выдает правильный, понятный ответ. Возможно, это какая-то информация из SIM900, или шилд трансформирует присылаемые данные каким-то образом, понять логику пока не представляется возможным.

Возможно, использование библиотеки для шилда SIM900 поможет в фильтрации ненужной информации из буфера SIM900. Или есть библиотека ModbusTCP для шилда SIM900, как в примере вверху темы, для Ethernet.

Возможно, надо учесть какие-либо нюансы, танцы с бубном, для корректного приема ответа с сервера через SIM900, как-то подготовить шилд?

Кто в курсе?

p/s/

Все, вопрос решил. Все написал без библиотек, нюансов море, понимаю, почему молчали. Примеров и подсказок на просторах интернета не нашел. Вопрос закрыт.

Deniska407
Offline
Зарегистрирован: 03.11.2014

Добрый вечер! Вот и мне интересно решить задачку с ModbusОм. Есть простое, слейв, устройство, которое должно выдавать по мадбас состояние концевиков. Код вот такой:

#include <SimpleModbusSlave.h>

enum 
{     
  DATA_VAL,     
  HOLDING_REGS_SIZE
};

unsigned int holdingRegs[HOLDING_REGS_SIZE]; 

void setup()
{

  pinMode(5,INPUT_PULLUP); pinMode(6,INPUT_PULLUP); pinMode(7,INPUT_PULLUP);

  pinMode(8,INPUT_PULLUP); pinMode(9,INPUT_PULLUP); 

       if ((digitalRead(6) == 1) && (digitalRead(7) == 1))
          {

              modbus_configure(&Serial, 9600, SERIAL_8N2, 50, 3, HOLDING_REGS_SIZE, holdingRegs);
              modbus_update_comms(9600, SERIAL_8N2, 50);
       
          }
       
       if ((digitalRead(6) == 1) && (digitalRead(7) == 0))
          {

              modbus_configure(&Serial, 9600, SERIAL_8N2, 51, 3, HOLDING_REGS_SIZE, holdingRegs);
              modbus_update_comms(9600, SERIAL_8N2, 51);
       
          }

       if ((digitalRead(6) == 0) && (digitalRead(7) == 1))
          {

              modbus_configure(&Serial, 9600, SERIAL_8N2, 52, 3, HOLDING_REGS_SIZE, holdingRegs);
              modbus_update_comms(9600, SERIAL_8N2, 52);
       
          }

       if ((digitalRead(6) == 0) && (digitalRead(7) == 0))
          {

              modbus_configure(&Serial, 9600, SERIAL_8N2, 53, 3, HOLDING_REGS_SIZE, holdingRegs);
              modbus_update_comms(9600, SERIAL_8N2, 53);
       
          }
  
}

void loop()
{



  //  формируем телеграмму

  MData = 5;
  
  if(digitalRead(8)==1 && digitalRead(9) == 1) MData = 0;
  if(digitalRead(8)==0 && digitalRead(9) == 1) MData = 1;
  if(digitalRead(8)==1 && digitalRead(9) == 0) MData = 2;
  if(digitalRead(8)==0 && digitalRead(9) == 0) MData = 3;

  //  отправляем телеграмму
  
  holdingRegs[DATA_VAL] = MData; // update data to be read by the master to adjust the PWM

  modbus_update();
  
  /* Note:
     The use of the enum instruction is not needed. You could set a maximum allowable
     size for holdinRegs[] by defining HOLDING_REGS_SIZE using a constant and then access 
     holdingRegs[] by "Index" addressing. 
     I.e.
     holdingRegs[0] = analogRead(A0);
     analogWrite(LED, holdingRegs[1]/4);
  */
  
}

 

Deniska407
Offline
Зарегистрирован: 03.11.2014

отсюда следующий вопрос: какой вид должен быть у телеграммы опроса? 

50 03 00 01 00 00 - не работает ...

 

SIR
SIR аватар
Offline
Зарегистрирован: 07.11.2016

Наиболее разжеван протокол вот здесь http://ipc2u.ru/articles/prostye-resheniya/modbus-tcp/

По Вашему запросу "50 03 00 01 00 00", где

50 - номер устройства, к которому Вы обращаетесь.

03  - читаем Analog Output Holding Registers.

00 01 - адрес регистра.

00 00 - количество требуемых регистров.... ?

Может количество требуемых регистров 00 01 ? Номер устройства 50?

Также Вы используете бибилиотеку, проконтролировали ее работу?

Dr_grizzly
Dr_grizzly аватар
Offline
Зарегистрирован: 07.12.2015

Я бы создал массив, в теле void считывал бы значения состояния ног и писал бы их в массив. А потом по модбас запрашивал значение этого массива. Интересующий меня бит массива

yden
Offline
Зарегистрирован: 30.01.2016

Здравствуйте 

Подскажите пожалуйста. Библиотеки SimpleModbusSlave.h и SimpleModbusMaster.h

К

enum
{
  //мастеру
  PACKET1, //4 слейв
  PACKET3, //2 слейв

  //слейву
  PACKET2, //4 слейв
  PACKET4, //2 слейв

  TOTAL_NO_OF_PACKETS // эту строку неменять
};

Packet packets[TOTAL_NO_OF_PACKETS];

unsigned int regs[TOTAL_NO_OF_REGISTERS];

void setup()
{
  time.begin();
  // Настраиваем пакеты
  // Шестой параметр - это индекс ячейки в массиве, размещенном в памяти ведущего устройства, в которую будет
  // помещен результат или из которой будут браться данные для передачи в подчиненное устройство. В нашем коде - это массив reg

  //стайка
  // Пакет,SLAVE адрес,функция модбус,адрес регистра,количесво запрашиваемых регистров,локальный адрес регистра.
  modbus_construct(&packets[PACKET1], 4, READ_HOLDING_REGISTERS, 0, 3, 0); // чтение данных slave-master
  // Пакет,SLAVE адрес,функция модбус,адрес регистра,данные,локальный адрес регистра.
  modbus_construct(&packets[PACKET2], 4, PRESET_MULTIPLE_REGISTERS, 3, 3, 3); // запись данных master-slave

  //свет веранда
  // Пакет,SLAVE адрес,функция модбус,адрес регистра,количесво запрашиваемых регистров,локальный адрес регистра.
  modbus_construct(&packets[PACKET3], 2, READ_HOLDING_REGISTERS, 6, 1, 6); // чтение данных slave-master
  // Пакет,SLAVE адрес,функция модбус,адрес регистра,данные,локальный адрес регистра.
  modbus_construct(&packets[PACKET4], 2, PRESET_MULTIPLE_REGISTERS, 7, 1, 7); // запись данных master-slave

  // инициализируем протокол модбус
  modbus_configure(&Serial, baud, SERIAL_8N2, timeout, polling, retry_count, TxEnablePin, packets, TOTAL_NO_OF_PACKETS, regs);

  pinMode(LED1, OUTPUT);

} // конец void setup()

Как думаете код будет работать?

Суть: мастер передает 4 слейву значения из 3 регистров (регистр 3, 4, 5). Принимает 3 регистра (регистр 0. 1, 2). Мастер передает 2 слейву одно значение (регистр 7) и принимает одно значение (регистр 6).

Или нужно в каждый пакет писать по одному регистру?

Благодарю.

yden
Offline
Зарегистрирован: 30.01.2016

Подниму тему. еще актуально.

Dr_grizzly
Dr_grizzly аватар
Offline
Зарегистрирован: 07.12.2015

Уточню: у вас на ардуине мастер и на ардуине несколько слейвов? так?  Мастер управляет слейвами. Т.е. только мастер может опрашивать и записывать данные в слейвы. Пакет Модбас на запись содержит - адрес устройства, функция записи (к примеру 06), адрес регистра слейва (00 01),  данные (03 E8), CR CR.  Запись происходит одним пакетом в один регистр. Следовательно, если мы хотим записать 3 регистра у слейва, то нам нужно 3 пакета. Но я не пробовал использовать мультипакеты, функции 15 и 16 (0х0F и 0x10). В теории:  Команда состоит из адреса элемента, количества изменяемых элементов, количества передаваемых байт устанавливаемых значений и самих устанавливаемых значений. Данные упаковываются так же, как в командах чтения данных. Ответ состоит из начального адреса и количества изменённых элементов.

Так что выбирать вам. Либо копать в направлении мульти-функций или посылать несколько пакетов )))

Dr_grizzly
Dr_grizzly аватар
Offline
Зарегистрирован: 07.12.2015

Нашел на Вики пример такого пакета https://ru.wikipedia.org/wiki/Modbus.

Dr_grizzly
Dr_grizzly аватар
Offline
Зарегистрирован: 07.12.2015

Накидал пример для себя и протестил пакет с функцией 10, вот что получилось

Адрес:Фукнция:Начальный регистр:Количество регистров:Количество байт данных:Данные

05:10:0000:0003:06:03F803F803F8:CRC

Замечу - что адреса записались с 0000 по 0002, т.е указав количество регистров - он включает начальный и плюс +2. Далее идет количество байт, в примере на Вики указано 2 байта, но у меня не получилось 2 байта задать всем трем регистрам, в первый попадает наше значение, а в остальные идет мусор. Поэтому, нужно указать значение для каждого регистра. Если у нас их 3 то 2*3=6, и следоватьельно все 6 байт мы должны указать. Это 03F8 три раза (к примеру). (значение в dec = 1000).

SIR
SIR аватар
Offline
Зарегистрирован: 07.11.2016

Проще формировать самому пакет, например, накидывая в массив Data [N] необходимые данные, привязав каждый байт к конкретной переменной, данные сформируете универсально и автоматически, далее отправляя весь массив в порт. Универсальную распаковку массива при получении тоже сделать не составит труда. Все необходимые функции можете прописать сами, в том числе и свыше 1-16 функций. https://ru.wikipedia.org/wiki/Modbus

Каждый байт расписан например тут https://ipc2u.ru/articles/prostye-resheniya/modbus-tcp/

Обработка не составит труда. Если не требуется TCP - выкиньте первые 4 байта.

Я пошел таким путем, так как было проще и объективней прописать пакет самому, накидывая данные по мере опроса функций.

yden
Offline
Зарегистрирован: 30.01.2016

Dr_grizzly пишет:

Уточню: у вас на ардуине мастер и на ардуине несколько слейвов? так?  Мастер управляет слейвами. Т.е. только мастер может опрашивать и записывать данные в слейвы. Пакет Модбас на запись содержит - адрес устройства, функция записи (к примеру 06), адрес регистра слейва (00 01),  данные (03 E8), CR CR.  Запись происходит одним пакетом в один регистр. Следовательно, если мы хотим записать 3 регистра у слейва, то нам нужно 3 пакета. Но я не пробовал использовать мультипакеты, функции 15 и 16 (0х0F и 0x10). В теории:  Команда состоит из адреса элемента, количества изменяемых элементов, количества передаваемых байт устанавливаемых значений и самих устанавливаемых значений. Данные упаковываются так же, как в командах чтения данных. Ответ состоит из начального адреса и количества изменённых элементов.

Так что выбирать вам. Либо копать в направлении мульти-функций или посылать несколько пакетов )))

Благодарю за разъяснения.

Для простоты чтения кода я думал запихать все в один пакет. Утрирую конечно, но суть та. 

Мультипакеты даже трогать не буду. Мне бы с обычными разобраться.

Еще раз спасибо.

SIR
SIR аватар
Offline
Зарегистрирован: 07.11.2016

Функции 15, 16 действительно удобны. Я например в основном пользуюсь 15, записывая несколько дискретных сигналов. Накидываю их в байт подобным образом:

bitWrite(Data[13], D1, Vent1); - где VN1 - номер дискретного канала  (1,2,3,4 и т.д. макс.8 - в 1 байте 8 битов.), а дискретный boolean Vent1 - HIGH или LOW.

Так можно накидать состояния всех необходимых регистров, отправить в порт и считать в приемнике:

BuferDiscret [i] = bitRead (Buffer [11], i); - где i - номер канала, Bufer [11] - одиннадцатый байт в пакете Modbus TCP (я читаю пакет в массив). В цикле, изменяя номера канала, считываем в массив дискреты, и далее:

Vent1 = BuferDiscret[0];   // Пишем в переменные дискретных каналов.
Vent2 = BuferDiscret[1];

Для Вас Modbus RTU протокол с подробнейшим описанием: https://ipc2u.ru/articles/prostye-resheniya/modbus-rtu/

 

yden
Offline
Зарегистрирован: 30.01.2016

SIR пишет:

Функции 15, 16 действительно удобны. Я например в основном пользуюсь 15, записывая несколько дискретных сигналов. Накидываю их в байт подобным образом:

bitWrite(Data[13], D1, Vent1); - где VN1 - номер дискретного канала  (1,2,3,4 и т.д. макс.8 - в 1 байте 8 битов.), а дискретный boolean Vent1 - HIGH или LOW.

Так можно накидать состояния всех необходимых регистров, отправить в порт и считать в приемнике:

BuferDiscret [i] = bitRead (Buffer [11], i); - где i - номер канала, Bufer [11] - одиннадцатый байт в пакете Modbus TCP (я читаю пакет в массив). В цикле, изменяя номера канала, считываем в массив дискреты, и далее:

Vent1 = BuferDiscret[0];   // Пишем в переменные дискретных каналов.
Vent2 = BuferDiscret[1];

Для Вас Modbus RTU протокол с подробнейшим описанием: https://ipc2u.ru/articles/prostye-resheniya/modbus-rtu/

 

благодарю

yden
Offline
Зарегистрирован: 30.01.2016

Ребята, а как можно обойти:

#define retry_count 10

Хотелось бы чтобы всегда мастер на связи был, т.е если слейвы не отвечают, мастер бы не впадал в спячку а продолжал их теребить запросами.

благодарю