What?
I worked on this project with Tim Haynes.
The goal of this project was to build a device that lets you know when your train stop is coming up.
Look!
It all started with the thought that when you’re coming home late, and you’re tired or drunk or both, you can sometimes miss your trainstop. Or if you’re reading a good book or watching a movie or even just having a nap. So, what if you had a device that could A) alert you that your stop was coming up and B) let you know that your stop was here.
How?
Enter… The Device
Right now it’s a simple box with very basic inputs and outputs.
- You turn on the Device. By default, its set to two train stops, because why would you need something like this for only one stop?
- You increment the number of stops by pushing a red on the side. This is also useful in the case of false stop detection.
- When you reach the stop before your stop, a yellow light goes on and stays on until…
- You near your stop where a red light comes on and a buzzer/vibrator goes off.
Do It Yourself
So the setup is pretty simple and the materials are cheap/easy to come by.
- Arduino (or some other microcontroller thingy)
- 2 LEDs
- 1 (or more) (we used a piezo) Buzzer/vibrating motor/loud-annoying-wake-me-up-thing
- 1 accelerometer
- 1 pushbutton switch
- 1 on/off type switch
- power supply for arduino (we used a 9v battery)
- breadboard, wires, wirecutter
- 2x 220 ohm resistors, 1x 10k ohm resistor
- A casing to hold the Device
Circuit Diagram
Code
int analogPin0 = 0; int analogPin1 = 1; int analogPin2 = 2; int analogValue0 = 0; int analogValue1 = 0; int analogValue2 = 0; // outgoing ADC value int wasOn=-1; int y = 0; float distance, distanceo, totaldist, totaldisto; float velocity, speedo, accel, accelo; int d1, d2, d3, d1o, d2o, d3o; int count = 1; float lowend, highend, lowendo, stayold, stayold2; int highcount, lowcount, staycount, mystopcount, countdown; int iteration, iterationo; int switchPin = 7; // digital input pin for a switch int yellowLedPin = 3; // digital output pin for an LED int redLedPin = 6; // digital output pin for an LED int buzzerPin = 8; // digital output pin for a buzzer int switchState = 0; // the state of the switch int stops = 2; // number of stops int buzzerState = 0; // if buzzer is on void setup() { pinMode(switchPin, INPUT); // set the switch pin to be an input pinMode(yellowLedPin, OUTPUT); // set the yellow LED pin to be an output pinMode(redLedPin, OUTPUT); // set the red LED pin to be an output pinMode(buzzerPin, OUTPUT); // set the buzzer pin to be an output // start serial port at 9600 bps: Serial.begin(9600); lowend=highend=accel=velocity=distance=d1=d2=d3=staycount=mystopcount=0; iteration=0; lowendo=totaldisto=accelo=speedo=totaldist=distanceo=d1o=d2o=d3o=iterationo=0; highcount=lowcount=3; stayold=0; stayold2=1; countdown=3; } void loop() { switchState = digitalRead(switchPin); analogValue0 = analogRead(analogPin0); analogValue1 = analogRead(analogPin1); analogValue2 = analogRead(analogPin2); if (switchState == 1) { wasOn=1; } if(switchState == 0 && wasOn == 1) { stops = stops + 1; //Serial.println("HAI! I'm on!"); wasOn=0; } /* Serial.print(analogValue0); Serial.print("*"); Serial.print(analogValue1); Serial.print("*"); Serial.println(analogValue2); */ // pause for 10 milliseconds: delay(10); /* the data is collected every 10 ms, so a count of 100 is 1 second */ if(count==100) { iteration++; /* if 3 data points are high and close enough to each other then a high-end point is recorded */ if(totaldist-300 > lowend) // looking for a number above the lowest //data point (lowend) { if(highcount >=2) // on the 3rd occurence of a high point within // 100 points of itself, see if its ok to say // its a high point { if(totaldisto+100 >= totaldist && totaldisto-100 <= totaldist) { highend=totaldist; } else{ highcount=0;} } else { //else the count is restarted and the search for // consecutive high data points continues if(totaldisto+100>=totaldist && totaldisto-100<=totaldist) { highcount++; } else { highcount=0; } } } /* A cheap hack. Just puts the first data point lower than the first high data point into the low end point. */ if(highend>0 && lowend==0) { if(totaldist 0) { if(totaldist>lowend){highend=totaldist;} } /* if 3 data points in succession are low, then a new low end point is recorded */ if(totaldist+300 < highend)// looking for a number below the high //data point { if(lowcount >=2){ // on the 3rd occurence of a low data point w/in // 100 points of itself, check to see if its ok // to record if(totaldisto+100>=totaldist && totaldisto-100<=totaldist) { lowendo=lowend; lowend=totaldist; } else {lowcount=0;} } else {//else the count is restarted and the search for consecutive // low points resumes if(totaldisto+100>=totaldist && totaldisto-100<=totaldist) { lowcount++; } else { lowcount=0; } } } /* Serial.print("!"); Serial.print("*"); Serial.print(highend,DEC); Serial.print("*"); Serial.print(lowend,DEC); Serial.print("*"); Serial.println(totaldist,DEC); */ /*if the last low end point is close enough to the current low end point, then start saying that maybe we're stopping or have stopped */ if(lowendo+100>=lowend && lowendo-100<=lowend) {staycount++; stayold2=lowendo;}else{staycount=0;} /* if we've been at a low end for quite a while and its not the beginning of the program then check to see if we've been staying at the same place as well and if so then say that we think that we've stopped */ if(staycount>=3 && highend!=0 && lowend!= 0 && stayold!=stayold2) { //Serial.println("@"); stayold=lowendo; stayold2=stayold; staycount=0; if(mystopcount==-1) { //Serial.println("#"); countdown--; stops--; } /* stops--; Serial.print(iteration); Serial.print(" "); Serial.println(stops); */ if (stops == 1) { digitalWrite(yellowLedPin, HIGH); // turn on the yellow LED } if (stops == 0) { digitalWrite(yellowLedPin, LOW); // turn off the yellow LED digitalWrite(redLedPin, HIGH); // turn on the red LED digitalWrite(buzzerPin, HIGH); // turn on the buzzer buzzerState = 1; stops=0; } mystopcount++; iterationo=iteration; } //this is the number we need to play with to finesse things if(iterationo+8 < iteration){mystopcount=-1;} if(iterationo+3 < iteration){ digitalWrite(buzzerPin, LOW); digitalWrite(redLedPin,LOW); } /* reset distances and times */ totaldisto=totaldist; totaldist=0; count=1; speedo=velocity; accelo=accel; } /*below this line is all graph data */ //X y = analogValue0; d1 = y- d1o; d1o = y; //Y y = analogValue1; d2 = y - d2o; d2o = y; //Z y = analogValue2; d3 = y - d3o; d3o = y; //Distance if(d1o != 0 && d2o != 0 && d3o != 0) { distance = sqrt(sq(d1)+sq(d2)+sq(d3)); distanceo= distance; } //Speed (averaged over time (every reading is 1 second)), reset @ STOPs totaldist+=distance; //DONT'T DELETE THIS LINE OR EVERYTHING WILL DIE velocity = totaldist/count; //Accel accel = velocity/count; count++; //OR THIS }
Thoughts, Commnets, Concerns
So this project went along at pretty steady pace. We had a few hiccoughs in the beginning, trying to interpret the accelerometer data in order to extrapolate the speed and then figure out how to detect a "stop" from all of that mess.
Once we got that figured out, it was just a matter of actually riding the train trying to fine tune our settings. The most trouble we had (still have actually) was with detecting false stops. Where the train decelerates while underground or stops for some train delay or another.
We've thought about combining the accelerometer with an audio sensor or something but none of those ideas were really feasible in the amount of time we had. So right now the Device only works for the 4/6 train line and its sometimes a bit shaky at that.