Memo on development of the car-rangefinder device/data logger for crosswalk study -Alex Bigazzi; abigazzi@pdx.edu; alexbigazzi.com; Sept. 16 th -19 th, 2013 The device is supposed to measure distances to vehicles, in order to measure where they stop WRT the crosswalk. By placing the device downstream of the crosswalk, angled upstream and in, one per lane, the measurements can be made. Locations and angles will have to be carefully measured in the field for each data collection. Hardware The device uses an Arduino microcontroller, Adafruit data logging shield, and MaxSonar ultrasonic rangefinder (MB1020-EZ2; http://www.maxbotix.com/ultrasonic_sensors/mb1020.htm). I started with the code from Ian Walker s website http://www.drianwalker.com/overtaking/buildasensor.html, mashed with code from my Portland ACE device Arduino sketch http://alexbigazzi.com/portlandace/. Compared to Ian Walker s device, I removed the record start/stop functionality of the button and decided to let recording be automatically started and stopped with device power. It is simpler and more appropriate in this context. The button now only is used to mark events. Application notes on the logger shield are here: http://learn.adafruit.com/adafruitdata-logger-shield/overview. Note that the Chip Select is automatically digital pin 10 on the Arduino for this shield, but can be modified with the CS connector on the shield (the chip select is also part of the code). There is a built-in red LED to indicate SD card activity, which is also connected to digital pin 13 (automatically). There are 2 additional yellow LED s on the shield I jumped LED1 to pin D3, to provide a separate feedback. To install the Arduino IDE (which you ll need to upload code), the official release is here: http://arduino.cc/en/main/software but there s a version I like better here: http://forum.arduino.cc/index.php?phpsessid=52j0dug3pspj2l5str5gn407r3&topic =118440.0. Install the IDE and you ll be able to easily upload sketches once you identify which COM port your Arduino is attached to (via USB). In order to view the real-time Serial output from the Arduino, call up Tools > Serial Monitor (the COM port must also be set correctly for this). The wiring schematic and other details on the device are in the Appendices. Rangefinder Datalogger A. Bigazzi 1
Setting the Real-Time Clock (RTC) A separate Arduino sketch has to be run every time a new coin cell battery is inserted in the logging shield, to set the internal clock. The sketch is located in the Arduino example menu, File > Examples > RTClib > ds1307.ini. The clock will be set to the time of the computer which is uploading the sketch. Note that the default baud for this sketch is 57600, while the default on the Serial Monitor is 9600; you ll have to change one to view the output to confirm that it worked (though you don t have to check this). I don t know how well the shield holds the time, so I would recommend doing some time sync checks with the button each data collection (and maybe resetting the clock shortly before the data collections). SD Cards You need a separate SD card reader to get the data off (most laptops have them now). Format the card with FAT16 or FAT32 file systems on your computer. You CANNOT format in FAT32 with a 64 Gb SD cards in Windows! Only up to a 32 Gb SD card can be formatted in FAT32, though you CAN format larger SD cards with a custom formatting utility. To check your SD card is working properly, run the example SD card sketch in Arduino File > Examples > SD > CardInfo.ino, to make sure the card is working. Operation Just power the device; it ll go through a ~6 second initialization process, then begin recording. It records continuously to a single file while powered on; remove power to stop recording, or press reset to start a new file. The initialization process begins with a fast 5-series blink of the yellow light, followed by a few slow flashes and another fast 5-series blink. There are separate LED s for writing to the SD card (red) and noting button presses (yellow). If there is a problem with initializing or opening a file, the yellow light will blink continuously, indicating a problem; if it s off, you re good to go. The yellow light will illuminate while the button is pressed (after the initialization process). If you have the device plugged into a computer via USB, you ll see continuous output (and any error messages from initialization) on the Serial Monitor. I made three devices and two 16-foot extension cords (between the device and the rangefinder head). The button is attached to the device, so that will be kept by the experimenter, and the rangefinder head can be mounted on a pole (up to 16 away). When connecting the device to the rangefinder header, (with or without the extensions) make sure the wire colors match up. The device is easily powered with a 9-volt battery. Note that the bottom of the Arduino has open metal connections, so it will need to be placed on an insulating surface. Rangefinder Datalogger A. Bigazzi 2
Output Data are saved in.csv files on the SD card, named with the file creation timestamp as mmddhhmm.csv. The CSV files have 4 columns: milliseconds counter, timestamp, range (in inches), and whether the button was depressed that interval (1=yes, 0=no). Samples/rows are batch written to the file every 5 seconds, but each row represents 200ms (i.e. 5 rows/second). These timings can easily be changed in the header section of the code ( sketch ). Appendix A: Device Schematic and Photos Rangefinder Datalogger A. Bigazzi 3
Rangefinder Datalogger A. Bigazzi 4
Appendix B: Arduino Source Code /* ** Crosswalk Study Code; Alex Bigazzi; abigazzi@gmail.com; alexbigazzi.com ** Loosely based on the "BikeSense Distance Sensor and Data Logger" by Ian Walker, University of Bath Changelog Version 0.1 Initial version, drawing from Ian Walker's code and the Portland ACE code Version 0.2 Functional version; reads/writes to SD card; always records; new file on each power-up; Separate LED for writing and communicating button presses */ // Libraries #include <Wire.h> #include "RTClib.h" // from http://www.ladyada.net/make/logshield/download.html #include <SD.h> // Definitions (replaced in the code upon compilation) #define sensorpin A0 // Analog Pin connected to the analog output on the rangefinder #define buttonpin 8 // Digital Pin connected to the pushbutton #define lightpin 3 // controls the signal light (13 is the built-in LED) #define samplepause 200 // in ms (200 ms is 1000/200=5 Hz #define syncinterval 5000 // ms between writes to disk #define CORRECTION 0 // adjustment to calibrate the sensor. in added inches RTC_DS1307 RTC; // initalize real time clock // Set up variables DateTime nowtime; // for real time clock readings float nowdistance = 999; // distance readings byte buttonreading = 0; // button state readings unsigned long lastreadingtime = 0; // for internal reading timer (working with millis) unsigned long readingtime; // will indicate the millis of the current reading unsigned long lastsynctime = 0; // when was the file last written? byte problem = 0; // to indicate trouble to the user const int chipselect = 10; // 10 for the Adafruit SD card system File logfile; // file pointer const int readperinch = 1024/512; // conversion for Vcc/512 per inch byte recording = 0; // indicates recording is happening Rangefinder Datalogger A. Bigazzi 5
const byte SerialOutput = 1; // Do we want to send to the computer display? /* ************************* ** Initialization ************************* */ void setup() { if (SerialOutput) { Serial.begin(9600); // for reading out on the computer pinmode(buttonpin, INPUT); // button will be used as an input pinmode(lightpin, OUTPUT); // LED as an output pinmode(sensorpin, INPUT); // setup analog input pin pinmode(chipselect, OUTPUT); // SD card Wire.begin(); RTC.begin(); flashlight(5, 50); // we've got this far okay, first flashes are distinctive to mark beginning delay(300); // Check RTC is okay if (! RTC.isrunning()) { problem = 1; if (SerialOutput) Serial.println("RTC fail"); return; // don't do anything more else { flashlight(1, 500); // Clock system is okay // see if the card is present and can be initialized: if (! SD.begin(chipSelect)) { problem = 1; if (SerialOutput) Serial.println("Card initialization fail"); return; // don't do anything more else { flashlight(1, 500); // Sd card is present and correct // Try to open a new file if (! problem &&! recording) { newfile(); if(! logfile) { // Did the file open successfully? problem = 1; if (SerialOutput) Serial.println("New File fail"); else { Rangefinder Datalogger A. Bigazzi 6
logfile.println("counter_ms,timestamp,range_in,button_down"); // Output the datafile headings recording = 1; // setup complete flashlight(5, 50); // we're all ready to go! delay(500); // short delay so the READY signal is distinct from the waiting-to-begin signal /* ************ ** MAIN LOOP ** ************ */ void loop() { // error reporting system if (problem) { // If something is wrong, do nothing but alert the user sos(); // Indicate with a continuous flashing light // Each loop, grab the timings nowtime = timestamp(); // Get the current real time readingtime = millis(); // and the millis time for checking interreading delay // Check the button if(!buttonreading) { buttonreading = 1-digitalRead(buttonPin); if(buttonreading) { digitalwrite(lightpin,high); // turn the LED on to indicate the button press else { digitalwrite(lightpin,low); // Output to computer, if desired if(serialoutput){ Serial.print((float)readingTime/1000); Serial.print("; "); if(buttonreading) { Serial.print("Button; "); else { Serial.print("No Button; "); Serial.println(distanceReading()); // if we're recording and ready for the next reading if (! problem && recording && (readingtime - lastreadingtime) >= samplepause) { nowdistance = distancereading(); // Get the range in centimetres nowdistance = nowdistance + CORRECTION; // correct it, if needed Rangefinder Datalogger A. Bigazzi 7
logfile.print(readingtime); // serial # logfile.print(","); logfile.print(nowtime.year()); logfile.print("-"); leadingzero(nowtime.month()); logfile.print("-"); leadingzero(nowtime.day()); logfile.print("t"); leadingzero(nowtime.hour()); logfile.print("-"); leadingzero(nowtime.minute()); logfile.print("-"); leadingzero(nowtime.second()); logfile.print(","); // CSV comma logfile.print(nowdistance); // write the distance datum logfile.print(","); logfile.println(buttonreading); // was the button down this loop? lastreadingtime = readingtime; the file // note when we last wrote to buttonreading = 0; // reset // end of reading IF // check if we need to write to disk if (! problem && recording && (readingtime - lastsynctime) >= syncinterval) { logfile.flush(); lastsynctime = readingtime; // end of sync IF /* **************** ** The functions ** **************** */ // Function to provide a timestamp DateTime timestamp() { // Written to work with the real-time clock that is integrated into an // Adafruit data logger shield. // Should also work with stand-alone clocks such as the Sparkfun RTC module // http://proto-pic.co.uk/real-time-clock-module/ // as it's the same chip, provided the same arduino pins are used DateTime nowtime = RTC.now(); return(nowtime); float distancereading() { // use a float format for decimal inches Rangefinder Datalogger A. Bigazzi 8
float inch; inch = analogread(sensorpin)/readperinch; return(inch); // Prints number to file with a leading zero if necessary. FIXME - make this a proper // function that returns the text rather than print it directly void leadingzero(uint8_t x) { if(x < 10) { logfile.print(0); logfile.print(x); // Flashes the signal light "x" times for "time" ms void flashlight(int x, int time) { for (int i = 0; i < x; i++) { digitalwrite(lightpin, HIGH); delay(time); digitalwrite(lightpin, LOW); delay(time); // error signal void sos() { flashlight(20,200); // quick flashing light // Create new file on SD card File newfile() { nowtime = timestamp(); // get the time, which we'll use for the filename String filename = String(); //filename += nowtime.year(); if (nowtime.month() < 10) { filename+= "0"; filename += nowtime.month(); if (nowtime.day() < 10) { filename+= "0"; filename += nowtime.day(); if (nowtime.hour() < 10) { filename+= "0"; filename += nowtime.hour(); if (nowtime.minute() < 10) { filename+= "0"; Rangefinder Datalogger A. Bigazzi 9
filename += nowtime.minute(); filename += ".csv"; // convert file string to character array and open the file for writing char filename2[30]; filename.tochararray(filename2, 30); logfile = SD.open(filename2, FILE_WRITE); // return(logfile); Rangefinder Datalogger A. Bigazzi 10