Go Down

Topic: Clearing an array sent from GSM module to display new message (Read 361 times) previous topic - next topic

NAJ9

Hi guys,

I am currently working on an electronic notice board project using the SIM900, Arduino uno and a 32x8 MAX7219 drove LED matrix. I am currently having issues when reading in more than one message. Currently I am reading in a message and it will scroll across the screen once and then freeze. Once I send another message this will do the same and scroll across the screen once and freeze. The message should continuously scroll across the screen until a new one is sent to replace it. I believe that the clearing of the array is messing up the scrolling but I am not sure why. The full code is below:

Code: [Select]
#include <LedControl.h>
#include "charmap.h"
#include <SoftwareSerial.h>

const int numDevices = 4;      // number of MAX7219s used
const long scrollDelay = 75;   // adjust scrolling speed
unsigned long bufferLong [14];
const byte numChars = 160;
char receivedChars[numChars];
int inbox = 0;
LedControl lc=LedControl(12,11,10,numDevices); // DOUT|CLK|CS|Devices
SoftwareSerial SIM900(7,8); // RX|TX
//---------------------------------------------------------------------------------------------------------
void setup(){
  for (int x=0; x<numDevices; x++){
      lc.shutdown(x,false);       // the MAX72XX is in power-saving mode on startup
      lc.setIntensity(x,8);       // set brightness of display
      lc.clearDisplay(x);         // clear display
  }
  digitalWrite(9, HIGH);    // turns on the shield
  delay(1000);
  digitalWrite(9, LOW);
  delay(5000);
  SIM900.begin(19200);   // sim900 operates at a baud rate of 19200
  Serial.begin(19200);   // sets the serial baud rate
  delay(10000);   // delay for GSM shield to log on
  SIM900.print("AT+CMGF=1\r");    // set sim900 to receive sms
  delay(100);
  SIM900.print("AT+CNMI=2,2,0,0,0\r");  // sms to be sent to serial output
  delay(100);
}
void loop(){
  scrollMessage();
}

void scrollMessage() {
  static boolean recvInProgress = false;
  static byte ndx = 0;
  char startMarker = '<';
  char endMarker = '>';
  char rc;
  boolean newData = false;
  int myChar = 0;
   
  while (SIM900.available() > 0 && newData == false) {
      if (inbox == 1){
          for(int i =0; i < 160; i++){
           receivedChars[i] = (char)0;
          }
          inbox = 0;
      }         
      rc = SIM900.read();
         
      if (recvInProgress == true) {
          if (rc != endMarker) {
              receivedChars[ndx] = rc;
              ndx++;
              if (ndx >= numChars) {
                  ndx = numChars - 1;
              }
          }
          else {
              receivedChars[ndx] = '\0'; // terminate the string
              recvInProgress = false;
              ndx = 0;
              newData = true;
              inbox++;
          }
      }
      else if (rc == startMarker) {
               recvInProgress = true;
      }
  }
  do {   
      for (int i = 0; i < 160; i++){
          myChar = receivedChars[i];
          if (myChar != 0){
              loadBufferLong(myChar);
          }
      }
    }
    while (myChar != 0);     
}
// Load character into scroll buffer
void loadBufferLong(int ascii){
  if (ascii >= 0x20 && ascii <=0x7f){
      for (int a=0;a<7;a++){                      // Loop 7 times for a 5x7 font
          unsigned long c = pgm_read_byte_near(font5x7 + ((ascii - 0x20) * 8) + a);     // Index into character table to get row data
          unsigned long x = bufferLong [a*2];     // Load current scroll buffer
          x = x | c;                              // OR the new character onto end of current
          bufferLong [a*2] = x;                   // Store in buffer
      }
      byte count = pgm_read_byte_near(font5x7 +((ascii - 0x20) * 8) + 7);     // Index into character table for kerning data
      for (byte x=0; x<count;x++){                                            // creates a space between characters
          rotateBufferLong();
          printBufferLong();
          delay(scrollDelay);
      }
  }
}
// Rotate the buffer
void rotateBufferLong(){
  for (int a=0;a<7;a++){                      // Loop 7 times for a 5x7 font
      unsigned long x = bufferLong [a*2];     // Get low buffer entry
      byte b = bitRead(x,31);                 // Copy high order bit that gets lost in rotation
      x = x<<1;                               // Rotate left one bit
      bufferLong [a*2] = x;                   // Store new low buffer
      x = bufferLong [a*2+1];                 // Get high buffer entry
      x = x<<1;                               // Rotate left one bit
      bitWrite(x,0,b);                        // Store saved bit
      bufferLong [a*2+1] = x;                 // Store new high buffer
  }

// Display Buffer on LED matrix
void printBufferLong(){
  for (int a=0;a<7;a++){                      // Loop 7 times for a 5x7 font
      unsigned long x = bufferLong [a*2+1];   // Get high buffer entry
      byte y = x;                             // mask for first character
      lc.setRow(3,a,y);                       // send row to relevent MAX7219 chip
      x = bufferLong [a*2];                   // Get low buffer entry
      y = (x>>24);                            // mask for second character
      lc.setRow(2,a,y);                       
      y = (x>>16);                            // mask for third character
      lc.setRow(1,a,y);                       
      y = (x>>8);                             // mask for fourth character
      lc.setRow(0,a,y);                       
  }
}

Deva_Rishi

Have you sorted your long message error out already ? I think you'll have to restructure your program a little, i suggest you actually use the non-blocking properties of your reception.

- Create a separate function that handles the reception of a message. (the first part of what is now
 scrollMessage() until the do {} while() ) (let's call it receiveMsg() )

- Create a function that loads the characters into the buffer (the 2nd part of what is now scrollMessage() ) and when it has reached the terminating null, start over with the first character. (let's call it loadChars() )

- the delay(scrollDelay); at the end of loadBufferLong() will have to go, instead you'll have to fire the function loadChars() using millis()

And yes you may need a second Array, 1 to receive the message in and 1 to draw characters of the previously received message out off, but that is not the reason that it is not continuously scrolling.
To 'Correct' you have to be Correct. (and not be condescending..)

NAJ9

I tried to increase the serial buffer to 256 from the 64, but didn't have much luck changing it so this is still an issue. I used this link http://www.hobbytronics.co.uk/arduino-serial-buffer-size

In terms of the scrolling I have successfully got it to scroll when I remove the code that clears the array, but then when I send a new message shorter than the previous that message is still present and only part of it is overwritten because the array hasnt been cleared.

I'll try splitting it up and see if i have any success.
How come the (scrollDelay) needs to be removed?
Thanks for the reply :)

NAJ9

Another thing I don't understand:

Code: [Select]
do {   
      for (int i = 0; i < 160; i++){
          myChar = receivedChars[i];
          if (myChar != 0){
              loadBufferLong(myChar);
          }
      }
     
    }
    while (myChar != 0);


In this statement once the first message has been sent the receivedChars array will always have data inside of it because once a new message is being detected, the array is cleared and then the characters are read into the array so surely this will continue to loop and display the current message over and over?

What seems to happen is once the message has been displayed this statement no longer loops, until a new message comes in where the new one is displayed and then the loop stops again. But surely once the null character is reached it will go back to the start of scrollMessage and therefore go through the array once again?

Deva_Rishi

I am also not so sure why your code doesn't quite work the way you want it to, but i did some restructuring and tested this code with the hwSerial monitor and i think it should work for you.
Code: [Select]
#include <LedControl.h>
#include "charmap.h"
#include <SoftwareSerial.h>

const int numDevices = 4;      // number of MAX7219s used
const long scrollDelay = 75;   // adjust scrolling speed
unsigned long bufferLong [14];
const byte numChars = 160;
char receivedChars[numChars],loopChars[numChars+5];


bool copyChars=false;
uint32_t scrollTimer=0;

LedControl lc=LedControl(12,11,10,numDevices); // DOUT|CLK|CS|Devices
SoftwareSerial SIM900(7,8); // RX|TX
//---------------------------------------------------------------------------------------------------------
void setup(){
  //Serial.begin(19200);
  for(int i =0; i < numChars; i++){
    receivedChars[i] = (char)0;
  }
  for (int i=0; i<numChars+5; i++) {
    loopChars[i] = (char) 0;
  }

  for (int x=0; x<numDevices; x++){
    lc.shutdown(x,false);       // the MAX72XX is in power-saving mode on startup
    lc.setIntensity(x,8);       // set brightness of display
    lc.clearDisplay(x);         // clear display
  }
  digitalWrite(9, HIGH);    // turns on the shield
  delay(1000);
  digitalWrite(9, LOW);
  delay(5000);
  SIM900.begin(19200);   // sim900 operates at a baud rate of 19200
  Serial.begin(19200);   // sets the serial baud rate
  delay(10000);   // delay for GSM shield to log on
  SIM900.print("AT+CMGF=1\r");    // set sim900 to receive sms
  delay(100);
  SIM900.print("AT+CNMI=2,2,0,0,0\r");  // sms to be sent to serial output
  delay(100);
}


void loop(){
  if (copyChars) copyMessage();
  LoopMessage();
  if (ReceiveMessage())  copyChars=true;
}

void copyMessage() {  // copy the message into the loop buffer
  int i=0;
  while ((receivedChars[i]) && (i<numChars)) {
    loopChars[i]=receivedChars[i];
    i++;
  }
  loopChars[i]=' ';   // these are the message separating chars
  loopChars[i+1]='-';
  loopChars[i+2]='-';
  loopChars[i+3]=' ';
  loopChars[i+4]='\0';
}

bool ReceiveMessage() {
  static boolean recvInProgress = false;
  static byte ndx = 0;
  char startMarker = '<';
  char endMarker = '>';
  char rc;
  boolean newData = false;
   
  while (SIM900.available() > 0 && newData == false) {             
    rc = SIM900.read();         
    if (recvInProgress == true) {
      if (rc != endMarker) {
        receivedChars[ndx] = rc;
        ndx++;
        if (ndx >= numChars) {
            ndx = numChars - 1;
        }
      }
      else {
        receivedChars[ndx] = '\0'; // terminate the string
        recvInProgress = false;
        ndx = 0;
        newData = true;
        //inbox++;   // if you want to keep track of the amount of mssages received, but there is no need
      }
    }
    else if (rc == startMarker) {
      recvInProgress = true;
    }
  }
  return newData;
}

void LoopMessage () {  // loop through the current message
  unsigned int i=0;
  char myChar = loopChars[0];
  while ((myChar) && (i<numChars+5)) {
    if (myChar){
      loadBufferLong(myChar);
    }
    i++;
    myChar = loopChars[i];
  }       
}


void loadBufferLong(int ascii){  // Load character into scroll buffer

  if (ascii >= 0x20 && ascii <=0x7f) ) {
    for (int a=0;a<7;a++){                      // Loop 7 times for a 5x7 font
        //unsigned long c = pgm_read_byte_near(font5x7 + ((ascii - 0x20) * 8) + a);     // Index into character table to get row data
        unsigned long x = bufferLong [a*2];     // Load current scroll buffer
        //x = x | c;                              // OR the new character onto end of current
        bufferLong [a*2] = x;                   // Store in buffer
    }
    byte count = 9; //pgm_read_byte_near(font5x7 +((ascii - 0x20) * 8) + 7);     // Index into character table for kerning data
    for (byte x=0; x<count;x++){                                            // creates a space between characters
      rotateBufferLong();
      printBufferLong();

      while (millis()-scrollTimer<scrollDelay) {
        if (ReceiveMessage())  copyChars=true;
      }
      scrollTimer=millis();
    }     
  }
}

void rotateBufferLong(){  // Rotate the buffer
 
  for (int a=0;a<7;a++){                      // Loop 7 times for a 5x7 font
    unsigned long x = bufferLong [a*2];     // Get low buffer entry
    byte b = bitRead(x,31);                 // Copy high order bit that gets lost in rotation
    x = x<<1;                               // Rotate left one bit
    bufferLong [a*2] = x;                   // Store new low buffer
    x = bufferLong [a*2+1];                 // Get high buffer entry
    x = x<<1;                               // Rotate left one bit
    bitWrite(x,0,b);                        // Store saved bit
    bufferLong [a*2+1] = x;                 // Store new high buffer
  }


void printBufferLong(){  // Display Buffer on LED matrix
 
  for (int a=0;a<7;a++){                      // Loop 7 times for a 5x7 font
    unsigned long x = bufferLong [a*2+1];   // Get high buffer entry
    byte y = x;                             // mask for first character
    lc.setRow(3,a,y);                       // send row to relevent MAX7219 chip
    x = bufferLong [a*2];                   // Get low buffer entry
    y = (x>>24);                            // mask for second character
    lc.setRow(2,a,y);                       
    y = (x>>16);                            // mask for third character
    lc.setRow(1,a,y);                       
    y = (x>>8);                             // mask for fourth character
    lc.setRow(0,a,y);                       
  }
}
You didn't include a link to the libraries you used, so i do hope there are no compile errors after i un-commented all those references (and removed Serial reads & prints)
To 'Correct' you have to be Correct. (and not be condescending..)

NAJ9

Sorry I really should have mentioned this before. The include "charmap.h" includes the ASCII definition of each character in binary (I have attached it to this message).
Code: [Select]
do {   
      for (int i = 0; i < 160; i++){
          myChar = receivedChars[i];
          if (myChar != 0){
              loadBufferLong(myChar);
          }
      }
     
    }
    while (myChar != 0);

So in this code it is passing the decimal value of the read in character eg when 'A' is in the array the decimal number 65 will be passed to the loadbufferlong() to find the correct binary pattern in the lookup table stored in PROGMEM which uses #include <avr/pgmspace.h>
Using the PROGMEM allows the use of the commands to read the binary sequence of each character so this needs to be in the code.

Will have a look at your code now and see if I can implement it into mine.



Deva_Rishi

Actually my code is a modification of yours, should run straight out of the box, during my day i remembered some inaccuracies.
Quote
So in this code it is passing the decimal value of the read in character eg when 'A' is in the array the decimal number 65 will be passed to the loadbufferlong() to find the correct binary pattern in the lookup table
yeah i figured as much.
anyway
Code: [Select]
char receivedChars[numChars+1],loopChars[numChars+5]; this should be the declaration of receivedChars[] the length of the cstring +1 (for the null terminator)
and this:
Code: [Select]
for(int i =0; i < numChars; i++){
    receivedChars[i] = (char)0;
  }
  for (int i=0; i<numChars+5; i++) {
    loopChars[i] = (char) 0;
  }
is exagerating things, only the first byte needs to be the terminating null
Code: [Select]
receivedChars[0] = (char)0;
loopChars[0] = (char) 0;
but it won't affect functionality (the other thing only if the sms is actually the maximum 160 characters, though then most probably your program will crash)
To 'Correct' you have to be Correct. (and not be condescending..)

NAJ9

Just tried out your code and it seems to be working correctly!
Thank you for your help. The only other thing is sorting out that previous problem with reading in large messages. What is the easiest way to increase the serial buffer as the method I tried before didn't work.

NAJ9

Also how exactly does this clear the previous message when the new one is read in, as the only time I can see the array being cleared is in the setup?

Deva_Rishi

Just tried out your code and it seems to be working correctly!
Thank you for your help. The only other thing is sorting out that previous problem with reading in large messages. What is the easiest way to increase the serial buffer as the method I tried before didn't work.
Do you still have the issue ? you shouldn't have unless the cause is different to what i thought it was. check it out let me know
i think/thought that because the printing of the loop took so long that the Serial buffer was getting over-run, but now the Serial buffer is read from within the LoadBufferLong() mainly (the call to ReceiveMessage() from within loop() is actually only there to make sure a call to it also happens if there are no characters in the loopChar[])
Quote
Also how exactly does this clear the previous message when the new one is read in, as the only time I can see the array being cleared is in the setup?
The array does not need to be cleared. It is copied into the array that does the loop (only after the loopArray has been printed completely) , and once the index has been reset it is simple overwritten and a new null terminator added when the message is complete
To 'Correct' you have to be Correct. (and not be condescending..)

Deva_Rishi

Oh hey, i just thought, that we do run the risk of losing an sms if another one is sent during the time the previous one is still being displayed. Maybe sms reception should be temporarily disabled until the received sms has been copied into loopChars[]
To 'Correct' you have to be Correct. (and not be condescending..)

NAJ9

The array does not need to be cleared. It is copied into the array that does the loop (only after the loopArray has been printed completely) , and once the index has been reset it is simple overwritten and a new null terminator added when the message is complete
Yes this makes sense now.

Do you still have the issue ? you shouldn't have unless the cause is different to what i thought it was. check it out let me know
i think/thought that because the printing of the loop took so long that the Serial buffer was getting over-run, but now the Serial buffer is read from within the LoadBufferLong() mainly (the call to ReceiveMessage() from within loop() is actually only there to make sure a call to it also happens if there are no characters in the loopChar[])
I just did a load of testing and the issue is this:
The display seems to have no issues with a low amount of characters say <40, but for example if I send in a 120 character message and it reads in 65 characters. These 65 characters will be printed and it will loop. If I send a new message it will then print the 65 characters and then sometimes it will start printing out other data received from the serial monitor, so the phone number the text was sent from, the date and the time.
However, I have been able to read in and display 100+ characters occasionally, but it seems to break most of the time.
So as of now I am not quite sure what is happening. How could it start reading and printing data from the serial monitor when it must be within the start/end marker to even be read in?

NAJ9

I also just found another strange situation. Lets say I send the message "the quick brown fox jumps over the lazy dog". This prints out without an issue. I send a new message that says "Testing display".
This time the message printed out was "Testing disrown fox jumps over the lazy dog". I then send a third message "ABC" and get "Testing dis+CMT phone number/date/time <ABC".

These seem to be the two different scenarios where the display messes up.

Deva_Rishi

Quote
it will then print the 65 characters and then sometimes it will start printing out other data received from the serial monitor, so the phone number the text was sent from, the date and the time.
However, I have been able to read in and display 100+ characters occasionally, but it seems to break most of the time.
I suggest you connect your GSM module directly to the Serial monitor to see what the characters actually are that it sends, cause i can not find any reason for the behavior that you describe. I don't have you module hence the testing program i am using is the following
Code: [Select]
//#include <LedControl.h>
//#include "charmap.h"
//#include <SoftwareSerial.h>

const int numDevices = 4;      // number of MAX7219s used
const long scrollDelay = 75;   // adjust scrolling speed
unsigned long bufferLong [14];
const byte numChars = 160;
char receivedChars[numChars],loopChars[numChars+5];

int inbox = 0;
bool copyChars=false;
//LedControl lc=LedControl(12,11,10,numDevices); // DOUT|CLK|CS|Devices
//SoftwareSerial SIM900(7,8); // RX|TX
//---------------------------------------------------------------------------------------------------------
void setup(){
  Serial.begin(19200);
  loopChars[0] = (char) 0;

  /*for (int x=0; x<numDevices; x++){
      lc.shutdown(x,false);       // the MAX72XX is in power-saving mode on startup
      lc.setIntensity(x,8);       // set brightness of display
      lc.clearDisplay(x);         // clear display
  }
  digitalWrite(9, HIGH);    // turns on the shield
  delay(1000);
  digitalWrite(9, LOW);
  delay(5000);
  SIM900.begin(19200);   // sim900 operates at a baud rate of 19200
  Serial.begin(19200);   // sets the serial baud rate
  delay(10000);   // delay for GSM shield to log on
  SIM900.print("AT+CMGF=1\r");    // set sim900 to receive sms
  delay(100);
  SIM900.print("AT+CNMI=2,2,0,0,0\r");  // sms to be sent to serial output
  delay(100);*/
}


void loop(){
  if (copyChars) copyMessage();
  LoopMessage();
  if (ReceiveMessage())  copyChars=true;
}

void copyMessage() {
  int i=0;
  while ((receivedChars[i]) && (i<numChars)) {
    loopChars[i]=receivedChars[i];
    i++;
  }
  loopChars[i]='\n';
  loopChars[i+1]='\0';
}

bool ReceiveMessage() {
  static boolean recvInProgress = false;
  static byte ndx = 0;
  char startMarker = '<';
  char endMarker = '>';
  char rc;
  boolean newData = false;
   
  //while (SIM900.available() > 0 && newData == false) {
  while (Serial.available() > 0 && newData == false) {
             
      //rc = SIM900.read();
      rc = Serial.read();
         
      if (recvInProgress == true) {
          if (rc != endMarker) {
              receivedChars[ndx] = rc;
              ndx++;
              if (ndx >= numChars) {
                  ndx = numChars - 1;
              }
          }
          else {
              receivedChars[ndx] = '\0'; // terminate the string
              recvInProgress = false;
              ndx = 0;
              newData = true;
              inbox++;
          }
      }
      else if (rc == startMarker) {
               recvInProgress = true;
      }
  }
  return newData;
}

void LoopMessage () {
  if (!inbox) return;  // no message received yet
  unsigned int i=0;
  char myChar = loopChars[0];
  while ((myChar) && (i<numChars+5)) {
    if (myChar){
      loadBufferLong(myChar);
    }
    i++;
    myChar = loopChars[i];
  }       
}


// Load character into scroll buffer
void loadBufferLong(int ascii){

  if ((ascii >= 0x20 && ascii <=0x7f) || (ascii==10)) {
      for (int a=0;a<7;a++){                      // Loop 7 times for a 5x7 font
          //unsigned long c = pgm_read_byte_near(font5x7 + ((ascii - 0x20) * 8) + a);     // Index into character table to get row data
          unsigned long x = bufferLong [a*2];     // Load current scroll buffer
          //x = x | c;                              // OR the new character onto end of current
          bufferLong [a*2] = x;                   // Store in buffer
      }
      byte count = 9; //pgm_read_byte_near(font5x7 +((ascii - 0x20) * 8) + 7);     // Index into character table for kerning data
      for (byte x=0; x<count;x++){                                            // creates a space between characters
          //rotateBufferLong();
          //printBufferLong();
          delay(scrollDelay);
          if (ReceiveMessage())  copyChars=true;
      }
     
      printCharacter(ascii);
  }
}


// Rotate the buffer
/*void rotateBufferLong(){
  for (int a=0;a<7;a++){                      // Loop 7 times for a 5x7 font
      unsigned long x = bufferLong [a*2];     // Get low buffer entry
      byte b = bitRead(x,31);                 // Copy high order bit that gets lost in rotation
      x = x<<1;                               // Rotate left one bit
      bufferLong [a*2] = x;                   // Store new low buffer
      x = bufferLong [a*2+1];                 // Get high buffer entry
      x = x<<1;                               // Rotate left one bit
      bitWrite(x,0,b);                        // Store saved bit
      bufferLong [a*2+1] = x;                 // Store new high buffer
  }
}  */


// Display Buffer on LED matrix
/*void printBufferLong(){
  for (int a=0;a<7;a++){                      // Loop 7 times for a 5x7 font
      unsigned long x = bufferLong [a*2+1];   // Get high buffer entry
      byte y = x;                             // mask for first character
      lc.setRow(3,a,y);                       // send row to relevent MAX7219 chip
      x = bufferLong [a*2];                   // Get low buffer entry
      y = (x>>24);                            // mask for second character
      lc.setRow(2,a,y);                       
      y = (x>>16);                            // mask for third character
      lc.setRow(1,a,y);                       
      y = (x>>8);                             // mask for fourth character
      lc.setRow(0,a,y);                       
  }
}*/

void printCharacter(int ascii) {
  Serial.print((char) ascii);
}
which just uses the communication that you specified over the Serial port (and allows for a non-printable '\n' to be echoed as well) instead of using the GSM module. The date time thing that was sent indicates that the GSM is not sending what we are expecting at all. We need to know what and how it is sending exactly, so first connect it straight to the Serial monitor, and see what that does after you send the commands : "AT+CMGF=1\r" & "AT+CNMI=2,2,0,0,0\r"
To 'Correct' you have to be Correct. (and not be condescending..)

NAJ9

Ok so on the serial monitor when a message is sent it looks like the following:
+CMT: "+123456789","","19/01/13,20:11:20+00"
<The quick brown fox jumps over the lazy dog>

I can see for example if a long message is cut off it will look like this:

+CMT: "+123456789","","19/01/13,20:11:47+00"
<Abcdefghijklmnopqrstuvwxyz28Abcdefghijklmnopqrstuvwxyz56Abcdefghijklmnopqrs
+CMT: "+123456789","","19/01/13,20:13:17+00"
<Test>

So when it cuts off a message and a new one is sent thats why it prints out the date and time.
Am I correct in saying the issue is that the data isn't being read in fast enough then?
Although this doesn't make much sense as this same issue can occur for small messages just not as often. I'm thinking it is to do with the while statement to read in the data?

Go Up