SMS message display on LED Matrix board with printer option

Dependencies:   AdafruitThermalPrinter HT1632_LedMatrix VodafoneUSBModem mbed-rtos mbed

Revision:
0:9d29b886d41b
Child:
1:243371cb92c8
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Fri Jan 18 08:41:20 2013 +0000
@@ -0,0 +1,468 @@
+/**
+ * Mbed SMS to Printer and LED Matrix Displays
+ * Based on SMS example from VodafoneUSBModem library and
+ * 3GReceiptPrinter app from Ashley Mills.
+ *
+ * Requires libraries:
+ * AdafruitThermalPrinter - Port of Arduino library by Ashley Mills
+ * VodafoneUSBModem - Driver for Vodafone K3370 Mobile Broadband dongle
+ * HT1632_LedMatrix - LED Matrix library by Andrew Lindsay, port of Arduino library by Andrew Lindsay
+ *
+ * @author Andrew Lindsay
+ *
+ * @section LICENSE
+ *
+ * Copyright (c) 2012 Andrew Lindsay (andrew [at] thiseldo [dot] co [dot] uk)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *  Display received SMS on scrolling LED matrix with optional output to thermal printer.
+ *
+ * TODO: Still have issue with restarts when using printer.
+ * mbed-rtos and serial problem?
+ *
+ */
+
+#define USE_LED
+#define USE_PRINTER
+
+#include <ctype.h>
+
+#include "mbed.h"
+#include "VodafoneUSBModem.h"
+//#include <LinkMonitor.h>
+
+#ifdef USE_LED
+#include "HT1632_LedMatrix.h"
+#endif
+#ifdef USE_PRINTER
+#include "AdafruitThermal.h"
+//#include "test.h"
+#endif
+
+#define DEBUG 1
+
+#ifdef USE_LED
+// Cound try to get own number usine USSD request
+#define INFO_MSG "       Send a message to 07765946942        "
+#endif
+
+// Vodafone USSD commands
+#define USSD_COMMAND_OWN_NUMBER "*#100#"
+#define USSD_COMMAND_BALANCE "*#134#"
+#define USSD_COMMAND_TIME "*#103#"
+
+
+#ifdef DEBUG
+//Serial pc(USBTX, USBRX); // tx, rx
+//Serial debug_pc(p13,p14); // tx, rx
+Serial debug_pc(USBTX, USBRX); // tx, rx
+#endif
+
+#ifdef USE_PRINTER
+AdafruitThermal printer(p28,p27);   // setup printer
+#endif
+// Define a maximum size for storage arrays, is 160 (SMS size) + a bit more.
+#define MAX_MSG_LENGTH 192
+
+// Month list used in converting to integer for set_time
+char months[12][4] = {"JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC" };
+
+#ifdef USE_LED
+
+#define LED_MAX_DISPLAY_X 4
+#define LED_MAX_DISPLAY_Y 1
+
+// create object to control the LED Matrix
+HT1632_LedMatrix led = HT1632_LedMatrix();
+#define DISPDELAY 90
+#endif
+
+// Message buffers. New message waiting to be displayed and current message being displayed
+static char cmdBuf[12];
+static char newMsgBuf[MAX_MSG_LENGTH];
+static char msgBuf[MAX_MSG_LENGTH];
+
+#ifdef USE_LED
+int crtPos = 0;
+int msgx = 1;    // position on message screen of current character, set to 1 to allow for first scroll
+bool resetMessage = false;
+
+void matrixDemo( void );
+#endif
+
+// Define the onboard LEDs to use as status indicators
+DigitalOut led1(LED1);      // Activity
+DigitalOut led2(LED2);      // Activity, alternates with led2
+DigitalOut led3(LED3);      // SMS received, turns off after processed
+DigitalOut led4(LED4);      // USSD requested
+
+int threadRestartCount = 0;
+
+// Convert string buffer to upper case, is destructive. Buffer will be changed.
+char* stoupper( char* s )
+{
+    char* p = s;
+    while (*p = toupper( *p )) p++;
+    return s;
+}
+
+
+#ifdef USE_PRINTER
+void timestampMessage( char *msg )
+{
+    char timebuf[50];
+    // Print date/time, then message
+    time_t seconds = time(NULL);
+    strftime(timebuf, 50, "%d/%m/%Y %X\n", localtime(&seconds));
+    printer.print(timebuf);
+    printer.print(msg);
+    // linefeed a couple of times
+    printer.feed(3);
+}
+#endif
+
+#ifdef USE_LED
+// Load a new message
+void setNewMessage( char *newMsgStart )
+{
+    strncpy( newMsgBuf, newMsgStart, MAX_MSG_LENGTH );
+    resetMessage = true;
+}
+#endif
+
+void sendUSSDCommand( VodafoneUSBModem *_modem, char *ussdCommand, bool setScrolling )
+{
+    led4 = 1;
+#ifdef DEBUG
+    debug_pc.printf("Sending %s on USSD channel\n", ussdCommand);
+#endif
+    int ret = _modem->sendUSSD(ussdCommand, newMsgBuf, MAX_MSG_LENGTH);
+    // Check for correct response
+    if(ret) {
+        // Non 0 value indicates an error??
+#ifdef DEBUG
+        debug_pc.printf("Send USSD command returned %d\n", ret);
+#endif
+        led4 = 0;
+        return;
+    }
+
+    // Should only display message if one has been received.
+#ifdef DEBUG
+    debug_pc.printf("Result of command: %s\n", newMsgBuf);
+#endif
+
+    led4 = 0;
+
+#ifdef USE_LED
+    resetMessage = setScrolling;
+#endif
+}
+
+void setTime(VodafoneUSBModem *_modem )
+{
+    char numBuf[10];
+    // Set RTC using received message, format is DD-MMM-YYYY HH:MM
+    // Month is in text name, e.g. NOV, time is using 24 hour clock.
+    struct tm t;
+    int retryCount = 3;
+
+    while( retryCount ) {
+        sendUSSDCommand( _modem, USSD_COMMAND_TIME, false );
+
+#ifdef DEBUG
+        debug_pc.printf("Time received is %s\n", newMsgBuf);
+#endif
+
+        t.tm_sec = 0;    // 0-59
+        strncpy(numBuf, &newMsgBuf[15], 2 );
+        t.tm_min = atoi(numBuf);    // 0-59
+        strncpy(numBuf, &newMsgBuf[12], 2 );
+        t.tm_hour = atoi(numBuf);   // 0-23
+        strncpy(numBuf, &newMsgBuf[0], 2 );
+        t.tm_mday = atoi(numBuf);   // 1-31
+        strncpy(numBuf, &newMsgBuf[3], 3 );
+        t.tm_mon = 0;     // 0-11
+        for( int i=0; i<12; i++ ) {
+            if( strncmp( months[i], numBuf, 3 ) == 0 ) {
+                t.tm_mon = i;     // 0-11
+                break;
+            }
+        }
+        strncpy(numBuf, &newMsgBuf[9], 2 );
+        t.tm_year = 100 + atoi( numBuf );  // year since 1900
+
+        if( t.tm_year >110 ) {
+            // convert to timestamp and display
+            time_t seconds = mktime(&t);
+            set_time( seconds );
+            retryCount = 0;     // No more retries, terminate while
+        } else {
+            // Failed to set time, decrement tries
+            retryCount--;
+        }
+    }
+}
+
+
+void receiveSMS(void const*)
+{
+    VodafoneUSBModem modem;
+    time_t seconds = time(NULL);
+
+    threadRestartCount++;
+
+//    int pRssi = 0;
+//    setNewMessage( "Starting" );
+
+
+//    LinkMonitor::REGISTRATION_STATE pRegistrationState;
+//    LinkMonitor::BEARER pBearer;
+
+//    modem.getLinkState( &pRssi,&pRegistrationState, &pBearer);
+#ifdef DEBUG
+//    debug_pc.printf("Link state Rssi: %d, Registration state %x Bearer %x\n",pRssi,pRegistrationState, pBearer);
+#endif
+
+//    sprintf(msgBuf, "Link state Rssi: %d, Registration state %x Bearer %x      ",pRssi,pRegistrationState, pBearer);
+#ifdef USE_PRINTER
+    // Check if time already set, if not then get it. Use year = 0 as test
+    struct tm *t = localtime(&seconds);
+    if( t->tm_year == 0 ) {
+        setTime( &modem );
+    }
+
+    sprintf(newMsgBuf, "Thread Start %d\r\n", threadRestartCount );
+    timestampMessage( newMsgBuf );
+#endif
+#ifdef USE_LED
+    //strcpy( msgBuf, INFO_MSG );
+    setNewMessage( INFO_MSG );
+    
+    led.displayOn();
+#endif
+    char num[17];
+    size_t smsCount;
+
+    while(true) {
+        if( modem.getSMCount(&smsCount) ==OK ) {
+            if( smsCount > 0) {
+                led3 = 1;
+#ifdef DEBUG
+                debug_pc.printf("%d SMS to read\n", smsCount);
+#endif
+                if( modem.getSM(num, newMsgBuf, MAX_MSG_LENGTH) == OK ) {
+
+#ifdef DEBUG
+                    debug_pc.printf("%s : %s\n", num, newMsgBuf);
+#endif
+#ifdef USE_PRINTER
+// Print date/time, then message
+                    timestampMessage( newMsgBuf );
+#endif
+#ifdef USE_LED
+                    resetMessage = true;
+#endif
+                    strncpy( cmdBuf, newMsgBuf, 10 );
+                    stoupper( cmdBuf );  // This is a destructive function, original characters are uppercased
+                    if( strncmp( cmdBuf, "BALANCE", 7 ) == 0 ) {
+                        sendUSSDCommand( &modem, USSD_COMMAND_BALANCE, true );
+#ifdef USE_PRINTER
+                        timestampMessage( newMsgBuf );
+#endif
+#ifdef USE_LED
+                        resetMessage = true;
+                    } else if ( strncmp( cmdBuf, "INFO", 4 ) == 0 ) {
+                        setNewMessage( INFO_MSG );
+#endif
+//                  } else if ( strncmp( cmdBuf, "DEMO", 4 ) == 0 ) {
+//                      matrixDemo();
+//                      setNewMessage( INFO_MSG );
+#ifdef USE_LED
+                    } else if ( strncmp( cmdBuf, "CLEAR", 5 ) == 0 ) {
+                        setNewMessage( "      " );
+#endif
+                    }
+                }
+            }
+        }
+        Thread::wait(3000);
+        led3 = 0;
+    }
+}
+
+#ifdef USE_LED
+void displayScrollingLine(void const*)
+{
+    int y,xmax,ymax;
+    led.getXYMax(&xmax,&ymax);
+
+    while(true) {
+        // shift the whole screen 6 times, one column at a time; making 1 character
+        if( strlen( msgBuf ) > 10 ) {
+            for (int x=0; x < 6; x++) {
+                led.scrollLeft(1, 1);
+                msgx--;
+                // fit as much as we can on the available display space
+
+                while (!led.putChar(msgx,0,msgBuf[crtPos]))  { // zero return if it all fitted
+                    led.getXY(&msgx,&y);
+                    crtPos++; // we got all of the character on!!
+                    if (crtPos >= strlen(msgBuf)) {
+                        crtPos = 0;
+                    }
+                }
+                led.putShadowRam();
+                Thread::wait(DISPDELAY);
+            }
+        }
+        // Look for a new message being available
+        // TODO add minimum interval between message changes, e.g. 60s
+        if( resetMessage ) {
+            led.clear();
+            crtPos = 0;
+            msgx = 1;
+            strncpy( msgBuf, newMsgBuf, MAX_MSG_LENGTH );
+            if( strlen( msgBuf ) > 10 ) {
+                strcat( msgBuf, "          ");
+            } else {
+                led.putString(0,0, msgBuf);
+            }
+            resetMessage = false;
+        }
+    }
+}
+
+
+void matrixDemo( void )
+{
+    int xmax,ymax;
+    led.getXYMax(&xmax,&ymax);
+    led.clear();
+    for( int n=0; n<3; n++ ) {
+        for( int i=0; i<8; i++ ) {
+            led.drawRectangle(i,i,xmax-i,ymax-i, 1);
+            wait(0.05);
+        }
+        for( int i=7; i>=0; i-- ) {
+            led.drawRectangle(i,i,xmax-i,ymax-i, 0);
+            wait(0.05);
+        }
+    }
+    led.clear();
+    for( int n=0; n<3; n++ ) {
+        for(int i=0; i<(xmax/2); i++) {
+            led.drawCircle((xmax/2), 7, i, 1 );
+            wait( 0.05 );
+        }
+        for(int i=(xmax/2); i>=0; i--) {
+            led.drawCircle((xmax/2), 7, i, 0 );
+            wait( 0.05 );
+        }
+    }
+    wait(2);
+
+    led.clear();
+    led.init(2,1);    // Use displays 1 and 2 as 64x8 display
+}
+
+
+void showTime()
+{
+    time_t seconds = time(NULL);
+    char timebuf[20];
+    strftime(timebuf, 20, "%X   ", localtime(&seconds));
+    led.putString(12,8, timebuf );
+}
+
+void showDate()
+{
+    time_t seconds = time(NULL);
+    char timebuf[20];
+    strftime(timebuf, 20, "%d/%m/%Y   ", localtime(&seconds));
+    led.putString(4,8, timebuf );
+}
+#endif
+
+
+int main()
+{
+#ifdef DEBUG
+    debug_pc.baud(57600);
+#ifdef USE_LED
+    debug_pc.printf("SMS to LED Matrix display\n");
+#endif
+#ifdef USE_PRINTER
+    printer.begin();
+    debug_pc.printf("SMS To Printer\n");
+    timestampMessage("SMS To Printer Starting");
+#endif
+#endif
+#ifdef USE_LED
+    int displayCount = 10;
+    bool displayTime = true;
+
+    led.init(LED_MAX_DISPLAY_X,LED_MAX_DISPLAY_Y);    // Use displays 1 and 2 as 64x8 display
+    led.clear();
+    led.setBrightness(2);
+    //led.displayOff(); // Turn off display for now until receiver tast has started
+    bool twoLineDisplay = (LED_MAX_DISPLAY_X > 1);
+    led.putString( 0, 0, "SMS to LED Display" );
+
+    Thread::wait(3000);   // Wait for display to initialise after power up
+#endif
+
+    // Set initial blank message
+    strcpy( msgBuf, "          " );
+
+    Thread receiveTask(receiveSMS, NULL, osPriorityNormal, 1024 * 8);   // try 6 next
+#ifdef USE_LED
+    Thread displayTask(displayScrollingLine, NULL, osPriorityNormal, 1024 * 4);
+#endif
+
+    // Show we are still working by alternitively flashing LED1 adn LED2, once a second
+    led1 = 0;       // Working
+    led2 = 1;       // Alternate with led1
+    led3 = 0;       // Received and processign SMS
+    led4 = 0;       // Processing USSD message queue
+    while(1) {
+#ifdef USE_LED
+        // Only display date/time is we have a 2 row display
+        if( twoLineDisplay ) {
+            if( --displayCount <= 0 ) {
+                displayTime = !displayTime;
+                displayCount = 10;
+                led.putString(0,8, "            " );
+            }
+            if( displayTime ) {
+                showTime();
+            } else {
+                showDate();
+            }
+        }
+#endif
+        led1=!led1;
+        led2=!led2;
+        Thread::wait(1000);
+    }
+
+    return 0;
+}
\ No newline at end of file