From ea5aa0845f607410c30f6a537e501c5536e0911b Mon Sep 17 00:00:00 2001 From: akiroz Date: Mon, 15 Aug 2016 14:27:49 +0800 Subject: [PATCH 1/2] convert doorlock daemon to cljs --- .gitignore | 2 +- README.md | 14 ++++-- embedded/.npmrc | 1 - embedded/doorlock.service | 2 +- embedded/project.clj | 13 +++-- embedded/src/com/oursky/doorlock/core.clj | 50 ------------------- embedded/src/com/oursky/doorlock/core.cljs | 57 ++++++++++++++++++++++ 7 files changed, 78 insertions(+), 61 deletions(-) delete mode 100644 embedded/.npmrc delete mode 100644 embedded/src/com/oursky/doorlock/core.clj create mode 100644 embedded/src/com/oursky/doorlock/core.cljs diff --git a/.gitignore b/.gitignore index 69c5b9b..9d432d1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ node_modules/ -target/ +dist/ .DS_Store ._* diff --git a/README.md b/README.md index ce14947..03b7dc5 100644 --- a/README.md +++ b/README.md @@ -26,26 +26,30 @@ Diagrams are saved as `txt` files, you can import them under `File > Import From **Dependencies:** -* clojure * leiningen **Build:** ``` [~/]$ git clone ... -[~/doorlock/embedded]$ lein uberjar +[~/doorlock/embedded]$ lein cljsbuild once ``` -The standalone JAR is now in `doorlock/embedded/target/doorlock--standalone.jar`. +The compiled JS is now in `doorlock/embedded/dist/index.js`. + +**Note:** +It is highly recommended that you build the JS file on a modern x86 computer. +Compilation takes around 30 secs on an i7 laptop. ### Deploy **Embedded OS:** ArchLinux ARM **Dependencies:** -* java + +* nodejs * wiringpi-git (AUR) **Install as systemd service:** -1. copy the standalone JAR to `/home/oursky/` +1. copy the compiled JS to `/home/oursky/doorlock.js` 2. copy `doorlock.service` to `/etc/systemd/system/` 3. enable and start the service: ``` diff --git a/embedded/.npmrc b/embedded/.npmrc deleted file mode 100644 index 2ae2317..0000000 --- a/embedded/.npmrc +++ /dev/null @@ -1 +0,0 @@ -loglevel=silent diff --git a/embedded/doorlock.service b/embedded/doorlock.service index be576e6..3b86ba0 100644 --- a/embedded/doorlock.service +++ b/embedded/doorlock.service @@ -3,7 +3,7 @@ Description=Doorlock Controller Daemon [Service] User=oursky -ExecStart=/usr/bin/java -jar /home/oursky/doorlock-1.0.0-SNAPSHOT-standalone.jar +ExecStart=/usr/bin/node /home/oursky/doorlock.js [Install] WantedBy=multi-user.target diff --git a/embedded/project.clj b/embedded/project.clj index d878d03..ca97d1a 100644 --- a/embedded/project.clj +++ b/embedded/project.clj @@ -1,9 +1,16 @@ (defproject com.oursky/doorlock "1.0.0-SNAPSHOT" :min-lein-version "2.0.0" :dependencies [[org.clojure/clojure "1.8.0"] + [org.clojure/clojurescript "1.9.198"] [org.clojure/core.async "0.2.385"] [com.taoensso/timbre "4.7.0"] - [http-kit "2.2.0"] ] - :aot :all - :main com.oursky.doorlock.core) + :plugins [[lein-cljsbuild "1.1.3"]] + :clean-targets ^{:protect false} ["dist"] + :cljsbuild {:builds [{:source-paths ["src"] + :compiler {:main com.oursky.doorlock.core + :output-dir "dist" + :output-to "dist/index.js" + ;:source-map "dist/index.js.map" + :target :nodejs + :optimizations :simple}}]}) diff --git a/embedded/src/com/oursky/doorlock/core.clj b/embedded/src/com/oursky/doorlock/core.clj deleted file mode 100644 index 601a7a1..0000000 --- a/embedded/src/com/oursky/doorlock/core.clj +++ /dev/null @@ -1,50 +0,0 @@ -(ns com.oursky.doorlock.core - (:require [taoensso.timbre :as log] - [clojure.java.shell :refer [sh]] - [clojure.core.async :refer [! >!! alts! go-loop chan timeout]] - [org.httpkit.server :refer [run-server]] - ) - (:gen-class)) - -; unlock triggering channel -; identify the trigger source by sending {:source } -(def unlock-chan (chan)) - -(defn http-handler [req] - (>!! unlock-chan {:source :network}) - {:status 200}) - -(defn -main [& args] - ; setup GPIO via wiringpi CLI interface - ; the clj-gpio library does not support internal pull-up - (sh "gpio" "mode" "0" "up") - (sh "gpio" "mode" "1" "out") - - ; button event listener - ; hold down for atleast 200ms to trigger - ; will emit event every 25000ms if held down - (go-loop [] - (if (= 1 (read-string (:out (sh "gpio" "read" "0")))) - (sh "gpio" "wfi" "0" "falling") - (! unlock-chan {:source :button})) - (recur)) - - ; listen on unlock-chan for unlock events - ; if a new unlock event is revieved before the 3000ms timeout, the door is kept open. - (go-loop [unlock nil] - (when unlock - (sh "gpio" "write" "1" "1") - (loop [[trigger _] [unlock nil]] - (when trigger - (log/info (str "Unlock triggered by " (:source trigger))) - (recur (alts! [unlock-chan (timeout 3000)])))) - (sh "gpio" "write" "1" "0") - (log/info "Door Locked")) - (recur (! put! alts! chan timeout]] + [cljs.nodejs :refer [require enable-util-print!]] + )) + +(def create-server (aget (require "http") "createServer")) +(def exec-sync (aget (require "child_process") "execSync")) + +; unlock triggering channel +; identify the trigger source by sending {:source } +(def unlock-chan (chan)) + +(defn -main [& args] + ; setup GPIO via wiringpi CLI interface + (exec-sync "gpio mode 0 up") + (exec-sync "gpio mode 1 out") + + ; listen on unlock-chan for unlock events + ; if a new unlock event is revieved before the 3000ms timeout, the door is kept open. + (go-loop [unlock nil] + (when unlock + (exec-sync "gpio write 1 1") + (loop [[trigger _] [unlock nil]] + (when trigger + (log/info (str "Unlock triggered by " (:source trigger))) + (recur (alts! [unlock-chan (timeout 3000)])))) + (exec-sync "gpio write 1 0") + (log/info "Door Locked")) + (recur (! unlock-chan {:source :button})) + (recur)) + + ; http event listener + (.listen + (create-server + (fn [req res] + (put! unlock-chan {:source (or (get-in req ["headers" "x-source"]) :network)}) + (.end res))) + 8090 "127.0.0.1") + + (log/info "=== Daemon Started ===")) + +(enable-util-print!) +(set! *main-cli-fn* -main) From 5a6678979771fae5e794f0564cfdc6b1f018071b Mon Sep 17 00:00:00 2001 From: akiroz Date: Mon, 15 Aug 2016 15:48:29 +0800 Subject: [PATCH 2/2] revert doorlock daemon to clj --- .gitignore | 2 +- README.md | 12 ++--- embedded/doorlock.service | 2 +- embedded/project.clj | 15 ++---- embedded/src/com/oursky/doorlock/core.clj | 50 +++++++++++++++++++ embedded/src/com/oursky/doorlock/core.cljs | 57 ---------------------- 6 files changed, 60 insertions(+), 78 deletions(-) create mode 100644 embedded/src/com/oursky/doorlock/core.clj delete mode 100644 embedded/src/com/oursky/doorlock/core.cljs diff --git a/.gitignore b/.gitignore index 9d432d1..69c5b9b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ node_modules/ -dist/ +target/ .DS_Store ._* diff --git a/README.md b/README.md index 03b7dc5..502e816 100644 --- a/README.md +++ b/README.md @@ -31,25 +31,21 @@ Diagrams are saved as `txt` files, you can import them under `File > Import From **Build:** ``` [~/]$ git clone ... -[~/doorlock/embedded]$ lein cljsbuild once +[~/doorlock/embedded]$ lein uberjar ``` -The compiled JS is now in `doorlock/embedded/dist/index.js`. - -**Note:** -It is highly recommended that you build the JS file on a modern x86 computer. -Compilation takes around 30 secs on an i7 laptop. +The compiled JAR is now in `doorlock/embedded/target/doorlock--standalone.jar`. ### Deploy **Embedded OS:** ArchLinux ARM **Dependencies:** -* nodejs +* java * wiringpi-git (AUR) **Install as systemd service:** -1. copy the compiled JS to `/home/oursky/doorlock.js` +1. copy the compiled JAR to `/home/oursky/doorlock.jar` 2. copy `doorlock.service` to `/etc/systemd/system/` 3. enable and start the service: ``` diff --git a/embedded/doorlock.service b/embedded/doorlock.service index 3b86ba0..8424e13 100644 --- a/embedded/doorlock.service +++ b/embedded/doorlock.service @@ -3,7 +3,7 @@ Description=Doorlock Controller Daemon [Service] User=oursky -ExecStart=/usr/bin/node /home/oursky/doorlock.js +ExecStart=/usr/bin/java -jar /home/oursky/doorlock.jar [Install] WantedBy=multi-user.target diff --git a/embedded/project.clj b/embedded/project.clj index ca97d1a..4f38e61 100644 --- a/embedded/project.clj +++ b/embedded/project.clj @@ -1,16 +1,9 @@ -(defproject com.oursky/doorlock "1.0.0-SNAPSHOT" +(defproject com.oursky/doorlock "1.0.0" :min-lein-version "2.0.0" :dependencies [[org.clojure/clojure "1.8.0"] - [org.clojure/clojurescript "1.9.198"] [org.clojure/core.async "0.2.385"] [com.taoensso/timbre "4.7.0"] + [http-kit "2.2.0"] ] - :plugins [[lein-cljsbuild "1.1.3"]] - :clean-targets ^{:protect false} ["dist"] - :cljsbuild {:builds [{:source-paths ["src"] - :compiler {:main com.oursky.doorlock.core - :output-dir "dist" - :output-to "dist/index.js" - ;:source-map "dist/index.js.map" - :target :nodejs - :optimizations :simple}}]}) + :aot :all + :main com.oursky.doorlock.core) diff --git a/embedded/src/com/oursky/doorlock/core.clj b/embedded/src/com/oursky/doorlock/core.clj new file mode 100644 index 0000000..5417cb7 --- /dev/null +++ b/embedded/src/com/oursky/doorlock/core.clj @@ -0,0 +1,50 @@ +(ns com.oursky.doorlock.core + (:require [taoensso.timbre :as log] + [clojure.java.shell :refer [sh]] + [clojure.core.async :refer [! >!! alts! go-loop chan timeout]] + [org.httpkit.server :refer [run-server]] + ) + (:gen-class)) + +; unlock triggering channel +; identify the trigger source by sending {:source } +(def unlock-chan (chan)) + +(defn -main [& args] + ; setup GPIO via wiringpi CLI interface + ; the clj-gpio library does not support internal pull-up + (sh "gpio" "mode" "0" "up") + (sh "gpio" "mode" "1" "out") + + ; button event listener + ; hold down for atleast 200ms to trigger + ; will emit event every 25000ms if held down + (go-loop [] + (if (= 1 (read-string (:out (sh "gpio" "read" "0")))) + (sh "gpio" "wfi" "0" "falling") + (! unlock-chan {:source :button})) + (recur)) + + ; listen on unlock-chan for unlock events + ; if a new unlock event is revieved before the 3000ms timeout, the door is kept open. + (go-loop [unlock nil] + (when unlock + (sh "gpio" "write" "1" "1") + (loop [[trigger _] [unlock nil]] + (when trigger + (log/info (str "Unlock triggered by " (:source trigger))) + (recur (alts! [unlock-chan (timeout 3000)])))) + (sh "gpio" "write" "1" "0") + (log/info "Door Locked")) + (recur (!! unlock-chan {:source (or (get-in req [:headers "x-source"]) + :network)}) + {:status 200}) + {:ip "0.0.0.0" :port 8090}) + + (log/info "=== Daemon Started ===")) diff --git a/embedded/src/com/oursky/doorlock/core.cljs b/embedded/src/com/oursky/doorlock/core.cljs deleted file mode 100644 index 440ab84..0000000 --- a/embedded/src/com/oursky/doorlock/core.cljs +++ /dev/null @@ -1,57 +0,0 @@ -(ns com.oursky.doorlock.core - (:require-macros [cljs.core.async.macros :refer [go-loop]]) - (:require [cljs.reader :refer [read-string]] - [taoensso.timbre :as log] - [cljs.core.async :refer [! put! alts! chan timeout]] - [cljs.nodejs :refer [require enable-util-print!]] - )) - -(def create-server (aget (require "http") "createServer")) -(def exec-sync (aget (require "child_process") "execSync")) - -; unlock triggering channel -; identify the trigger source by sending {:source } -(def unlock-chan (chan)) - -(defn -main [& args] - ; setup GPIO via wiringpi CLI interface - (exec-sync "gpio mode 0 up") - (exec-sync "gpio mode 1 out") - - ; listen on unlock-chan for unlock events - ; if a new unlock event is revieved before the 3000ms timeout, the door is kept open. - (go-loop [unlock nil] - (when unlock - (exec-sync "gpio write 1 1") - (loop [[trigger _] [unlock nil]] - (when trigger - (log/info (str "Unlock triggered by " (:source trigger))) - (recur (alts! [unlock-chan (timeout 3000)])))) - (exec-sync "gpio write 1 0") - (log/info "Door Locked")) - (recur (! unlock-chan {:source :button})) - (recur)) - - ; http event listener - (.listen - (create-server - (fn [req res] - (put! unlock-chan {:source (or (get-in req ["headers" "x-source"]) :network)}) - (.end res))) - 8090 "127.0.0.1") - - (log/info "=== Daemon Started ===")) - -(enable-util-print!) -(set! *main-cli-fn* -main)