Go Down

Topic: New peek function for HardwareSerial (Read 10446 times) previous topic - next topic

HazardsMind

Aug 16, 2014, 10:55 pm Last Edit: Aug 16, 2014, 10:58 pm by HazardsMind Reason: 1
I was trying to help out a new user with his project, but the solution I though would work turned out to only work once.

Users post
The user wanted to send a string from his phone to control a RGB led(s), and the code I gave him was:

Code: [Select]
char * strings[3] = {
 "RED", "GREEN", "BLUE"};
byte i = 0;

void setup()
{
 Serial.begin(115200);
}

void loop()
{
 if(Serial.available())
 {
   while(i < 3)
   {
     if(Serial.find(strings[i]))
     {
       Serial.print("Found ");
       Serial.println(strings[i]);
       i = 0;
       break;
     }
     Serial.println(i);
     i++;
   }
   if( i >= 3)
   {
     Serial.println("not found");
     i = 0;
   }
 }
}


This code should check the buffer and see if a string is found, and if not increment the index and check again.

It will find RED if typed in because it is in the first index of the strings array, but it won't find anything else. Upon further investigation, I found out that read() pops the ints (look at the .h and .cpp files for HWS) out of the buffer when it is called so this prevents my code from working. However another function peek() only looks at the first index of the buffer and doesn't move unless read is called.

I would like to add a new peek() function that allows the user to look through the buffer freely without popping anything out, thus allowing my code to work.

I have made an equivalent code that does what I want using the normal HWS library, but it is not very elegant. I'm sure I am not the only person this new function would help, so I am asking if it can be added.

New peek function, simple right?
Code: [Select]
int HardwareSerial::peek(uint8_t position)
{
 if (_rx_buffer->head == _rx_buffer->tail) {
   return -1;
 } else {
   return _rx_buffer->buffer[position];
 }
}
My GitHub:
https://github.com/AndrewMascolo?tab=repositories

Robin2

#1
Aug 16, 2014, 11:10 pm Last Edit: Aug 16, 2014, 11:12 pm by Robin2 Reason: 1
A somewhat related thing I have been thinking about (but not yet doing anything about) is the possibility of passing an array reference to HardwareSerial so it writes the data straight into my variable without needing to use Serial.read(). Then a user could have two or three "buffers" and alternate them to receive incoming data. The idea is to avoid the overhead of transferring the data from the HardwareSerial buffer to my buffer. It would also allow me to specify a larger buffer if necessary.

If the data is put directly into my variable I could "peek" to my heart's content with char x = myArray[indx];

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

HazardsMind

Well if you think about, all we need to do is access the buffer. The only problem that I can't seem to figure out is how to access the buffer

From HardwareSerial.cpp file.
Quote
struct ring_buffer
{
  unsigned char buffer[SERIAL_BUFFER_SIZE];
  volatile unsigned int head;
  volatile unsigned int tail;
};


Is there any way to access this struct outside the library?
My GitHub:
https://github.com/AndrewMascolo?tab=repositories

robtillaart

Quote
Is there any way to access this struct outside the library?


this works for version 1.5.6, (structure/permissions have been changed)

Code: [Select]

void setup()
{
  Serial.begin(115200);
  Serial.println("Start ");
}

void loop()
{
  int x = Serial._rx_buffer[1];
  Serial.println(x, DEC);
  int h = Serial._rx_buffer_head;
  Serial.println(h, DEC);
  int t = Serial._rx_buffer_tail;
  Serial.println(t, DEC);
  delay(1000);
}
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

HazardsMind

#4
Aug 17, 2014, 12:12 am Last Edit: Aug 17, 2014, 12:24 am by HazardsMind Reason: 1
@robtillaart
I just downloaded version 1.5.7, and I guess the permissions were changed back, because your code doesn't work anymore.

Edit:
I downloaded 1.5.6 r2 and it doesn't work either, I got the exact same error message :/

Error message:
Quote
Arduino: 1.5.7 (Windows 8 ), Board: "Arduino Uno"

In file included from C:\Users\Andrew\Documents\arduino-1.5.7\hardware\arduino\avr\cores\arduino/Arduino.h:221:0,
                 from sketch_aug16a.ino:1:
C:\Users\Andrew\Documents\arduino-1.5.7\hardware\arduino\avr\cores\arduino/HardwareSerial.h: In function 'void loop()':
C:\Users\Andrew\Documents\arduino-1.5.7\hardware\arduino\avr\cores\arduino/HardwareSerial.h:101:51: error: 'unsigned char HardwareSerial::_rx_buffer [64]' is protected
     unsigned char _rx_buffer[SERIAL_RX_BUFFER_SIZE];
                                                   ^
sketch_aug16a.ino:9:18: error: within this context
In file included from C:\Users\Andrew\Documents\arduino-1.5.7\hardware\arduino\avr\cores\arduino/Arduino.h:221:0,
                 from sketch_aug16a.ino:1:
C:\Users\Andrew\Documents\arduino-1.5.7\hardware\arduino\avr\cores\arduino/HardwareSerial.h:93:32: error: 'volatile rx_buffer_index_t HardwareSerial::_rx_buffer_head' is protected
     volatile rx_buffer_index_t _rx_buffer_head;
                                ^
sketch_aug16a.ino:11:18: error: within this context
In file included from C:\Users\Andrew\Documents\arduino-1.5.7\hardware\arduino\avr\cores\arduino/Arduino.h:221:0,
                 from sketch_aug16a.ino:1:
C:\Users\Andrew\Documents\arduino-1.5.7\hardware\arduino\avr\cores\arduino/HardwareSerial.h:94:32: error: 'volatile rx_buffer_index_t HardwareSerial::_rx_buffer_tail' is protected
     volatile rx_buffer_index_t _rx_buffer_tail;
                                ^
sketch_aug16a.ino:13:18: error: within this context

  This report would have more information with
  "Show verbose output during compilation"
  enabled in File > Preferences.

My GitHub:
https://github.com/AndrewMascolo?tab=repositories

robtillaart

Sorry,
then the only way seems to be to mod the libs...  :(

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

pYro_65

#6
Aug 17, 2014, 03:25 pm Last Edit: Aug 17, 2014, 03:51 pm by pYro_65 Reason: 1

Sorry,
then the only way seems to be to mod the libs...  :(


There is always a way. I'll have a crack and see what I can come up with.

Forum Mod anyone?
https://arduino.land/Moduino/

pYro_65

#7
Aug 17, 2014, 03:53 pm Last Edit: Aug 17, 2014, 04:36 pm by pYro_65 Reason: 1
Here is a solution. And you can access RX and TX buffers. Sorry for any headaches it may cause, and feel free to ask if you have questions.

Code: [Select]
template< typename Tag, typename Tag::type M >
 struct AccessMember{
   friend typename Tag::type get( Tag ){ return M; }
};

template< bool _RX >
struct HardwareSerial_Buffer{
 typedef uint8_t arr_t[ _RX ? SERIAL_RX_BUFFER_SIZE : SERIAL_TX_BUFFER_SIZE ];
 typedef arr_t HardwareSerial::*type;
 friend type get( HardwareSerial_Buffer );
};

template struct AccessMember< HardwareSerial_Buffer< true >, &HardwareSerial::_rx_buffer >;
template struct AccessMember< HardwareSerial_Buffer< false >, &HardwareSerial::_tx_buffer >;

uint8_t *GetRX( HardwareSerial &instance ){ return instance.*get( HardwareSerial_Buffer< true >() ); }
uint8_t *GetTX( HardwareSerial &instance ){ return instance.*get( HardwareSerial_Buffer< false >() ); }


And it is used very simply:
Code: [Select]
void setup() {
 Serial.begin( 9600 );
 
 uint8_t *rx_start = GetRX( Serial );
 uint8_t *tx_start = GetTX( Serial );
}

void loop() {}
Forum Mod anyone?
https://arduino.land/Moduino/

westfw

These are awful.  Typically the buffer used for serial IO at the hardware level would be separate from the buffer used for parsing, even though that is somewhat memory inefficient.  Something like:

Code: [Select]
String token = Serial.readStringUntil('\n');
if (token == "RED") {
  // light red
} else if (token == "BLUE") {
  // light blue
} else if (token == "GREEN") {
  // light green
}

(Insert warning about String not being a great idea, given its current state, and maybe using Serial.readBytesUntil() and strcmp() instead.)

HazardsMind

#9
Aug 21, 2014, 03:09 pm Last Edit: Aug 21, 2014, 03:14 pm by HazardsMind Reason: 1
I made a solution to the guy's problem without any special functions, but it looks like crap.
Code: [Select]
char * strings[] = {
  "RED", "GREEN", "BLUE","ORANGE"};
 
byte i = 0, j=0;
unsigned long _startMillis = 0;
char data[20];
int index = 0;
boolean search = false, found = false;
#define arrSize(x) sizeof(x)/sizeof(x[0])

void setup()
{
  Serial.begin(115200);
  clear(20);
  Serial.println(arrSize(strings));
}

void loop()
{
  searchBuff(20);
}

void searchBuff(byte samples)
{
  if(Serial.available() > 0)
  {
    for(int S = 0, tmp; S < samples; S++)
    {
      tmp = Serial.read();
      if(tmp != '\n' || tmp != '\r' || tmp != '\0')
        data[S] = tmp;
      delay(1); // this is needed, otherwise it will not show/store the correct data in the array.
    }
    search = true;
    found = false;
  }
  index = 0;
  if(search)
  {
    j = 0;
    while(j < arrSize(strings))
    {
      for(byte i = 0; i < samples; i++)
      {
        if(data[i] != strings[j][index])
          index = 0; // reset index if any char does not match

        if( data[i] == strings[j][index])
        {
          if(index == strlen(strings[j])-1) // is true if all chars in the target match
          {
            Serial.print("Found ");
            Serial.println(strings[j]);
            index = 0;
            found = true;
            break;
          }
          else
            index++;
        }
      }//end of FOR loop
      if(found)
        search = false;
      j++;
    }// End of WHILE loop
    if(!found)
    {
      Serial.println("not found");
      search = false;
    }
   
    clear(samples);
  }// End of Search
}

void clear(byte samples)
{
  int k = 0;
  while(k < samples)
  {
    data[k] = 0;
    k++;
  }
  search = false;
}


I also modified my HWS library with the peek function I wanted and it does work, but to clear the buffer, I need to use a loop to do multiple Serial.read(). I am going to see if there is a better way to clear the buffer, maybe its something really simple that I am not seeing, Idk.
My GitHub:
https://github.com/AndrewMascolo?tab=repositories

graynomad

I haven't looked at that code for ages but normally to clear a FIFO you just set the head pointer to = the tail pointer and (sometimes) clear a counter.

______
Rob
Rob Gray aka the GRAYnomad www.robgray.com

HazardsMind

I'll try it when I get home tonight.
My GitHub:
https://github.com/AndrewMascolo?tab=repositories

HazardsMind

Yea... it didn't quite work like expected. making the head = tail (or vs) only makes Serial.available = false. I was looking for a better way to clean out the buffer without using a while/for loop. The peek function I added, worked like a charm, so I was happy with that.

My GitHub:
https://github.com/AndrewMascolo?tab=repositories

graynomad

Quote
making the head = tail (or vs) only makes Serial.available = false

Isn't that a cleared buffer?

______
Rob
Rob Gray aka the GRAYnomad www.robgray.com

pYro_65

These are awful.


Its a solution, you may not be able to understand it, I'm sorry, however elaborate on your rant, otherwise you just fall in to the category of "one of those C guys".


@HazardsMind
It seems you still need to fix up the code pauls pointed out to you in the other thread.


Forum Mod anyone?
https://arduino.land/Moduino/

Go Up