tuangt
Published © GPL3+

Social Media Without the Internet

What'd it be to interact with people using 'social media features' in real life? There's a wearable that explores exactly that!!!

ExpertShowcase (no instructions)4,794
Social Media Without the Internet

Things used in this project

Hardware components

Arduino Leonardo
Arduino Leonardo
×1
Raspberry Pi 3 Model B
Raspberry Pi 3 Model B
×1
Adafruit NeoPixel Digital RGB LED Strip 144 LED, 1m White
Adafruit NeoPixel Digital RGB LED Strip 144 LED, 1m White
×2
Standard LCD - 16x2 White on Blue
Adafruit Standard LCD - 16x2 White on Blue
×1
mcp3008 8-channel 10-bit ADC with SPI Interface
×1
74AHCT125 Quad Level-Shifter
×1
Force-sensitive Resistor
×1
Pressure-sensitive conductive sheet (Velostat/Linqstat)
×1
7-inch touch screen monitor
×1
Tiny Nanny Cam
×3
Sewable Conductive Thread
Sewable Conductive Thread
×2
Lipo battery 7.4V 5200mAh
×1
NiMH Battery 7.2V 3000mAh
×1

Software apps and online services

Arduino IDE
Arduino IDE
Processing
The Processing Foundation Processing
Raspbian
Raspberry Pi Raspbian

Hand tools and fabrication machines

Sewing Machine
Bantam Tools Desktop PCB Milling Machine
Bantam Tools Desktop PCB Milling Machine

Story

Read more

Schematics

Visual Diagram for connecting parts

It's a Fritzing flie to see all parts using in this program. But the PDB board does not really work with aesthetic design so for printing on CNC machine use a file called Z3_pcb only.

For cutting PCB

This file is exactly what the designed pcb on the jacket but the visual plan on Fritzing does not show all parts.. Use this with your machine.

Code

For Raspberry Pi

Python
Code for a FSR sensor (Following feature) to Adafruit Neopixel and 16x2 LCD, and a micro switch (Follower feature) to 16x2 LCD. Run with Rpi terminal
#!/usr/bin/python
# Example using a character LCD connected to a Raspberry Pi or BeagleBone Black.
import time
import Adafruit_CharLCD as LCD
from ScrollLCD import*
#-----#
import Adafruit_GPIO.SPI as SPI
import MCP3008
#-----#
from neopixel import *
import argparse
import signal
import sys

# Raspberry Pi pin configuration:
# Note this might need to be changed to 21 for older revision Pi's.
lcd_rs        = 27 #26
lcd_en        = 22 #19
lcd_d4        = 25 #13
lcd_d5        = 24 #6
lcd_d6        = 23 #5
lcd_d7        = 17 #11
lcd_backlight = 4

# Define LCD column and row size for 16x2 LCD.
lcd_columns = 16
lcd_rows    = 2
#Hardware SPI configuration:
SPI_PORT   = 0
SPI_DEVICE = 0
mcp = MCP3008.MCP3008(spi=SPI.SpiDev(SPI_PORT, SPI_DEVICE))

# Initialize the LCD using the pins above.
lcd = LCD.Adafruit_CharLCD(lcd_rs, lcd_en, lcd_d4, lcd_d5, lcd_d6, lcd_d7,
                           lcd_columns, lcd_rows, lcd_backlight)

def signal_handler(signal, frame):
        colorWipe(strip, Color(0,0,0))
        sys.exit(0)

def opt_parse():
        parser = argparse.ArgumentParser()
        parser.add_argument('-c', action='store_true', help='clear the display on exit')
        args = parser.parse_args()
        if args.c:
                signal.signal(signal.SIGINT, signal_handler)

def wheel(pos):
        """Generate rainbow colors across 0-255 positions."""
        if pos < 85:
                return Color(pos * 3, 255 - pos * 3, 0)
        elif pos < 170:
                pos -= 85
                return Color(255 - pos * 3, 0, pos * 3)
        else:
                pos -= 170
                return Color(0, pos * 3, 255 - pos * 3)

# LED strip configuration:
LED_COUNT      = 48      # Number of LED pixels.
LED_PIN        = 18      # GPIO pin connected to the pixels (18 uses PWM!).
#LED_PIN        = 10      # GPIO pin connected to the pixels (10 uses SPI /dev/spidev0.0).
LED_FREQ_HZ    = 800000  # LED signal frequency in hertz (usually 800khz)
LED_DMA        = 10      # DMA channel to use for generating signal (try 10)
LED_BRIGHTNESS = 255     # Set to 0 for darkest and 255 for brightest
LED_INVERT     = False   # True to invert the signal (when using NPN transistor level shift)
LED_CHANNEL    = 0       # set to '1' for GPIOs 13, 19, 41, 45 or 53
LED_STRIP      = ws.WS2811_STRIP_GRB   # Strip type and colour ordering
#LED_STRIP      = ws.WS2812_STRIP

LED_i =0
light = True
aftertouch=False
count=0
aftertap=False
following=0

def tuang(strip,wait_ms=20,iterations=1):
        """Draw 1 pixel at a time."""
        for j in range(1):
                print(j)
                for i in range(strip.numPixels()):
                        strip.setPixelColor(strip.numPixels()-i, wheel((int(i * 256 / strip.numPixels())+j) & 255))
                        strip.show()
                        time.sleep(0.05)
                        # strip.show()
                        # time.sleep(0.05)
                for i in range(strip.numPixels()):
                        strip.setPixelColor(strip.numPixels()-i, 0)
                        strip.show()
                        time.sleep(wait_ms/1000.0)
                        LED_i = i
                        print(LED_i)

#text="Hi! I'm Tuang.  I do cool things."
#breaker=False
#n = 16
#rows = [text[i:i+n] for i in range(0, len(text), n)]
#n_rows = len(rows)
#scroll(lcd,text,False,False,1)

print('Reading MCP3008 values, press Ctrl-C to quit...')
# Print nice channel column headers.
print('| {0:>4} | {1:>4} | {2:>4} | {3:>4} | {4:>4} | {5:>4} | {6:>4} | {7:>4} |'.format(*range(8)))
print('-' * 57)

# Main program logic follows:
if __name__ == '__main__':
    #Process arguments
    opt_parse()
    # Create NeoPixel object with appropriate configuration.
    strip = Adafruit_NeoPixel(LED_COUNT, LED_PIN, LED_FREQ_HZ, LED_DMA, LED_INVERT, LED_BRIGHTNESS, LED_CHANNEL, LED_STRIP)
    #Intialize the library (must be called once before other functions)
    strip.begin()
    while True:
        #Read all the ADC channel values in a list.
        values = [0]*8
        for i in range(8):
                #The read_adc function will get the value of the specified channel (0-7).
                values[i] = mcp.read_adc(i)
                #Pause for half a second.
        print('| {0:>4} | {1:>4} | {2:>4} | {3:>4} | {4:>4} | {5:>4} | {6:>4} | {7:>4} |'.format(*values))
        time.sleep(0.2)
        #lcd.clear()
        if values[1]>800:
            if aftertouch:
                    count=count+1
                    lcd.clear()
                    lcd.message('following: '+str(following))
                    lcd.message('\nfollowers: '+str(count)+'     ')
                    #scroll(lcd,text,count,breaker,False,False,0)
                    aftertouch=False
        else:
            aftertouch=True
            #scroll(lcd,text,count,breaker,False,False,0)
            lcd.message('following: '+str(following))
            lcd.message('\nfollowers: '+str(count)+'     ')
        if values[2]>600:
            if aftertap:
                    #print('sholder tap!')
                    following=following+1
                    lcd.clear()
                    lcd.message('following: '+str(following))
                    lcd.message('\nfollowers: '+str(count)+'     ')
                    #scroll(lcd,text,count,breaker,False,False,0)
                    aftertap=False
        else:
            aftertap=True
            #scroll(lcd,text,count,breaker,False,False,0)
            lcd.message('following: '+str(following))
            lcd.message('\nfollowers: '+str(count)+'     ')
        if values[0]>600:
            print("Hi5!")
            light=True
        else:
            light=False
        if light:
            tuang(strip)
            light=False
        else:
            for i in range(strip.numPixels()):
                strip.setPixelColor(i,0)
            strip.show()
        #for x in range(n_rows):
         #       lcd.home()
          #      lcd.clear()
           #     nxt = x + 1
            #    lcd.message(rows[x])
             #   lcd.message('\nFollower:'+str(count)+'     ') #space 3 times to avoid any char on the first row drops
              #  if nxt == n_rows:
               #         sleep(2)
                        #break
                #else:
                 #       lcd.message(rows[nxt])
                  #      sleep(1)

For Arduino

Arduino
The file to be complied on Arduino, controlling the "Add Friends" and "Unfriends" features. Input sensors are contacting (conductive) rings, and a push switch. Output: Adafruit Neopixel and two 8x8 DotMatrixs
 #include <LedControl.h>
#include <Adafruit_NeoPixel.h>

#define PIXEL_PIN    6    // Digital IO pin connected to the NeoPixels.
#define PIXEL_COUNT 46

int DIN = 12;
int CS =  11;
int CLK = 10;
int count = 0;
String number = "";
long a = random(50, 255);
char Array[2];
byte one[8] =     {0x18, 0x28, 0x8, 0x8, 0x8, 0x8, 0x8, 0x3e};
byte two[8] =     {0x1c, 0x22, 0x2, 0x2, 0x1c, 0x20, 0x20, 0x3e};
byte three[8] =   {0x1c, 0x22, 0x2, 0x2, 0x1e, 0x2, 0x22, 0x1c};
byte four[8] =    {0xc, 0x14, 0x24, 0x44, 0x7f, 0x4, 0x4, 0x4};
byte five[8] =    {0x7e, 0x40, 0x40, 0x78, 0xe, 0x2, 0x2, 0x7c};
byte six[8] =     {0x3c, 0x42, 0x40, 0x40, 0x7c, 0x42, 0x42, 0x3c};
byte seven[8] =   {0x7e, 0x2, 0x2, 0x4, 0x8, 0x10, 0x10, 0x10};
byte eight[8] =   {0x3c, 0x42, 0x42, 0x3c, 0x66, 0x42, 0x42, 0x3c};
byte nine[8] =    {0x3c, 0x42, 0x42, 0x42, 0x3e, 0x2, 0x42, 0x3c};
byte zero[8] =    {0x3c, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3c};
byte smile[8] =   {0x3C, 0x42, 0xA5, 0x81, 0xA5, 0x99, 0x42, 0x3C};
byte sad[8] =   {0xff, 0x81, 0xa5, 0x81, 0x99, 0xa5, 0x81, 0xff};

bool smile_sw = false;

LedControl lc = LedControl(DIN, CLK, CS, 2); // Pins: DIN,CLK,CS, amount of display connected
Adafruit_NeoPixel strip = Adafruit_NeoPixel(PIXEL_COUNT, PIXEL_PIN, NEO_GRB + NEO_KHZ800);

const int unFriend_pin = 4;     // the number of the pushbutton pin
const int addFriend_pin = 5;  //the number of rings switch
const int ledPin =  13;      // the number of the LED pin
int buttonState = 0;
int ringState = 0;
int oldState = 0;
int oldRingState = 0;
int showType = 0;

void setup() {
  Serial.begin(9600);
  lc.shutdown(0, false);      //The MAX72XX is in power-saving mode on startup
  lc.setIntensity(0, 1);     // Set the brightness to maximum value. scale is 0-15; 15 is the brightest
  lc.clearDisplay(0);         // and clear the display

  lc.shutdown(1, false);      //The MAX72XX is in power-saving mode on startup
  lc.setIntensity(1, 1);     // Set the brightness to maximum value. scale is 0-15; 15 is the brightest
  lc.clearDisplay(1);         // and clear the display

  pinMode(ledPin, OUTPUT);
  // initialize the pushbutton pin as an input:
  pinMode(unFriend_pin, INPUT);
  strip.begin();
  strip.show(); // Initialize all pixels to 'off'

}

void loop() {

  buttonState = digitalRead(unFriend_pin);
  ringState = digitalRead(addFriend_pin);
  Serial.print("unfreinds x ");
  Serial.print(buttonState + " add freinds x ");
  Serial.println(addFriend_pin);
  // Get current button state.
  int newState = digitalRead(unFriend_pin);
  int newRingState = digitalRead(addFriend_pin);
  // Check if state changed from high to low (button press).
  //  if (newState >= 830 && oldState < 830) { //if analogRead
  if (newState == HIGH && oldState == LOW) {
    // Short delay to debounce button.
    delay(20);
    // Check if button is still low after debounce.
    newState = digitalRead(unFriend_pin);

    //    if (newState >=830) {  //if analogRead
    if (newState == HIGH) {
      showType = 10;
      count--;
      if (count <= 0) {
        count = 0;
      }
      printFir_Byte(sad);
      printSec_Byte(sad);
      startShow(showType);
      showNum();

      digitalWrite(ledPin, HIGH);
      delay(250);
    }

  }
  if (newRingState == HIGH && oldRingState == LOW) {
    // Short delay to debounce button.
    delay(20);
    newRingState = digitalRead(addFriend_pin);
    if (newRingState == HIGH) {
      showType++;
      count++;
      if (showType > 9) {
        showType = 0;
      }
      a = random(50, 255);
      Serial.println(a);
      printFir_Byte(smile);
      printSec_Byte(smile);
      startShow(showType);
      showNum();

      digitalWrite(ledPin, HIGH);
      delay(250);
    }

  }

  // Set the last button state to the old state.
  oldState = newState;
  oldRingState = newRingState;
  //  lc.clearDisplay(0);


}

void startShow(int i) {
  switch (i) {
    case 1: theaterChase(strip.Color(a,   0,   0), 50);  // Green
      break;
    case 2: theaterChase(strip.Color(0,   a,   0), 50); // Blue
      break;
    case 3: theaterChase(strip.Color(0,   0,   a), 50);  // light blue
      break;
    case 4: theaterChase(strip.Color(0,  100,   a), 50);  // mint
      break;
    case 5: theaterChase(strip.Color(0,   a,   100), 50);// warm yellow
      break;
    case 6: theaterChase(strip.Color(a,   100,   0), 50); // violet
      break;
    case 7: theaterChase(strip.Color( 100,   a, 100), 50); // christmas green
      break;
    case 8: theaterChase(strip.Color(0,   50,   a), 50); // dark red
      break;
    case 9: theaterChase(strip.Color(50,   a,   0), 50); // super hero blue
      break;
    case 0: theaterChase(strip.Color(a,   0,   50), 50); // bright red
      break;
    case 10: theaterChase(strip.Color(255,   0,   0), 50); // super hero blue
      break;
  }
  Serial.println(i);
  strip.show();
}

void showNum() {
  char Array[2];
  if (count <= 99) {
    dtostrf(count, 2, 0, Array);
  } else {
    count = 0;
    dtostrf(count, 2, 0, Array);
  }
  Serial.print(Array[0]);
  Serial.print(" , ");
  Serial.println(Array[1]);
  if (Array[0] == ' ') {
    printFir_Byte(zero);
  } else if (Array[0] == '1') {
    printFir_Byte(one);
  } else if (Array[0] == '2') {
    printFir_Byte(two);
  } else if (Array[0] == '3') {
    printFir_Byte(three);
  } else if (Array[0] == '4') {
    printFir_Byte(four);
  } else if (Array[0] == '5') {
    printFir_Byte(five);
  } else if (Array[0] == '6') {
    printFir_Byte(six);
  } else if (Array[0] == '7') {
    printFir_Byte(seven);
  } else if (Array[0] == '8') {
    printFir_Byte(eight);
  } else if (Array[0] == '9') {
    printFir_Byte(nine);
  } else if (Array[0] == '0') {
    printFir_Byte(zero);
  }


  if (Array[1] == '1') {
    printSec_Byte(one);
  } else if (Array[1] == '2') {
    printSec_Byte(two);
  } else if (Array[1] == '3') {
    printSec_Byte(three);
  } else if (Array[1] == '4') {
    printSec_Byte(four);
  } else if (Array[1] == '5') {
    printSec_Byte(five);
  } else if (Array[1] == '6') {
    printSec_Byte(six);
  } else if (Array[1] == '7') {
    printSec_Byte(seven);
  } else if (Array[1] == '8') {
    printSec_Byte(eight);
  } else if (Array[1] == '9') {
    printSec_Byte(nine);
  } else if (Array[1] == '0') {
    printSec_Byte(zero);
  }

}

void printFir_Byte(byte fir_char[])
{
  int i = 0;
  for (i = 0; i < 8; i++)
  {
    lc.setRow(0, i, fir_char[i]);
  }
}


void printSec_Byte(byte sec_char [])
{
  int i = 0;
  for (i = 0; i < 8; i++)
  {
    lc.setRow(1, i, sec_char[i]);
  }
}

//Theatre-style crawling lights.
void theaterChase(uint32_t c, uint8_t wait) {
  for (int j = 0; j < 10; j++) { //do 10 cycles of chasing
    for (int q = 0; q < 3; q++) {
      for (int i = 0; i < strip.numPixels(); i = i + 3) {
        strip.setPixelColor(i + q, c);  //turn every third pixel on
      }
      strip.show();
      delay(wait);
      for (int i = 0; i < strip.numPixels(); i = i + 3) {
        strip.setPixelColor(i + q, 0);      //turn every third pixel off

      }
    }
  }
}

// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) {
  WheelPos = 255 - WheelPos;
  if (WheelPos < 85) {
    return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
  }
  if (WheelPos < 170) {
    WheelPos -= 85;
    return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
  }
  WheelPos -= 170;
  return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}

User Interface (Processing)

Processing
Save this file on Raspberry Pi, and run this file using Processing for Raspberry Pi.
You will need to export font "ProcessingSanPro-Semibold-[SIZE].vlw" from the Processing program and put in the folder "data". Export this font in 3 sizes --48,70,250.
import processing.io.*;
MCP3008 adc;
PFont font;
int count=0;
boolean readytoHit=true;
boolean debugging =false;
int scale =2;
color[] colors;
float a, b;
int startTime;
int totalTime=3000;
int currentTime;
boolean glitch=false;
int smile=0, laugh=0, cry=0, kiss=0, confused=0;
void settings() {
  if (debugging) {
    size(800/scale, 480/scale);
  } else {
    fullScreen(0);
  }
  colors = new color [3];
  colors[0] = color(255, 0, 0);
  colors[1] = color(0, 255, 0);
  colors[2] = color(0, 0, 255);
}

void setup() {
  //printArray(SPI.list());
  adc = new MCP3008(SPI.list()[0]);
  font = loadFont("ProcessingSansPro-Semibold-250.vlw");
  textFont(font, 250);
}

void draw() { 
  if (glitch) {
    blendMode(BLEND);
    a = random(100);
    b = random(100, 255);
    background(255);
    fill(b, a, 0);
  } else {
    background(255, 0, 0);
    fill(255);
  }
  textAlign(CENTER, CENTER);
  //text("LIKES: "+count, width/2, height/2);
  countHit();
  graphic();
  timer();
  println(adc.getAnalog(0));
}
void countHit() {
  if (adc.getAnalog(0)<0.75 && adc.getAnalog(0)>0.0) {
    readytoHit=true;
  }

  if (readytoHit) {
    if (adc.getAnalog(0)>=0.75) {
      count++;
      glitch=true;
      readytoHit=false;
    }
  }

  println("hit: "+readytoHit);
  //println("glitch: "+glitch);
  //println("FSR chan 0: "+adc.getAnalog(0));
  delay(200);
}

void graphic() {
  //ProcessingSansPro-Semibold-48.vlw
  pushMatrix();
  translate(0, height);
  rotate(radians(-90));
  textSize(50);
  text("LIKE ME!?", height/2, 50);
  textSize(30);
  text("my likes today:", height/2, 100);
  textSize(200);
  text(count, height/2, width/2-50);
  textSize(35);
  text(" X ", height-60, 150);
  textSize(26);
  text("--------------------------------------------", height/2, width-340);
  text("how?", height/2, width-300);
  textAlign(LEFT);
  text("  ^_^ ", 50, width-250);
  text("  ^D^ ", 50, width-200);
  text(" T_T ", 50, width-150);
  text("  v3v ", 50, width-100);
  text("  *c* ", 50, width-50);
  noFill();
  stroke(255, 255, 255);
  strokeWeight(3);
  ellipse(81, width-260, 60, 45);
  ellipse(81, width-210, 60, 45);
  ellipse(81, width-160, 60, 45);
  ellipse(81, width-110, 60, 45);
  ellipse(81, width-60, 60, 45);
  ellipse(height-60, 150, 80, 65);
  noStroke();
  textAlign(RIGHT);
  text(smile, height-50, width-250);
  text(laugh, height-50, width-200);
  text(cry, height-50, width-150);
  text(kiss, height-50, width-100);
  text(confused, height-50, width-50);
  textAlign(CENTER);
  if (glitch) {
    blendMode(EXCLUSION);
    textSize(50);
    text("i got a like now", (height/2)-5, width/2-50);
    textSize(30);
    text("my likes today:", height/2, 100);
    textSize(200);
    text(count, height/2, (width/2)-3);
    textSize(52);
    text("LIKE ME!", (height/2), width-250);
    text("LIKE ME!", (height/2), width-200);
    text("LIKE ME!", (height/2), width-150);
    text("LIKE ME!", (height/2), width-100);
    text("LIKE ME!", (height/2), width-50);
  } else {
    blendMode(BLEND);
  }
  popMatrix();
}

void timer() {
  currentTime=millis()-startTime;

  if (currentTime>totalTime) {
    glitch = false;
    fill(255);
    startTime = millis();
    //println("*******restart*********");
  }
  // println("time: "+ round(currentTime));
}
int i=0;
void mousePressed() {
  /*text("(^_^)",50,width-250);
   text("(^D^)",50,width-200);
   text("(T_T)",50,width-150);
   text("(V3V)",50,width-100);
   text("(*c*)",50,width-50);*/
  if (mouseY<425 && mouseY>363) {
    if (mouseX >527 && mouseX<557) {
      smile++;
      count++;
      glitch=true;
    } else if (mouseX >577 && mouseX<607) {
      laugh++;
      count++;
      glitch=true;
    } else if (mouseX >627 && mouseX<657) {
      cry++;
      count++;
      glitch=true;
    } else if (mouseX >677 && mouseX<707) {
      kiss++;
      count++;
      glitch=true;
    } else if (mouseX >726 && mouseX<756) {
      confused++;
      count++;
      glitch=true;
    }
  } else if (mouseY < height-50 && mouseY>0) {
    if (mouseX >125&&mouseX<175) {
      count--;
      println("unlike");
    }
  }
  //i++;
  //println(i+" x: "+mouseX+" y: "+mouseY);
}

MCP3008 (Processing)

Processing
put this file in the same folder of the file name User Interface (Processing)
import processing.io.SPI;

// MCP3008 is a Analog-to-Digital converter using SPI
// other than the MCP3001, this has 8 input channels
// datasheet: http://ww1.microchip.com/downloads/en/DeviceDoc/21295d.pdf

class MCP3008 extends SPI {

  MCP3008(String dev) {
    super(dev);
    settings(500000, SPI.MSBFIRST, SPI.MODE0);
  }

  float getAnalog(int channel) {
    if (channel < 0 ||  7 < channel) {
      System.err.println("The channel needs to be from 0 to 7");
      throw new IllegalArgumentException("Unexpected channel");
    }
    byte[] out = { 0, 0, 0 };
    // encode the channel number in the first byte
    out[0] = (byte)(0x18 | channel);
    byte[] in = transfer(out);
    int val = ((in[1] & 0x03) << 8) | (in[2] & 0xff);
    // val is between 0 and 1023
    return val/1023.0;
  }
}

Credits

tuangt

tuangt

0 projects • 4 followers
Interactive Artist creates works to deliver joyfulness with a playful quality and friendly sarcasm.

Comments