Group 4: LumiTouch Frames to How's the Weather
Made by Freya Yang and Isabel Fleck
Made by Freya Yang and Isabel Fleck
Created: December 11th, 2024
This project draws inspiration from historical emotional communication devices like the LoveBox or the Tactile Internet, which use haptic and visual elements to convey feelings. Drawing from theories of affective computing and emotional design (Norman, 2004), this project emphasizes subtlety and intimacy in interaction. The bird design was inspired by symbolic connections to nature and harmony, aligning with the Lumi Touch's objective to create gentle, non-intrusive communication. We reference prior works on embodied interaction (Dourish, 2001) to underline the importance of physical touch in human connection.
The key goal was to rebuild the Lumi Touch frame as an IoT-enabled emotional interface. The focus was on reviving tactile and visual feedback mechanisms to communicate across physical distances seamlessly.
Input: User squeezes the bird
Processing: The signal is sent via the Particle Photon
[Firgue 1: First Attempt, using LED as the first prototype demo]
[Figure 2: Second Attempt, using the NeoPixel LED Strip]
[Insert Code here]
// This #include statement was automatically added by the Particle IDE.
#include <neopixel.h>
#include "Particle.h"
#include "neopixel.h"
SYSTEM_MODE(AUTOMATIC);
// Define pin and NeoPixel configuration for Photon 2
#if (PLATFORM_ID == 32)
#define NEOPIXEL_PIN SPI // Use MOSI for Photon 2
#else
#define NEOPIXEL_PIN D3 // Use D3 for other Particle devices
#endif
#define NUM_PIXELS 8 // Number of LEDs in the NeoPixel strip
#define PIXEL_TYPE WS2812B // Specify the NeoPixel type
// Pressure sensor pin
#define PRESSURE_PIN A0
// Create a NeoPixel object
Adafruit_NeoPixel strip(NUM_PIXELS, NEOPIXEL_PIN, PIXEL_TYPE);
void setup() {
strip.begin(); // Initialize NeoPixel strip
strip.show(); // Turn off all LEDs initially
Serial.begin(9600); // Debugging
// Subscribe to events from the Particle Cloud
Particle.subscribe("weatherEvent", weatherEventHandler);
}
// Function prototypes for weather effects
void thunderEffect();
void rainEffect();
void cloudyEffect();
void sunnyEffect();
// Function to publish weather events
void publishWeatherEvent(const char* eventName) {
String event = String("weatherEvent/") + eventName;
Particle.publish(event); // Publish the event with the prefix
weatherEventHandler(event.c_str(), ""); // Call event handler immediately
}
// Particle event handler
void weatherEventHandler(const char* event, const char* data) {
String eventString = String(event);
if (eventString == "weatherEvent/sunny") {
sunnyEffect(); // Call sunny effect function
}
else if (eventString == "weatherEvent/cloudy") {
cloudyEffect(); // Call cloudy effect function
}
else if (eventString == "weatherEvent/rainy") {
rainEffect(); // Call rainy effect function
}
else if (eventString == "weatherEvent/thunder") {
thunderEffect(); // Call thunder effect function
}
}
void loop() {
// Read the pressure sensor value
int pressure = analogRead(PRESSURE_PIN);
Serial.println(pressure); // Debugging the sensor value
// Check how long the pressure is held
static unsigned long pressStartTime = 0;
static bool isPressed = false;
if (pressure > 1000) { // Threshold for detecting a press
if (!isPressed) {
pressStartTime = millis(); // Start timing
isPressed = true;
}
} else {
isPressed = false;
}
if (isPressed) {
unsigned long pressDuration = millis() - pressStartTime;
if (pressDuration > 5000) {
publishWeatherEvent("thunder");
} else if (pressDuration > 3000) {
publishWeatherEvent("rainy");
} else if (pressDuration > 2000) {
publishWeatherEvent("cloudy");
} else if (pressDuration > 1000) {
publishWeatherEvent("sunny");
}
} else {
strip.clear(); // Turn off LEDs when not pressed
strip.show();
}
}
// Thunder: Blue and yellow flash
void thunderEffect() {
for (int i = 0; i < NUM_PIXELS; i++) {
strip.clear();
if (i % 2 == 0) {
strip.setPixelColor(i, strip.Color(0, 0, 128)); // Dim blue
} else {
strip.setPixelColor(i, strip.Color(128, 128, 0)); // Dim yellow
}
strip.show();
delay(50);
}
strip.clear();
strip.show();
}
// Rain: Light dim blue light
void rainEffect() {
for (int i = 0; i < NUM_PIXELS; i++) {
strip.clear();
strip.setPixelColor(i, strip.Color(0, 0, 128)); // Dim blue
strip.show();
delay(100);
}
}
// Cloudy: Dim violet/lavender light
void cloudyEffect() {
static int brightness = 0;
static int direction = 5;
brightness += direction;
if (brightness >= 100 || brightness <= 0) {
direction = -direction;
}
uint32_t color = strip.Color(75 + brightness, 50 + brightness, 150 + brightness); // Dim lavender/violet
for (int i = 0; i < NUM_PIXELS; i++) {
strip.setPixelColor(i, color);
}
strip.show();
delay(50);
}
// Sunny: Warm orange light
void sunnyEffect() {
static int brightness = 0;
static int direction = 5;
brightness += direction;
if (brightness >= 255 || brightness <= 50) {
direction = -direction;
}
uint32_t color = strip.Color(255, 140, 0); // Warm orange
for (int i = 0; i < NUM_PIXELS; i++) {
strip.setPixelColor(i, color);
}
strip.show();
delay(50);
}
Click to Expand
A paragraph of approach description here.
A paragraph of approach description here.
An Interactive IoT device that allows you to check in with a friend about their current mood and how they are feeling, and tell them how you are doing.
[Figure 3: How's weather Design Concept Storyboard]
Bills of Materials:
Workflow Diagram:
Circuit Diagram:
Code:
[Insert code description here
[Insert code below]
#include "Particle.h"
#include "neopixel.h"
SYSTEM_MODE(AUTOMATIC);
// Define pin and NeoPixel configuration for Photon 2
#if (PLATFORM_ID == 32)
#define NEOPIXEL_PIN SPI // Use MOSI for Photon 2
#else
#define NEOPIXEL_PIN D3 // Use D3 for other Particle devices
#endif
#define NUM_PIXELS 8 // Number of LEDs in the NeoPixel strip
#define PIXEL_TYPE WS2812B // Specify the NeoPixel type
// Pressure sensor pin
#define PRESSURE_PIN A0
// Create a NeoPixel object
Adafruit_NeoPixel strip(NUM_PIXELS, NEOPIXEL_PIN, PIXEL_TYPE);
void setup() {
strip.begin(); // Initialize NeoPixel strip
strip.show(); // Turn off all LEDs initially
Serial.begin(9600); // Debugging
// Subscribe to events from the Particle Cloud
Particle.subscribe("weatherEvent", weatherEventHandler);
}
// Function prototypes for weather effects
void thunderEffect();
void rainEffect();
void cloudyEffect();
void sunnyEffect();
// Function to publish weather events
void publishWeatherEvent(const char* eventName) {
String event = String("weatherEvent/") + eventName;
Particle.publish(event); // Publish the event with the prefix
weatherEventHandler(event.c_str(), ""); // Call event handler immediately
}
// Particle event handler
void weatherEventHandler(const char* event, const char* data) {
String eventString = String(event);
if (eventString == "weatherEvent/sunny") {
sunnyEffect(); // Call sunny effect function
}
else if (eventString == "weatherEvent/cloudy") {
cloudyEffect(); // Call cloudy effect function
}
else if (eventString == "weatherEvent/rainy") {
rainEffect(); // Call rainy effect function
}
else if (eventString == "weatherEvent/thunder") {
thunderEffect(); // Call thunder effect function
}
}
void loop() {
// Read the pressure sensor value
int pressure = analogRead(PRESSURE_PIN);
Serial.println(pressure); // Debugging the sensor value
// Determine weather event based on pressure level
if (pressure > 3600) {
publishWeatherEvent("thunder");
} else if (pressure > 2700) {
publishWeatherEvent("rainy");
} else if (pressure > 1800) {
publishWeatherEvent("cloudy");
} else if (pressure > 900) {
publishWeatherEvent("sunny");
} else {
strip.clear(); // Turn off LEDs when pressure is too low
strip.show();
}
}
// Thunder: Blue and yellow flash
void thunderEffect() {
for (int i = 0; i < NUM_PIXELS; i++) {
strip.clear();
if (i % 2 == 0) {
strip.setPixelColor(i, strip.Color(0, 0, 128)); // Dim blue
} else {
strip.setPixelColor(i, strip.Color(128, 128, 0)); // Dim yellow
}
strip.show();
delay(50);
}
strip.clear();
strip.show();
}
// Rain: Light dim blue light
void rainEffect() {
for (int i = 0; i < NUM_PIXELS; i++) {
strip.clear();
strip.setPixelColor(i, strip.Color(0, 0, 128)); // Dim blue
strip.show();
delay(100);
}
}
// Cloudy: Dim violet/lavender light
void cloudyEffect() {
static int brightness = 0;
static int direction = 5;
brightness += direction;
if (brightness >= 100 || brightness <= 0) {
direction = -direction;
}
uint32_t color = strip.Color(75 + brightness, 50 + brightness, 150 + brightness); // Dim lavender/violet
for (int i = 0; i < NUM_PIXELS; i++) {
strip.setPixelColor(i, color);
}
strip.show();
delay(50);
}
// Sunny: Warm orange light
void sunnyEffect() {
static int brightness = 0;
static int direction = 5;
brightness += direction;
if (brightness >= 255 || brightness <= 50) {
direction = -direction;
}
uint32_t color = strip.Color(255, 140, 0); // Warm orange
for (int i = 0; i < NUM_PIXELS; i++) {
strip.setPixelColor(i, color);
}
strip.show();
delay(50);
}
Click to Expand