packages feed

arduino-copilot 1.3.0 → 1.4.0

raw patch · 23 files changed

+1037/−62 lines, 23 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Copilot.Arduino: type Voltage = Int16
- Copilot.Arduino.Internals: instance Copilot.Arduino.Internals.IsAnalogInputPin t => Copilot.Arduino.Internals.Input (Copilot.Arduino.Internals.Pin t) Copilot.Arduino.Internals.Voltage
- Copilot.Arduino.Internals: type Voltage = Int16
- Copilot.Arduino.Library.Serial: class ShowableType t
- Copilot.Arduino.Library.Serial.Device: class ShowableType t
- Copilot.Arduino.Library.Serial.Device: instance Copilot.Arduino.Library.Serial.Device.ShowableType GHC.Int.Int16
- Copilot.Arduino.Library.Serial.Device: instance Copilot.Arduino.Library.Serial.Device.ShowableType GHC.Int.Int32
- Copilot.Arduino.Library.Serial.Device: instance Copilot.Arduino.Library.Serial.Device.ShowableType GHC.Int.Int64
- Copilot.Arduino.Library.Serial.Device: instance Copilot.Arduino.Library.Serial.Device.ShowableType GHC.Int.Int8
- Copilot.Arduino.Library.Serial.Device: instance Copilot.Arduino.Library.Serial.Device.ShowableType GHC.Types.Bool
- Copilot.Arduino.Library.Serial.Device: instance Copilot.Arduino.Library.Serial.Device.ShowableType GHC.Types.Double
- Copilot.Arduino.Library.Serial.Device: instance Copilot.Arduino.Library.Serial.Device.ShowableType GHC.Types.Float
- Copilot.Arduino.Library.Serial.Device: instance Copilot.Arduino.Library.Serial.Device.ShowableType GHC.Word.Word16
- Copilot.Arduino.Library.Serial.Device: instance Copilot.Arduino.Library.Serial.Device.ShowableType GHC.Word.Word32
- Copilot.Arduino.Library.Serial.Device: instance Copilot.Arduino.Library.Serial.Device.ShowableType GHC.Word.Word64
- Copilot.Arduino.Library.Serial.Device: instance Copilot.Arduino.Library.Serial.Device.ShowableType GHC.Word.Word8
- Copilot.Arduino.Library.Serial.Device: showCType :: ShowableType t => Proxy t -> String
- Copilot.Arduino.Library.Serial.XBee: class ShowableType t
+ Copilot.Arduino: data ClockMicros
+ Copilot.Arduino: data ClockMillis
+ Copilot.Arduino: frequency :: Integer -> Behavior Bool
+ Copilot.Arduino: instance Copilot.Arduino.Internals.Input Copilot.Arduino.ClockMicros GHC.Word.Word32
+ Copilot.Arduino: instance Copilot.Arduino.Internals.Input Copilot.Arduino.ClockMillis GHC.Word.Word32
+ Copilot.Arduino: micros :: ClockMicros
+ Copilot.Arduino: millis :: ClockMillis
+ Copilot.Arduino: type ADC = Int16
+ Copilot.Arduino.Internals: UniqueIds :: [Integer] -> UniqueIds
+ Copilot.Arduino.Internals: [earlySetups] :: Framework -> [CLine]
+ Copilot.Arduino.Internals: class ShowCType t
+ Copilot.Arduino.Internals: evalSketch :: Sketch a -> (Maybe Spec, Framework)
+ Copilot.Arduino.Internals: getUniqueId :: Sketch Integer
+ Copilot.Arduino.Internals: instance Control.Monad.State.Class.MonadState Copilot.Arduino.Internals.UniqueIds Copilot.Arduino.Internals.Sketch
+ Copilot.Arduino.Internals: instance Copilot.Arduino.Internals.IsAnalogInputPin t => Copilot.Arduino.Internals.Input (Copilot.Arduino.Internals.Pin t) Copilot.Arduino.Internals.ADC
+ Copilot.Arduino.Internals: instance Copilot.Arduino.Internals.ShowCType GHC.Int.Int16
+ Copilot.Arduino.Internals: instance Copilot.Arduino.Internals.ShowCType GHC.Int.Int32
+ Copilot.Arduino.Internals: instance Copilot.Arduino.Internals.ShowCType GHC.Int.Int64
+ Copilot.Arduino.Internals: instance Copilot.Arduino.Internals.ShowCType GHC.Int.Int8
+ Copilot.Arduino.Internals: instance Copilot.Arduino.Internals.ShowCType GHC.Types.Bool
+ Copilot.Arduino.Internals: instance Copilot.Arduino.Internals.ShowCType GHC.Types.Double
+ Copilot.Arduino.Internals: instance Copilot.Arduino.Internals.ShowCType GHC.Types.Float
+ Copilot.Arduino.Internals: instance Copilot.Arduino.Internals.ShowCType GHC.Word.Word16
+ Copilot.Arduino.Internals: instance Copilot.Arduino.Internals.ShowCType GHC.Word.Word32
+ Copilot.Arduino.Internals: instance Copilot.Arduino.Internals.ShowCType GHC.Word.Word64
+ Copilot.Arduino.Internals: instance Copilot.Arduino.Internals.ShowCType GHC.Word.Word8
+ Copilot.Arduino.Internals: newtype UniqueIds
+ Copilot.Arduino.Internals: showCType :: ShowCType t => Proxy t -> String
+ Copilot.Arduino.Internals: type ADC = Int16
+ Copilot.Arduino.Library.EEPROMex: EndAddress :: Word16 -> EndAddress
+ Copilot.Arduino.Library.EEPROMex: RangeWrites :: (Behavior Bool -> Behavior RangeIndex) -> Behavior t -> RangeWrites t
+ Copilot.Arduino.Library.EEPROMex: StartAddress :: Word16 -> StartAddress
+ Copilot.Arduino.Library.EEPROMex: alloc :: forall t. EEPROMable t => Sketch (Behavior t, Location t)
+ Copilot.Arduino.Library.EEPROMex: alloc' :: forall t. EEPROMable t => t -> Sketch (Behavior t, Location t)
+ Copilot.Arduino.Library.EEPROMex: allocRange :: forall t. EEPROMable t => Word16 -> Sketch (Range t)
+ Copilot.Arduino.Library.EEPROMex: class (ShowCType t, Typed t) => EEPROMable t
+ Copilot.Arduino.Library.EEPROMex: data Location t
+ Copilot.Arduino.Library.EEPROMex: data Range t
+ Copilot.Arduino.Library.EEPROMex: data RangeWrites t
+ Copilot.Arduino.Library.EEPROMex: instance Copilot.Arduino.Library.EEPROMex.EEPROMable GHC.Int.Int16
+ Copilot.Arduino.Library.EEPROMex: instance Copilot.Arduino.Library.EEPROMex.EEPROMable GHC.Int.Int32
+ Copilot.Arduino.Library.EEPROMex: instance Copilot.Arduino.Library.EEPROMex.EEPROMable GHC.Int.Int8
+ Copilot.Arduino.Library.EEPROMex: instance Copilot.Arduino.Library.EEPROMex.EEPROMable GHC.Types.Bool
+ Copilot.Arduino.Library.EEPROMex: instance Copilot.Arduino.Library.EEPROMex.EEPROMable GHC.Types.Double
+ Copilot.Arduino.Library.EEPROMex: instance Copilot.Arduino.Library.EEPROMex.EEPROMable GHC.Types.Float
+ Copilot.Arduino.Library.EEPROMex: instance Copilot.Arduino.Library.EEPROMex.EEPROMable GHC.Word.Word16
+ Copilot.Arduino.Library.EEPROMex: instance Copilot.Arduino.Library.EEPROMex.EEPROMable GHC.Word.Word32
+ Copilot.Arduino.Library.EEPROMex: instance Copilot.Arduino.Library.EEPROMex.EEPROMable GHC.Word.Word8
+ Copilot.Arduino.Library.EEPROMex: instance Copilot.Arduino.Library.EEPROMex.EEPROMable t => Copilot.Arduino.Internals.IsBehavior (Copilot.Arduino.Library.EEPROMex.RangeWrites t)
+ Copilot.Arduino.Library.EEPROMex: instance Copilot.Arduino.Library.EEPROMex.EEPROMable t => Copilot.Arduino.Internals.Output (Copilot.Arduino.Library.EEPROMex.Location t) (Copilot.Arduino.Internals.Event () (Copilot.Language.Stream.Stream t))
+ Copilot.Arduino.Library.EEPROMex: instance Copilot.Arduino.Library.EEPROMex.EEPROMable t => Copilot.Arduino.Internals.Output (Copilot.Arduino.Library.EEPROMex.Range t) (Copilot.Arduino.Internals.Event () (Copilot.Arduino.Library.EEPROMex.RangeWrites t))
+ Copilot.Arduino.Library.EEPROMex: instance Copilot.Arduino.Library.EEPROMex.EEPROMable t => Copilot.Arduino.Internals.Output (Copilot.Arduino.Library.EEPROMex.Range t) (Copilot.Arduino.Library.EEPROMex.RangeWrites t)
+ Copilot.Arduino.Library.EEPROMex: instance GHC.Classes.Eq Copilot.Arduino.Library.EEPROMex.EndAddress
+ Copilot.Arduino.Library.EEPROMex: instance GHC.Classes.Eq Copilot.Arduino.Library.EEPROMex.StartAddress
+ Copilot.Arduino.Library.EEPROMex: instance GHC.Classes.Ord Copilot.Arduino.Library.EEPROMex.EndAddress
+ Copilot.Arduino.Library.EEPROMex: instance GHC.Classes.Ord Copilot.Arduino.Library.EEPROMex.StartAddress
+ Copilot.Arduino.Library.EEPROMex: instance GHC.Enum.Bounded Copilot.Arduino.Library.EEPROMex.EndAddress
+ Copilot.Arduino.Library.EEPROMex: instance GHC.Enum.Bounded Copilot.Arduino.Library.EEPROMex.StartAddress
+ Copilot.Arduino.Library.EEPROMex: instance GHC.Enum.Enum Copilot.Arduino.Library.EEPROMex.EndAddress
+ Copilot.Arduino.Library.EEPROMex: instance GHC.Enum.Enum Copilot.Arduino.Library.EEPROMex.StartAddress
+ Copilot.Arduino.Library.EEPROMex: instance GHC.Num.Num Copilot.Arduino.Library.EEPROMex.EndAddress
+ Copilot.Arduino.Library.EEPROMex: instance GHC.Num.Num Copilot.Arduino.Library.EEPROMex.StartAddress
+ Copilot.Arduino.Library.EEPROMex: instance GHC.Read.Read Copilot.Arduino.Library.EEPROMex.EndAddress
+ Copilot.Arduino.Library.EEPROMex: instance GHC.Read.Read Copilot.Arduino.Library.EEPROMex.StartAddress
+ Copilot.Arduino.Library.EEPROMex: instance GHC.Real.Integral Copilot.Arduino.Library.EEPROMex.EndAddress
+ Copilot.Arduino.Library.EEPROMex: instance GHC.Real.Integral Copilot.Arduino.Library.EEPROMex.StartAddress
+ Copilot.Arduino.Library.EEPROMex: instance GHC.Real.Real Copilot.Arduino.Library.EEPROMex.EndAddress
+ Copilot.Arduino.Library.EEPROMex: instance GHC.Real.Real Copilot.Arduino.Library.EEPROMex.StartAddress
+ Copilot.Arduino.Library.EEPROMex: instance GHC.Show.Show Copilot.Arduino.Library.EEPROMex.EndAddress
+ Copilot.Arduino.Library.EEPROMex: instance GHC.Show.Show Copilot.Arduino.Library.EEPROMex.StartAddress
+ Copilot.Arduino.Library.EEPROMex: maxAllowedWrites :: Word16 -> Sketch ()
+ Copilot.Arduino.Library.EEPROMex: memPool :: StartAddress -> EndAddress -> Sketch ()
+ Copilot.Arduino.Library.EEPROMex: newtype EndAddress
+ Copilot.Arduino.Library.EEPROMex: newtype StartAddress
+ Copilot.Arduino.Library.EEPROMex: sweepRange :: RangeIndex -> Behavior t -> RangeWrites t
+ Copilot.Arduino.Library.EEPROMex: sweepRange' :: RangeIndex -> Behavior Bool -> Behavior RangeIndex
+ Copilot.Arduino.Library.EEPROMex: type RangeIndex = Word16
+ Copilot.Arduino.Library.Serial: FlashString :: String -> FlashString
+ Copilot.Arduino.Library.Serial: class OutputString t
+ Copilot.Arduino.Library.Serial: newtype FlashString
+ Copilot.Arduino.Library.Serial.Device: FlashString :: String -> FlashString
+ Copilot.Arduino.Library.Serial.Device: class OutputString t
+ Copilot.Arduino.Library.Serial.Device: instance Copilot.Arduino.Library.Serial.Device.OutputString Copilot.Arduino.Library.Serial.Device.FlashString
+ Copilot.Arduino.Library.Serial.Device: instance Copilot.Arduino.Library.Serial.Device.OutputString GHC.Base.String
+ Copilot.Arduino.Library.Serial.Device: newtype FlashString
+ Copilot.Arduino.Library.Serial.Device: quoteString :: String -> String
+ Copilot.Arduino.Library.Serial.XBee: FlashString :: String -> FlashString
+ Copilot.Arduino.Library.Serial.XBee: class OutputString t
+ Copilot.Arduino.Library.Serial.XBee: newtype FlashString
+ Copilot.Arduino.Nano: sizeOfEEPROM :: Word16
+ Copilot.Arduino.Uno: sizeOfEEPROM :: Word16
- Copilot.Arduino: MicroSeconds :: Stream Word16 -> MicroSeconds
+ Copilot.Arduino: MicroSeconds :: Stream Word32 -> MicroSeconds
- Copilot.Arduino: MilliSeconds :: Stream Word16 -> MilliSeconds
+ Copilot.Arduino: MilliSeconds :: Stream Word32 -> MilliSeconds
- Copilot.Arduino.Internals: Framework :: [CLine] -> [CLine] -> Map PinId (Set PinMode) -> [CLine] -> Framework
+ Copilot.Arduino.Internals: Framework :: [CLine] -> [CLine] -> [CLine] -> Map PinId (Set PinMode) -> [CLine] -> Framework
- Copilot.Arduino.Internals: Sketch :: Writer [(Spec, Framework)] t -> Sketch t
+ Copilot.Arduino.Internals: Sketch :: WriterT [(Spec, Framework)] (State UniqueIds) t -> Sketch t
- Copilot.Arduino.Library.Serial: show :: forall t. (ShowableType t, Typed t) => Stream t -> FormatOutput
+ Copilot.Arduino.Library.Serial: show :: forall t. (ShowCType t, Typed t) => Stream t -> FormatOutput
- Copilot.Arduino.Library.Serial: showFormatted :: forall t f. (ShowableType t, Typed t, FormatableType t f) => Stream t -> f -> FormatOutput
+ Copilot.Arduino.Library.Serial: showFormatted :: forall t f. (ShowCType t, Typed t, FormatableType t f) => Stream t -> f -> FormatOutput
- Copilot.Arduino.Library.Serial: str :: String -> FormatOutput
+ Copilot.Arduino.Library.Serial: str :: OutputString t => t -> FormatOutput
- Copilot.Arduino.Library.Serial.Device: show :: forall t. (ShowableType t, Typed t) => Stream t -> FormatOutput
+ Copilot.Arduino.Library.Serial.Device: show :: forall t. (ShowCType t, Typed t) => Stream t -> FormatOutput
- Copilot.Arduino.Library.Serial.Device: showFormatted :: forall t f. (ShowableType t, Typed t, FormatableType t f) => Stream t -> f -> FormatOutput
+ Copilot.Arduino.Library.Serial.Device: showFormatted :: forall t f. (ShowCType t, Typed t, FormatableType t f) => Stream t -> f -> FormatOutput
- Copilot.Arduino.Library.Serial.Device: str :: String -> FormatOutput
+ Copilot.Arduino.Library.Serial.Device: str :: OutputString t => t -> FormatOutput
- Copilot.Arduino.Library.Serial.XBee: show :: forall t. (ShowableType t, Typed t) => Stream t -> FormatOutput
+ Copilot.Arduino.Library.Serial.XBee: show :: forall t. (ShowCType t, Typed t) => Stream t -> FormatOutput
- Copilot.Arduino.Library.Serial.XBee: showFormatted :: forall t f. (ShowableType t, Typed t, FormatableType t f) => Stream t -> f -> FormatOutput
+ Copilot.Arduino.Library.Serial.XBee: showFormatted :: forall t f. (ShowCType t, Typed t, FormatableType t f) => Stream t -> f -> FormatOutput
- Copilot.Arduino.Library.Serial.XBee: str :: String -> FormatOutput
+ Copilot.Arduino.Library.Serial.XBee: str :: OutputString t => t -> FormatOutput

Files

CHANGELOG view
@@ -1,3 +1,16 @@+arduino-copilot (1.4.0) unstable; urgency=medium++  * New Copilot.Arduino.Library.EEPROMex module.+  * Serial: Added FlashString, to display strings directly from flash+    without copying into ram.+  * Added millis and micros.+  * MicroSeconds and MilliSeconds types changed to contain a Word32 not a+    Word16.+  * Renamed Voltage to ADC+  * Added frequency.++ -- Joey Hess <id@joeyh.name>  Sat, 01 Feb 2020 16:30:43 -0400+ arduino-copilot (1.3.0) unstable; urgency=medium    * New polymorphic "input" action that can be used to read digital and
README view
@@ -12,3 +12,15 @@  This and other examples are included in the examples/ directory, each with their own README explaining how to build and use them.++## Contributing++Contributions are welcome, including adding support for more+Arduino C libraries, provided that the C library is reasonably commonly+used, and is licensed with a free software license.++Contributors of such libraries will be expected to maintain them,+otherwise they may get removed if they are blocking changes to+arduino-copilot or are buggy.++Any contribution should have well documented functions and types.
TODO view
@@ -1,3 +1,4 @@+ * Currently all Inputs run each time through the loop, which could be a   problem when an input is slow. It would be good if the @: operator could   also rate limit when Inputs occur.@@ -60,12 +61,23 @@   three never runs, because both combinators must branch on the same   Stream Bool. +* The way serial output works is somewhat unsatisfying, because+  it does not take a Stream FormatOutput. So the Copilot DSL can't be used+  to adjust the output format as the arduino runs; it's fixed at compile+  time. While Serial.byte does operate on a Stream, it would be hard to do+  eg, an interactive program using it.++  It might be possible, using Copilot's support for C structs, to allow+  Stream FormatOutput to be accepted by Copilot. However, this issue+  seems like it would probably get in the way of actually constructing such+  a stream: https://github.com/Copilot-Language/copilot/issues/36+ * analogReference()   Takes one of a set of defines, which vary depending on board,   so how to pass that through Copilot? Could write a C   switch statement, on an int, and have the boade modules define   effectively enums.-* Voltage is a raw ADC value; add a stream function to convert that+* ADC is a raw ADC value; add a stream function to convert that   to a floating point actual voltage, given also a stream that contains   the reference voltage. * pulses@@ -73,3 +85,8 @@ * Wire library * test.hs is not wired into the cabal file because it depends on runghc   having the library available+* EEPROMex supports writing individial bits of a byte, but the haskell+  library does not expose it. What would be a good haskell interface to+  that, a Stream (Array 8 Bool)?+* reading from EEPROMex ranges+* Factor out Range from EEPROMex, and use it to also implement RAM ranges.
arduino-copilot.cabal view
@@ -1,5 +1,5 @@ Name: arduino-copilot-Version: 1.3.0+Version: 1.4.0 Cabal-Version: >= 1.8 License: BSD3 Maintainer: Joey Hess <id@joeyh.name>@@ -45,6 +45,14 @@   examples/serialport/README   examples/serialport/demo.hs   examples/serialport/pre-build-hook.sh+  examples/eeprom/Makefile+  examples/eeprom/README+  examples/eeprom/demo.hs+  examples/eeprom/pre-build-hook.sh+  examples/eepromrange/Makefile+  examples/eepromrange/README+  examples/eepromrange/demo.hs+  examples/eepromrange/pre-build-hook.sh   test.hs  Library@@ -58,6 +66,7 @@     Copilot.Arduino.Library.Serial     Copilot.Arduino.Library.Serial.Device     Copilot.Arduino.Library.Serial.XBee+    Copilot.Arduino.Library.EEPROMex   Other-Modules:     Copilot.Arduino.Main   Build-Depends:
examples/button/demo.hs view
@@ -2,10 +2,10 @@  import Copilot.Arduino.Uno -longer_and_longer :: Stream Word16+longer_and_longer :: Stream Word32 longer_and_longer = counter true $ counter true false `mod` 64 == 0 -counter :: Stream Bool -> Stream Bool -> Stream Word16+counter :: Stream Bool -> Stream Bool -> Stream Word32 counter inc reset = cnt    where 	cnt = if reset
+ examples/eeprom/Makefile view
@@ -0,0 +1,129 @@+# Arduino Make file. Refer to https://github.com/sudar/Arduino-Makefile+BOARD_TAG    = uno+ARDUINO_LIBS = EEPROMEx+CPPFLAGS     = -D_EEPROMEX_DEBUG+include /usr/share/arduino/Arduino.mk++# --- leonardo (or pro micro w/leo bootloader)+#BOARD_TAG    = leonardo+#MONITOR_PORT = /dev/ttyACM0+#include /usr/share/arduino/Arduino.mk++# --- mega2560 ide 1.0+#BOARD_TAG    = mega2560+#ARDUINO_PORT = /dev/ttyACM0+#include /usr/share/arduino/Arduino.mk++# --- mega2560 ide 1.6+#BOARD_TAG    = mega+#BOARD_SUB    = atmega2560+#MONITOR_PORT = /dev/ttyACM0+#ARDUINO_DIR  = /where/you/installed/arduino-1.6.5+#include /usr/share/arduino/Arduino.mk++# --- nano ide 1.0+#BOARD_TAG    = nano328+#MONITOR_PORT = /dev/ttyUSB0+#include /usr/share/arduino/Arduino.mk++# --- nano ide 1.6+#BOARD_TAG   = nano+#BOARD_SUB   = atmega328+#ARDUINO_DIR = /where/you/installed/arduino-1.6.5+#include /usr/share/arduino/Arduino.mk++# --- pro mini+#BOARD_TAG    = pro5v328+#MONITOR_PORT = /dev/ttyUSB0+#include /usr/share/arduino/Arduino.mk++# --- sparkfun pro micro+#BOARD_TAG         = promicro16+#ALTERNATE_CORE    = promicro+#BOARDS_TXT        = $(HOME)/arduino/hardware/promicro/boards.txt+#BOOTLOADER_PARENT = $(HOME)/arduino/hardware/promicro/bootloaders+#BOOTLOADER_PATH   = caterina+#BOOTLOADER_FILE   = Caterina-promicro16.hex+#ISP_PROG     	   = usbasp+#AVRDUDE_OPTS 	   = -v+#include /usr/share/arduino/Arduino.mk++# --- chipkit+#BOARD_TAG = mega_pic32+#MPIDE_DIR = /where/you/installed/mpide-0023-linux64-20130817-test+#include /usr/share/arduino/chipKIT.mk++# --- pinoccio+#BOARD_TAG         = pinoccio256+#ALTERNATE_CORE    = pinoccio+#BOOTLOADER_PARENT = $(HOME)/arduino/hardware/pinoccio/bootloaders+#BOOTLOADER_PATH   = STK500RFR2/release_0.51+#BOOTLOADER_FILE   = boot_pinoccio.hex+#CFLAGS_STD        = -std=gnu99+#CXXFLAGS_STD      = -std=gnu++11+#include /usr/share/arduino/Arduino.mk++# --- fio+#BOARD_TAG = fio+#include /usr/share/arduino/Arduino.mk++# --- atmega-ng ide 1.6+#BOARD_TAG    = atmegang+#BOARD_SUB    = atmega168+#MONITOR_PORT = /dev/ttyACM0+#ARDUINO_DIR  = /where/you/installed/arduino-1.6.5+#include /usr/share/arduino/Arduino.mk++# --- arduino-tiny ide 1.0+#ISP_PROG     	    = usbasp+#BOARD_TAG          = attiny85at8+#ALTERNATE_CORE     = tiny+#ARDUINO_VAR_PATH   = $(HOME)/arduino/hardware/tiny/cores/tiny+#ARDUINO_CORE_PATH  = $(HOME)/arduino/hardware/tiny/cores/tiny+#AVRDUDE_OPTS 	    = -v+#include /usr/share/arduino/Arduino.mk++# --- arduino-tiny ide 1.6+#ISP_PROG       = usbasp+#BOARD_TAG      = attiny85at8+#ALTERNATE_CORE = tiny+#ARDUINO_DIR    = /where/you/installed/arduino-1.6.5+#include /usr/share/arduino/Arduino.mk++# --- damellis attiny ide 1.0+#ISP_PROG       = usbasp+#BOARD_TAG      = attiny85+#ALTERNATE_CORE = attiny-master+#AVRDUDE_OPTS   = -v+#include /usr/share/arduino/Arduino.mk++# --- damellis attiny ide 1.6+#ISP_PROG       = usbasp+#BOARD_TAG      = attiny+#BOARD_SUB      = attiny85+#ALTERNATE_CORE = attiny+#F_CPU          = 16000000L+#ARDUINO_DIR    = /where/you/installed/arduino-1.6.5+#include /usr/share/arduino/Arduino.mk++# --- teensy3+#BOARD_TAG 	 = teensy31+#ARDUINO_DIR = /where/you/installed/the/patched/teensy/arduino-1.0.6+#include /usr/share/arduino/Teensy.mk++# --- mighty 1284p+#BOARD_TAG         = mighty_opt+#BOARDS_TXT        = $(HOME)/arduino/hardware/mighty-1284p/boards.txt+#BOOTLOADER_PARENT = $(HOME)/arduino/hardware/mighty-1284p/bootloaders+#BOOTLOADER_PATH   = optiboot+#BOOTLOADER_FILE   = optiboot_atmega1284p.hex+#ISP_PROG     	   = usbasp+#AVRDUDE_OPTS 	   = -v+#include /usr/share/arduino/Arduino.mk++# --- atmega328p on breadboard+#BOARD_TAG    = atmega328bb+#ISP_PROG     = usbasp+#AVRDUDE_OPTS = -v+#BOARDS_TXT   = $(HOME)/arduino/hardware/breadboard/boards.txt+#include /usr/share/arduino/Arduino.mk
+ examples/eeprom/README view
@@ -0,0 +1,25 @@+This is a demo program using arduino-copilot, showing how to use EEPROMex.++This needs the Arduino-Makefile to be installed. It relies on it+to set CPPFLAGS=-D_EEPROMEX_DEBUG when building the C code.+<https://github.com/sudar/Arduino-Makefile>++The path in the `Makefile` to `Arduino.mk` may need to be adjusted (the+default is the location where on a Debian system, `apt-get install+arduino-mk` will install it).++You will also need to install the EEPROMex library to someplace where+the Arduino toolchain can find it.+<https://github.com/thijse/Arduino-EEPROMEx/>++First, run this to build and upload a sketch that zeros out some values of+the EEPROM.++	ZERO=y runghc demo.hs+	ZERO=y make upload++Then, you can build and upload the sketch that actually does something+interesting.++	runghc demo.hs+	make upload
+ examples/eeprom/demo.hs view
@@ -0,0 +1,78 @@+{-# LANGUAGE RebindableSyntax #-}++import Copilot.Arduino.Uno+import qualified Copilot.Arduino.Library.EEPROMex as EEPROM+import qualified Copilot.Arduino.Library.Serial as Serial+import System.Posix.Env+import qualified Prelude++main :: IO ()+main = do+	z <- getEnvDefault "ZERO" ""+	arduino $ case z of+		"y" -> mainZero+		_ -> mainReal++-- Zero out the EEPROM, in preparation for mainReal.+mainZero :: Sketch ()+mainZero = do+	EEPROM.maxAllowedWrites 10++	(_, eepromloc) <- EEPROM.alloc' (20 :: ADC)+	eepromloc =: constant (0 :: ADC) @: firstIteration++	(_, uptimeloc) <- EEPROM.alloc' (0 :: Word32)+	uptimeloc =: constant (0 :: Word32) @: firstIteration++	Serial.baud 9600+	Serial.device =: [ Serial.str "EEPROM zeroed" ] @: firstIteration++mainReal :: Sketch ()+mainReal = do+	-- Store the maximum value read from a1 in the EEPROM.+	-- There are only 1024 possible values, so this can only possibly+	-- write new values that many times, so no delay is needed in this+	-- program. Just in case though, limit the total number of writes:+	EEPROM.maxAllowedWrites 2048+	(bootval, eepromloc) <- EEPROM.alloc' (20 :: ADC)+	currval <- input' a1 [10, 11, 25, 100, 99, 0, 0, 0, 0, 0]+	let maxval = if currval > bootval then currval else bootval+	-- Note that using maxBound here means it will never write+	-- on the first iteration, even if a1 is then larger than bootval.+	-- Any better way to write this that avoids the problem?+	let newmax = currval > ([maxBound] ++ maxval)+	eepromloc =: currval @: newmax++	-- Store the maximum MilliSeconds this program has been left running+	-- in the EEPROM too.+	(bootuptime, uptimeloc) <- EEPROM.alloc' (0 :: Word32)+	uptime <- input' millis (dummyclockinput [1, 1, 1, 2, 2, 2, 3, 3, 3])+	-- micros overflows after ~70 minutes; detect that and use it to+	-- trigger a periodic write of the uptime to EEPROM.+	hourlyish <- microsOverFlowed+	let maxuptime = if uptime > bootuptime then uptime else bootuptime+	uptimeloc =: maxuptime @: hourlyish+	+	Serial.baud 9600+	Serial.device =:+		[ Serial.str "max a1 seen: "+		-- This is not quite right, because maxval resets to+		-- bootval when this is displayed at hourlyish.+		-- Interpreting shows the problem:+		--    --              (3)             (20,3)+		-- How to fix this?+		, Serial.show maxval+		, Serial.str " max uptime: "+		, Serial.show maxuptime+		, Serial.char '\n'+		]+		@: (firstIteration || newmax || hourlyish)++microsOverFlowed :: Sketch (Behavior Bool)+microsOverFlowed = do+	t <- input' micros (dummyclockinput [1, 2, 3])+	let oldt = [0] ++ t+	return (t < oldt)++dummyclockinput :: [Word32] -> [Word32]+dummyclockinput = Prelude.cycle
+ examples/eeprom/pre-build-hook.sh view
@@ -0,0 +1,2 @@+#!/bin/sh+exec runghc demo.hs
+ examples/eepromrange/Makefile view
@@ -0,0 +1,129 @@+# Arduino Make file. Refer to https://github.com/sudar/Arduino-Makefile+BOARD_TAG    = uno+ARDUINO_LIBS = EEPROMEx+CPPFLAGS     = -D_EEPROMEX_DEBUG+include /usr/share/arduino/Arduino.mk++# --- leonardo (or pro micro w/leo bootloader)+#BOARD_TAG    = leonardo+#MONITOR_PORT = /dev/ttyACM0+#include /usr/share/arduino/Arduino.mk++# --- mega2560 ide 1.0+#BOARD_TAG    = mega2560+#ARDUINO_PORT = /dev/ttyACM0+#include /usr/share/arduino/Arduino.mk++# --- mega2560 ide 1.6+#BOARD_TAG    = mega+#BOARD_SUB    = atmega2560+#MONITOR_PORT = /dev/ttyACM0+#ARDUINO_DIR  = /where/you/installed/arduino-1.6.5+#include /usr/share/arduino/Arduino.mk++# --- nano ide 1.0+#BOARD_TAG    = nano328+#MONITOR_PORT = /dev/ttyUSB0+#include /usr/share/arduino/Arduino.mk++# --- nano ide 1.6+#BOARD_TAG   = nano+#BOARD_SUB   = atmega328+#ARDUINO_DIR = /where/you/installed/arduino-1.6.5+#include /usr/share/arduino/Arduino.mk++# --- pro mini+#BOARD_TAG    = pro5v328+#MONITOR_PORT = /dev/ttyUSB0+#include /usr/share/arduino/Arduino.mk++# --- sparkfun pro micro+#BOARD_TAG         = promicro16+#ALTERNATE_CORE    = promicro+#BOARDS_TXT        = $(HOME)/arduino/hardware/promicro/boards.txt+#BOOTLOADER_PARENT = $(HOME)/arduino/hardware/promicro/bootloaders+#BOOTLOADER_PATH   = caterina+#BOOTLOADER_FILE   = Caterina-promicro16.hex+#ISP_PROG     	   = usbasp+#AVRDUDE_OPTS 	   = -v+#include /usr/share/arduino/Arduino.mk++# --- chipkit+#BOARD_TAG = mega_pic32+#MPIDE_DIR = /where/you/installed/mpide-0023-linux64-20130817-test+#include /usr/share/arduino/chipKIT.mk++# --- pinoccio+#BOARD_TAG         = pinoccio256+#ALTERNATE_CORE    = pinoccio+#BOOTLOADER_PARENT = $(HOME)/arduino/hardware/pinoccio/bootloaders+#BOOTLOADER_PATH   = STK500RFR2/release_0.51+#BOOTLOADER_FILE   = boot_pinoccio.hex+#CFLAGS_STD        = -std=gnu99+#CXXFLAGS_STD      = -std=gnu++11+#include /usr/share/arduino/Arduino.mk++# --- fio+#BOARD_TAG = fio+#include /usr/share/arduino/Arduino.mk++# --- atmega-ng ide 1.6+#BOARD_TAG    = atmegang+#BOARD_SUB    = atmega168+#MONITOR_PORT = /dev/ttyACM0+#ARDUINO_DIR  = /where/you/installed/arduino-1.6.5+#include /usr/share/arduino/Arduino.mk++# --- arduino-tiny ide 1.0+#ISP_PROG     	    = usbasp+#BOARD_TAG          = attiny85at8+#ALTERNATE_CORE     = tiny+#ARDUINO_VAR_PATH   = $(HOME)/arduino/hardware/tiny/cores/tiny+#ARDUINO_CORE_PATH  = $(HOME)/arduino/hardware/tiny/cores/tiny+#AVRDUDE_OPTS 	    = -v+#include /usr/share/arduino/Arduino.mk++# --- arduino-tiny ide 1.6+#ISP_PROG       = usbasp+#BOARD_TAG      = attiny85at8+#ALTERNATE_CORE = tiny+#ARDUINO_DIR    = /where/you/installed/arduino-1.6.5+#include /usr/share/arduino/Arduino.mk++# --- damellis attiny ide 1.0+#ISP_PROG       = usbasp+#BOARD_TAG      = attiny85+#ALTERNATE_CORE = attiny-master+#AVRDUDE_OPTS   = -v+#include /usr/share/arduino/Arduino.mk++# --- damellis attiny ide 1.6+#ISP_PROG       = usbasp+#BOARD_TAG      = attiny+#BOARD_SUB      = attiny85+#ALTERNATE_CORE = attiny+#F_CPU          = 16000000L+#ARDUINO_DIR    = /where/you/installed/arduino-1.6.5+#include /usr/share/arduino/Arduino.mk++# --- teensy3+#BOARD_TAG 	 = teensy31+#ARDUINO_DIR = /where/you/installed/the/patched/teensy/arduino-1.0.6+#include /usr/share/arduino/Teensy.mk++# --- mighty 1284p+#BOARD_TAG         = mighty_opt+#BOARDS_TXT        = $(HOME)/arduino/hardware/mighty-1284p/boards.txt+#BOOTLOADER_PARENT = $(HOME)/arduino/hardware/mighty-1284p/bootloaders+#BOOTLOADER_PATH   = optiboot+#BOOTLOADER_FILE   = optiboot_atmega1284p.hex+#ISP_PROG     	   = usbasp+#AVRDUDE_OPTS 	   = -v+#include /usr/share/arduino/Arduino.mk++# --- atmega328p on breadboard+#BOARD_TAG    = atmega328bb+#ISP_PROG     = usbasp+#AVRDUDE_OPTS = -v+#BOARDS_TXT   = $(HOME)/arduino/hardware/breadboard/boards.txt+#include /usr/share/arduino/Arduino.mk
+ examples/eepromrange/README view
@@ -0,0 +1,26 @@+This is a demo program using arduino-copilot, showing how to use EEPROMex+with ranges.++This needs the Arduino-Makefile to be installed. It relies on it+to set CPPFLAGS=-D_EEPROMEX_DEBUG when building the C code.+<https://github.com/sudar/Arduino-Makefile>++The path in the `Makefile` to `Arduino.mk` may need to be adjusted (the+default is the location where on a Debian system, `apt-get install+arduino-mk` will install it).++You will also need to install the EEPROMex library to someplace where+the Arduino toolchain can find it.+<https://github.com/thijse/Arduino-EEPROMEx/>++First, run this to build and upload a sketch that zeros out the entire+EEPROM.++	ZERO=y runghc demo.hs+	ZERO=y make upload++Then, you can build and upload the sketch that actually does something+interesting.++	runghc demo.hs+	make upload
+ examples/eepromrange/demo.hs view
@@ -0,0 +1,40 @@+{-# LANGUAGE RebindableSyntax #-}++import Copilot.Arduino.Uno+import qualified Copilot.Arduino.Library.EEPROMex as EEPROM+import qualified Copilot.Arduino.Library.Serial as Serial+import System.Posix.Env++-- Change this to use a smaller part of the EEPROM.+sz :: Word16+sz = sizeOfEEPROM++main :: IO ()+main = do+	z <- getEnvDefault "ZERO" ""+	arduino $ case z of+		"y" -> mainZero+		_ -> mainReal++-- Zero out the EEPROM, in preparation for mainReal.+mainZero :: Sketch ()+mainZero = do+	EEPROM.maxAllowedWrites sz++	range <- EEPROM.allocRange sz :: Sketch (EEPROM.Range Word8)+	range =: EEPROM.sweepRange 0 (constant (0 :: Word8))+	+	Serial.baud 9600+	Serial.device =: [ Serial.str "EEPROM zeroing started" ]+		@: firstIteration++mainReal :: Sketch ()+mainReal = do+	EEPROM.maxAllowedWrites (sz * 10)+	range <- EEPROM.allocRange sz :: Sketch (EEPROM.Range ADC)+	v <- input' a1 ([10, 20..] :: [ADC])+	range =: EEPROM.sweepRange 0 v @: frequency 3+	led =: frequency 3+	Serial.device =: [ Serial.show v, Serial.char '\n']+	delay =: MilliSeconds (constant 10000)+	Serial.baud 9600
+ examples/eepromrange/pre-build-hook.sh view
@@ -0,0 +1,2 @@+#!/bin/sh+exec runghc demo.hs
examples/serialport/demo.hs view
@@ -16,11 +16,11 @@ useXBee :: Bool useXBee = True -userIsTyping :: Stream Int8 -> Stream Bool+userIsTyping :: Behavior Int8 -> Behavior Bool userIsTyping userinput = userinput /= constant Serial.noInput  getSerial :: Input o Int8 => Bool -> o -> Sketch (Behavior Int8)-getSerial True i = input i+getSerial True i = input' i (repeat Serial.noInput) getSerial False _ = return (constant Serial.noInput)  main :: IO ()@@ -28,14 +28,17 @@ 	when useSerial $ Serial.baud 9600 	when useXBee $ XBee.configure pin2 pin3 (XBee.Baud 9600) -	-- Measure the voltage of an analog input, and display it to the+	-- Measure the an analog input, and display it to the 	-- serial port. This is a handy demo because with nothing connected 	-- to a0, just moving your hand close to the arduino will probably 	-- cause some noise to be read.-	n <- input a0 :: Sketch (Behavior Voltage)+	n <- input' a0 [1, 50, 11, 99, 120] :: Sketch (Behavior ADC)+	c <- input' millis [20, 60, 80, 100] :: Sketch (Behavior Word32) 	let msg =  		[ Serial.str "analog input:" 		, Serial.showFormatted n Serial.HEX+		, Serial.str " millis:"+		, Serial.show c 		, Serial.char '\n' 		] 	when useSerial $ Serial.device =: msg
src/Copilot/Arduino.hs view
@@ -9,6 +9,8 @@ {-# LANGUAGE RebindableSyntax #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE TypeApplications #-}  module Copilot.Arduino ( 	-- * Arduino sketch generation@@ -25,7 +27,8 @@ 	input, 	input', 	pullup,-	Voltage,+	millis,+	micros, 	-- * Outputs 	-- 	-- | Only a few common outputs are included in this module.@@ -36,11 +39,16 @@ 	(=:), 	pwm, 	delay,+	-- * Other types+	ADC, 	MilliSeconds(..), 	MicroSeconds(..),+	ClockMillis,+	ClockMicros, 	-- * Utilities 	blinking, 	firstIteration,+	frequency, 	sketchSpec, 	-- * Copilot DSL 	--@@ -62,6 +70,8 @@ import Copilot.Arduino.Internals import Copilot.Arduino.Main import Control.Monad.Writer+import Data.Proxy+import Data.Maybe import qualified Data.Map as M import qualified Data.Set as S @@ -82,11 +92,19 @@ firstIteration :: Behavior Bool firstIteration = [True]++false +-- | Use this to make an event occur 1 time out of n.+--+-- This is implemented using Copilot's `clk`:+--+-- > frequency = clk (period n) (phase 1)+frequency :: Integer -> Behavior Bool+frequency n = clk (period n) (phase 1)+ -- | A stream of milliseconds.-data MilliSeconds = MilliSeconds (Stream Word16)+data MilliSeconds = MilliSeconds (Stream Word32)  -- | A stream of microseconds.-data MicroSeconds = MicroSeconds (Stream Word16)+data MicroSeconds = MicroSeconds (Stream Word32)  -- | Use this to add a delay between each iteration of the `Sketch`. -- A `Sketch` with no delay will run as fast as the hardware can run it.@@ -104,7 +122,46 @@ instance Output Delay MicroSeconds where 	Delay =: (MicroSeconds n) = tell 		[(trigger "delayMicroseconds" true [arg n], mempty)]-	++-- | Number of MillisSeconds since the Arduino booted.+--+-- > n <- input millis+-- +-- The value wraps back to zero after approximately 50 days.+millis :: ClockMillis+millis = ClockMillis++-- | Number of MicroSeconds since the Arduino booted.+--+-- > n <- input micros+-- +-- The value wraps back to zero after approximately 70 minutes.+micros :: ClockMicros+micros = ClockMicros++data ClockMillis = ClockMillis+data ClockMicros = ClockMicros++instance Input ClockMillis Word32 where+	input' ClockMillis = inputClock "millis"++instance Input ClockMicros Word32 where+	input' ClockMicros = inputClock "micros"++inputClock :: [Char] -> [Word32] -> Sketch (Behavior Word32)+inputClock src interpretvalues = mkInput $ InputSource+	{ setupInput = []+	, defineVar = [CLine $ showCType (Proxy @Word32) <> " " <> varname <>";"]+	, inputPinmode = mempty+	, readInput = [CLine $ varname <> " = " <> src <> "();"]+	, inputStream = extern varname interpretvalues'+	}+  where+	varname = "clock_" <> src+	interpretvalues'+		| null interpretvalues = Nothing+		| otherwise = Just interpretvalues+ -- | Use this to read a value from a component of the Arduino. -- -- For example, to read a digital value from pin12 and turn on the @@ -114,10 +171,11 @@ -- > led =: buttonpressed -- -- Some pins support multiple types of reads, for example pin a0--- supports a digital read (`Bool`), and an analog read (`Voltage`).--- In such cases you may need to specify the type of data to read:+-- supports a digital read (`Bool`), and an analog to digital converter+-- read (`ADC`). In such cases you may need to specify the type of+-- data to read: ----- > v <- input a0 :: Sketch (Behavior Voltage)+-- > v <- input a0 :: Sketch (Behavior ADC) input :: Input o t => o -> Sketch (Behavior t) input o = input' o [] @@ -154,6 +212,4 @@ -- This can be useful to intergrate with other libraries  -- such as copilot-theorem. sketchSpec :: Sketch a -> Spec-sketchSpec (Sketch s) = sequence_ is-  where-	(is, _fs) = unzip (execWriter s)+sketchSpec = fromMaybe (return ()) . fst . evalSketch
src/Copilot/Arduino/Internals.hs view
@@ -15,9 +15,12 @@  import Language.Copilot import Control.Monad.Writer+import Control.Monad.State.Strict+import Data.Functor.Identity import qualified Data.Map as M import qualified Data.Set as S import Data.Type.Bool+import Data.Proxy import GHC.TypeLits  -- | A value that changes over time.@@ -50,8 +53,14 @@ -- -- While it is a monad, a Sketch's outputs are not updated in any -- particular order, because Copilot does not guarantee any order.-newtype Sketch t = Sketch (Writer [(Spec, Framework)] t)-	deriving (Monad, Applicative, Functor, MonadWriter [(Spec, Framework)])+newtype Sketch t = Sketch (WriterT [(Spec, Framework)] (State UniqueIds) t)+	deriving +		( Monad+		, Applicative+		, Functor+		, MonadWriter [(Spec, Framework)]+		, MonadState UniqueIds+		)  instance Monoid (Sketch ()) where 	mempty = Sketch (return ())@@ -59,12 +68,36 @@ instance Semigroup (Sketch t) where 	(Sketch a) <> (Sketch b) = Sketch (a >> b) +newtype UniqueIds = UniqueIds [Integer]++evalSketch :: Sketch a -> (Maybe Spec, Framework)+evalSketch (Sketch s) = (spec, mconcat fs)+  where+	(is, fs) = unzip $ +		runIdentity $ evalStateT (execWriterT s) (UniqueIds [1..])+	-- Copilot will throw an ugly error if given a spec that does+	-- nothing at all, so return Nothing to avoid that.+	spec = if null is+		then Nothing+		else Just (sequence_ is)++getUniqueId :: Sketch Integer+getUniqueId = do+	v <- get+	case v of+		UniqueIds (u:us) -> do+			put (UniqueIds us)+			return u+		UniqueIds [] -> error "somehow UniqueIds ran out"+ -- | The framework of an Arduino sketch. data Framework = Framework 	{ defines :: [CLine] 	-- ^ Things that come before the C code generated by Copilot. 	, setups :: [CLine] 	-- ^ Things to do at setup, not including configuring pins.+	, earlySetups :: [CLine]+	-- ^ Things to do at setup, before the setups. 	, pinmodes :: M.Map PinId (S.Set PinMode) 	-- ^ How pins are used. 	, loops :: [CLine]@@ -78,12 +111,13 @@ 	a <> b = Framework 		{ defines = defines a <> defines b 		, setups = setups a <> setups b+		, earlySetups = earlySetups a <> earlySetups b 		, pinmodes = M.unionWith S.union (pinmodes a) (pinmodes b) 		, loops = loops a  <> loops b 		}  instance Monoid Framework where-	mempty = Framework mempty mempty mempty mempty+	mempty = Framework mempty mempty mempty mempty mempty  -- | Copilot only supports calling a trigger with a given name once -- per Spec; the generated C code will fail to build if the same name is@@ -117,6 +151,7 @@ 	f = Framework 		{ defines = defineVar i 		, setups = setupInput i+		, earlySetups = mempty 		, pinmodes = M.map S.singleton (inputPinmode i) 		, loops = readInput i 		}@@ -262,10 +297,10 @@ 			| null interpretvalues = Nothing 			| otherwise = Just interpretvalues --- | Voltage read from an Arduino's ADC. Ranges from 0-1023.-type Voltage = Int16+-- | Value read from an Arduino's ADC. Ranges from 0-1023.+type ADC = Int16 -instance IsAnalogInputPin t => Input (Pin t) Voltage where+instance IsAnalogInputPin t => Input (Pin t) ADC where 	input' (Pin (PinId n)) interpretvalues = mkInput $ InputSource 		{ defineVar = [CLine $ "int " <> varname <> ";"] 		, setupInput = []@@ -278,4 +313,19 @@ 		interpretvalues' 			| null interpretvalues = Nothing 			| otherwise = Just interpretvalues++class ShowCType t where+	showCType :: Proxy t -> String++instance ShowCType Bool where showCType _ = "bool"+instance ShowCType Int8 where showCType _ = "int8_t"+instance ShowCType Int16 where showCType _ = "int16_t"+instance ShowCType Int32 where showCType _ = "int32_t"+instance ShowCType Int64 where showCType _ = "int64_t"+instance ShowCType Word8 where showCType _ = "uint8_t"+instance ShowCType Word16 where showCType _ = "uint16_t"+instance ShowCType Word32 where showCType _ = "uint32_t"+instance ShowCType Word64 where showCType _ = "uint64_t"+instance ShowCType Float where showCType _ = "float"+instance ShowCType Double where showCType _ = "double" 
+ src/Copilot/Arduino/Library/EEPROMex.hs view
@@ -0,0 +1,368 @@+-- | EEPROMex library for arduino-copilot.+--+-- This module is designed to be imported qualified.+--+-- This is an interface to the C EEPROMex library, which will need to be+-- installed in your Arduino development environment.+-- https://playground.arduino.cc/Code/EEPROMex/++{-# LANGUAGE RebindableSyntax #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE TypeFamilies #-}++module Copilot.Arduino.Library.EEPROMex (+	-- * Configuration+	maxAllowedWrites,+	memPool,+	StartAddress(..),+	EndAddress(..),+	EEPROMable,+	-- * Single values+	alloc,+	alloc',+	Location,+	-- * Ranges+	Range,+	RangeIndex,+	allocRange,+	sweepRange,+	sweepRange',+	RangeWrites(..),+) where++import Copilot.Arduino+import Copilot.Arduino.Internals+import Control.Monad.Writer+import Data.Proxy++-- | Set the maximum number of writes to EEPROM that can be made while+-- the Arduino is running. When too many writes have been made,+-- it will no longer write to the EEPROM, and will send warning+-- messages to the serial port.+--+-- This is a strongly encouraged safety measure to use, because the+-- Arduino's EEPROM can only endure around 100,000 writes, and+-- a Sketch that's constantly writing to EEPROM, without a well chosen +-- `:@` rate limit or `delay`, could damage your hardware in just a few+-- minutes.+--+-- Note that this module uses EEPROMex's update facility for all writes+-- to EEPROM. That avoids writing a byte when its current value is+-- the same as what needed to be written. That prevents excessive wear+-- in some cases, but you should still use maxAllowedWrites too.+--+-- For this to work, CPPFLAGS needs to include "-D_EEPROMEX_DEBUG" when the+-- EEPROMex C library gets built. When you use this, it generates C+-- code that makes sure that is enabled.+maxAllowedWrites :: Word16 -> Sketch ()+maxAllowedWrites n = tell [(return (), f)]+  where+	f = mempty+		{ earlySetups = +			[ CLine "#ifdef _EEPROMEX_DEBUG"+			, CLine $ "EEPROM.setMaxAllowedWrites(" <> show n <> ");"+			, CLine "#else"+			, CLine "#error \"maxAllowedWrites cannot be checked because _EEPROMEX_DEBUG is not set.\""+			, CLine "#endif"+			]+		, defines = [ includeCLine ]+		}++-- | The address of the first byte of the EEPROM that will be allocated+-- by `alloc`. The default is to start allocation from 0. +--+-- Picking a different StartAddress can avoid overwriting the +-- part of the EEPROM that is used by some other program.+newtype StartAddress = StartAddress Word16+	deriving (Num, Eq, Ord, Enum, Show, Read, Bounded, Integral, Real)++-- | The address of the last byte of the EEPROM that can be allocated+-- by `alloc`. The default is to allow allocating 512 bytes.+--+-- Modules for particular boards, such as Copilot.Arduino.Library.Uno,+-- each define a sizeOfEEPROM value, so to use the entire EEPROM, use:+--+-- > EndAddress sizeOfEEPROM+newtype EndAddress = EndAddress Word16+	deriving (Num, Eq, Ord, Enum, Show, Read, Bounded, Integral, Real)++-- | Configure the EEPROM memory pool that the program can use.+--+-- > EEPROM.memPool 0 (EndAddress sizeOfEEPROM)+memPool :: StartAddress -> EndAddress -> Sketch ()+memPool (StartAddress start) (EndAddress end) = tell [(return (), f)]+  where+	f = mempty+		-- setMemPool() has to come before any+		-- getAddress(), so do it in earlySetups.+		{ earlySetups = +			[ CLine $ "EEPROM.setMemPool("+				<> show start+				<> ", "+				<> show end+				<> ");"+			]+		, defines = [ includeCLine ]+		}++-- | Allocates a location in the EEPROM. +--+-- Two things are returned; first a `Behavior` which contains a value+-- read from the EEPROM at boot; and secondly a `Location` that can be+-- written to later on.+--+-- Here's an example of using it, to remember the maximum value read from+-- a1, persistently across power cycles.+--+-- > EEPROM.maxAllowedWrites 100+-- > (bootval, eepromloc) <- EEPROM.alloc+-- > currval <- input a1 :: Sketch (Behavior ADC)+-- > let maxval = [maxBound] ++ if currval > bootval then currval else bootval+-- > eepromloc =: currval @: (currval > maxval)+-- > delay =: MilliSeconds (constant 10000)+--+-- Of course, the EEPROM could already contain any value at the allocated+-- location to start with (either some default value or something written+-- by another program). So you'll need to first boot up a sketch that zeros+-- it out before using the sketch above. Here's a simple sketch that zeros+-- two values:+--+-- > EEPROM.maxAllowedWrites 100+-- > (_, eepromloc1) <- EEPROM.alloc+-- > (_, eepromloc2) <- EEPROM.alloc+-- > eepromloc1 =: constant (0 :: ADC) :@ firstIteration+-- > eepromloc2 =: constant (0 :: Word16) :@ firstIteration+-- +-- `alloc` can be used as many times as you want, storing multiple values+-- in the EEPROM, up to the limit of the size of the EEPROM.+-- (Which is not statically checked.)+--+-- Do note that the EEPROM layout is controlled by the order of calls to+-- `alloc`, so take care when reordering or deleting calls.+alloc :: forall t. (EEPROMable t) => Sketch (Behavior t, Location t)+alloc = alloc' (factoryValue (Proxy @t))++-- | Same as `alloc'`, but with a value which will be used+-- as the EEPROM's boot value when interpreting the Sketch,+-- instead of the default of acting as if all bits of the EEPROM are+-- set.+alloc' :: forall t. (EEPROMable t) => t -> Sketch (Behavior t, Location t)+alloc' interpretval = do+	i <- getUniqueId+	let addrvarname = "eeprom_address" <> show i+	let bootvarname = "eeprom_boot_val" <> show i+	let writername = "eeprom_write" <> show i+	let proxy = Proxy @t+	bootval <- mkInput $ InputSource+		{ defineVar = +			[ includeCLine+			, CLine $ "int " <> addrvarname <> ";"+			, CLine $ showCType proxy <> " " <> bootvarname <> ";"+			, CLine $ "void " <> writername+				<> "(" <> showCType proxy <> " value) {"+			, CLine $ "  EEPROM." <> writeValue proxy+				<> "(" <> addrvarname <> ", value);"+			, CLine "}"+			]+		, setupInput =+			[ CLine $ addrvarname <> +				" = EEPROM.getAddress(sizeof(" +				<> showCType proxy <> "));"+			, CLine $ bootvarname <>+				" = EEPROM."+				<> readValue proxy <> "(" <> addrvarname <> ");"+			]+		, readInput = []+		, inputStream = extern bootvarname (Just (repeat interpretval))+		, inputPinmode = mempty+		}+	return (bootval, Location writername)++data Location t = Location String++instance EEPROMable t => Output (Location t) (Event () (Stream t)) where+	Location writername =: (Event v c) =+		tell [(trigger writername c [arg v], mempty)]++-- | A range of values in the EEPROM.+data Range t = Range+	{ rangeStart :: Location (Range t)+	, rangeSize :: Word16 -- ^ number of `t` values in the Range+	}++-- | Allocates a Range in the EEPROM, which stores the specified number of+-- items of some type.+--+-- This is an example of using a range of the EEPROM as a ring buffer,+-- to store the last 100 values read from a1.+--+-- > EEPROM.maxAllowedWrites 1000+-- > range <- EEPROM.allocRange 100 :: Sketch (EEPROM.Range ADC)+-- > currval <- input a1 :: Sketch (Behavior ADC)+-- > range =: EEPROM.sweepRange 0 currval+-- > delay =: MilliSeconds (constant 10000)+--+-- `allocRange` can be used as many times as you want, and combined with+-- uses of `alloc`, up to the size of the EEPROM. +-- (Which is not statically checked.)+--+-- Do note that the EEPROM layout is controlled by the order of calls to+-- `allocRange` and `alloc`, so take care when reordering or deleting calls.+allocRange :: forall t. (EEPROMable t) => Word16 -> Sketch (Range t)+allocRange sz = do+	i <- getUniqueId+	let startaddrvarname = "eeprom_range_address" <> show i+	let writername = "eeprom_range_write" <> show i+	let proxy = Proxy @t+	let f = Framework+		{ defines = +			[ includeCLine+			, CLine $ "int " <> startaddrvarname <> ";"+			, CLine $ "void " <> writername+				<> "(" <> showCType proxy <> " value"+				<> ", " <> showCType (Proxy @Word16) <> " offset"+				<> ") {"+			, CLine $ "  EEPROM." <> writeValue proxy+				<> "(" <> startaddrvarname +					<> " + offset*sizeof(" +						<> showCType proxy+						<> ")"+				<> ", value);"+			, CLine "}"+			]+		, setups = +			[ CLine $ startaddrvarname <> " = EEPROM.getAddress"+				<> "(sizeof(" <> showCType proxy <> ")" +				<> " * " <> show sz+				<> ");"+			]+		, earlySetups = []+		, pinmodes = mempty+		, loops = mempty+		}+	tell [(return (), f)]+	return (Range (Location writername) sz)++-- | Description of writes made to a Range.+--+-- This can be turned into an `Event` by using `@:` with it, and that's+-- what the Behavior Bool is needed for. Consider this example,+-- that ignores the Behavior Bool, and just uses a counter for the+-- `RangeIndex`:+--+-- > range =: EEPROM.RangeWrites (\_ -> counter) (constant 0) @: blinking+--+-- The use of `@:` `blinking` makes an `Event` that only occurs on every other+-- iteration of the Sketch. But, the counter increases on every+-- iteration of the Sketch. So that will only write to every other value+-- in the `Range`.+--+-- The Behavior Bool is only True when an event occurs, and so+-- it can be used to avoid incrementing the counter otherwise. See+--`sweepRange'` for an example.+data RangeWrites t = RangeWrites+	(Behavior Bool -> Behavior RangeIndex) +	(Behavior t)++-- | An index into a Range. 0 is the first value in the Range.+--+-- Indexes larger than the size of the Range will not overflow it,+-- instead they loop back to the start of the Range.+type RangeIndex = Word16++instance EEPROMable t => Output (Range t) (RangeWrites t) where+	(=:) = writeRange true++instance EEPROMable t => Output (Range t) (Event () (RangeWrites t)) where+	range =: Event ws c = +		writeRange c range ws++instance EEPROMable t => IsBehavior (RangeWrites t) where+	(@:) = Event++type instance BehaviorToEvent (RangeWrites t) = Event () (RangeWrites t)++writeRange :: EEPROMable t => Behavior Bool -> Range t -> RangeWrites t -> Sketch()+writeRange c range (RangeWrites idx v) = +	tell [(trigger writername c [arg idx', arg v], mempty)]+  where+	Location writername = rangeStart range+	idx' = idx c `mod` constant (rangeSize range)++-- | Treat the range as a ring buffer, and starting with the specified+-- `RangeIndex`, sweep over the range writing values from the+-- `Behavior`.+sweepRange :: RangeIndex -> Behavior t -> RangeWrites t+sweepRange start = RangeWrites (sweepRange' start)++-- | This is actually just a simple counter that increments on each write+-- to the range. That's sufficient, because writes that overflow the+-- end of the range wrap back to the start.+sweepRange' :: RangeIndex -> Behavior Bool -> Behavior RangeIndex+sweepRange' start c = cnt+  where+	cnt = [start] ++ rest+	rest = if c then cnt + 1 else cnt++class (ShowCType t, Typed t) => EEPROMable t where+	readValue :: Proxy t -> String+	writeValue :: Proxy t -> String+	factoryValue :: Proxy t -> t+	-- ^ The EEPROM comes from the factory with all bits set,+	-- this follows suite. Eg, maxBound for unsigned ints,+	-- minBound for signed ints since the sign bit being set+	-- makes it negative, and NaN for floats.++-- | This instance is not efficient; a whole byte is read/written+-- rather than a single bit.+instance EEPROMable Bool where+	readValue _ = "readByte"+	writeValue _ = "updateByte"+	factoryValue _ = True++instance EEPROMable Int8 where+	readValue _ = "readByte"+	writeValue _ = "updateByte"+	factoryValue _ = minBound++instance EEPROMable Int16 where+	readValue _ = "readInt"+	writeValue _ = "updateInt"+	factoryValue _= minBound++instance EEPROMable Int32 where+	readValue _ = "readLong"+	writeValue _ = "updateLong"+	factoryValue _= minBound++instance EEPROMable Word8 where+	readValue _ = "readByte"+	writeValue _ = "updateByte"+	factoryValue _ = maxBound++instance EEPROMable Word16 where+	readValue _ = "readInt"+	writeValue _ = "updateInt"+	factoryValue _ = maxBound++instance EEPROMable Word32 where+	readValue _ = "readLong"+	writeValue _ = "updateLong"+	factoryValue _ = maxBound++instance EEPROMable Float where+	readValue _ = "readFloat"+	writeValue _ = "updateFloat"+	factoryValue _ = 0/0 -- NaN++instance EEPROMable Double where+	readValue _ = "readDouble"+	writeValue _ = "updateDOuble"+	factoryValue _ = 0/0 -- NaN++includeCLine :: CLine+includeCLine = CLine "#include <EEPROMex.h>"
src/Copilot/Arduino/Library/Serial.hs view
@@ -10,13 +10,14 @@ 	device, 	char, 	str,+	FlashString(..), 	show, 	showFormatted, 	byte, 	noInput, 	SerialDevice, 	FormatOutput,-	ShowableType,+	OutputString, 	FormatableType, 	Base(..), ) where@@ -40,7 +41,7 @@ -- that describes the serial output. Note that you can only do this once -- in a Sketch. ----- > main = arduino $ doa+-- > main = arduino $ do -- > 	Serial.baud 9600 -- > 	b <- input pin4 -- > 	Serial.device =:
src/Copilot/Arduino/Library/Serial/Device.hs view
@@ -75,7 +75,7 @@ 			, readInput = [CLine $ varname <> " = " <> devname <> ".read();"] 			, inputStream = extern varname interpretvalues' 			}-		varname = "arduino_serial_" <> devname <> "_read"+		varname = "input_" <> devname 		interpretvalues' 			| null interpretvalues = Nothing 			| otherwise = Just interpretvalues@@ -94,7 +94,7 @@ 		go = trigger triggername c (mapMaybe formatArg l) 		f = mempty { defines = printer } -		triggername = "arduino_serial_" <> devname <> "_output"+		triggername = "output_" <> devname 	 		printer = concat 			[ [CLine $ "void " <> triggername <> "("@@ -120,7 +120,7 @@ 	, formatCLine :: SerialDeviceName -> String -> CLine 	} --- | Use this to output a Char+-- | Use this to output a Char. char :: Char -> FormatOutput char c = FormatOutput Nothing Nothing 	(\(SerialDeviceName devname) _ ->@@ -131,21 +131,37 @@ 	esc '\n' = "\\n" 	esc c' = [c'] --- | Use this to output a String-str :: String -> FormatOutput-str s = FormatOutput Nothing Nothing-	(\(SerialDeviceName devname) _ ->-		CLine $ devname <> ".print(\"" <> concatMap esc s <> "\");")+quoteString :: String -> String+quoteString s = '"' : concatMap esc s <> "\""   where 	esc '"' = "\"" 	esc '\\' = "\\" 	esc '\n' = "\\\n" 	esc c = [c] +class OutputString t where+	-- | Use this to output a `String` or `FlashString`+	str :: t -> FormatOutput++instance OutputString String where+	str s = FormatOutput Nothing Nothing $ \(SerialDeviceName devname) _ ->+		CLine $ devname <> ".print(" <> quoteString s <> ");"++-- | Normally a String will be copied into ram before it is output.+-- A FlashString will be output directly from flash memory.+--+-- Using this with `str` will reduce the amount of memory used by your+-- program, but will likely slightly increase the size of the program.+newtype FlashString = FlashString String++instance OutputString FlashString where+	str (FlashString s) = FormatOutput Nothing Nothing $ \(SerialDeviceName devname) _ ->+		CLine $ devname <> ".print(F(" <> quoteString s <> "));"+ -- | Use this to show the current value of a Stream. -- -- Numbers will be formatted in decimal. Bool is displayed as 0 and 1.-show :: forall t. (ShowableType t, Typed t) => Stream t -> FormatOutput+show :: forall t. (ShowCType t, Typed t) => Stream t -> FormatOutput show s = FormatOutput 	(Just (arg s)) 	(Just (showCType (Proxy @t)))@@ -171,7 +187,7 @@ -- -- > Serial.showFormatted (constant (78 :: Int8)) Serial.HEX -- "4E" showFormatted-	:: forall t f. (ShowableType t, Typed t, FormatableType t f)+	:: forall t f. (ShowCType t, Typed t, FormatableType t f) 	=> Stream t 	-> f 	-> FormatOutput@@ -182,21 +198,6 @@ 		CLine $ devname <> ".print(" <> v <> ", " <> formatter t f <> ");")   where 	t = Proxy @t--class ShowableType t where-	showCType :: Proxy t -> String--instance ShowableType Bool where showCType _ = "bool"-instance ShowableType Int8 where showCType _ = "int8_t"-instance ShowableType Int16 where showCType _ = "int16_t"-instance ShowableType Int32 where showCType _ = "int32_t"-instance ShowableType Int64 where showCType _ = "int64_t"-instance ShowableType Word8 where showCType _ = "uint8_t"-instance ShowableType Word16 where showCType _ = "uint16_t"-instance ShowableType Word32 where showCType _ = "uint32_t"-instance ShowableType Word64 where showCType _ = "uint64_t"-instance ShowableType Float where showCType _ = "float"-instance ShowableType Double where showCType _ = "double"  class FormatableType t f where 	formatter :: Proxy t -> f -> String
src/Copilot/Arduino/Library/Serial/XBee.hs view
@@ -11,13 +11,14 @@ 	device, 	char, 	str,+	FlashString(..), 	show, 	showFormatted, 	byte, 	noInput, 	SerialDevice, 	FormatOutput,-	ShowableType,+	OutputString, 	FormatableType, 	Base(..), ) where@@ -48,7 +49,7 @@ -- that describes the serial output. Note that you can only do this once -- in a Sketch. ----- > main = arduino $ doa+-- > main = arduino $ do -- >    XBee.configure pin2 pin3 (XBee.Baud 9600) -- > 	b <- input pin4 -- > 	XBee.device =:
src/Copilot/Arduino/Main.hs view
@@ -5,10 +5,11 @@ import Language.Copilot (Spec, interpret, reify) import Copilot.Compile.C99 (compile) import Copilot.Arduino.Internals+import System.IO import System.Directory import System.Posix.Temp (mkdtemp) import System.FilePath-import Control.Monad.Writer+import System.Exit import Data.List (isInfixOf) import Data.Maybe import qualified Data.Map as M@@ -41,23 +42,25 @@ -- > (100)          (13,false)     -- > (100)          (13,true)      arduino :: Sketch () -> IO ()-arduino (Sketch s) = go =<< execParser opts+arduino s = go =<< execParser opts   where 	opts = info (parseCmdLine <**> helper) 		( fullDesc 		<> progDesc "Run this program with no options to generate an Arduino sketch." 		) -	go o = case interpretSteps o of-		Just n -> interpret n spec-		Nothing -> writeIno spec f+	go o = case (mspec, interpretSteps o) of+		(Nothing, _) -> do+			hPutStrLn stderr "This Sketch does not do anything."+			exitFailure+		(Just spec, Just n) -> interpret n spec+		(Just spec, Nothing) -> writeIno spec f' 	-	(is, fs) = unzip (execWriter s)-	spec = sequence_ is+	(mspec, f) = evalSketch s 	 	-- Strict evaluation because this may throw errors, 	-- and we want to throw them before starting compilation.-	!f = finalizeFramework (mconcat fs)+	!f' = finalizeFramework f  data CmdLine = CmdLine 	{ interpretSteps :: Maybe Integer@@ -111,7 +114,7 @@ 	, 		[ "void setup()" 		]-	, codeblock $ map fromCLine (setups f)+	, codeblock $ map fromCLine (earlySetups f <> setups f) 	, [blank] 	, 		[ "void loop()"
src/Copilot/Arduino/Nano.hs view
@@ -25,6 +25,7 @@ 	, a5 	, a6 	, a7+	, sizeOfEEPROM ) where  import Copilot.Arduino@@ -58,3 +59,8 @@ a6, a7 :: Pin '[ 'AnalogInput ] a6 = Pin (PinId 20) a7 = Pin (PinId 21)++-- | Different versions of Arduino Nano have different EEPROM sizes.+-- This is a safe value that will work on all versions.+sizeOfEEPROM :: Word16+sizeOfEEPROM = 512
src/Copilot/Arduino/Uno.hs view
@@ -23,6 +23,7 @@ 	, a3 	, a4 	, a5+	, sizeOfEEPROM ) where  import Copilot.Arduino@@ -51,3 +52,6 @@ a3 = Pin (PinId 17) a4 = Pin (PinId 18) a5 = Pin (PinId 19)++sizeOfEEPROM :: Word16+sizeOfEEPROM = 512