Jazlyn Ortiz
Published

Homelessness Awareness Project

A physical computing and sculpture project that brings awareness to chicagoans about homelessness within the city.

IntermediateShowcase (no instructions)10 hours43
Homelessness Awareness Project

Things used in this project

Hardware components

Photon
Particle Photon
×1
Small Protoboard
×1
DC POWER JACK 2.1MM BARREL-TYPE PCB MOUNT
TaydaElectronics DC POWER JACK 2.1MM BARREL-TYPE PCB MOUNT
×1
Female Header 8 Position 1 Row (0.1")
Female Header 8 Position 1 Row (0.1")
×1
Adafruit NeoPixel Digital RGB LED Strip 144 LED, 1m White
Adafruit NeoPixel Digital RGB LED Strip 144 LED, 1m White
×1
SG90 Micro-servo motor
SG90 Micro-servo motor
×1
360 Continuous Servo Motor
×1
Jumper wires (generic)
Jumper wires (generic)
×1
Male/Female Jumper Wires
Male/Female Jumper Wires
×1
Solid Core Wire
×1
USB A to Micro-B Cable
Digilent USB A to Micro-B Cable
×1
9V 1A Switching Wall Power Supply
9V 1A Switching Wall Power Supply
×1
Linear Actuator
×1

Software apps and online services

Particle Build Web IDE
Particle Build Web IDE

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
Solder Wire, Lead Free
Solder Wire, Lead Free
Wire Stripper & Cutter, 18-10 AWG / 0.75-4mm² Capacity Wires
Wire Stripper & Cutter, 18-10 AWG / 0.75-4mm² Capacity Wires
3D Printer (generic)
3D Printer (generic)

Story

Read more

Custom parts and enclosures

Linear Servo Actuator

I 3D printed the mini version which included the mini motor bracket and the mini pinion gear. I also printed the 100mm long pusher.

Schematics

Photon 2 Pinout & Circuitry

The circuitry and wiring for this project is relatively simple. The 180 degree servo controlling the linear actuator is wired to ground, 5V, and D15 pin. The 360 degree continuous is wired to ground, 5V and D16 pin. The 4 strip NeoPixel LEDs is wired to ground, 5V, and the D2 pin. And the 5V pin is connected to external power.

Code

struggle.ino

C/C++
Program for the project
#include <neopixel.h>

#include "Particle.h"

SYSTEM_MODE(AUTOMATIC);

SerialLogHandler logHandler(LOG_LEVEL_INFO);

// NeoPixel Setup
#if (PLATFORM_ID == 32)
  #define PIXEL_PIN SPI1
#else
  #define PIXEL_PIN D2
#endif

#define PIXEL_COUNT 4
#define PIXEL_TYPE WS2812B

Adafruit_NeoPixel strip(PIXEL_COUNT, PIXEL_PIN, PIXEL_TYPE);

// Servo Setup
Servo fullServo;
Servo halfServo;


String occupancyStatus = "";
int policeDistrict = -1;

bool actionInProgress = false;
unsigned long actionStartTime = 0;
bool movingForward = true;
bool servoIncreasing = true;
int servoPosition = 180;

bool pixelsOn = false;
unsigned long lastFlashTime = 0;
const unsigned long flashInterval = 500;

bool flashRed = true;

void setup() {
  Serial.begin(9600);

  // Initialize NeoPixel
  strip.begin();
  strip.show();

  // Subscribe to the webhook
  Particle.subscribe("hook-response/service_request", handleData);

  // Attach servos 
  fullServo.attach(16);
  halfServo.attach(15);
  

  // Wait to connect
  delay(5000);

  // Publish event to trigger the webhook
  Particle.publish("service_request");
  Serial.println("Published service_request event. Waiting for response...");
}

void loop() {
  if (actionInProgress) {
    unsigned long currentMillis = millis();

    // Check if action duration passed
    if (currentMillis - actionStartTime >= 15000) { // 15 seconds
      stopActions();
      Serial.println("Action finished after 15 seconds.");
    } else {
      // Continue moving servos gradually
      moveServosGradually();

      // Flash the NeoPixels
      if (currentMillis - lastFlashTime >= flashInterval) {
        lastFlashTime = currentMillis;
        if (pixelsOn) {
          turnOffPixels();
        } else {
          if (flashRed) {
            flashRedPixels();
          } else {
            flashWhitePixels();
            }
        }
        pixelsOn = !pixelsOn; // Toggle state
      }
    }
  }
}

// Handle incoming webhook data
void handleData(const char *event, const char *data) {
  Serial.println("Received webhook response!");

  // Convert data to String
  String response = String(data);
  Serial.println("Raw data: " + response);

  int commaIndex = response.indexOf(',');

  if (commaIndex != -1) {
    occupancyStatus = response.substring(0, commaIndex);
    occupancyStatus.trim();

    String districtStr = response.substring(commaIndex + 1);
    districtStr.trim();
    policeDistrict = districtStr.toInt();

    Serial.println("Occupancy Status: " + occupancyStatus);
    Serial.println("Police District: " + String(policeDistrict));

    // Determine flash color
    flashRed = matchesDistrict(policeDistrict);

    if (occupancyStatus == "true") {
      Serial.println("Starting servo and LED actions based on webhook!");
      startActions();
    }
  } else {
    Serial.println("ERROR: Response format is unexpected.");
  }
}

// Start the servo and pixel actions
void startActions() {
  actionInProgress = true;
  actionStartTime = millis();
  servoPosition = 0;
  pixelsOn = false;
  lastFlashTime = millis();
}

// Stop all actions
void stopActions() {
  actionInProgress = false;

  // Turn off pixels
  turnOffPixels();

  // Detach servos to save power
  fullServo.detach();
  halfServo.detach();
}

void moveServosGradually() {
    halfServo.write(servoPosition);
    
    if (servoIncreasing) {
        servoPosition += 2;
        if (servoPosition >= 180) {
            servoIncreasing = false;
        }
    } else {
        servoPosition -= 2;
        if (servoPosition <= 0) {
            servoIncreasing = true;
        }
    }
}

// Flash NeoPixels red
void flashRedPixels() {
  for (int i = 0; i < PIXEL_COUNT; i++) {
    strip.setPixelColor(i, strip.Color(255, 0, 0)); // Red
  }
  strip.show();
}
// Flash NeoPixels white
void flashWhitePixels() {
  for (int i = 0; i < PIXEL_COUNT; i++) {
    strip.setPixelColor(i, strip.Color(255, 255, 255)); // White
  }
  strip.show();
}
// Turn off NeoPixels
void turnOffPixels() {
  for (int i = 0; i < PIXEL_COUNT; i++) {
    strip.setPixelColor(i, strip.Color(0, 0, 0)); // Off
  }
  strip.show();
}

// Check if the police district matches the target list
bool matchesDistrict(int district) {
  return (district == 9 || district == 8 || district == 7 || district == 2 ||
          district == 3 || district == 6 || district == 22 || district == 5 ||
          district == 4 || district == 12 || district == 1);
}

Credits

Jazlyn Ortiz
2 projects • 0 followers

Comments