packages feed

arduino-copilot 1.0.1 → 1.1.1

raw patch · 9 files changed

+545/−137 lines, 9 filesdep +containersdep +copilot-languagePVP ok

version bump matches the API change (PVP)

Dependencies added: containers, copilot-language

API changes (from Hackage documentation)

- Copilot.Arduino: data GPIO
- Copilot.Arduino: type Stream t = Stream t
- Copilot.Arduino.Internals: GPIO :: Int16 -> GPIO
- Copilot.Arduino.Internals: newtype GPIO
- Copilot.Arduino.Internals: sketchFramework :: Framework -> [String] -> [CFragment]
- Copilot.Arduino.Internals: type CFragment = String
+ Copilot.Arduino: AnalogInput :: PinCapabilities
+ Copilot.Arduino: DigitalIO :: PinCapabilities
+ Copilot.Arduino: PWM :: PinCapabilities
+ Copilot.Arduino: data Pin t
+ Copilot.Arduino: data PinCapabilities
+ Copilot.Arduino: data Stream a
+ Copilot.Arduino: infixr 4 ||
+ Copilot.Arduino: pullup :: Pin t -> Sketch ()
+ Copilot.Arduino: pwm :: IsPWMPin t => Pin t -> Output DutyCycle
+ Copilot.Arduino: readvoltage :: IsAnalogInputPin t => Pin t -> Input Voltage
+ Copilot.Arduino: readvoltage' :: IsAnalogInputPin t => Pin t -> [Int16] -> Input Voltage
+ Copilot.Arduino: type DutyCycle = Word8
+ Copilot.Arduino: type Voltage = Int16
+ Copilot.Arduino.Internals: AnalogInput :: PinCapabilities
+ Copilot.Arduino.Internals: CLine :: String -> CLine
+ Copilot.Arduino.Internals: DigitalIO :: PinCapabilities
+ Copilot.Arduino.Internals: InputMode :: PinMode
+ Copilot.Arduino.Internals: InputPullupMode :: PinMode
+ Copilot.Arduino.Internals: OutputMode :: PinMode
+ Copilot.Arduino.Internals: PWM :: PinCapabilities
+ Copilot.Arduino.Internals: Pin :: PinId -> Pin t
+ Copilot.Arduino.Internals: PinId :: Int16 -> PinId
+ Copilot.Arduino.Internals: [fromCLine] :: CLine -> String
+ Copilot.Arduino.Internals: [inputPinmode] :: InputSource t -> Map PinId PinMode
+ Copilot.Arduino.Internals: [outputPinmode] :: Output t -> Map PinId PinMode
+ Copilot.Arduino.Internals: [pinmodes] :: Framework -> Map PinId (Set PinMode)
+ Copilot.Arduino.Internals: data PinCapabilities
+ Copilot.Arduino.Internals: data PinMode
+ Copilot.Arduino.Internals: instance GHC.Classes.Eq Copilot.Arduino.Internals.PinCapabilities
+ Copilot.Arduino.Internals: instance GHC.Classes.Eq Copilot.Arduino.Internals.PinId
+ Copilot.Arduino.Internals: instance GHC.Classes.Eq Copilot.Arduino.Internals.PinMode
+ Copilot.Arduino.Internals: instance GHC.Classes.Ord Copilot.Arduino.Internals.PinCapabilities
+ Copilot.Arduino.Internals: instance GHC.Classes.Ord Copilot.Arduino.Internals.PinId
+ Copilot.Arduino.Internals: instance GHC.Classes.Ord Copilot.Arduino.Internals.PinMode
+ Copilot.Arduino.Internals: instance GHC.Show.Show Copilot.Arduino.Internals.PinCapabilities
+ Copilot.Arduino.Internals: instance GHC.Show.Show Copilot.Arduino.Internals.PinId
+ Copilot.Arduino.Internals: instance GHC.Show.Show Copilot.Arduino.Internals.PinMode
+ Copilot.Arduino.Internals: instance forall k (t :: k). GHC.Classes.Eq (Copilot.Arduino.Internals.Pin t)
+ Copilot.Arduino.Internals: instance forall k (t :: k). GHC.Classes.Ord (Copilot.Arduino.Internals.Pin t)
+ Copilot.Arduino.Internals: instance forall k (t :: k). GHC.Show.Show (Copilot.Arduino.Internals.Pin t)
+ Copilot.Arduino.Internals: newtype CLine
+ Copilot.Arduino.Internals: newtype Pin t
+ Copilot.Arduino.Internals: newtype PinId
+ Copilot.Arduino.Internals: type family SameCapability (a :: PinCapabilities) (b :: PinCapabilities) :: Bool
+ Copilot.Arduino.Library.Serial: BIN :: Base
+ Copilot.Arduino.Library.Serial: DEC :: Base
+ Copilot.Arduino.Library.Serial: HEX :: Base
+ Copilot.Arduino.Library.Serial: OCT :: Base
+ Copilot.Arduino.Library.Serial: baud :: Int -> Sketch ()
+ Copilot.Arduino.Library.Serial: char :: Char -> FormatOutput
+ Copilot.Arduino.Library.Serial: class FormatableType t f
+ Copilot.Arduino.Library.Serial: class ShowableType t
+ Copilot.Arduino.Library.Serial: data Base
+ Copilot.Arduino.Library.Serial: input :: Input Int8
+ Copilot.Arduino.Library.Serial: input' :: [Int8] -> Input Int8
+ Copilot.Arduino.Library.Serial: instance Copilot.Arduino.Library.Serial.FormatableType GHC.Types.Float GHC.Types.Int
+ Copilot.Arduino.Library.Serial: instance Copilot.Arduino.Library.Serial.ShowableType GHC.Int.Int16
+ Copilot.Arduino.Library.Serial: instance Copilot.Arduino.Library.Serial.ShowableType GHC.Int.Int32
+ Copilot.Arduino.Library.Serial: instance Copilot.Arduino.Library.Serial.ShowableType GHC.Int.Int64
+ Copilot.Arduino.Library.Serial: instance Copilot.Arduino.Library.Serial.ShowableType GHC.Int.Int8
+ Copilot.Arduino.Library.Serial: instance Copilot.Arduino.Library.Serial.ShowableType GHC.Types.Bool
+ Copilot.Arduino.Library.Serial: instance Copilot.Arduino.Library.Serial.ShowableType GHC.Types.Double
+ Copilot.Arduino.Library.Serial: instance Copilot.Arduino.Library.Serial.ShowableType GHC.Types.Float
+ Copilot.Arduino.Library.Serial: instance Copilot.Arduino.Library.Serial.ShowableType GHC.Word.Word16
+ Copilot.Arduino.Library.Serial: instance Copilot.Arduino.Library.Serial.ShowableType GHC.Word.Word32
+ Copilot.Arduino.Library.Serial: instance Copilot.Arduino.Library.Serial.ShowableType GHC.Word.Word64
+ Copilot.Arduino.Library.Serial: instance Copilot.Arduino.Library.Serial.ShowableType GHC.Word.Word8
+ Copilot.Arduino.Library.Serial: instance GHC.Real.Integral t => Copilot.Arduino.Library.Serial.FormatableType t Copilot.Arduino.Library.Serial.Base
+ Copilot.Arduino.Library.Serial: instance GHC.Show.Show Copilot.Arduino.Library.Serial.Base
+ Copilot.Arduino.Library.Serial: noInput :: Int8
+ Copilot.Arduino.Library.Serial: output :: Stream Bool -> [FormatOutput] -> Sketch ()
+ Copilot.Arduino.Library.Serial: show :: forall t. (ShowableType 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: str :: String -> FormatOutput
+ Copilot.Arduino.Nano: a0 :: Pin '[ 'AnalogInput, 'DigitalIO]
+ Copilot.Arduino.Nano: a1 :: Pin '[ 'AnalogInput, 'DigitalIO]
+ Copilot.Arduino.Nano: a2 :: Pin '[ 'AnalogInput, 'DigitalIO]
+ Copilot.Arduino.Nano: a3 :: Pin '[ 'AnalogInput, 'DigitalIO]
+ Copilot.Arduino.Nano: a4 :: Pin '[ 'AnalogInput, 'DigitalIO]
+ Copilot.Arduino.Nano: a5 :: Pin '[ 'AnalogInput, 'DigitalIO]
+ Copilot.Arduino.Nano: a6 :: Pin '[ 'AnalogInput]
+ Copilot.Arduino.Nano: a7 :: Pin '[ 'AnalogInput]
+ Copilot.Arduino.Uno: a0 :: Pin '[ 'AnalogInput, 'DigitalIO]
+ Copilot.Arduino.Uno: a1 :: Pin '[ 'AnalogInput, 'DigitalIO]
+ Copilot.Arduino.Uno: a2 :: Pin '[ 'AnalogInput, 'DigitalIO]
+ Copilot.Arduino.Uno: a3 :: Pin '[ 'AnalogInput, 'DigitalIO]
+ Copilot.Arduino.Uno: a4 :: Pin '[ 'AnalogInput, 'DigitalIO]
+ Copilot.Arduino.Uno: a5 :: Pin '[ 'AnalogInput, 'DigitalIO]
- Copilot.Arduino: infixr 1 =<<
+ Copilot.Arduino: infixr 1 ++
- Copilot.Arduino: readfrom :: GPIO -> Input Bool
+ Copilot.Arduino: readfrom :: IsDigitalIOPin t => Pin t -> Input Bool
- Copilot.Arduino: readfrom' :: GPIO -> [Bool] -> Input Bool
+ Copilot.Arduino: readfrom' :: IsDigitalIOPin t => Pin t -> [Bool] -> Input Bool
- Copilot.Arduino: writeto :: GPIO -> Output Bool
+ Copilot.Arduino: writeto :: IsDigitalIOPin t => Pin t -> Output Bool
- Copilot.Arduino.Internals: Framework :: [CFragment] -> [CFragment] -> [CFragment] -> Framework
+ Copilot.Arduino.Internals: Framework :: [CLine] -> [CLine] -> Map PinId (Set PinMode) -> [CLine] -> Framework
- Copilot.Arduino.Internals: InputSource :: [CFragment] -> [CFragment] -> [CFragment] -> Stream t -> InputSource t
+ Copilot.Arduino.Internals: InputSource :: [CLine] -> [CLine] -> Map PinId PinMode -> [CLine] -> Stream t -> InputSource t
- Copilot.Arduino.Internals: Output :: [CFragment] -> Stream Bool -> (Stream Bool -> Behavior t) -> Output t
+ Copilot.Arduino.Internals: Output :: [CLine] -> Map PinId PinMode -> Stream Bool -> (Stream Bool -> Behavior t) -> Output t
- Copilot.Arduino.Internals: [defineVar] :: InputSource t -> [CFragment]
+ Copilot.Arduino.Internals: [defineVar] :: InputSource t -> [CLine]
- Copilot.Arduino.Internals: [defines] :: Framework -> [CFragment]
+ Copilot.Arduino.Internals: [defines] :: Framework -> [CLine]
- Copilot.Arduino.Internals: [loops] :: Framework -> [CFragment]
+ Copilot.Arduino.Internals: [loops] :: Framework -> [CLine]
- Copilot.Arduino.Internals: [readInput] :: InputSource t -> [CFragment]
+ Copilot.Arduino.Internals: [readInput] :: InputSource t -> [CLine]
- Copilot.Arduino.Internals: [setupInput] :: InputSource t -> [CFragment]
+ Copilot.Arduino.Internals: [setupInput] :: InputSource t -> [CLine]
- Copilot.Arduino.Internals: [setupOutput] :: Output t -> [CFragment]
+ Copilot.Arduino.Internals: [setupOutput] :: Output t -> [CLine]
- Copilot.Arduino.Internals: [setups] :: Framework -> [CFragment]
+ Copilot.Arduino.Internals: [setups] :: Framework -> [CLine]
- Copilot.Arduino.Nano: pin10 :: GPIO
+ Copilot.Arduino.Nano: pin10 :: Pin '[ 'DigitalIO, 'PWM]
- Copilot.Arduino.Nano: pin11 :: GPIO
+ Copilot.Arduino.Nano: pin11 :: Pin '[ 'DigitalIO, 'PWM]
- Copilot.Arduino.Nano: pin12 :: GPIO
+ Copilot.Arduino.Nano: pin12 :: Pin '[ 'DigitalIO]
- Copilot.Arduino.Nano: pin13 :: GPIO
+ Copilot.Arduino.Nano: pin13 :: Pin '[ 'DigitalIO]
- Copilot.Arduino.Nano: pin2 :: GPIO
+ Copilot.Arduino.Nano: pin2 :: Pin '[ 'DigitalIO]
- Copilot.Arduino.Nano: pin3 :: GPIO
+ Copilot.Arduino.Nano: pin3 :: Pin '[ 'DigitalIO, 'PWM]
- Copilot.Arduino.Nano: pin4 :: GPIO
+ Copilot.Arduino.Nano: pin4 :: Pin '[ 'DigitalIO]
- Copilot.Arduino.Nano: pin5 :: GPIO
+ Copilot.Arduino.Nano: pin5 :: Pin '[ 'DigitalIO, 'PWM]
- Copilot.Arduino.Nano: pin6 :: GPIO
+ Copilot.Arduino.Nano: pin6 :: Pin '[ 'DigitalIO, 'PWM]
- Copilot.Arduino.Nano: pin7 :: GPIO
+ Copilot.Arduino.Nano: pin7 :: Pin '[ 'DigitalIO]
- Copilot.Arduino.Nano: pin8 :: GPIO
+ Copilot.Arduino.Nano: pin8 :: Pin '[ 'DigitalIO]
- Copilot.Arduino.Nano: pin9 :: GPIO
+ Copilot.Arduino.Nano: pin9 :: Pin '[ 'DigitalIO, 'PWM]
- Copilot.Arduino.Uno: pin10 :: GPIO
+ Copilot.Arduino.Uno: pin10 :: Pin '[ 'DigitalIO, 'PWM]
- Copilot.Arduino.Uno: pin11 :: GPIO
+ Copilot.Arduino.Uno: pin11 :: Pin '[ 'DigitalIO, 'PWM]
- Copilot.Arduino.Uno: pin12 :: GPIO
+ Copilot.Arduino.Uno: pin12 :: Pin '[ 'DigitalIO]
- Copilot.Arduino.Uno: pin13 :: GPIO
+ Copilot.Arduino.Uno: pin13 :: Pin '[ 'DigitalIO]
- Copilot.Arduino.Uno: pin2 :: GPIO
+ Copilot.Arduino.Uno: pin2 :: Pin '[ 'DigitalIO]
- Copilot.Arduino.Uno: pin3 :: GPIO
+ Copilot.Arduino.Uno: pin3 :: Pin '[ 'DigitalIO, 'PWM]
- Copilot.Arduino.Uno: pin4 :: GPIO
+ Copilot.Arduino.Uno: pin4 :: Pin '[ 'DigitalIO]
- Copilot.Arduino.Uno: pin5 :: GPIO
+ Copilot.Arduino.Uno: pin5 :: Pin '[ 'DigitalIO, 'PWM]
- Copilot.Arduino.Uno: pin6 :: GPIO
+ Copilot.Arduino.Uno: pin6 :: Pin '[ 'DigitalIO, 'PWM]
- Copilot.Arduino.Uno: pin7 :: GPIO
+ Copilot.Arduino.Uno: pin7 :: Pin '[ 'DigitalIO]
- Copilot.Arduino.Uno: pin8 :: GPIO
+ Copilot.Arduino.Uno: pin8 :: Pin '[ 'DigitalIO]
- Copilot.Arduino.Uno: pin9 :: GPIO
+ Copilot.Arduino.Uno: pin9 :: Pin '[ 'DigitalIO, 'PWM]

Files

CHANGELOG view
@@ -1,3 +1,18 @@+arduino-copilot (1.1.1) unstable; urgency=medium++  * New Copilot.Arduino.Library.Serial module.+  * Added the analog pins for Uno and Nano, and support using+    them for digital IO, when the hardware allows that to be done.+  * Support reading voltage from analog pins.+  * Support PWM.+  * Added a way to enable pullup resistors for input pins.+  * Detect if a program tries to use the same pin for both input and+    output. This is not currently supported, so it will refuse to build the+    program.+  * Copilot.Arduino.Internals API changed.++ -- Joey Hess <id@joeyh.name>  Sun, 26 Jan 2020 20:37:26 -0400+ arduino-copilot (1.0.1) unstable; urgency=medium    * Move example from cabal description (where it did not render correctly)
TODO view
@@ -1,2 +1,11 @@-* analog pins-* serial console support (involves arrays of bytes)+* 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+  to a floating point actual voltage, given also a stream that contains+  the reference voltage.+* delayMicroseconds+* random numbers+* Wire library
arduino-copilot.cabal view
@@ -1,5 +1,5 @@ Name: arduino-copilot-Version: 1.0.1+Version: 1.1.1 Cabal-Version: >= 1.8 License: BSD3 Maintainer: Joey Hess <id@joeyh.name>@@ -50,18 +50,21 @@     Copilot.Arduino.Nano     Copilot.Arduino.Uno     Copilot.Arduino.Internals+    Copilot.Arduino.Library.Serial   Other-Modules:     Copilot.Arduino.Main   Build-Depends:     base (>= 4.5 && < 5),     copilot (== 3.1.*),     copilot-c99 (== 3.1.*),+    copilot-language (== 3.1.*),     filepath,     directory,     mtl,     unix,-    optparse-applicative (>= 0.14.1)+    optparse-applicative (>= 0.14.1),+    containers  source-repository head   type: git-  location: git://git.joeyh.name/haskell-arduino-copilot.git+  location: git://git.joeyh.name/arduino-copilot.git
src/Copilot/Arduino.hs view
@@ -1,9 +1,15 @@ -- | Programming the Arduino with Copilot. -- -- This module should work on any model of Arduino.--- See Arduino.Uno and Arduino.Nano for model-specific code.+-- See Copilot.Arduino.Uno and Copilot.Arduino.Nano for model-specific code.+--+-- There are also libraries like Copilot.Arduino.Library.Serial to support+-- additional hardware.  {-# LANGUAGE RebindableSyntax #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE TypeFamilies #-}  module Copilot.Arduino ( 	-- * Arduino sketch generation@@ -11,21 +17,35 @@ 	Sketch, 	Input, 	Output,+	-- | A value that varies over time.+	--+	-- The Copilot DSL provides many operations on streams, for example+	-- `Language.Copilot.&&` to combine two streams of Bools.+	-- +	-- For documentation on using the Copilot DSL, see+	-- <https://copilot-language.github.io/> 	Stream,-	GPIO,+	Pin,+	PinCapabilities(..), 	-- * Combinators 	(=:), 	(@:), 	-- * Inputs 	readfrom, 	readfrom',+	pullup,+	Voltage,+	readvoltage,+	readvoltage', 	-- * Outputs 	-- 	-- | Only outputs that work on all Arduino boards are included-	-- here. Load a module such as Arduino.Uno for model-specific-	-- outputs.+	-- here. Import a module such as Copilot.Arduino.Uno for+	-- model-specific outputs. 	led, 	writeto,+	DutyCycle,+	pwm, 	MicroSeconds, 	delay, 	-- * Utilities@@ -43,27 +63,20 @@ 	-- 	-- For documentation on using the Copilot DSL, see 	-- <https://copilot-language.github.io/>-	module Language.Copilot,+	module X, ) where -import qualified Language.Copilot-import Language.Copilot hiding (Stream)+import Language.Copilot as X hiding (Stream)+import Language.Copilot (Stream) import Copilot.Arduino.Internals import Copilot.Arduino.Main import Control.Monad.Writer---- | A value that varies over time.------ The Copilot DSL provides many operations on streams, for example--- `Language.Copilot.&&` to combine two streams of Bools.--- --- For documentation on using the Copilot DSL, see--- <https://copilot-language.github.io/>-type Stream t = Language.Copilot.Stream t+import qualified Data.Map as M+import qualified Data.Set as S  -- | Connect a `Stream` to an `Output`. ----- > led := blinking+-- > led =: blinking (=:) :: Output t -> Stream t -> Sketch () o =: s = tell [(go, toFramework o)]   where@@ -109,50 +122,110 @@  -- | 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.+--+-- > delay =: constant 100 delay :: Output MicroSeconds delay = Output 	{ setupOutput = [] 	, outputBehavior = \c n -> trigger "delay" c [arg n] 	, outputCond = true+	, outputPinmode = mempty 	} --- | Reading from a GPIO pin.+-- | Reading a Bool from a `Pin`. -- -- > do -- > 	buttonpressed <- readfrom pin12--- > 	led := buttonpressed-readfrom :: GPIO -> Input Bool-readfrom n = readfrom' n []+-- > 	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' :: GPIO -> [Bool] -> Input Bool-readfrom' (GPIO n) interpretvalues = mkInput $ InputSource-	{ defineVar = ["bool " <> varname]-	, setupInput = ["pinMode(" <> show n <> ", INPUT)"]-	, readInput = [varname <> " = digitalRead(" <> show n <> ")"]+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_gpio_input" <> show n+	varname = "arduino_digital_pin_input" <> show n 	interpretvalues' 		| null interpretvalues = Nothing 		| otherwise = Just interpretvalues --- | Writing to a GPIO pin.-writeto :: GPIO -> Output Bool-writeto  (GPIO n) = Output-	{ setupOutput = ["pinMode(" <> sn <> ", OUTPUT)"]+-- | Normally when a `Pin` is read with `readfrom`, 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 :: Pin t -> Sketch ()+pullup (Pin p) = tell [(return (), f)]+  where+	f = mempty+		{ 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++-- | Writing to a pin.+--+-- > writeto pin3 =: blinking+writeto :: IsDigitalIOPin t => Pin t -> Output Bool+writeto (Pin p@(PinId n)) = Output+	{ setupOutput = []+	, outputPinmode = M.singleton p OutputMode 	, outputBehavior =  		\c v -> trigger "digitalWrite" c [arg (constant n), arg v] 	, outputCond = true 	}-  where-	sn = show n +-- | PWM output to a pin.+--+-- > pwm pin3 =: constant 128+pwm :: IsPWMPin t => Pin t -> Output DutyCycle+pwm (Pin (PinId n)) = Output+	{ setupOutput = []+	, outputPinmode = mempty -- analogWrite does not need pinMode+	, outputBehavior =+		\c v -> trigger "analogWrite" c [arg (constant n), arg v]+	, outputCond = true+	}++-- | Describes a PWM square wave. Between 0 (always off) and 255 (always on).+type DutyCycle = Word8+ -- | The on-board LED. led :: Output Bool-led = writeto (GPIO 13)+led = writeto p+  where+	p :: Pin '[ 'DigitalIO ]+	p = Pin (PinId 13) +-- | An 8 bit character. -- | Extracts a copilot `Spec` from a `Sketch`. -- -- This can be useful to intergrate with other libraries 
src/Copilot/Arduino/Internals.hs view
@@ -1,14 +1,22 @@ -- | You should not need to import this module unless you're adding support -- for a new model of Arduino, or an Arduino library. -{-# LANGUAGE RebindableSyntax #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE UndecidableInstances #-}  module Copilot.Arduino.Internals where  import Language.Copilot import Control.Monad.Writer+import qualified Data.Map as M+import qualified Data.Set as S+import Data.Type.Bool+import GHC.TypeLits  -- | An Arduino sketch, implemented using Copilot. --@@ -30,26 +38,29 @@  -- | The framework of an Arduino sketch. data Framework = Framework-	{ defines :: [CFragment]+	{ defines :: [CLine] 	-- ^ Things that come before the C code generated by Copilot.-	, setups :: [CFragment]-	-- ^ Things to run in `setup`.-	, loops :: [CFragment]+	, setups :: [CLine]+	-- ^ Things to do at setup, not including configuring pins.+	, pinmodes :: M.Map PinId (S.Set PinMode)+	-- ^ How pins are used.+	, loops :: [CLine] 	-- ^ Things to run in `loop`. 	} --- | A fragment of C code.-type CFragment = String+-- | A line of C code.+newtype CLine = CLine { fromCLine :: String }  instance Semigroup Framework where 	a <> b = Framework 		{ defines = defines a <> defines b 		, setups = setups a <> setups 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 = Framework mempty mempty mempty mempty  class ToFramework t where 	toFramework :: t -> Framework@@ -59,8 +70,9 @@ -- | Somewhere that a Stream can be directed to, in order to control the -- Arduino. data Output t = Output-	{ setupOutput :: [CFragment]-	-- ^ How to set up the output.+	{ setupOutput :: [CLine]+	-- ^ How to set up the output, not including pin mode.+	, outputPinmode :: M.Map PinId PinMode 	, outputCond :: Stream Bool 	, outputBehavior :: Stream Bool -> Behavior t 	}@@ -69,6 +81,7 @@ 	toFramework o = Framework 		{ defines = mempty 		, setups = setupOutput o+		, pinmodes = M.map S.singleton (outputPinmode o) 		, loops = mempty 		} @@ -78,12 +91,13 @@ type Input t = Sketch (Stream t)  data InputSource t = InputSource-	{ defineVar :: [CFragment]+	{ defineVar :: [CLine] 	-- ^ Added to the `Framework`'s `defines`, this typically 	-- defines a C variable.-	, setupInput :: [CFragment]-	-- ^ How to set up the input.-	, readInput :: [CFragment]+	, setupInput :: [CLine]+	-- ^ How to set up the input, not including pin mode.+	, inputPinmode :: M.Map PinId PinMode+	, readInput :: [CLine] 	-- ^ How to read a value from the input, this typically 	-- reads a value into a C variable. 	, inputStream :: Stream t@@ -93,6 +107,7 @@ 	toFramework i = Framework 		{ defines = defineVar i 		, setups = setupInput i+		, pinmodes = M.map S.singleton (inputPinmode i) 		, loops = readInput i 		} @@ -101,45 +116,57 @@ 	tell [(return (), toFramework i)] 	return (inputStream i) --- | A GPIO pin+-- | A pin on the Arduino board. ----- For definitions of GPIO pins like `Copilot.Arduino.Uno.pin12`, +-- For definitions of pins like `Copilot.Arduino.Uno.pin12`,  -- load a module such as Copilot.Arduino.Uno, which provides the pins of a -- particular board.-newtype GPIO = GPIO Int16+--+-- Some pins can only be used for digital IO, while others support+-- analog input and/or digital IO. A type-level list of PinCapabilties+-- indicates how a Pin can be used.+newtype Pin t = Pin PinId+	deriving (Show, Eq, Ord) +newtype PinId = PinId Int16+	deriving (Show, Eq, Ord)++data PinCapabilities+	= DigitalIO+	| AnalogInput+	| PWM+	deriving (Show, Eq, Ord)++type family IsDigitalIOPin t where+	IsDigitalIOPin t = +		'True ~ If (HasPinCapability 'DigitalIO t)+			('True)+			(TypeError ('Text "This Pin does not support digital IO"))++type family IsAnalogInputPin t where+	IsAnalogInputPin t = +		'True ~ If (HasPinCapability 'AnalogInput t)+			('True)+			(TypeError ('Text "This Pin does not support analog input"))++type family IsPWMPin t where+	IsPWMPin t = +		'True ~ If (HasPinCapability 'PWM t)+			('True)+			(TypeError ('Text "This Pin does not support PWM"))++type family HasPinCapability (c :: t) (list :: [t]) :: Bool where+	HasPinCapability c '[] = 'False+	HasPinCapability c (x ': xs) = SameCapability c x || HasPinCapability c xs++type family SameCapability (a :: PinCapabilities) (b :: PinCapabilities) :: Bool where+	SameCapability 'DigitalIO 'DigitalIO = 'True+	SameCapability 'AnalogInput 'AnalogInput = 'True+	SameCapability 'PWM 'PWM = 'True+	SameCapability _ _ = 'False++data PinMode = InputMode | InputPullupMode | OutputMode+	deriving (Show, Eq, Ord)+ -- FIXME should be a newtype, but how to make a stream of a newtype? type MicroSeconds = Int16---- | Makes an arduino sketch, using a Framework, and a list of lines of C--- code generated by Copilot.-sketchFramework :: Framework -> [String] -> [CFragment]-sketchFramework f ccode = concat-	[-		[ "/* automatically generated, do not edit */"-		, blank-		, "#include <stdbool.h>"-		, "#include <stdint.h>"-		, blank-		]-	, map statement (defines f)-	, [blank]-	, ccode-	, [blank]-	,-		[ "void setup()"-		]-	, codeblock $ map statement (setups f)-	, [blank]-	,-		[ "void loop()"-		]-	, codeblock $ map statement $ (loops f) <>-		[ "step()"-		]-	]-  where-	blank = ""-	indent l = "  " <> l-	statement d = d <> ";"-	codeblock l = ["{"] <> map indent l <> ["}"]
+ src/Copilot/Arduino/Library/Serial.hs view
@@ -0,0 +1,192 @@+-- | Serial port library for arduino-copilot.+--+-- This module is designed to be imported qualified as Serial++{-# LANGUAGE RebindableSyntax #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}++module Copilot.Arduino.Library.Serial (+	baud,+	output,+	char,+	str,+	show,+	showFormatted,+	input,+	input',+	noInput,+	ShowableType,+	FormatableType,+	Base(..),+) where++import Copilot.Arduino hiding (show)+import Copilot.Arduino.Internals+import Control.Monad.Writer+import Copilot.Language.Stream (Arg)+import Data.List+import Data.Maybe+import Data.Proxy+import qualified Prelude++-- | Configure the baud rate of the serial port.+--+-- This must be included in your sketch if it uses the serial port.+baud :: Int -> Sketch ()+baud n = tell [(return (), f)]+  where+	f = mempty+		{ setups = [CLine $ "Serial.begin(" <> Prelude.show n <> ");"]+		}++-- | Output to the serial port.+--+-- Note that this can only be used once in a Sketch.+--+-- > main = arduino $ do+-- > 	Serial.baud 9600+-- > 	b <- readfrom pin3+-- > 	n <- readvoltage a1+-- > 	Serial.output true+-- > 		[ Serial.str "pin3:"+-- > 		, 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 c l = tell [(go, f)]+  where+	go = trigger "arduino_serial_output" c (mapMaybe formatArg l)+	f = mempty { defines = printer }++	printer = concat+		[ [CLine $ "void arduino_serial_output("+			<> intercalate ", " arglist <> ") {"]+		, map (\(fmt, n) -> CLine ("  " <> fromCLine (fmt n)))+			(zip (map formatCLine 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++data FormatOutput = FormatOutput+	{ formatArg :: Maybe Arg+	, formatCType :: Maybe String+	, formatCLine :: String -> CLine+	}++-- | Use this to output a Char+char :: Char -> FormatOutput+char c = FormatOutput Nothing Nothing+	(\_ -> CLine $ "Serial.print('" <> esc c <> "');")+  where+	esc '\'' = "\\\'"+	esc '\\' = "\\\\"+	esc '\n' = "\\n"+	esc c' = [c']++-- | Use this to output a String+str :: String -> FormatOutput+str s = FormatOutput Nothing Nothing+	(\_ -> CLine $ "Serial.print(\"" <> concatMap esc s <> "\");")+  where+	esc '"' = "\""+	esc '\\' = "\\"+	esc c = [c]++-- | 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 s = FormatOutput+	(Just (arg s))+	(Just (showCType (Proxy @t)))+	(\v -> CLine $ "Serial.print(" <> v <> ");")++-- | Show the current value of a Stream with control over the formatting.+--+-- When used with a Float, provide the number of decimal places+-- to show.+--+-- > Serial.show (constant (1.234 :: Float)) 2 -- "1.23"+--+-- When used with any Integral type, provide the `Base` to display it in+--+-- > Serial.show (constant (78 :: Int8)) Serial.HEX -- "4E"+showFormatted :: forall t f. (ShowableType t, Typed t, FormatableType t f) => Stream t -> f -> FormatOutput+showFormatted s f = FormatOutput+	(Just (arg s))+	(Just (showCType t))+	(\v -> CLine $ "Serial.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++instance FormatableType Float Int where+	formatter _ precision = Prelude.show precision++instance Integral t => FormatableType t Base where+	formatter _ b = Prelude.show b++data Base = BIN | OCT | DEC | HEX+	deriving (Show)++-- | Input from the serial port.+--+-- Reads one byte on each iteration of the sketch. When there is no+-- serial input available, reads `noInput`.+--+-- > userinput <- Serial.input+input :: Input Int8+input = input' []++-- | The list is used to simulate serial input when interpreting the program.+input' :: [Int8] -> Input Int8+input' interpretvalues = mkInput $ InputSource+	{ defineVar = [CLine $ "int " <> varname <> ";"]+	, setupInput = []+	, inputPinmode = mempty+	, readInput = [CLine $ varname <> " = Serial.read();"]+	, inputStream = extern varname interpretvalues'+	}+  where+	varname = "arduino_serial_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/Main.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE BangPatterns #-}+ module Copilot.Arduino.Main (arduino) where  import Language.Copilot (Spec, interpret, reify)@@ -8,6 +10,9 @@ import System.FilePath import Control.Monad.Writer import Data.List (isInfixOf)+import Data.Maybe+import qualified Data.Map as M+import qualified Data.Set as S import Options.Applicative  -- | Typically your Arduino program's main will be implemented using this.@@ -45,10 +50,14 @@  	go o = case interpretSteps o of 		Just n -> interpret n spec-		Nothing -> writeIno spec (mconcat fs)+		Nothing -> writeIno spec f 	 	(is, fs) = unzip (execWriter s) 	spec = sequence_ is+	+	-- Strict evaluation because this may throw errors,+	-- and we want to throw them before starting compilation.+	!f = finalizeFramework (mconcat fs)  data CmdLine = CmdLine 	{ interpretSteps :: Maybe Integer@@ -80,5 +89,64 @@ 	d <- getCurrentDirectory 	let dirbase = takeFileName d 	writeFile (addExtension dirbase "ino") $ -		unlines $ sketchFramework framework c'+		unlines $ map fromCLine $+			sketchFramework framework (map CLine c') 	removeDirectoryRecursive mytmpdir++-- Makes an arduino sketch, using a Framework, and a list of lines of C+-- code generated by Copilot.+sketchFramework :: Framework -> [CLine] -> [CLine]+sketchFramework f ccode = map CLine $ concat+	[+		[ "/* automatically generated, do not edit */"+		, blank+		, "#include <stdbool.h>"+		, "#include <stdint.h>"+		, blank+		]+	, map fromCLine (defines f)+	, [blank]+	, map fromCLine ccode+	, [blank]+	,+		[ "void setup()"+		]+	, codeblock $ map fromCLine (setups f)+	, [blank]+	,+		[ "void loop()"+		]+	, codeblock $ map fromCLine (loops f) <>+		[ "step();"+		]+	]+  where+	blank = ""+	indent l = "  " <> l+	codeblock l = ["{"] <> map indent l <> ["}"]++finalizeFramework :: Framework -> Framework+finalizeFramework f = +	let pinsetups = mapMaybe setuppinmode (M.toList $ pinmodes f)+	in f { setups = pinsetups <> setups f }+  where+	setuppinmode (PinId n, s)+		| s == S.singleton OutputMode =+			setmode n "OUTPUT"+		| s == S.singleton InputMode =+			setmode n "INPUT"+		| s == S.singleton InputPullupMode =+			setmode n "INPUT_PULLUP"+		| s == S.fromList [InputMode, InputPullupMode] =+			-- Enabling pullup is documented to make all+			-- reads from that pin be with pullup.+			setmode n "INPUT_PULLUP"+		| S.null s = Nothing+		| otherwise = error $+			"The program uses pin " ++ show n +++			" in multiple ways in different places (" +++			unwords (map show (S.toList s)) ++ "). " +++			"This is not currently supported by arduino-copilot."+	+	setmode n v = Just $ CLine $+		"pinMode(" <> show n <> ", " ++ v ++ ");"
src/Copilot/Arduino/Nano.hs view
@@ -1,9 +1,11 @@ -- | Programming the Arduino Nano with Copilot. +{-# LANGUAGE DataKinds #-}+ module Copilot.Arduino.Nano ( 	-- * General Arduino programming infrastructure 	module Copilot.Arduino-	-- * GPIO+	-- * Pins 	, pin2 	, pin3 	, pin4@@ -16,16 +18,14 @@ 	, pin11 	, pin12 	, pin13-	-- * Analog input-	-- TODO!-	--, a0-	--, a1-	--, a2-	--, a3-	--, a4-	--, a5-	--, a6-	--, a7+	, a0+	, a1+	, a2+	, a3+	, a4+	, a5+	, a6+	, a7 	-- * UART 	-- Not currently supported! 	--, uart@@ -34,18 +34,31 @@ import Copilot.Arduino import Copilot.Arduino.Internals -pin2, pin3, pin4, pin5, pin6, pin7, pin8, pin9, pin10, pin11, pin12, pin13 :: GPIO-pin2 = GPIO 2-pin3 = GPIO 3-pin4 = GPIO 4-pin5 = GPIO 5-pin6 = GPIO 6-pin7 = GPIO 7-pin8 = GPIO 8-pin9 = GPIO 9-pin10 = GPIO 10-pin11 = GPIO 11-pin12 = GPIO 12+pin2, pin4, pin7, pin8, pin12, pin13 :: Pin '[ 'DigitalIO ]+pin3, pin5, pin6, pin9, pin10, pin11 :: Pin '[ 'DigitalIO, 'PWM ]+pin2 = Pin (PinId 2)+pin3 = Pin (PinId 3)+pin4 = Pin (PinId 4)+pin5 = Pin (PinId 5)+pin6 = Pin (PinId 6)+pin7 = Pin (PinId 7)+pin8 = Pin (PinId 8)+pin9 = Pin (PinId 9)+pin10 = Pin (PinId 10)+pin11 = Pin (PinId 11)+pin12 = Pin (PinId 12) -- | This pin is connected to the `led`-pin13 = GPIO 13+pin13 = Pin (PinId 13) +a0, a1, a2, a3, a4, a5 :: Pin '[ 'AnalogInput, 'DigitalIO ]+a0 = Pin (PinId 14)+a1 = Pin (PinId 15)+a2 = Pin (PinId 16)+a3 = Pin (PinId 17)+a4 = Pin (PinId 18)+a5 = Pin (PinId 19)++-- | Limited to analog input.+a6, a7 :: Pin '[ 'AnalogInput ]+a6 = Pin (PinId 20)+a7 = Pin (PinId 21)
src/Copilot/Arduino/Uno.hs view
@@ -1,9 +1,11 @@ -- | Programming the Arduino Uno with Copilot. +{-# LANGUAGE DataKinds #-}+ module Copilot.Arduino.Uno ( 	-- * General Arduino programming infrastructure 	module Copilot.Arduino-	-- * GPIO+	-- * Pins 	, pin2 	, pin3 	, pin4@@ -16,14 +18,12 @@ 	, pin11 	, pin12 	, pin13-	-- * Analog input-	-- TODO!-	--, a0-	--, a1-	--, a2-	--, a3-	--, a4-	--, a5+	, a0+	, a1+	, a2+	, a3+	, a4+	, a5 	-- * UART 	-- Not currently supported! 	--, uart@@ -32,18 +32,26 @@ import Copilot.Arduino import Copilot.Arduino.Internals -pin2, pin3, pin4, pin5, pin6, pin7, pin8, pin9, pin10, pin11, pin12, pin13 :: GPIO-pin2 = GPIO 2-pin3 = GPIO 3-pin4 = GPIO 4-pin5 = GPIO 5-pin6 = GPIO 6-pin7 = GPIO 7-pin8 = GPIO 8-pin9 = GPIO 9-pin10 = GPIO 10-pin11 = GPIO 11-pin12 = GPIO 12+pin2, pin4, pin7, pin8, pin12, pin13 :: Pin '[ 'DigitalIO ]+pin3, pin5, pin6, pin9, pin10, pin11 :: Pin '[ 'DigitalIO, 'PWM ]+pin2 = Pin (PinId 2)+pin3 = Pin (PinId 3)+pin4 = Pin (PinId 4)+pin5 = Pin (PinId 5)+pin6 = Pin (PinId 6)+pin7 = Pin (PinId 7)+pin8 = Pin (PinId 8)+pin9 = Pin (PinId 9)+pin10 = Pin (PinId 10)+pin11 = Pin (PinId 11)+pin12 = Pin (PinId 12) -- | This pin drives the `led`-pin13 = GPIO 13+pin13 = Pin (PinId 13) +a0, a1, a2, a3, a4, a5 :: Pin '[ 'AnalogInput, 'DigitalIO ]+a0 = Pin (PinId 14)+a1 = Pin (PinId 15)+a2 = Pin (PinId 16)+a3 = Pin (PinId 17)+a4 = Pin (PinId 18)+a5 = Pin (PinId 19)