Text Message in a Bottle
What is It
How often have you sent off a message in a bottle, only to realise, as it floats out of sight, that you regret the contents of your message? Well, with the Text Message in a Bottle (TM) that will no longer be a problem! You can update your message at any time, just by sending a text. Fantastic!
Why Did I Build It
I have been following the oodles of submissions to the LinkIt One MediaTek Collection, and most of them are without housings, even projects like weather stations that are ostensibly for use outdoors, so I decided to demonstrate a simple waterproof project with off the (kitchen) shelf parts.
Features (or, what you can learn in this Instructable)
- Battery Powered
- Solar Charged
- Display battery/charging status on one line of a 16x2 character LCD
- Receives SMS
- Display scrolling SMS on the other line of a 16x2 character LCD
Other Ideas/Complications
My original plan was to make a completely self-contained, solar-powered, WiFi-hotspot-in-a-jar, that would allow people to connect and download files or leave notes on a web server or something, but unfortunately the LinkIt One does not support running the WiFi in Access Point (AP) mode.
My next idea was to the same sort of thing using Bluetooth, like some shops do, where they send you an image or message when you come in range (proximity marketing), but it seems that this is also not yet in the API.
Components Used
Linkit One
The MediaTek LinkIt One is an Arduino-compatible board with some very cool features built in, in this project we are using the built-in GSM for receiving SMS messages as well as the built-in battery charger to charge the included battery.
Sim Card
A prepaid sim-card that can receive text messages. Preferably one without a PIN.
Solar Panels
I used 3 long narrow 8-10V solar panels of approximately 1W output, I got them out of broken/obsolete equipment, so I don't have any info at all on them.
You should be able to buy things like this easily on sites such as Aliexpress if you don't mind waiting for the shipping.
DC-DC Converter/Regulator
I chose the Power Trends PT78ST105S because I had one lying around, here are its specs
Input Voltage: 9-38V
Output Voltage: 5V
Output Current: 1.5A
Duct Tape
I used a knock-off of Duct Tape to turn the three solar panels into a foldable unit, as well as to attach the various modules to the back of the panels. It looks like a hack job when open, but when folded up it is rather neat.
Jar
Jars designed for pickling or jams are ideal, because they have wide necks. I couldn't find a large enough Mason Jar, which i think would be the best, so I chose a 1.5l swing top instead.
Wire Solar Panels and Converter
The Easy Way
There is an premade solution of course if you don't want to build the circuit yourself, just search places like aliexpress for solar-powered cellphone chargers, swipe your credit card and 60 days later you will have a solution.
The Hard Way
I wrote an in-depth Instructable on powering your LinkIt One via solar power over here, so I recommend reading over that if you need more details.
Wiring The Panels
Since I arranged my panels in a triangular tube shape (like a Toblerone), I knew that at least one would always be considerably more shaded than the others.
This is a complicated topic (which you can read all about over here), but the key issue is that
- The output current of a string of cells in series is limited to the current generating capability of the most shaded cell.
- The output voltage is roughly constant above a certain illumination level.
If the cells are all in series and one of them is shaded, the total current flow will be restricted, but if they are in parallel then the cells that are receiving light will be able to deliver their generated power unimpeded.
Choosing the 5V Converter
The LinkIt One has an on-board battery charger, which expects to be provided with a nice steady 5V via the USB port.
The output voltage of a solar panel varies slightly with the amount of illumination it receives, but it is almost always going to be more than 5V, so we need to convert it down using a switch-mode regulator.
There are oodles of options, you just need to make sure that the one you choose meets the following requirements
Input Voltage (from the panels)
Make sure that the expected output voltage of your solar panel fall within the input voltage range of the switch-mode regulator
Output Voltage (to the LinkIt One)
5V, no more, no less
Output Current
The regulator must be able to handle the current that you are putting through it.
Take a look at your solar panel's current rating (or determine it from power/voltage), this will be the maximum input current, and the regulator must be able to handle it.
Ignoring inefficiencies, output current will be (input current)*(input voltage/output voltage), and the regulator must be able to handle it.
Example Regulator 1
I chose the Power Trends PT78ST105S because I had one lying around, here are its specs
Input Voltage: 9-38V
Output Voltage: 5V
Output Current: 1.5A
Example Regulator 2
I am also quite a fan of these little guys, but note here how the input voltage is 14V minimum, which means that I would have needed two of my 8V solar panels in series.
Input Voltage: 14-28V
Output Voltage: 5V
Output Current: 500mA
Wiring the 5V Converter
You will need a micro-USB connector, I got one off a broken cellphone charger.
The wiring is as follows
- Positive output of the solar panel to Vin on the regulator
- Negative output of the solar panel to GND on the regulator
- Vout from the regulator goes to +5V on the USB connector
- GND from the regulator goes to GND on the USB connector
Connect the 16x2 LCD
Make the connections
I used a 16x2 LCD that I had scavenged out of some old electronics, but if you have a 16x4 or similar that would work too, anything based on the HD44780 chip will work with this code.
The following table shows the purposes of all of the pins, as well as the connections to make to the LinkIt One.
If the LCD has a backlight then it will probably be an LED across pins 15 and 15, but I would consider it a waste of power in this application.
LCD Pin Number | Symbol | Function | Connection |
---|---|---|---|
1 | Vss | GND | LinkIt One GND |
2 | Vdd | +5V | LinkIt One 5V |
3 | Vo | Contrast Adjustment | GND (via 150Ohm resistor) |
4 | RS | Register Select | LinkIt One D13 |
5 | R/W | Read/Write | GND |
6 | E | Enable Signal | LinkIt One D12 |
7 | DB0 | Data Bus Line | N/C |
8 | DB1 | Data Bus Line | N/C |
9 | DB2 | Data Bus Line | N/C |
10 | DB3 | Data Bus Line | N/C |
11 | DB4 | Data Bus Line | LinkIt One D11 |
12 | DB5 | Data Bus Line | LinkIt One D10 |
13 | DB6 | Data Bus Line | LinkIt One D09 |
14 | DB7 | Data Bus Line | LinkIt One D08 |
If you want more information on connecting up a 16x2 LCD, then Adafruit's well documented articles are always a good start.
Test
Once you have connected these pins and powered up the LinkIt via USB or battery you should see a single row of black blocks on the LCD. If you don't then something is wrong, check all the connections and consider adjusting the contrast resistor, connecting pin 3 to GND directly will set maximum contrast.
Now you are ready to write some code to control the display.
Write Program
IMPORTANT NOTE: LiquidCrystal Library
At the time of writing this the LinkIt One IDE comes with version 1.6.5 of the Arduino IDE, which happens to be one where the LiquidCrystal Library that we are using is broken. Look at this Instructable on how to replace the LiquidCrystal files with working ones.
Intro
I am assuming that you have already set up your Arduino IDE to work with the LinkIt One, if not, have a look at the "getting started" guide over here.
I have attached my final code to this step, you should be able to read through it and its comments to get a pretty good idea of how it works. I will describe the important segments here.
Import Libraries
The first step is to included the various libraries that are going to be used.
#include <LGSM.h> #include <LiquidCrystal.h> #include <LBattery.h>
Define Variables
Next we define various global variables that will be used in the program. You will see that a number of them are defined as "char name[x]" which means that they are an array of x characters, which is use to store things like the text message or a status message.
//these variables are for the sms char smsContent[160]; int smsLength = 0; char smsSender[20]; boolean newMessage = LOW; //variables for battery status char statusMessage[16]; // initialize the LCD library with the numbers of the interface pins // lcd(RS, Enable, DataBus4, DataBus5, DataBus6, DataBus7) LiquidCrystal lcd(13, 12, 11, 10, 9, 8); int scrollInterval = 500; unsigned long lastUpdate = 0; int msgStartChar = 0; int lcdStartChar = 0;
setup() Function
This function is run once when the board boots.
void setup() { lcd.begin(16, 2); // set up the LCD's number of columns and rows: waitForSim(); //wait for simcard to be active Serial.begin(9600); //start the serial port }
waitForSim() Function
The rest of the program won't work if the SIM card is not initialised, so this function just holds the device in a loop, waiting for the SIM to be ready (and printing out little dots on the serial interface, so that you know it hasn't died).
void waitForSim() { Serial.print("waiting for sim"); while (!LSMS.ready()) { delay(250); Serial.print('.'); } Serial.println("Sim Ready"); }
receiveSMS() Function
This function will check if there is a new SMS. If there is one, it is read in, one character at a time, and stored in the smsContent buffer (a character array), then the function returns HIGH (true/1). If there is no new SMS then the function returns LOW (false/0). This allows us to call it as often as we like in the main loop, but only do something if there is a new message.
The "newMessage" boolean is also set to TRUE when a new message is received.
Once the SMS has been read it is removed with the flush() command.
boolean receiveSMS() { //this function will store a new sms in the buffers //or return false if there is no new sms int v; if (LSMS.ready() && LSMS.available()) // Check if there is new SMS { Serial.println("There is new message."); newMessage = HIGH; LSMS.remoteNumber(smsSender, 20); // store sender in buffer smsLength = 0; //storing a new sms while (true) { v = LSMS.read(); if (v < 0) break; smsContent[smsLength] = v; smsLength++; } smsContent[smsLength] = '\0'; smsLength++; LSMS.flush(); // delete message return HIGH; } else { return LOW; } }
chargeStatus() Function
This function creates a 16 character line of text about charge status.
Lbattery.isCharging() returns a 1 if the battery is charging and a 0 if it is not.
Lbattery.level() returns 0,33,66 or 100 depending on the battery's current charge level.
We use the "sprintf" function to format the output from the previous two functions into a nice line of text, which we store in the statusMessage character array for use in the updateDisplay() function.
void chargeStatus() { if(LBattery.isCharging()) { sprintf(statusMessage,"%3d%% charging", LBattery.level() ); } else { sprintf(statusMessage,"%3d%% charged", LBattery.level() ); } }
updateDisplay() Function
This is where all the previously generated content is sent to the LCD for display. It will make the most sense to read it through and look at the inline comments.
The first thing to note is the use of an counter, which takes note of when the screen was last updated (in milliseconds, provided by the Arduino millis() function), and only updates it again if sufficient time (scrollInterval) has elapsed.
The scrolling of the message on the 2nd line is achieved by storing two variables:
msgStartChar
This variable stores which character of the SMS content we are printing first (because we cannot fit the whole 160 character sms on a 16 character line)
lcdStartChar
This variable stores where on the LCD the message starts, since we want the message to scroll in from the right, it cannot always just start on the first character.
Each time that the display is updated we start from the right (position 15) and reduce lcdStartChar until the message is starting on the first character (position 0), this effectively scrolls the message left.
Once the start of the message is at the far left, it needs to scroll off-screen, so we increment the msgStartChar on each update.
void updateDisplay() { //this function will scroll the message on one line and display battery status on the other unsigned long currentMillis = millis(); if (currentMillis - lastUpdate > scrollInterval) { lastUpdate = currentMillis; //get battery status chargeStatus(); lcd.clear(); //first we clear the whole lcd lcd.setCursor(0, 0); //set cursor to first line lcd.print(statusMessage); //print sms on the 2nd line if (newMessage == HIGH) { msgStartChar = 0; //goto first line of new message lcdStartChar = 15; newMessage = LOW; //message dealt with } lcdStartChar = max(0,lcdStartChar - 1); //move one char along on the message (for scrolling) lcd.setCursor(0, 1); //set cursor to bottom line for (int j=0; j<= 15; j++) { if (j < lcdStartChar) { lcd.print(" "); } else if ((msgStartChar+(j-lcdStartChar)) < (smsLength-1)) { lcd.print(smsContent[msgStartChar+(j-lcdStartChar)]); } else { lcd.print(" "); } } if (lcdStartChar <= 0) { msgStartChar++; //move message to the left } if (msgStartChar >= smsLength) { Serial.println("End of message"); //we have reached the end of the message, go back to the start lcdStartChar = 15; msgStartChar = 0; } } }
Downloads
Put It All Together
There isn't much to this step, and it will depend largely on the panels, LCD and jar that you choose.
Take a look at my photos, you will see that I connected the three panels using duct tape to form triangular tube that can be easily unfolded to work on the components that are mounted inside.
The fourth side of the triangle.. ok, it's not a triangle anymore... is just a bit of balsa wood cut to size, with a rectangular hole cut for the LCD. I wrapped the balsa in duct tape too to make it prettier.
The unconnected edge of the tube has small magnets under the tape, so that it can easily be opened and unfolded.