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

Maximus
Offline
Зарегистрирован: 24.07.2015

Добрый день! 

У меня Ардуино мега и нано. Сначала заставил простым вещам , мигать светодиодом .

мастер:


#include <SoftwareSerial.h>

#define DIR 8 // переключатель прием\передача
 
SoftwareSerial RS485 (7, 6); // RX, TX

struct namer{
  char one;
  char two[6]= "Maxim";
  int a;
  };

void setup(){
  Serial.begin(9600); 
  RS485.begin(4800); // SoftwareSerial   
   
  pinMode(DIR, OUTPUT);
  digitalWrite(DIR, HIGH); // включаем передачу
  
}

void loop(){  
  namer onetwo;
   byte id = random(1, 3);
   RS485.write(id); // отправляем первый байт, в нем ID
   RS485.println(onetwo.two);
   Serial.print(id);
   Serial.print("   ");
   Serial.print(id);
   Serial.print(onetwo.two);
     
  delay(500);
    
}
 

слейв


#include <SoftwareSerial.h>

#define DIR 8 // переключатель прием\передача
#define ID 1 // номер ардуины

struct namer{
  char one;
  char two[6]={0};
  int a;
  };
  
SoftwareSerial RS485 (7, 6); // RX, TX

void setup(){
  Serial.begin(9600); 
  RS485.begin(4800); // SoftwareSerial   
   
  pinMode(DIR, OUTPUT);
  pinMode(3, OUTPUT);
  digitalWrite(DIR, LOW); // включаем прием
}

void loop(){
   namer onetwo;
  if (RS485.available() > 0) {
    byte id = RS485.read(); // читаем байт, в нем для кого этот пакет    
    if (id == ID){ // и если пакет пришел нам  
       //digitalWrite(3, HIGH); // мигнем диодом  
         // digitalWrite(3, LOW);   
         RS485.readBytes(onetwo.two,5);
        digitalWrite(3, HIGH); // мигнем диодом
        delay(100);
        digitalWrite(3, LOW);  
              Serial.print("   ");
    Serial.print(id);
     Serial.print("   ");
       Serial.print(onetwo.two);
         Serial.print("   ");   
     }
    else RS485.flush(); // если не нам, очищаем буфер

  }
   
}

Но в итоге мне надо передать стуктуру, мучаюсь, ничего не получается... Подскажите как это делать? Желательно с примерами

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

На структуру умеете взять указатель? Приведите его к типу uint8_t* и пробегитесь по памяти, начиная с него, на дистанцию размера структуры. Т.е. представьте его массивом, который можно побайтно передавать. На другой стороне примите в массив и посмотрите на него через указатель на вашу структуру (namer или number?).

 

5N62V
Offline
Зарегистрирован: 25.02.2016

Скромное имхо: 

У Вас слейв Нано? Если да, то строчку

SoftwareSerial RS485 (7, 6); // RX, TX

Надо заменить на, скажем , 

SoftwareSerial RS485 (2, 6); // RX, TX

И подавать сигнал именно на 2й пин.  SoftwareSerial работает по прерыванию, поэтому RX надо определять либо на 2й , либо на 3й пин.

И зачем на Меге использовать SoftwareSerial, если там и так UART портов навалом!

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Первый совет точно такой же, как написал sadman41.

Но если хотите выпендриться и сделать правоверно-ООПно, то определите в своей структуре метод для вывода в поток, а потом просто пихайте её в поток обычным Serial.print. Как это делается, с примерами, можно посмотреть вот здесь.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

5N62V пишет:

RX надо определять либо на 2й , либо на 3й пин.

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

Maximus
Offline
Зарегистрирован: 24.07.2015

А можите этот участок кода набросать ? 

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

ЕвгенийП пишет:

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

Внимания на это не обращал, но сейчас глянул в исходник - действительно на доступные PCINT-ы вешается. Выходит так, что SoftSerial отвлекает МК на любое внешнее изменение любого пина и дает оверхед.

Maximus
Offline
Зарегистрирован: 24.07.2015

Типо того ? код просто в кодблоксе написан, не под ардуино.

#include <iostream>
#include <stdint.h>

using namespace std;

    struct name{
    char a;
    char b;
    int c;
    };

int main()
{
    name* ptr;
    uint8_t* ptr28;
    name one ={'N','M',3};
    ptr=&one;
    ptr28=(uint8_t* )ptr;
    cout << "Hello world! "<< *(ptr28+1) << endl;
    return 0;
}

 

А читать Serial.read просто?  А куда? В массив? но как его потом чатать ?

Или в мастере я привожу структуру к указателя на uint8_t*, передаю в цикле каждый байт, а читаю в  uint8_t массив[sizeof(структура)] и потом привожу его к указателю структуры и уже разыменовывыю:  структура-> данные.

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

Примерно так, навскидку:

// наша структура
typedef struct
{
	byte head;
	byte data[10];
	byte tail;
	byte crc8;
} OurStruct;

// передаём структуру
void SendStruct(const OurStruct& toSend, Stream* s)
{
	byte* b = (byte*) &toSend;
	
	for(size_t i=0;i<sizeof(OurStruct);i++)
	{
		s->write(*b++);
	}
}

// получаем структуру
OurStruct structToReceive;
byte readed = 0;

bool ReceiveStruct(Stream* s)
{
		byte* bWrite = (byte*)&structToReceive;
		bWrite += readed;
		
		while(s->available())
		{
			if(readed >= sizeof(OurStruct)) {
                                readed = 0;
				return true;
                        }
			
			*bWrite++ = (byte) s->read();
			readed++;
			
		}
	return false;
}

Скармливать любой наследник Stream, например:

OurStruct toSend;
SendStruct(toSend,&Serial);

Получать:

if(ReceiveStruct(&Serial))
{
// тут что-то делаем с structToReceive
}

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

DIYMan, Вы вернёте флаг, что структура прочитана не тогда, когда прочитан последний её байт, а только тогда, когда в сериал придёт какой-то следующий после структуры байт. А если не придёт?

Maximus
Offline
Зарегистрирован: 24.07.2015

м.б.

if(readed >= sizeof(OurStruct)-1) {

                                readed = 0;

                return true;

                        }

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Ну, предположите, что структура имеет длину 1 байт. Тогда справа от >= у Вас ноль, т.е. Вы вообще ничего читать не будете.

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

Ну да, писал навскидку, поправить - дело полминуты ;) Достаточно поменять проверку и чтение местами:

	while(s->available())
        {
            *bWrite++ = (byte) s->read();
            readed++;

			if(readed >= sizeof(OurStruct)) 
			{
				readed = 0;
                return true;
			}
             
        }

 

Maximus
Offline
Зарегистрирован: 24.07.2015

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

Мастер:

#include <SoftwareSerial.h>

#define DIR 8 // переключатель прием\передача

SoftwareSerial RS485 (7, 6); // RX, TX

    struct namer{
    byte a;
    char b;
    int c;
    };


void setup(){
  Serial.begin(9600); 
  RS485.begin(4800); // SoftwareSerial   
   
  pinMode(DIR, OUTPUT);
  digitalWrite(DIR, HIGH); // включаем передачу
  
}

void loop(){  
    namer one ={random(1, 3),'M',23};
    byte* ptr28 = (byte* )&one;
    for(size_t i=0;i<sizeof(namer);i++){
      
       RS485.write(*(ptr28+i));
                       Serial.println("   ");
        Serial.println(*(ptr28+i));
                Serial.println("   ");
    }
    
}

 

 
Слейв ( в нем я так понимаю проблема):



#include <SoftwareSerial.h>

#define DIR 8 // переключатель прием\передача
#define ID 1 // номер ардуины

struct namer
{
    byte a;
    char b;
    int c;
};

SoftwareSerial RS485 (7, 6); // RX, TX

void setup()
{
    Serial.begin(9600);
    RS485.begin(9600); // SoftwareSerial

    pinMode(DIR, OUTPUT);
    pinMode(3, OUTPUT);
    digitalWrite(DIR, LOW); // включаем прием
}

void loop()
{
  
    namer one= {0};
    byte* ptr28 = (byte* )&one;
    if(RS485.available()){
     for(size_t i=0;i<sizeof(namer)-1;i++){
      ptr28[i] = RS485.read();
      Serial.println("   ");
      Serial.println(i);
      Serial.println(ptr28[i]);
      Serial.println("   ");
       
      }
    }
       // Serial.println("   ");
      // Serial.println(*(ptr28+1));  
}

Короче не судите строго, я запутался. 

 

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

Натыкайте в приемник Serial.print-ов и посмотрите - те ли байты и в том ли порядке доходят, в котором отправляются.

Maximus
Offline
Зарегистрирован: 24.07.2015

Ковыряюсь, ничего не выходит. Подумал тут, а как слейв будет понимать с какого бита считать начало структуры? у меня в этом и проблема, что каждый раз он в разную переменную записывает значения.  Может в структуру поместить какой нибудь стартовый бит типо byte start=-1? У меня подозрения что из за этого слей не понимает когда ему надо читать начинать и читает когда может, а мастер потоком шлет структуру за структурой. 

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

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

Maximus
Offline
Зарегистрирован: 24.07.2015

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

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

Проблема тут:

if(RS485.available()){
     for(size_t i=0;i<sizeof(namer)-1;i++){

Хинт - почему вы уверены, что в буфере приёма БОЛЬШЕ 1 байта? 

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

Maximus пишет:

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

Для каких "таких целей"? Это не TCP/IP, пакет не стандартизирован. Физические уровни RS485 вам обеспечил, то, что выше - каждый пилит по своей нужде. Общие принципы есть, а генерализованного решения я пока не встречал.  Велосипед и всякие структуры вам, для вашего термоядерного реактора не нужен. Его ответ проще для обработки в обычном массиве иметь.

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

ЕвгенийП пишет:

5N62V пишет:

RX надо определять либо на 2й , либо на 3й пин.

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

В Меге - не на любом. Но там, как справедливо было отмечено выше, есть аппараные порты, коими и следует пользоваться.

Maximus
Offline
Зарегистрирован: 24.07.2015

Да я понимаю что не нужно, я ради интереса)

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

Мастер:


#include <SoftwareSerial.h>

#define DIR 8 // переключатель прием\передача

SoftwareSerial RS485 (7, 6); // RX, TX

struct namer
{
    byte start;
    byte a;
    char b;
    int c;
};


void setup()
{
    Serial.begin(9600);
    RS485.begin(9600); // SoftwareSerial

    pinMode(DIR, OUTPUT);
    digitalWrite(DIR, HIGH); // включаем передачу

}

void loop()
{
    namer one = {124,random(1, 3),'M',23};
    byte* ptr28 = (byte* )&one;
    for(size_t i=0; i<sizeof(namer)-1; i++)
    {
        RS485.write(ptr28[i]);
        Serial.println(i);
        Serial.println("   ");
        Serial.println(ptr28[i]);
        Serial.println("   ");
    }
    delay(100);

}

Слейв:


#include <SoftwareSerial.h>

#define DIR 8 // переключатель прием\передача
#define ID 1 // номер ардуины

struct namer
{
    byte start;
    byte a;
    char b;
    int c;
};

SoftwareSerial RS485 (7, 6); // RX, TX

void setup()
{
    Serial.begin(9600);
    RS485.begin(9600); // SoftwareSerial

    pinMode(DIR, OUTPUT);
    pinMode(3, OUTPUT);
    digitalWrite(DIR, LOW); // включаем прием
}

void loop()
{
    namer one = {0};
    byte* ptr28 = (byte* )&one;

    if (RS485.available())
    {
        for (size_t i = 0; i < sizeof(namer); i++)
        {
            byte buf = RS485.read();
            if (buf == 124)//это проверяет стартовый байт.
            {
                i = 0;// если да, обнуляем счетчик
                ptr28[i] = buf;
                continue;//пропускаем
            }
            ptr28[i] = buf;
            if(one.a==ID){// мигаем светодиодом
               digitalWrite(3, HIGH); // мигнем диодом
               delay(100);
               digitalWrite(3, LOW);
              }
            delay(100);
            Serial.println("   ");
            Serial.println(i);
            Serial.println(one.start);
            Serial.println(one.a);
            Serial.println(one.b);
            Serial.println(one.c);
            Serial.println("   ");
            delay(100);
        }
    }
}














 

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

Лично я бы цикл чтения for()-ом не организовывал, конечно. А так - для защиты от неприятных сбоев при приеме вам неплохо бы CRC пихать в конец. И проверять ее при приеме. По ней выносить решение - доверять пришедшим данным или нет.