Andrew Lindsay / Mbed 2 deprecated SMS_LEDMatrixPrinter

Dependencies:   AdafruitThermalPrinter HT1632_LedMatrix VodafoneUSBModem mbed-rtos mbed

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers main.cpp Source File

main.cpp

00001 /**
00002  * Mbed SMS to Printer and LED Matrix Displays
00003  * Based on SMS example from VodafoneUSBModem library and
00004  * 3GReceiptPrinter app from Ashley Mills.
00005  *
00006  * Requires libraries:
00007  * AdafruitThermalPrinter - Port of Arduino library by Ashley Mills
00008  * VodafoneUSBModem - Driver for Vodafone K3370 Mobile Broadband dongle
00009  * HT1632_LedMatrix - LED Matrix library by Andrew Lindsay, port of Arduino library by Andrew Lindsay
00010  *
00011  * @author Andrew Lindsay
00012  *
00013  * @section LICENSE
00014  *
00015  * Copyright (c) 2012 Andrew Lindsay (andrew [at] thiseldo [dot] co [dot] uk)
00016  *
00017  * Permission is hereby granted, free of charge, to any person obtaining a copy
00018  * of this software and associated documentation files (the "Software"), to deal
00019  * in the Software without restriction, including without limitation the rights
00020  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
00021  * copies of the Software, and to permit persons to whom the Software is
00022  * furnished to do so, subject to the following conditions:
00023 
00024  * The above copyright notice and this permission notice shall be included in
00025  * all copies or substantial portions of the Software.
00026  *
00027  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00028  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00029  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
00030  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00031  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00032  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
00033  * THE SOFTWARE.
00034  *
00035  * @section DESCRIPTION
00036  *  Display received SMS on scrolling LED matrix with optional output to thermal printer.
00037  *
00038  * TODO: Still have issue with restarts when using printer.
00039  * mbed-rtos and serial problem?
00040  * Incoming queue for multiple messages, give each at least 60 to be displayed
00041  *
00042  */
00043 
00044 #define USE_LED
00045 #undef USE_PRINTER
00046 
00047 #include <ctype.h>
00048 
00049 #include "mbed.h"
00050 //#include "beep.h"
00051 #include "VodafoneUSBModem.h"
00052 //#include "LinkMonitor.h"
00053 
00054 #ifdef USE_LED
00055 #include "HT1632_LedMatrix.h"
00056 #endif
00057 #ifdef USE_PRINTER
00058 #include "AdafruitThermal.h"
00059 #endif
00060 
00061 //#define DEBUG 1
00062 
00063 #ifdef USE_LED
00064 // Default scrolling message includes own number obtained using USSD request
00065 #define INFO_MSG "       Send a message to "
00066 //#define INFO_MSG "       Welcome to the IoT London Showcase. Send a message to "
00067 //#define INFO_MSG "       Welcome to the Reading Geek Night. Send a message to "
00068 #endif
00069 
00070 #define BRIGHTNESS 10
00071 
00072 // Vodafone USSD commands
00073 #define USSD_COMMAND_OWN_NUMBER "*#100#"
00074 #define USSD_COMMAND_BALANCE "*#134#"
00075 #define USSD_COMMAND_TIME "*#103#"
00076 
00077 #ifdef DEBUG
00078 Serial debug_pc(USBTX, USBRX); // tx, rx
00079 #endif
00080 
00081 #ifdef USE_PRINTER
00082 AdafruitThermal printer(p28,p27);   // setup printer
00083 #endif
00084 // Define a maximum size for storage arrays, is 160 (SMS size) + a bit more.
00085 #define MAX_MSG_LENGTH 192
00086 
00087 // Month list used in converting to integer for set_time
00088 char months[12][4] = {"JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC" };
00089 
00090 #ifdef USE_LED
00091 
00092 #define LED_MAX_DISPLAY_X 4
00093 #define LED_MAX_DISPLAY_Y 1
00094 
00095 
00096 // create object to control the LED Matrix
00097 HT1632_LedMatrix led = HT1632_LedMatrix(p7, p5, p19, p17, p18, p20);    //, LED_MAX_DISPLAY_X,LED_MAX_DISPLAY_Y );
00098 //HT1632_LedMatrix led = HT1632_LedMatrix(p7, p5, p17, p18, p19, p20);    //, LED_MAX_DISPLAY_X,LED_MAX_DISPLAY_Y );
00099 #define DISPDELAY 90
00100 #endif
00101 
00102 // Message buffers. New message waiting to be displayed and current message being displayed
00103 //#define MAX_NUM_MSGS 10
00104 static char cmdBuf[12];
00105 static char newMsgBuf[MAX_MSG_LENGTH];
00106 static char msgBuf[MAX_MSG_LENGTH];
00107 //static int currentMsg = 0;
00108 //static int numberOfMsgs = 0;
00109 static bool getNextMessage = true;
00110 
00111 #ifdef USE_LED
00112 static char ownNumber[20];
00113 
00114 int crtPos = 0;
00115 int msgx = 1;    // position on message screen of current character, set to 1 to allow for first scroll
00116 bool resetMessage = false;
00117 
00118 void matrixDemo( void );
00119 #endif
00120 
00121 // Define object for buzzer to signal new message received
00122 //Beep buzzer(p21);
00123 
00124 // Define the onboard LEDs to use as status indicators
00125 DigitalOut led1(LED1);      // Activity
00126 DigitalOut led2(LED2);      // Activity, alternates with led2
00127 DigitalOut led3(LED3);      // SMS received, turns off after processed
00128 DigitalOut led4(LED4);      // USSD request in progress
00129 
00130 int threadRestartCount = 0;
00131 
00132 // Convert string buffer to upper case, is destructive. Buffer will be changed.
00133 char* stoupper( char* s )
00134 {
00135     char* p = s;
00136     while (*p = toupper( *p )) p++;
00137     return s;
00138 }
00139 
00140 
00141 #ifdef USE_PRINTER
00142 void timestampMessage( char *msg )
00143 {
00144     char timebuf[50];
00145     // Print date/time, then message
00146     time_t seconds = time(NULL);
00147     strftime(timebuf, 50, "%d/%m/%Y %X\n", localtime(&seconds));
00148     printer.print(timebuf);
00149     printer.print(msg);
00150     // linefeed a couple of times
00151     printer.feed(3);
00152 }
00153 #endif
00154 
00155 #ifdef USE_LED
00156 // Load a new message
00157 
00158 void setNewMessage( char *newMsgStart )
00159 {
00160     strncpy( newMsgBuf, newMsgStart, MAX_MSG_LENGTH );
00161     resetMessage = true;
00162  //   buzzer.beep(1000,0.5);
00163 }
00164 
00165 
00166 void resetMessageBuffers( )
00167 {
00168     strcpy( msgBuf, "          " );
00169     newMsgBuf[0] = '\0';
00170 }
00171 #endif
00172 
00173 void sendUSSDCommand( VodafoneUSBModem *_modem, char *ussdCommand, bool setScrolling )
00174 {
00175     led4 = 1;
00176 #ifdef DEBUG
00177     debug_pc.printf("Sending %s on USSD channel\n", ussdCommand);
00178 #endif
00179     int ret = _modem->sendUSSD(ussdCommand, newMsgBuf, MAX_MSG_LENGTH);
00180     // Check for correct response
00181     if(ret) {
00182         // Non 0 value indicates an error??
00183 #ifdef DEBUG
00184         debug_pc.printf("Send USSD command returned %d\n", ret);
00185 #endif
00186         led4 = 0;
00187         return;
00188     }
00189 
00190     // Should only display message if one has been received.
00191 #ifdef DEBUG
00192     debug_pc.printf("Result of command: %s\n", newMsgBuf);
00193 #endif
00194 
00195     led4 = 0;
00196 
00197 #ifdef USE_LED
00198     resetMessage = setScrolling;
00199 #endif
00200 }
00201 
00202 void setTime(VodafoneUSBModem *_modem )
00203 {
00204     char numBuf[10];
00205     // Set RTC using received message, format is DD-MMM-YYYY HH:MM
00206     // Month is in text name, e.g. NOV, time is using 24 hour clock.
00207     struct tm t;
00208     int retryCount = 3;
00209 
00210     while( retryCount ) {
00211         sendUSSDCommand( _modem, USSD_COMMAND_TIME, false );
00212 
00213 #ifdef DEBUG
00214         debug_pc.printf("Time received is %s\n", newMsgBuf);
00215 #endif
00216 
00217         t.tm_sec = 0;    // 0-59
00218         strncpy(numBuf, &newMsgBuf[15], 2 );
00219         t.tm_min = atoi(numBuf);    // 0-59
00220         strncpy(numBuf, &newMsgBuf[12], 2 );
00221         t.tm_hour = atoi(numBuf);   // 0-23
00222         strncpy(numBuf, &newMsgBuf[0], 2 );
00223         t.tm_mday = atoi(numBuf);   // 1-31
00224         strncpy(numBuf, &newMsgBuf[3], 3 );
00225         t.tm_mon = 0;     // 0-11
00226         for( int i=0; i<12; i++ ) {
00227             if( strncmp( months[i], numBuf, 3 ) == 0 ) {
00228                 t.tm_mon = i;     // 0-11
00229                 break;
00230             }
00231         }
00232         strncpy(numBuf, &newMsgBuf[9], 2 );
00233         t.tm_year = 100 + atoi( numBuf );  // year since 1900
00234 
00235         if( t.tm_year >110 ) {
00236             // convert to timestamp and display
00237             time_t seconds = mktime(&t);
00238             set_time( seconds );
00239             retryCount = 0;     // No more retries, terminate while
00240         } else {
00241             // Failed to set time, decrement tries
00242             retryCount--;
00243         }
00244     }
00245 }
00246 
00247 void getOwnNumber(VodafoneUSBModem *_modem, char *numPtr  )
00248 {
00249     int retryCount = 3;
00250 
00251     while( retryCount ) {
00252         sendUSSDCommand( _modem, USSD_COMMAND_OWN_NUMBER, false );
00253 
00254 #ifdef DEBUG
00255         debug_pc.printf("Own Number received %s\n", newMsgBuf);
00256 #endif
00257         if( strlen(newMsgBuf) > 0 ) {
00258             // Save number in array pointed to be numPtr
00259             strncpy(ownNumber, newMsgBuf, strlen(newMsgBuf) );
00260             retryCount = 0;
00261         } else
00262             retryCount--;
00263     }
00264 }
00265 
00266 
00267 char linkStateStr[6][13] = { "Unknown     ", "Registering ", "Denied      ", "No Signal   ", "Home Network", "Roaming     " };
00268 char bearerStr[6][5] = {"Unkn", "GSM ", "EDGE", "3G  ", "HSPA", "LTE " };
00269 
00270 void receiveSMS(void const*)
00271 {
00272     VodafoneUSBModem modem;
00273     time_t seconds = time(NULL);
00274     int pRssi = 0;
00275     LinkMonitor::REGISTRATION_STATE pRegistrationState;
00276     LinkMonitor::BEARER pBearer;
00277 
00278     threadRestartCount++;
00279 
00280     // Getting the link state seems to speed up startup of the library
00281     // Sometimes startup doesnt happen - need way to retry this
00282     modem.getLinkState( &pRssi,&pRegistrationState, &pBearer);
00283 #ifdef DEBUG
00284     debug_pc.printf("Link state Rssi: %d, Registration state %x Bearer %x\n",pRssi,pRegistrationState, pBearer);
00285 #endif
00286     sprintf(newMsgBuf, "Link State: %s on %s          ", linkStateStr[pRegistrationState], bearerStr[pBearer] );
00287 
00288 #ifdef USE_LED
00289     setNewMessage( newMsgBuf );
00290 #endif
00291 #ifdef USE_PRINTER
00292     timestampMessage( newMsgBuf );
00293 #endif
00294 
00295     Thread::wait(3000);
00296 
00297     // Get own number from the  network
00298     getOwnNumber( &modem, ownNumber );
00299 #ifdef USE_LED
00300     sprintf(newMsgBuf, "Own number %s", ownNumber );
00301     setNewMessage( newMsgBuf );
00302 #endif
00303 
00304 #ifdef USE_PRINTER
00305     // Check if time already set, if not then get it. Use year = 0 as test
00306     struct tm *t = localtime(&seconds);
00307     if( t->tm_year == 0 )
00308         setTime( &modem );
00309 
00310     sprintf(newMsgBuf, "Thread Start %d\r\n", threadRestartCount );
00311     timestampMessage( newMsgBuf );
00312 #endif
00313 #ifdef USE_LED
00314     //strcpy( &msgBuf[currentMsg][0], INFO_MSG );
00315     sprintf(newMsgBuf, "%s %s          ", INFO_MSG, ownNumber );
00316     setNewMessage( newMsgBuf );
00317 
00318     led.displayOn();
00319 #endif
00320     char num[17];
00321     size_t smsCount;    
00322     getNextMessage = true;
00323 
00324     while(true) {
00325         if( modem.getSMCount(&smsCount) == OK ) {
00326             if( smsCount > 0 && getNextMessage ) {
00327                 led3 = 1;
00328                 getNextMessage = false;
00329 #ifdef DEBUG
00330                 debug_pc.printf("%d SMS to read\n", smsCount);
00331 #endif
00332                 if( modem.getSM(num, newMsgBuf, MAX_MSG_LENGTH) == OK ) {
00333 #ifdef DEBUG
00334                     debug_pc.printf("%s : %s\n", num, newMsgBuf);
00335 #endif
00336 #ifdef USE_PRINTER
00337 // Print date/time, then message
00338                     timestampMessage( newMsgBuf );
00339 #ifndef USE_LED
00340                     // Force processing of next messages if only printer being used
00341                     // If LED display then update when displayed once
00342                     getNextMessage = true;
00343 #endif
00344 #endif
00345 #ifdef USE_LED
00346                     resetMessage = true;
00347 #endif
00348                     strncpy( cmdBuf, newMsgBuf, 10 );
00349                     stoupper( cmdBuf );  // This is a destructive function, original characters are uppercased
00350                     if( strncmp( cmdBuf, "BALANCE", 7 ) == 0 ) {
00351                         sendUSSDCommand( &modem, USSD_COMMAND_BALANCE, true );
00352 #ifdef USE_PRINTER
00353                         timestampMessage( newMsgBuf );
00354 #endif
00355 #ifdef USE_LED
00356                         resetMessage = true;
00357                     } else if ( strncmp( cmdBuf, "INFO", 4 ) == 0 ) {
00358                         sprintf(newMsgBuf, "%s %s          ", INFO_MSG, ownNumber );
00359                         setNewMessage( newMsgBuf );
00360 #endif
00361 //                  } else if ( strncmp( cmdBuf, "DEMO", 4 ) == 0 ) {
00362 //                        matrixDemo();
00363 //                        sprintf(newMsgBuf, "%s %s          ", INFO_MSG, ownNumber );
00364 //                        setNewMessage( newMsgBuf );
00365 #ifdef USE_LED
00366                     } else if ( strncmp( cmdBuf, "CLEAR", 5 ) == 0 ) {
00367                         setNewMessage( "      " );
00368 #endif
00369                     }
00370                 }
00371             }
00372         }
00373         Thread::wait(3000);
00374         led3 = 0;
00375     }
00376 }
00377 
00378 #ifdef USE_LED
00379 void displayScrollingLine(void const*)
00380 {
00381     int y,xmax,ymax;
00382     led.getXYMax(&xmax,&ymax);
00383     bool msgFinished = true;
00384 
00385     while(true) {
00386         // shift the whole screen 6 times, one column at a time; making 1 character
00387         if( strlen( &msgBuf[0] ) > 10 ) {
00388             for (int x=0; x < 6; x++) {
00389                 led.scrollLeft(1, 1);
00390                 msgx--;
00391                 if( msgx <= 0 ) {
00392                     msgFinished = true;
00393                     getNextMessage = true;
00394                 }
00395 
00396                 // fit as much as we can on the available display space
00397 
00398                 while (!led.putChar(msgx,0,msgBuf[crtPos]))  { // zero return if it all fitted
00399                     led.getXY(&msgx,&y);
00400                     crtPos++; // we got all of the character on!!
00401                     if (crtPos >= strlen(msgBuf)) {
00402                         crtPos = 0;
00403                     }
00404                 }
00405                 led.putShadowRam();
00406                 Thread::wait(DISPDELAY);
00407             }
00408         } else {
00409             getNextMessage = true;
00410             msgFinished = true;
00411         }
00412         // Look for a new message being available and current message has been displayed
00413         
00414         // TODO add minimum interval between message changes, e.g. 60s
00415         if( resetMessage && msgFinished ) {
00416             led.clear();
00417             led.setBrightness(BRIGHTNESS);
00418             crtPos = 0;
00419             msgx = 1;
00420             msgFinished = false;
00421 
00422             strncpy( msgBuf, newMsgBuf, MAX_MSG_LENGTH );
00423             if( strlen( msgBuf ) > 10 ) {
00424                 strcat( msgBuf, "          ");
00425             } else {
00426                 led.putString(0,0, msgBuf);
00427             }
00428             resetMessage = false;
00429         }
00430     }
00431 }
00432 
00433 
00434 /*
00435 void matrixDemo( void )
00436 {
00437     int xmax,ymax;
00438     led.getXYMax(&xmax,&ymax);
00439     led.clear();
00440     for( int n=0; n<3; n++ ) {
00441         for( int i=0; i<8; i++ ) {
00442             led.drawRectangle(i,i,xmax-i,ymax-i, 1);
00443             wait(0.05);
00444         }
00445         for( int i=7; i>=0; i-- ) {
00446             led.drawRectangle(i,i,xmax-i,ymax-i, 0);
00447             wait(0.05);
00448         }
00449     }
00450     led.clear();
00451     for( int n=0; n<3; n++ ) {
00452         for(int i=0; i<(xmax/2); i++) {
00453             led.drawCircle((xmax/2), 7, i, 1 );
00454             wait( 0.05 );
00455         }
00456         for(int i=(xmax/2); i>=0; i--) {
00457             led.drawCircle((xmax/2), 7, i, 0 );
00458             wait( 0.05 );
00459         }
00460     }
00461     wait(2);
00462 
00463     led.clear();
00464     led.init(LED_MAX_DISPLAY_X,LED_MAX_DISPLAY_Y);
00465 }
00466 */
00467 
00468 void showTime()
00469 {
00470     time_t seconds = time(NULL);
00471     char timebuf[20];
00472     strftime(timebuf, 20, "%X   ", localtime(&seconds));
00473     led.putString(12,8, timebuf );
00474 }
00475 
00476 void showDate()
00477 {
00478     time_t seconds = time(NULL);
00479     char timebuf[20];
00480     strftime(timebuf, 20, "%d/%m/%Y   ", localtime(&seconds));
00481     led.putString(4,8, timebuf );
00482 }
00483 #endif
00484 
00485 
00486 int main()
00487 {
00488 #ifdef DEBUG
00489     debug_pc.baud(57600);
00490 #ifdef USE_LED
00491     debug_pc.printf("SMS to LED Matrix display\n");
00492 #endif
00493 #ifdef USE_PRINTER
00494     printer.begin();
00495     debug_pc.printf("SMS To Printer\n");
00496     timestampMessage("SMS To Printer Starting");
00497 #endif
00498 #endif
00499 #ifdef USE_LED
00500     int displayCount = 10;
00501     bool displayTime = true;
00502 
00503     led.init(LED_MAX_DISPLAY_X,LED_MAX_DISPLAY_Y);    // Use all displays as 128x8 display
00504     led.clear();
00505     led.setBrightness(BRIGHTNESS);
00506     //led.displayOff(); // Turn off display for now until receiver task has started
00507     bool twoLineDisplay = (LED_MAX_DISPLAY_X > 1);
00508     led.putString( 0, 0, "SMS to LED Display" );
00509 
00510 //    Thread::wait(3000);   // Wait for display to initialise after power up
00511 #endif
00512 
00513     // Startup beep
00514  //   buzzer.beep(1000,0.5);
00515     
00516     Thread::wait(30000);     // Wait for bit longer to ensure dongle powers up
00517 
00518     // Set initial blank message
00519     resetMessageBuffers();
00520 
00521     Thread receiveTask(receiveSMS, NULL, osPriorityNormal, 1024 * 8);   // try 6 next
00522 #ifdef USE_LED
00523     Thread displayTask(displayScrollingLine, NULL, osPriorityNormal, 1024 * 4);
00524 #endif
00525 
00526     // Show we are still working by alternitively flashing LED1 and LED2, once a second
00527     led1 = 0;       // Working
00528     led2 = 1;       // Alternate with led1
00529     led3 = 0;       // Received and processing SMS
00530     led4 = 0;       // Processing USSD message queue
00531     while(1) {
00532 #ifdef USE_LED
00533         // Only display date/time is we have a 2 row display
00534         if( twoLineDisplay ) {
00535             if( --displayCount <= 0 ) {
00536                 displayTime = !displayTime;
00537                 displayCount = 10;
00538                 led.putString(0,8, "            " );
00539             }
00540             if( displayTime ) {
00541                 showTime();
00542             } else {
00543                 showDate();
00544             }
00545         }
00546 #endif
00547         led1=!led1;
00548         led2=!led2;
00549         Thread::wait(1000);
00550     }
00551 }