Hack Your Own Public Transportation Timetable
by seliasson in Circuits > Gadgets
12263 Views, 184 Favorites, 0 Comments
Hack Your Own Public Transportation Timetable
I like my bike. And whenever I can take it to reach my destination - I'll do it. But sometimes I need to take the public transportation and I live quite central in munich. There are a bunch of lines right in front of my place and I cant remember when I can take witch line. And EVERYTIME I left to take a tram - the tram is passing by without me. And I could see on those fancy digital time tables, that I have to wait. I wished so bad to have one of those at home - so I build one. It shows me the 10 next departures including delays!
I used a Intel Edison with a I2C Grove RGB LCD - and that's all ;). Easy to build - great to use! I used Adurino code, so make sure you installed it according to the official guides.
I will show you the following step to build your own TransporTable:
1) How to get the data you need?
2) How to make the Edison do that?
3) How to build the stuff?
Ok! Lets get it started..
Start With the Website of Your Local Transportation Company
First of all - you need the Information witch line departures at what time. It can take quite a lot of time to write down all departures for a station close to you. And even if you do that - the time table might change in future. Its impossible to keep your table updated and you cannot include delays. But most of the public transport companies offer a web service to get all the connections of your station.
Go to the website of your transportation company and try to get the information of all the depatures manually!
Do it now ;) After that come back!
You did it? Cool - How does that help you?
Try to Get the Data Manually and Check What Your Browser Did
I cannot promise that this approach works for your local transportation system - but it worked for me and some others I tested. Go to the site of your transportation company. When you enter your local station to a search formular etc. - your browser sends a GET HTTP request to the server of the company and receives all the connections afterwards. You can monitor whats happening by using the developer tools in Chrome or Firefox. (Ctr+Shift+I für Chrome). While developer tools open, perform a browser based request like you would always do (pic in the step before). In the "network" tab you can see all the network activity. After I requested the departures from the Server a new entry containing "/xhr_depatures?...." appears. You can check what realy happen in this transaction by double click on the entry (pic).
OK nice... but what does that mean at all?
What Happend?
We can see that a GET request was performed with a very long URL (inside the red circle). To reproduce what this transaction did- just copy that link into your browser and see what happens. (see pic). OK!! it looks like this is what I need - I can see all the departures in the near future. Great! If you are not successful - continue with the next entry in the network tab.
Again - all the server knows must (should :P) be inside this URL. Lets have a look at the URL (see pic). In the top of pic #2 you can see the whole URL. Pretty long beast! I just reformatted the string a bit, so we can get a better overview. In the beginning there is obviously the server we try to reach ( "http://efa.mvv-muenchen.de" ) and a statement of what we want " /xhr_departures?". The rest are parameters we have to attache so we get the right departures. To be honest I don't understand all of them - but its not really necessary to ;). The most important ones are:
limit = 20 // How many departures do I want to receive?
iitdDate=20150504 // current date
itdTime=2349 // current time
The parameter will be most probable be different for the web service you used. You maybe need to try out by testing which parameters are the ones you need. This is the little "hacking" part. Be creative and solve the puzzle. In this case it was quite easy :P.
OK - perfect! I just need to change those values - and I get all the information I want! Lets try if our little Edison can do the same!
How to Reproduce That Manually?
You can use Netcat to mimic what your browser did without your interaction - I entered those commands directly on the Intel Edison over Putty.
1) First of all you need the IP address of the server you want to reach. I did that by using the ping command
"ping http://efa.mvv-muenchen.de/" : Output : "PING efa.mvv-muenchen.de (195.30.121.20): 56 data bytes"
2) So the IP address is "195.30.121.20". I make a connection to this Server via netcat by entering
"nc 195.30.121.20 80" : 80 is the port, since we want to perform a http request and the default port for that is 80. After netcat connected to the server I can enter m y request. It should be a "GET" request since I want data from the server. I type :
"GET /xhr_departures?locationServerActive=1&stat...." : please note the missing server address!
Output: A loooong string witch looks like the formatted list we also received over the browser! We can vary date and time to ensure its working like we want it to be. OK! Edison gets the information too - lets move forward!
Implementation - Overview & How to Get Date/time?
Before I start with details - just a short overview what the Edison should do
1) Get time and date
2) Send a HTTP GET request and collect answer
3) Extract the informations
4) Write it to the display
5) repeat :P
We start at 1). The default route to get the current date and time is a "NTP" request to a time server. But since we are on a nice embedded linux system - the little Edison already does that for us! If you ever tried to implement a NPT protocol - be thankful for that! You can get the date and time by:
char *cmd = "/bin/date +%F%t%T"; // outputs as "2013-10-21 22:25:00"
FILE *ptr;
char buf[64];
if ((ptr = popen(cmd, "r")) != NULL){
while (fgets(buf, 64, ptr) != NULL){ //Serial.print(buf); }
"buf" now contains the current timestamp in the format of . Reformat according to your needs.
Implementation - Send a HTTP Request With Edison
Edison's Ardurino environment delivers all the functions you need to perform a http request. Just include <Ethernet.h> and create a instance of EthernetClient in my example declared as "client". Have a look at the examples of the "Ethernet" entry in case you want to learn more about the library.
Here just some code snippets to show you the basic functionality. I attached the whole source code in the end - this here is just for you understanding - I hope it's self-explaining:
char readstring[5000];
EthernetClient client;
int i =0;
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress server(195,30,121,20); // Server is efa.mvv-muenchen.de
Ethernet.begin(mac);
client.println(cmd); // cmd is the request you want to send as a String
if(client.available()) {
char c = client.read(); // read a char
readstring[i++] = c; // put it to the char array
}
If you have further questions please visit : http://www.arduino.cc/en/reference/ethernet
The string "cmd" must have the same structure as the string we used in netcat before.
cmd = GET /xhr_departures?locationServerActive=1&stateless=1&type_d................... HTTP/1.0"
Make sure to handle the correct time and date inside the string (in the correct format!). I added a 'HTTP/1.0' in the end like it was done in the examples.
First of all you should check if you get the same results as with netcat by printing the chars to the serial console. If you don't - you did something wrong ;). Check for typos and make sure to send a line feed '\n' in the end.
Assume that's working - congratulations! You are almost done! The rest of your job is to extract the information you really need from the received string. You have to that according to the data you receive by the server. I did it in a "quick and dirty" way and you can do it much more elegant.
I discard all chars before I see a beginning of the body. You dont have to do that - but it felt uncomfortable to keep all the useless data ;). I used "if( strstr(readstring, " <tbody>")){" to check.
- I looked for common pattern to extract the information I need. With "j" and "k" I mark the index right before and after the chars I need. Use "indexOf" to find it. In my case the line number was surrounded by "..printable>" and "<\span>". Use "substring" to store the line number itself in a seperate string. Repeat that for all the information you need.
j = bodyString.indexOf("\"printable\">",j) + strlen("\"printable\">");
k = bodyString.indexOf("</span>",j);
content = bodyString.substring(j,k)
Write the Data to the Display - Profit!
Include the headers of <Wire.h" and the display library and create a display instance. Its quite striaght forward so I just show you some lines of code :
#include "rgb_lcd.h"
#include <Wire.h>
rgb_lcd lcd;
const int colorR = 255; const int colorG = 0; const int colorB = 0;
(...)
lcd.begin(16, 2);
lcd.setRGB(colorR, colorG, colorB);
lcd.setCursor(0, 1);
lcd.print(message);
Its very simple to use - otherwise please check the example of the "Grove RGB LCD". Make sure to set the supply voltage to 5V - the backlight will work, but no text will be printed. It took me a hour ;).
And thats basicly it! I used a pizza box to build the front plate and attached it to the wall next to my door.
If you have any questions please ask here in the comments or write an email Sven.Eliasson@gmx.de - check out my git for the source code and feel free to copy :
https://github.com/comino/IntelEdison_TransporTabl...
I would be happy if you can give some ideas for improvements or send me pictures of your own creations!