Skip to content

Instantly share code, notes, and snippets.

@oveddan
Last active February 14, 2022 15:10
Show Gist options
  • Save oveddan/4fddf79bc2a030d2bfaeab27a04242e8 to your computer and use it in GitHub Desktop.
Save oveddan/4fddf79bc2a030d2bfaeab27a04242e8 to your computer and use it in GitHub Desktop.
Japanese Weather Diorama Arduino and Node.js code - oveddan.github.io/blog/posts/physical_computing/weather-diorama/
const SerialPort = require('serialport');
const request = require('request');
const myKey = '12312314';
const moment = require('moment')
const Readline = SerialPort.parsers.Readline;
const thirtyMinutesInSeconds = 60 * 60
const SUNRISE = 0
const SUNSET = 1
const MIDDAY = 2
const NIGHTTIME = 3
const getTimeOfDayCode = weatherResult => {
const { dt, sys: { sunset, sunrise } } = weatherResult
const currentTime = moment(dt * 1000)
const sunriseTime = moment(sunrise * 1000)
const sunsetTime = moment(sunset * 1000)
if (currentTime.isBefore(sunriseTime.subtract(30, 'minutes')))
return NIGHTTIME
if (currentTime.isBefore(sunriseTime.add(30, 'minutes')))
return SUNRISE
if (currentTime.isBefore(sunsetTime.subtract(30, 'minutes')))
return MIDDAY
if (currentTime.isBefore(sunsetTime.add(30, 'minutes')))
return SUNSET
return NIGHTTIME
}
const getWeather = weatherResult => weatherResult.weather[0].main.toLowerCase()
const CLEARSKIES = 0
const OVERCAST = 1
const RAIN = 2
const getWeatherCode = weatherResult => {
switch(getWeather(weatherResult)) {
case "smoke":
case "fog":
case "cloudy":
return OVERCAST
case "rain":
case "snow":
return RAIN
default:
return CLEARSKIES
}
}
SerialPort.list((err, ports) => {
const lastPort = ports[ports.length - 1];
var port = new SerialPort(lastPort.comName, {
baudRate: 9600
});
port.on('error', function(err) {
console.log('Error: ', err.message);
})
const parser = new Readline();
// Switches the port into "flowing mode"
port.pipe(parser);
parser.on('data', data => {
console.log('got data:', data);
if (data === 'getWeather\r') {
// request('http://api.openweathermap.org/data/2.5/weather?id=5128581&appid=85bbe8e9824e89a038ea3aa9ab31a0eb', (error, response, body) => {
request('http://api.openweathermap.org/data/2.5/weather?id=1864134&appid=85bbe8e9824e89a038ea3aa9ab31a0eb', (error, response, body) => {
const weatherResult = JSON.parse(body);
const timeOfDayCode = getTimeOfDayCode(weatherResult)
const weatherCode = getWeatherCode(weatherResult)
console.log('result:', timeOfDayCode, weatherCode)
port.write(`${weatherCode},${timeOfDayCode}\r`);
})
}
});
})
{
"name": "weather",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"moment": "^2.19.1",
"request": "^2.83.0",
"serialport": "^6.0.3"
}
}
#include "FastLED.h"
#define NUM_LEDS 60
#define TIME_BUTTON_PIN 3
#define WEATHER_BUTTON_PIN 2
#define BUTTON_PINC 4
#define DATA_PIN 6
#define LATCH_PIN 8
//Pin connected to SH_CP of 74HC595
#define CLOCK_PIN 12
////Pin connected to DS of 74HC595
#define SHIFT_DATA_PIN 11
CRGB leds[NUM_LEDS];
CRGB lastLeds[NUM_LEDS];
CRGB targetLeds[NUM_LEDS];
void setup() {
FastLED.addLeds<WS2812, 6, GRB>(leds, NUM_LEDS);
FastLED.setBrightness(255);
pinMode(TIME_BUTTON_PIN, INPUT);
pinMode(WEATHER_BUTTON_PIN, INPUT);
pinMode(BUTTON_PINC, INPUT);
Serial.begin(9600);
for(int i = 0; i < NUM_LEDS; i++) {
leds[i] = CRGB::Red;
lastLeds[i] = CRGB::Red;
targetLeds[i] = CRGB::Red;
}
pinMode(LATCH_PIN, OUTPUT);
pinMode(CLOCK_PIN, OUTPUT);
pinMode(SHIFT_DATA_PIN, OUTPUT);
}
int lightState = 0;
int lastButtonAState = LOW;
int lastButtonBState = LOW;
int lastButtonCState = LOW;
int maximumState = 1;
void setLed(int ledIndex, CRGB targetColor) {
targetLeds[ledIndex] = CRGB(0, 0, 255);
}
long lastChange = millis();
void setLastLeds() {
for(int i = 0; i < NUM_LEDS; i++) {
lastLeds[i] = leds[i];
}
}
#define SUNRISE 0
#define MIDDAY 1
#define SUNSET 2
#define NIGHTTIME 3
int timeOfDay = SUNRISE;
#define CLEARSKIES 0
#define OVERCAST 1
#define RAIN 2
int weather = CLEARSKIES;
int layerLeds[][2] = {{0, 9}, {10, 19}, {20, 29}, {30, 39}, {40, 49}, {50, 59}, {60, 60}};
#define SUN 5
#define MOUNTAIN1 0
#define MOUNTAIN2 1
#define MOUNTAIN3 2
#define MOUNTAIN4 3
#define SKY 4
CRGB mountain1Color = CRGB::White;
CRGB mountain2Color = CRGB::Chartreuse;//;// CRGB(28, 148, 173);
CRGB mountain3Color = CRGB(90, 166, 181);
CRGB mountain4Color = CRGB::Coral;//CRGB(228, 176, 168);
CRGB skyColor = CRGB::DeepSkyBlue;
CRGB sunColor = CRGB(249, 171, 136);
CRGB fadeColorByWeather(CRGB color, int weather) {
switch(weather) {
case OVERCAST:
return color.fadeLightBy(255/2);
case RAIN:
return color.fadeLightBy(255*3/4);
default:
return color;
}
}
CRGB paintColorByTimeOfDay(CRGB color, int timeOfDay) {
switch(timeOfDay) {
case SUNSET:
return CRGB::Red;
case SUNRISE:
return CRGB::OrangeRed;
case NIGHTTIME:
return CRGB::Blue;
default:
return color;
}
}
CRGB getLayerColor(int layer, int timeOfDay, int weather) {
//Serial.println(layer);
if(layer == SUN) {
CRGB color;
switch(timeOfDay) {
case SUNRISE:
color = CRGB::Orange;
case MIDDAY:
color = CRGB::Yellow;
case SUNSET:
color = CRGB::Yellow;
default:
color = CRGB::MintCream;
break;
}
color.fadeLightBy(200);
return color;
} else if(layer == MOUNTAIN1) {
CRGB color = mountain1Color;
color = paintColorByTimeOfDay(color, timeOfDay);
color = fadeColorByWeather(color, weather);
return color;
} else if(layer == MOUNTAIN2) {
if (timeOfDay == NIGHTTIME)
return CRGB::Black;
CRGB color = mountain2Color;
color = paintColorByTimeOfDay(color, timeOfDay);
color = fadeColorByWeather(color, weather);
return color;
} else if(layer == MOUNTAIN3) {
if (timeOfDay == NIGHTTIME)
return CRGB::Black;
CRGB color = mountain3Color;
color = paintColorByTimeOfDay(color, timeOfDay);
color = fadeColorByWeather(color, weather);
return color;
} else if(layer == MOUNTAIN4) {
if (timeOfDay == NIGHTTIME)
return CRGB::Black;
CRGB color = mountain4Color;
color = paintColorByTimeOfDay(color, timeOfDay);
color = fadeColorByWeather(color, weather);
return color;
} else if (layer == SKY) {
CRGB color;
switch(timeOfDay) {
case SUNRISE:
return CRGB::DeepPink;
case MIDDAY:
color = CRGB::SkyBlue;
color.fadeLightBy(100);
return color;
case SUNSET:
return CRGB::Red;
default:
return CRGB::LightGoldenrodYellow;
}
}
else {
return CRGB::White;
}
}
void setConditions(int timeOfDay, int weather) {
setLastLeds();
lastChange = millis();
byte statusData = 0;
bitSet(statusData, weather);
bitSet(statusData, timeOfDay + 3);
digitalWrite(LATCH_PIN, LOW);
// shift out the bits:
shiftOut(SHIFT_DATA_PIN, CLOCK_PIN, MSBFIRST, statusData);
//take the latch pin high so the LEDs will light up:
digitalWrite(LATCH_PIN, HIGH);
for(int i = 0; i <= 5; i++) {
CRGB color = getLayerColor(i, timeOfDay, weather);
for(int j = layerLeds[i][0]; j <= layerLeds[i][1]; j++) {
targetLeds[j] = color;
}
}
}
#define TRANSITION_DURATION 1000.0
void advanceAnimation() {
float percentage = constrain((millis() - lastChange * 1.) / TRANSITION_DURATION, 0., 0.9999);
fract8 fract = floor(percentage * 256);
if (fract <= 256) {
for(int i = 0; i < NUM_LEDS; i++) {
leds[i] = blend(lastLeds[i], targetLeds[i], fract);
}
}
if (timeOfDay == NIGHTTIME) {
animateNightStars();
}
FastLED.show();
delay(20);
}
#define SPARKLE_DURATION 1000
long lastStarChange = millis();
void animateNightStars() {
int firstSkyLed = layerLeds[MOUNTAIN3][0];
int lastSkyLed = layerLeds[SKY][1];
if(millis() - lastStarChange > SPARKLE_DURATION) {
Serial.println("sparkling");
lastStarChange = millis();
for(int i = firstSkyLed; i <= lastSkyLed; i++) {
lastLeds[i] = leds[i];
targetLeds[i] = CRGB::Blue;
}
for(int i = 0; i < 10; i++) {
int randomStarOn = random(firstSkyLed, lastSkyLed + 1);
Serial.print(randomStarOn);
Serial.print(" ");
targetLeds[randomStarOn] = CRGB::White;
targetLeds[randomStarOn].fadeLightBy(200);
}
Serial.println("");
lastStarChange = millis();
}
float percentage = constrain((millis() - lastStarChange * 1.) / SPARKLE_DURATION, 0., 0.9999);
fract8 fract = floor(percentage * 256);
for(int i = firstSkyLed; i <= lastSkyLed; i++) {
leds[i] = blend(lastLeds[i], targetLeds[i], fract);
}
}
void loop() {
// read the state of the pushbutton value:
int currentButtonAState = digitalRead(TIME_BUTTON_PIN);
int currentButtonBState = digitalRead(WEATHER_BUTTON_PIN);
int currentButtonCState = digitalRead(BUTTON_PINC);
if (currentButtonAState == HIGH && lastButtonAState == LOW) {
timeOfDay++;
if (timeOfDay >NIGHTTIME)
timeOfDay = 0;
Serial.print("time of day: ");
Serial.println(timeOfDay);
setConditions(timeOfDay, weather);
// for(int i = 0; i < NUM_LEDS; i++) {
// targetLeds[i] = CRGB::Green;
// }
}
if (currentButtonBState == HIGH && lastButtonBState == LOW) {
weather++;
if (weather > RAIN)
weather = 0;
Serial.print("weather: ");
Serial.println(weather);
setConditions(timeOfDay, weather);
// for(int i = 0; i < NUM_LEDS; i++) {
// targetLeds[i] = CRGB::Blue;
// }
}
if (currentButtonCState == HIGH && lastButtonCState == LOW) {
Serial.println("getWeather");
}
lastButtonAState = currentButtonAState;
lastButtonBState = currentButtonBState;
lastButtonCState = currentButtonCState;
advanceAnimation();
if (Serial.available() > 0) {
// read the incoming byte:
String incomingMessage = Serial.readString();
int commaIndex = incomingMessage.indexOf(',');
timeOfDay = incomingMessage.substring(0, commaIndex).toInt();
weather = incomingMessage.substring(commaIndex + 1, incomingMessage.length()).toInt();
setConditions(timeOfDay, weather);
Serial.print("weather received: ");
Serial.print(timeOfDay);
Serial.print(" ");
Serial.println(weather);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment