CommonLisp와 RaspberryPi로 웹 애플리케이션 ~L치카편~

소개



지난번 , 간단한 웹 어플리케이션을 작성했으므로, 이번에는 웹 브라우저로부터 GPIO를 제어해 L치카를 해 보고 싶습니다.

다양한 라이브러리를 최신 상태로 만들기



이번 실시하는 내용에서는 필요한 각종 라이브러리를 최신 상태로 갱신하지 않으면 에러가 나오고 동작하지 않았습니다.
그래서 우선은 이들을 최신 상태로 하고 싶습니다.

아래의 웹 페이지를 참고로 했습니다.
· Raspberry Pi 2에서 Ningle을 움직일 수 없었기 때문에 조사해 보았습니다.

주)Quicklisp를 설치하고 있다고 가정합니다.

먼저 다음 명령을 사용하여 "~/quicklisp/local-projects/"로 이동합니다.
cd ~/quicklisp/local-projects/

ASDF 최신화



다음 명령으로 Github에서 최신 ASDF 다운로드
git clone https://github.com/fare/asdf.git

CFFI 최신화



다음 명령으로 Github에서 최신 CFFI 다운로드
git clone https://github.com/cffi/cffi.git

Clack 라이브러리 다운로드



Quicklisp에서 기본적으로 사용할 수있는 Clack 라이브러리에서는 왜 "clack-app-route"등을 사용할 수 없었습니다.
그래서 다음 방법으로 Github에서 새로운 Clack 라이브러리를 다운로드했습니다.
git clone https://github.com/unbit/clack.git

이제 필요한 라이브러리가 모두 "~/quicklisp/local-projects/"에 들어갔습니다.

회로도



회로도는 다음과 같습니다.



패키징



cffi, clack, clack-app-route, cl-markup을 Quicklisp로 로드하고 패키지 정의.

packages.lisp
;; Load CFFI, Clack, cl-markup
(ql:quickload :cffi)
(ql:quickload :clack)
(ql:quickload :clack-app-route)
(ql:quickload :cl-markup)

;; Declare as a package
(defpackage cl-iot
  (:use :common-lisp
        :cffi
        :clack
        :clack.request
        :clack.app.route
        :cl-markup))

API 래퍼 작성



이전부터 전자 공작에서 사용하고 있던 것을 사용.
이번은 L치카이므로, 이하에 든 Core function만 사용.

libwiringPi.lisp
(define-foreign-library libwiringPi
  (:unix "libwiringPi.so"))

(use-foreign-library libwiringPi)

;;;; Core function

;; Initialization of the wiringPi
(defcfun "wiringPiSetupGpio" :int)

;; Set the mode of the GPIO pin
(defcfun "pinMode" :void (pin :int) (mode :int))

;; GPIO pin output control
(defcfun "digitalWrite" :void (pin :int) (value :int))

;; Waiting process
(defcfun "delay" :void (howlong :uint))

;; Set the state when nothing is connected to the terminal
(defcfun "pullUpDnControl" :void (pin :int) (pud :int))

;; Read the status of the GPIO pin
(defcfun "digitalRead" :int (pin :int))

웹 애플리케이션을 통한 L 치카 프로그램



웹브라우저에 「ON」과「OFF」의 2개의 버튼을 표시해, 그 버튼으로 LED를 점등/소등하는 프로그램입니다.
제출 버튼을 누를 때 페이지 전환을 피하기 위해 iframe을 사용했습니다.
또한 iframe에 LED 상태를 표시하도록했습니다.

아래의 웹 페이지를 참고로 했습니다.
· Getting started with clack
· OSS는 알루미늄 날개로 비행한다 CL clack & cl-markup

iot-blink.lisp
;; Load packages
(load "packages.lisp" :external-format :utf-8)

(in-package :cl-iot)

;; Load wrapper API
(load "libwiringPi.lisp" :external-format :utf-8)

;; Define GPIO11(pin 23) with the name "+pin+"
(defconstant +pin+ 11)

;; GPIO pin mode
(defconstant +output+ 1)
(defconstant +input+  0)

;; GPIO level
(defconstant +high+ 1)
(defconstant +low+  0)

;; GPIO初期化処理
(defun init-gpio ()
  (wiringPiSetupGpio)              ; Initialize GPIO
  (pinMode +pin+ +output+))        ; Set GPIO11 to output mode

;; Lチカ実行処理
(defun blink (status)
  (if (string= status "on")
      (digitalWrite +pin+ +high+)  ; Lights the LED with GPIO set to 3.3V
      (digitalWrite +pin+ +low+))) ; Set GPIO to 0V and turn off LED

;; トップページを作成する処理
(defun web-page ()
  (list
   (markup
    (html
     (:head
      (:meta :content "text/html" :charset "UTF-8")
      (:title "Web Application with Common Lisp and RaspberryPi"))
     (:body
      (:p "LED Blink") (:br)
      (:form :method "post" :action "/post" :target "iframe"
             (:input :type "submit" :name "led-blink" :value "on"))
      (:form :method "post" :action "/post" :target "iframe"
             (:input :type "submit" :name "led-blink" :value "off"))
      (:iframe :name "iframe"))))))

;; トップページ作成
(defun index (env)
  (declare (ignore env))
  `(200
    (:content-type "text/html")
    ,(web-page)))

;; POST処理
(defun post (env)
  (let ((req (make-request env))
    status)
    `(200
      (:content-type "text/plain")
      ,(list
        (setf status (body-parameter req "led-blink"))  ; POSTされた情報を取得
        (blink status)                                  ; Lチカ実行
        (format nil "LED Status : ~a" status)))))       ; LEDの状態をiframeに表示

;; ページ単位の処理の振り分け
;; このdefroutesマクロが使いたかったため、clack-app-routeを入れました。
(defroutes app (env)
  (GET "/" #'index)                ; GETで"/"が呼ばれたら関数indexを呼び出す
  (POST "/post" #'post))           ; POSTで"/post"が呼ばれたら関数postを呼び出す

;; メイン関数
(defun main ()
  ;; GPIO初期化
  (init-gpio)
  ;; Clack起動
  (clackup #'app :port 5000 :debug t))

;; 実行!!
(main)

실행



다음 명령으로 실행합니다.
sbcl --load iot-blink.lisp

실행하면 다음과 같습니다.



상기 화면에서 멈추면 준비 완료입니다.

여러가지 단말로부터 「http://"RaspberryPi의 IP주소":5000/」에 Web브라우저로부터 액세스 해 보세요.

이하, 각종 웹 브라우저의 모습

Windows7 Internet Explorer



「ON」버튼을 눌렀을 때의 모습


「OFF」버튼을 눌렀을 때의 모습


Windows10 Edge



「ON」버튼을 눌렀을 때의 모습


「OFF」버튼을 눌렀을 때의 모습


Android google 크롬



「ON」버튼을 눌렀을 때의 모습


「OFF」버튼을 눌렀을 때의 모습


실제 LED의 모습



LED ON


LED OFF


마지막으로



IOT처럼 할 수 있었습니다~∩(・ω・)∩
이제 다양한 단말기에서 RaspberryPi의 GPIO를 제어할 수 있습니다!

Web어플리케이션은 어렵지만 재미있네요~.
더 공부하고 할 수 있는 것을 늘려 가고 싶습니다.

이번 소스 코드는 Github에서 공개하고 있습니다.
잘하면 아래에서 부디 ~.
· Github

좋은 웹페이지 즐겨찾기