SMS message display on LED Matrix board with printer option
Dependencies: AdafruitThermalPrinter HT1632_LedMatrix VodafoneUSBModem mbed-rtos mbed
main.cpp
- Committer:
- SomeRandomBloke
- Date:
- 2013-07-29
- Revision:
- 8:401c7a593dd0
- Parent:
- 7:b46d5d98434a
File content as of revision 8:401c7a593dd0:
/** * 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? * Incoming queue for multiple messages, give each at least 60 to be displayed * */ #define USE_LED #undef USE_PRINTER #include <ctype.h> #include "mbed.h" //#include "beep.h" #include "VodafoneUSBModem.h" //#include "LinkMonitor.h" #ifdef USE_LED #include "HT1632_LedMatrix.h" #endif #ifdef USE_PRINTER #include "AdafruitThermal.h" #endif //#define DEBUG 1 #ifdef USE_LED // Default scrolling message includes own number obtained using USSD request #define INFO_MSG " Send a message to " //#define INFO_MSG " Welcome to the IoT London Showcase. Send a message to " //#define INFO_MSG " Welcome to the Reading Geek Night. Send a message to " #endif #define BRIGHTNESS 10 // Vodafone USSD commands #define USSD_COMMAND_OWN_NUMBER "*#100#" #define USSD_COMMAND_BALANCE "*#134#" #define USSD_COMMAND_TIME "*#103#" #ifdef DEBUG 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(p7, p5, p19, p17, p18, p20); //, LED_MAX_DISPLAY_X,LED_MAX_DISPLAY_Y ); //HT1632_LedMatrix led = HT1632_LedMatrix(p7, p5, p17, p18, p19, p20); //, LED_MAX_DISPLAY_X,LED_MAX_DISPLAY_Y ); #define DISPDELAY 90 #endif // Message buffers. New message waiting to be displayed and current message being displayed //#define MAX_NUM_MSGS 10 static char cmdBuf[12]; static char newMsgBuf[MAX_MSG_LENGTH]; static char msgBuf[MAX_MSG_LENGTH]; //static int currentMsg = 0; //static int numberOfMsgs = 0; static bool getNextMessage = true; #ifdef USE_LED static char ownNumber[20]; 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 object for buzzer to signal new message received //Beep buzzer(p21); // 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 request in progress 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; // buzzer.beep(1000,0.5); } void resetMessageBuffers( ) { strcpy( msgBuf, " " ); newMsgBuf[0] = '\0'; } #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 getOwnNumber(VodafoneUSBModem *_modem, char *numPtr ) { int retryCount = 3; while( retryCount ) { sendUSSDCommand( _modem, USSD_COMMAND_OWN_NUMBER, false ); #ifdef DEBUG debug_pc.printf("Own Number received %s\n", newMsgBuf); #endif if( strlen(newMsgBuf) > 0 ) { // Save number in array pointed to be numPtr strncpy(ownNumber, newMsgBuf, strlen(newMsgBuf) ); retryCount = 0; } else retryCount--; } } char linkStateStr[6][13] = { "Unknown ", "Registering ", "Denied ", "No Signal ", "Home Network", "Roaming " }; char bearerStr[6][5] = {"Unkn", "GSM ", "EDGE", "3G ", "HSPA", "LTE " }; void receiveSMS(void const*) { VodafoneUSBModem modem; time_t seconds = time(NULL); int pRssi = 0; LinkMonitor::REGISTRATION_STATE pRegistrationState; LinkMonitor::BEARER pBearer; threadRestartCount++; // Getting the link state seems to speed up startup of the library // Sometimes startup doesnt happen - need way to retry this 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(newMsgBuf, "Link State: %s on %s ", linkStateStr[pRegistrationState], bearerStr[pBearer] ); #ifdef USE_LED setNewMessage( newMsgBuf ); #endif #ifdef USE_PRINTER timestampMessage( newMsgBuf ); #endif Thread::wait(3000); // Get own number from the network getOwnNumber( &modem, ownNumber ); #ifdef USE_LED sprintf(newMsgBuf, "Own number %s", ownNumber ); setNewMessage( newMsgBuf ); #endif #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[currentMsg][0], INFO_MSG ); sprintf(newMsgBuf, "%s %s ", INFO_MSG, ownNumber ); setNewMessage( newMsgBuf ); led.displayOn(); #endif char num[17]; size_t smsCount; getNextMessage = true; while(true) { if( modem.getSMCount(&smsCount) == OK ) { if( smsCount > 0 && getNextMessage ) { led3 = 1; getNextMessage = false; #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 ); #ifndef USE_LED // Force processing of next messages if only printer being used // If LED display then update when displayed once getNextMessage = true; #endif #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 ) { sprintf(newMsgBuf, "%s %s ", INFO_MSG, ownNumber ); setNewMessage( newMsgBuf ); #endif // } else if ( strncmp( cmdBuf, "DEMO", 4 ) == 0 ) { // matrixDemo(); // sprintf(newMsgBuf, "%s %s ", INFO_MSG, ownNumber ); // setNewMessage( newMsgBuf ); #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); bool msgFinished = true; while(true) { // shift the whole screen 6 times, one column at a time; making 1 character if( strlen( &msgBuf[0] ) > 10 ) { for (int x=0; x < 6; x++) { led.scrollLeft(1, 1); msgx--; if( msgx <= 0 ) { msgFinished = true; getNextMessage = true; } // 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); } } else { getNextMessage = true; msgFinished = true; } // Look for a new message being available and current message has been displayed // TODO add minimum interval between message changes, e.g. 60s if( resetMessage && msgFinished ) { led.clear(); led.setBrightness(BRIGHTNESS); crtPos = 0; msgx = 1; msgFinished = false; 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(LED_MAX_DISPLAY_X,LED_MAX_DISPLAY_Y); } */ 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 all displays as 128x8 display led.clear(); led.setBrightness(BRIGHTNESS); //led.displayOff(); // Turn off display for now until receiver task 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 // Startup beep // buzzer.beep(1000,0.5); Thread::wait(30000); // Wait for bit longer to ensure dongle powers up // Set initial blank message resetMessageBuffers(); 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 and LED2, once a second led1 = 0; // Working led2 = 1; // Alternate with led1 led3 = 0; // Received and processing 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); } }