Software apps and online services

Story

Introduction

Looking to recreate the glory of the late 90s and early 2000s, I thought I would try to make my own two-way pager, just like Jay-Z used to use! But I’ve found this idea to be more useful than at first glance—I designed it so it could be used by children in lieu of a real cell phone, or it could be used as an “SOS” button for someone working alone outdoors or even exercising (if it was a bit smaller). And for the last few weeks, this device has been always on in my living room where my wife and I leave silly messages for each other.

Summary: This project sends and receives text (SMS) messages via an arduino with cellular modem built into a small enclosure with an LCD display and simple control buttons.

Demo of messaging system working to send and then receive a message

Note that the code for this project includes the ability to run the system from the serial interface so you can build and use a version of this project even if you don't have an LCD shield!

Hardware selection

I had an Arduino GSM Shield I wanted to use with this project along with a SIM card from Hologram, so combining with an Arduino UNO I had made a lot of sense. Adafruit makes an LCD display shield with buttons built-in, and because it uses the I2C bus, it uses only two pins on the Arduino for the display and five buttons, leaving lots of room for future customization.

Arduino Uno, GSM shield, and LCD shield

The LCD has a nice blue background color, so I figured combining it with a translucent blue enclosure would look nice. Stacking the three boards on top of each other made for some very clean connections and was no issue electrically because the two shields use different pins from each other.

LCD shield: Analog 4 and 5 pins for the I2C bus (along with +5V power and ground [but only the ground next to the Vin pin, not the one next to the 5V pin])

GSM shield: pins 2 and 3 for software serial, and pin 7 as a power reset (plus power and ground obviously, even though I never saw any details of this in the documentation)

Everything else is free to expand still! Other devices could even still be added to the I2C bus.

During development, the system ran fine on just USB power from my laptop for sending and receiving SMS, though I suspect it will need more power for using GPRS cellular data reliably.

Prototype working and running off of USB power

Cellular communication

Hologram provides a cellular data service that works with any device that accepts a SIM card. After registering my SIM card for service, with just an extra click I was able to assign it a phone number in my area code of choice. Another feature that makes this project great as a kids’ communicator, for example, is that it’s so inexpensive to keep the service active ($0.40/mo) and I can pause it at any time if I stop using the device. Like most cell phone plans, receiving texts is free and sending is $0.19 each. In the future, this cost can be lowered even further by using a data connection to Hologram’s cloud service where a message can be automatically routed to a free SMS service. $0.60 for one megabyte of data will get you a whole lot of tiny text messages.

One of the setup screens in the excellent Hologram cloud service

For whatever reason, even though the GSM shield was ultimately working properly, it would not correctly run the GSMScanner
and GSMModem getIMEI()
functions designed for initial setup and troubleshooting. Pretty misleading! SMS and GPRS data functions worked fine, so if anyone knows why the modem still can’t tell me its own IMEI number, I’d love to hear your ideas.

Code and Interface

Adafruit has excellent instructions to show the LCD shield in action and Arduino has a good getting-started guide for the GSM shield that showcases the important functions, so most of my code was based on those two bodies of functions, plus the addition of my own user interface.

I had never programmed a user interface before that wasn’t entering a letter into a command line with a keyboard, so I wanted to keep it simple. I decided to make a selection of canned messages to send that could be scrolled through with the up and down keys—actually easier than I thought!

Just by stacking the three boards on top of each other with a working SIM card (even from your own phone!) and flashing the code below, you’ll get a text messaging device that works like this:

Power it on, it boots up, and tells you once the network is connected

Left button: Go to the “home” screen

Right button: Acknowledge in incoming message, allowing the next message to come in if there is one. Also dims the screen if there is no current message.

Up/down buttons: Scroll through selection of pre-programmed messages to send. I guess you could put as many as you want into the code; I started with four.

Select button: Send the currently selected message

That’s it! Incoming messages are automatically displayed no matter what menu is active on the device, and will even turn the backlight on if it had been off.

The next logical step might be to add a menu of destination numbers for the outgoing messages, or just have it automatically respond to the last message it received (using the remoteNumber()
function).

Final build

To cut the openings in the project box, I used a drill for normal holes and then a Dremel multipurpose cutting bit (#561) with guide (#565) for the larger cutouts. Using some extra boards and clamps to make a jig to guide the Dremel would have made for cleaner lines, but I am happy enough with what I did freehand. The bit definitely wanted to wander more than I expected!

The only actual wires in the project are for power to add a 9V battery and switch—without a battery it can be powered from USB or a wall plug and you’re done!

A 9V battery and clip fits nicely next to the Arduino and underneath the GSM shield (which is about the same size as an Arduino Mega). I soldered wires to some header pins to make the power connection easy to attach.

Thankfully the GSM shield had extra long and bendable leads on its pins. I was able to bend them in slightly so that they missed the sockets on the Arduino, leaving those connections free for my power header. I suspect this is why the pins are like that! The LCD shield on top has short and stout pins with no female connections on the front side, so bending pins away is the only method I found to connect things other than the shields in this setup.

Clipping down the USB port solder leads and a few others on the underside of the Arduino allow it to sit flat in the case on some double-stick foam tape. Then the boards stack up to leave the LCD and buttons just barely protruding above the top of the case—perfect!

1 / 4 • Putting the pieces together

Conclusion

I’ll keep tinkering with this system and even let my 4-year-old nephew bang it around a little. I think it works pretty well as a messenger, but I was surprised that I like it even more as a tiny billboard in my house to display messages. I’m happy with the aesthetic, which helps there.

1 / 2 • It's alive! Hello, Hologram!

Further work on this could it have a second menu for selecting the recipient of a message, automatically dim the display to save power, add lights/buzzers/pager motors for notifications, or any other preferred user interface behavior you could want!

The system could also be fundamentally changed to display notifications from a service like IFTTT, like for if you had a package delivered or someone’s latest tweets. What are you building? How would you improve the system?

Code

System code for Arduino

Arduino

/** * Two-way pager system with Arduino and Hologram cellular service * Mike Schaus * Dec 28, 2016 * Made as part of the Hologram Hacker-In-Residence program * * This project sends and receives text (SMS) messages via * an arduino with cellular modem built into a small enclosure * with an LCD display and simple control buttons. * * Note that code is included to function via the LCD shield OR * the serial monitor, so you can use this code even without * an LCD shield. */#include<GSM.h>#define PINNUMBER ""// include the LCD library code:#include<Wire.h>#include<Adafruit_RGBLCDShield.h>#include<utility/Adafruit_MCP23017.h>// The shield uses the I2C SCL and SDA pins. On classic Arduinos// this is Analog 4 and 5 so you can't use those for analogRead() anymore// However, you can connect other I2C sensors to the I2C bus and share// the I2C bus.Adafruit_RGBLCDShieldlcd=Adafruit_RGBLCDShield();// These #defines make it easy to set the backlight color#define OFF 0x0#define ON 0x1// make the arrow special character on the LCDconstbytearrow[8]={B00000,B00000,B01000,B01100,B01110,B01100,B01000,B00000};// initialize the GSM library instanceGSMgsmAccess(false);// include a 'true' parameter for debug enabledGSM_SMSsms;// char array of the telephone number to send SMS// change the number 12125551212 to a number// you have access tocharremoteNumber[20]="12125551212";// Array to hold the number a SMS is retreived fromcharsenderNumber[20];// char array of the possible outgoing messages to choose from the menuchar*responses[]={"Mike=Awesome!","Yes","No","Howdy!"};//#define NUMRESPONSES 4 // if someone knows how to calculate this instead, I'm all ears#define NUMRESPONSES (sizeof(responses)/sizeof(char *)) // thanks to Steve Kemp's comment!intposition=-1;// this way the first button press will always show first option of the menuintinByte=0;// incoming serial byte for keyboard interfacebooleanbacklight=true;// track backlight status for togglingunsignedlongpreviousMillis=0;// will store last time messages were checked#define CHECKINTERVAL 1500 // how often to check for text messagesvoidsetup(){// put your setup code here, to run once:// initialize serial communicationsSerial.begin(9600);Serial.println(F("SMS Message Sender -- starting up..."));// set up the LCD's number of columns and rows: lcd.begin(16,2);// Print a message to the LCDlcd.print(F("Hello, Hologram!"));lcd.setCursor(0,1);lcd.print(F("Starting up..."));lcd.setBacklight(ON);// set up the arrow character for displaylcd.createChar(0,arrow);// connection statebooleannotConnected=true;// Start GSM shield// If your SIM has PIN, pass it as a parameter of begin() in quoteswhile(notConnected){if(gsmAccess.begin(PINNUMBER)==GSM_READY){notConnected=false;Serial.println(F("GSM is connected because you are so awesome"));Serial.println(F("Waiting for messages, or send with \"s\""));Serial.println();lcd.clear();lcd.setCursor(0,0);homeScreen();}else{Serial.println(F("Not connected"));lcd.clear();lcd.setCursor(0,0);lcd.print(F("Not connected"));delay(1000);}}}// this is the menu system functionvoidshowResponses(){// Serial.println(position); // only for debugging menu systemlcd.clear();lcd.setCursor(0,0);// make sure cursor position is legalif(position<0)position=0;if(position>NUMRESPONSES-1)position=NUMRESPONSES-1;// write current selection and next option if there is another optionlcd.write(0);//arrow characterlcd.print(position+1);lcd.print("-");lcd.print(responses[position]);if(position<NUMRESPONSES-1){lcd.setCursor(0,1);lcd.print(" ");lcd.print(position+2);lcd.print("-");lcd.print(responses[position+1]);}}voidhomeScreen(){lcd.clear();lcd.setCursor(0,0);lcd.print("SMS Messenger!");lcd.setCursor(0,1);lcd.print("Ready; up/dn snd");position=-1;//reset response selection}voidreceiveSMS(){charc;// If there are any SMSs available()if(sms.available()){Serial.println("Message received from:");// Get remote numbersms.remoteNumber(senderNumber,20);Serial.println(senderNumber);lcd.clear();lcd.setCursor(0,0);backlight=true;lcd.setBacklight(ON);// An example of message disposal// Any messages starting with # should be discardedif(sms.peek()=='#'){Serial.println("Discarded SMS");sms.flush();}// Read message bytes and print them// because sms.read only returns one character at a timeinti=0;while(c=sms.read()){i++;Serial.print(c);if(i==17)lcd.setCursor(0,1);// move to next line if neededif(i<33)lcd.print(c);// don't try to print more than 32 chars just in case}Serial.println("\nEND OF MESSAGE");// Delete message from modem memorysms.flush();Serial.println("MESSAGE DELETED");Serial.println();// wait for right button to acknowlege before letting program continuebooleanacknowledged=false;while(!acknowledged){uint8_tbuttons=lcd.readButtons();if(buttons&BUTTON_RIGHT)acknowledged=true;delay(50);//short delay for troubleshooting -- without this it behaves strangely}homeScreen();delay(400);// prevent multiple presses in a row}}// function to show message options in the serial monitorvoidprintResponseOptions(){for(inti=0;i<NUMRESPONSES;i++){Serial.print(i);Serial.print("-");Serial.println(responses[i]);}Serial.println();}voidsendSMS(constchar*txtMsg){Serial.print("Message to mobile number: ");Serial.println(remoteNumber);// print sms text infoSerial.println("SENDING");Serial.println("Message:");Serial.println(txtMsg);// send the messagesms.beginSMS(remoteNumber);sms.print(txtMsg);// next, add a signature to the chosen messagesms.print(" --Be sure to connect with me on my blog http://mschausprojects.blogspot.com");// call endSMS function to finish sending; it will return 1 if successfulif(sms.endSMS()==1){Serial.println("\nCOMPLETE!\n");homeScreen();}else{Serial.println("\nERROR\n");lcd.clear();lcd.setCursor(0,0);lcd.print("error");}Serial.println();}voidloop(){// put your main code here, to run repeatedly:uint8_tbuttons=lcd.readButtons();if(buttons){if(buttons&BUTTON_UP){position--;showResponses();backlight=true;lcd.setBacklight(ON);}if(buttons&BUTTON_DOWN){position++;showResponses();backlight=true;lcd.setBacklight(ON);}if(buttons&BUTTON_LEFT){homeScreen();backlight=true;lcd.setBacklight(ON);}if(buttons&BUTTON_RIGHT){backlight=!backlight;// toggle the backlight stateif(backlight)lcd.setBacklight(ON);elselcd.setBacklight(OFF);homeScreen();// have to write to screen after turning light off, otherwise it goes blank}if(buttons&BUTTON_SELECT){// make sure cursor selected position is legalif(position<0)position=0;lcd.clear();lcd.setCursor(0,0);lcd.print("Sending...");lcd.setCursor(0,1);lcd.print(responses[position]);backlight=true;lcd.setBacklight(ON);sendSMS(responses[position]);}delay(200);// prevent multiple presses in a row}// this is for serial interface only, not related to LCD and buttons// send a message when I type "s" in serial monitor// then wait for my selection of the response numberif(Serial.available()>0){inByte=Serial.read();// get incoming byteif(inByte=='s'){printResponseOptions();while(Serial.available()>0){// clear the keyboard buffer just in casecharjunk=Serial.read();}while(Serial.available()==0);// Wait here until input buffer has a characterinByte=Serial.parseInt();// would want to check for valid choice here to be more robustsendSMS(responses[inByte]);}}// check for new messages only once every few seconds to keep interface more responsiveunsignedlongcurrentMillis=millis();if(currentMillis-previousMillis>=CHECKINTERVAL){previousMillis=currentMillis;receiveSMS();// takes about 26ms when there are no messages}}