An I/O controller for virtual pinball machines: accelerometer nudge sensing, analog plunger input, button input encoding, LedWiz compatible output controls, and more.

Dependencies:   mbed FastIO FastPWM USBDevice

Fork of Pinscape_Controller by Mike R

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers distanceSensor.h Source File

distanceSensor.h

00001 // Plunger sensor type for distance sensors.
00002 //
00003 // This type of sensor measures the distance to a target by sending
00004 // optical or sound signals and watching for the reflection.  There are
00005 // many types of these sensors, including sensors that measure the 
00006 // intensity of reflected sound or light signals, sensors that measure
00007 // the round-trip time of "pings", and sensors that measure optical
00008 // parallax.
00009 //
00010 // The basic installation for this type of sensor involves placing the
00011 // sensor itself in a fixed location at one end of the plunger, pointing
00012 // down the length of the plunger, and placing a reflective target at
00013 // the end of the plunger.  The target can simply be an ordinary plunger
00014 // tip, if the sensor is at the far end of the plunger facing forward
00015 // (facing the front of the cabinet).  Alternatively, the target can
00016 // be a disk or similar object attached to the end of the plunger, and
00017 // the sensor can be placed at the front of the machine facing the target.
00018 // In either case, the sensor measures the distance to the target at any
00019 // given time, and we interpret that into the plunger position.
00020 //
00021 // Here are the specific sensor types we currently support:
00022 //
00023 // VCNL4010: An IR proximity sensor.  This sensor shines an IR light at a
00024 // target and measures the intensity of the reflected light.  This doesn't
00025 // measure distance per se, but since the intensity of a light source
00026 // falls off as the square of the distance, we can use the reflected
00027 // intensity as a proxy for the distance by calculating 1/sqrt(intensity).
00028 // The main reason to support this type of sensor is that it's used in the
00029 // VirtuaPin v3 plunger kit, and several people have requested support so
00030 // that they can move re-flash that kit using the Pinscape software and
00031 // continue using their existing plunger sensor.  Many people might also
00032 // consider this sensor for new DIY builds, since it produces pretty good
00033 // results.  It's not as accurate as a potentiometer or quadrature sensor,
00034 // but it yields low-noise results with good enough precision for smooth
00035 // on-screen animation (maybe around 1mm precision).  Its main drawback
00036 // is that it's relatively slow (250 Hz maximum sampling rate), but it's
00037 // still fast enough to be usable.  It has several virtues that might more
00038 // than orffset its technical limitations for many paople: it's easy to
00039 // set up physically, it's completely non-contact, and it's cheap (under
00040 // $10 for the Adafruit breakout board).
00041 // 
00042 // VL6180X: This is an optical (IR) "time of flight" sensor that measures
00043 // the distance to the target by sending optical pings and timing the 
00044 // return signal, converting the result to distance via the known speed 
00045 // of light.  This sensor has nominal 1mm precision, although its true
00046 // precision in testing is closer to 5mm.  Sample times are around 16ms.
00047 // This makes the sensor acceptable but not great by Pinscape standards;
00048 // we generally consider 2.5ms read times and .25mm precision to be the
00049 // minimum standards.  However, this sensor is inexpensive and easier to
00050 // set up than most of the better options, so it might be attractive to
00051 // some cab builders despite the quality tradeoffs.
00052 //
00053 //
00054 
00055 #ifndef _DISTANCESENSOR_H_
00056 #define _DISTANCESENSOR_H_
00057 
00058 #include "plunger.h"
00059 #include "VL6180X.h"
00060 #include "VCNL4010.h"
00061 
00062 
00063 // Base class for distance sensors
00064 class PlungerSensorDistance: public PlungerSensor
00065 {
00066 public:
00067     PlungerSensorDistance(int nativeScale) : PlungerSensor(nativeScale)
00068     {
00069         totalTime = 0;
00070         nRuns = 0;
00071     }
00072 
00073     // get the average scan time
00074     virtual uint32_t getAvgScanTime() { return uint32_t(totalTime / nRuns); }
00075 
00076 protected:
00077     // collect scan time statistics
00078     void collectScanTimeStats(uint32_t dt)
00079     {
00080         totalTime += dt;
00081         nRuns += 1;
00082     }
00083 
00084     // scan time statistics
00085     uint64_t totalTime;       // total time consumed by all reads so far
00086     uint32_t nRuns;           // number of runs so far
00087 };
00088 
00089 // PlungerSensor interface implementation for VL6180X sensors.  
00090 //
00091 // The VL6180X reports distances in millimeter quanta, so the native
00092 // sensor units are millimeters.  A physical plunger has about 3" of
00093 // total travel, but leave a little extra padding for measurement
00094 // inaccuracies and other unusual situations, so'll use an actual
00095 // native scale of 150mm.
00096 class PlungerSensorVL6180X: public PlungerSensorDistance
00097 {
00098 public:
00099     PlungerSensorVL6180X(PinName sda, PinName scl, PinName gpio0)
00100         : PlungerSensorDistance(150),
00101           sensor(sda, scl, I2C_ADDRESS, gpio0, true)
00102     {
00103     }
00104     
00105     // fixed I2C bus address for the VL6180X
00106     static const int I2C_ADDRESS = 0x29;
00107     
00108     virtual void init()
00109     {
00110         // initialize the sensor and set the default configuration
00111         sensor.init();
00112         sensor.setDefaults();
00113         
00114         // start a reading
00115         sensor.startRangeReading();
00116     }
00117     
00118     virtual bool ready()
00119     {
00120         // make sure a reading has been initiated
00121         sensor.startRangeReading();
00122         
00123         // check if a reading is ready
00124         return sensor.rangeReady();
00125     }
00126     
00127     virtual bool readRaw(PlungerReading &r)
00128     {
00129         // if we have a new reading ready, collect it
00130         if (sensor.rangeReady())
00131         {
00132             // Get the range reading.  Note that we already know that the
00133             // sensor has a reading ready, so it shouldn't be possible to 
00134             // time out on the read.  (The sensor could have timed out on 
00135             // convergence, but if it did, that's in the past already so 
00136             // it's not something we have to wait for now.)
00137             uint8_t d;
00138             uint32_t t, dt;
00139             lastErr = sensor.getRange(d, t, dt, 100);
00140             
00141             // if we got a reading, update the last reading
00142             if (lastErr == 0)
00143             {
00144                 // save the new reading
00145                 last.pos = d;
00146                 last.t = t;
00147             
00148                 // collect scan time statistics
00149                 collectScanTimeStats(dt);
00150             }
00151     
00152             // start a new reading
00153             sensor.startRangeReading();
00154         }
00155         
00156         // return the most recent reading
00157         r = last;
00158         return lastErr == 0;
00159     }
00160     
00161 protected:
00162     // underlying sensor interface
00163     VL6180X sensor;
00164     
00165     // last reading and error status
00166     PlungerReading last;
00167     int lastErr;
00168 };
00169 
00170 
00171 // PlungerSensor interface implementation for VCNL4010 IR proximity sensors
00172 //
00173 // Our hardware interface for this sensor reports distances in abstract
00174 // units that fit a 16-bit int, so the native distance scale is 0..65535.
00175 // (The sensor itself doesn't have a native distance scale per se, since
00176 // it reports results in terms of the intensity of the reflected light.
00177 // This is related to the distance by an inverse square law, so since we
00178 // have to do some math on the raw readings anyway to convert them to
00179 // distances, we can choose whatever units we want for the conversion.
00180 // We choose units that are convenient for our purposes at the joystick
00181 // layer, given the 16-bit field we use to report the position back to
00182 // the PC.)
00183 //
00184 // The iredCurrent parameter sets the brightness of the sensor's IR LED,
00185 // which serves as the light source for the reflected light intensity
00186 // readings used for proximity measurements.  This is given in units of
00187 // 10mA, so 1 means 10mA, 2 means 20mA, etc.  Valid values are from 1
00188 // (10mA) to 20 (200mA).
00189 //
00190 class PlungerSensorVCNL4010: public PlungerSensorDistance
00191 {
00192 public:
00193     PlungerSensorVCNL4010(PinName sda, PinName scl, int iredCurrent)
00194         : PlungerSensorDistance(65535),
00195           sensor(sda, scl, true, iredCurrent)
00196     {
00197     }
00198     
00199     virtual void init()
00200     {
00201         // initialize the sensor
00202         sensor.init();
00203         
00204         // start a reading
00205         sensor.startProxReading();
00206     }
00207     
00208     virtual bool ready()
00209     {
00210         // check if a reading is ready
00211         return sensor.proxReady();
00212     }
00213     
00214     virtual bool readRaw(PlungerReading &r)
00215     {
00216         // if we have a new reading ready, collect it
00217         if (sensor.proxReady())
00218         {
00219             // Get the proximity count reading.  Note that we already know 
00220             // that the sensor has a reading ready, so it shouldn't be
00221             // possible to time out on the read.
00222             int rawCount;
00223             uint32_t t, dt;
00224             lastErr = sensor.getProx(rawCount, t, dt, 100);
00225             
00226             // if we got a reading, update the last reading
00227             if (lastErr == 0)
00228             {
00229                 // run the proximity count through the jitter filter
00230                 int filteredCount = jitterFilter(rawCount);
00231                 
00232                 // convert the count to a distance, using the filtered count
00233                 int dist = sensor.countToDistance(filteredCount);
00234             
00235                 // save the new reading
00236                 last.pos = dist;
00237                 last.t = t;
00238                 lastFilteredCount = filteredCount;
00239                 lastRawCount = rawCount;
00240             
00241                 // collect scan time statistics
00242                 collectScanTimeStats(dt);
00243             }
00244         }
00245         
00246         // return the most recent reading
00247         r = last;
00248         return lastErr == 0;
00249     }
00250     
00251     // The VCNL4010 applies jitter filtering to the physical sensor reading
00252     // instead of to the distance reading.  This produces much better results
00253     // for this sensor because the sensor's distance resolution gets lower
00254     // at longer distances, so the conversion to distance tends to amplify
00255     // noise quite a bit at the distant end.  It's therefore important to
00256     // do the noise reduction in the brightness domain, before that
00257     // amplification takes place.
00258     virtual int postJitterFilter(int pos) { return pos; }
00259     
00260     // Send a status report for the config tool sensor viewer
00261     virtual void sendStatusReport(class USBJoystick &js, uint8_t flags)
00262     {
00263         // send the common status report
00264         PlungerSensor::sendStatusReport(js, flags);
00265 
00266         // send the extra VCNL4010 sensor status report
00267         js.sendPlungerStatusVCNL4010(lastFilteredCount, lastRawCount);
00268     }
00269 
00270     // Restore the saved calibration data from the configuration.  The
00271     // main loop calls this at initialization time to pass us saved
00272     // private configuration data.  The VCNL4010 uses this to store the
00273     // minimum proximity count reading observed during calibration, which
00274     // it uses to figure the scaling factor for the 1/sqrt(intensity)
00275     // distance calculation.
00276     virtual void restoreCalibration(Config &config) 
00277     {
00278         // restore the saved minimum count reading
00279         sensor.restoreCalibration(config);
00280     }
00281     
00282     // Begin calibration.  The main loop calls this when the user
00283     // initiates a calibration cycle.  The VCNL4010 code uses this to
00284     // reset its internal record of the proximity minimum.
00285     virtual void beginCalibration(Config &)
00286     {
00287         sensor.beginCalibration();
00288     }
00289     
00290     // End calibration.  The main loop calls this when a calibration
00291     // cycle finishes.  The VCNL4010 code uses this to save the minimum
00292     // count value observed during the calibration interval, and to
00293     // calculate the new scaling factor for the 1/sqrt(intensity)
00294     // distance calculation.
00295     virtual void endCalibration(Config &config)
00296     {
00297         // let the sensor figure the new scaling factor
00298         sensor.endCalibration(config);
00299     }
00300   
00301         
00302 protected:
00303     // underlying sensor interface
00304     VCNL4010 sensor;
00305     
00306     // last reading and error status
00307     PlungerReading last;
00308     int lastFilteredCount;
00309     int lastRawCount;
00310     int lastErr;
00311 };
00312 
00313 #endif