0

I'm using boost to asynchronously read and write to my microcontroller. I have my microcontroller rigged so that it reads the data sent by the asynchronous write and echoes it back to the computer, where the computer reads it via an asynchronous read on a single thread. I'm sending over "15" to the microcontroller. Every first send after plugging the microcontroller in it works well, but after this it will sporadically "read" from the serial port "f" and "?f15". Whenever f or ?f15 is sent over, 7 bytes are transferred in the callback, which makes very little sense to me, since f is just a single ascii value. Here is my clientside Serial port wrapper code:

void Serial::async_write(std::string string){
  std::cout << "String size:" << string.size() << std::endl;
  // char stringToChar[string.size() + 1];
  // strcpy(stringToChar, string.c_str());
  // this->async_write(stringToChar);
  boost::asio::async_write(*port_, boost::asio::buffer(string, string.length()), boost::bind(&Serial::async_write_handler, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
}

void Serial::async_write_buffer(std::vector<char> data){
  int num = data.size();
  std::cout << num << std::endl;
  boost::asio::mutable_buffer buf(&data, data.size());
  boost::asio::async_write(*port_, boost::asio::buffer(data, data.size()), boost::bind(&Serial::async_write_handler, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
}

void Serial::async_write_handler(const boost::system::error_code &e, std::size_t bytes_written){
  std::cout << "Data written" << std::endl;
  //b_->consume(bytes_written);
}

void Serial::async_read_handler(const boost::system::error_code &e, std::size_t bytes_read){
  if(!(*e)){
    std::cout << "bytes read in async read handler:" << bytes_read << std::endl;
    if(bytes_read > 0){
      b_->commit(bytes_read);
      std::istream* instream = new std::istream(b_);
      std::string streamtostring;
      *instream >> streamtostring;
      std::cout << "size of input buffer:" << b_->size() << std::endl;
      std::cout << "Read: " <<std::endl;
      b_->consume(bytes_read);
      std::cout << streamtostring << std::endl;
    }
    else{
      std::cout << "No bytes read" << std::endl;
    }
  }
  else{
    std::cout << "Error occurred!" << std::endl;
    std::cerr << e.message() << std::endl;
  }
}
void Serial::async_read_until(std::string delim){
  boost::system::error_code e;
  boost::asio::async_read_until(*port_, *b_, delim, boost::bind(&Serial::async_read_handler, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
}

and here is the code that calls it in main.cpp:

int main(){
    boost::asio::io_service io;
    Serial::Serial serial(PORT, &io, 9600);
    if(!serial.is_open()){
      serial.open(PORT);
    }
    std::string s = "15";
    serial.async_write(s);
    serial.async_read_until("\n");
    // const char arr[] = {'2', '5', '5'};
    // serial.async_write(arr);
    // std::string s = "50089q503320232500202";
    // std::vector<char> data(s.begin(), s.end());
    // serial.async_write_buffer(data);
    io.run();
}

Now on the microcontroller side, I have it place each of the incoming data bytes into a stackArray of chars, where they are then popped out one by one into a char array that is 1 more character long than that of the stack array. Since the asynchronous read reads until a newline, I insert a newline at the very end of the character array. I then send it off across the stream.

#include <StackArray.h>
StackArray<int> binary;
int ledPin = 13;
int numberOfExecs = 0;
byte data = 0;
void setup() {
  Serial.begin(9600);
  //binary.setPrinter(Serial);
  pinMode(ledPin, OUTPUT);
}

void blink(int times, int duration){
  for(int i = 0; i < times; i++){
    digitalWrite(ledPin, HIGH);
    delay(duration);
    digitalWrite(ledPin, LOW);
    delay(duration);
  }
}

void loop() {
  //get number of bytes waiting in the serial buffer
  int bytesWaiting = Serial.available();
  //create array of character values
  StackArray<char> letterVals;
  //Set the printer for the stack array to serial
  letterVals.setPrinter(Serial);
  //while serial is available, push each byte of data to the stack array
  while(Serial.available() > 0){
    byte data = Serial.read();
    char c = data;
    //Serial.println(c);
    letterVals.push(c);
//    convertToBinary(data, binary);
//    printToLED(binary);
  }
  //Get the number of elements in the stack array
  int numElements = letterVals.count();
  //indicate how many elements there are on the led
  blink(numElements, 1000);
 // blink(1, 5000);
 //length of array
  int len = numElements + 1;
  //create array to send back data
  char sendback[len];
  if(bytesWaiting > 0){
      for(int i = len - 2; i >= 0; i--){
      //pop each character into its original position
      int asciiVal = letterVals.pop();
      //blink(asciiVal, 350);
      //blink(20, 20);
      sendback[i] = asciiVal;
    }
  }
  //set last character to newline
  sendback[len - 1] = 10;
  //if there are no bytes available to read, send off data
  if(bytesWaiting > 0){  
    Serial.println(sendback);
  }
}

Does anyone know why random f's and ?f's keep appearing? Thanks.

Tanner Sansbury
  • 51,153
  • 9
  • 112
  • 169
wfehrnstrom
  • 329
  • 2
  • 17

1 Answers1

0

This may be the result of the client code invoking undefined behavior. Specifically, the code fails to meet a lifetime requirement for boost::asio::async_write()'s buffers parameter:

[...] ownership of the underlying memory blocks is retained by the caller, which must guarantee that they remain valid until the handler is called.

In both Serial::async_write() and Serial::async_write_buffer(), the underlying memory provided as the buffer is owned by an object whose lifetime ends once the function returns. As neither of these functions makes no guarantee that they will not return until async_write's completion handler has been invoked, the lifetime of the temporary violates a requirement of async_write(), resulting in in undefined behavior.

void Serial::async_write(std::string string)
{
  boost::asio::async_write(
    ...,
    boost::asio::buffer(string, string.length()),
    ...);
} // `string`'s lifetime ends.

void Serial::async_write_buffer(std::vector<char> data)
{
  boost::asio::async_write(
    ...,
    boost::asio::buffer(data, data.size()),
    ...);
} // `data`'s lifetime ends.
Tanner Sansbury
  • 51,153
  • 9
  • 112
  • 169
  • Code-review comments: when sending all data in a buffer-like object, consider _not_ specifying the buffer size, as the [`buffer()`](http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/buffer.html) overloads will deduce the size; `istream` is allocated on the free-store and never deleted, consider using an automatic variable; the `streambuf`'s usage is awkward, as commit and consume will be no-ops (see [this](http://stackoverflow.com/a/31992879/1053968) answer for more details). – Tanner Sansbury Jun 07 '16 at 14:37