SMS message display on LED Matrix board with printer option
Dependencies: AdafruitThermalPrinter HT1632_LedMatrix VodafoneUSBModem mbed-rtos mbed
Diff: main.cpp
- 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