Motivation
My newly ordered Arduino Uno came in the post today so I got to start building with it!
An upcoming sensing deployment needs a way of sensing gas usage, so I’m building a basic sensor to measure it. Luckily, most gas meters have an LED pulse which goes off every so often and we can measure that. The one in my house informs me the light will pulse every 10 dm^3 of gas, so every time the second number from the right on the meter ticks the light will pulse. Failing this, the 0 on the right most dial is very reflective, and an LED can be used to reflect light from it.
As the extra bits I need to finish the sensor hasn’t come yet, namely the SD card shield and a battery pack, I can’t lock the sensor in my gas cupboard and give it a test, so for the time being I’m turning off my computer screens and lights, then flashing a red head torch at it to simulate a pulse.
Code
After a bit of internet trawling, I found an ace site laying down how to do the wiring for a digital light sensor (http://www.ladyada.net/learn/sensors/cds.html#bonus_reading_photocells_without_analog_pins [This link seems to have died since the post was written, the circuit diagram will be linked in at the end of the post.]). From that, I added in a method which took a rolling average of the last 5 measurements and outputted if the measured light was brighter than 110% of the average (not exactly statistical, but I intend for these to be locked in a dark box…).
Here’s what I have so far:
/* Based on: Photocell simple testing sketch. Connect one end of photocell to power, the other end to pin 2. Then connect one end of a 0.1uF capacitor from pin 2 to ground For more information see www.ladyada.net/learn/sensors/cds.html */ // Number of measurements for averaging const int AVERAGE_LENGTH = 5; int photocellPin = 2; // the LDR and cap are connected to pin2 int photocellReading; // the digital reading int ledPin = 13; // you can just use the 'built in' LED // for the circular buffer int lastReadings[AVERAGE_LENGTH]; int average = 0; int counter = 0; void setup(void) { // We'll send debugging information via the Serial monitor Serial.begin(9600); // Initialise the array for(int i=0;i<AVERAGE_LENGTH;i++) { lastReadings[i] = 0; } } void loop(void) { // read the resistor using the RCtime technique photocellReading = RCtime(photocellPin); // Calculate rolling average ++counter %= AVERAGE_LENGTH; lastReadings[counter] = photocellReading; calcStats(); int bound = average - (average/10); // Glorious debug Serial.print("Av = "); Serial.print(average); Serial.print(" = ["); for(int i=0; i<AVERAGE_LENGTH;i++) { Serial.print(lastReadings[i]); Serial.print(","); } Serial.print("] "); if (photocellReading < bound) { Serial.print(" . RCtime reading = "); Serial.println(photocellReading); // the raw analog reading } else { Serial.println(); } delay(100); } // Calculates the new mean based on the last 20 measurements int calcStats() { // average average = 0; for(int i=0;i<AVERAGE_LENGTH;i++) { average += lastReadings[i]; } average /= AVERAGE_LENGTH; } // Uses a digital pin to measure a resistor (like an FSR or photocell!) // We do this by having the resistor feed current into a capacitor and // counting how long it takes to get to Vcc/2 (for most arduinos, thats 2.5V) int RCtime(int RCpin) { int reading = 0; // start with 0 // set the pin to an output and pull to LOW (ground) pinMode(RCpin, OUTPUT); digitalWrite(RCpin, LOW); // Now set the pin to an input and... pinMode(RCpin, INPUT); while (digitalRead(RCpin) == LOW) { // count how long it takes to rise up to HIGH reading++; // increment to keep track of time if (reading == 30000) { // if we got this far, the resistance is so high // its likely that nothing is connected! break; // leave the loop } } // OK either we maxed out at 30000 or hopefully got a reading, return the count return reading; }
Next Steps
Well apart from the hardware I need, so issues need to be addressed which may or may not require extra hardware – as I’ve just thought of them.
- DateTime equivalent object for when I register a pulse
- Work out how long these will last on battery
- Can I set an interrupt to go off when a digital value hits a threshold? Or does this require analogue input? If I can it would massively save on battery as no polling! But, it may require fiddly per-house calibration, which the brute force method ignores
- Laser/3d Printed box and some form of mounting which will let me attach to anything. Probably going to be velcro.
Here’s a video of it working: