From aae8613cbacfe06cc8e605c67d8350ad33c3fd27 Mon Sep 17 00:00:00 2001 From: McKay Broderick Date: Sun, 13 Dec 2020 18:11:02 -0700 Subject: [PATCH 1/5] Initial commit --- haskell-arduino-p05-mood-cue/.ghcid | 1 + haskell-arduino-p05-mood-cue/.gitignore | 4 + haskell-arduino-p05-mood-cue/ChangeLog.md | 3 + haskell-arduino-p05-mood-cue/LICENSE | 30 +++++++ haskell-arduino-p05-mood-cue/README.md | 1 + haskell-arduino-p05-mood-cue/Setup.hs | 2 + haskell-arduino-p05-mood-cue/app/Main.hs | 6 ++ haskell-arduino-p05-mood-cue/package.yaml | 51 +++++++++++ haskell-arduino-p05-mood-cue/src/Lib.hs | 10 +++ haskell-arduino-p05-mood-cue/stack.yaml | 77 +++++++++++++++++ haskell-arduino-p05-mood-cue/stack.yaml.lock | 89 ++++++++++++++++++++ haskell-arduino-p05-mood-cue/test/Spec.hs | 2 + 12 files changed, 276 insertions(+) create mode 100644 haskell-arduino-p05-mood-cue/.ghcid create mode 100644 haskell-arduino-p05-mood-cue/.gitignore create mode 100644 haskell-arduino-p05-mood-cue/ChangeLog.md create mode 100644 haskell-arduino-p05-mood-cue/LICENSE create mode 100644 haskell-arduino-p05-mood-cue/README.md create mode 100644 haskell-arduino-p05-mood-cue/Setup.hs create mode 100644 haskell-arduino-p05-mood-cue/app/Main.hs create mode 100644 haskell-arduino-p05-mood-cue/package.yaml create mode 100644 haskell-arduino-p05-mood-cue/src/Lib.hs create mode 100644 haskell-arduino-p05-mood-cue/stack.yaml create mode 100644 haskell-arduino-p05-mood-cue/stack.yaml.lock create mode 100644 haskell-arduino-p05-mood-cue/test/Spec.hs diff --git a/haskell-arduino-p05-mood-cue/.ghcid b/haskell-arduino-p05-mood-cue/.ghcid new file mode 100644 index 0000000..949edb2 --- /dev/null +++ b/haskell-arduino-p05-mood-cue/.ghcid @@ -0,0 +1 @@ +-c "stack ghci haskell-arduino-p04-color-mixing-lamp:lib" diff --git a/haskell-arduino-p05-mood-cue/.gitignore b/haskell-arduino-p05-mood-cue/.gitignore new file mode 100644 index 0000000..ae83ae0 --- /dev/null +++ b/haskell-arduino-p05-mood-cue/.gitignore @@ -0,0 +1,4 @@ +.stack-work/ +*~ +*.ino +*.cabal \ No newline at end of file diff --git a/haskell-arduino-p05-mood-cue/ChangeLog.md b/haskell-arduino-p05-mood-cue/ChangeLog.md new file mode 100644 index 0000000..2bf7c63 --- /dev/null +++ b/haskell-arduino-p05-mood-cue/ChangeLog.md @@ -0,0 +1,3 @@ +# Changelog for haskell-arduino-p05-mood-cue + +## Unreleased changes diff --git a/haskell-arduino-p05-mood-cue/LICENSE b/haskell-arduino-p05-mood-cue/LICENSE new file mode 100644 index 0000000..e637cde --- /dev/null +++ b/haskell-arduino-p05-mood-cue/LICENSE @@ -0,0 +1,30 @@ +Copyright Author name here (c) 2020 + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * 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. + + * Neither the name of Author name here nor the names of other + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT +OWNER 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. diff --git a/haskell-arduino-p05-mood-cue/README.md b/haskell-arduino-p05-mood-cue/README.md new file mode 100644 index 0000000..7c61668 --- /dev/null +++ b/haskell-arduino-p05-mood-cue/README.md @@ -0,0 +1 @@ +# haskell-arduino-p05-mood-cue diff --git a/haskell-arduino-p05-mood-cue/Setup.hs b/haskell-arduino-p05-mood-cue/Setup.hs new file mode 100644 index 0000000..9a994af --- /dev/null +++ b/haskell-arduino-p05-mood-cue/Setup.hs @@ -0,0 +1,2 @@ +import Distribution.Simple +main = defaultMain diff --git a/haskell-arduino-p05-mood-cue/app/Main.hs b/haskell-arduino-p05-mood-cue/app/Main.hs new file mode 100644 index 0000000..de1c1ab --- /dev/null +++ b/haskell-arduino-p05-mood-cue/app/Main.hs @@ -0,0 +1,6 @@ +module Main where + +import Lib + +main :: IO () +main = someFunc diff --git a/haskell-arduino-p05-mood-cue/package.yaml b/haskell-arduino-p05-mood-cue/package.yaml new file mode 100644 index 0000000..820f20c --- /dev/null +++ b/haskell-arduino-p05-mood-cue/package.yaml @@ -0,0 +1,51 @@ +name: haskell-arduino-p05-mood-cue +version: 0.1.0.0 +github: "githubuser/haskell-arduino-p05-mood-cue" +license: BSD3 +author: "Author name here" +maintainer: "example@example.com" +copyright: "2020 Author name here" + +extra-source-files: +- README.md +- ChangeLog.md + +# Metadata used when publishing your package +# synopsis: Short description of your package +# category: Web + +# To avoid duplicated efforts in documentation and dealing with the +# complications of embedding Haddock markup inside cabal files, it is +# common to point users to the README.md file. +description: Please see the README on GitHub at + +dependencies: +- base >= 4.7 && < 5 +- arduino-copilot + +library: + source-dirs: src + default-extensions: + - RebindableSyntax + +executables: + haskell-arduino-p05-mood-cue-exe: + main: Main.hs + source-dirs: app + ghc-options: + - -threaded + - -rtsopts + - -with-rtsopts=-N + dependencies: + - haskell-arduino-p05-mood-cue + +tests: + haskell-arduino-p05-mood-cue-test: + main: Spec.hs + source-dirs: test + ghc-options: + - -threaded + - -rtsopts + - -with-rtsopts=-N + dependencies: + - haskell-arduino-p05-mood-cue diff --git a/haskell-arduino-p05-mood-cue/src/Lib.hs b/haskell-arduino-p05-mood-cue/src/Lib.hs new file mode 100644 index 0000000..baab140 --- /dev/null +++ b/haskell-arduino-p05-mood-cue/src/Lib.hs @@ -0,0 +1,10 @@ +module Lib + ( someFunc + ) where + +import Copilot.Arduino + +someFunc = arduino $ do + led =: blinking + delay =: MilliSeconds (constant 100) + diff --git a/haskell-arduino-p05-mood-cue/stack.yaml b/haskell-arduino-p05-mood-cue/stack.yaml new file mode 100644 index 0000000..1f1b6b2 --- /dev/null +++ b/haskell-arduino-p05-mood-cue/stack.yaml @@ -0,0 +1,77 @@ +# This file was automatically generated by 'stack init' +# +# Some commonly used options have been documented as comments in this file. +# For advanced use and comprehensive documentation of the format, please see: +# https://docs.haskellstack.org/en/stable/yaml_configuration/ + +# Resolver to choose a 'specific' stackage snapshot or a compiler version. +# A snapshot resolver dictates the compiler version and the set of packages +# to be used for project dependencies. For example: +# +# resolver: lts-3.5 +# resolver: nightly-2015-09-21 +# resolver: ghc-7.10.2 +# +# The location of a snapshot can be provided as a file or url. Stack assumes +# a snapshot provided as a file might change, whereas a url resource does not. +# +# resolver: ./custom-snapshot.yaml +# resolver: https://example.com/snapshots/2018-01-01.yaml +resolver: lts-14.27 + +# User packages to be built. +# Various formats can be used as shown in the example below. +# +# packages: +# - some-directory +# - https://example.com/foo/bar/baz-0.0.2.tar.gz +# subdirs: +# - auto-update +# - wai +packages: +- . +# Dependency packages to be pulled from upstream that are not in the resolver. +# These entries can reference officially published versions as well as +# forks / in-progress versions pinned to a git hash. For example: +# +extra-deps: + - arduino-copilot-1.5.1@sha256:338b23c6c016b2f8a8e79d5d16151ec1369e10c40dd9b09ae3fdb903d5ba5881,2802 + - copilot-3.1@sha256:966318c27ea4e2877ffba45f455be9310426b3e4b2c8fa8041d51e451879cc2b,4001 + - copilot-c99-3.1.2@sha256:8603972c549131c48294fc2b1808f599e829e7b324048d8f6ac9994fce840364,2348 + - copilot-language-3.1@sha256:3ec5d98e2579e692930a47d77c30070906523f9cf9028728812633734ff9c80e,2942 + - copilot-core-3.1@sha256:3c435b7edd14de8995cc9a987ebff648eb2b2ef92c2fefe748b94b9bcca6dc48,2003 + - copilot-libraries-3.1@sha256:cb6597c4bc556b9e153baa3006989676c489a756f514b73666062c8a485fa672,1977 + - copilot-theorem-3.1@sha256:846e58c9f4a28a5224d994aa01a74d273161852c13a42b09b9ce7ce91fe93205,4263 + - language-c99-0.1.2@sha256:6c9022378c7ed57d080fe41442066a5f2657347fd02a07ad291bbf41d71ab954,1587 + - language-c99-simple-0.1.2@sha256:5b522616fa22a0909250d56a0632f1f1cae1c38d585149e13eeb809103af9543,1540 + - language-c99-util-0.1.1@sha256:480adee2ba3045d04305be5362be991b1814faf58cfd7af1f845d0fc9dbcf3a7,1177 + - bimap-0.3.3@sha256:232518c0410990665b9c8677eb9318ee355c001d58945ddcbedec3baa30b4160,1475 +# - acme-missiles-0.3 +# - git: https://github.com/commercialhaskell/stack.git +# commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a +# +# extra-deps: [] + +# Override default flag values for local packages and extra-deps +# flags: {} + +# Extra package databases containing global packages +# extra-package-dbs: [] + +# Control whether we use the GHC we find on the path +# system-ghc: true +# +# Require a specific version of stack, using version ranges +# require-stack-version: -any # Default +# require-stack-version: ">=2.3" +# +# Override the architecture used by stack, especially useful on Windows +# arch: i386 +# arch: x86_64 +# +# Extra directories used by stack for building +# extra-include-dirs: [/path/to/dir] +# extra-lib-dirs: [/path/to/dir] +# +# Allow a newer minor version of GHC than the snapshot specifies +# compiler-check: newer-minor diff --git a/haskell-arduino-p05-mood-cue/stack.yaml.lock b/haskell-arduino-p05-mood-cue/stack.yaml.lock new file mode 100644 index 0000000..25202f4 --- /dev/null +++ b/haskell-arduino-p05-mood-cue/stack.yaml.lock @@ -0,0 +1,89 @@ +# This file was autogenerated by Stack. +# You should not edit this file by hand. +# For more information, please see the documentation at: +# https://docs.haskellstack.org/en/stable/lock_files + +packages: +- completed: + hackage: arduino-copilot-1.5.1@sha256:338b23c6c016b2f8a8e79d5d16151ec1369e10c40dd9b09ae3fdb903d5ba5881,2802 + pantry-tree: + size: 2946 + sha256: 7155a293006ef384ca5f85965594b595956b36f20f7f231df562568f9ec67d4e + original: + hackage: arduino-copilot-1.5.1@sha256:338b23c6c016b2f8a8e79d5d16151ec1369e10c40dd9b09ae3fdb903d5ba5881,2802 +- completed: + hackage: copilot-3.1@sha256:966318c27ea4e2877ffba45f455be9310426b3e4b2c8fa8041d51e451879cc2b,4001 + pantry-tree: + size: 847 + sha256: 02784c5d047085da3176e68f34f40f18d770511f039e7c33d8fc9bcf41f5a597 + original: + hackage: copilot-3.1@sha256:966318c27ea4e2877ffba45f455be9310426b3e4b2c8fa8041d51e451879cc2b,4001 +- completed: + hackage: copilot-c99-3.1.2@sha256:8603972c549131c48294fc2b1808f599e829e7b324048d8f6ac9994fce840364,2348 + pantry-tree: + size: 692 + sha256: d4f07c09596979c9be45283d0df03139dfb5dbbc9f61c03a1333711d2e05fe29 + original: + hackage: copilot-c99-3.1.2@sha256:8603972c549131c48294fc2b1808f599e829e7b324048d8f6ac9994fce840364,2348 +- completed: + hackage: copilot-language-3.1@sha256:3ec5d98e2579e692930a47d77c30070906523f9cf9028728812633734ff9c80e,2942 + pantry-tree: + size: 2229 + sha256: 08d9b5cc844624a977aa884c8d75390c1edc57ee0f7e5d8b514e78456446e491 + original: + hackage: copilot-language-3.1@sha256:3ec5d98e2579e692930a47d77c30070906523f9cf9028728812633734ff9c80e,2942 +- completed: + hackage: copilot-core-3.1@sha256:3c435b7edd14de8995cc9a987ebff648eb2b2ef92c2fefe748b94b9bcca6dc48,2003 + pantry-tree: + size: 1668 + sha256: db6ce9f6fc3c3472b1d3bfa007b3c9f28f80b5817efa7ba346d671975a4e9b03 + original: + hackage: copilot-core-3.1@sha256:3c435b7edd14de8995cc9a987ebff648eb2b2ef92c2fefe748b94b9bcca6dc48,2003 +- completed: + hackage: copilot-libraries-3.1@sha256:cb6597c4bc556b9e153baa3006989676c489a756f514b73666062c8a485fa672,1977 + pantry-tree: + size: 956 + sha256: 81f759cca2af5249f76e11d79024aa775b51ebc140963762078ea5ef81d07893 + original: + hackage: copilot-libraries-3.1@sha256:cb6597c4bc556b9e153baa3006989676c489a756f514b73666062c8a485fa672,1977 +- completed: + hackage: copilot-theorem-3.1@sha256:846e58c9f4a28a5224d994aa01a74d273161852c13a42b09b9ce7ce91fe93205,4263 + pantry-tree: + size: 2672 + sha256: 6a4dd0389484b413c9e3daa840b02cfd64d50df5148175626da03028eaa0fdb9 + original: + hackage: copilot-theorem-3.1@sha256:846e58c9f4a28a5224d994aa01a74d273161852c13a42b09b9ce7ce91fe93205,4263 +- completed: + hackage: language-c99-0.1.2@sha256:6c9022378c7ed57d080fe41442066a5f2657347fd02a07ad291bbf41d71ab954,1587 + pantry-tree: + size: 399 + sha256: 53d202a7489f2501c633e5c1f4d6a63deed004df1bec36d3ce29d54388c4a619 + original: + hackage: language-c99-0.1.2@sha256:6c9022378c7ed57d080fe41442066a5f2657347fd02a07ad291bbf41d71ab954,1587 +- completed: + hackage: language-c99-simple-0.1.2@sha256:5b522616fa22a0909250d56a0632f1f1cae1c38d585149e13eeb809103af9543,1540 + pantry-tree: + size: 574 + sha256: 1d297b46c78e2a498f7828bddc794d8355656273f7334f8ab27452ccfd28d5be + original: + hackage: language-c99-simple-0.1.2@sha256:5b522616fa22a0909250d56a0632f1f1cae1c38d585149e13eeb809103af9543,1540 +- completed: + hackage: language-c99-util-0.1.1@sha256:480adee2ba3045d04305be5362be991b1814faf58cfd7af1f845d0fc9dbcf3a7,1177 + pantry-tree: + size: 489 + sha256: 3a5e1387e9b073dad4f51fa749fd4c33e2abe31566b63a120a38a7f88d809493 + original: + hackage: language-c99-util-0.1.1@sha256:480adee2ba3045d04305be5362be991b1814faf58cfd7af1f845d0fc9dbcf3a7,1177 +- completed: + hackage: bimap-0.3.3@sha256:232518c0410990665b9c8677eb9318ee355c001d58945ddcbedec3baa30b4160,1475 + pantry-tree: + size: 414 + sha256: 5c7485014c2f342d215ace99cd06361f7607169aebe8d4aca2f265fa5c3c6761 + original: + hackage: bimap-0.3.3@sha256:232518c0410990665b9c8677eb9318ee355c001d58945ddcbedec3baa30b4160,1475 +snapshots: +- completed: + size: 524996 + url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/14/27.yaml + sha256: 7ea31a280c56bf36ff591a7397cc384d0dff622e7f9e4225b47d8980f019a0f0 + original: lts-14.27 diff --git a/haskell-arduino-p05-mood-cue/test/Spec.hs b/haskell-arduino-p05-mood-cue/test/Spec.hs new file mode 100644 index 0000000..cd4753f --- /dev/null +++ b/haskell-arduino-p05-mood-cue/test/Spec.hs @@ -0,0 +1,2 @@ +main :: IO () +main = putStrLn "Test suite not yet implemented" From 250a05d66d44b6ecd320203c624577a78357ba6d Mon Sep 17 00:00:00 2001 From: McKay Broderick Date: Sun, 13 Dec 2020 18:27:33 -0700 Subject: [PATCH 2/5] WIP Grabbed some code as a starting point. Need to tweak to work with copilot --- .../src/Servo/Servo.hs | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 haskell-arduino-p05-mood-cue/src/Servo/Servo.hs diff --git a/haskell-arduino-p05-mood-cue/src/Servo/Servo.hs b/haskell-arduino-p05-mood-cue/src/Servo/Servo.hs new file mode 100644 index 0000000..a8873aa --- /dev/null +++ b/haskell-arduino-p05-mood-cue/src/Servo/Servo.hs @@ -0,0 +1,78 @@ +module Servo where + +{- +Servo's move by Pulse Width Modulation, or PWM. +The usual frequency of these PWM signals is 50Hz, which is every 20 milliseconds. +The servomotor has a rotating shaft and potentiometer that detects it's position. +When there is a pulse from the control signal, it applies current to the motor that +makes the shaft move until the potentiometer indicates that the position is +in line with the width of the pulse. + +A servomotor turns 90 degrees in either direction. Maximum movement is 180. +Width of the pulses determine the position of the shaft. For eample, a pulse of 1ms +will move the shaft anticlockwise at -90 degress. A pulse of 1.5ms will move the shaft +at the neutral position at 0 degrees and a pulse of 2 ms will move the shaft at +90 degrees. +-} + +{- +Following code stolen from here for guidance as I try to make one that works with Copilot +http://hackage.haskell.org/package/hArduino-1.1/docs/src/System-Hardware-Arduino-Parts-Servo.html#Servo +-- | A servo motor. Note that this type is abstract, use 'attach' to +-- create an instance. +data Servo = Servo { servoPin :: IPin -- ^ The internal-pin that controls the servo + , minPulse :: Int -- ^ Pulse-width (microseconds) for the minumum (0-degree) angle. + , maxPulse :: Int -- ^ Pulse-width (microseconds) for the maximum (typically 180-degree) angle. + } + +-- | Create a servo motor instance. The default values for the min/max angle pulse-widths, while typical, +-- may need to be adjusted based on the specs of the actual servo motor. Check the data-sheet for your +-- servo to find the proper values. The default values of @544@ and @2400@ microseconds are typical, so you might +-- want to start by passing 'Nothing' for both parameters and adjusting as necessary. +attach :: Pin -- ^ Pin controlling the servo. Should be a pin that supports SERVO mode. + -> Maybe Int -- ^ Pulse-width (in microseconds) for the minumum 0-degree angle. Default: @544@. + -> Maybe Int -- ^ Pulse-width (in microseconds) for the maximum, typically 180-degree, angle. Default: @2400@. + -> Arduino Servo +attach p mbMin mbMax + | Just m <- mbMin, m < 0 + = die "Servo.attach: minimum pulse width must be positive" ["Received: " ++ show m] + | Just m <- mbMax, m < 0 + = die "Servo.attach: maximum pulse width must be positive" ["Received: " ++ show m] + | True + = do let minPulse = fromMaybe 544 mbMin + maxPulse = fromMaybe 2400 mbMax + debug $ "Attaching servo on pin: " ++ show p ++ " with parameters: " ++ show (minPulse, maxPulse) + when (minPulse >= maxPulse) $ die "Servo.attach: min pulse duration must be less than max pulse duration" + [ "Received min-pulse: " ++ show minPulse + , "Received max-pulse: " ++ show maxPulse + ] + setPinMode p SERVO + (ip, _) <- convertAndCheckPin "Servo.attach" p SERVO + return Servo { servoPin = ip + , minPulse = fromMaybe 544 mbMin + , maxPulse = fromMaybe 2400 mbMax + } + +-- | Set the angle of the servo. The argument should be a number between 0 and 180, +-- indicating the desired angle setting in degrees. +setAngle :: Servo -> Int -> Arduino () +setAngle Servo{servoPin, minPulse, maxPulse} angle + | angle < 0 || angle > 180 + = die "Servo.setAngle: angle must be between 0 and 180." ["Received: " ++ show angle] + | True + = do let duration = minPulse + ((maxPulse - minPulse) * angle) `div` 180 + debug $ "Setting servo on pin: " ++ show servoPin ++ " " ++ show angle ++ " degrees, via a pulse of " ++ show duration ++ " microseconds." + -- In arduino, the most we can send is 16383; not that a servo should need such a large value, but + -- just in case + when (duration >= 16383) $ die "Servo.setAngle angle setting: out-of-range." + [ "Servo pin : " ++ show servoPin + , "Angle required : " ++ show angle + , "Min pulse duration: " ++ show minPulse + , "Max pulse duration: " ++ show maxPulse + , "Duration needed : " ++ show duration + , "Exceeds max value : 16383" + ] + let msb = fromIntegral $ (duration `shiftR` 7) .&. 0x7f + lsb = fromIntegral $ duration .&. 0x7f + send $ AnalogPinWrite servoPin lsb msb + +-} \ No newline at end of file From 49a879e00389c6037c9a9f1f7444ef4f54806be0 Mon Sep 17 00:00:00 2001 From: McKay Broderick Date: Sun, 13 Dec 2020 21:01:13 -0700 Subject: [PATCH 3/5] WIP on a servo implementation --- haskell-arduino-p05-mood-cue/.ghcid | 2 +- haskell-arduino-p05-mood-cue/package.yaml | 1 + haskell-arduino-p05-mood-cue/src/Lib.hs | 13 ++++ .../src/{Servo => }/Servo.hs | 61 +++++++++++++++++++ 4 files changed, 76 insertions(+), 1 deletion(-) rename haskell-arduino-p05-mood-cue/src/{Servo => }/Servo.hs (72%) diff --git a/haskell-arduino-p05-mood-cue/.ghcid b/haskell-arduino-p05-mood-cue/.ghcid index 949edb2..98f033c 100644 --- a/haskell-arduino-p05-mood-cue/.ghcid +++ b/haskell-arduino-p05-mood-cue/.ghcid @@ -1 +1 @@ --c "stack ghci haskell-arduino-p04-color-mixing-lamp:lib" +-c "stack ghci haskell-arduino-p05-mood-cue:lib" diff --git a/haskell-arduino-p05-mood-cue/package.yaml b/haskell-arduino-p05-mood-cue/package.yaml index 820f20c..fb801b5 100644 --- a/haskell-arduino-p05-mood-cue/package.yaml +++ b/haskell-arduino-p05-mood-cue/package.yaml @@ -22,6 +22,7 @@ description: Please see the README on GitHub at = 4.7 && < 5 - arduino-copilot +- mtl library: source-dirs: src diff --git a/haskell-arduino-p05-mood-cue/src/Lib.hs b/haskell-arduino-p05-mood-cue/src/Lib.hs index baab140..d908a48 100644 --- a/haskell-arduino-p05-mood-cue/src/Lib.hs +++ b/haskell-arduino-p05-mood-cue/src/Lib.hs @@ -3,8 +3,21 @@ module Lib ) where import Copilot.Arduino +import Copilot.Arduino.Uno +import qualified Copilot.Arduino.Library.Serial as Serial +import qualified Servo someFunc = arduino $ do + redSensorVal <- input a0 :: Sketch (Behavior ADC) + + Serial.baud 9600 + Serial.device =: [ Serial.str "Raw Sensor Values: \t Red: " + , Serial.show redSensorVal + , Serial.char '\n' + ] + + let servo = Servo.servo pin9 + led =: blinking delay =: MilliSeconds (constant 100) diff --git a/haskell-arduino-p05-mood-cue/src/Servo/Servo.hs b/haskell-arduino-p05-mood-cue/src/Servo.hs similarity index 72% rename from haskell-arduino-p05-mood-cue/src/Servo/Servo.hs rename to haskell-arduino-p05-mood-cue/src/Servo.hs index a8873aa..806e7d3 100644 --- a/haskell-arduino-p05-mood-cue/src/Servo/Servo.hs +++ b/haskell-arduino-p05-mood-cue/src/Servo.hs @@ -1,5 +1,66 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE UndecidableInstances #-} + module Servo where +import Data.Function (const) +import Control.Monad.Writer (MonadWriter(tell)) +import Copilot.Arduino.Internals +import Copilot.Arduino.Uno + +data Servo a = Servo { minPulseWidth :: Int, maxPulseWidth :: Int, pin :: a } + +defaultPulseWidth :: Integer +defaultPulseWidth = 1500 + +servo :: IsPWMPin p => Pin p -> Servo (Pin p) +servo = Servo minPulseWidth maxPulseWidth + where + minPulseWidth = 544 + maxPulseWidth = 2400 + +{- +transform' :: Fractional a => a -> a -> a -> a -> a -> a +transform' lmin lmax rmin rmax value = + (((value - lmin) * (rmax - rmin)) / (lmax - lmin)) + rmin + +transform :: Fractional a => a -> a +transform = transform' 0 1023 0 179 +-} + +setAngle :: IsPWMPin p => Servo (Pin p) -> Int -> Sketch () +setAngle (Servo minPulse maxPulse pin) angle = do + let floatMin = fromIntegral minPulse + let floatMax = fromIntegral maxPulse + let floatAngle = fromIntegral angle + + let duration = floatMin + (((floatMax - floatMin) * floatAngle) / 180) :: Float + pin =: pwm' (constF duration) + + +-- The library only defines pwm for Word8, so we did some hacking here to allow floats. +pwm' :: Behavior a -> TypedBehavior 'PWM a +pwm' = TypedBehavior + +instance (Typed a, IsPWMPin t) => Output (Pin t) (Event 'PWM (Stream a)) where + (Pin (PinId n)) =: (Event v c) = do + (f, triggername) <- defineTriggerAlias' ("pin_" <> show n) "analogWrite" mempty + tell [(go triggername, const f)] + where + go triggername tl = + let c' = addTriggerLimit tl c + in trigger triggername c' [arg (constant n), arg v] + + + + + +{- +150 / 250 -> +-} + {- Servo's move by Pulse Width Modulation, or PWM. The usual frequency of these PWM signals is 50Hz, which is every 20 milliseconds. From c939e9faeb487773a20d097d2c2e31b5fc6211be Mon Sep 17 00:00:00 2001 From: McKay Broderick Date: Wed, 16 Dec 2020 20:08:44 -0700 Subject: [PATCH 4/5] Got the arduino servo working --- haskell-arduino-p05-mood-cue/src/Lib.hs | 22 +++-- haskell-arduino-p05-mood-cue/src/Servo.hs | 112 +--------------------- 2 files changed, 14 insertions(+), 120 deletions(-) diff --git a/haskell-arduino-p05-mood-cue/src/Lib.hs b/haskell-arduino-p05-mood-cue/src/Lib.hs index d908a48..83b6743 100644 --- a/haskell-arduino-p05-mood-cue/src/Lib.hs +++ b/haskell-arduino-p05-mood-cue/src/Lib.hs @@ -2,22 +2,24 @@ module Lib ( someFunc ) where -import Copilot.Arduino import Copilot.Arduino.Uno +import Servo import qualified Copilot.Arduino.Library.Serial as Serial -import qualified Servo +someFunc :: IO () someFunc = arduino $ do - redSensorVal <- input a0 :: Sketch (Behavior ADC) + potVal <- unsafeCast <$> (input a0 :: Sketch (Behavior ADC)) :: Sketch (Behavior Float) Serial.baud 9600 - Serial.device =: [ Serial.str "Raw Sensor Values: \t Red: " - , Serial.show redSensorVal + Serial.device =: [ Serial.str "potVal: " + , Serial.show potVal + ] + let angle = Servo.transform potVal + Serial.device =: [ Serial.str "angle: " + , Serial.show angle , Serial.char '\n' ] - let servo = Servo.servo pin9 - - led =: blinking - delay =: MilliSeconds (constant 100) - + pin9 =: pwm' angle + delay =: MilliSeconds (constant 15) + \ No newline at end of file diff --git a/haskell-arduino-p05-mood-cue/src/Servo.hs b/haskell-arduino-p05-mood-cue/src/Servo.hs index 806e7d3..6f4ccbe 100644 --- a/haskell-arduino-p05-mood-cue/src/Servo.hs +++ b/haskell-arduino-p05-mood-cue/src/Servo.hs @@ -10,130 +10,22 @@ import Control.Monad.Writer (MonadWriter(tell)) import Copilot.Arduino.Internals import Copilot.Arduino.Uno -data Servo a = Servo { minPulseWidth :: Int, maxPulseWidth :: Int, pin :: a } - -defaultPulseWidth :: Integer -defaultPulseWidth = 1500 - -servo :: IsPWMPin p => Pin p -> Servo (Pin p) -servo = Servo minPulseWidth maxPulseWidth - where - minPulseWidth = 544 - maxPulseWidth = 2400 - -{- transform' :: Fractional a => a -> a -> a -> a -> a -> a transform' lmin lmax rmin rmax value = (((value - lmin) * (rmax - rmin)) / (lmax - lmin)) + rmin transform :: Fractional a => a -> a transform = transform' 0 1023 0 179 --} - -setAngle :: IsPWMPin p => Servo (Pin p) -> Int -> Sketch () -setAngle (Servo minPulse maxPulse pin) angle = do - let floatMin = fromIntegral minPulse - let floatMax = fromIntegral maxPulse - let floatAngle = fromIntegral angle - - let duration = floatMin + (((floatMax - floatMin) * floatAngle) / 180) :: Float - pin =: pwm' (constF duration) - -- The library only defines pwm for Word8, so we did some hacking here to allow floats. pwm' :: Behavior a -> TypedBehavior 'PWM a pwm' = TypedBehavior -instance (Typed a, IsPWMPin t) => Output (Pin t) (Event 'PWM (Stream a)) where +instance (IsPWMPin t) => Output (Pin t) (Event 'PWM (Stream Float)) where (Pin (PinId n)) =: (Event v c) = do (f, triggername) <- defineTriggerAlias' ("pin_" <> show n) "analogWrite" mempty tell [(go triggername, const f)] where go triggername tl = let c' = addTriggerLimit tl c - in trigger triggername c' [arg (constant n), arg v] - - - - - -{- -150 / 250 -> --} - -{- -Servo's move by Pulse Width Modulation, or PWM. -The usual frequency of these PWM signals is 50Hz, which is every 20 milliseconds. -The servomotor has a rotating shaft and potentiometer that detects it's position. -When there is a pulse from the control signal, it applies current to the motor that -makes the shaft move until the potentiometer indicates that the position is -in line with the width of the pulse. - -A servomotor turns 90 degrees in either direction. Maximum movement is 180. -Width of the pulses determine the position of the shaft. For eample, a pulse of 1ms -will move the shaft anticlockwise at -90 degress. A pulse of 1.5ms will move the shaft -at the neutral position at 0 degrees and a pulse of 2 ms will move the shaft at +90 degrees. --} - -{- -Following code stolen from here for guidance as I try to make one that works with Copilot -http://hackage.haskell.org/package/hArduino-1.1/docs/src/System-Hardware-Arduino-Parts-Servo.html#Servo --- | A servo motor. Note that this type is abstract, use 'attach' to --- create an instance. -data Servo = Servo { servoPin :: IPin -- ^ The internal-pin that controls the servo - , minPulse :: Int -- ^ Pulse-width (microseconds) for the minumum (0-degree) angle. - , maxPulse :: Int -- ^ Pulse-width (microseconds) for the maximum (typically 180-degree) angle. - } - --- | Create a servo motor instance. The default values for the min/max angle pulse-widths, while typical, --- may need to be adjusted based on the specs of the actual servo motor. Check the data-sheet for your --- servo to find the proper values. The default values of @544@ and @2400@ microseconds are typical, so you might --- want to start by passing 'Nothing' for both parameters and adjusting as necessary. -attach :: Pin -- ^ Pin controlling the servo. Should be a pin that supports SERVO mode. - -> Maybe Int -- ^ Pulse-width (in microseconds) for the minumum 0-degree angle. Default: @544@. - -> Maybe Int -- ^ Pulse-width (in microseconds) for the maximum, typically 180-degree, angle. Default: @2400@. - -> Arduino Servo -attach p mbMin mbMax - | Just m <- mbMin, m < 0 - = die "Servo.attach: minimum pulse width must be positive" ["Received: " ++ show m] - | Just m <- mbMax, m < 0 - = die "Servo.attach: maximum pulse width must be positive" ["Received: " ++ show m] - | True - = do let minPulse = fromMaybe 544 mbMin - maxPulse = fromMaybe 2400 mbMax - debug $ "Attaching servo on pin: " ++ show p ++ " with parameters: " ++ show (minPulse, maxPulse) - when (minPulse >= maxPulse) $ die "Servo.attach: min pulse duration must be less than max pulse duration" - [ "Received min-pulse: " ++ show minPulse - , "Received max-pulse: " ++ show maxPulse - ] - setPinMode p SERVO - (ip, _) <- convertAndCheckPin "Servo.attach" p SERVO - return Servo { servoPin = ip - , minPulse = fromMaybe 544 mbMin - , maxPulse = fromMaybe 2400 mbMax - } - --- | Set the angle of the servo. The argument should be a number between 0 and 180, --- indicating the desired angle setting in degrees. -setAngle :: Servo -> Int -> Arduino () -setAngle Servo{servoPin, minPulse, maxPulse} angle - | angle < 0 || angle > 180 - = die "Servo.setAngle: angle must be between 0 and 180." ["Received: " ++ show angle] - | True - = do let duration = minPulse + ((maxPulse - minPulse) * angle) `div` 180 - debug $ "Setting servo on pin: " ++ show servoPin ++ " " ++ show angle ++ " degrees, via a pulse of " ++ show duration ++ " microseconds." - -- In arduino, the most we can send is 16383; not that a servo should need such a large value, but - -- just in case - when (duration >= 16383) $ die "Servo.setAngle angle setting: out-of-range." - [ "Servo pin : " ++ show servoPin - , "Angle required : " ++ show angle - , "Min pulse duration: " ++ show minPulse - , "Max pulse duration: " ++ show maxPulse - , "Duration needed : " ++ show duration - , "Exceeds max value : 16383" - ] - let msb = fromIntegral $ (duration `shiftR` 7) .&. 0x7f - lsb = fromIntegral $ duration .&. 0x7f - send $ AnalogPinWrite servoPin lsb msb - --} \ No newline at end of file + in trigger triggername c' [arg (constant n), arg v] \ No newline at end of file From c1ce5bc0d9cc41754c931a86a3b37cc52b17c618 Mon Sep 17 00:00:00 2001 From: McKay Broderick Date: Wed, 16 Dec 2020 21:17:33 -0700 Subject: [PATCH 5/5] Seemed to get better coordinates. But I'm not sure why yet --- haskell-arduino-p05-mood-cue/src/Lib.hs | 6 ++++-- haskell-arduino-p05-mood-cue/src/Servo.hs | 12 +++++++++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/haskell-arduino-p05-mood-cue/src/Lib.hs b/haskell-arduino-p05-mood-cue/src/Lib.hs index 83b6743..79061f2 100644 --- a/haskell-arduino-p05-mood-cue/src/Lib.hs +++ b/haskell-arduino-p05-mood-cue/src/Lib.hs @@ -8,13 +8,15 @@ import qualified Copilot.Arduino.Library.Serial as Serial someFunc :: IO () someFunc = arduino $ do - potVal <- unsafeCast <$> (input a0 :: Sketch (Behavior ADC)) :: Sketch (Behavior Float) + potVal <- cast <$> (input a0 :: Sketch (Behavior ADC)) :: Sketch (Behavior Int32) Serial.baud 9600 Serial.device =: [ Serial.str "potVal: " , Serial.show potVal ] - let angle = Servo.transform potVal + -- TODO: Figure out why do I need to map this again to values that are close to 60 and 245? + let angle = Servo.intTransform' 0 1023 60 245 potVal + Serial.device =: [ Serial.str "angle: " , Serial.show angle , Serial.char '\n' diff --git a/haskell-arduino-p05-mood-cue/src/Servo.hs b/haskell-arduino-p05-mood-cue/src/Servo.hs index 6f4ccbe..97e2985 100644 --- a/haskell-arduino-p05-mood-cue/src/Servo.hs +++ b/haskell-arduino-p05-mood-cue/src/Servo.hs @@ -10,6 +10,7 @@ import Control.Monad.Writer (MonadWriter(tell)) import Copilot.Arduino.Internals import Copilot.Arduino.Uno + transform' :: Fractional a => a -> a -> a -> a -> a -> a transform' lmin lmax rmin rmax value = (((value - lmin) * (rmax - rmin)) / (lmax - lmin)) + rmin @@ -17,11 +18,20 @@ transform' lmin lmax rmin rmax value = transform :: Fractional a => a -> a transform = transform' 0 1023 0 179 +intTransform :: (Typed a, Integral a) => Stream a -> Stream a +-- intTransform = intTransform' 0 1023 54 250 +intTransform = intTransform' 0 1023 0 179 + +intTransform' :: (Typed a, Integral a) => Stream a -> Stream a -> Stream a -> Stream a -> Stream a -> Stream a +intTransform' lmin lmax rmin rmax value = + (((value - lmin) * (rmax - rmin)) `div` (lmax - lmin)) + rmin + + -- The library only defines pwm for Word8, so we did some hacking here to allow floats. pwm' :: Behavior a -> TypedBehavior 'PWM a pwm' = TypedBehavior -instance (IsPWMPin t) => Output (Pin t) (Event 'PWM (Stream Float)) where +instance (IsPWMPin t) => Output (Pin t) (Event 'PWM (Stream Int32)) where (Pin (PinId n)) =: (Event v c) = do (f, triggername) <- defineTriggerAlias' ("pin_" <> show n) "analogWrite" mempty tell [(go triggername, const f)]