created LimitEncoderReader
diff --git a/aos/common/libstdc++/README b/aos/common/libstdc++/README
new file mode 100644
index 0000000..df8269e
--- /dev/null
+++ b/aos/common/libstdc++/README
@@ -0,0 +1,3 @@
+This directory contains replacement include files for ones that the libstdc++ we're using on the cRIO either don't implement completely or doesn't have at all.
+The ones in aos/crio/libstdc++ are copied from the libstdc++ that came with the compiler and tweaked so that they work.
+When used for compiles not for vxworks, they just #include the usual standard library header.
diff --git a/aos/common/libstdc++/memory b/aos/common/libstdc++/memory
new file mode 100644
index 0000000..31ab6e9
--- /dev/null
+++ b/aos/common/libstdc++/memory
@@ -0,0 +1,4 @@
+#include <memory>
+#ifdef __VXWORKS__
+#include "aos/crio/libstdc++/unique_ptr.h"
+#endif
diff --git a/aos/common/libstdc++/utility b/aos/common/libstdc++/utility
new file mode 100644
index 0000000..50b8aa3
--- /dev/null
+++ b/aos/common/libstdc++/utility
@@ -0,0 +1,4 @@
+#include <utility>
+#ifdef __VXWORKS__
+#include "aos/crio/libstdc++/move.h"
+#endif
diff --git a/aos/common/zero_switch_value.h b/aos/common/zero_switch_value.h
new file mode 100644
index 0000000..fa8609d
--- /dev/null
+++ b/aos/common/zero_switch_value.h
@@ -0,0 +1,32 @@
+#ifndef AOS_COMMON_ZERO_SWITCH_VALUE_H_
+#define AOS_COMMON_ZERO_SWITCH_VALUE_H_
+
+#include "aos/common/type_traits.h"
+#include "aos/common/byteorder.h"
+
+namespace aos {
+
+// Contains all of the information from a zeroing sensor of some kind.
+// It is all contained here because it all has to be retrieved at the same time
+// or else there are race conditions with the sensor triggering and retrieving
+// the encoder values that would make writing code to deal with the information
+// hard (ie if the trigger sensor is read, then it changes->triggers the
+// interrupt which reads the encoder value).
+struct ZeroSwitchValue {
+  // What the curent encoder value is.
+  int32_t current_encoder;
+  // What the value of the encoder was when the interrupt triggered.
+  int32_t edge_encoder;
+  // What the current value of the sensor is.
+  bool current_value;
+
+  void NetworkToHost() {
+    current_encoder = ntoh(current_encoder);
+    edge_encoder = ntoh(edge_encoder);
+  }
+};
+static_assert(shm_ok<ZeroSwitchValue>::value, "it's getting sent over the wire");
+
+}  // namespace aos
+
+#endif  // AOS_COMMON_ZERO_SWITCH_VALUE_H_
diff --git a/aos/crio/libstdc++/move.h b/aos/crio/libstdc++/move.h
new file mode 100644
index 0000000..425fcb6
--- /dev/null
+++ b/aos/crio/libstdc++/move.h
@@ -0,0 +1,117 @@
+// Move, forward and identity for C++0x + swap -*- C++ -*-
+
+// Copyright (C) 2007, 2008, 2009 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// Under Section 7 of GPL version 3, you are granted additional
+// permissions described in the GCC Runtime Library Exception, version
+// 3.1, as published by the Free Software Foundation.
+
+// You should have received a copy of the GNU General Public License and
+// a copy of the GCC Runtime Library Exception along with this program;
+// see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+// <http://www.gnu.org/licenses/>.
+
+/** @file move.h
+ *  This is an internal header file, included by other library headers.
+ *  You should not attempt to use it directly.
+ */
+
+#ifndef _MOVE_H
+#define _MOVE_H 1
+
+#include "aos/crio/type_traits/type_traits"
+
+namespace std {
+
+  /// identity
+  template<typename _Tp>
+    struct identity
+    {
+      typedef _Tp type;
+    };
+
+  /// forward (as per N2835)
+  /// Forward lvalues as rvalues.
+  template<typename _Tp>
+    inline typename enable_if<!is_lvalue_reference<_Tp>::value, _Tp&&>::type
+    forward(typename std::identity<_Tp>::type& __t)
+    { return static_cast<_Tp&&>(__t); }
+
+  /// Forward rvalues as rvalues.
+  template<typename _Tp>
+    inline typename enable_if<!is_lvalue_reference<_Tp>::value, _Tp&&>::type
+    forward(typename std::identity<_Tp>::type&& __t)
+    { return static_cast<_Tp&&>(__t); }
+
+  // Forward lvalues as lvalues.
+  template<typename _Tp>
+    inline typename enable_if<is_lvalue_reference<_Tp>::value, _Tp>::type
+    forward(typename std::identity<_Tp>::type __t)
+    { return __t; }
+
+  // Prevent forwarding rvalues as const lvalues.
+  template<typename _Tp>
+    inline typename enable_if<is_lvalue_reference<_Tp>::value, _Tp>::type
+    forward(typename std::remove_reference<_Tp>::type&& __t) = delete;
+
+  /**
+   *  @brief Move a value.
+   *  @ingroup mutating_algorithms
+   *  @param  __t  A thing of arbitrary type.
+   *  @return Same, moved.
+  */
+  template<typename _Tp>
+    inline typename std::remove_reference<_Tp>::type&&
+    move(_Tp&& __t)
+    { return static_cast<typename std::remove_reference<_Tp>::type&&>(__t); }
+
+  /// declval, from type_traits.
+
+#define _GLIBCXX_MOVE(_Tp) std::move(_Tp)
+#define _GLIBCXX_FORWARD(_Tp, __val) std::forward<_Tp>(__val)
+
+#if 0
+  /**
+   *  @brief Swaps two values.
+   *  @ingroup mutating_algorithms
+   *  @param  __a  A thing of arbitrary type.
+   *  @param  __b  Another thing of arbitrary type.
+   *  @return   Nothing.
+  */
+  template<typename _Tp>
+    inline void
+    swap(_Tp& __a, _Tp& __b)
+    {
+      // concept requirements
+      __glibcxx_function_requires(_SGIAssignableConcept<_Tp>)
+
+      _Tp __tmp = _GLIBCXX_MOVE(__a);
+      __a = _GLIBCXX_MOVE(__b);
+      __b = _GLIBCXX_MOVE(__tmp);
+    }
+
+  // _GLIBCXX_RESOLVE_LIB_DEFECTS
+  // DR 809. std::swap should be overloaded for array types.
+  template<typename _Tp, size_t _Nm>
+    inline void
+    swap(_Tp (&__a)[_Nm], _Tp (&__b)[_Nm])
+    {
+      for (size_t __n = 0; __n < _Nm; ++__n)
+	swap(__a[__n], __b[__n]);
+    }
+#endif
+
+}  // namespace std
+
+#endif /* _MOVE_H */
diff --git a/aos/crio/libstdc++/unique_ptr.h b/aos/crio/libstdc++/unique_ptr.h
new file mode 100644
index 0000000..f6bda76d
--- /dev/null
+++ b/aos/crio/libstdc++/unique_ptr.h
@@ -0,0 +1,419 @@
+// unique_ptr implementation -*- C++ -*-
+
+// Copyright (C) 2008, 2009, 2010 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// Under Section 7 of GPL version 3, you are granted additional
+// permissions described in the GCC Runtime Library Exception, version
+// 3.1, as published by the Free Software Foundation.
+
+// You should have received a copy of the GNU General Public License and
+// a copy of the GCC Runtime Library Exception along with this program;
+// see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+// <http://www.gnu.org/licenses/>.
+
+/** @file unique_ptr.h
+ *  This is an internal header file, included by other library headers.
+ *  You should not attempt to use it directly.
+ */
+
+#ifndef _UNIQUE_PTR_H
+#define _UNIQUE_PTR_H 1
+
+#include "aos/crio/type_traits/type_traits"
+#include "aos/common/libstdc++/utility"
+#include <assert.h>
+
+namespace std {
+
+  /**
+   * @addtogroup pointer_abstractions
+   * @{
+   */
+
+  /// Primary template, default_delete.
+  template<typename _Tp> 
+    struct default_delete
+      {
+	default_delete() { }
+
+	template<typename _Up>
+	  default_delete(const default_delete<_Up>&) { }
+
+	void
+	operator()(_Tp* __ptr) const
+	{
+	  static_assert(sizeof(_Tp)>0,
+			"can't delete pointer to incomplete type");
+	  delete __ptr;
+	}
+    };
+
+  // _GLIBCXX_RESOLVE_LIB_DEFECTS
+  // DR 740 - omit specialization for array objects with a compile time length
+  /// Specialization, default_delete.
+  template<typename _Tp> 
+    struct default_delete<_Tp[]>
+    {
+      void
+      operator()(_Tp* __ptr) const
+      {
+	static_assert(sizeof(_Tp)>0,
+		      "can't delete pointer to incomplete type");
+	delete [] __ptr;
+      }
+    };
+
+  /// 20.7.12.2 unique_ptr for single objects.
+  template <typename _Tp, typename _Tp_Deleter = default_delete<_Tp> > 
+    class unique_ptr
+    {
+      typedef _Tp* unique_ptr::*             __unspecified_pointer_type;
+
+    public:
+      typedef _Tp*               pointer;
+      typedef _Tp                element_type;      
+      typedef _Tp_Deleter        deleter_type;
+
+      // Constructors.
+      unique_ptr()
+      : _t(pointer()), _deleter(deleter_type())
+      { static_assert(!std::is_pointer<deleter_type>::value,
+		      "constructed with null function pointer deleter"); }
+
+      explicit
+      unique_ptr(pointer __p)
+      : _t(__p), _deleter(deleter_type())
+      { static_assert(!std::is_pointer<deleter_type>::value,
+		     "constructed with null function pointer deleter"); }
+
+      unique_ptr(pointer __p,
+          typename std::conditional<std::is_reference<deleter_type>::value, 
+            deleter_type, const deleter_type&>::type __d)
+      : _t(__p), _deleter(__d) { }
+
+      unique_ptr(pointer __p,
+          typename std::remove_reference<deleter_type>::type&& __d)
+      : _t(std::move(__p)), _deleter(std::move(__d))
+      { static_assert(!std::is_reference<deleter_type>::value, 
+		      "rvalue deleter bound to reference"); }
+
+      // Move constructors.
+      unique_ptr(unique_ptr&& __u) 
+      : _t(__u.release()), _deleter(std::forward<deleter_type>(__u.get_deleter())) { }
+
+      template<typename _Up, typename _Up_Deleter> 
+        unique_ptr(unique_ptr<_Up, _Up_Deleter>&& __u) 
+        : _t(__u.release()), _deleter(std::forward<deleter_type>(__u.get_deleter()))
+	{ }
+
+      // Destructor.
+      ~unique_ptr() { reset(); }
+    
+      // Assignment.
+      unique_ptr&
+      operator=(unique_ptr&& __u)
+      { 
+        reset(__u.release()); 
+        get_deleter() = std::move(__u.get_deleter()); 
+        return *this;
+      }
+
+      template<typename _Up, typename _Up_Deleter> 
+        unique_ptr&
+        operator=(unique_ptr<_Up, _Up_Deleter>&& __u)
+	{
+          reset(__u.release()); 
+          get_deleter() = std::move(__u.get_deleter()); 
+          return *this;
+        }
+
+      unique_ptr&
+      operator=(__unspecified_pointer_type) 
+      {
+	reset();
+	return *this;
+      }
+
+      // Observers.
+      typename std::add_lvalue_reference<element_type>::type
+      operator*() const
+      {
+	assert(get() != pointer());
+	return *get();
+      }
+
+      pointer
+      operator->() const
+      {
+	assert(get() != pointer());
+	return get();
+      }
+
+      pointer
+      get() const
+      { return _t; }
+
+      deleter_type&
+      get_deleter()
+      { return _deleter; }
+
+      const deleter_type&
+      get_deleter() const
+      { return _deleter; }
+
+      explicit operator bool() const
+      { return get() == pointer() ? false : true; }
+
+      // Modifiers.
+      pointer
+      release() 
+      {
+	pointer __p = get();
+	_t = pointer();
+	return __p;
+      }
+
+      void
+      reset(pointer __p = pointer())
+      {
+	using std::swap;
+	swap(_t, __p);
+	if (__p != pointer())
+	  get_deleter()(__p);
+      }
+
+      void
+      swap(unique_ptr& __u)
+      {
+	using std::swap;
+	swap(_t, __u._t);
+	swap(_deleter, __u._deleter);
+      }
+
+      // Disable copy from lvalue.
+      unique_ptr(const unique_ptr&) = delete;
+      unique_ptr& operator=(const unique_ptr&) = delete;
+
+    private:
+			_Tp *_t;
+			_Tp_Deleter _deleter;
+  };
+ 
+  /// 20.7.12.3 unique_ptr for array objects with a runtime length
+  // [unique.ptr.runtime]
+  // _GLIBCXX_RESOLVE_LIB_DEFECTS
+  // DR 740 - omit specialization for array objects with a compile time length
+  template<typename _Tp, typename _Tp_Deleter> 
+    class unique_ptr<_Tp[], _Tp_Deleter>
+    {
+      typedef _Tp* unique_ptr::*             __unspecified_pointer_type;
+
+    public:
+      typedef _Tp*               pointer;
+      typedef _Tp                element_type;      
+      typedef _Tp_Deleter        deleter_type;
+
+      // Constructors.
+      unique_ptr()
+      : _t(pointer()), _deleter(deleter_type())
+      { static_assert(!std::is_pointer<deleter_type>::value,
+		      "constructed with null function pointer deleter"); }
+
+      explicit
+      unique_ptr(pointer __p)
+      : _t(__p), _deleter(deleter_type())
+      { static_assert(!std::is_pointer<deleter_type>::value,
+		      "constructed with null function pointer deleter"); }
+
+      unique_ptr(pointer __p,
+          typename std::conditional<std::is_reference<deleter_type>::value, 
+              deleter_type, const deleter_type&>::type __d) 
+      : _t(__p), _deleter(__d) { }
+
+      unique_ptr(pointer __p,
+		 typename std::remove_reference<deleter_type>::type && __d)
+      : _t(std::move(__p)), _deleter(std::move(__d))
+      { static_assert(!std::is_reference<deleter_type>::value, 
+		      "rvalue deleter bound to reference"); }
+
+      // Move constructors.
+      unique_ptr(unique_ptr&& __u) 
+      : _t(__u.release()), _deleter(std::forward<deleter_type>(__u.get_deleter())) { }
+
+      template<typename _Up, typename _Up_Deleter> 
+        unique_ptr(unique_ptr<_Up, _Up_Deleter>&& __u) 
+	: _t(__u.release()), _deleter(std::forward<deleter_type>(__u.get_deleter()))
+	{ }
+
+      // Destructor.
+      ~unique_ptr() { reset(); }
+
+      // Assignment.
+      unique_ptr&
+      operator=(unique_ptr&& __u)
+      {
+	reset(__u.release());
+	get_deleter() = std::move(__u.get_deleter()); 
+	return *this; 
+      }
+
+      template<typename _Up, typename _Up_Deleter> 
+        unique_ptr&
+        operator=(unique_ptr<_Up, _Up_Deleter>&& __u)
+	{
+          reset(__u.release());
+          get_deleter() = std::move(__u.get_deleter()); 
+          return *this;
+        }
+
+      unique_ptr&
+      operator=(__unspecified_pointer_type)
+      {
+	reset();
+	return *this;
+      }
+
+      // Observers.
+      typename std::add_lvalue_reference<element_type>::type 
+      operator[](size_t __i) const 
+      {
+	assert(get() != pointer());
+	return get()[__i];
+      }
+
+      pointer
+      get() const
+      { return _t; }
+
+      deleter_type& 
+      get_deleter()
+      { return _deleter; }
+
+      const deleter_type&
+      get_deleter() const
+      { return _deleter; }    
+
+      explicit operator bool() const 
+      { return get() == pointer() ? false : true; }
+    
+      // Modifiers.
+      pointer
+      release() 
+      {
+	pointer __p = get();
+	_t = pointer();
+	return __p;
+      }
+
+      void
+      reset(pointer __p = pointer()) 
+      {
+	using std::swap;
+	swap(_t, __p);
+	if (__p != pointer())
+	  get_deleter()(__p);
+      }
+
+      // DR 821.
+      template<typename _Up>
+        void reset(_Up) = delete;
+
+      void
+      swap(unique_ptr& __u)
+      {
+	swap(_t, __u._t);
+	swap(_deleter, __u._deleter);
+      }
+
+      // Disable copy from lvalue.
+      unique_ptr(const unique_ptr&) = delete;
+      unique_ptr& operator=(const unique_ptr&) = delete;
+
+      // Disable construction from convertible pointer types.
+      // (N2315 - 20.6.5.3.1)
+      template<typename _Up>
+        unique_ptr(_Up*, typename
+		   std::conditional<std::is_reference<deleter_type>::value,
+		   deleter_type, const deleter_type&>::type,
+		   typename std::enable_if<std::is_convertible<_Up*, 
+		   pointer>::value>::type* = 0) = delete;
+
+      template<typename _Up>
+        unique_ptr(_Up*, typename std::remove_reference<deleter_type>::type&&,
+		   typename std::enable_if<std::is_convertible<_Up*, 
+		   pointer>::value>::type* = 0) = delete;
+
+      template<typename _Up>
+        explicit
+        unique_ptr(_Up*, typename std::enable_if<std::is_convertible<_Up*, 
+		   pointer>::value>::type* = 0) = delete;
+
+    private:
+			_Tp *_t;
+			_Tp_Deleter _deleter;
+  };
+  
+  template<typename _Tp, typename _Tp_Deleter> 
+    inline void
+    swap(unique_ptr<_Tp, _Tp_Deleter>& __x,
+	 unique_ptr<_Tp, _Tp_Deleter>& __y)
+    { __x.swap(__y); }
+
+  template<typename _Tp, typename _Tp_Deleter,
+	   typename _Up, typename _Up_Deleter>
+    inline bool
+    operator==(const unique_ptr<_Tp, _Tp_Deleter>& __x,
+	       const unique_ptr<_Up, _Up_Deleter>& __y)
+    { return __x.get() == __y.get(); }
+
+  template<typename _Tp, typename _Tp_Deleter,
+	   typename _Up, typename _Up_Deleter>
+    inline bool
+    operator!=(const unique_ptr<_Tp, _Tp_Deleter>& __x,
+	       const unique_ptr<_Up, _Up_Deleter>& __y)
+    { return !(__x.get() == __y.get()); }
+
+  template<typename _Tp, typename _Tp_Deleter,
+	   typename _Up, typename _Up_Deleter>
+    inline bool
+    operator<(const unique_ptr<_Tp, _Tp_Deleter>& __x,
+	      const unique_ptr<_Up, _Up_Deleter>& __y)
+    { return __x.get() < __y.get(); }
+
+  template<typename _Tp, typename _Tp_Deleter,
+	   typename _Up, typename _Up_Deleter>
+    inline bool
+    operator<=(const unique_ptr<_Tp, _Tp_Deleter>& __x,
+	       const unique_ptr<_Up, _Up_Deleter>& __y)
+    { return !(__y.get() < __x.get()); }
+
+  template<typename _Tp, typename _Tp_Deleter,
+	   typename _Up, typename _Up_Deleter>
+    inline bool
+    operator>(const unique_ptr<_Tp, _Tp_Deleter>& __x,
+	      const unique_ptr<_Up, _Up_Deleter>& __y)
+    { return __y.get() < __x.get(); }
+
+  template<typename _Tp, typename _Tp_Deleter,
+	   typename _Up, typename _Up_Deleter>
+    inline bool
+    operator>=(const unique_ptr<_Tp, _Tp_Deleter>& __x,
+	       const unique_ptr<_Up, _Up_Deleter>& __y)
+    { return !(__x.get() < __y.get()); }
+
+  // @} group pointer_abstractions
+
+}  // namespace std
+
+#endif /* _UNIQUE_PTR_H */
diff --git a/aos/crio/shared_libs/limit_encoder_reader.cc b/aos/crio/shared_libs/limit_encoder_reader.cc
new file mode 100644
index 0000000..f3391f1
--- /dev/null
+++ b/aos/crio/shared_libs/limit_encoder_reader.cc
@@ -0,0 +1,102 @@
+#include "aos/crio/shared_libs/limit_encoder_reader.h"
+
+#include <functional>
+
+#include "WPILib/AnalogTrigger.h"
+
+using ::std::unique_ptr;
+
+namespace aos {
+namespace crio {
+
+class LimitEncoderReader::AnalogOnOffGetter
+    : public LimitEncoderReader::OnOffGetter {
+ public:
+  AnalogOnOffGetter(unique_ptr<AnalogChannel> channel,
+                    unique_ptr<AnalogTrigger> trigger,
+                    unique_ptr<AnalogTriggerOutput> source)
+      : channel_(::std::move(channel)), trigger_(::std::move(trigger)),
+        source_(::std::move(source)) {}
+
+  virtual bool Get() {
+    return source_->Get();
+  }
+
+ private:
+  unique_ptr<AnalogChannel> channel_;
+  unique_ptr<AnalogTrigger> trigger_;
+  unique_ptr<AnalogTriggerOutput> source_;
+};
+class LimitEncoderReader::DigitalOnOffGetter
+    : public LimitEncoderReader::OnOffGetter {
+ public:
+  DigitalOnOffGetter(DigitalInput *source)
+      : source_(source) {}
+
+  virtual bool Get() {
+    return source_->Get();
+  }
+
+ private:
+  DigitalInput *source_;
+};
+
+LimitEncoderReader::LimitEncoderReader(const unique_ptr<Encoder> &encoder,
+                                       unique_ptr<AnalogChannel> channel,
+                                       AnalogTriggerOutput::Type type,
+                                       AnalogTriggerOutput::Type triggeredType,
+                                       bool posEdge, bool negEdge,
+                                       float lowerVoltage,
+                                       float upperVoltage) 
+    : encoder_(encoder),
+      getter_(NULL),
+      source_(NULL) {
+  unique_ptr<AnalogTrigger> trigger(new AnalogTrigger(channel.get()));
+  trigger->SetLimitsVoltage(lowerVoltage, upperVoltage);
+  source_ = unique_ptr<AnalogTriggerOutput>(trigger->CreateOutput(type));
+  getter_ = unique_ptr<AnalogOnOffGetter>(
+      new AnalogOnOffGetter(::std::move(channel), ::std::move(trigger),
+                            unique_ptr<AnalogTriggerOutput>(
+                                trigger->CreateOutput(triggeredType))));
+  Init(posEdge, negEdge);
+}
+
+LimitEncoderReader::LimitEncoderReader(const unique_ptr<Encoder> &encoder,
+                                       unique_ptr<DigitalInput> source,
+                                       bool posEdge, bool negEdge)
+    : encoder_(encoder),
+      getter_(new DigitalOnOffGetter(source.get())),
+      source_(::std::move(source)) {
+  Init(posEdge, negEdge);
+}
+
+void LimitEncoderReader::Init(bool posEdge, bool negEdge) {
+  notifier_ = unique_ptr<InterruptNotifier<LimitEncoderReader>>(
+      new InterruptNotifier<LimitEncoderReader>(ReadValueStatic,
+                                                source_.get(),
+                                                this));
+  source_->SetUpSourceEdge(posEdge, negEdge);
+}
+
+void LimitEncoderReader::ReadValue() {
+  // Retrieve the value before potentially waiting for somebody else currently
+  // looking at the saved one.
+  int32_t new_value = encoder_->GetRaw();
+  {
+    MutexLocker locker(&value_sync_);
+    value_ = new_value;
+  }
+}
+
+ZeroSwitchValue LimitEncoderReader::Get() {
+  MutexLocker locker(&value_sync_);
+  return ZeroSwitchValue{encoder_->GetRaw(), value_, getter_->Get()};
+}
+
+void LimitEncoderReader::Start() {
+  encoder_->Start();
+  notifier_->Start();
+}
+
+}  // namespace crio
+}  // namespace aos
diff --git a/aos/crio/shared_libs/limit_encoder_reader.h b/aos/crio/shared_libs/limit_encoder_reader.h
new file mode 100644
index 0000000..1bd2d57
--- /dev/null
+++ b/aos/crio/shared_libs/limit_encoder_reader.h
@@ -0,0 +1,102 @@
+#ifndef AOS_CRIO_SHARED_LIBS_ENCODER_READER_H_
+#define AOS_CRIO_SHARED_LIBS_ENCODER_READER_H_
+
+#include "aos/common/libstdc++/memory"
+
+#include "WPILib/DigitalSource.h"
+#include "WPILib/AnalogChannel.h"
+#include "WPILib/AnalogTriggerOutput.h"
+#include "WPILib/DigitalInput.h"
+#include "WPILib/Encoder.h"
+
+#include "aos/common/mutex.h"
+#include "aos/common/zero_switch_value.h"
+#include "aos/common/macros.h"
+#include "aos/crio/shared_libs/interrupt_notifier.h"
+
+namespace aos {
+namespace crio {
+
+// This class handles reading an Encoder's value each time a digital sensor
+// triggers, including all of the irritating WPILib setup and synchronization.
+class LimitEncoderReader {
+ public:
+  // Defaults for the voltages for AnalogTriggers. They work well for digital
+  // sensors connected to analog inputs.
+  // TODO(brians): make sure these values are reasonable
+  static const float kDefaultLowerVoltage = 1;
+  static const float kDefaultUpperVoltage = 3;
+
+  // See InterruptNotifier for details about the state of the sensor object
+  // before the constructor is called.
+  // The different constructors take in different types of inputs and configure
+  // them to give interrupts.
+  //
+  // type is the type for triggering interrupts while triggeredType is the one
+  // to use for writing *triggered in Get().
+  // AnalogTriggerOutput::Type::k*Pulse don't seem to do anything...
+  LimitEncoderReader(const ::std::unique_ptr<Encoder> &encoder,
+                     ::std::unique_ptr<AnalogChannel> channel,
+                     AnalogTriggerOutput::Type type,
+                     AnalogTriggerOutput::Type triggeredType,
+                     bool posEdge, bool negEdge,
+                     float lowerVoltage = kDefaultLowerVoltage,
+                     float upperVoltage = kDefaultUpperVoltage);
+  LimitEncoderReader(const ::std::unique_ptr<Encoder> &encoder,
+                     ::std::unique_ptr<DigitalInput> sensor,
+                     bool posEdge, bool negEdge);
+
+  // Retrieves the values. See ZeroSwitchValue's declaration for an explanation
+  // of why retrieving all of them is necessary.
+  ZeroSwitchValue Get();
+
+  // Calls Start() on all contained objects.
+  void Start();
+
+  // Only to set things up etc. Getting values through these methods will always
+  // have race conditions!
+  // Also helpful for debugging.
+  const ::std::unique_ptr<Encoder> &encoder() const { return encoder_; }
+  const ::std::unique_ptr<DigitalSource> &source() const { return source_; }
+  
+ private:
+  // A class to deal with the fact that WPILib's AnalogTriggerOutput and
+  // DigitalInput have no common superclass with their Get() functions.
+  // Also handles taking ownership of some attached objects for
+  // AnalogTriggerOutput.
+  class OnOffGetter {
+   public:
+    virtual bool Get() = 0;
+
+    virtual ~OnOffGetter() {}
+  };
+  class AnalogOnOffGetter;
+  class DigitalOnOffGetter;
+
+  // The common initialization code.
+  // Gets called by all of the constructors.
+  // getter_, encoder_, and source_ must be set before calling this.
+  void Init(bool posEdge, bool negEdge);
+
+  static void ReadValueStatic(LimitEncoderReader *self) {
+    self->ReadValue();
+  }
+  void ReadValue();
+
+  ::std::unique_ptr<InterruptNotifier<LimitEncoderReader>> notifier_;
+  const ::std::unique_ptr<Encoder> &encoder_;
+
+  ::std::unique_ptr<OnOffGetter> getter_;
+  ::std::unique_ptr<DigitalSource> source_;
+
+  // The most recently read value.
+  int32_t value_;
+  Mutex value_sync_;
+
+  DISALLOW_COPY_AND_ASSIGN(LimitEncoderReader);
+};
+
+}  // namespace crio
+}  // namespace aos
+
+#endif  // AOS_CRIO_SHARED_LIBS_ENCODER_READER_H_