Every morning, especially in Midwest springs, I have to check the weather in order to know what to wear. To solve this, I've created an LCD display that shows all the information I need. This includes current time, current temperature, likelihood of rain, and the day's high temperature.
The DisplayThe display is an I2C-powered LCD display I picked up from Amazon for under $10. This display worked out-of-the-box with a community contributed Particle LCD library, so I assume most I2C LCD displays will work without too many issues.
WiringGetting the LCD display properly connected to the Particle Photon was the most challenging part since documentation and examples were sparse.
1. Connect two pullup resistors to the Photon's D0 and D1 pins. Particle's documentation shows 1.5k to 10k will work. I'm using 2.2k without any issues.
2. Connect the Photon's D0 pin to the LCD display's SDA pin. Connect the Photon's D1 pin to the LCD display's SCL pin.
3. Connect the LCD display's GND pin to the Photon's GND pin.
4. Connect the LCD display's VCC pin to the Photon's VIN pin. We do this because the LCD display requires 5V and the only voltage output on the Photon is 3.3V. The VIN pin on the Photon is connected directly to the USB 5V input. Obviously if you're powering the Photon using something other than USB you'll have to find another way to provide 5V to the display.
Here's an image of the final wiring:
Follow Particle's documentation to get your Photon all setup and connected to your WiFi. We'll then use their Build IDE to program the Photon. I made the Photon's firmware very general, so it could be used to display any arbitrary text sent to it from the Losant IoT Platform. The firmware uses a Particle Function that is invoked with JSON data that contains two lines of text. The actual text to display is all generated using a Losant workflow, which we'll cover later.
First, include the SparkJson and LiquidCrystal_I2C_Spark libraries to your app using Particle's library manager.
The entire code is below and also available on GitHub. Copy/paste this code into the Build IDE and flash it to your device.
#include "SparkJson/SparkJson.h"
#include "LiquidCrystal_I2C_Spark/LiquidCrystal_I2C_Spark.h"
LiquidCrystal_I2C *lcd;
// Particle function handler. Called whenever
// the setText function is invoked from Losant.
int setText(String arg) {
Serial.println(arg);
// The text to write to the display is sent as a JSON object with
// two properties for each line.
// { L1: "line one text", L2: "line two text" }
StaticJsonBuffer<200> jsonBuffer;
JsonObject& text = jsonBuffer.parseObject((char*)arg.c_str());
// Clear the screen.
lcd->clear();
// Move the cursor to the start of the first line and print the text.
lcd->setCursor(0,0);
lcd->print(text["L1"].asString());
// Move the cursor to the start of the second line and print the text.
lcd->setCursor(0,1);
lcd->print(text["L2"].asString());
return 0;
}
void setup()
{
Serial.begin(9600);
// The address is typically 0x27.
lcd = new LiquidCrystal_I2C(0x27 /*address*/, 16 /*columns*/, 2/*rows*/);
lcd->init();
lcd->backlight();
lcd->clear();
// Register a setText function that can be invoked by Losant.
Particle.function("setText", setText);
}
void loop()
{
}
As you can see, the code is pretty short and simple. This firmware registers a Particle function, named "setText", that expects a JSON payload containing two lines of text. It then simply prints those lines to the LCD.
Losant WorkflowLosant is an easy-to-use cloud platform to IoT projects. As I mentioned earlier, we're going to use a Losant Workflow to periodically pull forecast information and then send it to the Photon to display. The first thing you'll have to do is register for a free Losant account. Once you have an account, create an application and a new workflow. You can name both anything you want.
Once you create the workflow, you'll be presented with an empty canvas. Instead of going step-by-step, I've exported a workflow that you can simply import to get up and running much faster.
Download workflow file from GitHub
Once you have the file downloaded, import it into your workflow.
Once imported, your workflow now looks like this:
All workflows start with a trigger. The above workflow has two triggers: a virtual button for manually testing and a timer that fires every two minutes.
The next node gets the weather information from forecast.io. In order to display the weather for your area, you'll have to change the configuration of that node to set your GPS coordinates in the request. Click on the node and edit the URL field.
The next node is responsible for generating all of the text sent to the Photon. This is where all the complexity is. It pulls the time, current temp, day's high, and precipitation probability from the payload and builds the JSON object that Particle expects when invoking their functions using their API. You can modify this function node however you like to display whatever information you want. Below is the code from the function node:
// Get local time string in hours and minutes, e.g. "4:02 PM".
var timeString = payload.time.toLocaleTimeString(
"en-US",
{
timeZone: "America/New_York", // TODO: change to your timezone
hour: 'numeric',
minute: '2-digit'
}
);
// Get chance of precipitation for the day.
var precip = (payload.data.weather.body.daily.data[0].precipProbability * 100) + '%';
// Build Line 1.
var line1 = timeString;
// LCD lines are 16 characters. Pad the string so the precipitation
// probability is right aligned.
for(var i = timeString.length; i < 16 - precip.length; i++) {
line1 += ' ';
}
line1 += precip;
// Get current temperature and high for the day.
var currentTemp = payload.data.weather.body.currently.temperature.toString();
var highTemp = payload.data.weather.body.daily.data[0].temperatureMax.toString();
// Build Line 2.
var line2 = currentTemp;
// LCD lines are 16 characters. Pad the string so the current
// temp is left aligned and the high temp is right aligned.
for(var i = currentTemp.length; i < 16 - highTemp.length; i++) {
line2 += ' ';
}
line2 += highTemp;
var arg = { L1 : line1, L2 : line2 };
// Particle expects a payload that looks like { "arg" : "custom payload" }.
payload.text = JSON.stringify({ arg: JSON.stringify(arg) });
The next node, named "setText", is an HTTP node that invokes the Particle function to update the text on the LCD screen. We're using Particle's Rest API to make this happen. You may notice the "Body Template" is set to {{ text }}, which is probably confusing if this is your first time working with Losant Workflows. Anything surrounded in double curly braces will pull the value from the current payload. In the function node above this, the text field is added to the payload on the last line, which means we can now access it and send its contents to Particle.
The last node is a Debug node, which will simply add an entry to the Debug tab whenever it gets executed. This allows us to easily inspect the payload and see any errors if they occur.
Lastly, open the Globals tab to fill out values required to communicate with Particle and Forecast.io.
As you navigate the configuration for the other nodes, you might notice that these variables are referenced using syntax like {{globals.deviceId}}. Globals allow us to define configuration values in a single place and access them from any of our nodes.
deviceId: The ID of your Particle Photon. You can get this by clicking the Devices menu in the Build IDE and then selecting the small down array next to your device.
forecastAPIKey: Register for the Forecast.io API and enter the API key provided.
particleAPIKey: Your Particle Access Token. You can get this by clicking the Setting menu on the Build IDE.
Once all of the values are filled, you can deploy this workflow.
Once deployed, you can either wait two minutes or click the virtual button to send the information to the Photon.
That's it for this project. You've now got an LCD display connected to a Particle Photon whose text is sent from the Losant IoT Platform. The sky is the limit for how flexible this project can be. You can tweak the Losant Workflow to change the text to anything you want. You can even pull data from other sources if forecast data isn't what you want to display.
Comments