Hardware components | ||||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 |
This project displays weather from Weather Underground on a serial vacuum fluorescent display (VFD).
Behind the scenes, the Photon publishes an event that includes the requested weather station ID to the Particle cloud. This event by name triggers the webhook. This webhook is then returned to a subscribed event in the Photon. The data is then parsed into variables that can be displayed on the serial VFD.
This project explores using system threading to allow the display to operate immediately upon power up and provide connection status messages.
A Serial level converter was necessary to adapt the 3.3v serial signal from the photon to be compatible with the VFD display. This converter boost the serial voltages up to +/-15v. This display will not operate properly with a 0-5v or 0-3.3v level serial signal.
Add the webhook in your console.particle.io. Go to add a new integration:
Paste custom JSON code here. Be sure to edit the API key to be your key. The text below is the default webhook code. Delete this. Use the code provided in the attachments of this project instead.
Now you should have a webhook:
Update: The legendary Forrest M. Mims III approves!
More pics of the business end.
SYSTEM_THREAD(ENABLED);
int rssival = 0;
// Define the pins we're going to call pinMode on
int led = D3; // You'll need to wire an LED to this one to see it blink.
int led2 = D7; // This one is the built-in tiny one to the right of the USB jack
int vfdhandler(String command);
int vfdwaitflag = 0;
double analog1 = 0;
double analog2 = 0;
double analog3 = 0;
double analog4 = 0;
double analog5 = 0;
double analog6 = 0;
double analog7 = 0;
double analog8 = 0;
double analog1raw = 0;
double analog2raw = 0;
double analog3raw = 0;
double analog4raw = 0;
double analog5raw = 0;
double analog6raw = 0;
double analog7raw = 0;
double analgo8raw = 0;
//char *ip = "no habla IP address ";
const char vfdnocursorconst = 0x0E;
const char vfderaseconst = 0x15;
const char vfdhomeconst = 0x16;
const char vfdmvcursorconst = 0x1B;
const char vfdbright = 0x30;
//char publishString[40];
//char data[125];
char txbuff[64];
//const String cityLocation = "Cornelius"; //City for my Photon
const String stationid = "KNCCORNE10"; // weather station ID for my Photon
SYSTEM_MODE(SEMI_AUTOMATIC); //allows for control of Spark.connect() and Spark.process()
//Timer intervaltimer(100, intervaltask);
Timer wxintervaltimer(300000, wxintervaltask);
Timer sparkfuncintervaltimer(10000, sparkfuncintervaltask);
// This routine runs only once upon reset
void setup() {
//...
// Initialize D0 + D7 pin as output
// It's important you do this here, inside the setup() function rather than outside it or in the loop function.
Serial.begin(230400); //open serial for debugging
pinMode(led, OUTPUT);
pinMode(led2, OUTPUT);
pinMode(A0, INPUT);
//digitalWrite(A0,0);
Serial1.begin(19200); // open serial for the display
vfdnocursor();
vfderase();
//ip = {"IP:", WiFi.localIP()};
//String ip = WiFi.localIP();
Spark.function("vfdprint", vfdhandler);
Spark.function("vfdctrl", vfdcontrol);
Particle.subscribe("hook-response/current_weather", wxHandler, MY_DEVICES);
pinMode(A0, INPUT_PULLDOWN);
pinMode(A1, INPUT_PULLDOWN);
pinMode(A2, INPUT_PULLDOWN);
pinMode(A3, INPUT_PULLDOWN);
pinMode(A4, INPUT_PULLDOWN);
pinMode(A5, INPUT_PULLDOWN);
pinMode(A6, INPUT_PULLDOWN);
pinMode(A7, INPUT_PULLDOWN);
digitalWrite(A0,1);
}
void loop() {
if (Spark.connected() == false){
//intervaltimer.stop();
wxintervaltimer.stop();
sparkfuncintervaltimer.stop();
//vfderase();
vfdmvcursor(0);
Serial1.print(" Yo from PARTICLE! ");
vfdmvcursor(20);
Serial1.print(" Connecting");
Serial.println("Connecting:"); //print to usb
//Serial1.print("SSID: ");
//Serial1.print(WiFi.SSID());
Spark.connect();
vfdwait(35,1);
}
else {
vfderase();
Serial1.print(" I found a way out. ");
vfdmvcursor(20);
Serial1.print(" ");
vfdmvcursor(20);
Serial1.print("SSID: ");
Serial1.print(WiFi.SSID());
Serial.print("SSID: "); //print to usb
Serial.println(WiFi.SSID()); //print to usb
vfdwait(39,2);
vfdmvcursor(20);
Serial1.print("IP: ");
Serial1.print(WiFi.localIP());
Serial.print("IP: ");
Serial.println(WiFi.localIP());
vfdwait(39,2);
vfdmvcursor(20);
Serial1.print(" ");
vfdmvcursor(20);
Serial1.print("RSSI: ");
Serial1.print(WiFi.RSSI());
Serial.print("RSSI: "); //print to usb
Serial.println(WiFi.RSSI()); //print to usb
vfdwait(39,2);
vfderase();
wxintervaltask();
//intervaltimer.start();
wxintervaltimer.start();
//sparkfuncintervaltimer.start();
while (Spark.connected()){ //main system loop when connected
if (System.buttonPushed() > 500) {
wxintervaltask();
}
Spark.process();
analog1raw = analogRead(A0);
analog1 = (analog1 *63 +analog1raw *20.0/4095)/64;
//analog1 = (analog1*10.0 + analog1raw)/11.0;
// calibrate pot such that 12.00V in is 1.961V at micro.
analog2raw = analogRead(A1);
analog2 = (analog1 *63 +analog1raw *20.0/4095)/64;
analog3raw = analogRead(A2);
analog3 = (analog3 *63 +analog3raw *20.0/4095)/64;
}
}
// delay(1000);
}
void sparkfuncintervaltask(){
Serial.println(".");
/*
rssival = WiFi.RSSI();
sprintf(publishString,"%d",rssival);
Spark.publish("RSSI",publishString);
Serial.print("RSSI:");
Serial.println(publishString);
*/
}
void wxintervaltask(){
//String publishString = "{\"my-city\": \"" + cityLocation + "\", \"my-state\": \"" + stateLocation + "\" }";
Serial.println("Running webook query current_weather:");
String publishString = "{\"stationid\": \"" + stationid + "\"}";
Particle.publish("current_weather", publishString, PRIVATE);
vfdwait(39,2);
}
void wxHandler(const char *event, const char *data) {
Serial.println("Receiving wxhandler subscribe data:");
//Serial.println(data);
//data = data.remove(40);
Serial.println(data);
String str = String(data);
char strBuffer[64] = "";
str.toCharArray(strBuffer, 64); // example: "\"21~99~75~0~22~98~77~20~23~97~74~10~24~94~72~10~\""
//{{#response}}{{#current_observation}}{{#estimated}}{{weather}}~{{temp_f}}~{{relative_humidity}}~{{wind_dir}}~{{wind_gust_mph}}~{{dewpoint_f}}~{{/estimated}}{{/current_observation}}{{/response}}
/*
Serial.println("Running gotweatherData function");
String str = String(data);
char strBuffer[500] = "";
str.toCharArray(strBuffer, 500); // example: \"Clear~78.8~24%~NORTH~3.36~360~\",
*/
String weatherState = strtok(strBuffer, "\"~");
//float currentTemp = atof(strtok(NULL, "~"));
String currentTemp = strtok(NULL, "~");
String humidity = strtok(NULL, "~");
String windDir = strtok(NULL, "~");
String windSpeed = String(atoi(strtok(NULL, "~"))); //convert from string float to integer then back to string to eliminate fractional wind speed
String dewPoint = strtok(NULL, "~");
/*
float maxtemp = atof(strtok(NULL, "~"));
int mainHumidity = atoi(strtok(NULL, "~"));
int maxwind = atoi(strtok(NULL, "~"));
float windDeg = atof(strtok(NULL, "~"));
uint32_t sunrise = atoi(strtok(NULL, "~"));
uint32_t sunset = atoi(strtok(NULL, "~"));
String weatherName = strtok(NULL, "~");
*/
Serial.print("WeatherState: ");
Serial.println(weatherState);
Serial.print("CurrentTemp: ");
Serial.println(currentTemp);
Serial.print("Humidity: ");
Serial.println(humidity);
Serial.print("WindDir: ");
Serial.println(windDir);
Serial.print("WindSpeed: ");
Serial.println(windSpeed);
Serial.print("DewPoint: ");
Serial.println(dewPoint);
//delay(1000);
while (vfdwaitflag==1) {
delay(10);
}
vfderase();
//standard print raw string
//Serial1.print(str.remove(40)); //print weather data to VFD
vfdmvcursor(0);
Serial1.print(weatherState);
vfdmvcursor(14);
Serial1.print(currentTemp);
Serial1.print("F");
vfdmvcursor(20);
Serial1.print(humidity);
vfdmvcursor(24);
Serial1.print(windDir);
vfdmvcursor(30);
//Serial1.print(dewPoint);
vfdmvcursor(38);
Serial1.print(" ");
vfdmvcursor(34);
Serial1.print(windSpeed);
Serial1.print("mph");
digitalWrite(led2, !digitalRead(led2)); //toggle led
/*
if (forecastday1 == Time.day()) {
currentFcastday = forecastday1;
currentFcastmax = maxtempday1;
currentFcastmin = mintempday1;
currentFcastwind = maxwindday1;
Serial.print("Selected forecastday1");
}
if (forecastday2 == Time.day()){
currentFcastday =forecastday2;
currentFcastmax = maxtempday2;
currentFcastmin = mintempday2;
currentFcastwind = maxwindday2;
Serial.print("Selected forecastday2");
}
bool weatherGood = true;
*/
int updateweatherhour = Time.hour();
}
void intervaltask() {
digitalWrite(led, !digitalRead(led)); // Turn ON the LED pins
digitalWrite(led2, !digitalRead(led2));
vfdmvcursor(0); //move cursor to position 0,0 home
//Serial1.print(data);
Serial1.print(" -=PARTICLE=- ");
Serial1.print(WiFi.RSSI());
Serial1.print(" ");
vfdmvcursor(20);
Serial1.print(" Volts: ");
//Serial1.print(WiFi.RSSI());
Serial1.print(analog1, 4);
Serial1.print(" ");
Serial.print("ana");
Serial.print(analog1raw, 0);
Serial.print(" ");
Serial.print(analog2, 0);
Serial.print(" ");
Serial.print(analog3, 0);
Serial.print(" ");
Serial.print(analog4, 0);
Serial.print(" ");
Serial.print(analog5, 0);
Serial.print(" ");
Serial.print(analog6, 0);
Serial.print(" ");
Serial.print(analog7, 0);
Serial.print(" ");
Serial.println(analog8, 0);
}
void vfdnocursor() {
Serial1.write(vfdnocursorconst);
}
void vfdhome() {
Serial1.write(vfdhomeconst);
}
void vfderase() {
Serial1.write(vfderaseconst);
delay(2);
}
void vfdmvcursor(int cursorloc) {
Serial1.write(vfdmvcursorconst);
Serial1.write(cursorloc); //move cursor to line 1,0 line 2
delay(1);
}
void vfdwait(int cursorloc, int loops) {
vfdwaitflag = 1;
char* spinner[] = {"-","\\","|","/","-","\\","|","/"};
for (int i=0; i<loops; i++){
if (Spark.connected() == false){
Spark.process();
}
for (int j=0; j<8; j++) {
vfdmvcursor(cursorloc);
Serial1.write(spinner[j]);
/*timeElapsed = 0;
while (timeElapsed< interval){
if (Spark.connected() == false){
Spark.process();
}
}
*/
delay(100);
//if (Spark.connected() == false){
// Spark.process();
//}
}
}
vfdwaitflag = 0;
}
int vfdhandler(String vfdstring){
vfderase();
Serial1.print(vfdstring);
vfdwait(39,5);
return 1;
}
int vfdcontrol(String vfdstring){
int x;
char* bytecmd;
int brightness;
char* charbuffer[60];
brightness = vfdstring.charAt(0) - '0';
if (brightness< 0 || brightness >7) return -1;
delay(2);
//vfdstring.getBytes(charbuffer, vfdstring);
Serial1.write(0x31);
Serial1.write(0xFF);
Serial1.write(brightness);
delay(2);
return 1;
}
/*example json webhook
{
"event": "current_weather",
"url": "http://api.wunderground.com/api/YOUR-KEY-HERE/conditions/q/pws:{{stationid}}.json",
"requestType": "POST",
"headers": null,
"query": null,
"responseTemplate": "{{#response}}{{#current_observation}}{{#estimated}}{{weather}}~{{temp_f}}~{{relative_humidity}}~{{wind_dir}}~{{wind_gust_mph}}~{{dewpoint_f}}~{{/estimated}}{{/current_observation}}{{/response}}",
"responseTopic": "hook-response/current_weather",
"json": null,
"auth": null,
"mydevices": true
}
*/
/*
00h Null 4.6 Miscellaneous Codes
01h Prepare to Read Display Identification 4.4 Prepare to Read Codes
02h Prepare to Read Software Check sum 4.4 Prepare to Read Codes
03h Prepare to Read Cursor Location 4.4 Prepare to Read Codes
04h Prepare to Read Data at Cursor Location 4.4 Prepare to Read Codes
05h Prepare to Read Data at Cursor Location and Increment 4.4 Prepare to Read Codes
06h Unassigned
07h Bell/Alarm Output 4.6 Miscellaneous Codes
08h Backspace Cursor 4.2 Cursor Control Codes
09h Advance Cursor 4.2 Cursor Control Codes
0Ah Line Feed 4.2 Cursor Control Codes
0Bh Blink Block Cursor 4.2 Cursor Control Codes *
0Ch Underbar Cursor 4.2 Cursor Control Codes *
0Dh Carriage Return 4.2 Cursor Control Codes
0Eh Cursor Off 4.2 Cursor Control Codes
0Fh Cursor On 4.2 Cursor Control Codes
10h Scroll Line Lock 4.6 Miscellaneous Codes
11h Set Vertical Scroll Mode 4.3 Data Display Codes
12h Unassigned
13h Set Horizontal Scroll Mode 4.3 Data Display Codes
14h Software Reset 4.6 Miscellaneous Codes
15h Clear Display and Home Cursor 4.2 Cursor Control Codes
16h Home Cursor 4.2 Cursor Control Codes
17h Set Data Bit 7 High 4.6 Miscellaneous Codes
18h Begin User Defined Character 4.3 Data Display Codes
19h Set Address Bit 0 High 4.6 Miscellaneous Codes
1Ah Cursor up One Line 4.2 Cursor Control Codes
1Bh Move Cursor to Designated Location 4.2 Cursor Control Codes
1Ch Select European Character Set 4.3 Data Display Codes
1Dh Select Katakana Character Set 4.3 Data Display Codes
1Eh Select Cyrillic 4.3 Data Display Codes
1Fh Select Hebrew Character Set 4.3 Data Display Codes
30h Set Display Screen or Column Brightness Level 4.5 Screen Control Codes
31h Begin Blinking Character(s) 4.5 Screen Control Codes
32h End Blinking Character(s) 4.5 Screen Control Codes
33h Blank Display Screen 4.5 Screen Control Codes
34h Unblank Display Screen 4.5 Screen Control Codes
+35h Comma/Period/Triangle Function 4.5 Screen Control Codes
36h Erase Line data With End Blink 4.5 Screen Control Codes
37h Set Carriage Return and Line Feed Definitions 4.5 Screen Control Codes
38h Underbar On 4.2 Cursor Control Codes *
39h Underbar Off 4.2 Cursor Control Codes *
3Ah Select Right to Left Data Entry 4.5 Screen Control Codes
3Bh Select Left to Right Data Entry 4.5 Screen Control Codes
3Ch Screen Saver On 4.5 Screen Control Codes
3Dh Screen Saver Off 4.5 Screen Control Codes
3Eh Execute Self–test 4.6 Miscellaneous Codes
3Fh Terminate Self–test 4.6 Miscellaneous Codes
*/
{
"event": "current_weather",
"url": "http://api.wunderground.com/api/YOUR-API-KEY-HERE/conditions/q/pws:{{stationid}}.json",
"requestType": "POST",
"headers": null,
"query": null,
"responseTemplate": "{{#response}}{{#current_observation}}{{#estimated}}{{weather}}~{{temp_f}}~{{relative_humidity}}~{{wind_dir}}~{{wind_gust_mph}}~{{dewpoint_f}}~{{/estimated}}{{/current_observation}}{{/response}}",
"responseTopic": "hook-response/current_weather",
"json": null,
"auth": null,
"mydevices": true
}
Comments