Trade you a MCP7940 RTC lib for some advice on serial communications?

Hello everyone, thanks for taking a look!
I am working on what is essentially a library for an MPC7940 real time clock chip to be used in a different project.
I am attempting to create a structure within the arduino to communicate with processing over serial and then send the data to the RTC. The RTC is accessed by the arduino over I2C,
I have the chip communicating nicely with my arduino and I can even send a nice 11 byte unix epoch time code and watch the RTC chip update.

All is well with that I hope other people might find that part of the code helpful.

For debigging (lol debugging) purposes, I would like to add several other commands to read a few other registers on the RTC such as to check the oscillator status or manipulate the alarms. My problem exists in the processSyncMessage() function, as I dont know how to filter serial messages based on their length. I am very new to programming so I wont be suprised at all if while loops are a terrible way of accomplishing this task.

What I want: To be able to send codes of different lengths and give the arduino a threshold to differentiate between them.
I.e. serial messages > 5 triggers someFunction(), serial messages < 5 triggers someOtherFunction()

**What happens:**I can only send one command and then the while loops get confused and wont respond to the commands. I can succesfully send a command under or over the threshold, however only one before I have to reset the arduino.

I think what I am looking for is some way of clearing the serial data so the length of the previous command isn't included in the next command?

Anyways, thanks for any help, if I find the answer before anyone responds I will post the solution in case anyone else is having a problem similar to this.

Please excuse the multiple posts, I guess my massive amount of debug codes aren't so friendly to rackspace.

#include <Time.h>
#include <Wire.h>
int rtc_address = 0x6F;
#define TIME_MSG_LEN  11 
#define TIME_HEADER  'T' ;  // Header tag for serial time sync message
#define TIME_REQUEST  7    // ASCII bell character requests a time sync message 
#define time_format 63 //bit 6: 0 = 24 hour format, 1 = 12 hour
#define month_mask 31 //for discarding leap year bit
#define sec_mask 127 //for discarding leap year bit
#define osc_on 128 //sets first bit to 1 for starting osc.
#define first_five 7 //for discarding bits 7-2


void setup()
{
  
   Serial.println("begin"); 
  Wire.begin();        // join i2c bus (address optional for master)
  Serial.begin(9600);  // start serial for output
  

  
  Serial.println("test time sent"); 
  read_Time();
  Serial.println("----read test----"); 
 const char timeHeader = TIME_HEADER;
  
  
}






long processSyncMessage() {
  const char timeHeader = TIME_HEADER;
  char osc_test = 'O';
  char reload_header = 'R';
  char stop_osc = 'S';
  char set_alarm = 'A';
  
  // if time sync available from serial port, update time and return true
  while(Serial.available() >=  TIME_MSG_LEN ){  // time message consists of header & 10 ASCII digits
    char c = Serial.read() ;
    Serial.print(c);  
    Serial.println("-header");  
    if( c == timeHeader ) {  
    Serial.println("headed"); 
      time_t pctime = 0;
      for(int i=0; i < TIME_MSG_LEN -1; i++){  
        c = Serial.read();          
        if( c >= '0' && c <= '9'){  
          pctime = (10 * pctime) + (c - '0') ; // convert digits to a number    
        }
      }  
      
    setTime(pctime);
    set_Time();
    delay(100);
    }  
    Serial.flush();
  }
  
  while(Serial.available() ==  2 ){
    char b = Serial.read() ;
    Serial.print(b);  
    Serial.println("-header");
  if ( b == reload_header ) {
      Serial.println("rereading");
      read_Time();
      
    }
    
    if ( b == osc_test ) {
     int oscBit =  readFunction(0x03) & 32;
      if (oscBit >= 1){
      Serial.println("running");}
      else{
        Serial.println("stopped");}
    }
       if ( b == stop_osc ) {
         writeFunction(0x00, 0); //stop oscillator
     int oscBit =  readFunction(0x03) & 32;
      if (oscBit < 1){
      Serial.println("stopped");}
      else{
        Serial.println("running");}
    }
    if ( b == set_alarm ) {
         setAlarm(2);
      
        Serial.println("Alarm set to");
      
    }
    
    
  }
  
  
  
}








void loop()
{
 
 
  if (Serial.available()) {
    //recieve_sync();
    processSyncMessage();
    Serial.flush();
  }
  
  Serial.println("loop");
while (!Serial.available()){}
  int alarm_flag = readFunction(0x0D);
  Serial.println(alarm_flag);
  for(int af = 0; af == 60; af++){
  Serial.print(alarm_flag & 8);
   if (alarm_flag & 8 > 1){
   Serial.print(alarm_flag & 8);
   Serial.print("alarm triggered");
   writeFunction(0x0D, 32); //reset alarm bit
  
   }
  }
  
  
  
   delay(2000);
}











void writeFunction(byte reg_address, byte rtc_write_data) {
   
  
  Wire.beginTransmission(rtc_address);    // send to slave ID 0x6F =  
  Wire.write(reg_address); //send address
  Wire.write(rtc_write_data); //send date
  Wire.endTransmission(); //End
  Serial.print(rtc_write_data);
  Serial.print(" - sent to address:");  //sent
  Serial.println(reg_address); //announce
  
}

 byte readFunction(byte reg_address) {
  //Serial.println(reg_address); //announce reading address
  Wire.beginTransmission(rtc_address);    // send to slave ID 0x6F =  
  Wire.write(reg_address);
  Wire.endTransmission(); // send reg address to device address
  Wire.requestFrom(rtc_address, 1); //request reg contents
  byte rtc_read_data = Wire.read();
  return rtc_read_data;
    
  }
  
  void set_Time(){
    Serial.println("atmegatime");
    Serial.print("sec ");
    Serial.println(second());
    Serial.print("min ");
    Serial.println(minute());
    Serial.print("hour ");
    Serial.println(hour());
    Serial.print("day ");
    Serial.println(day());
    Serial.print("weekday ");
    Serial.println(weekday());
    Serial.print("month ");
    Serial.println(month());
    Serial.print("year ");
    Serial.println(year());
    delay(100);
    
    
    byte pc_sec= decToBcd(second()); 
    byte pc_min= decToBcd(minute());
    byte pc_hour= decToBcd(hour()-8); //PST baby
    byte pc_day= decToBcd(weekday());
    byte pc_date= decToBcd(day());
    byte pc_month= decToBcd(month());
    byte pc_year= decToBcd(year()-2000); //fuck it new millenium
    byte pc_control = 0;
    
    Serial.println("- BCD sending times -");
    Serial.print("sec ");
    Serial.println(pc_sec);
     Serial.print("min ");
    Serial.println(pc_min);
    Serial.print("hour ");
    Serial.println(pc_hour);
    Serial.print("day ");
    Serial.println(pc_day);
    Serial.print("weekday ");
    Serial.println(pc_date);
    Serial.print("month ");
    Serial.println(pc_month);
    Serial.print("year ");
    Serial.println(pc_year);
    
    writeFunction(0x00, 0); //stop oscillator
    writeFunction(0x01, pc_min & 127);
    writeFunction(0x02, pc_hour & time_format);
    writeFunction(0x03, pc_day & first_five );
    writeFunction(0x04, pc_date & 63);
    writeFunction(0x05, pc_month & month_mask);
    writeFunction(0x06, pc_year );
    //writeFunction(0x07, pc_control);
    writeFunction(0x00, pc_sec | osc_on); //set sec and start osc
    
    loop();
  }
  
    void read_Time(){
      
      byte clock_sec = 0;
      byte clock_min = 0;
      byte clock_hour = 0;
      byte clock_day = 0;
      byte clock_date = 0;
      byte clock_month = 0;
      byte clock_year = 0;
      byte clock_control = 0;
      
      
     
   clock_sec = readFunction(0x00);
   clock_min = readFunction(0x01);
   clock_hour  = readFunction(0x02);
   clock_day = readFunction(0x03);
   clock_date = readFunction(0x04);
   clock_month =  readFunction(0x05);
   clock_year =  readFunction(0x06);
   clock_control =  readFunction(0x07);
      
      Serial.println("-rtc time----bcd---masked-------");
      Serial.print("rtcsec ");
      Serial.print(clock_sec);
      Serial.print(" - ");
      Serial.println(bcdToDec(clock_sec & sec_mask));
      Serial.print("rtcmin ");
      Serial.print(clock_min);
      Serial.print(" - ");
      Serial.println(bcdToDec(clock_min & sec_mask));
      Serial.print("rtchour ");
      Serial.print(clock_hour);
      Serial.print(" - ");
      Serial.println(bcdToDec(clock_hour & time_format));
      Serial.print("rtcday ");
      Serial.print(clock_day);
      Serial.print(" - ");
      Serial.println(bcdToDec(clock_day & first_five));
      Serial.print("rtcdate ");
      Serial.print(clock_date);
      Serial.print(" - ");
      Serial.println(bcdToDec(clock_date & time_format));
      Serial.print("rtcmonth ");
      Serial.print(clock_month);
      Serial.print(" - ");
      Serial.println(bcdToDec(clock_month & month_mask));
      Serial.print("rtcyear ");
      Serial.print(clock_year);
      Serial.print(" - ");
      Serial.println(bcdToDec(clock_year ));
    loop();
  }
  
  int setAlarm(int timeAdd){
    Serial.println("atmegatime");
    Serial.print("sec");
    Serial.println(second());
    Serial.print("min");
    Serial.println(minute());
    Serial.print("hour");
    Serial.println(hour());
    Serial.print("day");
    Serial.println(day());
    Serial.print("weekday");
    Serial.println(weekday());
    Serial.print("month");
    Serial.println(month());
    Serial.print("year");
    Serial.println(year());
    Serial.print("alarm time add");
    Serial.println(timeAdd);
    delay(100);
    
    
    byte pc_sec= decToBcd(second()); 
    byte pc_min= decToBcd(minute()+timeAdd);
    byte pc_hour= decToBcd(hour());
    byte pc_day= decToBcd(weekday());
    byte pc_date= decToBcd(day());
    byte pc_month= decToBcd(month());
    byte pc_year= decToBcd(year()-2000);
    byte pc_control = 0;
    
    Serial.println("- BCD sending times -");
    Serial.println(pc_sec);
    Serial.println(pc_min);
    Serial.println(pc_hour);
    Serial.println(pc_day);
    Serial.println(pc_date);
    Serial.println(pc_month);
    Serial.println(pc_year);
    
    writeFunction(0x00, 0); //stop oscillator
    writeFunction(0x01, pc_min & 127);
    writeFunction(0x02, pc_hour & time_format);
    writeFunction(0x03, pc_day & first_five );
    writeFunction(0x04, pc_date & 63);
    writeFunction(0x05, pc_month & month_mask);
    writeFunction(0x06, pc_year );
    //writeFunction(0x07, pc_control);
    writeFunction(0x00, pc_sec | osc_on); //set sec and start osc
    
    loop();
  }






byte bcdToDec(byte val)
{
  return ( (val/16*10) + (val%16) );
}

byte decToBcd(byte val)
{
  return ( (val/10*16) + (val%10) );
}

tip: improve your coding style
both Arduino and Processing have a CTRL-T autoformatting function.
That makes code more readable by indenting in a consistent way.
Also to much empty lines between functions, decrease readability.

The first error is already in popup

void setup()
{
  
   Serial.println("begin");   // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< what baudrate should it use?
  Wire.begin();        // join i2c bus (address optional for master)
  Serial.begin(9600);  // start serial for output

Oooh, Good catch. Thanks for the advice!
I admit this is very messy, this is maybe my third sketch so I am not familiar most of the conventions. All that white space was supposed to make is easier for my brain to process this in chunks.. :fearful:

Oh wow, CTRL-T, you spiffy little hotkey.

At least you did it for the good reason :slight_smile:

Don't use comments until they explain something properly'

#define month_mask 31         //for discarding leap year bit
#define sec_mask 127             //for discarding leap year bit

copy paste?

refactored your first chunk ( a bit) with some comments, just to get a feeling of how style can help (it is definitely not 100% yet)
Note I did not restructure the code itself except for the switch() instead of multiple if statements.

// use a header to have some meta information for version control etc.
//
//    FILE: .ino
//  AUTHOR:  
// VERSION: 0.1. 
// PURPOSE:  
//    DATE: 2013-12-...
//     URL:
//
// Released to the public domain
//

#include <Time.h>
#include <Wire.h>

int rtc_address = 0x6F;

// RTC communication constants
#define TIME_MSG_LEN  11 
#define TIME_HEADER  'T' ;  // Header tag for serial time sync message
#define TIME_REQUEST  7    // ASCII bell character requests a time sync message 

// 'standard' states that #defines use CAPITALS like above
// RTC masks
#define time_format 63          // bit 6: 0 = 24 hour format, 1 = 12 hour    <<< good comment.
#define month_mask 31
#define sec_mask 127
#define osc_on 128
#define first_five 7

void setup()
{
  Serial.begin(9600);   // use 115200 if possible;  is 12x as fast
  Serial.println("Begin"); 

  Wire.begin(); 

  Serial.println("test time sent"); 
  read_Time();
  Serial.println("----read test----"); 
}

//
// explain purpose of function here
//
long processSyncMessage() 
{
  const char timeHeader = TIME_HEADER;
  char osc_test = 'O';
  char reload_header = 'R';
  char stop_osc = 'S';
  char set_alarm = 'A';

  // if time sync available from serial port, update time and return true
  while (Serial.available() >=  TIME_MSG_LEN )        // time message consists of header & 10 ASCII digits
  {
    char c = Serial.read() ;
    Serial.print(c);  
    Serial.println("-header");
    
    if ( c == timeHeader ) 
    {  
      Serial.println("headed"); 
      time_t pctime = 0;
      for (int i=0; i < TIME_MSG_LEN -1; i++)
      {  
        c = Serial.read();          
        if ( c >= '0' && c <= '9')
        {  
          pctime = (10 * pctime) + (c - '0') ; // convert digits to a number    
        }
        else
        {
          // ??? error ???
        }
      }  

      setTime(pctime);
      set_Time();
      delay(100);
    }  
    Serial.flush();
  }

  while (Serial.available() ==  2 )  // VERY dangerous as one extra char would let this fail..
  {
    char command = Serial.read() ;  // renamed b to command as that tells more about 
    Serial.print(b);  
    Serial.println("-header");

    switch (command )  // this is the way to handle multiple if statements 
    {
    case reload_header:
      Serial.println("rereading");
      read_Time();
      break;

    case test:
      int oscBit = readFunction(0x03) & 32;
      if (oscBit >= 1) Serial.println("running");
      else Serial.println("stopped");
      break;

    case stop_osc:
      writeFunction(0x00, 0);
      int oscBit =  readFunction(0x03) & 32;
      if (oscBit < 1) Serial.println("stopped");
      else Serial.println("running");
      break;

    case set_alarm:
      setAlarm(2);
      Serial.println("Alarm set to");
      break;

    default:
      break;
    }
  }
}

Again thanks for the tips! I see why sometimes it is frustrating finding documentation for software, the software is the documentation.

The switch case worked almost perfectly, however I had to add some brackets for each case to keep it from getting confused. Now it seems like all of the bytes are properly handled as well because the while loop successfully pauses the loop() function when there is no serial available. Am I correct in thinking that no loop() progress means no left over serial bytes?

robtillaart:

  while (Serial.available() ==  2 )  // VERY dangerous as one extra char would let this fail..

What do you think would be a safer alternative? Longer command codes perhaps?

Heres what I changed it to in case anyone is curious:

long processSyncMessage() 
{
  char timeHeader = TIME_HEADER;
  const char osc_test = 'O';
  const char reload_header = 'R';
  const char stop_osc = 'S';
  const char set_alarm = 'A';

  // if time sync available from serial port, update time and return true
  while (Serial.available() >=  TIME_MSG_LEN )        // time message consists of header & 10 ASCII digits
  {
    char c = Serial.read() ;
    Serial.print(c);  
    Serial.println("-header");

    if ( c == timeHeader ) 
    {  
      Serial.println("headed"); 
      time_t pctime = 0;
      for (int i=0; i < TIME_MSG_LEN -1; i++)
      {  
        c = Serial.read();          
        if ( c >= '0' && c <= '9')
        {  
          pctime = (10 * pctime) + (c - '0') ; // convert digits to a number    
        }
        else
        {
          // ??? error ???
        }
      }  

      setTime(pctime);
      set_Time();
      delay(100);
    }  
    Serial.flush();
  }

  while (Serial.available() ==  2 )  // VERY dangerous as one extra char would let this fail..
  {
    char command = Serial.read() ;  // renamed b to command as that tells more about 
    Serial.print(command);  
    Serial.println("-header");

    switch (command )  // this is the way to handle multiple if statements 
    {
      {
      case reload_header:
        Serial.println("rereading");
        read_Time();
        break;
      }

      {
      case osc_test:
        int oscBit = readFunction(0x03) & 32;
        if (oscBit >= 1) Serial.println("running");
        else Serial.println("stopped");
        break;
      }
      {
      case stop_osc:
        writeFunction(0x00, 0);
        int oscBit =  readFunction(0x03) & 32;
        if (oscBit < 1) Serial.println("stopped");
        else Serial.println("running");
        break;
      }
      {
      case set_alarm:
        setAlarm(2);
        Serial.println("Alarm set to");
        break;
      }
      {
      default:
        break;
      }
    }
  }
}

Check it out:
https://github.com/JChristensen/MCP79412RTC/blob/master/MCP79412RTC.h

Your code is wasting a lot of SRAM unnecessarily. Using the F() macro would stop that.
Serial.println(F("-header"));

What do you think would be a safer alternative? Longer command codes perhaps?

while (Serial.available() >= 2 )

If you get the header ("T"), there is NO guarantee that the other 10 characters are available to be read. Your code assumes that they are. That's a very bad assumption.

PaulS:
Your code is wasting a lot of SRAM unnecessarily. Using the F() macro would stop that.

Thanks, I didn't even know about that.

If you get the header ("T"), there is NO guarantee that the other 10 characters are available to be read. Your code assumes that they are. That's a very bad assumption.

Don't I need to have at least 11 to get the T header in the first place?

Everything as it is is working well now, now I am trying to figure out the correct bits to set to configure the alarm to set the Multi Function Pin high.

Don't I need to have at least 11 to get the T header in the first place?

I misread something, I guess. Never mind. Nothing to see here, folks, Move along.