openobject.org

Adding a Sensor

From Open Source Urbanism

To get a stationary image from the POV device and to collect data such as speed and distance travelled we need to count the revolutions of the bike wheel. The SpokePOV kits we constructed at the beginning of the project use a Hall effect switch and a magnet mounted on the bike frame/forks. The Hall effect switch measures changes in magnetic flux; as the switch passes the magnet it signals a change to the microcontroller; the specific component is a US5881 (data sheet here). This IC gives a switched HIGH/LOW output that can be easily connected to TTL or CMOS logic. It should be fairly simple to connect one of these to an input on the Arduino and modify the program to count wheel revolutions.

Contents

Testing the Sensor

The US5881 Hall effect switch was hard to come by so I purchased a UGN3503U Hall Effect Sensor from my local electronics shop. Unlike the US5881 switch this sensor gives a variable analogue output (it was the only hall effect sensor my local store carried) but this is not really a problem for this project - it may even be beneficial. The Arduino has 6 analogue in pins, the output from the UGN3503U can be directly connected to one of these pins without any additional circuitry. The Arduino sketch can then check the analogue pin for changes in magnetic flux.

I wrote the following sketch to test the Hall Effect Sensor. The sensor is connected to analogue pin 0 and to the +5V and GND Arduino pins.

int inPin = 0;    // select the input pin for the Hall Effect Sensor
int val = 0;       // variable to store the value coming from the sensor

void setup() {
 Serial.begin(9600); // connect to the serial port
}

void loop() {
 val = analogRead(inPin);  // read the Hall Effect Sensor
 Serial.println(val);
}

This code sends sensor data to the Arduino programming environment allowing changes in magnetic flux to be monitored on the computer. With no (or weak) magnetic fields present the Hall Effect Sensor gives a reading of 508 from the analogue input. The presence of a strong North Pole will drive that value down to around 250 and a South Pole will push it up to about 850.

Blinking an LED

The following sketch blinks the LED on pin 13 when a magnet is passed near the sensor. It does this by setting a mean sensor value and activating when the sensor reading deviates from this mean by more than the sensitivity value. (this will work irrespective of which pole is facing the sensor).

int inPin = 0;    // select the input pin for the Hall Effect Sensor
int ledPin = 13;   // select the pin for the LED
int val = 0;       // variable to store the value coming from the sensor
int mean = 508;
int sensitivity = 20;

void setup() {
 pinMode(ledPin, OUTPUT);  // declare the ledPin as an OUTPUT
}

void loop() {
 val = analogRead(inPin);  // read the Hall Effect Sensor
 if (val > (mean + sensitivity) || val < (mean - sensitivity)) {
   digitalWrite(ledPin, HIGH);  // turn the ledPin on
 } 
 else {                 
   digitalWrite(ledPin, LOW);   // turn the ledPin off
 }
}

Combining the Sensor with a POV

I decided to construct a new shield to hold the sensor and LEDs. The shield connects to the Arduino's digital pins (0-8), the analogue pins (0-5) and the power header pins.

The LEDs connect to pins 1-7 via 100 Ohm resistors (as with the previous shield), the sensor is connected to analogue pin 0.

The difficulty with combining the sensor with the POV code developed in the previous section (available here Bike_POV_Beta_2) is the inclusion of delay() functions. When you ask the Arduino to delay() it just sits there doing nothing for the requested period of time; if the sensor was to wiz past the magnet while the Arduino was delayed then the Arduino wouldn't register it. This problem is however also a solution because it provides a place for us to put the sensor read function.

By replacing the delay() function with our own function pauseFrame() we can pause the LED display and at the same time check on the sensor.

void pauseFrame(int duration){
 int sensVal;  // variable to store the value coming from the sensor
 unsigned long endTime = millis() + duration;  // calculate the end time

 // wait for a while
 while(millis() < endTime){
   // and keep an eye on the sensor
   sensVal = analogRead(sensorPIN);  // read the Hall Effect Sensor
   if (sensVal > (mean + sensitivity) || sensVal < (mean - sensitivity)) {
     sensorFlag = true;  // set the sensor flag variable
     break;  // exit the pause
   }
 }
}

The sensorFlag variable is used to reset the displayed text by testing for it in the loop() function.

void loop()
{
 // printing every letter of the textString
 for (int i=0; i<sizeof(textString); i++){
   printLetter(textString[i]);
   if(sensorFlag){
     sensorFlag = false;  // reset the sensor flag
     break;  // restart the text string
   }
 }
  // space between strings
 pauseFrame(timer3);
}

The full code is available here: Bike_POV_Beta_3

Measuring Wheel Speed

In order to keep the letter size and position stable we need to tie the pause function to the wheel speed. We can do this by getting the Arduino time (returned by the millis() function) when the sensor is triggered. When the sensor is triggered a second time we can compare the two millis() readings and calculate the wheels rotation speed (I'm using microseconds for the units because I found that milliseconds weren't accurate enough). By dividing the time it takes for the wheel to complete a full rotation by the number of frames per rotation (a frame is one line of LEDs) we can find the duration of each frame and use this in our pauseFrame() function.

The new pauseFrame() function looks like this:

// set up display parameters
const int numFrames = 350;	// number of frames per rotation
unsigned long frameDuration = 1000;	// time for each frame in microseconds - updated from rotation speed
const int letterSpace = 4;	// number of frames between letters
const int stringSpace = 20;	// number of frames between text string repeat
unsigned long startTime = (millis() * 1000);	// start time for wheel rotation in microseconds

void pauseFrame(int frames){
  int sensVal;  // variable to store the value coming from the sensor
  unsigned long endTime = (millis() * 1000) + (frames * frameDuration);  // calculate the end time

  if(!sensorFlag){  // if the sensor hasn't been triggered then wait for a while
    while((millis() * 1000) < endTime){
      // check the sensor
      sensVal = analogRead(sensorPIN);  // read the Hall Effect Sensor
      if (sensVal > (mean + sensitivity) || sensVal < (mean - sensitivity)) {
        if((millis() * 1000) < (startTime + (50 * frameDuration))){  // debounce the sensor reading
          startTime = (millis() * 1000);  // reset the start time
        } 
        else {
          sensorFlag = true;  // set the sensor flag variable
          frameDuration = ((millis() * 1000) - startTime) / numFrames;  // recalculate frameDuration
          startTime = (millis() * 1000);  // reset the start time
          break;  // exit the pause
        }
      }
    }
  }
}

The if((millis() * 1000) < (startTime + (50 * frameDuration))) structure is used to debounce the sensor reading; it prevents false readings from occurring if the magnet lingers over the sensor. When a valid sensor reading occurs the code recalculates the frameDuration interval and resets the startTime variable to the Arduino's current time.

The full code is available here: Bike_POV_Beta_4


The new beta 4 version in operation.


< Moving to the Arduino | Counting Revolutions >