Text Message in a Bottle

by ossum in Circuits > Arduino

12263 Views, 133 Favorites, 0 Comments

Text Message in a Bottle

IMG_0001_edited.JPG
IMG_0004.JPG
IMG_9999_edited.JPG
IMG_0026_edited.JPG
IMG_0019.JPG

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

IMG_20151215_153509.jpg

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

IMG_20151217_153700.jpg

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

IMG_20151217_091118.jpg

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 SymbolFunctionConnection
1VssGNDLinkIt One GND
2Vdd+5VLinkIt One 5V
3VoContrast AdjustmentGND (via 150Ohm resistor)
4RSRegister SelectLinkIt One D13
5R/WRead/WriteGND
6EEnable SignalLinkIt One D12
7DB0Data Bus LineN/C
8DB1Data Bus LineN/C
9DB2Data Bus LineN/C
10DB3Data Bus LineN/C
11DB4Data Bus LineLinkIt One D11
12DB5Data Bus LineLinkIt One D10
13DB6Data Bus LineLinkIt One D09
14DB7Data Bus LineLinkIt 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

code.jpg

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;
    }
  }
}

Put It All Together

IMG_20151217_154659.jpg
IMG_20151216_234441.jpg
IMG_20151216_230921.jpg
IMG_20151217_160315.jpg

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.