конвертер MAX485 подключение

geb2010
Offline
Зарегистрирован: 04.04.2016

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

Мною изготовлено 5 плат, на которых установлены Ардуино МЕГА 2560 и купленные  конвертеры RS485 <-> TTL. выполненные на МАХ485.  Необходимо подключить их по сети RS485 к ПК для передачи данных. При подключении 2-х плат проблем не было, данные принимались по запросу с ПК с адресом нужной платы. При подключении 3-х плат данные передавались не устойчиво, а при подключении 5-и вообще платы не отвечали на запросы, хотя по этому интерфейсу можно подключать до 32 модулей.

Посмотрев данные на модуль конвертера RS485 <-> TTL ни каких противопоказаний я не увидел.  В инете приведены примеры подключений только 3-х подобных модулей, а большего количества я не нашел.

Когда я нашел принципиальную схему конвертера RS485 <-> TTL, выполненную на МАХ485, то увидел что в них уже подключены резисторы в 120 ом между проводами (А и В).  Поэтому у меня появилось сомнения в правильности подключения модулей.  Как я читал об интерфейсе RS485 должно быть только два резистора по 120 ом на входе линии и на выходе, а при объединении  модулей RS485 <-> TTL в сеть их получалось 5 и эквивалентное сопротивление получалось 24 ома вместо 60.  В модулях нет переключателей отключающих эти резисторы.

Вопрос:  правильно ли я посчитал и что эти модули нельзя объединять в сеть более 2-х ? 

 

 

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

geb2010 пишет:

В модулях нет переключателей отключающих эти резисторы.

странно, вроде раньше были перемычки.

Тогдая вижу один путь - выпаять резисторы с двух или трех модулей

geb2010
Offline
Зарегистрирован: 04.04.2016

b707:

мысли мои правильные ?

 

FoxJone
Offline
Зарегистрирован: 19.04.2019

Все верно. Лишние резисторы уберите. Это именно они мешают. Я с этим тоже сталкивался.

geb2010
Offline
Зарегистрирован: 04.04.2016

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

Еще вопрос:  что может быть если при приеме данных ПК по RS485 от  Ардуино 1  включаю  питания Ардуино 2 и связь ПК по RS485 с  Ардуино 1 нарушается ?

С уважением Геннадий.

FoxJone
Offline
Зарегистрирован: 19.04.2019

Не сталкивался с таким, но думаю что при включении идет какой то мусор по линии и каким то образом блокирует дальнейший прием. Без кода не понятно, что происходит.

Просниффьте линию и посмотрите что происходит.

geb2010
Offline
Зарегистрирован: 04.04.2016

Я извиняюсь, за то что не указал существенную поправку просто забыл - Ардуино 2 не подключен к сети RS485, подключен только один Ардуино 1.  Я думаю, что это может быть из-за того, что они поключены к одному источнику питания +12 в  (ОВЕН  БП30Б-Д3-12)  и может быть проскакивает помеха потому что нет никакой фильтрации, а модуль преобразования RS485 - TTL  подключен к 5 в от Ардуино?

 

sadman41
Offline
Зарегистрирован: 19.10.2016

Стабилизатор с обвязкой на Arduino вполне себе фильтрация, полагаю.

FoxJone
Offline
Зарегистрирован: 19.04.2019

Ну раз так, то однозначно питание. 

Вообще, я никогда и ничего не питаю от ардуин, питание всех компонентов идет от БП. На такой случай у меня на столе стоит 5-вольтовый БП на 200 ватт, от которого куча выводов со всякими разными разъемами.

geb2010
Offline
Зарегистрирован: 04.04.2016

У меня все Ардуино (5 штук) в цехе, расстояние между приборами по разному,  от 3 метров до 6,  от последнего прибора  до ПК длина линии примерно 10 - 15 метров. Запросы посылает ПК и ждет ответа 10 секунд, затем запрос следующему. Скорость по сети 19200.  Схемы приборов абсолютно одинаковые. Ставить БП на каждый прибор руководство посчитало неэкономично и поэтому все запитаны от одного БП с выходным током 2.5 А.

FoxJone
Offline
Зарегистрирован: 19.04.2019

geb2010 пишет:

Ставить БП на каждый прибор руководство посчитало неэкономично и поэтому все запитаны от одного БП с выходным током 2.5 А.

Купите 5 зарядок телефонных и от них питайте. 1А на один комплект - за глаза. Цена вопроса - 500 рублей за все 5 штук, дешевле, чем ваш БП на 12.

Я для одного завода делал контроллеры, так и поступил (там 4 комплекта было). Единственно, я зарядки из корпуса вынул и в коробку с контроллером определил.

А насколько проседают ваши 12 вольт на 15 метрах - один Ом ведает...

asam
asam аватар
Offline
Зарегистрирован: 12.12.2018

geb2010 пишет:

Ставить БП на каждый прибор руководство посчитало неэкономично и поэтому все запитаны от одного БП с выходным током 2.5 А.

Повесте ферритовые кольца на провода питания возле каждой Ардуины.

Вот нашел образовательное видео по этому поводу - https://youtu.be/7Ou7RTLV3fQ

geb2010
Offline
Зарегистрирован: 04.04.2016

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

Спасибо за советы.

Долго не писал, проводил эксперименты.  Выпаял резисторы и тогда исчезли проблемы с питанием - включение или выключение любого прибора не влияет на считывание другого.   Но осталась проблема:   к сети RS485 подключил два прибора.  Из програмы на компьютере (ПК)  запускаю цикл опроса этих приборов,  причем программа позволяет посылать запросы поочереди на каждый прибор или выборочно.  Если запросы посылаются на любой из приборов, а другой в это время в программе на  ПК  отключен, то все работает - ответ от прибора идет.  Если в программе на ПК разрешено посылать запросы на оба прибора, то отвечает только один.   Если в программе на ПК запрещаю посылать запрос на прибор, который отвечал, то  прибор, который не отвечал дает ответ.

В чем дело не могу понять - программы на обоих приборах одна и таже.  Определение прибором своего адреса, для ответа, проходит нормально. 

Еще момент - при скорости  обмена 9600 перестают отвечать оба прибора даже в одиночку.  Работают на скорости  19200.   Пробовал скорость  38400,  работают как на скорости  19200.

Не понимаю в чем дело ?   

Подскажите, пожалуйста,  что делать,  какие эксперименты еще провести ?

 

FoxJone
Offline
Зарегистрирован: 19.04.2019

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

geb2010
Offline
Зарегистрирован: 04.04.2016

Когда один прибор отвечает, а другой нет, я на монитор Ардуино прибора, который не отвечал, выводил то, что он принимает и что передает.  Получилось что запрос от ПК прибор не видит  и соответственно не отвечает ?  Почему этот же запрос прибор видит когда остается один в сети не понимаю ? 

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

При приеме данных в Ардуино у меня анализируется первые 3 байта на адрес и если он совпадает, то прибор отвечает.  Но ответ первого прибора также поступает и на второй прибор.  Второй прибор анализирует 3 байта, видит, что это не адрес и ждет следующих данных.  При этом не дочитанные данные полученные вторым прибором при ответе первым остаются в буфере. Запрос для второго прибора от ПК пишется в конец буфера приема и второй прибор анализирует первые 3 байта не дочитанных данных - естественно никакого адреса.

Это повторяется в каждом цикле.   Как Вы считаете есть в моих рассуждениях здравое зерно ?

И подскажите как можно очистить буфер приема ?

sadman41
Offline
Зарегистрирован: 19.10.2016

Каков формат пакета (полностью)?

geb2010
Offline
Зарегистрирован: 04.04.2016

Команда запроса с ПК:  M01  где:  М - признак модуль,  01 - адрес модуля

Ответ Ардуино для ПК:  М1, 100, 395   где:  М1 - модуль и его номер,  100, 395 - целочисленные данные по 4 символа,  разделитель запятая.

FoxJone
Offline
Зарегистрирован: 19.04.2019

geb2010 пишет:

Команда запроса с ПК:  M01  где:  М - признак модуль,  01 - адрес модуля

Ответ Ардуино для ПК:  М1, 100, 395   где:  М1 - модуль и его номер,  100, 395 - целочисленные данные по 4 символа,  разделитель запятая.

Я правильно понял, что вы там что то в ASCII читаете? Вы про байты слышали вообще?

Лично я бы ответ слал вот так: AE 01 20 0F 14 22 44. Что означает: AE - признак начала посылки (можете выбрать любой по вкусу) 01 - номер модуля, 20 0F - число 1 (8207 в данном случае),  14 22 - число 2 (5154), 44 - CRC8 (в данном случае простая байтовая сумма всей посылки начиная с 01 и заканчивая 22) и если CRC не совпадет, то посылка просто откидывается, как ошибочная.

А буфер приема не надо чистить, его надо вычитывать и анализировать. Можно вообще посмотреть что там и как в приборе принимает то?

sadman41
Offline
Зарегистрирован: 19.10.2016

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

https://www.gammon.com.au/forum/?id=11428 -> "Error checking protocol"

FoxJone
Offline
Зарегистрирован: 19.04.2019

sadman41 пишет:

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

Если вы шлете числовые данные размером больше 254 (а обычно это так и бывает, если это не команды типа вкл/выкл), то это нереально.  Поэтому проверяешь начало пакета. Если начало совпало с префиксом - то пакет пошел дальше в буфер. По окончанию пакета проверяешь контрольную сумму - если совпало, то весь пакет из буфера на расшифровку. Я таким образом даже модбас читал - лень было библиотеки модбаса искать и разбираться, оказалось проще самому написать.

Опять же никто не мешает сделать префикс из двух-трех байт, которые уже ТОЧНО не появятся в посылке вместе.

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

АА 55 АДРЕС_ОТПР АДРЕС_ПОЛ ДЛИНА_ПАКЕТА САМПАКЕТЛЮБОЙДЛИНЫ КС

geb2010
Offline
Зарегистрирован: 04.04.2016

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

Проблема решена !  Как я выше писал о получении ответа  1-го  прибора  вторым,  так оно и получилось.  Запрос от ПК для второго прибора записывался в конец сообщения первого.  Я сделал очистку буфера приема, но это не помогло, запрос все равно дописывался в конец,  поэтом у я подумал, что наверно очистка буфера не успевает закончится и сделал задержку 1000 мсек на посылку запроса для второго прибора с ПК.  Все заработало, я уменьшал эту задержку до 250 мсек, при этом проблема стала снова возникать.  Я увеличил задержку в два раза и все работает.

Большое спасибо всем за советы.

FoxJone
Offline
Зарегистрирован: 19.04.2019

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

Он и сейчас неправильно организован, просто с костылями заработал. Буфер приема нельзя очищать! В нем может быть что то полезное. Его надо вычитывать, анализировать и лишь потом отбрасывать ненужное.

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

берём нормальный протокол ICOM CI-V и будет счастье

geb2010
Offline
Зарегистрирован: 04.04.2016

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

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

Еще раз благодарю за советы.

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

Поискал, нашёл у себя код для обмена по UARTу.   Положу сюда, вдруг кто поиском наткнётся на эту тему, мож пригодится кому. 

состав пакета:

0хАА / 0х55 / размер пакета / сам пакет (payload) / контрольная сумма(простое сложение)

там функции как запаковки так и распаковки. 

//#define BUS Serial  // UART на котором висит применяемая шина

#include <SoftwareSerial.h>
SoftwareSerial BUS (10, 11); // (RX, TX) UART на котором висит применяемая шина



const bool echo = 0;  // меняем тут на 1 , если шина имеет эхо, т.е. когда отправленные байты сразу видим в приёмнике, (например к-лайн)
const byte bufsize = 150;           // размер буфера принятого сообщения
static byte buf [bufsize] = {0};    // буфер принятого сообщения
uint32_t currmillis = 0;            // переменная системного времени

#define DEBUG // раскоментировать для отладки в монитор Serial порта (выводит лог сообщений на шине)


byte meSS[] = {1,2,3,4,5,1,8,9,0,0,0,0,123,123,156,255}; // Тестовое исходящее сообщение, так, чтобы показать как отправляется. содержимое в нём от балды, просто для примера. 



void setup() 
{
#ifdef DEBUG 
 Serial.begin(38400);
#endif
BUS.begin(38400); 
sendMessage (meSS, sizeof(meSS)); // вот так отправляем сообщение в шину - для примера сделал здесь, в сетапе. Вы делайте это там, где вам необходимо. Вместо meSS может быть любой ваш массив
pinMode (13, OUTPUT);

}

void loop() 
{
currmillis = millis();  // снимок системного времени
BUSread();              // читаем шину  

// тут остальной код

}




 void messageOKreceived()
{
 //  ТУТ ДЕЛАЕМ ЧТО-ТО НУЖНОЕ - функция будет выполняться только когда СООБЩЕНИЕ УСПЕШНО ПРИНЯТО!

digitalWrite(13, buf[0]); // например из нулевого байта буфера принятого сообщения рулим диодом
// т.е. нужно, чтобы из шины на эту ардуино прилетело сообщение АА 55 1 0 0  - выкл led13
//                                                          или AA 55 1 1 1  - вкл  led13     
}


//ниже функция отправки сообщения на шину. 
//кидаем в эту функцию нужный массив байт и она запакует его в необходимый пакет и отправит в шину

void sendMessage(const byte *payload, const size_t size)
{
  const byte siZe = size+4;
  byte Mes[siZe];
  byte Checksum = 0;
  for(byte i=0; i<siZe; i++) 
  {
    if (i==0) {Mes[i] = 0xAA;}
    if (i==1) {Mes[i] = 0x55;}
    if (i==2) {Mes[i]=size;}    
    if (i==3) 
    {
      for (byte t=0; t<size; t++ ) 
         {
          Mes[i]=payload[t]; Checksum+=Mes[i]; BUS.write (Mes[i]); 
          if (echo) BUS.read(); 
          i++;
         }
    }
    if (i!=siZe-1) Checksum+=Mes[i];
    else Mes[i] = Checksum;    
    BUS.write (Mes[i]);  if (echo) BUS.read(); 
  }
} 


// функция чтения сообщений из шины и распаковки в массив
void BUSread()
{
  static byte header = 0;             // состояние заголовка 
  static byte message_size = 0;       // размер тела принимаемого сообщения, кол-во байт
  static byte j = 0;                  // инкремент
  static byte checksum = 0xFF;        // контрольная сумма входящего сообщения
  static uint32_t prevRESETheader=0;  // таймер сброса заголовка если в момент приёма сообщения данные оборвались
  static bool RESETheader_timer = 0;  // таймер сброса заголовка если в момент приёма сообщения данные оборвались

if (BUS.available()){
    

 // первый старт байт
 if (header == 0)
      {
       if (BUS.read()==0xAA){header++; RESETheader_timer=1; prevRESETheader=currmillis;}
      }                  

 // второй старт байт
 else if (header == 1)
      {
       if (BUS.read()==0x55){ header++;prevRESETheader=currmillis;} else {header = 0; RESETheader_timer=0;}
      } 

 // третий байт - размер тела сообщения
 else if (header == 2)
      { 
        message_size=BUS.read(); 
        header++; prevRESETheader=currmillis; checksum = 0xFF;
     if (message_size>bufsize){header = 0; RESETheader_timer=0;}
      }
                            
  // пишем тело сообщения 
 else if (header == 3 && j<message_size+1) 
      {
        buf[j] = BUS.read(); 
        if (j<message_size) checksum+= buf[j]; // подсчёт КС
        if (j==message_size) {header++;}
        prevRESETheader=currmillis;
        j++;
      } 

 } // end of BUS.available()

 // сообщение приняли, действуем
 if (header == 4) 
 {  
    checksum+=message_size; // прибавляем к контрольной сумме шапку сообщения
 
   
    
    // если контрольная сумма верна: 
    if (buf[message_size] == checksum) 
  {
  #ifdef DEBUG
  // распечатаем сообщение в отладку
  Serial.print(F("AA 55 ")); Serial.print(message_size, HEX); Serial.print(F(" "));
  for (int g = 0; g < message_size+1; g++) {Serial.print(buf[g], HEX); Serial.print(F(" "));}
  Serial.println();
  #endif
  
  messageOKreceived(); //  ТУТ ДЕЛАЕМ ЧТО-ТО НУЖНОЕ, ПОТОМУ ЧТО СООБЩЕНИЕ УСПЕШНО ПРИНЯТО!
  }   

    //ниже в else что-то делаем, если контрольная сумма не совпала: 
    else 
    { 
      #ifdef DEBUG
      Serial.println("Checksumm fail!!!" );
      #endif
    }
    
    message_size = 0; header=0; RESETheader_timer=0; j=0; checksum = 255;
  }

// таймер сброса сборки пакета, если данные перестали поступать более чем полсекунды 
if (RESETheader_timer && currmillis - prevRESETheader > 500) 
     { 
      // тут можно что-то делать, когда сообщение обрывается по середине
      // например плюс к счетчику ошибок
      #ifdef DEBUG
      Serial.println("Fail! Receive timeout!!!" ); 
      #endif
       RESETheader_timer = 0; header = 0; message_size = 0; j=0;
     }   
}

 

uragan
Offline
Зарегистрирован: 23.02.2015

Правильно ли я понимаю, что вместо элементов массива ставить напрямую нужные цифры нельзя?

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

uragan пишет:

Правильно ли я понимаю, что вместо элементов массива ставить напрямую нужные цифры нельзя?


что мешает ставить нужные цифры в элементы массива?

uragan
Offline
Зарегистрирован: 23.02.2015

Имеется в виду тип int. Т е предварительно нужно разложить а в приемнике собрать?

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

Я не совсем понимаю, что вы хотите. Опишите задачу подробнее. Откуда берутся "цифры", которые вы хотите передавать? Вы хотите их прямо в коде забить? Или это будут переменные в программе?

uragan
Offline
Зарегистрирован: 23.02.2015

Как передать например цифру 785 чтобы вторая ардуинка поняла ее именно как 785?

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

uragan пишет:

Как передать например цифру 785 чтобы вторая ардуинка поняла ее именно как 785?

Нет такой цифры.

Есть цифры 7, 8 и 5.

Если хотите получить вменяемый ответ на свой вопрос, пользуйтесь общепринятой терминологией.

uragan
Offline
Зарегистрирован: 23.02.2015

Можно ли передать число 785 или необходимо разбить на два?

uragan
Offline
Зарегистрирован: 23.02.2015

b707 пишет:
Я не совсем понимаю, что вы хотите. Опишите задачу подробнее. Откуда берутся "цифры", которые вы хотите передавать? Вы хотите их прямо в коде забить? Или это будут переменные в программе?

хочу передать по НС-12 значения нескольких переменных. Передавал по одному - иногда теряются.

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

uragan пишет:

Можно ли передать число 785 или необходимо разбить на два?

Можно.

На два чего?

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

uragan пишет:

Можно ли передать число 785 или необходимо разбить на два?

передача идёт по байтам по любому. А ваше число состоит из двух байт. Функция отправки из моего скетча #25 работает с массивом байт, соответственно ответ на ваш вопрос - да, нужно разбить число на массив из 2-х байт. 

можно к скетчу дописать функции запаковки и распаковки int  и пользоваться этим на приёмнике и передатчике. Примерно так (не претендую на красоту): 

//#define BUS Serial  // UART на котором висит применяемая шина

#include <SoftwareSerial.h>
SoftwareSerial BUS (10, 11); // (RX, TX) UART на котором висит применяемая шина



const bool echo = 0;  // меняем тут на 1 , если шина имеет эхо, т.е. когда отправленные байты сразу видим в приёмнике, (например к-лайн)
const byte bufsize = 150;           // размер буфера принятого сообщения
byte buf [bufsize] = {0};    // буфер принятого сообщения
uint32_t currmillis = 0;            // переменная системного времени

#define DEBUG // раскомментировать для отладки в монитор Serial порта (выводит лог сообщений на шине)

int Var = 785;  // подопытная отправляемая переменная int 

byte *Int (const int &integer)  // функция запаковки int
{
  static byte buf_int[2]; 
  buf_int[0]=((integer & 0xFF00)>>8); 
  buf_int[1] = integer; 
  return buf_int;
} 

int int_parse ()                // функция распаковки int
{
  int Integer = (buf[0] & 0xFFFF)<<8 | buf[1]; 
  return Integer;
}  




void setup() 
{
#ifdef DEBUG 
 Serial.begin(38400);
#endif
BUS.begin(38400); 
sendMessage (Int(Var), sizeof(int)); // вот так отправляем переменную int в шину - для примера сделал здесь, в сетапе. Вы делайте это там, где вам необходимо. Вместо Var может быть любая ваша переменная int 
pinMode (13, OUTPUT);

}

void loop() 
{
currmillis = millis();  // снимок системного времени
BUSread();              // читаем шину  

// тут остальной код

}




 void messageOKreceived()
{
 //  ТУТ ДЕЛАЕМ ЧТО-ТО НУЖНОЕ - функция будет выполняться только когда СООБЩЕНИЕ УСПЕШНО ПРИНЯТО!

// парсим из принятого сообщения нужный нам int 
int Recieve_int = int_parse();     //подопытная принимаемая переменная int 

}


//ниже функция отправки сообщения на шину. 
//кидаем в эту функцию нужный массив байт и она запакует его в необходимый пакет и отправит в шину

void sendMessage(const byte *payload, const size_t size)
{
          #ifdef DEBUG
          Serial.print (F("    TX:   "));
          #endif
  const byte siZe = size+4;
  byte Mes[siZe];
  byte Checksum = 0;
  for(byte i=0; i<siZe; i++) 
  {
    if (i==0) {Mes[i] = 0xAA;}
    if (i==1) {Mes[i] = 0x55;}
    if (i==2) {Mes[i]=size;}    
    if (i==3) 
    {
      for (byte t=0; t<size; t++ ) 
         {
          Mes[i]=payload[t]; Checksum+=Mes[i]; BUS.write (Mes[i]); 
          #ifdef DEBUG
          Serial.print (Mes[i], HEX); Serial. print (" ");
          #endif
          if (echo) BUS.read(); 
          i++;
         }
    }
    if (i!=siZe-1) Checksum+=Mes[i];
    else Mes[i] = Checksum;    
    BUS.write (Mes[i]);  if (echo) BUS.read(); 
    #ifdef DEBUG
    Serial.print (Mes[i], HEX); Serial. print (" ");
    #endif
  }
  #ifdef DEBUG
  Serial.println();
  #endif
} 


// функция чтения сообщений из шины и распаковки в массив
void BUSread()
{
  static byte header = 0;             // состояние заголовка 
  static byte message_size = 0;       // размер тела принимаемого сообщения, кол-во байт
  static byte j = 0;                  // инкремент
  static byte checksum = 0xFF;        // контрольная сумма входящего сообщения
  static uint32_t prevRESETheader=0;  // таймер сброса заголовка если в момент приёма сообщения данные оборвались
  static bool RESETheader_timer = 0;  // таймер сброса заголовка если в момент приёма сообщения данные оборвались

if (BUS.available()){
    

 // первый старт байт
 if (header == 0)
      {
       if (BUS.read()==0xAA){header++; RESETheader_timer=1; prevRESETheader=currmillis;}
      }                  

 // второй старт байт
 else if (header == 1)
      {
       if (BUS.read()==0x55){ header++;prevRESETheader=currmillis;} else {header = 0; RESETheader_timer=0;}
      } 

 // третий байт - размер тела сообщения
 else if (header == 2)
      { 
        message_size=BUS.read(); 
        header++; prevRESETheader=currmillis; checksum = 0xFF;
     if (message_size>bufsize){header = 0; RESETheader_timer=0;}
      }
                            
  // пишем тело сообщения 
 else if (header == 3 && j<message_size+1) 
      {
        buf[j] = BUS.read(); 
        if (j<message_size) checksum+= buf[j]; // подсчёт КС
        if (j==message_size) {header++;}
        prevRESETheader=currmillis;
        j++;
      } 

 } // end of BUS.available()

 // сообщение приняли, действуем
 if (header == 4) 
 {  
    checksum+=message_size; // прибавляем к контрольной сумме шапку сообщения
 
   
    
    // если контрольная сумма верна: 
    if (buf[message_size] == checksum) 
  {
  #ifdef DEBUG
  // распечатаем сообщение в отладку
  Serial.print(F("Rx: AA 55 ")); Serial.print(message_size, HEX); Serial.print(F(" "));
  for (int g = 0; g < message_size+1; g++) {Serial.print(buf[g], HEX); Serial.print(F(" "));}
  Serial.println();
  #endif
  
  messageOKreceived(); //  ТУТ ДЕЛАЕМ ЧТО-ТО НУЖНОЕ, ПОТОМУ ЧТО СООБЩЕНИЕ УСПЕШНО ПРИНЯТО!
  }   

    //ниже в else что-то делаем, если контрольная сумма не совпала: 
    else 
    { 
      #ifdef DEBUG
      Serial.println("Checksumm fail!!!" );
      #endif
    }
    
    message_size = 0; header=0; RESETheader_timer=0; j=0; checksum = 255;
  }

// таймер сброса сборки пакета, если данные перестали поступать более чем полсекунды 
if (RESETheader_timer && currmillis - prevRESETheader > 500) 
     { 
      // тут можно что-то делать, когда сообщение обрывается по середине
      // например плюс к счетчику ошибок
      #ifdef DEBUG
      Serial.println("Fail! Receive timeout!!!" ); 
      #endif
       RESETheader_timer = 0; header = 0; message_size = 0; j=0;
     }   
}


 

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

Макс, не нужна тут никакая функция упаковки. Переменная типа int лежит в памяти как два байта подряд, по сути это уже твой массив, который тебе нужен.

Так что в строчке 40 просто вызываешь

sendMessage ((byte*) &Var, sizeof(int));

и никакая функция не нужна

 

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

я в этом плохо пока разбираюсь, полчаса пытался такую строчку написать, чтоб работала) спасибо.

А при приёме можно ли также в массиве byte указать для переменной int адрес например byte mess [3]. Чтобы подхватила два байта mess [3] и mess [4] ? 

типа так, но естественно так не компилируется

byte mess[] = {0xAA, 0x55, 0x02, 0x03, 0x11, 0x15};

int *int_ptr = &mess[3];
int receive_value  = *int_ptr;

 

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

MaksVV пишет:

А при приёме можно ли также в массиве byte указать для переменной int адрес например byte mess [3]. Чтобы подхватила два байта mess [3] и mess [4] ?

можно воспользоваться функцией memcpy()

byte mess[] = {0xAA, 0x55, 0x02, 0x03, 0x11, 0x15};

int receive_value;
memcpy((byte*) &receive_value, mess +3, sizeof(int));

 

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

благодарю за пример, почитаю про это. 

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

MaksVV пишет:

типа так, но естественно так не компилируется

byte mess[] = {0xAA, 0x55, 0x02, 0x03, 0x11, 0x15};

int *int_ptr = &mess[3];
int receive_value  = *int_ptr;

 

а что-то зря я сказал что нельзя - так тоже можно, только адрес третьего элемента массива это (mess +3) Вот так попробуй:

byte mess[] = {0xAA, 0x55, 0x02, 0x03, 0x11, 0x15};

int *int_ptr = (int*) (mess+ 3);
int receive_value  = *int_ptr;

 

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

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

поэтому получаемый массив должен быть такой 

byte mess[] = {0xAA, 0x55, 0x02, 0x11, 0x03, 0x15};

собственно при отправке по твоему варианту sendMessage ((byte*) &Var, sizeof(int));, он так и отправляется 

 

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

to Uragan.  вот такой вариант тогда проще, без функций упаковки и распаковки переменной, см. внимательно комменты

//#define BUS Serial  // UART на котором висит применяемая шина

#include <SoftwareSerial.h>
SoftwareSerial BUS (10, 11); // (RX, TX) UART на котором висит применяемая шина



const bool echo = 0;  // меняем тут на 1 , если шина имеет эхо, т.е. когда отправленные байты сразу видим в приёмнике, (например к-лайн)
const byte bufsize = 150;           // размер буфера принятого сообщения
byte buf [bufsize] = {0};    // буфер принятого сообщения
uint32_t currmillis = 0;            // переменная системного времени

#define DEBUG // раскоментировать для отладки в монитор Serial порта (выводит лог сообщений на шине)


int Var = 785;  // подопытная отправляемая переменная int 


void setup() 
{
#ifdef DEBUG 
 Serial.begin(38400);
#endif
BUS.begin(38400); 
sendMessage ((byte*) &Var, sizeof(Var)); // вот так отправляем переменную в шину - для примера сделал здесь, в сетапе. Вы делайте это там, где вам необходимо. Вместо Var может быть любая ваша переменная. 
pinMode (13, OUTPUT);

}

void loop() 
{
currmillis = millis();  // снимок системного времени
BUSread();              // читаем шину  

// тут остальной код

}




 void messageOKreceived()
{
 //  ТУТ ДЕЛАЕМ ЧТО-ТО НУЖНОЕ - функция будет выполняться только когда СООБЩЕНИЕ УСПЕШНО ПРИНЯТО!

// парсим из принятого сообщения нужную нам переменную, не забываем нужный тип указывать в скобках перед buf 
int Recieve_int = *(int*)buf;

}


//ниже функция отправки сообщения на шину. 
//кидаем в эту функцию нужный массив байт и она запакует его в необходимый пакет и отправит в шину

void sendMessage(const byte *payload, const size_t size)
{
          #ifdef DEBUG
          Serial.print (F("    TX:   "));
          #endif
  const byte siZe = size+4;
  byte Mes[siZe];
  byte Checksum = 0;
  for(byte i=0; i<siZe; i++) 
  {
    if (i==0) {Mes[i] = 0xAA;}
    if (i==1) {Mes[i] = 0x55;}
    if (i==2) {Mes[i]=size;}    
    if (i==3) 
    {
      for (byte t=0; t<size; t++ ) 
         {
          Mes[i]=payload[t]; Checksum+=Mes[i]; BUS.write (Mes[i]); 
          #ifdef DEBUG
          Serial.print (Mes[i], HEX); Serial. print (" ");
          #endif
          if (echo) BUS.read(); 
          i++;
         }
    }
    if (i!=siZe-1) Checksum+=Mes[i];
    else Mes[i] = Checksum;    
    BUS.write (Mes[i]);  if (echo) BUS.read(); 
    #ifdef DEBUG
    Serial.print (Mes[i], HEX); Serial. print (" ");
    #endif
  }
  #ifdef DEBUG
  Serial.println();
  #endif
} 


// функция чтения сообщений из шины и распаковки в массив
void BUSread()
{
  static byte header = 0;             // состояние заголовка 
  static byte message_size = 0;       // размер тела принимаемого сообщения, кол-во байт
  static byte j = 0;                  // инкремент
  static byte checksum = 0xFF;        // контрольная сумма входящего сообщения
  static uint32_t prevRESETheader=0;  // таймер сброса заголовка если в момент приёма сообщения данные оборвались
  static bool RESETheader_timer = 0;  // таймер сброса заголовка если в момент приёма сообщения данные оборвались

if (BUS.available()){
    

 // первый старт байт
 if (header == 0)
      {
       if (BUS.read()==0xAA){header++; RESETheader_timer=1; prevRESETheader=currmillis;}
      }                  

 // второй старт байт
 else if (header == 1)
      {
       if (BUS.read()==0x55){ header++;prevRESETheader=currmillis;} else {header = 0; RESETheader_timer=0;}
      } 

 // третий байт - размер тела сообщения
 else if (header == 2)
      { 
        message_size=BUS.read(); 
        header++; prevRESETheader=currmillis; checksum = 0xFF;
     if (message_size>bufsize){header = 0; RESETheader_timer=0;}
      }
                            
  // пишем тело сообщения 
 else if (header == 3 && j<message_size+1) 
      {
        buf[j] = BUS.read(); 
        if (j<message_size) checksum+= buf[j]; // подсчёт КС
        if (j==message_size) {header++;}
        prevRESETheader=currmillis;
        j++;
      } 

 } // end of BUS.available()

 // сообщение приняли, действуем
 if (header == 4) 
 {  
    checksum+=message_size; // прибавляем к контрольной сумме шапку сообщения
 
   
    
    // если контрольная сумма верна: 
    if (buf[message_size] == checksum) 
  {
  #ifdef DEBUG
  // распечатаем сообщение в отладку
  Serial.print(F("Rx: AA 55 ")); Serial.print(message_size, HEX); Serial.print(F(" "));
  for (int g = 0; g < message_size+1; g++) {Serial.print(buf[g], HEX); Serial.print(F(" "));}
  Serial.println();
  #endif
  
  messageOKreceived(); //  ТУТ ДЕЛАЕМ ЧТО-ТО НУЖНОЕ, ПОТОМУ ЧТО СООБЩЕНИЕ УСПЕШНО ПРИНЯТО!
  }   

    //ниже в else что-то делаем, если контрольная сумма не совпала: 
    else 
    { 
      #ifdef DEBUG
      Serial.println("Checksumm fail!!!" );
      #endif
    }
    
    message_size = 0; header=0; RESETheader_timer=0; j=0; checksum = 255;
  }

// таймер сброса сборки пакета, если данные перестали поступать более чем полсекунды 
if (RESETheader_timer && currmillis - prevRESETheader > 500) 
     { 
      // тут можно что-то делать, когда сообщение обрывается по середине
      // например плюс к счетчику ошибок
      #ifdef DEBUG
      Serial.println("Fail! Receive timeout!!!" ); 
      #endif
       RESETheader_timer = 0; header = 0; message_size = 0; j=0;
     }   
}


 

uragan
Offline
Зарегистрирован: 23.02.2015

Что делать с одним числом понятно, с массивом чисел не очень. Для начала попробую более понятным способОм разбирать число на два.

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

uragan пишет:

Что делать с одним числом понятно, с массивом чисел не очень. Для начала попробую более понятным способОм разбирать число на два.

Не надл ничего разбирать  - все переменные в МК и без вашего разбора представляют из себя наборы байт. А лишний разбор на байты помешает вам работать с массивом.

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

Например код из #38 будет работать с массивом проактически без правки

uragan
Offline
Зарегистрирован: 23.02.2015

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

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

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

Но впрочем как хотите.

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

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

//#define BUS Serial  // UART на котором висит применяемая шина

#include <SoftwareSerial.h>
SoftwareSerial BUS (10, 11); // (RX, TX) UART на котором висит применяемая шина



const bool echo = 0;  // меняем тут на 1 , если шина имеет эхо, т.е. когда отправленные байты сразу видим в приёмнике, (например к-лайн)
const byte bufsize = 150;           // размер буфера принятого сообщения
byte buf [bufsize] = {0};    // буфер принятого сообщения
uint32_t currmillis = 0;            // переменная системного времени

#define DEBUG // раскоментировать для отладки в монитор Serial порта (выводит лог сообщений на шине)
#define SEND (byte*) &

int VarI = 785;                      // подопытные отправляемые переменые
float VarF= -1.53;                   // и
int VarI_array[]  = {23,  568 , -56}; // массивы 
byte VarB_array[]= {0x26,0x78,0xFD};


void setup() 
{
#ifdef DEBUG 
 Serial.begin(38400);
#endif
BUS.begin(38400); 

sendMessage (SEND VarI, sizeof(VarI));              // отправляем переменную int
sendMessage (SEND VarF, sizeof(VarF));              // отправляем переменную float
sendMessage (SEND VarI_array, sizeof(VarI_array));  // отправляем массив int
sendMessage (SEND VarB_array, sizeof(VarB_array));  // отправляем массив byte


pinMode (13, OUTPUT);

}

void loop() 
{
currmillis = millis();  // снимок системного времени
BUSread();              // читаем шину  

// тут остальной код

}




 void messageOKreceived()
{
 //  ТУТ ДЕЛАЕМ ЧТО-ТО НУЖНОЕ - функция будет выполняться только когда СООБЩЕНИЕ УСПЕШНО ПРИНЯТО!

// парсим из принятого сообщения нужную нам переменную, не забываем нужный тип указывать в скобках перед buf 
int Recieve_int = *(int*)buf;

}


//ниже функция отправки сообщения на шину. 
//кидаем в эту функцию нужный массив байт и она запакует его в необходимый пакет и отправит в шину

void sendMessage(const byte *payload, const size_t size)
{
          #ifdef DEBUG
          Serial.print (F("    TX:   "));
          #endif
  const byte siZe = size+4;
  byte Mes[siZe];
  byte Checksum = 0;
  for(byte i=0; i<siZe; i++) 
  {
    if (i==0) {Mes[i] = 0xAA;}
    if (i==1) {Mes[i] = 0x55;}
    if (i==2) {Mes[i]=size;}    
    if (i==3) 
    {
      for (byte t=0; t<size; t++ ) 
         {
          Mes[i]=payload[t]; Checksum+=Mes[i]; BUS.write (Mes[i]); 
          #ifdef DEBUG
          Serial.print (Mes[i], HEX); Serial. print (" ");
          #endif
          if (echo) BUS.read(); 
          i++;
         }
    }
    if (i!=siZe-1) Checksum+=Mes[i];
    else Mes[i] = Checksum;    
    BUS.write (Mes[i]);  if (echo) BUS.read(); 
    #ifdef DEBUG
    Serial.print (Mes[i], HEX); Serial. print (" ");
    #endif
  }
  #ifdef DEBUG
  Serial.println();
  #endif
} 


// функция чтения сообщений из шины и распаковки в массив
void BUSread()
{
  static byte header = 0;             // состояние заголовка 
  static byte message_size = 0;       // размер тела принимаемого сообщения, кол-во байт
  static byte j = 0;                  // инкремент
  static byte checksum = 0xFF;        // контрольная сумма входящего сообщения
  static uint32_t prevRESETheader=0;  // таймер сброса заголовка если в момент приёма сообщения данные оборвались
  static bool RESETheader_timer = 0;  // таймер сброса заголовка если в момент приёма сообщения данные оборвались

if (BUS.available()){
    

 // первый старт байт
 if (header == 0)
      {
       if (BUS.read()==0xAA){header++; RESETheader_timer=1; prevRESETheader=currmillis;}
      }                  

 // второй старт байт
 else if (header == 1)
      {
       if (BUS.read()==0x55){ header++;prevRESETheader=currmillis;} else {header = 0; RESETheader_timer=0;}
      } 

 // третий байт - размер тела сообщения
 else if (header == 2)
      { 
        message_size=BUS.read(); 
        header++; prevRESETheader=currmillis; checksum = 0xFF;
     if (message_size>bufsize){header = 0; RESETheader_timer=0;}
      }
                            
  // пишем тело сообщения 
 else if (header == 3 && j<message_size+1) 
      {
        buf[j] = BUS.read(); 
        if (j<message_size) checksum+= buf[j]; // подсчёт КС
        if (j==message_size) {header++;}
        prevRESETheader=currmillis;
        j++;
      } 

 } // end of BUS.available()

 // сообщение приняли, действуем
 if (header == 4) 
 {  
    checksum+=message_size; // прибавляем к контрольной сумме шапку сообщения
 
   
    
    // если контрольная сумма верна: 
    if (buf[message_size] == checksum) 
  {
  #ifdef DEBUG
  // распечатаем сообщение в отладку
  Serial.print(F("Rx: AA 55 ")); Serial.print(message_size, HEX); Serial.print(F(" "));
  for (int g = 0; g < message_size+1; g++) {Serial.print(buf[g], HEX); Serial.print(F(" "));}
  Serial.println();
  #endif
  
  messageOKreceived(); //  ТУТ ДЕЛАЕМ ЧТО-ТО НУЖНОЕ, ПОТОМУ ЧТО СООБЩЕНИЕ УСПЕШНО ПРИНЯТО!
  }   

    //ниже в else что-то делаем, если контрольная сумма не совпала: 
    else 
    { 
      #ifdef DEBUG
      Serial.println("Checksumm fail!!!" );
      #endif
    }
    
    message_size = 0; header=0; RESETheader_timer=0; j=0; checksum = 255;
  }

// таймер сброса сборки пакета, если данные перестали поступать более чем полсекунды 
if (RESETheader_timer && currmillis - prevRESETheader > 500) 
     { 
      // тут можно что-то делать, когда сообщение обрывается по середине
      // например плюс к счетчику ошибок
      #ifdef DEBUG
      Serial.println("Fail! Receive timeout!!!" ); 
      #endif
       RESETheader_timer = 0; header = 0; message_size = 0; j=0;
     }   
}