packages feed

arduino-copilot 1.2.0 → 1.3.0

raw patch · 16 files changed

+499/−228 lines, 16 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Copilot.Arduino: PWMDutyCycle :: Behavior Word8 -> PWMDutyCycle
- Copilot.Arduino: data PWMDutyCycle
- Copilot.Arduino: instance Copilot.Arduino.Internals.IsPWMPin t => Copilot.Arduino.Internals.Output (Copilot.Arduino.Internals.Pin t) (Copilot.Arduino.Internals.Event Copilot.Arduino.PWMDutyCycle)
- Copilot.Arduino: instance Copilot.Arduino.Internals.IsPWMPin t => Copilot.Arduino.Internals.Output (Copilot.Arduino.Internals.Pin t) Copilot.Arduino.PWMDutyCycle
- Copilot.Arduino: readfrom :: IsDigitalIOPin t => Pin t -> Input Bool
- Copilot.Arduino: readfrom' :: IsDigitalIOPin t => Pin t -> [Bool] -> Input Bool
- Copilot.Arduino: readvoltage :: IsAnalogInputPin t => Pin t -> Input Voltage
- Copilot.Arduino: readvoltage' :: IsAnalogInputPin t => Pin t -> [Int16] -> Input Voltage
- Copilot.Arduino: type Input t = Sketch (Stream t)
- Copilot.Arduino.Internals: alwaysEvent :: v -> Event v
- Copilot.Arduino.Internals: instance Copilot.Arduino.Internals.IsDigitalIOPin t => Copilot.Arduino.Internals.Output (Copilot.Arduino.Internals.Pin t) (Copilot.Arduino.Internals.Event (Copilot.Arduino.Internals.Behavior GHC.Types.Bool))
- Copilot.Arduino.Internals: instance Copilot.Arduino.Internals.Output o (Copilot.Arduino.Internals.Event (Copilot.Arduino.Internals.Behavior v)) => Copilot.Arduino.Internals.Output o (Copilot.Arduino.Internals.Behavior v)
- Copilot.Arduino.Internals: type Input t = Sketch (Stream t)
- Copilot.Arduino.Library.Serial: input :: Input Int8
- Copilot.Arduino.Library.Serial: input' :: [Int8] -> Input Int8
- Copilot.Arduino.Library.Serial: output :: Stream Bool -> [FormatOutput] -> Sketch ()
- Copilot.Arduino.Library.Serial.Device: deviceD :: (IsDigitalIOPin rx, IsDigitalIOPin tx) => SerialDeviceName -> Pin rx -> Pin tx -> Baud -> Sketch ()
- Copilot.Arduino.Library.Serial.Device: input'D :: SerialDeviceName -> [Int8] -> Input Int8
- Copilot.Arduino.Library.Serial.Device: inputD :: SerialDeviceName -> Input Int8
- Copilot.Arduino.Library.Serial.Device: outputD :: SerialDeviceName -> Stream Bool -> [FormatOutput] -> Sketch ()
- Copilot.Arduino.Library.Serial.XBee: input :: Input Int8
- Copilot.Arduino.Library.Serial.XBee: input' :: [Int8] -> Input Int8
- Copilot.Arduino.Library.Serial.XBee: output :: Stream Bool -> [FormatOutput] -> Sketch ()
+ Copilot.Arduino: class Input o t
+ Copilot.Arduino: data TypedBehavior p t
+ Copilot.Arduino: input :: Input o t => o -> Sketch (Behavior t)
+ Copilot.Arduino: input' :: Input o t => o -> [t] -> Sketch (Behavior t)
+ Copilot.Arduino: pwm :: Behavior Word8 -> TypedBehavior 'PWM Word8
+ Copilot.Arduino.Internals: TypedBehavior :: Behavior t -> TypedBehavior p t
+ Copilot.Arduino.Internals: class Input o t
+ Copilot.Arduino.Internals: class IsBehavior behavior
+ Copilot.Arduino.Internals: data TypedBehavior p t
+ Copilot.Arduino.Internals: input' :: Input o t => o -> [t] -> Sketch (Behavior t)
+ 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: instance Copilot.Arduino.Internals.IsBehavior (Copilot.Arduino.Internals.Behavior v)
+ Copilot.Arduino.Internals: instance Copilot.Arduino.Internals.IsDigitalIOPin t => Copilot.Arduino.Internals.Input (Copilot.Arduino.Internals.Pin t) GHC.Types.Bool
+ Copilot.Arduino.Internals: instance Copilot.Arduino.Internals.IsDigitalIOPin t => Copilot.Arduino.Internals.Output (Copilot.Arduino.Internals.Pin t) (Copilot.Arduino.Internals.Event () (Copilot.Language.Stream.Stream GHC.Types.Bool))
+ Copilot.Arduino.Internals: instance Copilot.Arduino.Internals.IsPWMPin t => Copilot.Arduino.Internals.Output (Copilot.Arduino.Internals.Pin t) (Copilot.Arduino.Internals.Event 'Copilot.Arduino.Internals.PWM (Copilot.Language.Stream.Stream GHC.Word.Word8))
+ Copilot.Arduino.Internals: instance Copilot.Arduino.Internals.Output o (Copilot.Arduino.Internals.Event () (Copilot.Language.Stream.Stream v)) => Copilot.Arduino.Internals.Output o (Copilot.Arduino.Internals.Behavior v)
+ Copilot.Arduino.Internals: instance forall k (p :: k) v. Copilot.Arduino.Internals.IsBehavior (Copilot.Arduino.Internals.TypedBehavior p v)
+ Copilot.Arduino.Internals: instance forall k o (p :: k) v. Copilot.Arduino.Internals.Output o (Copilot.Arduino.Internals.Event p (Copilot.Language.Stream.Stream v)) => Copilot.Arduino.Internals.Output o (Copilot.Arduino.Internals.TypedBehavior p v)
+ Copilot.Arduino.Internals: type Voltage = Int16
+ Copilot.Arduino.Library.Serial: data FormatOutput
+ Copilot.Arduino.Library.Serial: data SerialDevice
+ Copilot.Arduino.Library.Serial: device :: SerialDevice
+ Copilot.Arduino.Library.Serial.Device: SerialDevice :: SerialDeviceName -> SerialDevice
+ Copilot.Arduino.Library.Serial.Device: configureD :: (IsDigitalIOPin rx, IsDigitalIOPin tx) => SerialDeviceName -> Pin rx -> Pin tx -> Baud -> Sketch ()
+ Copilot.Arduino.Library.Serial.Device: instance Copilot.Arduino.Internals.Input Copilot.Arduino.Library.Serial.Device.SerialDevice GHC.Int.Int8
+ Copilot.Arduino.Library.Serial.Device: instance Copilot.Arduino.Internals.IsBehavior [Copilot.Arduino.Library.Serial.Device.FormatOutput]
+ Copilot.Arduino.Library.Serial.Device: instance Copilot.Arduino.Internals.Output Copilot.Arduino.Library.Serial.Device.SerialDevice (Copilot.Arduino.Internals.Event () [Copilot.Arduino.Library.Serial.Device.FormatOutput])
+ Copilot.Arduino.Library.Serial.Device: instance Copilot.Arduino.Internals.Output Copilot.Arduino.Library.Serial.Device.SerialDevice [Copilot.Arduino.Library.Serial.Device.FormatOutput]
+ Copilot.Arduino.Library.Serial.Device: newtype SerialDevice
+ Copilot.Arduino.Library.Serial.XBee: configure :: (IsDigitalIOPin rx, IsDigitalIOPin tx) => Pin rx -> Pin tx -> Baud -> Sketch ()
+ Copilot.Arduino.Library.Serial.XBee: data FormatOutput
+ Copilot.Arduino.Library.Serial.XBee: data SerialDevice
- Copilot.Arduino: (@:) :: v -> Behavior Bool -> Event v
+ Copilot.Arduino: (@:) :: IsBehavior behavior => behavior -> Behavior Bool -> BehaviorToEvent behavior
- Copilot.Arduino: data Event v
+ Copilot.Arduino: data Event p v
- Copilot.Arduino.Internals: (@:) :: v -> Behavior Bool -> Event v
+ Copilot.Arduino.Internals: (@:) :: IsBehavior behavior => behavior -> Behavior Bool -> BehaviorToEvent behavior
- Copilot.Arduino.Internals: Event :: v -> Stream Bool -> Event v
+ Copilot.Arduino.Internals: Event :: v -> Stream Bool -> Event p v
- Copilot.Arduino.Internals: data Event v
+ Copilot.Arduino.Internals: data Event p v
- Copilot.Arduino.Internals: mkInput :: InputSource t -> Input t
+ Copilot.Arduino.Internals: mkInput :: InputSource t -> Sketch (Behavior t)
- Copilot.Arduino.Internals: type family SameCapability a b :: Bool
+ Copilot.Arduino.Internals: type family BehaviorToEvent a
- Copilot.Arduino.Library.Serial.XBee: device :: (IsDigitalIOPin rx, IsDigitalIOPin tx) => Pin rx -> Pin tx -> Baud -> Sketch ()
+ Copilot.Arduino.Library.Serial.XBee: device :: SerialDevice

Files

CHANGELOG view
@@ -1,3 +1,14 @@+arduino-copilot (1.3.0) unstable; urgency=medium++  * New polymorphic "input" action that can be used to read digital and+    analog pins, as well as read from serial ports, and replaces+    readfrom, readvoltage, etc.+  * Replaced PWMDutyCycle with pwm.+  * Changed the Serial and XBee modules to work with the =: operator+    and "input".++ -- Joey Hess <id@joeyh.name>  Thu, 30 Jan 2020 22:33:20 -0400+ arduino-copilot (1.2.0) unstable; urgency=medium    * Bugfix: Doing the same kind of write to two different pins in the
TODO view
@@ -12,29 +12,64 @@   or check that the C compiler can optimise away a check of a flag that   always succeeds. -* Naming could be more FRP-like. readfrom and readvoltage-  are too imperative sounding. (Outputs were already fixed to be-  FRP-style.)+  (Note that, if @: makes the first read be skipped, or all of them,+  there would need to be a default value produced. Or, to avoid default+  values, it could force the first read to always occur.) -  A polymorphic read operation could work, assuming type inference is-  able to usually determine what kind of value is wanted to be read.+  An Input returns a Behavior; with @: it should really return an Event.+  But, that would need boilerplate to get the Stream out to use. -	a <- stream a1-	pin1 := a+ 	- foo <- input pin4+ 	+ Event foo _ <- input pin4 @: longer_and_longer+	  pin5 =: streamfunc foo -  Looks like this would work: While the type of a could be Bool or Voltage,-  going to a pin that only supports digital IO implies it must be a Bool.-  (Need to check if the type checker is able to work this out!)+  Seems not worth the distinction of Behavior vs Event here.+  +* If @: could somehow be applied to a whole Sketch () action, it would be+  possible to write some interesting combinators based on it, eg: +  ifM :: Stream Bool -> Sketch a -> Sketch a -> Sketch a++  schedule :: Stream Int -> [(Int, Sketch a] -> Sketch a++  Although, it may be that these would cause some explosion of the binary+  size, since each trigger in a Sketch's guard would, presumably, turn+  into a C function that duplicates the same code. Perhaps the C compiler+  could notice the common expressions and optimise them?++  Anyway, this would need the Sketch () to contain not a Spec, which I+  think is too opaque, but a data structure that allows adjusting the+  guards of `trigger` and an equivilant guard be added for `extern`.+  (See also, previous item.) And, it pushes things in the direction of+  needing to support multiple calls to Serial.display and wait with +  different parameters, and generally never hardcode the name of the C+  function called by a trigger.++  Now, I like the way that @: currently turns a behavior into an event, in+  FRP style. If @: is applied to a whole Sketch, it would stop doing that.+  That feels like a loss. So, perhaps @: stays as-is, and there's a new+  combinator to do the same thing to a Sketch. Internally, they would both+  adjust the guards of triggers. A bit redundant, but seems worth it to+  keep @: FRP style. (Making @: more polymorphic is also a possibility.)++  Another combinator, that I considered but rejected is:++  (|:) :: Sketch a -> Sketch a -> Sketch a ++  The problem with this is that in foo |: bar |: baz, one of the+  three never runs, because both combinators must branch on the same+  Stream Bool.+ * 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.-* readvoltage gets a raw ADC value; add a stream function to convert that+* Voltage 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.-* delayMicroseconds * pulses * random numbers * Wire library+* test.hs is not wired into the cabal file because it depends on runghc+  having the library available
arduino-copilot.cabal view
@@ -1,5 +1,5 @@ Name: arduino-copilot-Version: 1.2.0+Version: 1.3.0 Cabal-Version: >= 1.8 License: BSD3 Maintainer: Joey Hess <id@joeyh.name>@@ -41,6 +41,11 @@   examples/button/README   examples/button/demo.hs   examples/button/pre-build-hook.sh+  examples/serialport/Makefile+  examples/serialport/README+  examples/serialport/demo.hs+  examples/serialport/pre-build-hook.sh+  test.hs  Library   GHC-Options: -Wall -fno-warn-tabs
examples/button/demo.hs view
@@ -17,6 +17,6 @@  main :: IO () main = arduino $ do-	buttonpressed <- readfrom' pin12 [False, False, False, True, True]+	buttonpressed <- input' pin12 [False, False, False, True, True] 	led =: buttonpressed || blinking 	delay =: MilliSeconds (longer_and_longer * 2)
+ examples/serialport/Makefile view
@@ -0,0 +1,127 @@+# Arduino Make file. Refer to https://github.com/sudar/Arduino-Makefile+BOARD_TAG    = uno+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/serialport/README view
@@ -0,0 +1,24 @@+This is a demo program using arduino-copilot. ++To build the C code:++	runghc demo.hs++The resulting `.ino` sketch can be loaded into the Arduino IDE and flashed+to an Arduino Uno board using the IDE.++## Arduino-Makefile integration++The `Makefile` and `pre-build-hook.sh` show how to integrate arduino-copilot+with <https://github.com/sudar/Arduino-Makefile>. This automates generating+the C code, compiling that, and flashing it onto an Arduino Uno board,+with a single command++	make upload++Note that you will need to manually build the C code once, as shown above+, since the Arduino-Makefile expects to find a `.ino` file.++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).
+ examples/serialport/demo.hs view
@@ -0,0 +1,49 @@+{-# LANGUAGE RebindableSyntax #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}++import Copilot.Arduino.Uno+import qualified Copilot.Arduino.Library.Serial as Serial+import qualified Copilot.Arduino.Library.Serial.XBee as XBee+import Control.Monad++-- Should it use the onboard serial port?+useSerial :: Bool+useSerial = True++-- Should it use the XBee wireless serial port?+-- (it can use more than one serial port at the same time.)+useXBee :: Bool+useXBee = True++userIsTyping :: Stream Int8 -> Stream Bool+userIsTyping userinput = userinput /= constant Serial.noInput++getSerial :: Input o Int8 => Bool -> o -> Sketch (Behavior Int8)+getSerial True i = input i+getSerial False _ = return (constant Serial.noInput)++main :: IO ()+main = arduino $ do+	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+	-- 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)+	let msg = +		[ Serial.str "analog input:"+		, Serial.showFormatted n Serial.HEX+		, Serial.char '\n'+		]+	when useSerial $ Serial.device =: msg+	when useXBee $ XBee.device =: msg++	-- Light the led whenever the user types on the serial port.+	userinput <- getSerial useSerial Serial.device+	userinputxbee <- getSerial useXBee XBee.device+	led =: userIsTyping userinput || userIsTyping userinputxbee++	delay =: MilliSeconds (constant 100)
+ examples/serialport/pre-build-hook.sh view
@@ -0,0 +1,2 @@+#!/bin/sh+exec runghc demo.hs
src/Copilot/Arduino.hs view
@@ -8,11 +8,7 @@  {-# LANGUAGE RebindableSyntax #-} {-# LANGUAGE DataKinds #-}-{-# LANGUAGE PolyKinds #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE UndecidableInstances #-}  module Copilot.Arduino ( 	-- * Arduino sketch generation@@ -21,16 +17,15 @@ 	Pin, 	-- * Functional reactive programming 	Behavior,+	TypedBehavior, 	Event, 	(@:), 	-- * Inputs 	Input,-	readfrom,-	readfrom',+	input,+	input', 	pullup, 	Voltage,-	readvoltage,-	readvoltage', 	-- * Outputs 	-- 	-- | Only a few common outputs are included in this module.@@ -39,7 +34,7 @@ 	Output, 	led, 	(=:),-	PWMDutyCycle(..),+	pwm, 	delay, 	MilliSeconds(..), 	MicroSeconds(..),@@ -109,37 +104,31 @@ instance Output Delay MicroSeconds where 	Delay =: (MicroSeconds n) = tell 		[(trigger "delayMicroseconds" true [arg n], mempty)]---- | Reading a Bool from a `Pin`.+	+-- | Use this to read a value from a component of the Arduino. ----- > buttonpressed <- readfrom pin12+-- For example, to read a digital value from pin12 and turn on the +-- led when the pin is high:+--+-- > buttonpressed <- input pin12 -- > led =: buttonpressed-readfrom :: IsDigitalIOPin t => Pin t -> Input Bool-readfrom p = readfrom' p []---- | The list is used as simulated input when interpreting the program.-readfrom' :: IsDigitalIOPin t => Pin t -> [Bool] -> Input Bool-readfrom' (Pin p@(PinId n)) interpretvalues = mkInput $ InputSource-	{ defineVar = [CLine $ "bool " <> varname <> ";"]-	, setupInput = []-	, inputPinmode = M.singleton p InputMode-	, readInput = [CLine $ varname <> " = digitalRead(" <> show n <> ");"]-	, inputStream = extern varname interpretvalues'-	}-  where-	varname = "arduino_digital_pin_input" <> show n-	interpretvalues'-		| null interpretvalues = Nothing-		| otherwise = Just interpretvalues+--+-- 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:+--+-- > v <- input a0 :: Sketch (Behavior Voltage)+input :: Input o t => o -> Sketch (Behavior t)+input o = input' o [] --- | Normally when a `Pin` is read with `readfrom`, it is configured+-- | Normally when a digital value is read from a `Pin`, it is configured -- without the internal pullup resistor being enabled. Use this to enable -- the pullup register for all reads from the `Pin`. -- -- Bear in mind that enabling the pullup resistor inverts the value that -- will be read from the pin. ----- > pullup pin3+-- > pullup pin12 pullup :: Pin t -> Sketch () pullup (Pin p) = tell [(return (), f)]   where@@ -147,44 +136,14 @@ 		{ pinmodes = M.singleton p (S.singleton InputPullupMode) 		} --- | Voltage read from an Arduino's ADC. Ranges from 0-1023.-type Voltage = Int16---- | Measuring the voltage of a `Pin`.-readvoltage :: IsAnalogInputPin t => Pin t -> Input Voltage-readvoltage p = readvoltage' p []--readvoltage' :: IsAnalogInputPin t => Pin t -> [Int16] -> Input Voltage-readvoltage' (Pin (PinId n)) interpretvalues = mkInput $ InputSource-	{ defineVar = [CLine $ "int " <> varname <> ";"]-	, setupInput = []-	, inputPinmode = mempty-	, readInput = [CLine $ varname <> " = analogRead(" <> show n <> ");"]-	, inputStream = extern varname interpretvalues'-	}-  where-	varname = "arduino_analog_pin_input" <> show n-	interpretvalues'-		| null interpretvalues = Nothing-		| otherwise = Just interpretvalues- -- | Use this to do PWM output to a pin. ----- > pin3 =: PWMDutyCycle (constant 128)+-- > pin3 =: pwm (constant 128) -- --- Each Word8 from the Stream describes a PWM square wave.+-- Each Word8 of the Behavior describes a PWM square wave. -- 0 is always off and 255 is always on.-data PWMDutyCycle = PWMDutyCycle (Behavior Word8)--instance IsPWMPin t => Output (Pin t) (Event PWMDutyCycle) where-	(Pin (PinId n)) =: (Event (PWMDutyCycle v) c) = tell [(go, f)]-	  where-		go = trigger triggername c [arg (constant n), arg v]-		-- analogWrite does not need any pinmodes set up-		(f, triggername) = defineTriggerAlias (show n) "analogWrite" mempty--instance IsPWMPin t => Output (Pin t) PWMDutyCycle where-	(=:) o s = o =: alwaysEvent s+pwm :: Behavior Word8 -> TypedBehavior 'PWM Word8+pwm = TypedBehavior  -- | The on-board LED. led :: Pin '[ 'DigitalIO ]
src/Copilot/Arduino/Internals.hs view
@@ -30,6 +30,16 @@ -- <https://copilot-language.github.io/> type Behavior t = Stream t +-- | A Behavior with an additional phantom type `p`.+--+-- The Compilot DSL only lets a Stream contain basic C types,+-- a limitation that `Behavior` also has. When more type safely+-- is needed, this can be used.+data TypedBehavior p t = TypedBehavior (Behavior t)++-- | A discrete event, that occurs at particular points in time.+data Event p v = Event v (Stream Bool)+ -- | An Arduino sketch, implemented using Copilot. -- -- It's best to think of the `Sketch` as a description of the state of the@@ -75,10 +85,16 @@ instance Monoid Framework where 	mempty = Framework mempty mempty mempty mempty --- | A source of a `Stream` of values input from the Arduino.------ Runs in the `Sketch` monad.-type Input t = Sketch (Stream t)+-- | 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+-- used in two triggers. This generates a name from a suffix, which should+-- be somehow unique.+defineTriggerAlias :: String -> String -> Framework -> (Framework, String)+defineTriggerAlias suffix cfuncname f = +	(f { defines = define : defines f }, triggername)+  where+	triggername = cfuncname <> "_" <> suffix+	define = CLine $ "#define " <> triggername <> " " <> cfuncname  data InputSource t = InputSource 	{ defineVar :: [CLine]@@ -93,7 +109,7 @@ 	, inputStream :: Stream t 	} -mkInput :: InputSource t -> Input t+mkInput :: InputSource t -> Sketch (Behavior t) mkInput i = do 	tell [(return (), f)] 	return (inputStream i)@@ -159,7 +175,7 @@ -- | Things that can have a `Behavior` or `Event` output to them. class Output o t where 	(=:) :: o -> t -> Sketch ()-	-- ^ Conneact a `Behavior` or `Event` to an `Output`+	-- ^ Connect a `Behavior` or `Event` to an `Output` 	-- 	-- > led =: blinking 	--@@ -173,7 +189,8 @@ 	-- > led =: true 	-- 	-- To avoid unncessary work being done, you can use an `Event`-	-- instead. Then only new values of the `Event` will be written.+	-- instead. Then the write only happens at the points in time+	-- when the `Event` occurs. 	--  	-- So to make the LED only be turned on in the first iteration, 	-- and allow it to remain on thereafter without doing extra work:@@ -183,25 +200,36 @@ -- Same fixity as =<< infixr 1 =: --- | A discrete event, that occurs at particular points in time.-data Event v = Event v (Stream Bool)+instance Output o (Event () (Stream v)) => Output o (Behavior v) where+	(=:) o b = o =: te+	  where+	  	te :: Event () (Stream v)+		te = Event b true -instance Output o (Event (Behavior v)) => Output o (Behavior v) where-	(=:) o s = o =: alwaysEvent s+instance Output o (Event p (Stream v)) => Output o (TypedBehavior p v) where+	(=:) o (TypedBehavior b) = o =: te+	  where+		te :: Event p (Stream v)+		te = Event b true -alwaysEvent :: v -> Event v-alwaysEvent s = Event s true+-- | This type family is open, so it can be extended when adding other data+-- types to the IsBehavior class.+type family BehaviorToEvent a+type instance BehaviorToEvent (Behavior v) = Event () (Stream v)+type instance BehaviorToEvent (TypedBehavior p v) = Event p (Stream v) --- | Generate an event, that only occurs when the `Behavior` Bool is True.------ While `v` is usually some type of `Behavior`, this can also be used with--- some other data types that contain a `Behavior`. For example:------ > pin3 := PWMDutyCycle (constant 128) @: firstIteration-(@:) :: v -> Behavior Bool -> Event v-(@:) = Event+class IsBehavior behavior where+	-- | Generate an event, from some type of behavior,+	-- that only occurs when the `Behavior` Bool is True.+	(@:) :: behavior -> Behavior Bool -> BehaviorToEvent behavior -instance IsDigitalIOPin t => Output (Pin t) (Event (Behavior Bool)) where+instance IsBehavior (Behavior v) where+	b @: c = Event b c++instance IsBehavior (TypedBehavior p v) where+	(@:) (TypedBehavior b) c = Event b c++instance IsDigitalIOPin t => Output (Pin t) (Event () (Stream Bool)) where 	(Pin p@(PinId n)) =: (Event b c) = tell [(go, f)] 	  where 		go = trigger triggername c [arg (constant n), arg b]@@ -209,13 +237,45 @@ 			defineTriggerAlias (show n) "digitalWrite" $ 				mempty { pinmodes = M.singleton p (S.singleton OutputMode) } --- | 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--- used in two triggers. This generates a name from a suffix, which should--- be somehow unique.-defineTriggerAlias :: String -> String -> Framework -> (Framework, String)-defineTriggerAlias suffix cfuncname f = -	(f { defines = define : defines f }, triggername)-  where-	triggername = cfuncname <> "_" <> suffix-	define = CLine $ "#define " <> triggername <> " " <> cfuncname+instance IsPWMPin t => Output (Pin t) (Event 'PWM (Stream Word8)) where+	(Pin (PinId n)) =: (Event v c) = tell [(go, f)]+	  where+		go = trigger triggername c [arg (constant n), arg v]+		-- analogWrite does not need any pinmodes set up+		(f, triggername) = defineTriggerAlias (show n) "analogWrite" mempty++class Input o t where+	-- | The list is input to use when simulating the Sketch.+	input' :: o -> [t] -> Sketch (Behavior t)++instance IsDigitalIOPin t => Input (Pin t) Bool where+	input' (Pin p@(PinId n)) interpretvalues = mkInput $ InputSource+		{ defineVar = [CLine $ "bool " <> varname <> ";"]+		, setupInput = []+		, inputPinmode = M.singleton p InputMode+		, readInput = [CLine $ varname <> " = digitalRead(" <> show n <> ");"]+		, inputStream = extern varname interpretvalues'+		}+	  where+		varname = "arduino_digital_pin_input" <> show n+		interpretvalues'+			| null interpretvalues = Nothing+			| otherwise = Just interpretvalues++-- | Voltage read from an Arduino's ADC. Ranges from 0-1023.+type Voltage = Int16++instance IsAnalogInputPin t => Input (Pin t) Voltage where+	input' (Pin (PinId n)) interpretvalues = mkInput $ InputSource+		{ defineVar = [CLine $ "int " <> varname <> ";"]+		, setupInput = []+		, inputPinmode = mempty+		, readInput = [CLine $ varname <> " = analogRead(" <> show n <> ");"]+		, inputStream = extern varname interpretvalues'+		}+	  where+		varname = "arduino_analog_pin_input" <> show n+		interpretvalues'+			| null interpretvalues = Nothing+			| otherwise = Just interpretvalues+
src/Copilot/Arduino/Library/Serial.hs view
@@ -7,15 +7,15 @@  module Copilot.Arduino.Library.Serial ( 	baud,-	output,+	device, 	char, 	str, 	show, 	showFormatted, 	byte,-	input,-	input', 	noInput,+	SerialDevice,+	FormatOutput, 	ShowableType, 	FormatableType, 	Base(..),@@ -34,37 +34,27 @@ baud :: Int -> Sketch () baud = baudD dev --- | Output to the serial port.+-- | Use this to communicate with the serial port, both input and output. ----- Note that this can only be used once in a Sketch.+-- To output to the serial port, simply connect this to a [`FormatOutput`]+-- that describes the serial output. Note that you can only do this once+-- in a Sketch. ----- > main = arduino $ do+-- > main = arduino $ doa -- > 	Serial.baud 9600--- > 	b <- readfrom pin3--- > 	n <- readvoltage a1--- > 	Serial.output true--- > 		[ Serial.str "pin3:"+-- > 	b <- input pin4+-- > 	Serial.device =:+-- > 		[ Serial.str "pin4:" -- > 		, Serial.show b--- > 		, Serial.str " a1:"--- > 		, Serial.show n -- > 		, Serial.char '\n' -- > 		]-output-	:: Stream Bool-	-- ^ This Stream controls when output is sent to the serial port.-	-> [FormatOutput]-	-> Sketch ()-output = outputD dev---- | Input from the serial port. ----- Reads one byte on each iteration of the sketch. When there is no--- serial input available, reads `noInput`.+-- To input from the serial port, use this with `input`. ----- > userinput <- Serial.input-input :: Input Int8-input = inputD dev---- | The list is used to simulate serial input when interpreting the program.-input' :: [Int8] -> Input Int8-input' = input'D dev+-- > userinput <- input Serial.device+--+-- The resulting `Behavior Int8` will be updated on each iteration+-- of the sketch. When there is no new serial input available, it will+-- contain `noInput`.+device :: SerialDevice+device = SerialDevice dev
src/Copilot/Arduino/Library/Serial/Device.hs view
@@ -25,7 +25,7 @@ import Data.Proxy import qualified Prelude --- | Eg "Serial" or "Serial2"+-- | Eg \"Serial\" or \"Serial2\" newtype SerialDeviceName = SerialDeviceName String  baudD :: SerialDeviceName -> Int -> Sketch ()@@ -38,14 +38,14 @@ newtype Baud = Baud Int 	deriving (Show, Eq) -deviceD+configureD 	:: (IsDigitalIOPin rx, IsDigitalIOPin tx) 	=> SerialDeviceName 	-> Pin rx 	-> Pin tx 	-> Baud 	-> Sketch ()-deviceD d@(SerialDeviceName devname) (Pin (PinId rxpin)) (Pin (PinId txpin)) (Baud n) = do+configureD d@(SerialDeviceName devname) (Pin (PinId rxpin)) (Pin (PinId txpin)) (Baud n) = do 	baudD d n 	tell [(return (), f)]   where@@ -62,32 +62,58 @@ 			] 		} -outputD-	:: SerialDeviceName-	-> Stream Bool-	-- ^ This Stream controls when output is sent to the serial port.-	-> [FormatOutput]-	-> Sketch ()-outputD sdn@(SerialDeviceName devname) c l = tell [(go, f)]-  where-	go = trigger triggername c (mapMaybe formatArg l)-	f = mempty { defines = printer }+newtype SerialDevice = SerialDevice SerialDeviceName -	triggername = "arduino_serial_" <> devname <> "_output"+instance Input SerialDevice Int8 where+	input' (SerialDevice (SerialDeviceName devname)) interpretvalues =+		mkInput s+	  where+		s = InputSource+			{ defineVar = [CLine $ "int " <> varname <> ";"]+			, setupInput = []+			, inputPinmode = mempty+			, readInput = [CLine $ varname <> " = " <> devname <> ".read();"]+			, inputStream = extern varname interpretvalues'+			}+		varname = "arduino_serial_" <> devname <> "_read"+		interpretvalues'+			| null interpretvalues = Nothing+			| otherwise = Just interpretvalues -	printer = concat-		[ [CLine $ "void " <> triggername <> "("-			<> intercalate ", " arglist <> ") {"]-		, map (\(fmt, n) -> CLine ("  " <> fromCLine (fmt n)))-			(zip (map (\fo -> formatCLine fo sdn) l) argnames)-		, [CLine "}"]-		]+-- | Value that is read from serial port when there is no input available.+noInput :: Int8+noInput = -1++instance Output SerialDevice [FormatOutput] where+	sdn =: l = sdn =: (Event l true :: Event () [FormatOutput])++instance Output SerialDevice (Event () [FormatOutput]) where+	SerialDevice sdn@(SerialDeviceName devname) =: (Event l c) =+		tell [(go, f)]+	  where+		go = trigger triggername c (mapMaybe formatArg l)+		f = mempty { defines = printer }++		triggername = "arduino_serial_" <> devname <> "_output" 	-	argnames = map (\n -> "arg" <> Prelude.show n) ([1..] :: [Integer])-	arglist = mapMaybe mkarg (zip (map formatCType l) argnames)-	mkarg (Just ctype, argname) = Just (ctype <> " " <> argname)-	mkarg (Nothing, _) = Nothing+		printer = concat+			[ [CLine $ "void " <> triggername <> "("+				<> intercalate ", " arglist <> ") {"]+			, map (\(fmt, n) -> CLine ("  " <> fromCLine (fmt n)))+				(zip (map (\fo -> formatCLine fo sdn) l) argnames)+			, [CLine "}"]+			]+		+		argnames = map (\n -> "arg" <> Prelude.show n) ([1..] :: [Integer])+		arglist = mapMaybe mkarg (zip (map formatCType l) argnames)+		mkarg (Just ctype, argname) = Just (ctype <> " " <> argname)+		mkarg (Nothing, _) = Nothing +instance IsBehavior [FormatOutput] where+	(@:) = Event++type instance BehaviorToEvent [FormatOutput] = Event () [FormatOutput]+ data FormatOutput = FormatOutput 	{ formatArg :: Maybe Arg 	, formatCType :: Maybe String@@ -183,26 +209,3 @@  data Base = BIN | OCT | DEC | HEX 	deriving (Show)--inputD :: SerialDeviceName -> Input Int8-inputD d = input'D d []---- | The list is used to simulate serial input when interpreting the program.-input'D :: SerialDeviceName -> [Int8] -> Input Int8-input'D (SerialDeviceName devname) interpretvalues = mkInput $ InputSource-	{ defineVar = [CLine $ "int " <> varname <> ";"]-	, setupInput = []-	, inputPinmode = mempty-	, readInput = [CLine $ varname <> " = " <> devname <> ".read();"]-	, inputStream = extern varname interpretvalues'-	}-  where-	varname = "arduino_serial_" <> devname <> "_read"-	interpretvalues'-		| null interpretvalues = Nothing-		| otherwise = Just interpretvalues---- | Value that is read from serial port when there is no input available.-noInput :: Int8-noInput = -1-
src/Copilot/Arduino/Library/Serial/XBee.hs view
@@ -7,16 +7,16 @@  module Copilot.Arduino.Library.Serial.XBee ( 	Baud(..),+	configure, 	device,-	output, 	char, 	str, 	show, 	showFormatted, 	byte,-	input,-	input', 	noInput,+	SerialDevice,+	FormatOutput, 	ShowableType, 	FormatableType, 	Base(..),@@ -33,46 +33,36 @@ -- -- This must be included in your sketch if it uses XBee. ----- > XBee.device pin2 pin3 (XBee.Baud 9600)-device+-- > XBee.configure pin2 pin3 (XBee.Baud 9600)+configure 	:: (IsDigitalIOPin rx, IsDigitalIOPin tx) 	=> Pin rx -- ^ pin on which to receive serial data-	-> Pin tx -- ^ pin on which to transfer serial data+	-> Pin tx -- ^ pin on which to send serial data 	-> Baud 	-> Sketch ()-device = deviceD dev+configure = configureD dev --- | Output to XBee.+-- | Use this to communicate with the XBee, both input and output. ----- Note that this can only be used once in a Sketch.+-- To output to the XBee, simply connect this to a [`FormatOutput`]+-- that describes the serial output. Note that you can only do this once+-- in a Sketch. -- -- > main = arduino $ doa--- >    XBee.device pin2 pin3 (XBee.Baud 9600)--- > 	b <- readfrom pin4--- > 	n <- readvoltage a1--- > 	XBee.output true+-- >    XBee.configure pin2 pin3 (XBee.Baud 9600)+-- > 	b <- input pin4+-- > 	XBee.device =: -- > 		[ Serial.str "pin4:" -- > 		, Serial.show b--- > 		, Serial.str " a1:"--- > 		, Serial.show n -- > 		, Serial.char '\n' -- > 		]-output-	:: Stream Bool-	-- ^ This Stream controls when output is sent to the XBee.-	-> [FormatOutput]-	-> Sketch ()-output = outputD dev---- | Input from the XBee. ----- Reads one byte on each iteration of the sketch. When there is no--- serial input available, reads `noInput`.+-- To input from the XBee, use this with `input`. ----- > userinput <- XBee.input-input :: Input Int8-input = inputD dev---- | The list is used to simulate Xbee input when interpreting the program.-input' :: [Int8] -> Input Int8-input' = input'D dev+-- > userinput <- input XBee.device+--+-- The resulting `Behavior Int8` will be updated on each iteration+-- of the sketch. When there is no new serial input available, it will+-- contain `noInput`.+device :: SerialDevice+device = SerialDevice dev
src/Copilot/Arduino/Nano.hs view
@@ -3,7 +3,6 @@ {-# LANGUAGE DataKinds #-}  module Copilot.Arduino.Nano (-	-- * General Arduino programming infrastructure 	module Copilot.Arduino 	-- * Pins 	, pin2@@ -26,9 +25,6 @@ 	, a5 	, a6 	, a7-	-- * UART-	-- Not currently supported!-	--, uart ) where  import Copilot.Arduino
src/Copilot/Arduino/Uno.hs view
@@ -3,7 +3,6 @@ {-# LANGUAGE DataKinds #-}  module Copilot.Arduino.Uno (-	-- * General Arduino programming infrastructure 	module Copilot.Arduino 	-- * Pins 	, pin2@@ -24,9 +23,6 @@ 	, a3 	, a4 	, a5-	-- * UART-	-- Not currently supported!-	--, uart ) where  import Copilot.Arduino
+ test.hs view
@@ -0,0 +1,24 @@+{-# LANGUAGE LambdaCase #-}++import Control.Monad+import System.FilePath+import System.Directory+import System.Process+import System.Exit++main = do+	examples <- filterM doesFileExist+		=<< (map (</> "demo.hs") . map ("examples" </>))+		<$> getDirectoryContents "examples"+	if null examples+		then error "did not find any examples to test"+		else mapM_ shouldCompile examples++shouldCompile :: FilePath -> IO ()+shouldCompile f = do+	let p = (proc "runghc" ["-Wall", "-fno-warn-tabs", takeFileName f])+		{ cwd = Just (takeDirectory f) }+	readCreateProcessWithExitCode p "" >>= \case+		(ExitSuccess, "", "") -> return ()+		v -> error $ "runghc " ++ f +++			" produced unexpected result: " ++ show v