Serial Read and Output to DMD

Greetings and Salutations!

I was trying to find a solution to being able to write text live to a 32x16 DMD. I have managed to create the below code to accomplish this. I have this working to an extent, however I seem to have an issue with loosing Characters from my input.

eg. I Type "This is my example string" into my Serial Monitor.
The output I get to my screen (and whats written to the String) is random and can be something like "This is my exe string" or "This is my examptring".

This must be to do with this section of my code. But I'm lost as to what may be causing it.

inData[index] = inChar; // Store it
    index++; // Increment where to write next
    while(Serial.available() > 0){
         if(index < 254){
              inChar = Serial.read(); // Read a character
              inData[index] = inChar; // Store it
              delay(200);
              index++; // Increment where to write next
              inData[index] = 0; // Null terminate the string
         }
     }

I'm still learning how to program for Arduino, so I do apologise if it is a bit messy.

I appreciate any help you may be able to provide.

Thank you,

DJD

The Full code for this project is:

/*--------------------------------------------------------------------------------------

Serial Read and Display.

This project is designed to Read inputs from Serial and Display the input on a 
Freetronic 32x16 Dot Matrix Display.

To wipe your existing String simply input a Tilda (~) into your Serial Command.

Note, for debugging purposes, Serial.print has been used in both the Stop Display
and Write Display loops. These can be removed.

--------------------------------------------------------------------------------------*/

/*--------------------------------------------------------------------------------------
  Includes
--------------------------------------------------------------------------------------*/
#include <SPI.h>        //SPI.h must be included as DMD is written by SPI (the IDE complains otherwise)
#include <DMD.h>        //
#include <TimerOne.h>   //
#include "SystemFont5x7.h"
#include "Arial_black_16.h"

//Fire up the DMD library as dmd
#define DISPLAYS_ACROSS 1
#define DISPLAYS_DOWN 1
DMD dmd(DISPLAYS_ACROSS, DISPLAYS_DOWN);

char inData[255]; // Allocate some space for the string
char inChar; // Where to store the character read
byte index = 0; // Index into array; where to store the character

void ScanDMD(){ 
    dmd.scanDisplayBySPI();
}

void setup(void){
    Serial.begin(57600);
    Serial.write("Power On");

   //initialize TimerOne's interrupt/CPU usage used to scan and refresh the display
    Timer1.initialize( 5000 );           //period in microseconds to call ScanDMD. Anything longer than 5000 (5ms) and you can see flicker.
    Timer1.attachInterrupt( ScanDMD );   //attach the Timer1 interrupt to ScanDMD which goes to dmd.scanDisplayBySPI()

   //clear/init the DMD pixels held in RAM
   dmd.clearScreen( true );   //true is normal (all pixels off), false is negative (all pixels on)
   dmd.selectFont(Arial_Black_16);
}

void loop(void){
    while(Serial.available() == 0){ //check to see if data on Serial
        if (index > 0){ // if index is greater than 0, data must exist in inData, so display text. Otherwise do nothing.
            display_text();
        }else{
            break;
        }
    }
    while(Serial.available() > 0){
        inChar = Serial.read(); // Read a character
            if (inChar == '~') { // ~ is the command to stop display of any text and to wipe the inData String.
                  cancel_text();
                  break;
            }else{
                  write_text(); // This will write your Serial text to a String. Note if you do not ~ before typing a new string, this will append your new string to the previous one.
                  break;
            }
    }
}

   // Command to Stop Display and wipe InData String/Reset index.
void cancel_text(){
    index = 0;
    *inData = 0;
    Serial.println("Cancel");
}
  // Read Serial and Write to String to be Displayed.
void write_text(){
    inData[index] = inChar; // Store it
    index++; // Increment where to write next
    while(Serial.available() > 0){
         if(index < 254){
              inChar = Serial.read(); // Read a character
              inData[index] = inChar; // Store it
              delay(200);
              index++; // Increment where to write next
              inData[index] = 0; // Null terminate the string
         }
     }
}

// Scrolling Text the inData string on a 32x16 DMD 
void display_text(){
    Serial.println(inData);
    dmd.drawMarquee(inData,index,(32*1)-1,0); // This uses the Freeduino 32x16 DMD
    long start=millis();
    long timer=start;
    boolean ret=false;
    while(!ret){
        if ((timer+30) < millis()) {  //Speed of Marquee Text
             ret=dmd.stepMarquee(-1,0);
             timer=millis();
        }
    }
}
    while(Serial.available() == 0){ //check to see if data on Serial
        if (index > 0){ // if index is greater than 0, data must exist in inData, so display text. Otherwise do nothing.
            display_text();
        }else{
            break;
        }
    }

While is this a while loop? If index is greater than 0, this loop will never end. If it isn't, this loop will never do anything. Seems completely pointless to have a loop here.

    while(Serial.available() > 0){
        inChar = Serial.read(); // Read a character
            if (inChar == '~') { // ~ is the command to stop display of any text and to wipe the inData String.
                  cancel_text();
                  break;
            }else{
                  write_text(); // This will write your Serial text to a String. Note if you do not ~ before typing a new string, this will append your new string to the previous one.
                  break;
            }
    }

Again, why is this a while loop? If there is anything to read, you read it, and then call write_text which contains another while loop.

Thank you for your response.

without these while loops i seem to get some gibberish on my serial monitor

Power Onÿ
ÿÿ
ÿÿÿ
ÿÿÿÿ
ÿÿÿÿÿ
ÿÿÿÿÿÿ
ÿÿÿÿÿÿtest
ÿÿÿÿÿÿtestÿ
ÿÿÿÿÿÿtestÿÿ
ÿÿÿÿÿÿtestÿÿÿ
ÿÿÿÿÿÿtestÿÿÿÿ
ÿÿÿÿÿÿtestÿÿÿÿÿ
ÿÿÿÿÿÿtestÿÿÿÿÿÿ

The idea was to stop the loop if there was no data in the string. i figured referencing the index variable would have been best as it is reset to 0 when the Stop command is given.

Removing these While loops still do not resolve the initial issue of missing data

ÿ

Usually implies you're reading serial data that hasn't arrived yet.

ok thanks. So what in my code would be causing the dropping of characters into the string?

   while(Serial.available() > 0){
         if(index < 254){
              inChar = Serial.read(); // Read a character
              inData[index] = inChar; // Store it
              delay(200);
              index++; // Increment where to write next
              inData[index] = 0; // Null terminate the string
         }
     }

why is there a delay() in this code?

This seems to imply that you want to read 254 characters so why not have if (Serial.available() >= 254) ?

On the other hand if 254 is meant to be an upper limit then you need to be checking the incoming code for some character that signals the end of the data. I wrote a demo sketch here which illustrates how to send and receive data with start- and end-markers. It may give you an idea.

...R

This seems to imply that you want to read 254 characters so why not have if (Serial.available() >= 254) ?

Because the serial buffer is (unless modified) only 64 bytes. Serial.available() will never report more than 64 (unless the buffer size has been changed).

But you are quite right in that the delay has got to go.

PaulS:
Because the serial buffer is (unless modified) only 64 bytes. Serial.available() will never report more than 64 (unless the buffer size has been changed).

Good point.

I was really concentrating on the next paragraph of my reply in which 254 is an upper limit - which seems more a likely scenario.

...R

I have been working on a DMD scoreboard and found that the serial comms is unreliable when u have a high baud rate while driving DMDs -- use a lower one like 2400 which worked for me.

Hi,

I'm still not getting display print in DMD board, @H74N as you reported that you will get the display while selecting the lower baud rate like (2400), but in my case DMD board are not displaying any things.

could you please help as i'm new with arduino, as i need the display the DMD message through the PC.

My Program
// 12 Mar 2014
// this works with ComArduino.py and ComArduinoA4e.rb
// this version uses a start marker 254 and an end marker of 255
// it uses 253 as a special byte to be able to reproduce 253, 254 and 255
// it also sends data to the PC using the same system
// if the number of bytes is 0 the PC will assume a debug string and just print it to the screen

//================

#define startMarker 254
#define endMarker 255
#define specialByte 253
#define maxMessage 16

// the program could be rewritten to use local variables instead of some of these globals
// however globals make the code simpler
// and simplify memory management

byte bytesRecvd = 0;
byte dataSentNum = 0; // the transmitted value of the number of bytes in the package i.e. the 2nd byte received
byte dataRecvCount = 0;

byte dataRecvd[maxMessage];
byte dataSend[maxMessage];
byte tempBuffer[maxMessage];

byte dataSendCount = 0; // the number of 'real' bytes to be sent to the PC
byte dataTotalSend = 0; // the number of bytes to send to PC taking account of encoded bytes

boolean inProgress = false;
boolean startFound = false;
boolean allReceived = false;

//================

void setup() {
pinMode(13, OUTPUT); // the onboard LED
Serial.begin(9600);
debugToPC("Arduino Ready from ArduinoPC.ino");

delay(500);
blinkLED(5); // just so we know it's alive
}

//================

void loop() {

getSerialData();

processData();

}

//================

void getSerialData() {

// Receives data into tempBuffer[]
// saves the number of bytes that the PC said it sent - which will be in tempBuffer[1]
// uses decodeHighBytes() to copy data from tempBuffer to dataRecvd[]

// the Arduino program will use the data it finds in dataRecvd[]

if(Serial.available() > 0) {

byte x = Serial.read();
if (x == startMarker) {
bytesRecvd = 0;
inProgress = true;
// blinkLED(2);
// debugToPC("start received");
}

if(inProgress) {
tempBuffer[bytesRecvd] = x;
bytesRecvd ++;
}

if (x == endMarker) {
inProgress = false;
allReceived = true;

// save the number of bytes that were sent
dataSentNum = tempBuffer[1];

decodeHighBytes();
}
}
}

//============================

void processData() {

// processes the data that is in dataRecvd[]

if (allReceived) {

// for demonstration just copy dataRecvd to dataSend
dataSendCount = dataRecvCount;
for (byte n = 0; n < dataRecvCount; n++) {
dataSend[n] = dataRecvd[n];
}

dataToPC();

delay(100);
allReceived = false;
}
}

//============================

void decodeHighBytes() {

// copies to dataRecvd[] only the data bytes i.e. excluding the marker bytes and the count byte
// and converts any bytes of 253 etc into the intended numbers
// Note that bytesRecvd is the total of all the bytes including the markers
dataRecvCount = 0;
for (byte n = 2; n < bytesRecvd - 1 ; n++) { // 2 skips the start marker and the count byte, -1 omits the end marker
byte x = tempBuffer[n];
if (x == specialByte) {
// debugToPC("FoundSpecialByte");
n++;
x = x + tempBuffer[n];
}
dataRecvd[dataRecvCount] = x;
dataRecvCount ++;
}
}

//====================

void dataToPC() {

// expects to find data in dataSend[]
// uses encodeHighBytes() to copy data to tempBuffer
// sends data to PC from tempBuffer
encodeHighBytes();

Serial.write(startMarker);
Serial.write(dataSendCount);
Serial.write(tempBuffer, dataTotalSend);
Serial.write(endMarker);
}

//============================

void encodeHighBytes() {
// Copies to temBuffer[] all of the data in dataSend[]
// and converts any bytes of 253 or more into a pair of bytes, 253 0, 253 1 or 253 2 as appropriate
dataTotalSend = 0;
for (byte n = 0; n < dataSendCount; n++) {
if (dataSend[n] >= specialByte) {
tempBuffer[dataTotalSend] = specialByte;
dataTotalSend++;
tempBuffer[dataTotalSend] = dataSend[n] - specialByte;
}
else {
tempBuffer[dataTotalSend] = dataSend[n];
}
dataTotalSend++;
}
}

//=========================

void debugToPC( char arr[]) {
byte nb = 0;
Serial.write(startMarker);
Serial.write(nb);
Serial.print(arr);
Serial.write(endMarker);
}

//=========================

void debugToPC( byte num) {
byte nb = 0;
Serial.write(startMarker);
Serial.write(nb);
Serial.print(num);
Serial.write(endMarker);
}

//=========================

void blinkLED(byte numBlinks) {
for (byte n = 0; n < numBlinks; n ++) {
digitalWrite(13, HIGH);
delay(200);
digitalWrite(13, LOW);
delay(200);
}
}

prashantb2sharma:
I'm still not getting display print in DMD board, @H74N as you reported that you will get the display while selecting the lower baud rate like (2400), but in my case DMD board are not displaying any things.

could you please help as i'm new with arduino, as i need the display the DMD message through the PC.

You are quoting stuff from 2014 that I think I wrote. However I can't remember it all 4 years later.

Please modify your post and use the code button </> so your code looks like this and is easy to copy to a text editor. See How to use the Forum

Also please post a link to where you got the program.

Finally (for now) please tell us exactly what happens when you run the program and what you want it to do that is different.

...R