Swift 程式語言

透過 Firebase 與 Raspberry Pi 製作簡單的物聯網 iOS 程式

透過 Firebase 與 Raspberry Pi 製作簡單的物聯網 iOS 程式
透過 Firebase 與 Raspberry Pi 製作簡單的物聯網 iOS 程式
In: Swift 程式語言

看過了這麼多的 Firebase 教學,如果能搭上現在最紅的物聯網應用是不是會很酷?這次想來跟大家分享如何使用 Firebase 將你開發的 iOS app 搭上現實生活的硬體。這邊我分成五個部分來介紹:

  1. 事前準備
  2. 樹莓派設置
  3. Firebase 設定
  4. 樹莓派連接上網
  5. iOS app 撰寫

1. 事前準備

樹莓派 Raspberry Pi 3 Model B

首先,你需要一塊樹莓派 Raspberry Pi 3 Model B 。這塊是最新的開發版,但基本上只要可以連上網以前的板子都可以使用哦,一塊大約35美金。請各位讀者先將樹莓派的作業系統灌好,可以從樹莓派官方教學網站取得安裝 NOOBS 系統: https://www.raspberrypi.org/learning/noobs-install/

Raspberry-Pi-3-top-down-web

圖片取自:https://www.raspberrypi.org/products/raspberry-pi-3-model-b/

基本上就是先將 NOOBS 系統下載下來並放入 micro sd記憶卡中,並將樹莓派開機即可依步驟安裝。

3 顆 LED 燈泡

另外,要購買3 顆 LED 燈泡。顏色不一樣會比較有趣哦!

Color LEDs

杜邦線 / 單芯線

還有,你需要 4 條 公對母杜邦線、2 條 公對公杜邦線 或 單芯線。

connector

電阻 / 電路板

最後,你要準備 1 顆 超過 50歐姆(Ω)的電阻和 1 塊 麵包電路板。

電阻 / 麵包電路板

先來看看最終結果

未開始講解原理之前,先來看看最終結果。當模擬器按下某一顆按鈕時,透過 Firebase 連線的三顆 LED 會分別跟按下的按鈕亮起。

raspberry-firebase-demo

這樣的功能能有什麼應用呢?其實可以把接在樹莓派的三顆 LED 看作任何的硬體開關,也就是說只要有辦法將電路接在家中的檯燈、冷暖氣…等上面,你就有著可以用自己開發的 App 來遙控他們的能力,不覺得很酷嗎?甚至可以接上一些溫濕度感測器、相機模組…等接收家中的資訊,並顯示在手機端上,如此一來你就可以隨時得知家中的狀況並適時的控制家中家電等應用。

那就讓我們開始吧!

2. 樹莓派設置

在寫任何程式之前,我們要先設置好樹莓派和接上電路板。樹莓派的官網有關於實體元件如何接上電路板的教學 ,你可以先去看一看。

https://www.raspberrypi.org/learning/physical-computing-with-python/worksheet/

rasberry-pi-connector

以上為樹莓派 IO port 的對照圖,而下圖是我們即將接出來的電路示意圖。請注意示意圖左方黑色的電路板並非樹莓派!

好!來跟大家說明一下各步驟:

  1. 首先我將三顆 LED 接載麵包板上 – 注意正負極,LED 較短的腳為負極,較長的為正極

  2. 分別使用 兩條公對公杜邦接頭/單芯線 往中間黃色那顆LED接過去 (藍色線為示意)

  3. 從黃色 LED 下方接上一個 50 歐姆以上的電阻,並將電阻另外一端接在麵包板的左側的負極。

  4. 將麵包板左側的負極用 公對母杜邦接頭 接上樹莓派板的 GND(接地) 腳上 (上面樹莓派的 IO port 對照圖有說明 GND 於樹莓派右側的第三隻腳位)

  5. 最後將三顆 LED 個別正極用 公對母杜邦接頭 分別接在樹莓派的三個腳位上 (在我的實體範例是接在樹莓派左側的 17、27、22 腳位,也就是上方示意圖的 GP17, GP27, GP22)

pi-led-connection

完成樹莓派的電路後,來著手 Python 程式的部分吧!首先,讓我們先從樹莓派的系統中開啟 Programming -> Python 3(IDLE)。

python

開發樹莓派的過程會使用到 Python 來做為開發語言,但讀者們不用擔心,Python的語法跟 Swift 很像,並且這樣的應用只需要短短十行不到的程式碼,我也會做註解讓各位了解。

之後,在 Python 3(IDLE)預設的開啟畫面中,新增一個檔案。

python-new-file

該新增的檔案會很像文字編輯器一樣,裡面就會我們寫python的地方。

from gpiozero import LED  
## 就像swift import UIKit 或其他 Library一樣,我們要從樹莓派內建的 gpiozero 裡面匯入 LED 這個 Library

led17 = LED(17)   ## 不像 swift,python宣告變數時不用加入 var,我們直
led22 = LED(22)   ## 接宣告 led17 位在 LED 這個 Library 的第17腳位,
led27 = LED(27)   ## 也就是宣告 led17 為 LED(17) 這個物件。
                  ## 其他下方的 led22, 27 以此類推

while True:       ## 用 while 做一個迴圈

   led17.on()      ## 非常簡單的,如果你要讓某一個腳位有輸出,就是直接呼
   led22.off()     ## 叫官方的函式 on() 即可讓該腳位輸出電壓,反之 off
   led27.on()      ## 就是關閉輸出電壓,被揭在該端的 LED 也就不會亮了。

電路接好跟程式寫好後,讓我們現來測試看看先前接好的電路有沒有成功吧!在編輯器的上方會有一個 Run(執行),或你直接按鍵盤 F5,即可直接編譯此 Python程式檔。依照我所接的電路:紅色LED 接在 17腳位,也就是 LED(17); 黃色接 22腳位; 綠色接 27腳位。編譯執行成功後,應該是紅色與綠色的 LED 會亮。

若沒有成功也別氣餒,可以直接關閉編譯的視窗或按 Ctrl-c 停止執行,檢查看看是不是電路接錯,或者用樹莓派左邊第一個 3V3的腳位接到 LED的正極,GND 接在 麵包板的負極,確認看看是不是 LED的問題。

如果都成功的話,那我們就進行下一階段吧!

3. Firebase 設定

相信各位讀者如果有在追蹤 Appcoda的部落格,會發現有許多 Firebase 的教學文,我們這邊的設定基本上是大同小異。首先,進入 Firebase 首頁 並點選 Get Started For Free(或者你已經在 Firebase 的 Console ),點選 Create New Project。

firebase-home

將專案命名、選擇地區並按下 Create Project後,會進入專案的管理頁面。

之後,讓我們畫下去一點直接點選進入右下角 Database 區塊的 Get Started,進入的畫面會直接是一個像下方這樣的圖。

點選上方的 Rules 權限區塊,看到很像 json的巢狀資料,直接用滑鼠點擊兩下即可編輯 Read, Write的權限,將兩者都改為 true,並按下上方的 Publish。Firebase 會警告你任何有你此網址的人都可以讀寫你的 Firebase。但在此是為了方便,把所有權限都打開比較方便開發,記得別把 Data 區塊的網址連結公布出去即可。

回到 Data 的 tab (Rules旁邊),將滑鼠滑到下方 null 的地方會有 + 跟 x 出現,讓我們按下 + 新增一些預設的資料在 firebase 的資料庫中。

按下 + 號以後會出現可以讓你填寫的 Name 跟 Value 。

Firebase 都是以json去建立檔案,所有的東西都是建立在 name/value上頭 (如同 swift dictionary 的 key/value)。所以如果我們想要建立三顆 LED 亮起的狀態,我會在每一顆的 LED 名稱下建立一個狀態的 name(key) 為 on,而 on 裡面所存的資料就是 LED 是否要亮的布林 true/false。將之轉換成 json 的話就是:

{ "led17" : { "on" : true } }

接著我們要將 17 這筆資料與其他兩個 LED 並存 (led22, led27),所以我們需要一個最上層的 name (key)去存這三顆 LED的資料,而我們暫時先把這最上層命名為 led。而在這邊的做法就是在你剛剛新增的 Name處填寫 “led” (可以依你的喜好命名,在此的 “led” 為最上層的 key),而在 Value 的地方填寫剛剛的 json資料 “{ “led17” : { “on” : true } }” 。這裡我們先把第一顆編號 17 的 led 資料存入。

按下 Add 後會在 Firebase 呈現如下圖的資料結構 。

這表示在我們專案目錄 appcoda-10f88 (每個新建的同名專案後五碼都會不同) 底下有著 led 這個key,而它的 value 則是 led17 這個資料,而以 led17 為 key的資料底下,有著掌管我們 LED 是否要亮的 on 這個資料,它的 value 則是 開/關 的 true/false。

現在,讓我們用滑鼠滑到 led 旁,點擊 + 並新增 led22 與 led27 的資料。注意,因爲 led22/led27 跟 led17 都是我們要一起控制的,屬於同一階層的,所以都要一起加在 led 這個 Name(key) 底下。

因為我們位置已經在 led 這個 key 底下,所以我們要新增的就是剩下的兩個 led (led22/led27),所以我們就把 Name 設為 其中一個的 led22,而 value 就是設為控制 LED 要不要亮的資料:

{"on":true}

讓我們在重複步驟六以上動作,新增最後一個 led27。 完成後的資料結構將會如下圖所示:

提醒一下,我們這邊三個 led 的 on 都先暫時設為 true,就是希望等等讓樹莓派接上去後,三顆燈都會亮起。現在讓我們回去樹莓派測試看看吧!

4. 樹莓派連接上網

在這邊要告知讀者一下,由於 Firebase 官方並沒有開發給 Python 的開發套件 SDK,所以我們將會直接使用 RESTful API 來取得我們的 led 資料。要使用得到 Firebase 資料非常簡單,在我們剛剛的資料結構上方有一串連結,例如我這邊就是:https://appcoda-10f88.firebaseio.com/。

然而你直接點進去該連結,會直接連到你的 Firebase Console 控制台,但如果我們要讀取裡面的資料,只需要很簡單的在連結後面加上 .json 即可。也就是:

https://appcoda-10f88.firebaseio.com/.json

讓我們先將此連結記起來,等等會使用到。你亦可以試試貼上瀏覽器,應該會成功出現如下圖的結果。裡面就是我們剛剛所建立的 led 資料,並且以 json 格式呈現。

讓我們回到樹莓派的程式上面,我們需要加幾行程式碼讓樹莓派可以連上網路。

from gpiozero import LED
  from requests import get   ## 匯入網路中 requests 的 get方法
  import json                ## Library。
                             ## 並再匯入 json Library,才可以讀取
                             ## 與設定 json 資料。

  firebase_url = 'https://appcoda-10f88.firebaseio.com/.json'
  ## 新增一個存放剛剛的 firebase 連結

  led17 = LED(17)
  led22 = LED(22)
  led27 = LED(27)

  while True:

      ledData = get(firebase_url).json()['led']
      ## 在迴圈內宣告 ledData,並讓它用 get 方式去讀取我們的網址,並轉成
      ## json 格式,轉完以後我們使用 python getValueForKey 的方法去取
      ## 得我們資料庫裡 led 底下的資料。
      led17State = ledData['led17']['on']
      led22State = ledData['led22']['on']
      led27State = ledData['led27']['on']
      ## 宣告三個變數 分別是led17的狀態, led22的狀態, led27的狀態,
      ## 所以我們需要從剛剛取得的 ledData資料中繼續往下取得個別 led 屬性
      ## on 的狀態。(還記得我們之前的預設嗎?都是 true)

      ##  讓我們先把先前所寫的三行led測試刪除或助解掉
      ## led17.on()
      ## led22.off()
      ## led27.off()

      ## 最後讓我們加上控制 led17, led22, led27物件開關發亮的屬性
      ## 可以從這邊取得 LED 的官方文件
      ## https://goo.gl/2zHxen
      led17.value = led17State
      led22.value = led22State
      led27.value = led27State
      ## 由於我們所設的變數led17State, led22State, led27State 所
      ## 取得的屬性為一個布林(true, false),所以可以直接設定為 LED
      ## 物件的 value。

完成後,像之前測試樹莓派上LED一樣,讓我們在按下 F5 執行Python程式吧!如果成功,你就會看到目前你接好的三顆 LED 都是亮著的狀態哦!

若你想從網路測試,你可以從 Firebase的資料樹狀圖中更改某一顆 led “on”的狀態,把某一顆的 true改成 false,你應該會發現在改的當下麵包板上的 LED 就會熄滅。

5. 撰寫iOS App

呼~ 過了這麼久,我們終於來到最後一階段要寫 app了!

走吧!繼續前進!

讓我們先建立一個新的專案。我這邊也附上 起始專案 給你。 在專案Storyboard 裡新增三個按鈕,分別將他的背景顏色改成綠、黃、紅,並且在程式碼那端建立個別的 Outlet 連結。

另外讓我們先在右邊的設定欄位 (inspector) 分別設定他們的 tag 為 100(綠), 200(黃), 300(紅),之後會用到。

除了設定 tag 以外,我們也先將按鈕在不同狀態下的 title 先設定好,可以減少在程式撰寫上面的判斷。一般 inspector 裡面預設的 Default 是在按鈕尚未被點選的狀態,所以我們在下方的 title 就打上 off,而要更改按鈕點選中狀態則是選擇 Selected,並將下方的 title 改為 on

再讓我們建立三顆按鈕共同的 IBAction 連結,我們叫它 btnAction,sender 為 UIButton。

如果都建立完,滑鼠移到 IBAction 旁的節點後會看到 Storyboard 上三顆按鈕都有被選到。

讓我們先在 btnAction 裡寫下簡單的測試碼:

sender.isSelected = sender.isSelected ? false : true
// 判斷如果該 sender 的狀態是已經點選的話,讓它改變成跟原本不同的狀態。

讓我們執行看看,應該會出現三顆按鈕,並在你點下去時會更改他的標題狀態,藉此我們會用來控制並顯示樹莓派上 LED 閃亮的狀態。

如果以上都沒問題,我們就要將 Firebase 資料庫接在 iOS app 上囉~

先讓我們回到 Firebase 專案 Console Overview 的地方,並點選 iOS 選項。 他會要你填入 iOS app 的 bundle ID,填完後讓我們按下 Add App 。注意,你不要用和我相同的bundle ID呀。

firebase-bundle-id

按下 Add App 以後會出現如下這樣的畫面,並讓你下載一個 GoogleService-Info.plist 檔案,讓我們直接拉到專案裡面並複製。

接下來是時候安裝套件管理工具 CocoaPods 了。它可以方便我們統整並加入 Firebase 官方開發出的套件來使用。

編者註:如你想了解CocodPods如何操作,可參考這篇文章

要如何安裝呢?讓我們先關掉 Xcode 專案,並開啟 Mac裡的 Terminal 終端機,在終端機裡輸入 cd,並空格再將你開發專案的資料夾拉進去(或者手動輸入開發資料夾的目錄資料),最後按下 Enter。 並繼續在 Terminal 輸入 pod init,再次按下 Enter。

完成後,CocoaPods會產生一個Podfile。利用 vi 或其他 editor編輯 Podfile,並在 use_frameworks! 下方加入兩個pods:

# Uncomment the next line to define a global platform for your project
# platform :ios, '9.0'

target 'ControlLED' do
  # Comment the next line if you're not using Swift and don't want to use dynamic frameworks
  use_frameworks!

  # Pods for ControlLED
    pod 'Firebase/Core'
    pod 'Firebase/Database'
end

儲存檔案之後,讓我們在 Terminal 輸入 pod install 並按下 Enter。成功的話 Terminal 會自動下載一些資料並顯示完成加入。

cocoapods-led

自此以後,我們的專案資料夾裡就會多出了一些檔案,倘若我們以後要開發包含 CocoaPods 的套件管理工具,我們就需要打開白色的那個 .xcworkspace 檔案(即ControlLED.xcworkspace)開發。

繼續之前 Firebase 尚未完成的頁面,依照他的做法在我們專案的 AppDelegate file 中加入兩行程式碼,並按下 Finish 完成 Firebase 設定。開啟 AppDelegate.swift,加入兩行程式碼(黃色標籤)以初始化Firebase app:

import UIKit
import Firebase

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?


    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        
        FIRApp.configure()
        
        return true
    }

    func applicationWillResignActive(_ application: UIApplication) {
        // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
        // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
    }

    func applicationDidEnterBackground(_ application: UIApplication) {
        // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
        // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
    }

    func applicationWillEnterForeground(_ application: UIApplication) {
        // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
    }

    func applicationDidBecomeActive(_ application: UIApplication) {
        // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
    }

    func applicationWillTerminate(_ application: UIApplication) {
        // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
    }


}

之後,回到我們的 ViewController 裡匯入 Firebase DB 的 Library:

import FirebaseDatabase

並在 Class 底下宣告一個全域變數 ref,而它是 Firebase 用來取得 DB 資料的一種型別:

var ref: FIRDatabaseReference!

接著我們建立一個 function 使得 App 一開啟時就可以確認 Firebase 裡 led 的狀態,好讓 UIButton 上的 State 可以準確地顯示目前狀態:

func getOnNet() {

    ref = FIRDatabase.database().reference()
    // 從 Firebase 取得資料庫資料。

    // 進入該資料並指定位置 "led"。
    // 官方 observe 方法去監測指定位置底下的數據變化 。
    ref.child("led").observe(.value, with {                   
        (snapshot) in
        // 並設定 snapshot 為閉包回傳的資料。

        let value = snapshot.value as? [String:AnyObject]
        // 從 snapshot 的 value 取得的就是我們在資料庫的資料,
        // 並指定它的型別就是 [String:AnyObject]。

        let led17State = value?["led17"]?["on"] as! Bool
        let led22State = value?["led22"]?["on"] as! Bool
        let led27State = value?["led27"]?["on"] as! Bool
        // 宣告三個常數,讓它們解析從資料庫取得下來的資料,
        // 分別解析 led 資料底下的 led17, led22, led27,
        // 再從中取得 on 的狀態,
        // 並轉換指定型別為布林。

        self.greenBtn.isSelected = led17State
        self.yellowBtn.isSelected = led22State
        self.redBtn.isSelected = led27State
        // 最後就是設定每一顆 iOS 上的按鈕的狀態等於從網路上取得的狀態。
      })
}

完成後記得要將上方的 getOnNet() 放進 viewDidLoad() 哦。執行看看,iOS app 上面按鈕的狀態就會跟 Firebase 資料庫或樹莓派所亮的燈一樣哦!

最後最後了!就是當我們按下 iOS app 上的按鈕時,要改變資料庫的狀態,如此一來樹莓派上的 led 就會跟著改變了。讓我們在 IBAction btnAction 下加入幾行程式碼,方能將資料寫入 Firebase。

@IBAction func btnAction(_ sender: UIButton) {

        // tag100: 綠色btn, 200: 黃色btn, 300: 紅色btn

        sender.isSelected = sender.isSelected ? false : true

        switch sender.tag {
        case 100:
            ref.child("led/led17/on").setValue(sender.isSelected ? true : false)
        case 200:
            ref.child("led/led22/on").setValue(sender.isSelected ? true : false)
        case 300:
            ref.child("led/led27/on").setValue(sender.isSelected ? true : false)
        default:
            break
        }
        // 我們使用了 Swith Case 去判斷被按下的按鈕是哪一顆,
        // 依據先前所設定的 tag 即可以判斷是 綠/黃/紅 哪一顆按鈕被傳進來這個函式,
        // 當 tag 為 100 的按鈕進來後,我們進去設定全域變數 ref 資料的 child,
        // 這個情況下我們要改變的就是 Firebase 裡 led 底下 led17 的 on 狀態,
        // 所以可以直接寫成 "led/led17/on" (就像電腦目錄一層一層下去一樣),
        // 之後用官方的方法 setValue 給他,
        // 而因為在 Firebase 上 on 的狀態是布林,
        // 所以我們就將 iOS App 該按鈕是否被點選的狀態寫入,
        // 在裡面判斷式的解釋為:
        // 如果 sender.isSelected (被點擊的狀態) 是 true,我就寫入 true,反之則 false。
    }

呼~ 完成了!讓我們將樹莓派放在旁邊,並將 iOS 程式執行。結果應該就會像我們一開始看到的那樣,你可以透過 iOS App 控制樹莓派的 LED 狀態!

如果你發現 iOS app 上面按鈕跟數莓派的 led 是相反的話,可能就是在程式設定腳位時有錯, 亦或者是樹莓派上的腳位接錯。但沒關係,你可以直接調整 iOS app 寫入資料庫的 led 位置,或者直接改變樹莓派 led 的線路。

對我來說直接改變樹莓派的線路好像比較快一點 XD

是時候發揮你的想像力

就如前面所說,你可以把樹莓派輸出的腳位都當成一種開關,自己接上家裡的電器,如此一來你就可以實現透過網路遠端控制家電的能力。亦或者接上眾多的模組化感測器,遠端監控某一個場所的任何狀態,唯一侷限你的就是自己的想像力而已 XD。

期待看到你們所做出並分享的創意!

在這邊我附上完整的程式碼給你參考。

作者
Chen Kuan L.
Kuan - 任職於 25sprout 新芽網路擔任 iOS 開發者,喜歡閱讀關於科技、設計、商業相關書籍文章,熱愛嘗試與實作不同想法,歡迎一起討論研究。可以透過email與我聯絡([email protected])。
評論
很好! 你已成功註冊。
歡迎回來! 你已成功登入。
你已成功訂閱 AppCoda 中文版 電子報。
你的連結已失效。
成功! 請檢查你的電子郵件以獲取用於登入的連結。
好! 你的付費資料已更新。
你的付費方式並未更新。