packages feed

arduino-copilot (empty) → 0.0.1

raw patch · 11 files changed

+550/−0 lines, 11 filesdep +basedep +copilotdep +copilot-c99setup-changed

Dependencies added: base, copilot, copilot-c99, directory, filepath, mtl, unix

Files

+ CHANGELOG view
@@ -0,0 +1,5 @@+arduino-copilot (0.0.1) unstable; urgency=medium++  * First release.++ -- Joey Hess <id@joeyh.name>  Sat, 25 Jan 2020 10:33:09 -0400
+ LICENSE view
@@ -0,0 +1,22 @@+Copyright 2020 Joey Hess <id@joeyh.name>.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions+are met:+1. Redistributions of source code must retain the above copyright+   notice, this list of conditions and the following disclaimer.+2. Redistributions in binary form must reproduce the above copyright+   notice, this list of conditions and the following disclaimer in the+   documentation and/or other materials provided with the distribution.++THIS SOFTWARE IS PROVIDED BY AUTHORS AND CONTRIBUTORS ``AS IS'' AND+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE+ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF+SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ TODO view
+ arduino-copilot.cabal view
@@ -0,0 +1,63 @@+Name: arduino-copilot+Version: 0.0.1+Cabal-Version: >= 1.8+License: BSD3+Maintainer: Joey Hess <id@joeyh.name>+Author: Joey Hess+Stability: Experimental+Copyright: 2020 Joey Hess+License-File: LICENSE+Build-Type: Simple+Category: Embedded, Language+Synopsis: Arduino programming in haskell using the stream DSL+Description:+ arduino-copilot contains the basic building blocks to program your+ Arduino Nano, Arduino Uno or compatible hardware using Haskell+ and Functional Reactive Programming (FRP).+ .+ arduino-copilot uses the Copilot stream domain-specific language (DSL)+ to generate an Arduino sketch, which can be loaded in the Arduino IDE+ and uploaded to the board. Other methods of deploying sketches to the+ Arduino, such as the Arduino-Makefile, can also be used with a sketch+ generated by arduino-copilot.+ .+ All the messy details are abstracted away, letting you focus on the+ desired behavior of the Arduino. This is all the code needed to make+ it blink its LED:+ .+ -- > import Arduino+ -- > main = arduino $ do+ -- >    led =: clk (period 2) (phase 1)+ -- >    delay =: const16 100+ .+ Copilot is a stream (i.e., infinite lists) domain-specific language+ (DSL) in Haskell that compiles into embedded C.  Copilot contains an+ interpreter, multiple back-end compilers, and other verification tools.+ A tutorial, bug reports, and todos are available at+ <https://github.com/Copilot-Language/copilot-discussion>.+Extra-Source-Files:+  CHANGELOG+  TODO+  stack.yaml+  demo/Makefile+  demo/README+  demo/demo.hs+  demo/pre-build-hook.sh++Library+  GHC-Options: -Wall -fno-warn-tabs+  Hs-Source-Dirs: src+  Exposed-Modules:+    Arduino+  Build-Depends:+    base (>= 4.5 && < 5),+    copilot (== 3.1.*),+    copilot-c99 (== 3.1.*),+    filepath,+    directory,+    mtl,+    unix++source-repository head+  type: git+  location: git://git.joeyh.name/haskell-arduino-copilot.git
+ demo/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
+ demo/README view
@@ -0,0 +1,28 @@+This is a demo program using arduino-copilot. ++To build the C code:++	stack run demo++The resulting `.ino` sketch can be loaded into the Arduino IDE and flashed+to an Arduino board using the IDE.++It simply flashes the Arduino's on board LED, so should work on most or all+Arduino boards.++## 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 flash++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). You may also adjust the `Makefile` to target+a different board than the Arduino Uno.
+ demo/demo.hs view
@@ -0,0 +1,25 @@+{-# LANGUAGE RebindableSyntax #-}++import Arduino++flashing :: Stream Bool+flashing = clk (period (2 :: Int16)) (phase 1)++longer_and_longer :: Stream Int16+longer_and_longer = counter true $ counter true false `mod` 64 == 0++counter :: Stream Bool -> Stream Bool -> Stream Int16+counter inc reset = cnt+   where+	cnt = if reset+		then 0+		else if inc+			then z + 1+			else z+	z = [0] ++ cnt++main :: IO ()+main = arduino $ do+	buttonpressed <- boolInput (Pin 14)+	led =: buttonpressed || flashing+	delay =: longer_and_longer
+ demo/pre-build-hook.sh view
@@ -0,0 +1,2 @@+#!/bin/sh+exec stack run demo -Wall -fno-warn-tabs
+ src/Arduino.hs view
@@ -0,0 +1,262 @@+{-# LANGUAGE RebindableSyntax #-}++module Arduino (+	module X,+	-- * Arduino sketch generation+	Sketch,+	Input,+	Output,+	arduino,+	-- * Combinators+	(=:),+	(@:),+	-- * Inputs+	boolInput,+	boolInput',+	-- TODO enumerate all avail pins, don't export constructor+	Pin(..),+	-- * Outputs+	led,+	MicroSeconds,+	delay,+	-- * Utilities+	firstIteration,+	sketchSpec,+) where++import Language.Copilot as X+import Copilot.Compile.C99+import System.Environment+import System.Directory+import System.Posix.Temp (mkdtemp)+import System.FilePath+import Text.Read+import Control.Monad.Writer+import Data.List (isInfixOf)+import qualified Prelude++-- | An Arduino sketch, implemented using copilot.+--+-- On each iteration of a Sketch, all inputs used by it are first+-- collected, before any outputs are performed.+--+-- Like a copilot `Spec`, a Sketch's outputs are not run in any+-- particular order.+type Sketch = Sketch' ()++type Sketch' t = Writer [(Spec, Framework)] t++data Framework = Framework+	{ defines :: [CFragment]+	, setups :: [CFragment]+	, loops :: [CFragment]+	}++type CFragment = String++instance Semigroup Framework where+	a <> b = Framework+		{ defines = defines a <> defines b+		, setups = setups a <> setups b+		, loops = loops a  <> loops b+		}++instance Monoid Framework where+	mempty = Framework mempty mempty mempty++class ToFramework t where+	toFramework :: t -> Framework++type Behavior t = Stream t -> Spec++-- | Somewhere that a Stream can be directed to, in order to control the+-- Arduino.+data Output t = Output+	{ setupOutput :: [CFragment]+	, outputCond :: Stream Bool+	, outputBehavior :: Stream Bool -> Behavior t+	}++instance ToFramework (Output t) where+	toFramework o = Framework+		{ defines = mempty+		, setups = setupOutput o+		, loops = mempty+		}++-- | A source of a `Stream` of values input from the Arduino.+type Input t = Sketch' (Stream t)++data Input' t = Input'+	{ defineVar :: [CFragment]+	, setupInput :: [CFragment]+	, readInput :: [CFragment]+	, inputStream :: Stream t+	}++instance ToFramework (Input' t) where+	toFramework i = Framework+		{ defines = defineVar i+		, setups = setupInput i+		, loops = readInput i+		}++-- | A GPIO pin+newtype Pin = Pin Int++mkInput :: Input' t -> Input t+mkInput i = do+	tell [(return (), toFramework i)]+	return (inputStream i)++-- | Connect a `Stream` to an `Output`.+(=:) :: Output t -> Stream t -> Sketch+o =: s = tell [(go, toFramework o)]+  where+	go = (outputBehavior o) (outputCond o) s++-- Same fixity as =<<+infixr 1 =:++-- | By default, an `Output` is written to on each iteration of the Sketch.+--+-- For example, this constantly turns on the LED, even though it will+-- already be on after the first iteration.+--+-- > led =: true+--+-- To avoid unnecessary work being done, this combinator can make an+-- `Output` only be written to when the current value of the provided+-- `Stream` is True.+--+-- So to make the LED only be turned on in the first iteration,+-- and allow it to remain on thereafter without doing extra work:+--+-- > led @: firstIteration =: true+(@:) :: Output t -> Stream Bool -> Output t+(@:) o c = o { outputCond = c }++-- | True on the first iteration of the Sketch, and False thereafter.+firstIteration :: Stream Bool+firstIteration = [True]++false++-- FIXME should be a newtype, but how to make a stream of a newtype?+type MicroSeconds = Int16++-- | The on-board LED.+led :: Output Bool+led = Output+	{ setupOutput = ["pinMode(13, OUTPUT)"]+	, outputBehavior = +		\c v -> trigger "digitalWrite" c [arg (constI16 13), arg v]+	, outputCond = true+	}++-- | Use this to add a delay between each iteration of the Sketch.+delay :: Output MicroSeconds+delay = Output+	{ setupOutput = []+	, outputBehavior = \c n -> trigger "delay" c [arg n]+	, outputCond = true+	}++-- | Reading from a GPIO pin.+boolInput :: Pin -> Input Bool+boolInput n = boolInput' n []++-- | The optional list is used as simulated input+-- when interpreting the program.+boolInput' :: Pin -> [Bool] -> Input Bool+boolInput' (Pin n) interpretvalues = mkInput $ Input'+	{ defineVar = ["bool " <> varname]+	, setupInput = ["pinMode(" <> show n <> ", INPUT)"]+	, readInput = [varname <> " = digitalRead(" <> show n <> ")"]+	, inputStream = extern varname interpretvalues'+	}+  where+	varname = "arduino_boolinput" <> show n+	interpretvalues'+		| null interpretvalues = Nothing+		| otherwise = Just interpretvalues++-- | Typically your Arduino program's main will use this. For example:+--+-- > main = arduino $ do+-- >	led =: clk (period 2) (phase 1)+-- > 	delay =: const16 100+--+-- The `Sketch` is compiled into C code using copilot, and written+-- to a .ino file. That can be built and uploaded to your Arduino+-- using the Arduino IDE, or any other toolchain for Arduino sketches.+arduino :: Sketch -> IO ()+arduino s = do+	let (is, fs) = unzip (execWriter s)+	let spec = sequence_ is+	-- TODO optparse-applicative+	ps <- getArgs+	case ps of+		[] -> writeIno spec (mconcat fs)+		["-i", n] -> case readMaybe n of+			Just n' -> interpret n' spec+			Nothing -> error "expected a number after -i"+		_ -> error "bad parameters"++-- | Extracts a copilot `Spec` from a `Sketch`.+--+-- This can be useful to intergrate with other libraries +-- such as copilot-theorem.+sketchSpec :: Sketch -> Spec+sketchSpec s = sequence_ is+  where+	(is, _fs) = unzip (execWriter s)++writeIno :: Spec -> Framework -> IO ()+writeIno spec framework = do+	-- This could be a lot prettier, unfortunately copilot only exports+	-- an interface that writes the generated code to a file.+	-- And, the .c file includes a .h file that will make it fail to+	-- build when used in the .ino file, so that include has to be+	-- filtered out.+	toptmpdir <- getTemporaryDirectory+	mytmpdir <- mkdtemp (toptmpdir </> "copilot")+	reify spec >>= compile (mytmpdir </> "copilot")+	c <- lines <$> readFile (mytmpdir </> "copilot.c")+	let c' = filter (Prelude.not . isInfixOf "#include \"") c+	-- Use a name for the ino file that will let the Arduino IDE find it.+	d <- getCurrentDirectory+	let dirbase = takeFileName d+	writeFile (addExtension dirbase "ino") $ +		sketchFramework framework c'+	removeDirectoryRecursive mytmpdir+	++sketchFramework :: Framework -> [String] -> String+sketchFramework f ccode = unlines $ 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 <> ["}"]
+ stack.yaml view
@@ -0,0 +1,14 @@+packages:+- '.'+resolver: lts-13.29+extra-deps:+- copilot-3.1+- copilot-c99-3.1+- copilot-core-3.1+- copilot-language-3.1+- copilot-libraries-3.1+- copilot-theorem-3.1+- language-c99-0.1.2+- language-c99-simple-0.1.2+- language-c99-util-0.1.1+explicit-setup-deps: