uses pushing box to publish to google spreadsheets with a state machine instead of a while loop

Dependents:   DCS_FINAL_CODE

Fork of GSM_PUSHING_BOX_STATE_MACHINE by DCS_TEAM

GSMLibrary.cpp

Committer:
es_marble
Date:
2015-04-25
Revision:
29:bc5f53f2922a
Parent:
28:81f1c8bd3299
Child:
30:421aae087064

File content as of revision 29:bc5f53f2922a:

//Libraries
#include "GSMLibrary.h"
#include "gsmqueue.h"
#include <string.h>

//Global defines
#define TIMEOUTLIMIT SECONDS_TIMEOUT/TIME_CONST  //Defines how many "ticks" of the GSM will constitute "timeout" of our "watchdog timer"

//Extra defines for transmitter
#define START_SMS_TRANSMISSION "START"
#define STOP_SMS_TRANSMISSION "STOP"
#define GPS_SMS_TRANSMISSION "GPS"
#define RECEIVER_PHONE_NUMBER "\"+13853357314\""
#define AT_CMGS "AT+CMGS=" RECEIVER_PHONE_NUMBER    //Begin sending SMS with this send command and the phone number
#define NUM_SIZE 25 //Size of a temporary variable to help with concatenation of strings
#define BEGIN_SENSOR_DATA "$"   //$ and % are parsed by using regex by the GUI for the receiver and indicate the start and end of data received
#define END_SENSOR_DATA  "%"

//Define AT commands to send
#define AT_OK "AT"                  //Ask GSM if everything is 'ok'
#define AT_CSQ "AT+CSQ"             //Check signal strength
#define AT_CREG "AT+CREG?"          //Check if GSM is registered on cellular network
#define AT_CNMI "AT+CNMI=2,0,0,0,0" //Turn off the notification that is normally received when text messages are received
#define AT_CMGF "AT+CMGF=1"         //Turn on "text message mode" so we can characters (rather than PDU mode in which you send bytes)
#define AT_READ_MSG "AT+CMGL=\"REC UNREAD\"" //Read received messages that have not yet been read
#define AT_DEL_R_MSGS "AT+QMGDA=\"DEL READ\""   //Delete read messages

//Define expected responses for AT commands
//Please notice that after ":" the gsm will usually send aditional information
#define AT_OK_RESPONSE "OK" //Response after sending "AT" message
#define AT_CSQ_RESPONSE "+CSQ:" //+CSQ: <arg1>,<arg2> where <arg1> is signal strength arg1 = 0-30 where a number below 10 means low signal strength and 99 is not knwn or detectable signal and arg2 is bit error rate form 0-7, 99 will represent error 
#define AT_CREG_RESPONSE "+CREG:"//+CREG: <arg1>,<arg2> where <arg1> = 0-2(see AT command descriptions), <arg2> =  0-5, 0 not registered to nework and not looking for one. 1 is conected to network, 2 is not conected but searching
#define AT_CNMI_RESPONSE "OK"
#define AT_CMGF_RESPONSE "OK"
#define AT_CMGS_RESPONSE ">"  //Received after you give the GSM the phone number. (We mistakenly thought it was received a second time after SMS was sent... this is not true!)
#define AT_SENDSMS_RESPONSE "+CMGS:" // +CMGS: <id> this will include the message id, or CMS ERROR for error.
#define AT_DEL_R_MSGS_RESPONSE "OK"

//External variables
extern Serial pc;   //To print output to computer
extern Serial gsm;  //To communicate with GSM
extern uint8_t buffer[BUFFER_LENGTH];   //DMA queue

//Internal variables
gsm_states gsm_current_state = GSM_INITIALIZE;
int timeout_count = 0;
char state_chars[] = "iosntmRPWD";  //For debugging - 1 char to represent each state: init, ok, signalstrength, network, turn off notifications, messagemode, read, phone, writesms, del

//Extras for transmitter
char send = false;  //if true => we will send something (only if send_enable is true)
char send_enable = false;   //Sending start and stop commands to GSM via SMS changes this variable. If true, we will send SMS messages of our data received
char undeleted_msgs = true; //At the beginning assume we have undeleted messages
char gsm_msg[MAX_SMS_LENGTH + 250]; //String storing SMS message that will be sent (add 250 length to give leeway)
char num[NUM_SIZE];   //Temporary string storage to help with concatenation of strings

//"Tick" of the GSM (i.e. this is a state machine)
void gsm_tick()
{
    //Don't do anything, unless i) we received a response from the GSM, ii) the watchdog timer timed out, or iii) or we are initializing the GSM
    if (queueHasResponse() || gsm_timeOut() || gsm_current_state == GSM_INITIALIZE)
    {
        //gsm_printState();   //&debug
        //printQueue();   //&debug
        gsm_nextStateLogic();   //Next state
        gsm_mealyOutputs(); //Mealy outputs. This state machine is a little different because Mealy outputs come after the next state logic
    }
}

//Prints the current state. To save time printing, simply prints 1 character to indicate the current state.
void gsm_printState()
{
    pc.printf("S:%c;", state_chars[gsm_current_state]);
}
 
//Advance timeout counter; if timeout, return true
bool gsm_timeOut()
{
    if(++timeout_count >= TIMEOUTLIMIT){
        timeout_count = 0; 
        gsm_reset();
        return true;
    }
    else
        return false;     
}

//Have the GSM send data - L = long, S = short, hh/mm/ss for time, "lat ns" for latitute, "lon we" for longitude
void gsm_send_data(float L, float Lref, float S, float Sref, int hh, int mm, int ss, float lat, char ns, float lon, char we)
{
    if (!gsm_ready())   //Don't send if gsm not ready
        return;
    
    //Concatenate data
    gsm_msg[0] = NULL;
    strcat(gsm_msg, BEGIN_SENSOR_DATA);
    snprintf(num, NUM_SIZE, "%f,", L);
    strcat(gsm_msg, num);
    snprintf(num, NUM_SIZE, "%f,", Lref);
    strcat(gsm_msg, num);
    snprintf(num, NUM_SIZE, "%f,", S);
    strcat(gsm_msg, num);
    snprintf(num, NUM_SIZE, "%f,", Sref);
    strcat(gsm_msg, num);
    snprintf(num, NUM_SIZE, "%d:%d:%d,", hh, mm, ss);   //If there is no data from GPS, the time will just be "0:0:0" (that is okay)
    strcat(gsm_msg, num);
    if (ns != NULL) //If there is a gps fix (i.e. the gps has data on our location), ns will be set
    { 
        snprintf(num, NUM_SIZE, "%.4f,%.4f", (ns == 'N') ? lat : -lat, (we == 'E') ? lon : -lon);   //Use + or - rather than N/S, E/W
        strcat(gsm_msg, num);
    }
        else strcat(gsm_msg, "0,0");    //Otherwise just send 0's for latitude and longitude
    strcat(gsm_msg, END_SENSOR_DATA);
    
    send = true;    //Mark that we are currently sending a message
    strcat(gsm_msg, SMS_END_CHAR);  //Add SMS end char
}
 
//Return true if gsm is ready to send sms
//This only occurs if send = false (not currently sending a message) AND gsm received start sequence
bool gsm_ready()
{
    return ((!send) && send_enable) ? true : false;
}
 
//Reset the gsm. Currently this only resets the state, whether we are currently sending a message, and whether there are messages to delete.
//It does not reset send_enable
void gsm_reset()
{
    //If we are in the middle of sending a text message, we need to "escape" out of this mode by sending the escape character
    if (gsm_current_state == GSM_AT_CMGS)
        sendCommand(SMS_ESCAPE_CHAR);
    
    gsm_current_state = GSM_INITIALIZE;
    undeleted_msgs = true;
    send = false;
}
 
//Next state logic -----------------------------------------------------
//Note how each state (except init) checks the response received to make sure
//GSM has properly executed the command. If the response is correct, it changes
//the state so that the next command will be sent in the gsm_mealyOutputs function
//below.
void gsm_nextStateLogic()
{
    switch(gsm_current_state)
    {
        case GSM_INITIALIZE:
            timeout_count = 0;  //No AT commands have been sent: this will send the first one
            gsm_current_state = GSM_AT_OK;
            break;
        case GSM_AT_OK:
            if (findInQueue(AT_OK_RESPONSE, true))
                gsm_current_state = GSM_AT_CSQ;
            break;
        case GSM_AT_CSQ:
            if(findInQueue(AT_CSQ_RESPONSE, true))
                gsm_current_state = GSM_AT_CREG;
            break;
        case GSM_AT_CREG:
            if(findInQueue(AT_CREG_RESPONSE, true))
            {
                parseInt(); //After the CREG response we receive two integers. The second should be '1' (see AT_CREG_RESPONSE #define)
                if(parseInt() == 1)
                    gsm_current_state = GSM_AT_CNMI;
            } 
            break;
        case GSM_AT_CNMI:
            if(findInQueue(AT_CNMI_RESPONSE, true))
                gsm_current_state = GSM_AT_CMGF;
            break;  
        case GSM_AT_CMGF:
            if(findInQueue(AT_CMGF_RESPONSE, true))
                gsm_current_state = GSM_READ_MSG;
            break;
        case GSM_READ_MSG:
            if(send_enable) //If we are currently sending SMS, check if we need to stop transmission of SMS
            {
                if(findInQueue(STOP_SMS_TRANSMISSION, true))  //Always stop sending if stop sequence received by SMS
                {
                    undeleted_msgs = true;
                    send_enable = false;    //Set send_enable to indicate we will stop sending SMS
                }
                else if (send) //Only continue sending if we didn't find stop sequence AND user requested that we send sms
                    gsm_current_state = GSM_AT_CMGS;    //Continue sending SMS (change state accordingly)
                //Implicit: otherwise we will continue checking text messages in this state.
            }
            else    //If we are not yet sending SMS, check if we need to start transmission of SMS
            {
                if(findInQueue(START_SMS_TRANSMISSION, true))
                {
                    undeleted_msgs = true;
                    send_enable = true; //Now we are in send SMS mode: whenever 'send' variable is set to true we will send an SMS
                    if (send) //Only begin sending a new SMS if user requested that we send SMS (i.e. there is data to send)
                        gsm_current_state = GSM_AT_CMGS;    //Start sending SMS (change state accordingly)
                }
            }
            break;
        case GSM_AT_CMGS:
            if(findInQueue(AT_CMGS_RESPONSE, true))
                gsm_current_state = GSM_AT_SENDSMS;
            break;
        case GSM_AT_SENDSMS:
            if(findInQueue(AT_SENDSMS_RESPONSE, true))
            {
                //pc.printf("(Wid%i)",parseInt());//&debug
                timeout_count = 0;  //Reset timeout count
                send = false;   //Indicate we are done sending the text message
                if (undeleted_msgs) //Check if we need to delete read messages
                    gsm_current_state = GSM_DEL_R_MSGS; //Only delete messages if there are unread messages
                else
                {
                    gsm_current_state = GSM_READ_MSG;   //Otherwise skip the delete messages state; go read text messages again
                    //pc.printf("(Dnone)");//&debug 
                }
            }
            else
            {
                //pc.printf("(Werr)"); //&debug
                gsm_current_state = GSM_AT_CMGS;    //If failed, try resending the message (i.e. continue until timeout)
            }
            break;
        case GSM_DEL_R_MSGS:
            if (findInQueue(AT_DEL_R_MSGS_RESPONSE, true))
            {
                undeleted_msgs = false;
                //pc.printf("(Dsucc)");   //&debug
            }
            //else
                //pc.printf("(Derr)");    //&debug
            gsm_current_state = GSM_READ_MSG;
            break;
        default:
            //pc.printf("This is a state error\r\n");
    }
}
 
//Mealy output logic ------------------------------------------------------
void gsm_mealyOutputs()
{
    switch(gsm_current_state)
    {
        case GSM_INITIALIZE:
            break;
        case GSM_AT_OK:
            sendCommand(AT_OK);
            break;
        case GSM_AT_CSQ:
            sendCommand(AT_CSQ);
            break;
        case GSM_AT_CREG:
            sendCommand(AT_CREG);
            break;
        case GSM_AT_CNMI:
            sendCommand(AT_CNMI);
            break; 
        case GSM_AT_CMGF:
            sendCommand(AT_CMGF);
            break;         
        case GSM_READ_MSG:
            sendCommand(AT_READ_MSG);
            break; 
        case GSM_AT_CMGS:   
            sendCommand(AT_CMGS);
            break;
        case GSM_AT_SENDSMS:
            sendCommand(gsm_msg); //end char included
            break;
        case GSM_DEL_R_MSGS:
            sendCommand(AT_DEL_R_MSGS);
            break;
        default:
            //pc.printf("This is a state error\r\n");
    }
}

//Initialize the GSM
void gsm_initialize(){ 
     wait(2.3); //Wait for the GSM to turn on properly before doing this initialization
      SIM_SCGC6 |= SIM_SCGC6_DMAMUX_MASK; //enabling dmamux clock
      SIM_SCGC7 |= SIM_SCGC7_DMA_MASK;  // enebaling dma clock
      //pc.printf("initializing DMA...\r\n");
     // control register mux, enabling uart3 receive
     DMAMUX_CHCFG0 |= DMAMUX_CHCFG_ENBL_MASK|DMAMUX_CHCFG_SOURCE(8); 
     
     // Enable request signal for channel 0 
     DMA_ERQ = DMA_ERQ_ERQ0_MASK;
     
      // select round-robin arbitration priority
     DMA_CR |= DMA_CR_ERCA_MASK;
     
     //enable error interrupt for DMA0 (commented out because we won't use interrupts for our implementation)
     //DMA_EEI = DMA_EEI_EEI0_MASK;
     
     //Address for buffer
     DMA_TCD0_SADDR = (uint32_t) &UART_D_REG(UART3_BASE_PTR);
     DMA_TCD0_DADDR = (uint32_t) buffer;
     // Set an offset for source and destination address
     DMA_TCD0_SOFF = 0x00; 
     DMA_TCD0_DOFF = 0x01; // Destination address offset of 1 byte per transaction
     
     // Set source and destination data transfer size
     DMA_TCD0_ATTR = DMA_ATTR_SSIZE(0) | DMA_ATTR_DSIZE(0);
     
     // Number of bytes to be transfered in each service request of the channel
     DMA_TCD0_NBYTES_MLNO = 0x01;
     // Current major iteration count
    DMA_TCD0_CITER_ELINKNO = DMA_CITER_ELINKNO_CITER(BUFFER_LENGTH);
    DMA_TCD0_BITER_ELINKNO = DMA_BITER_ELINKNO_BITER(BUFFER_LENGTH);
    // Adjustment value used to restore the source and destiny address to the initial value
    // After reading 'len' number of times, the DMA goes back to the beginning by subtracting len*2 from the address (going back to the original address)
    DMA_TCD0_SLAST = 0;   // Source address adjustment
    DMA_TCD0_DLASTSGA = -BUFFER_LENGTH;  // Destination address adjustment   
    // Setup control and status register
    DMA_TCD0_CSR = 0;
       
    // enable interrupt call at end of major loop
    DMA_TCD0_CSR |= DMA_CSR_INTMAJOR_MASK;
    
    //Activate dma transfer rx interrupt
    UART_C2_REG(UART3) |= UART_C2_RIE_MASK;
    UART_C5_REG(UART3) |= UART_C5_RDMAS_MASK | UART_C5_ILDMAS_MASK | UART_C5_LBKDDMAS_MASK;
    //activate p fifo register
    UART_PFIFO_REG(UART3) |= UART_PFIFO_RXFE_MASK; //RXFE and buffer size of 1 word
    queueInit();
    //pc.printf("done...\n\r");
}
 
//For debugging: print registers related to DMA and UART setup
void print_registers()
{
    pc.printf("\n\rDMA REGISTERS\n\r");
    pc.printf("DMA_MUX: 0x%08x\r\n",DMAMUX_CHCFG0);
    pc.printf("SADDR0: 0x%08x\r\n",DMA_TCD0_SADDR);
    pc.printf("DADDR0: 0x%08x\r\n",DMA_TCD0_DADDR);
    pc.printf("CITER0: 0x%08x\r\n",DMA_TCD0_CITER_ELINKNO);
    pc.printf("BITER0: 0x%08x\r\n",DMA_TCD0_BITER_ELINKNO);
    pc.printf("DMA_CR: %08x\r\n", DMA_CR);
    pc.printf("DMA_ES: %08x\r\n", DMA_ES);
    pc.printf("DMA_ERQ: %08x\r\n", DMA_ERQ);
    pc.printf("DMA_EEI: %08x\r\n", DMA_EEI);
    pc.printf("DMA_CEEI: %02x\r\n", DMA_CEEI);
    pc.printf("DMA_SEEI: %02x\r\n", DMA_SEEI);
    pc.printf("DMA_CERQ: %02x\r\n", DMA_CERQ);
    pc.printf("DMA_SERQ: %02x\r\n", DMA_SERQ);
    pc.printf("DMA_CDNE: %02x\r\n", DMA_CDNE);
    pc.printf("DMA_SSRT: %02x\r\n", DMA_SSRT);
    pc.printf("DMA_CERR: %02x\r\n", DMA_CERR);
    pc.printf("DMA_CINT: %02x\r\n", DMA_CINT);
    pc.printf("DMA_INT: %08x\r\n", DMA_INT);
    pc.printf("DMA_ERR: %08x\r\n", DMA_ERR);
    pc.printf("DMA_HRS: %08x\r\n", DMA_HRS);
    pc.printf("DMA_TCD0_DOFF: %08x\r\n",DMA_TCD0_DOFF);
    pc.printf("\n\rUART REGISTERS\n\r");
    pc.printf("UART_BDH_REG: %08x\r\n",UART_BDH_REG(UART3)); 
    pc.printf("UART_C1_REG: %08x\r\n",UART_C1_REG(UART3));
    pc.printf("UART_C2_REG: %08x\r\n",UART_C2_REG(UART3));
    pc.printf("UART_S1_REG: %08x\r\n",UART_S1_REG(UART3));
    pc.printf("UART_s2_REG: %08x\r\n",UART_S2_REG(UART3));  
    pc.printf("UART_C3_REG: %08x\r\n",UART_C3_REG(UART3));
    pc.printf("UART_D_REG: %08x\r\n",UART_D_REG(UART3));
    pc.printf("UART_MA1_REG: %08x\r\n",UART_MA1_REG(UART3));
    pc.printf("UART_MA2_REG: %08x\r\n",UART_MA2_REG(UART3));
    pc.printf("UART_C4_REG: %08x\r\n",UART_C4_REG(UART3));
    pc.printf("UART_C5_REG: %08x\r\n",UART_C5_REG(UART3));
    pc.printf("UART_ED_REG: %08x\r\n",UART_ED_REG(UART3));   
    pc.printf("UART_MODEM_REG: %08x\r\n",UART_MODEM_REG(UART3));
    pc.printf("UART_IR_REG: %08x\r\n",UART_IR_REG(UART3)); 
    pc.printf("UART_PFIFO_REG: %08x\r\n",UART_PFIFO_REG(UART3));
    pc.printf("UART_CFIFO_REG: %08x\r\n",UART_CFIFO_REG(UART3));
    pc.printf("UART_SFIFO_REG: %08x\r\n",UART_SFIFO_REG(UART3)); 
    pc.printf("UART_TWFIFO_REG: %08x\r\n",UART_TWFIFO_REG(UART3));
    pc.printf("UART_TCFIFO_REG: %08x\r\n",UART_TCFIFO_REG(UART3)); 
    pc.printf("UART_RWFIFO_REG: %08x\r\n",UART_RWFIFO_REG(UART3)); 
    pc.printf("UART_RCFIFO_REG: %08x\r\n",UART_RCFIFO_REG(UART3));
}