Commits

Cisco Zabala  committed 09d214c

restructured files to XZLab standard.

  • Participants
  • Parent commits 57cef82

Comments (0)

Files changed (73)

File LICENSE

-                                 Apache License
-                           Version 2.0, January 2004
-                        http://www.apache.org/licenses/
-
-   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-   1. Definitions.
-
-      "License" shall mean the terms and conditions for use, reproduction,
-      and distribution as defined by Sections 1 through 9 of this document.
-
-      "Licensor" shall mean the copyright owner or entity authorized by
-      the copyright owner that is granting the License.
-
-      "Legal Entity" shall mean the union of the acting entity and all
-      other entities that control, are controlled by, or are under common
-      control with that entity. For the purposes of this definition,
-      "control" means (i) the power, direct or indirect, to cause the
-      direction or management of such entity, whether by contract or
-      otherwise, or (ii) ownership of fifty percent (50%) or more of the
-      outstanding shares, or (iii) beneficial ownership of such entity.
-
-      "You" (or "Your") shall mean an individual or Legal Entity
-      exercising permissions granted by this License.
-
-      "Source" form shall mean the preferred form for making modifications,
-      including but not limited to software source code, documentation
-      source, and configuration files.
-
-      "Object" form shall mean any form resulting from mechanical
-      transformation or translation of a Source form, including but
-      not limited to compiled object code, generated documentation,
-      and conversions to other media types.
-
-      "Work" shall mean the work of authorship, whether in Source or
-      Object form, made available under the License, as indicated by a
-      copyright notice that is included in or attached to the work
-      (an example is provided in the Appendix below).
-
-      "Derivative Works" shall mean any work, whether in Source or Object
-      form, that is based on (or derived from) the Work and for which the
-      editorial revisions, annotations, elaborations, or other modifications
-      represent, as a whole, an original work of authorship. For the purposes
-      of this License, Derivative Works shall not include works that remain
-      separable from, or merely link (or bind by name) to the interfaces of,
-      the Work and Derivative Works thereof.
-
-      "Contribution" shall mean any work of authorship, including
-      the original version of the Work and any modifications or additions
-      to that Work or Derivative Works thereof, that is intentionally
-      submitted to Licensor for inclusion in the Work by the copyright owner
-      or by an individual or Legal Entity authorized to submit on behalf of
-      the copyright owner. For the purposes of this definition, "submitted"
-      means any form of electronic, verbal, or written communication sent
-      to the Licensor or its representatives, including but not limited to
-      communication on electronic mailing lists, source code control systems,
-      and issue tracking systems that are managed by, or on behalf of, the
-      Licensor for the purpose of discussing and improving the Work, but
-      excluding communication that is conspicuously marked or otherwise
-      designated in writing by the copyright owner as "Not a Contribution."
-
-      "Contributor" shall mean Licensor and any individual or Legal Entity
-      on behalf of whom a Contribution has been received by Licensor and
-      subsequently incorporated within the Work.
-
-   2. Grant of Copyright License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      copyright license to reproduce, prepare Derivative Works of,
-      publicly display, publicly perform, sublicense, and distribute the
-      Work and such Derivative Works in Source or Object form.
-
-   3. Grant of Patent License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      (except as stated in this section) patent license to make, have made,
-      use, offer to sell, sell, import, and otherwise transfer the Work,
-      where such license applies only to those patent claims licensable
-      by such Contributor that are necessarily infringed by their
-      Contribution(s) alone or by combination of their Contribution(s)
-      with the Work to which such Contribution(s) was submitted. If You
-      institute patent litigation against any entity (including a
-      cross-claim or counterclaim in a lawsuit) alleging that the Work
-      or a Contribution incorporated within the Work constitutes direct
-      or contributory patent infringement, then any patent licenses
-      granted to You under this License for that Work shall terminate
-      as of the date such litigation is filed.
-
-   4. Redistribution. You may reproduce and distribute copies of the
-      Work or Derivative Works thereof in any medium, with or without
-      modifications, and in Source or Object form, provided that You
-      meet the following conditions:
-
-      (a) You must give any other recipients of the Work or
-          Derivative Works a copy of this License; and
-
-      (b) You must cause any modified files to carry prominent notices
-          stating that You changed the files; and
-
-      (c) You must retain, in the Source form of any Derivative Works
-          that You distribute, all copyright, patent, trademark, and
-          attribution notices from the Source form of the Work,
-          excluding those notices that do not pertain to any part of
-          the Derivative Works; and
-
-      (d) If the Work includes a "NOTICE" text file as part of its
-          distribution, then any Derivative Works that You distribute must
-          include a readable copy of the attribution notices contained
-          within such NOTICE file, excluding those notices that do not
-          pertain to any part of the Derivative Works, in at least one
-          of the following places: within a NOTICE text file distributed
-          as part of the Derivative Works; within the Source form or
-          documentation, if provided along with the Derivative Works; or,
-          within a display generated by the Derivative Works, if and
-          wherever such third-party notices normally appear. The contents
-          of the NOTICE file are for informational purposes only and
-          do not modify the License. You may add Your own attribution
-          notices within Derivative Works that You distribute, alongside
-          or as an addendum to the NOTICE text from the Work, provided
-          that such additional attribution notices cannot be construed
-          as modifying the License.
-
-      You may add Your own copyright statement to Your modifications and
-      may provide additional or different license terms and conditions
-      for use, reproduction, or distribution of Your modifications, or
-      for any such Derivative Works as a whole, provided Your use,
-      reproduction, and distribution of the Work otherwise complies with
-      the conditions stated in this License.
-
-   5. Submission of Contributions. Unless You explicitly state otherwise,
-      any Contribution intentionally submitted for inclusion in the Work
-      by You to the Licensor shall be under the terms and conditions of
-      this License, without any additional terms or conditions.
-      Notwithstanding the above, nothing herein shall supersede or modify
-      the terms of any separate license agreement you may have executed
-      with Licensor regarding such Contributions.
-
-   6. Trademarks. This License does not grant permission to use the trade
-      names, trademarks, service marks, or product names of the Licensor,
-      except as required for reasonable and customary use in describing the
-      origin of the Work and reproducing the content of the NOTICE file.
-
-   7. Disclaimer of Warranty. Unless required by applicable law or
-      agreed to in writing, Licensor provides the Work (and each
-      Contributor provides its Contributions) on an "AS IS" BASIS,
-      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-      implied, including, without limitation, any warranties or conditions
-      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-      PARTICULAR PURPOSE. You are solely responsible for determining the
-      appropriateness of using or redistributing the Work and assume any
-      risks associated with Your exercise of permissions under this License.
-
-   8. Limitation of Liability. In no event and under no legal theory,
-      whether in tort (including negligence), contract, or otherwise,
-      unless required by applicable law (such as deliberate and grossly
-      negligent acts) or agreed to in writing, shall any Contributor be
-      liable to You for damages, including any direct, indirect, special,
-      incidental, or consequential damages of any character arising as a
-      result of this License or out of the use or inability to use the
-      Work (including but not limited to damages for loss of goodwill,
-      work stoppage, computer failure or malfunction, or any and all
-      other commercial damages or losses), even if such Contributor
-      has been advised of the possibility of such damages.
-
-   9. Accepting Warranty or Additional Liability. While redistributing
-      the Work or Derivative Works thereof, You may choose to offer,
-      and charge a fee for, acceptance of support, warranty, indemnity,
-      or other liability obligations and/or rights consistent with this
-      License. However, in accepting such obligations, You may act only
-      on Your own behalf and on Your sole responsibility, not on behalf
-      of any other Contributor, and only if You agree to indemnify,
-      defend, and hold each Contributor harmless for any liability
-      incurred by, or claims asserted against, such Contributor by reason
-      of your accepting any such warranty or additional liability.
-
-   END OF TERMS AND CONDITIONS
-
-   APPENDIX: How to apply the Apache License to your work.
-
-      To apply the Apache License to your work, attach the following
-      boilerplate notice, with the fields enclosed by brackets "[]"
-      replaced with your own identifying information. (Don't include
-      the brackets!)  The text should be enclosed in the appropriate
-      comment syntax for the file format. We also recommend that a
-      file or class name and description of purpose be included on the
-      same "printed page" as the copyright notice for easier
-      identification within third-party archives.
-
-   Copyright [yyyy] [name of copyright owner]
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
-
-
-
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+
+

File expresso_firmware/DerivFilter.cpp

+// DerivFilter.cpp
+//
+// Implements a simple central differences derivative filter. 
+//
+// Will Dickson, IO Rodeo Inc.
+//
+#include "DerivFilter.h"
+#include <string.h>
+#include "constants.h"
+
+DerivFilter::DerivFilter(uint16 _windowLen, uint8 _scale, uint8 _shift) {
+    shift = _shift;
+    scale = _scale;
+    setWindowLen(_windowLen);
+}
+
+void DerivFilter::setWindowLen(uint16 _windowLen) {
+    windowLen = _windowLen;
+}
+
+void DerivFilter::setScale(uint8 _scale) {
+    scale = _scale;
+}
+
+void DerivFilter::setShift(uint8 _shift) {
+    shift = _shift;
+}
+
+void DerivFilter::setThreshold(uint16 x_val, uint16 y_val){
+    threshold = {x_val, y_val};
+}
+
+uint16* DerivFilter::getThreshold() {
+    return threshold;
+}
+
+void DerivFilter::apply(uint8 *data, uint16 len) {
+    int32 kNeg;
+    int32 kPos;
+    uint16 maxValueX = 0;
+    uint16 maxValueY = 0;
+    uint16 n = windowLen/2;
+    uint8 dataFilt[len];
+    float value;
+    float scaleFact = (2*(float)scale)/((float)windowLen - 1.0);
+
+    for (uint16 i=0; i<len; i++) {
+        kNeg = i-n;
+        kPos = i+n;
+        if (kNeg < 0) {
+            value = (float) shift;
+        }
+        else if (kPos >=len) {
+            value = (float) shift;
+        }
+        else {
+            value = (scaleFact*(float)data[kPos] + ((float) shift - (float)scaleFact*data[kNeg]));
+        }
+        dataFilt[i] = (uint8) value;
+
+        // Search for the max if the index is in the searchable region.
+        if (i < constants::maxSearchPixel) {
+            if (value > maxValueY) {
+                maxValueY = (uint16) value;
+                maxValueX = i;
+            }
+        }
+    }
+    setThreshold(maxValueX,maxValueY);
+    memcpy(data,dataFilt,len*sizeof(uint8));
+}

File expresso_firmware/DerivFilter.h

+// DerivFilter.h
+//
+// Implements a simple central differences derivative filter. 
+//
+// Will Dickson, IO Rodeo Inc.
+//
+#ifndef _DERIV_FILTER_H_ 
+#define _DERIV_FILTER_H_
+#include "WProgram.h"
+
+class DerivFilter {
+    public:
+        DerivFilter(uint16 _windowLen=5, uint8 _scale=10, uint8 _shift=128);
+        void apply(uint8 *data, uint16 len);
+        void setWindowLen(uint16 _windowLen);
+        void setScale(uint8 _scale);
+        void setShift(uint8 _shift);
+        void setThreshold(uint16 x_val,uint16 y_val);
+        uint16* getThreshold();
+    private:
+        uint16 threshold[2];
+        uint8 shift;
+        uint8 scale;
+        uint16 windowLen;
+};
+
+#endif

File expresso_firmware/LevelDetector.cpp

+// LevelDetector.h
+//
+// Implements a simple fluid level detector for the capillary sensor. 
+//
+// Will Dickson, IO Rodeo Inc.
+//
+#include "LevelDetector.h"
+#include "WProgram.h"
+#include "Streaming.h"
+#include <string.h>
+#include <math.h>
+#include "median.h"
+#include <stdarg.h>
+
+const float levelNotFound = -1.0;
+const float levelChanError = -2.0;
+
+LevelDetector::LevelDetector() {
+    refLevelSampleNum =constants::refLevelSampleNum;
+    maxSearchPixel = constants::maxSearchPixel;
+    reverseBuffer = constants::reverseBuffer;
+    peakFitTol = constants::peakFitTol;
+    medianFilter.setWindowLen(constants::medianFilterWindow);
+    derivFilter.setWindowLen(constants::derivFilterWindow);
+    derivFilter.setScale(constants::derivFilterScale);
+    derivFilter.setShift(constants::derivFilterShift);
+}
+
+float LevelDetector::findLevel(uint8 *dataBuffer, int32* a, int32* b, float *lastLevel) {
+    float level;
+    uint16* threshold;
+
+    level = findLevel(dataBuffer, lastLevel);
+    *a = indNeg;
+    *b = indPos;
+    //threshold = derivFilter.getThreshold();
+    //*a = (int32) threshold[0];
+    //*a = (int32) findRefLevel();
+    //*b = (int32) threshold[1];
+    return level;
+}
+
+float LevelDetector::findLevel(uint8 *dataBuffer,float *lastLevel) {
+    bool found;
+    int32 indBegin;
+    uint8 refLevel;
+    float midPoint;
+    float indDelta;
+    float peakFit;
+    float level;
+    uint16* threshold;
+    uint8 thresholdDelta;
+
+    // Copy data buffer and apply median and derivative filters
+    if (!reverseBuffer) {
+        memcpy(workBuffer,dataBuffer,numPixel*sizeof(uint8));
+    }
+    else {
+        for (uint16 i=0; i<numPixel; i++) {
+            workBuffer[i] = *(dataBuffer+numPixel-i-1);
+        }
+    }
+    medianFilter.apply(workBuffer,numPixel);
+    derivFilter.apply(workBuffer,numPixel);
+    lowpassFilter.apply(workBuffer,numPixel);
+
+    // The derivFilter object returns the x,y values
+    // of the maximum within the searchable region of the 
+    // data.  Note: whereas the deriFilter is applied to the
+    // entire workBuffer, the search for a max value only 
+    // takes place within the searchable region. 
+    threshold = derivFilter.getThreshold();
+
+    upperThreshold = (uint8) threshold[1];
+    lowerThreshold = (uint8) (constants::lowerThresholdFraction*threshold[1]);
+
+    // Find reference level
+    refLevel = constants::refLevel;
+    
+    // Compare the upperThreshold to the (baseline) refLevel.
+    // If below some arbitrary threshold, cannot find level.
+    //
+    // Adding hysteresis to this check.
+    //
+    //if (*lastLevel == levelNotFound) {
+    if (*lastLevel < 0) {
+        thresholdDelta = constants::thresholdDeltaHigh;
+    } else {
+        thresholdDelta = constants::thresholdDeltaLow;
+    }
+
+    if ((upperThreshold - refLevel) < thresholdDelta) {
+        return levelNotFound;
+    }
+
+    // Index corresponds to the x value of the upperThreshold as 
+    // found in the derivative data.
+    indBegin = threshold[0];
+
+    // Search backward until the first data point less than the lower 
+    // threshold.
+    indNeg = indBegin;
+    found = false;
+    while ((!found) && (indNeg >= 0)) {
+        if (workBuffer[indNeg] < (lowerThreshold)) {
+            found = true;
+        }
+        else {
+            indNeg--;
+        }
+    }
+    if (!found) {
+        return levelNotFound-1;
+    }
+
+    // Search forward until the first data point less than the lower 
+    // threshold.
+    indPos = indBegin;
+    found = false;
+    while ((!found) && (indPos < (constants::NUM_PIXEL-1))) {
+        if (workBuffer[indPos] < (lowerThreshold)) {
+            found = true;
+        }
+        else {
+            indPos++;
+        }
+    }
+    if (!found) {
+        return levelNotFound-2;
+    }
+
+    // Symmetry check for indexes
+    //if (abs(indPos-2*indBegin+indNeg)>constants::thresholdSymm) {
+        //return levelNotFound-3;    
+    //}
+
+    // Compute the mid point, delta and fit the peak with a quadratic
+    midPoint = 0.5*(float)(indNeg) + 0.5*(float)(indPos);
+    //indDelta = (float) (indPos - indNeg);
+    //peakFit = findPeak((uint16) indNeg, workBuffer+indNeg, indPos-indNeg+1);
+
+    //// Only use peakfit if it is close enough to the mid point. 
+    //if ( fabs(peakFit - midPoint) <= peakFitTol*indDelta) {
+        //level = peakFit;
+    //}
+    //else {
+        //level = midPoint;
+    //}
+    //return level; 
+    return midPoint; 
+}
+
+uint8 LevelDetector::findRefLevel() {
+    uint8 refLevel;
+    uint8 sampleData[refLevelSampleNum];
+    memcpy(sampleData,workBuffer,refLevelSampleNum*sizeof(uint8));
+    refLevel = getMedian(sampleData,refLevelSampleNum);
+    return refLevel;
+}
+
+
+float findPeak(uint16 x0, uint8 *y, uint16 num) {
+    uint8 x[num];
+    float a,b,c;
+    float peak;
+    for (uint16 i=0; i<num; i++) {
+        x[i] = x0 + i;
+    }
+    fitQuadratic(x,y,num,a,b,c);
+    peak = -b/(2*a);
+    return peak;
+}
+
+
+
+void fitQuadratic( uint8 *x, uint8 *y, uint16 num, float &a, float &b, float &c) 
+{
+    // Fits the quadratic curve y = a*x^2 + b*x + x to the data given in the arrays
+    // x[] and y[]
+    //
+    // Solution to fit is given by linear system
+    //
+    // [ A4  A3  A2 ] [ a ]    [ B2 ] 
+    // [ A3  A2  A1 ] [ b ]  = [ B1 ] 
+    // [ A2  A1  A0 ] [ c ]    [ B0 ]
+    //
+    // where 
+    //
+    // Aj = sum(x[i]^j)
+    // Bj = sum(y[i]*x[i]^j)
+    //
+    // The solution is given by Cramer's rule in terms of the determinates
+    //
+    // a = Da/D, b = Db/D, c = Dc/c
+    //
+
+    float temp;
+    float A[5];
+    float B[3];
+    float D, Da, Db, Dc;
+
+    // Create the A and B values 
+    for (uint16 i=0; i<5; i++) {
+        A[i] = 0.0;
+    }
+    for (uint16 i=0; i<3; i++) {
+        B[i] = 0.0;
+    }
+    for (uint16 i=0; i<num; i++) {
+        temp = 1.0;
+        for (uint16 j=0; j<5; j++) {
+            A[j] += temp;
+            if (j<3) {
+                B[j] += temp*((float) *(y+i));
+            }
+            temp *= (float) *(x+i);
+        }
+    }
+    // Find D 
+    D =  A[4]*( A[2]*A[0] - A[1]*A[1] ); 
+    D -= A[3]*( A[3]*A[0] - A[1]*A[2] ); 
+    D += A[2]*( A[3]*A[1] - A[2]*A[2] );
+
+    // Find Da
+    Da =  B[2]*( A[2]*A[0] - A[1]*A[1] ); 
+    Da -= B[1]*( A[3]*A[0] - A[1]*A[2] );
+    Da += B[0]*( A[3]*A[1] - A[2]*A[2] );
+   
+    // Find Db
+    Db =  A[4]*( B[1]*A[0] - B[0]*A[1] ); 
+    Db -= A[3]*( B[2]*A[0] - B[0]*A[2] ); 
+    Db += A[2]*( B[2]*A[1] - B[1]*A[2] );
+
+    // Find Dc
+    Dc =  A[4]*( A[2]*B[0] - A[1]*B[1] ); 
+    Dc -= A[3]*( A[3]*B[0] - A[1]*B[2] ); 
+    Dc -= A[2]*( A[3]*B[1] - A[2]*B[2] );  
+
+    // Compute a, b and c for parabolic find.
+    a = Da/D;
+    b = Db/D;
+    c = Dc/D;
+}
+

File expresso_firmware/LevelDetector.h

+// LevelDetector.h
+//
+// Implements a simple fluid level detector for the capillary sensor. 
+//
+// Will Dickson, IO Rodeo Inc.
+//
+#ifndef _LEVEL_DETECTOR_H_
+#define _LEVEL_DETECTOR_H_
+#include "WProgram.h"
+#include "constants.h"
+#include "MedianFilter.h"
+#include "DerivFilter.h"
+#include "LowpassFilter.h"
+
+const extern float levelNotFound;
+const extern float levelChanError;
+
+class LevelDetector {
+    public:
+        static const uint16 numPixel = constants::NUM_PIXEL;
+        bool reverseBuffer;
+        uint8 workBuffer[constants::NUM_PIXEL];
+        LevelDetector();
+        float findLevel(uint8 *data, float *lastLevel);
+        float findLevel(uint8 *data, int32* a, int32* b, float *lastLevel);
+        int32 indNeg;
+        int32 indPos;
+    private:
+        uint8 lowerThreshold;
+        uint8 upperThreshold;
+        uint16 refLevelSampleNum;
+        uint16 maxSearchPixel;
+        float peakFitTol;
+        MedianFilter medianFilter;
+        DerivFilter derivFilter;
+        LowpassFilter lowpassFilter;
+        uint8 findRefLevel();
+};
+
+float findPeak(uint16 x0, uint8 *y, uint16 num);
+void fitQuadratic(uint8 *x, uint8 *y, uint16 num, float &a, float &b, float &c); 
+
+#endif

File expresso_firmware/LowpassFilter.cpp

+// LowpassFilter.h
+//
+// The LowpassFilter class provides a simple implementation of a lowpass filter. 
+//
+// Cisco Zabala, IO Rodeo.
+//
+#include "LowpassFilter.h"
+#include <string.h>
+
+LowpassFilter::LowpassFilter() {
+    _cutoff = constants::cutoff_freq;
+    _t_last = 0;
+}
+
+void LowpassFilter::apply(uint8* data, uint16 len) {
+    uint8 dataFilt[len];  
+    float tau;
+    float alpha;
+    int32 t;
+    int32 dt;
+
+    t = millis()*1000;
+    dt = t-_t_last;
+    _t_last = t;
+
+    dataFilt[0] = data[0];
+    for (uint16 i=1; i<len; i++) {
+        tau = 1.0/(2*M_PI*_cutoff);
+        alpha = dt/(dt  + tau);
+        _value = (1-alpha)*_value + alpha*data[i];
+        dataFilt[i] = (uint8) _value;
+    }
+    memcpy(data,dataFilt, len*sizeof(uint8));
+
+}
+
+void setCutoff(int32 _cutoff) {
+
+}

File expresso_firmware/LowpassFilter.h

+// LowpassFilter.h
+//
+// The LowpassFilter class provides a simple implementation of a lowpass filter. 
+//
+// Cisco Zabala, IO Rodeo.
+//
+#define _USE_MATH_DEFINES
+#ifndef _LOWPASS_FILTER_H_
+#define _LOWPASS_FILTER_H_
+#include "WProgram.h"
+#include "constants.h"
+
+class LowpassFilter {
+    public:
+        LowpassFilter();
+        void apply(uint8 *data, uint16 len);
+        void setCutoff(int32 _cutoff);
+    private:
+        int32 _cutoff;
+        uint8 _value;
+        int32 _t_last;
+};
+
+#endif

File expresso_firmware/MedianFilter.cpp

+// MedianFilter.h
+//
+// The MedianFilter class provides a simple implementatin of a 1D median 
+// filter. 
+//
+// Will Dickson, IO Rodeo.
+//
+#include "MedianFilter.h"
+#include <string.h>
+#include "median.h"
+
+MedianFilter::MedianFilter(uint16 _windowLen) {
+    setWindowLen(_windowLen);
+}
+
+void MedianFilter::setWindowLen(uint16 _windowLen) {
+    if (_windowLen%2==0) {
+        _windowLen += 1;
+    }
+    windowLen = _windowLen;
+}
+
+
+void MedianFilter::apply(uint8* data, uint16 len) {
+    uint8 median;
+    uint8 dataFilt[len];
+    uint8 windowData[windowLen];
+
+    for (uint16 i=0; i<len; i++) {
+        getWindowData(windowData,data,len,i);
+        median = getMedian(windowData,windowLen);
+        dataFilt[i] = median;
+    }
+    memcpy(data,dataFilt, len*sizeof(uint8));
+}
+
+void MedianFilter::getWindowData(uint8 *windowData, uint8 *data, uint16 len, uint16 i) {
+
+    int32 k;
+    uint16 n=windowLen/2;
+
+    for (uint16 j=0; j<windowLen; j++) {
+        // Compute window index in data array. 
+        k = i + j - n;
+        k = k < 0 ? 0 : k;
+        k = k>=len ? (len-1) : k;
+        *(windowData+j) = *(data+k);
+    }
+}

File expresso_firmware/MedianFilter.h

+// MedianFilter.h
+//
+// The MedianFilter class provides a simple implementatin of a 1D median 
+// filter. 
+//
+// Will Dickson, IO Rodeo.
+//
+#ifndef _MEDIAN_FILTER_H_
+#define _MEDIAN_FILTER_H_
+#include "WProgram.h"
+
+class MedianFilter {
+    public:
+        MedianFilter(uint16 _windowLen=5);
+        void apply(uint8 *data, uint16 len);
+        void setWindowLen(uint16 _windowLen);
+    private:
+        uint16 windowLen;
+        void getWindowData(uint8 *windowData, uint8 *data, uint16 len, uint16 i);
+};
+
+#endif

File expresso_firmware/MessageHandler.cpp

+#include "MessageHandler.h"
+
+// Serial Commands ids
+const int cmdSetMode = 0;
+const int cmdGetMode = 1;
+const int cmdGetChannel = 2;
+const int cmdGetLevel = 3;
+const int cmdGetLevels = 4;
+const int cmdGetPixelData = 5;
+const int cmdGetWorkingBuffer = 6;
+const int cmdGetDeviceId = 7;
+const int cmdGetDeviceNumber =8;
+const int cmdSetDeviceNumber = 9;
+const int cmdUnSetNormConst = 10;
+const int cmdSetNormConstFromBuffer = 11;
+const int cmdSetNormConstFromFlash = 12;
+const int cmdSetChannel = 13;
+const int cmdSaveNormConstToFlash=14;
+const int cmdGetBoundData = 15;
+
+// Serial Response ids 
+const int rspSuccess = 0;
+const int rspError = -1;
+const uint8 pixelSendChunk = 64;
+
+void MessageHandler::processMsg() {
+    if(SerialUSB.isConnected() && (SerialUSB.getDTR() || SerialUSB.getRTS())) {
+        while (SerialUSB.available() > 0) {
+            process(SerialUSB.read());
+            if (messageReady()) {
+                msgSwitchYard();
+                reset();
+            }
+        }
+    }
+    return;
+}
+
+void MessageHandler::msgSwitchYard() {
+    int cmd;
+    cmd = readInt(0); 
+
+    switch (cmd) {
+
+        case cmdSetMode:
+            setMode();
+            break;
+
+        case cmdGetMode:
+            getMode();
+            break;
+
+        case cmdGetChannel:
+            getChannel();
+            break;
+
+        case cmdSetChannel:
+            setChannel();
+            break;
+
+        case cmdGetLevel:
+            getLevel();
+            break;
+
+        case cmdGetLevels:
+            getLevels();
+            break;
+
+        case cmdGetPixelData:
+            getPixelData();
+            break;
+
+        case cmdGetBoundData:
+            getBoundData();
+            break;
+
+        case cmdGetWorkingBuffer:
+            getWorkingBuffer();
+            break;
+
+        case cmdGetDeviceId:
+            getDeviceId();
+            break;
+
+        case cmdGetDeviceNumber:
+            getDeviceNumber();
+            break;
+
+        case cmdSetDeviceNumber:
+            setDeviceNumber();
+            break;
+
+        case cmdUnSetNormConst:
+            unSetNormConst();
+            break;
+
+        case cmdSetNormConstFromBuffer:
+            setNormConstFromBuffer();
+            break;
+
+        case cmdSetNormConstFromFlash:
+            setNormConstFromFlash();
+            break;
+
+        case cmdSaveNormConstToFlash:
+            saveNormConstToFlash();
+            break;
+
+        default:
+            SerialUSB << rspError << endl;
+            break;
+    }
+    return;
+}
+
+
+void MessageHandler::setMode() {
+    // Set the device's current operating mode
+    uint16 mode;
+    uint8 channel;
+    uint16 num = numberOfItems();
+    if (num == 2) {
+        mode = (uint16) readInt(1);
+        if (systemState.setMode(mode)) {
+            SerialUSB <<  rspSuccess << endl;
+            return;
+        }
+    }
+    if (num == 3) {
+        mode = (uint16) readInt(1);
+        channel = (uint8) readInt(2);
+        if (systemState.setMode(mode,channel)) {
+            SerialUSB <<  rspSuccess << endl;
+            return;
+        } 
+    } 
+    SerialUSB << rspError  << endl;
+    return;
+} 
+
+void MessageHandler::getMode() {
+    // Get the device's current operating mode.
+    uint16 mode = systemState.getMode();
+    SerialUSB << rspSuccess << " " << mode << endl;
+    return;
+}
+
+void MessageHandler::getChannel() {
+    // Get the device's "single channel mode" channel
+    uint8 chan = systemState.getChannel();
+    SerialUSB << rspSuccess << " " << chan << endl;
+    return;
+}
+
+void MessageHandler::setChannel() {
+    uint16 num = numberOfItems();
+    uint8 chan;
+    if (num == 2) {
+        chan = (uint8) readInt(1);
+        if (systemState.setChannel(chan)) {
+            SerialUSB << rspSuccess << endl;
+            return;
+        }
+    }
+    SerialUSB << rspError << endl;
+    return;
+}
+
+void MessageHandler::getLevel() {
+    uint8 chan;
+    float level;
+    if (numberOfItems() == 2) {
+        chan = (uint8) readInt(1);
+        level = systemState.getLevel(chan);
+        if (level) {
+            SerialUSB << rspSuccess << " " << level << endl;
+            return;
+        }
+    }
+    SerialUSB << rspError << endl;
+    return;
+}
+
+void MessageHandler::getLevels() {
+    // Get current capillary level measurements
+    SerialUSB << rspSuccess;
+    for (uint8 i=0; i<constants::NUM_AIN; i++) {
+        SerialUSB << " ";
+        SerialUSB << systemState.getLevel(i);
+    }
+    SerialUSB << endl;
+    return;
+}
+
+void MessageHandler::getBoundData() {
+    // When in "debug mode" returns the level and 
+    // the pixel intensity data for the current channel.
+    uint16 mode;
+    uint8 channel;
+    int32 a;
+    int32 b;
+
+    mode = systemState.getMode();
+    channel = systemState.getChannel();
+
+    if (mode == sysModeDebug) {
+        systemState.getBounds(channel,&a,&b);
+        SerialUSB << rspSuccess << " "; 
+        SerialUSB << systemState.getLevel(channel); 
+        SerialUSB << " " << a; 
+        SerialUSB << " " << b; 
+        SerialUSB << endl;
+
+        // Send pixel data as raw bytes
+        sendPixelData(channel);
+        return;
+    }
+    SerialUSB << rspError << endl;
+    return;
+}
+
+void MessageHandler::getPixelData() {
+    // When in "single channel OR debug mode" returns the level and 
+    // the pixel intensity data for the current channel.
+    uint16 mode;
+    uint8 channel;
+
+    mode = systemState.getMode();
+    channel = systemState.getChannel();
+
+    if ((mode == sysModeSingleChannel) || (mode == sysModeDebug)) {
+
+        SerialUSB << rspSuccess << " "; 
+        SerialUSB << systemState.getLevel(channel); 
+        SerialUSB << endl;
+
+        // Send pixel data as raw bytes
+        sendPixelData(channel);
+        return;
+    }
+    SerialUSB << rspError << endl;
+    return;
+}
+
+void MessageHandler::getWorkingBuffer() {
+    uint16 mode;
+    uint8 channel;
+
+    mode = systemState.getMode();
+    channel = systemState.getChannel();
+
+    if (mode == sysModeDebug) {
+
+        SerialUSB << rspSuccess << " "; 
+        SerialUSB << systemState.getLevel(channel); 
+        SerialUSB << endl;
+
+        // Send pixel data as raw bytes
+        sendWorkingBuffer();
+        return;
+    }
+    SerialUSB << rspError << endl;
+    return;
+}
+
+void MessageHandler::getDeviceId() {
+    SerialUSB << rspSuccess << " ";
+    SerialUSB << constants::deviceId << endl;
+    return;
+}
+
+void MessageHandler::getDeviceNumber() {
+    // NOT DONE
+    return;
+}
+
+void MessageHandler::setDeviceNumber() {
+    // NOT DONE
+    return;
+}
+
+void MessageHandler::unSetNormConst() {
+    uint8 chan;
+    if (numberOfItems()==2) {
+        chan = (uint8) readInt(1);
+        if (chan < linearArray.numAin) { 
+            linearArray.unSetNormConst(chan);
+            SerialUSB << rspSuccess << endl;
+            return;
+        }
+    }
+    SerialUSB << rspError << endl;
+    return;
+}
+
+void MessageHandler::setNormConstFromBuffer() {
+    uint8 chan;
+    if (numberOfItems()==2) {
+        chan = (uint8) readInt(1);
+        if (chan < linearArray.numAin) {
+            linearArray.setNormConstFromBuffer(chan);
+            SerialUSB << rspSuccess << endl;
+            return;
+        }
+    }
+    SerialUSB << rspError << endl;
+    return;
+}
+
+void MessageHandler::setNormConstFromFlash() {
+    uint8 chan;
+    if (numberOfItems()==2){
+        chan = (uint8) readInt(1);
+        if (chan < linearArray.numAin) {
+            linearArray.setNormConstFromFlash(chan);
+            SerialUSB << rspSuccess << endl;
+            return;
+        }
+    }
+    SerialUSB << rspError << endl;
+    return;
+}
+
+void MessageHandler::saveNormConstToFlash() {
+    uint8 chan;
+    if (numberOfItems()==2) {
+        chan = (uint8) readInt(1);
+        if (chan < linearArray.numAin) {
+            linearArray.saveNormConstToFlash(chan);
+            SerialUSB << rspSuccess << endl;
+            return;
+        }
+    }
+    SerialUSB << rspError << endl;
+    return;
+}
+
+void sendPixelData(uint8 channel) {
+    // Sends linear array pixel data for the given channel to the host PC. 
+    // Note, the sensor data is bit shifted by 4 to reduced it is size from 
+    // 12bit to 8bits.
+    
+    // 
+    
+    //
+    // Old style send - too slow when connected to windows machine
+    // --------------------------------------------------------------------
+    //uint16 n;
+    //uint8 pixelValue;
+
+    //for (uint16 i=0; i<linearArray.numPixel; i++) {
+    //    if (constants::reverseBuffer) {
+    //        n = linearArray.numPixel - i - 1;
+    //    }
+    //    else {
+    //        n = i;
+    //    }
+    //    pixelValue = linearArray.buffer[channel][n]; 
+    //    SerialUSB << _BYTE((char) pixelValue );
+    //}
+    //return;
+    //--------------------------------------------------------------------
+    
+    // New style send faster when connected to windows also good when connected 
+    // to linus. Note, currently doesn't handle buffer reverse, but do we care?
+    uint16 numSend;
+    numSend = linearArray.numPixel/pixelSendChunk;
+    for (uint16 i=0; i<numSend; i++) {
+        SerialUSB.write(
+                (linearArray.buffer[channel] + pixelSendChunk*i), 
+                pixelSendChunk
+                );
+    }
+}
+
+void sendWorkingBuffer() {
+    uint16 numSend;
+    numSend = linearArray.numPixel/pixelSendChunk;
+    for (uint16 i=0; i<numSend; i++) {
+        SerialUSB.write(
+                (systemState.detector.workBuffer + pixelSendChunk*i), 
+                pixelSendChunk
+                );
+    }
+}
+

File expresso_firmware/MessageHandler.h

+#ifndef _MESSAGE_HANDER_H_
+#define _MESSAGE_HANDER_H_
+#include <Streaming.h>
+#include <SerialReceiver.h>
+#include "constants.h"
+#include "SystemState.h"
+#include "TaosLinearArray.h"
+
+class MessageHandler : public SerialReceiver {
+    public:
+        void processMsg();
+        void msgSwitchYard();
+    private:
+        void setMode();
+        void getMode();
+        void setChannel();
+        void getChannel();
+        void getLevel();
+        void getLevels();
+        void getPixelData();
+        void getBoundData();
+        void getWorkingBuffer();
+        void getDeviceId();
+        void getDeviceNumber();
+        void setDeviceNumber();
+        void unSetNormConst();
+        void setNormConstFromBuffer();
+        void setNormConstFromFlash();
+        void saveNormConstToFlash();
+};
+
+void sendPixelData(uint8 chan);
+void sendWorkingBuffer();
+
+#endif

File expresso_firmware/SystemState.cpp

+#include "SystemState.h"
+
+// Operating modes
+const uint16 sysModeStopped = 0;
+const uint16 sysModeSingleChannel = 1;
+const uint16 sysModeAllChannels = 2;
+const uint16 sysModeDebug = 3;
+const uint16 numberOfModes = 4;
+
+SystemState::SystemState() {
+    mode = sysModeStopped;
+    channel = 0;
+    for (uint8 i=0; i<constants::NUM_AIN; i++) {
+        level[i] = levelNotFound; 
+        levelRaw[i] = levelNotFound;
+    }
+}
+
+bool SystemState::setMode(uint16 _mode) {
+    if (_mode < numberOfModes) {
+        mode = _mode;
+        return true;
+    }
+    else {
+        return false;
+    }
+}
+
+// Overloaded method for handling sysModeSingleChannel
+bool SystemState::setMode(uint16 _mode, uint8 _channel) {
+    if (!setMode(_mode)) {
+        return false;
+    }
+    if (!setChannel(_channel)) {
+        return false;
+    }
+    return true;
+}
+
+uint16 SystemState::getMode() {
+    return mode;
+}
+
+bool SystemState::setChannel(uint8 _channel) {
+    if (_channel < constants::NUM_AIN) {
+        channel = _channel;
+        return true;
+    }
+    else {
+        return false;
+    }
+}
+
+uint8 SystemState::getChannel() {
+    return channel;
+}
+
+float SystemState::getLevel(uint8 chan) {
+    if (chan < constants::NUM_AIN) {
+        return level[chan];
+    }
+    else {
+        return levelChanError;
+    }
+}
+
+void SystemState::setLevel(uint8 chan, float value) {
+    if (chan < constants::NUM_AIN) {
+        level[chan] = value;
+        //if (fabs(levelRaw[chan] - value) < constants::levelMaxChange) {
+            //level[chan] = value;
+        //}
+        //else {
+            //level[chan] = levelNotFound;
+        //}
+        //levelRaw[chan] = value;
+    }
+} 
+
+void SystemState::setBounds(uint8 chan, int32 a, int32 b) {
+    if (chan < constants::NUM_AIN) {
+        bounds[chan][0] = a;
+        bounds[chan][1] = b;
+    }
+} 
+
+void SystemState::getBounds(uint8 chan, int32* a, int32* b) {
+    if (chan < constants::NUM_AIN) {
+        *a = bounds[chan][0];
+        *b = bounds[chan][1];
+    }
+} 
+
+void SystemState::update() {
+    float levelTemp;
+    float lastLevel;
+    linearArray.readData();
+    switch (mode) {
+        case sysModeSingleChannel:
+            // Find level only for one channel
+            lastLevel = level[channel];
+            levelTemp = detector.findLevel(linearArray.buffer[channel],&lastLevel);
+            setLevel(channel, levelTemp); // special setter - handles levelMaxChange
+            break;
+        case sysModeAllChannels:
+            // Find level for all channels
+            for (uint8 i=0; i<constants::NUM_AIN; i++) {
+                lastLevel = level[i];
+                levelTemp = detector.findLevel(linearArray.buffer[i],&lastLevel);
+                setLevel(i, levelTemp);  // special setter - handles levelMaxChange
+            }   
+            break;
+        case sysModeDebug:
+            int32 a;
+            int32 b;
+            lastLevel = level[channel];
+            levelTemp = detector.findLevel(linearArray.buffer[channel],&a,&b,&lastLevel);
+            setLevel(channel, levelTemp);  // special setter - handles levelMaxChange
+            setBounds(channel,a,b);
+        default:
+            break;
+    }  
+}
+
+SystemState systemState;

File expresso_firmware/SystemState.h

+#ifndef _SYSTEM_STATE_H_
+#define _SYSTEM_STATE_H_
+#include "WProgram.h"
+#include "constants.h"
+#include "LevelDetector.h"
+#include "TaosLinearArray.h"
+
+class SystemState {
+    public:
+
+        SystemState();
+        bool setMode(uint16 _mode);
+        bool setMode(uint16 _mode, uint8 _channel);
+        uint16 getMode();
+
+        bool setChannel(uint8 _channel);
+        uint8 getChannel();
+
+        float getLevel(uint8 chan);
+        void getBounds(uint8 chan, int32* a, int32* b);
+        void setBounds(uint8 chan, int32 a, int32 b);
+        void setLevel(uint8 chan, float value); 
+        void update();
+    //private:
+        uint16 mode;                     // Operating mode
+        uint8 channel;                   // Channel setting for single channel operation
+        float level[constants::NUM_AIN]; // Capillary level data
+        float levelRaw[constants::NUM_AIN]; 
+        float lastLevel;
+        int32 bounds[constants::NUM_AIN][2]; // Debug data (bounds for level detection)
+        LevelDetector detector;     // Level detector object  
+};
+
+extern const uint16 sysModeStopped;
+extern const uint16 sysModeSingleChannel;
+extern const uint16 sysModeAllChannels;
+extern const uint16 sysModeDebug;
+
+extern SystemState systemState;
+
+#endif

File expresso_firmware/TaosLinearArray.cpp

+// TaosLinearArray.cpp
+//
+// The TaosLinearArray class provides an interface to the TAOS Linear Array
+// Sensors (e.g. TSL1406) for use with LeafLabs Maple, Maple Mini, and Maple
+// Native microcontroller boards. 
+//
+// Will Dickson, IO Rodeo Inc.
+//
+#include "TaosLinearArray.h"
+
+// Hardware timer 
+HardwareTimer timer(constants::timerNum);
+
+// TaosLinearArray class methods
+// ----------------------------------------------------------------------------
+TaosLinearArray::TaosLinearArray()
+{
+    si1Pin = constants::si1Pin;
+    clkPin = constants::clkPin;
+    ainPin = (uint8*) constants::ainPin;
+    setExposure(constants::exposure);
+    normBaseLevel = constants::normBaseLevel;
+
+    clkCnt = 0;
+    ainCnt = 0;
+    noInterrupts();
+    startSignal = false;
+    readInProgress = false;
+    dataReadySignal = false;
+    interrupts();
+
+    for (int i=0; i<numAin; i++) {
+        for (int j=0; j<numPixel; j++) {
+            buffer[i][j] = 0;
+            normConst[i][j] = normBaseLevel;
+        }
+        normScaleFact[i][0] = 1;
+        normScaleFact[i][1] = 1;
+    }
+}
+
+void TaosLinearArray::initialize() {
+    // Initializes the GPIO for the sensor and the hardware timer used
+    // by the sensor to pace the readings.
+    initializeGPIO();
+    initializeTimer(timer,constants::timerPeriod);
+}
+
+void TaosLinearArray::initializeGPIO() {
+    // Initializes the GPIO for the linear array sensor. The si1 pin is
+    // set to digital output, the clk pin is set to a pwm output, and the
+    // analog input pins are set to analog inputs.
+
+    pinMode(si1Pin, OUTPUT);
+    pinMode(clkPin, PWM);
+    for (int i=0; i<numAin; i++) {
+        pinMode(ainPin[i], INPUT_ANALOG);
+    }
+    digitalWrite(si1Pin, LOW);
+    digitalWrite(clkPin, LOW);
+}
+
+void TaosLinearArray::setExposure(uint16 val) {
+    // Sets the exposure value for the linear array sensor. The exposure
+    // setting determines the number of additional clock ticks which occur
+    // after all sensor data has been before a new read is initialed.
+    clkCntMax = numPixel + val + 1;
+}
+
+uint16 TaosLinearArray::getExposure() {
+    // Returns the current exposure setting.
+    return clkCntMax - numPixel - 1;
+}
+
+void TaosLinearArray::readData() {
+    // Initiates a read into the sensor buffer and waits for the read to
+    // complete.
+    startDataRead();
+    while (!dataReady()) {};
+}
+
+void TaosLinearArray::unSetNormConst() {
+    // Unset sets the normalization constants - so that the data in the buffer
+    // is the raw sensor data.
+    for (int i=0; i<numAin; i++) {
+        unSetNormConst(i);
+    }
+}
+
+void TaosLinearArray::unSetNormConst(uint8 chanNum) {
+    if (chanNum < numAin) {
+        for (int j=0; j<numPixel; j++) {
+            normConst[chanNum][j] = normBaseLevel; 
+        }
+        normScaleFact[chanNum][0] = 1;
+        normScaleFact[chanNum][1] = 1;
+    }
+}
+
+void TaosLinearArray::setNormConstFromBuffer() {
+    // Sets the normalization constant values, for all channels, based on current
+    // values in the acquisition buffer.  
+    for (uint8 i=0; i<numAin; i++) {
+        setNormConstFromBuffer(i);
+    }
+}
+
+void TaosLinearArray::setNormConstFromBuffer(uint8 chanNum) {
+    // Sets the pixel normalization constant values, for the given chan, to the
+    // current values in the acquisition buffer.
+    if (chanNum < numAin) {
+        for (uint16 j=0; j<numPixel; j++) {
+            normConst[chanNum][j] = buffer[chanNum][j];
+        }
+        normScaleFact[chanNum][0] = constants::normScaleFact[0];
+        normScaleFact[chanNum][1] = constants::normScaleFact[1];
+    }
+}
+
+void TaosLinearArray::setNormConstFromFlash() {
+    // Sets the pixel normalization constants, for all channels, from the values
+    // stored in flash memory. 
+    for (uint8 i=0; i<numAin; i++) {
+        setNormConstFromFlash(i);
+    }
+}
+
+void TaosLinearArray::setNormConstFromFlash(uint8 chanNum) {
+    // Sets the normalization constants for the given channel from the values
+    // stored in flash memory. 
+    uint16 ind0;
+    uint16 ind1;
+    uint8 data0;
+    uint8 data1;
+    uint16 maxAddress;
+    if ((chanNum < numAin) && (chanNum < flashMemory.numPages)) {
+        if (numPixel%2==0) {
+            maxAddress = numPixel/2;
+        }
+        else {
+            maxAddress = numPixel/2 + 1;
+        }
+        if (maxAddress <= flashMemory.maxAddress) {
+            for (uint16 address=0; address<maxAddress; address++) {
+                ind0 = 2*address;
+                ind1 = 2*address + 1;
+                flashMemory.readData(chanNum,address,data0,data1);
+                normConst[chanNum][ind0] = data0;
+                normConst[chanNum][ind1] = data1;
+            }
+            normScaleFact[chanNum][0] = constants::normScaleFact[0];
+            normScaleFact[chanNum][1] = constants::normScaleFact[1];
+        }
+    }
+}
+
+void TaosLinearArray::saveNormConstToFlash() {
+    // Saves the normalization constants, for all channels, to flash
+    // memory.
+    for (uint8 i=0; i<numAin; i++) {
+        saveNormConstToFlash(i);
+    }
+}
+
+void TaosLinearArray::saveNormConstToFlash(uint8 chanNum) {
+    // Saves the normalization constants, for the given channel,  to flash
+    // memory.
+    uint16 ind0;
+    uint16 ind1;
+    uint8 data0;
+    uint8 data1;
+    uint16 maxAddress;
+    if ((chanNum < numAin) && (chanNum < flashMemory.numPages)) {
+        flashMemory.erasePage(chanNum);
+        if (numPixel%2==0) {
+            maxAddress = numPixel/2;
+        }
+        else {
+            maxAddress = numPixel/2 + 1;
+        }
+        if (maxAddress <= flashMemory.maxAddress) {
+            for (uint16 address=0; address<maxAddress; address++) {
+                ind0 = 2*address;
+                ind1 = 2*address + 1;
+                data0 = normConst[chanNum][ind0];
+                data1 = normConst[chanNum][ind1];
+                flashMemory.writeData(chanNum,address,data0,data1);
+            }
+        }
+    }
+} 
+
+void TaosLinearArray::startDataRead() {
+    // Sets the start signal which signals that data should be read into
+    // the sensor buffer.
+    noInterrupts();
+    ainCnt = 0;
+    dataReadySignal = false;
+    startSignal = true;
+    interrupts();
+}
+
+bool TaosLinearArray::dataReady() {
+    // Checks the data ready signal which indicates that the data int the
+    // sensor buffer is ready - i.e., that the last read has completed.
+    return dataReadySignal;
+}
+
+void TaosLinearArray::timerUpdate1stQtr() {
+    // Update function for the 1st quater of timer period. Set the SI 
+    // (read initiation) pin low on the 2nd (clkCnt=1) cycle of a read from 
+    // the sensor.
+    if (clkCnt == 1) {
+        digitalWrite(si1Pin, LOW); 
+    }
+}
+
+void TaosLinearArray::timerUpdate2ndQtr() {
+    // Update function for the 2nd quater of the timer period. Reads data
+    // from the sensor and applies gain normalization to the values.
+    uint16 pixelValue;
+    uint16 normValue;
+    uint16 normPixelValue;
+    int16  scaledPixelValue;
+
+    if ((clkCnt >= 1) && (clkCnt < (numPixel+1))) {
+
+        if (readInProgress) {
+            // Read pixel value, normalize and scale
+            pixelValue = analogRead(ainPin[ainCnt]);
+            normValue = (uint16) normConst[ainCnt][clkCnt-1];
+            normPixelValue = pixelValue*normBaseLevel/normValue;
+            // Shift offset to zero and scale result and add back in offset
+            scaledPixelValue = (int16) normPixelValue - (int16) (normBaseLevel<<4);
+            scaledPixelValue = (normScaleFact[ainCnt][0]*scaledPixelValue)/normScaleFact[ainCnt][1];
+            scaledPixelValue = scaledPixelValue + (int16) (normBaseLevel<<4);
+            if (scaledPixelValue < 0) {
+                scaledPixelValue = 0;
+            }
+            if (scaledPixelValue > (255<<4)) {
+                scaledPixelValue = (255<<4);
+            }
+            buffer[ainCnt][clkCnt-1] = (uint8) (scaledPixelValue >> 4);
+            if (clkCnt == numPixel) {
+                ainCnt++;
+                if (ainCnt >= numAin) {
+                    readInProgress = false;
+                    dataReadySignal = true;
+                }
+            }  
+        } // if (readInProgress) 
+    }
+}
+
+void TaosLinearArray::timerUpdate3rdQtr() {
+    // Update function for 3rd quarter of timer period. Sets the initial
+    // SI pin High to initial read from sensor when the clock count is 0. 
+    // Registers the startSignal to begin a read of data into the buffer. 
+
+    // Initiate read from sensor
+    if (clkCnt == 0) {
+        digitalWrite(si1Pin, HIGH);
+    }
+
+    // Update clock count and start new reading if start signal has been
+    // recieved.
+    clkCnt++;
+    if (clkCnt >= clkCntMax) { 
+        clkCnt = 0;
+        if (startSignal) {
+            startSignal = false;
+            readInProgress = true;
+        }
+    }
+}
+
+
+// Hardware timer functions
+// ----------------------------------------------------------------------------
+
+void initializeTimer(HardwareTimer &timer, uint32 timerPeriod) {
+    // Initializes the hardware timer used by the linear array sensor. 
+    uint16 overflow;
+    timer.pause();
+    timer.setCount(0);
+    timer.setPeriod(timerPeriod);
+    timer.refresh();
+    overflow = timer.getOverflow();
+    timer.setCompare(constants::timerChanPwm,2*overflow/4); // PWM output on clk pin
+    timer.setCompare(constants::timerChan1stQtr,1*overflow/4); // 1st Qtr 
+    timer.setCompare(constants::timerChan2ndQtr,2*overflow/4); // 2nd Qtr
+    timer.setCompare(constants::timerChan3rdQtr,3*overflow/4); // 3rd Qtr
+    timer.attachInterrupt(constants::timerChan1stQtr, timerInterrupt1stQtr);
+    timer.attachInterrupt(constants::timerChan2ndQtr, timerInterrupt2ndQtr); 
+    timer.attachInterrupt(constants::timerChan3rdQtr, timerInterrupt3rdQtr); 
+    timer.refresh();
+    timer.resume();
+}
+
+void timerInterrupt1stQtr() {
+    // Interrupt hander which is called during the 1st quater of a timer period.
+    linearArray.timerUpdate1stQtr();
+}
+
+void timerInterrupt2ndQtr() {
+    // Interrupt handler which is called during the  2nd quater of a timer period.
+    linearArray.timerUpdate2ndQtr();
+}
+
+void timerInterrupt3rdQtr() {
+    // Interrupt handler which is called during the 3rd quater of a timer period.
+    linearArray.timerUpdate3rdQtr();
+}
+
+
+TaosLinearArray linearArray;

File expresso_firmware/TaosLinearArray.h

+// TaosLinearArray.h
+//
+// The TaosLinearArray class provides an interface to the TAOS Linear Array
+// Sensors (e.g. TSL1406) for use with LeafLabs Maple, Maple Mini, and Maple
+// Native microcontroller boards. 
+//
+// Will Dickson, IO Rodeo Inc.
+//
+#ifndef _TAOS_LINEAR_ARRAY_H_
+#define _TAOS_LINEAR_ARRAY_H_
+#include "WProgram.h"
+#include <FlashMemory.h>
+#include "constants.h"
+
+class TaosLinearArray {
+
+    public:
+
+        uint8 si1Pin;
+        uint8 clkPin;
+        uint8 *ainPin;
+        uint8 normBaseLevel;
+        uint8 normScaleFact[constants::NUM_AIN][2];
+        static const uint8 numAin=constants::NUM_AIN;
+        static const uint16 numPixel=constants::NUM_PIXEL;
+
+        uint8 buffer[constants::NUM_AIN][constants::NUM_PIXEL];
+        uint8 normConst[constants::NUM_AIN][constants::NUM_PIXEL];
+
+        TaosLinearArray(); 
+        void initialize();
+        void readData();
+        void setExposure(uint16 val);
+        uint16 getExposure();
+
+        void unSetNormConst();
+        void unSetNormConst(uint8 chanNum);
+        void setNormConstFromBuffer();
+        void setNormConstFromBuffer(uint8 chanNum);
+
+        void setNormConstFromFlash();
+        void setNormConstFromFlash(uint8 chanNum);
+        void saveNormConstToFlash();
+        void saveNormConstToFlash(uint8 chanNum);
+
+        friend void timerInterrupt1stQtr();
+        friend void timerInterrupt2ndQtr();
+        friend void timerInterrupt3rdQtr();
+
+    private:
+
+        volatile bool startSignal;
+        volatile bool readInProgress;
+        volatile bool dataReadySignal;
+
+        volatile uint8 ainCnt;
+        volatile uint16 clkCnt;
+        uint16 clkCntMax;
+
+        bool dataReady();
+        void startDataRead();
+        void initializeGPIO();
+
+        void timerUpdate1stQtr();
+        void timerUpdate2ndQtr();
+        void timerUpdate3rdQtr(); 
+};
+
+void initializeTimer(HardwareTimer &timer, uint32 timerPeriod);
+void timerInterrupt1stQtr();
+void timerInterrupt2ndQtr();
+void timerInterrupt3rdQtr();
+
+extern TaosLinearArray linearArray;
+
+#endif

File expresso_firmware/array_reader.py

+import time
+import serial
+import struct
+import matplotlib.pylab as pylab
+
+ARRAY_SZ = 768
+INWAITING_DT = 0.05
+BUF_EMPTY_NUM = 3
+BUF_EMPTY_DT = 0.01
+
+class ArrayReader(serial.Serial):
+
+    def __init__(self,**kwargs):
+        super(ArrayReader,self).__init__(**kwargs)
+        time.sleep(2.0)
+
+    def emptyBuffer(self):
+        """
+        Empty the serial input buffer.
+        """
+        for i in range(0,BUF_EMPTY_NUM):
+            self.flushInput()
+            time.sleep(BUF_EMPTY_DT)