Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: mbed FastIO FastPWM USBDevice
Plunger/tsl14xxSensor.h@87:8d35c74403af, 2017-05-09 (annotated)
- Committer:
- mjr
- Date:
- Tue May 09 05:48:37 2017 +0000
- Revision:
- 87:8d35c74403af
- Parent:
- 86:e30a1f60f783
- Child:
- 100:1ff35c07217c
AEDR-8300, VL6180X, TLC59116; new plunger firing detection
Who changed what in which revision?
| User | Revision | Line number | New contents of line |
|---|---|---|---|
| mjr | 82:4f6209cb5c33 | 1 | // Base class for TSL14xx-based plunger sensors. |
| mjr | 82:4f6209cb5c33 | 2 | // |
| mjr | 82:4f6209cb5c33 | 3 | // This provides a common base class for plunger sensors based on |
| mjr | 82:4f6209cb5c33 | 4 | // AMS/TAOS TSL14xx sensors (TSL1410R, TSL1412S, TSL1401CL). The sensors |
| mjr | 82:4f6209cb5c33 | 5 | // in this series all work the same way, differing mostly in the number |
| mjr | 82:4f6209cb5c33 | 6 | // of pixels. However, we have two fundamentally different ways of using |
| mjr | 82:4f6209cb5c33 | 7 | // these image sensors to detect position: sensing the position of the |
| mjr | 82:4f6209cb5c33 | 8 | // shadow cast by the plunger on the sensor, and optically reading a bar |
| mjr | 82:4f6209cb5c33 | 9 | // code telling us the location of the sensor along a scale. This class |
| mjr | 82:4f6209cb5c33 | 10 | // provides the low-level pixel-sensor interface; subclasses provide the |
| mjr | 82:4f6209cb5c33 | 11 | // image analysis that figures the position from the captured image. |
| mjr | 82:4f6209cb5c33 | 12 | |
| mjr | 82:4f6209cb5c33 | 13 | |
| mjr | 82:4f6209cb5c33 | 14 | #ifndef _TSL14XXSENSOR_H_ |
| mjr | 82:4f6209cb5c33 | 15 | #define _TSL14XXSENSOR_H_ |
| mjr | 82:4f6209cb5c33 | 16 | |
| mjr | 82:4f6209cb5c33 | 17 | #include "plunger.h" |
| mjr | 87:8d35c74403af | 18 | #include "edgeSensor.h" |
| mjr | 87:8d35c74403af | 19 | #include "barCodeSensor.h" |
| mjr | 82:4f6209cb5c33 | 20 | #include "TSL14xx.h" |
| mjr | 82:4f6209cb5c33 | 21 | |
| mjr | 87:8d35c74403af | 22 | class PlungerSensorImageInterfaceTSL14xx: public PlungerSensorImageInterface |
| mjr | 82:4f6209cb5c33 | 23 | { |
| mjr | 82:4f6209cb5c33 | 24 | public: |
| mjr | 87:8d35c74403af | 25 | PlungerSensorImageInterfaceTSL14xx(int nativePix, PinName si, PinName clock, PinName ao) |
| mjr | 87:8d35c74403af | 26 | : PlungerSensorImageInterface(nativePix), sensor(nativePix, si, clock, ao) |
| mjr | 82:4f6209cb5c33 | 27 | { |
| mjr | 82:4f6209cb5c33 | 28 | } |
| mjr | 82:4f6209cb5c33 | 29 | |
| mjr | 82:4f6209cb5c33 | 30 | // is the sensor ready? |
| mjr | 82:4f6209cb5c33 | 31 | virtual bool ready() { return sensor.ready(); } |
| mjr | 82:4f6209cb5c33 | 32 | |
| mjr | 87:8d35c74403af | 33 | // is a DMA transfer in progress? |
| mjr | 87:8d35c74403af | 34 | virtual bool dmaBusy() { return sensor.dmaBusy(); } |
| mjr | 87:8d35c74403af | 35 | |
| mjr | 82:4f6209cb5c33 | 36 | virtual void init() |
| mjr | 82:4f6209cb5c33 | 37 | { |
| mjr | 82:4f6209cb5c33 | 38 | sensor.clear(); |
| mjr | 82:4f6209cb5c33 | 39 | } |
| mjr | 82:4f6209cb5c33 | 40 | |
| mjr | 87:8d35c74403af | 41 | // get the average sensor scan time |
| mjr | 87:8d35c74403af | 42 | virtual uint32_t getAvgScanTime() { return sensor.getAvgScanTime(); } |
| mjr | 87:8d35c74403af | 43 | |
| mjr | 87:8d35c74403af | 44 | protected: |
| mjr | 87:8d35c74403af | 45 | virtual void getStatusReportPixels( |
| mjr | 87:8d35c74403af | 46 | uint8_t* &pix, uint32_t &t, int axcTime, int extraTime) |
| mjr | 82:4f6209cb5c33 | 47 | { |
| mjr | 86:e30a1f60f783 | 48 | // The sensor's internal buffering scheme makes it a little tricky |
| mjr | 86:e30a1f60f783 | 49 | // to get the requested timing, and our own double-buffering adds a |
| mjr | 86:e30a1f60f783 | 50 | // little complexity as well. To get the exact timing requested, we |
| mjr | 86:e30a1f60f783 | 51 | // have to work with the buffering pipeline like so: |
| mjr | 82:4f6209cb5c33 | 52 | // |
| mjr | 86:e30a1f60f783 | 53 | // 1. Call startCapture(). This waits for any in-progress pixel |
| mjr | 86:e30a1f60f783 | 54 | // transfer from the sensor to finish, then executes a HOLD/SI pulse |
| mjr | 86:e30a1f60f783 | 55 | // on the sensor. The HOLD/SI takes a snapshot of the current live |
| mjr | 86:e30a1f60f783 | 56 | // photo receptors to the sensor shift register. These pixels have |
| mjr | 86:e30a1f60f783 | 57 | // been integrating starting from before we were called; call this |
| mjr | 86:e30a1f60f783 | 58 | // integration period A. So the shift register contains period A. |
| mjr | 86:e30a1f60f783 | 59 | // The HOLD/SI then grounds the photo receptors, clearing their |
| mjr | 86:e30a1f60f783 | 60 | // charge, thus starting a new integration period B. After sending |
| mjr | 86:e30a1f60f783 | 61 | // the HOLD/SI pulse, startCapture() begins a DMA transfer of the |
| mjr | 86:e30a1f60f783 | 62 | // shift register pixels (period A) to one of our two buffers (call |
| mjr | 86:e30a1f60f783 | 63 | // it the EVEN buffer). |
| mjr | 82:4f6209cb5c33 | 64 | // |
| mjr | 86:e30a1f60f783 | 65 | // 2. Wait for the current transfer (period A to the EVEN buffer) |
| mjr | 86:e30a1f60f783 | 66 | // to finish. The minimum integration time is the time of one |
| mjr | 86:e30a1f60f783 | 67 | // transfer cycle, so this brings us to the minimum time for |
| mjr | 86:e30a1f60f783 | 68 | // period B. |
| mjr | 86:e30a1f60f783 | 69 | // |
| mjr | 86:e30a1f60f783 | 70 | // 3. Now pause for the reqeusted extra delay time. Period B is |
| mjr | 86:e30a1f60f783 | 71 | // still running at this point (it keeps going until we start a |
| mjr | 86:e30a1f60f783 | 72 | // new capture), so this pause adds the requested extra time to |
| mjr | 86:e30a1f60f783 | 73 | // period B's total integration time. This brings period B to |
| mjr | 86:e30a1f60f783 | 74 | // exactly the requested total time. |
| mjr | 82:4f6209cb5c33 | 75 | // |
| mjr | 86:e30a1f60f783 | 76 | // 4. Call startCapture() to end period B, move period B's pixels |
| mjr | 86:e30a1f60f783 | 77 | // to the sensor's shift register, and begin period C. This |
| mjr | 86:e30a1f60f783 | 78 | // switches DMA buffers, so the EVEN buffer (with period A) is now |
| mjr | 86:e30a1f60f783 | 79 | // available, and the ODD buffer becomes the DMA target for the |
| mjr | 86:e30a1f60f783 | 80 | // period B pixels. |
| mjr | 82:4f6209cb5c33 | 81 | // |
| mjr | 86:e30a1f60f783 | 82 | // 5. Wait for the period B pixels to become available, via |
| mjr | 86:e30a1f60f783 | 83 | // waitPix(). This waits for the DMA transfer to complete and |
| mjr | 86:e30a1f60f783 | 84 | // hands us the latest (ODD) transfer buffer. |
| mjr | 86:e30a1f60f783 | 85 | // |
| mjr | 86:e30a1f60f783 | 86 | sensor.startCapture(axcTime); // begin transfer of pixels from incoming period A, begin integration period B |
| mjr | 82:4f6209cb5c33 | 87 | sensor.wait(); // wait for scan of A to complete, as minimum integration B time |
| mjr | 82:4f6209cb5c33 | 88 | wait_us(long(extraTime) * 100); // add extraTime (0.1ms == 100us increments) to integration B time |
| mjr | 86:e30a1f60f783 | 89 | sensor.startCapture(axcTime); // begin transfer of pixels from integration period B, begin period C; period A pixels now available |
| mjr | 82:4f6209cb5c33 | 90 | |
| mjr | 86:e30a1f60f783 | 91 | // wait for the DMA transfer of period B to finish, and get the |
| mjr | 86:e30a1f60f783 | 92 | // period B pixels |
| mjr | 86:e30a1f60f783 | 93 | sensor.waitPix(pix, t); |
| mjr | 87:8d35c74403af | 94 | } |
| mjr | 87:8d35c74403af | 95 | |
| mjr | 87:8d35c74403af | 96 | // reset after a status report |
| mjr | 87:8d35c74403af | 97 | virtual void resetAfterStatusReport(int axcTime) |
| mjr | 87:8d35c74403af | 98 | { |
| mjr | 82:4f6209cb5c33 | 99 | // It takes us a while to send all of the pixels, since we have |
| mjr | 82:4f6209cb5c33 | 100 | // to break them up into many USB reports. This delay means that |
| mjr | 82:4f6209cb5c33 | 101 | // the sensor has been sitting there integrating for much longer |
| mjr | 82:4f6209cb5c33 | 102 | // than usual, so the next frame read will be overexposed. To |
| mjr | 82:4f6209cb5c33 | 103 | // mitigate this, make sure we don't have a capture running, |
| mjr | 82:4f6209cb5c33 | 104 | // then clear the sensor and start a new capture. |
| mjr | 82:4f6209cb5c33 | 105 | sensor.wait(); |
| mjr | 82:4f6209cb5c33 | 106 | sensor.clear(); |
| mjr | 82:4f6209cb5c33 | 107 | sensor.startCapture(axcTime); |
| mjr | 82:4f6209cb5c33 | 108 | } |
| mjr | 87:8d35c74403af | 109 | |
| mjr | 82:4f6209cb5c33 | 110 | // the low-level interface to the TSL14xx sensor |
| mjr | 82:4f6209cb5c33 | 111 | TSL14xx sensor; |
| mjr | 82:4f6209cb5c33 | 112 | }; |
| mjr | 82:4f6209cb5c33 | 113 | |
| mjr | 86:e30a1f60f783 | 114 | // --------------------------------------------------------------------- |
| mjr | 86:e30a1f60f783 | 115 | // |
| mjr | 86:e30a1f60f783 | 116 | // Subclass for the large sensors, such as TSL1410R (1280 pixels) |
| mjr | 86:e30a1f60f783 | 117 | // and TSL1412S (1536 pixels). |
| mjr | 86:e30a1f60f783 | 118 | // |
| mjr | 86:e30a1f60f783 | 119 | // For the large sensors, pixel transfers take a long time: about |
| mjr | 86:e30a1f60f783 | 120 | // 2.5ms on the 1410R and 1412S. This is much longer than our main |
| mjr | 86:e30a1f60f783 | 121 | // loop time, so we don't want to block other work to do a transfer. |
| mjr | 86:e30a1f60f783 | 122 | // Instead, we want to do our transfers asynchronously, so that the |
| mjr | 86:e30a1f60f783 | 123 | // main loop can keep going while a transfer proceeds. This is |
| mjr | 86:e30a1f60f783 | 124 | // possible via our DMA double buffering. |
| mjr | 86:e30a1f60f783 | 125 | // |
| mjr | 86:e30a1f60f783 | 126 | // This scheme gives us three images in our pipeline at any given time: |
| mjr | 86:e30a1f60f783 | 127 | // |
| mjr | 86:e30a1f60f783 | 128 | // - a live image integrating light on the photo receptors on the sensor |
| mjr | 86:e30a1f60f783 | 129 | // - the prior image being held in the sensor's shift register and being |
| mjr | 86:e30a1f60f783 | 130 | // transferred via DMA into one of our buffers |
| mjr | 86:e30a1f60f783 | 131 | // - the image before that in our other buffer |
| mjr | 86:e30a1f60f783 | 132 | // |
| mjr | 86:e30a1f60f783 | 133 | // Integration of a live image starts when we begin the transfer of the |
| mjr | 86:e30a1f60f783 | 134 | // prior image. Integration ends when we start the next transfer after |
| mjr | 86:e30a1f60f783 | 135 | // that. So the total integration time, which is to say the exposure |
| mjr | 86:e30a1f60f783 | 136 | // time, is the time between consecutive transfer starts. It's important |
| mjr | 86:e30a1f60f783 | 137 | // for this time to be consistent from image to image, because that |
| mjr | 86:e30a1f60f783 | 138 | // determines the exposure level. We use polling from the main loop |
| mjr | 86:e30a1f60f783 | 139 | // to initiate new transfers, so the main loop is responsible for |
| mjr | 86:e30a1f60f783 | 140 | // polling frequently during the 2.5ms transfer period. It would be |
| mjr | 86:e30a1f60f783 | 141 | // more consistent if we did this in an interrupt handler instead, |
| mjr | 86:e30a1f60f783 | 142 | // but that would complicate things considerably since our image |
| mjr | 86:e30a1f60f783 | 143 | // analysis is too time-consuming to do in interrupt context. |
| mjr | 86:e30a1f60f783 | 144 | // |
| mjr | 87:8d35c74403af | 145 | class PlungerSensorTSL14xxLarge: public PlungerSensorImageInterfaceTSL14xx |
| mjr | 86:e30a1f60f783 | 146 | { |
| mjr | 86:e30a1f60f783 | 147 | public: |
| mjr | 87:8d35c74403af | 148 | PlungerSensorTSL14xxLarge(int nativePix, PinName si, PinName clock, PinName ao) |
| mjr | 87:8d35c74403af | 149 | : PlungerSensorImageInterfaceTSL14xx(nativePix, si, clock, ao) |
| mjr | 86:e30a1f60f783 | 150 | { |
| mjr | 86:e30a1f60f783 | 151 | } |
| mjr | 86:e30a1f60f783 | 152 | |
| mjr | 87:8d35c74403af | 153 | virtual void readPix(uint8_t* &pix, uint32_t &t, int axcTime) |
| mjr | 87:8d35c74403af | 154 | { |
| mjr | 86:e30a1f60f783 | 155 | // start reading the next pixel array (this waits for any DMA |
| mjr | 86:e30a1f60f783 | 156 | // transfer in progress to finish, ensuring a stable pixel buffer) |
| mjr | 86:e30a1f60f783 | 157 | sensor.startCapture(axcTime); |
| mjr | 86:e30a1f60f783 | 158 | |
| mjr | 86:e30a1f60f783 | 159 | // get the image array from the last capture |
| mjr | 87:8d35c74403af | 160 | sensor.getPix(pix, t); |
| mjr | 86:e30a1f60f783 | 161 | |
| mjr | 86:e30a1f60f783 | 162 | } |
| mjr | 86:e30a1f60f783 | 163 | }; |
| mjr | 86:e30a1f60f783 | 164 | |
| mjr | 86:e30a1f60f783 | 165 | // --------------------------------------------------------------------- |
| mjr | 86:e30a1f60f783 | 166 | // |
| mjr | 86:e30a1f60f783 | 167 | // Subclass for the small sensors, such as TSL1401CL (128 pixels). |
| mjr | 86:e30a1f60f783 | 168 | // |
| mjr | 86:e30a1f60f783 | 169 | // For the small sensors, we can't use the asynchronous transfer |
| mjr | 86:e30a1f60f783 | 170 | // scheme we use for the large sensors, because the transfer times |
| mjr | 86:e30a1f60f783 | 171 | // are so short that the main loop can't poll frequently enough to |
| mjr | 86:e30a1f60f783 | 172 | // maintain consistent exposure times. With the short transfer |
| mjr | 86:e30a1f60f783 | 173 | // times, though, we don't need to do them asynchronously. |
| mjr | 86:e30a1f60f783 | 174 | // |
| mjr | 86:e30a1f60f783 | 175 | // Instead, each time we want to read the sensor, we do the whole |
| mjr | 86:e30a1f60f783 | 176 | // integration and transfer synchronously, so that we can precisly |
| mjr | 86:e30a1f60f783 | 177 | // control the total time. This requires two transfers. First, |
| mjr | 86:e30a1f60f783 | 178 | // we start a transfer in order to set the exact starting time of |
| mjr | 86:e30a1f60f783 | 179 | // an integration period: call it period A. We wait for the |
| mjr | 86:e30a1f60f783 | 180 | // transfer to complete, which fills a buffer with the prior |
| mjr | 86:e30a1f60f783 | 181 | // integration period's pixels. We don't want those pixels, |
| mjr | 86:e30a1f60f783 | 182 | // because they started before we got here and thus we can't |
| mjr | 86:e30a1f60f783 | 183 | // control how long they were integrating. So we discard that |
| mjr | 86:e30a1f60f783 | 184 | // buffer and start a new transfer. This starts period B while |
| mjr | 86:e30a1f60f783 | 185 | // transferring period A's pixels into a DMA buffer. We want |
| mjr | 86:e30a1f60f783 | 186 | // those period A pixels, so we wait for this transfer to finish. |
| mjr | 86:e30a1f60f783 | 187 | // |
| mjr | 87:8d35c74403af | 188 | class PlungerSensorTSL14xxSmall: public PlungerSensorImageInterfaceTSL14xx |
| mjr | 86:e30a1f60f783 | 189 | { |
| mjr | 86:e30a1f60f783 | 190 | public: |
| mjr | 87:8d35c74403af | 191 | PlungerSensorTSL14xxSmall(int nativePix, PinName si, PinName clock, PinName ao) |
| mjr | 87:8d35c74403af | 192 | : PlungerSensorImageInterfaceTSL14xx(nativePix, si, clock, ao) |
| mjr | 86:e30a1f60f783 | 193 | { |
| mjr | 86:e30a1f60f783 | 194 | } |
| mjr | 86:e30a1f60f783 | 195 | |
| mjr | 87:8d35c74403af | 196 | // read the image |
| mjr | 87:8d35c74403af | 197 | virtual void readPix(uint8_t* &pix, uint32_t &t, int axcTime) |
| mjr | 86:e30a1f60f783 | 198 | { |
| mjr | 86:e30a1f60f783 | 199 | // Clear the sensor. This sends a HOLD/SI pulse to the sensor, |
| mjr | 86:e30a1f60f783 | 200 | // which ends the current integration period, starts a new one |
| mjr | 86:e30a1f60f783 | 201 | // (call the new one period A) right now, and clocks out all |
| mjr | 86:e30a1f60f783 | 202 | // of the pixels from the old cycle. We want to discard these |
| mjr | 86:e30a1f60f783 | 203 | // pixels because they've been integrating from some time in |
| mjr | 86:e30a1f60f783 | 204 | // the past, so we can't control the exact timing of that cycle. |
| mjr | 86:e30a1f60f783 | 205 | // Clearing the sensor clocks the pixels out without waiting to |
| mjr | 86:e30a1f60f783 | 206 | // read them on DMA, so it's much faster than a regular transfer |
| mjr | 86:e30a1f60f783 | 207 | // and thus gives us the shortest possible base integration time |
| mjr | 86:e30a1f60f783 | 208 | // for period A. |
| mjr | 86:e30a1f60f783 | 209 | sensor.clear(); |
| mjr | 86:e30a1f60f783 | 210 | |
| mjr | 86:e30a1f60f783 | 211 | // Start a real transfer. This ends integration period A (the |
| mjr | 86:e30a1f60f783 | 212 | // one we just started), starts a new integration period B, and |
| mjr | 86:e30a1f60f783 | 213 | // begins transferring period A's pixels to memory via DMA. We |
| mjr | 86:e30a1f60f783 | 214 | // use the auto-exposure time to get the optimal exposure. |
| mjr | 86:e30a1f60f783 | 215 | sensor.startCapture(axcTime); |
| mjr | 86:e30a1f60f783 | 216 | |
| mjr | 86:e30a1f60f783 | 217 | // wait for the period A pixel transfer to finish, and grab |
| mjr | 86:e30a1f60f783 | 218 | // its pixels |
| mjr | 87:8d35c74403af | 219 | sensor.waitPix(pix, t); |
| mjr | 86:e30a1f60f783 | 220 | } |
| mjr | 86:e30a1f60f783 | 221 | }; |
| mjr | 86:e30a1f60f783 | 222 | |
| mjr | 86:e30a1f60f783 | 223 | |
| mjr | 87:8d35c74403af | 224 | // ------------------------------------------------------------------------- |
| mjr | 87:8d35c74403af | 225 | // |
| mjr | 87:8d35c74403af | 226 | // Concrete TSL14xx sensor types |
| mjr | 87:8d35c74403af | 227 | // |
| mjr | 87:8d35c74403af | 228 | |
| mjr | 87:8d35c74403af | 229 | |
| mjr | 87:8d35c74403af | 230 | // TSL1410R sensor - edge detection sensor |
| mjr | 87:8d35c74403af | 231 | class PlungerSensorTSL1410R: public PlungerSensorEdgePos |
| mjr | 87:8d35c74403af | 232 | { |
| mjr | 87:8d35c74403af | 233 | public: |
| mjr | 87:8d35c74403af | 234 | PlungerSensorTSL1410R(PinName si, PinName clock, PinName ao) |
| mjr | 87:8d35c74403af | 235 | : PlungerSensorEdgePos(sensor, 1280), sensor(1280, si, clock, ao) |
| mjr | 87:8d35c74403af | 236 | { |
| mjr | 87:8d35c74403af | 237 | } |
| mjr | 87:8d35c74403af | 238 | |
| mjr | 87:8d35c74403af | 239 | protected: |
| mjr | 87:8d35c74403af | 240 | PlungerSensorTSL14xxLarge sensor; |
| mjr | 87:8d35c74403af | 241 | }; |
| mjr | 87:8d35c74403af | 242 | |
| mjr | 87:8d35c74403af | 243 | // TSL1412R - edge detection sensor |
| mjr | 87:8d35c74403af | 244 | class PlungerSensorTSL1412R: public PlungerSensorEdgePos |
| mjr | 87:8d35c74403af | 245 | { |
| mjr | 87:8d35c74403af | 246 | public: |
| mjr | 87:8d35c74403af | 247 | PlungerSensorTSL1412R(PinName si, PinName clock, PinName ao) |
| mjr | 87:8d35c74403af | 248 | : PlungerSensorEdgePos(sensor, 1536), sensor(1536, si, clock, ao) |
| mjr | 87:8d35c74403af | 249 | { |
| mjr | 87:8d35c74403af | 250 | } |
| mjr | 87:8d35c74403af | 251 | |
| mjr | 87:8d35c74403af | 252 | protected: |
| mjr | 87:8d35c74403af | 253 | PlungerSensorTSL14xxLarge sensor; |
| mjr | 87:8d35c74403af | 254 | }; |
| mjr | 87:8d35c74403af | 255 | |
| mjr | 87:8d35c74403af | 256 | // TSL1401CL - bar code sensor |
| mjr | 87:8d35c74403af | 257 | class PlungerSensorTSL1401CL: public PlungerSensorBarCode<7, 0, 1, 16> |
| mjr | 87:8d35c74403af | 258 | { |
| mjr | 87:8d35c74403af | 259 | public: |
| mjr | 87:8d35c74403af | 260 | PlungerSensorTSL1401CL(PinName si, PinName clock, PinName ao) |
| mjr | 87:8d35c74403af | 261 | : PlungerSensorBarCode(sensor, 128), sensor(128, si, clock, ao) |
| mjr | 87:8d35c74403af | 262 | { |
| mjr | 87:8d35c74403af | 263 | } |
| mjr | 87:8d35c74403af | 264 | |
| mjr | 87:8d35c74403af | 265 | protected: |
| mjr | 87:8d35c74403af | 266 | PlungerSensorTSL14xxSmall sensor; |
| mjr | 87:8d35c74403af | 267 | }; |
| mjr | 87:8d35c74403af | 268 | |
| mjr | 87:8d35c74403af | 269 | |
| mjr | 82:4f6209cb5c33 | 270 | #endif |