+/*
+ * Copyright (c) 2008, Titanium Mirror, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * - Neither the name of Titanium Mirror, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * Take an instantaneous reading of the wind vane position.
+ *
+ * @author R. Steve McKown <smckown@gmail.com>
+ */
+
+module WindVaneReadP @safe()
+{
+ provides interface Read<uint16_t>;
+ uses {
+ interface Get<uint16_t> as Count;
+ interface Read<uint16_t> as SubRead;
+ interface GeneralIO as WPower;
+ interface GeneralIO as WDead;
+ interface State;
+ }
+}
+implementation
+{
+ enum {
+ S_IDLE = 0,
+ S_READ,
+ S_CHECK,
+ S_OUTPUT,
+
+ /* The wind vane has a dead zone where it returns an ADC value that also
+ * represents another angular location. Therefore, we implement special
+ * dead zone checking over a certain range of return values from the
+ * wind vane.
+ *
+ * Note: the wind vane's dead zone is probably 5-10 degrees. We would be
+ * more accurate if we took the ADC range over 350 or 355 degrees, then
+ * added 1/2 of the dead zone as an offset. However, without this logic,
+ * the wind vane appears more than accurate enough. It is less accurate
+ * within the dead zone, of course, but would be even if we did implement
+ * the extra logic.
+ */
+ DEAD_BEGIN = 6000, /* 4 12-bit ADC readings; about 120 degrees */
+ DEAD_END = 9000, /* About 198 degrees */
+ DEAD_THRESH = 14472, /* > when checking means in dead zone */
+ };
+
+ char m_msg[80];
+
+ command error_t Read.read()
+ {
+ if (!(call State.isIdle()))
+ return EBUSY;
+
+ call WPower.set();
+ if (call SubRead.read() == SUCCESS) {
+ call State.forceState(S_READ);
+ return SUCCESS;
+ } else {
+ call WPower.clr();
+ return FAIL;
+ }
+ }
+
+ void signalReadDone(error_t error, uint16_t value)
+ {
+ call WDead.makeInput();
+ call WPower.clr();
+
+ /* Value is the sum of multiple 12-bit ADC acquisitions, per the attached
+ * MultiSampleC component. Convert the value to degrees. Note, the values
+ * 360 and 0 (zero) represent the same angular position. We don't do a
+ * modulus operation here because we fully expect the client will do
+ * additional processing that will likely involve a modulus function anyway.
+ */
+ value = (uint16_t)(((uint32_t)(value / call Count.get()) * 360 + 2047) /
+ 4096);
+
+ call State.toIdle();
+ signal Read.readDone(error, value);
+ }
+
+ event void SubRead.readDone(error_t error, uint16_t result)
+ {
+ static uint16_t m_value;
+
+ if (error != SUCCESS) {
+ signalReadDone(error, 0);
+ return;
+ }
+
+ switch (call State.getState()) {
+ case S_READ:
+ if (result < DEAD_BEGIN || result > DEAD_END)
+ signalReadDone(SUCCESS, result);
+ else {
+ m_value = result;
+ call WDead.makeOutput();
+ if (call SubRead.read() == SUCCESS)
+ call State.forceState(S_CHECK);
+ else
+ signalReadDone(FAIL, 0);
+ }
+ break;
+ case S_CHECK:
+ signalReadDone(SUCCESS, (result > DEAD_THRESH) ? 0 : m_value);
+ break;
+ }
+ }
+}