napkinsterror
napkinsterror

Reputation: 1965

State Machine Problems

I am having a problem using a state machine setup. I am knew to these so I am having a problem. So there are some methods here can be ignored. The main problem is for some reason it sends a message for every byte it receives, but I thought Serial.read() clears the buffer after reading. So here is the bulk of the code:

#include "Arduino_Structures.h"
#include <SPI.h>
#include <Ethernet.h>
#include "Time.h"

//GLOBAL DECLARATIONS
enum { STANDBY, SEND, RECEIVE, PROCESS} state = SEND;
enum { STATUS, CONFIG, CURRENT, TIME, VOLTAGE} messageType = STATUS;

char lcv;
char lcv2; //loop control variables

MESSAGE_STRUCT outgoing;    //changing outgoing message
MESSAGE_STRUCT incoming;    //changing incoming message
OODLES_BLOCK oodles;            //oodles of information from the following  5 blocks
    STATUS_BLOCK temp_status;       //temporary status block
    CONFIG_BLOCK temp_config;       //temporary config block
    CURRENT_BLOCK temp_current; //temporary current block
    TIME_BLOCK temp_time;               //temporary time block
    VOLTAGE_BLOCK temp_voltage; //temporary voltage block


//FUNCATION DECLARATIONS
void sendMsg(MESSAGE_STRUCT* outgoing);
void receiveMsg(MESSAGE_STRUCT* incoming);

//ARDUINO SETUP 
void setup()
{
    delay(TIMEOUT); //wait for the boards to start up
    Serial.begin(BAUD); //set the arduino to be at the Micro-AT baud rate
    do
    {
        lcv = Ethernet.begin(mac); //start etherent board, get IP
    }while(!lcv);

}

//ARDUINO LOOP
void loop()
{
    switch(state)
    {
    case STANDBY:
        delay(1000);
        state = SEND;
        break;

    case SEND:

        switch(messageType)
        {
        case STATUS:
            outgoing.start_byte = 0x00;
            outgoing.length = 0x00;
            outgoing.address_1 = 0xFF;
            outgoing.address_2 = 0xFF;
            outgoing.code_word = REQUEST_STATUS;
            outgoing.checksum = 0;

            sendMsg(&outgoing);
            state = RECEIVE;
            break;

        case CONFIG:
            outgoing.start_byte = 0x00;
            outgoing.length = 0x00;
            outgoing.address_1 = 0xFF;
            outgoing.address_2 = 0xFF;
            outgoing.code_word = REQUEST_CONFIG;
            outgoing.checksum = 0;

            sendMsg(&outgoing);
            state = RECEIVE;
            break;

        case CURRENT:
            outgoing.start_byte = 0x00;
            outgoing.length = 0x00;
            outgoing.address_1 = 0xFF;
            outgoing.address_2 = 0xFF;
            outgoing.code_word = REQUEST_CURRENT;
            outgoing.checksum = 0;

            sendMsg(&outgoing);
            state = RECEIVE;
            break;

        case TIME:
            outgoing.start_byte = 0x00;
            outgoing.length = 0x00;
            outgoing.address_1 = 0xFF;
            outgoing.address_2 = 0xFF;
            outgoing.code_word = REQUEST_TIME;
            outgoing.checksum = 0;

            sendMsg(&outgoing);
            state = RECEIVE;
            break;

        case VOLTAGE:
            outgoing.start_byte = 0x00;
            outgoing.length = 0x00;
            outgoing.address_1 = 0xFF;
            outgoing.address_2 = 0xFF;
            outgoing.code_word = REQUEST_VOLTAGE;
            outgoing.checksum = 0;

            sendMsg(&outgoing);
            state = RECEIVE;
            break;

        default:
            break;
        }
    break;
    case RECEIVE:
        if(Serial.available())
        {
                      state = SEND;
              receiveMsg(&incoming);
                  //NEED TO CHECK TO MAKE SURRE START BYTE AND ADDRESS ARE CORRECT
                  //ALSO THIS IS WHERE I SHOULD CHECK THE CHECKSUM
                  //ONCE INSIDE SWITCHES NEED TO MAKE SURE THE RESPONSE IS CORRECT
            switch(messageType)
            {
            case STATUS: 
                //copy information from incoming's data array to the temp_status block so that it retains its structure
                memcpy(&temp_status, &incoming.data, sizeof(STATUS_BLOCK));

                //these are directly taken from the status block information (Arduino_Structures.h)
                oodles.left_source = temp_status.left_source;
                oodles.right_source = temp_status.right_source;
                oodles.left_overcurrent = temp_status.left_overcurrent;
                oodles.right_overcurrent = temp_status.right_overcurrent;
                oodles.automatic_transfer = temp_status.ready;
                oodles.event_led = temp_status.event;
                oodles.bus_type = temp_status.bus_type;
                oodles.preferred = temp_status.preferred;
                oodles.lockout_installed = temp_status.lockout_installed;
                oodles.supervisory_control = temp_status.supervisory_control;

                //put the time into the TimeElement then convert it to unix time
                TimeElements timeInfo; //will be used (from Time.h library)
                timeInfo.Year = temp_status.year;
                timeInfo.Month = temp_status.month;
                timeInfo.Day = temp_status.day;
                timeInfo.Hour = temp_status.hour;
                timeInfo.Minute = temp_status.minute;
                timeInfo.Second = temp_status.second;
                oodles.unix_time = makeTime(timeInfo);

                //might want to wipe incoming and outogoing messages to make sure they get correctly rewritten
                //messageType = CONFIG;
                //state = SEND;
                break;

            case CONFIG:            
                break;

            case CURRENT:
                break;

            case TIME: 
                break;

            case VOLTAGE: 
                break;
            }
                }
        break;

    case PROCESS:
        break;
    }
}

void sendMsg(MESSAGE_STRUCT* message)
{
    //brake up integers from MESSAGE_STRUCT to bytes (see intByte in Arduino_Structures.h)

  intByte code_word, checksum;

  code_word.intValue = message->code_word;
  checksum.intValue = message->checksum;

  //send byte by byte
    Serial.write(message->start_byte);
    Serial.write(message->length);
    Serial.write(message->address_1);
    Serial.write(message->address_2);
    Serial.write(code_word.byte1);
        Serial.write(code_word.byte2);

    for(lcv = 0; lcv < message->length; lcv++)
        Serial.write(message->data[lcv]);

    Serial.write(checksum.byte1);
        Serial.write(checksum.byte2);
}

void receiveMsg(MESSAGE_STRUCT* message)
{
  //receive bytes and put them back as integers (see intByte in Arduino_Structures.h)
      intByte code_word, checksum;

  //receive byte by byte
    message->start_byte = Serial.read();
    message->length = Serial.read();
    message->address_1 = Serial.read();
    message->address_2 = Serial.read();
    code_word.byte1 = Serial.read();
        code_word.byte2 = Serial.read();
        message->code_word = code_word.intValue;

    for(lcv = 0; lcv < message->length; lcv++)
        message->data[lcv] = Serial.read();

        checksum.byte1 = Serial.read();
        checksum.byte2 = Serial.read();
        message->checksum = checksum.intValue;
}

And here is the Serial monitor showing the error, it should only respond once, and if I send it only one byte it responds once. If I send it an 8 byte response as below, it responds 8 times("Answer" means arduino to laptop, and "request" means laptop to arduino):

Answer: 6/26/2013 4:30:59 PM.56364 (+11.3133 seconds)

 00 00 FF FF 00 01 00 00                        

Request: 6/26/2013 4:31:00 PM.48564 (+0.9219 seconds)

 00 00 FF FF 01 01 00 00                        

Answer: 6/26/2013 4:31:00 PM.51664 (+0.0156 seconds)

 00 00 FF FF 00 01 00 00 00 00 FF FF 00 01 00 00
 00 00 FF FF 00 01 00 00 00 00 FF FF 00 01 00 00
 00 00 FF FF 00 01 00 00 00 00 FF FF 00 01 00 00
 00 00 FF FF 00 01 00 00 00 00 FF FF 00 01 00 00

Upvotes: 0

Views: 426

Answers (1)

macduff
macduff

Reputation: 4685

It looks like you're checking to see that Serial.available() is not zero and then reading a bunch of data. It could be that you are not done recieving the data when you begin your receiveMsg function. You should:

  1. Check to make sure that the bytes you need are available Wait if
  2. They are not available, but you expect them to be coming soon

Just as an example:

void receiveMsg(MESSAGE_STRUCT* message)
{
  // receive bytes and put them back as integers
  intByte code_word, checksum;

  // receive byte by byte, wait for it if need be
  while( Serial.available() < 1 ) {delay(10);}
  message->start_byte = Serial.read();
  while( Serial.available() < 1 ) {delay(10);}
  message->length = Serial.read();

There are better, more robust ways to do it, but this is pretty simple and easily implemented for a test to see if the input buffer is not getting filled.

Upvotes: 1

Related Questions