Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: AdafruitThermalPrinter HT1632_LedMatrix VodafoneUSBModem mbed-rtos mbed
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 }
Generated on Sun Jul 17 2022 09:39:55 by
1.7.2