Go Down

Topic: Matlab - slow writeDigitalPin workaround? (Read 1 time) previous topic - next topic

McKendrigo

Hi everyone,

Some background of what I'm wanting to do: I'm wanting to use an LED to transmit some text optically from one PC to another. To do this, I want to read in some text the user types in, convert this to binary data and then use this to pulse the LED on/off as required. Then, at the receiver side, read in these optical Hi/Lo signals, and convert this binary back into the original text.

I'm using MATLAB to do handle the (basic!) user interface and conversion of the text input into 0's and 1's to send to the LED. Then I'm using the "writeDigitalPin" command to turn an LED that's connected to my Arduino Uno on/off.

That all works fine, but my issue now is the slow "writeDigitalPin" command. Ideally I'd like the LED pulsing to happen quite fast (too fast to see by eye, at least), but the writeDigitalPin command is quite slow so I can't do that.

I know the background as to why this command is quite slow (https://forum.arduino.cc/index.php?topic=346289.0) but I'm looking for some help with a workaround.

Rather than send the binary data bit by bit, can I load the complete binary sequence to the Arduino and execute it locally...if that makes sense? :/ In other words, instead of Matlab sending the "Write" commands one by one to the Arduino, it sends all the data at once and the Arduino then writes this bit-by-bit to the relevant pin?

Any suggestions are welcome.

Thanks!

McKendrigo

Okay, working on Plan B, again...would be good to get some pointers:

This Arduino sketch (no Matlab!) will read in some text I type into the serial monitor and print its binary equivalent out:

byte byteRead;

void setup() {               
// Turn the Serial Protocol ON
  Serial.begin(9600);
}

void loop() {
   /*  check if data has been sent from the computer: */
  if (Serial.available()) {
    /* read the most recent byte */
    byteRead = Serial.read();
    /*ECHO the value that was read, back to the serial port as binary */
    Serial.print(byteRead, BIN);
  }
}

So, if I type in Hello the serial monitor prints out 10010001100101110110011011001101111, which is great.

Is there a way that I can use that stream of 0's and 1's to switch the Digital Pin on/off in my Arduino sketch? I'm not sure how to get from printing "1001000...." to telling the Arduino to toggle a digital pin "On-Off-Off-On-Off..."

Thanks again!

Robin2

#2
Oct 05, 2015, 06:21 pm Last Edit: Oct 05, 2015, 06:22 pm by Robin2
You need to work on each byte separately.

The 2nd and 3rd examples in Serial Input Basics save the message into a char array (and could easily be changed to a byte array) which would make it easy to work through all the bytes.

To use each of the bits in a byte you need to mask off all but the low bit and step by step shift all the bits into the low bit position.

maskedVal = byteVal & 0b00000001;  // will hide all but the low bit

byteVal = byteVal >> 1; // will shift all the bits one place to the right after dropping the lowest bit

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

McKendrigo

Thanks, I'll give that a go and post the code once I've got something that's working.

Cheers!

McKendrigo

I've almost got what I want, but with one small issue. I cobbled the following code together from Robin2's link and here: https://www.arduino.cc/en/Tutorial/BitMask

The following code seems to successfully read in text from the Serial Monitor, displays what is to be 'transmitted' and pulses the output pin (pin 12) on/off to send the binary representation of the ASCII character:

Code: [Select]
const byte numChars = 64;
byte receivedChars[numChars]; // an array to store the received data
byte transmit = 12; //define our transmit test pin
byte mask = 128; //our bitmask
int bitDelay = 2; // Transmit delay in ms
byte byteread; // Byte currently being read

boolean newData = false;

void setup() {
Serial.begin(9600);
Serial.println("<Arduino is ready>");
}

void loop() {
       
recvWithEndMarker();
        showNewData();
        outputloop();
       

}

void recvWithEndMarker() {
static byte ndx = 0;
char endMarker = '\n';
byte rc;

// if (Serial.available() > 0)
           while (Serial.available() > 0 && newData == false) {
rc = Serial.read();

if (rc != endMarker) {
receivedChars[ndx] = rc;
ndx++;
if (ndx >= numChars) {
ndx = numChars - 1;
}
}
else {
receivedChars[ndx] = '\0'; // terminate the string
ndx = 0;
newData = true;
}
}
}

void showNewData() {
if (newData == true) {
Serial.print("Text to be transmitted ... ");
Serial.println((char*)receivedChars);
newData = false;
}
}

void outputloop() {
  for (int i = 0; i < numChars; i++) {
 
    byteread = receivedChars[i];
 
  for (mask = 10000000; mask>0; mask >>= 1) { //iterate through bit mask

    if (byteread & mask){ // if bitwise AND resolves to true
      digitalWrite(transmit,HIGH); // send 1
    }
    else{ //if bitwise and resolves to false
      digitalWrite(transmit,LOW); // send 0
    }
delay(bitDelay); //delay
  }
}
}


The problem I have is that when I type in new text, previously entered text is not cleared from receivedChars, but is transmitted to the output pin (but doesn't appear printed on the Serial Monitor. For example:

  • I enter the text ABCDEF. The serial monitor displays "Text to be transmitted ...ABCDEF" and the output pin pulses the binary representation of ABCDEF.
  • Next I enter new text, lets call it XYZ. The serial monitor displays "Text to be transmitted ...XYZ", as expected...
  • However, the output pin will pulse the binary representation of XYZDEF, not XYZ.


I know what's going on - text that is read in remains stored, unless overwritten. The serial monitor presumably doesn't display the old text because of the newline marker, but the code to pulse the output pin doesn't recognise the marker.

I know a solution would be to clear the array receivedChars just before reading in new data, but whenever I've tried that it's either a) not had any noticable effect or b) it's constantly clearing the array so I never see any output.

If someone could suggest where and how I can flush out the old data, or an alternative solution, that would be great.

Thanks!

Robin2

My guess is that the problem is due to the location of the line newData = false;  I think you should move that to the bottom of your outputloop() function. You don't want to start taking in new data until you have done all the stuff with the existing data.

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

McKendrigo

Thanks for the suggestion. I haven't tried it yet, but I found an alternative. I use a counter to count how many characters are read in each time text is received from the serial port, and this counter terminates outputloop() once the same number of characters has been processed. So, if I enter "Hello", it only processes 5 characters and will ignore any that are stored from previous text entries.

Here's my final code

Code: [Select]
const byte numChars = 64;
byte receivedChars[numChars]; // an array to store the received data
byte transmit = 12; //define our transmit test pin
byte mask = 128; //our bitmask
int bitDelay = 500; // Transmit delay in us
byte byteread; // Byte currently being read
int counter = 0;

boolean newData = false;

void setup() {
Serial.begin(9600);
Serial.println("<Arduino is ready>");
}

void loop() {
       
recvWithEndMarker();
        showNewData();
        outputloop();
       

}

void recvWithEndMarker() {
static byte ndx = 0;
char endMarker = '\n';
byte rc;

// if (Serial.available() > 0)
           while (Serial.available() > 0 && newData == false) {
rc = Serial.read();

if (rc != endMarker) {
receivedChars[ndx] = rc;
ndx++;
                        counter = ndx;
if (ndx >= numChars) {
ndx = numChars - 1;
                        counter = ndx;
}
}
else {
receivedChars[ndx] = '\0'; // terminate the string
ndx = 0;
newData = true;
}
}
}

void showNewData() {
if (newData == true) {
Serial.print("Text to be transmitted ... ");
Serial.println((char*)receivedChars);
newData = false;
}
}

void outputloop() {
  for (int i = 0; i < counter; i++) {
 
    byteread = receivedChars[i];
 
  for (mask = 10000000; mask>0; mask >>= 1) { //iterate through bit mask

    if (byteread & mask){ // if bitwise AND resolves to true
      digitalWrite(transmit,HIGH); // send 1
    }
    else{ //if bitwise and resolves to false
      digitalWrite(transmit,LOW); // send 0
    }
delayMicroseconds(bitDelay); //delay
  }
}
}


Thanks again for your help!

Robin2

I use a counter to count how many characters are read in each time text is received from the serial port,
I think that is what is called a "kludge".

But kludges do work.

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

Go Up