Android Things 開發入門

singwhatiwanna發表於2018-06-20

本文由玉剛說寫作平臺提供寫作贊助

原作者:AndroFarmer

版權宣告:本文版權歸微信公眾號 玉剛說 所有,未經許可,不得以任何形式轉載

Android Tings 是什麼

Android things(後面正文內容簡稱ats)是一個物聯網平臺,他基於Android,並做了相當多的改造以適合在一些低配置的物聯網裝置上執行,同時它又是安卓,因為其保留了絕大部分Android framework的功能。因此藉助ats平臺我們不需要了解嵌入式(準確的說還是需要了解一些的)相關的知識就可以開發出一系列智慧硬體產品。

Android Tings 能做什麼

要說明這個問題,我們不如從反面說明,ats不能做什麼。
先看張ats的框架圖:

Android Things 開發入門

從圖上可以看出,ats是在標準安卓的framework層上增加了things support library,同時為了保證嵌入式效能以及根據嵌入式裝置的特點的需要,精簡和修改了部分標準framework層的東西。
那麼具體ats不能做什麼,除了下圖中的這些標準android的特性不支援外,其他都支援(gms除外,因為ats的gms框架是定製的跟標準android不通用):

Android Things 開發入門

可以看出ats對標準android framework的支援還是挺多的,這也就保證了app開發者們可以很輕鬆的做ats的開發。

開發Android Tings的硬體條件

由於android studio 並未有提供ats的模擬器,所以我們必須要有一個能刷ats系統的硬體(比如樹莓派)才行,同時為了還需要一些感測器、電阻、電子按鈕、麵包板、led燈等一些列配套外設,因為iot(物聯網)的開發很多時候都是對硬體的操作,有了這些外設才能更好的去實驗一些demo。
下面貼出我購買的硬體全家桶套裝:

Android Things 開發入門

樹莓派針腳說明

ats的開發很多時候都是操作硬體,所以我們就有必要去研究,如何去操作外設裝置,一個很重要的方法就是通過外設介面去操作。
看下圖:

Android Things 開發入門
Android Things 開發入門

第一張圖畫紅線的地方,按照針腳一對一的順序分別對應下面的這張圖說明。
看到這可能有些暈,這這是什麼鬼,當然電子工程相關專業的應該一看就懂,沒錯這就是匯流排。

何為匯流排

匯流排,匯流排,就是總讓你陷進去

Android Things 開發入門

請原諒我不會搞笑還胡說的壞毛病。

樹莓派支援的匯流排型別:
GPIO,I2C,I2S,SPI,PWM,UART

關於匯流排我也不是專業的。以我的理解就是為了控制不同的硬體裝置,而對電訊號做不同的處理而劃分的標準。這裡我們先混個臉熟,後面用的最多的是GPIO,也就是以名稱BCM開通的針腳,後面我們會通過名稱去控制這個針腳上的裝置。
關於匯流排詳細的介紹,大家可以參考下這篇文章:
https://blog.csdn.net/haima1998/article/details/18729929

如何刷寫ats到樹莓派開發板上

關於這方面的介紹,網上還是挺多的,我搜了一下最不缺的就是這類文章,所以這裡就不做詳細介紹了,簡單介紹下
步驟:

  1. 進入android things console,建立屬於自己硬體的rom,這是google的雲管理平臺(在這裡可以建立硬體裝置的rom,釋出ota更新等)
    地址為:
    https://partner.android.com/things/console/
Android Things 開發入門

2. 通過軟體刷寫rom到記憶體卡上,這裡推薦使用Etcher這個軟體

Android Things 開發入門

這個軟體的使用還是很簡單的,一鍵式的。
3. 刷寫完成後通電,插入顯示器,不出什麼意外就可以正常開機了
至此刷寫rom的工作就完成了

  1. 這裡推薦另一種更簡便的方式:使用官方提供工具的:android-things-setup-utility
    下載地址:
    https://partner.android.com/things/console/#/tools
    解壓完了以後如圖:
    Android Things 開發入門

    可以根據自己的平臺選擇操作。

如何操作Android Things

由於物聯網設配的特殊性,我們沒有像安卓一樣的觸控式螢幕和按鍵等外設來操作裝置,所以我們需要通過如下兩種方式去連線裝置。

1.通過usb轉ttl裝置直接連線

Android Things 開發入門

具體如何操作,可以自行百度

2.通過區域網連線(推薦方式)
先讓ats裝置連線到路由器,這裡推薦連線顯示器滑鼠鍵盤視覺化操作連線網路等操作,連線上顯示器如下圖:

WechatIMG16.jpeg

常用的adb 命令

通過這些命令我們可以更好的管理和使用ats

  1. 連線AndroidThings
    adb connect
  2. 斷開AndroidThings
    adb disconnect
  3. 關機
    adb shell reboot -p
    4.解除安裝應用
    adb shell uninstall
    使用adb connect 命令連線到ats裝置後就可以像開發app一樣用android studio去開發部署和除錯了。

Demo展示及硬體搭建-light

這是一個操作LED讓其閃爍的demo,
我們通過這個demo來介紹基本的操作硬體的方法
先看下最終效果:

Android Things 開發入門

硬體搭建步驟:

  1. 通過麵包板串聯一個電阻和一個LED燈並連線到麵包板的正極
  2. 麵包板的正極連線到樹莓派一個GPIO匯流排埠
  3. LED另一個針腳通過跳線連線到樹莓派的GROUND針腳上
    附上我的連線圖:
Android Things 開發入門

至此硬體模組的搭建就完畢了。

更直觀一點的參考如下官方圖片:

Android Things 開發入門

這裡需要著重說明是:Ground為地線,用來模擬零電壓線,GPIO匯流排埠可以根據所加的電阻以及LED選擇不同的電壓,我這裡選擇的是電壓3.3v名為BCM2的埠

light程式碼分析

public class LightActivity extends Activity {
    Handler mHandler;
    PeripheralManager mPeripheralManager;
    Gpio mLightGpio;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_light);
        mHandler = new Handler();
        mPeripheralManager = PeripheralManager.getInstance();
        try {
            mLightGpio = mPeripheralManager.openGpio("BCM2");
            mLightGpio.setDirection(Gpio.DIRECTION_OUT_INITIALLY_LOW);
            mHandler.post(mBlinkRunnable);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    private Runnable mBlinkRunnable = new Runnable() {
        @Override
        public void run() {
            try {
                if (mLightGpio == null)
                    return;
                mLightGpio.setValue(!mLightGpio.getValue());
                mHandler.postDelayed(mBlinkRunnable, 1000);
            } catch (IOException e) {

            }
        }
    };

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mLightGpio != null) {
            try {
                mLightGpio.close();
                mLightGpio = null;
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
複製程式碼

程式碼不多,就全貼上去了,下面我們分析下程式碼。

mPeripheralManager=PeripheralManager.getInstance();
mLightGpio= mPeripheralManager.openGpio("BCM2");
複製程式碼

我們通過PeripheralManager單例後呼叫openGpio方法,傳入的引數為GPIO埠對應的名稱,這樣就拿到這個埠控制物件Gpio的一個例項mLightGpio。

mLightGpio.setDirection(Gpio.DIRECTION_OUT_INITIALLY_HIGH);
複製程式碼

這句話程式碼有兩個作用:1.設定電平方向為輸出方向;2設定初始電平為高電平並立即啟用。
這句程式碼執行後,LED會變為常亮狀態。
我們還可以通過以下三行程式碼實現跟上面一句同樣的效果:

//設定電平方向為輸出方向,設定初始電平為低電平並立即啟用
mLightGpio.setDirection(Gpio.DIRECTION_OUT_INITIALLY_LOW);
//設定啟用狀態為高電平
mLightGpio.setActiveType(Gpio.ACTIVE_HIGH)
//進行啟用
mLightGpio.setValue(true);
複製程式碼

在mBlinkRunnable中通過 mLightGpio.setValue(!mLightGpio.getValue())
來迴圈改變電平的啟用狀態來實現LED的閃爍,至此LED就可以blingbling的閃了。

最後看下ats專案和標準安卓有和區別
主要區別有兩點:

  1. ats專案許可權不需要使用者動態授權,直接在manifest中聲名即可,如訪問GPIO匯流排埠以及下面需要講到的註冊使用者驅動所需要的許可權
<uses-permission android:name="com.google.android.things.permission.USE_PERIPHERAL_IO"/>
<uses-permission android:name="com.google.android.things.permission.MANAGE_INPUT_DRIVERS" />
複製程式碼
  1. ats專案部署到硬體後可以設定其意外關閉重啟和開機自啟動,這樣可以保證物聯網裝置的高可用狀態,不至於程式崩潰導致裝置無法使用
    具體步驟只需要在入口Activity加入如下intent-filter:
 <intent-filter>
     <action android:name="android.intent.action.MAIN"/>
     <category android:name="android.intent.category.HOME"/>
     <category android:name="android.intent.category.DEFAULT"/>
 </intent-filter>
複製程式碼

使用者驅動-userdriver

所謂使用者驅動就是ats允許你把相應的硬體的電訊號轉化成系統事件,比如按鈕的點按事件註冊成系統的鍵盤按鍵事件,溫度感應器的電訊號註冊成系統已存在的感應器事件,這樣各個元件都可以很方便的使用標準的framework api去操作硬體了。

舉個例子

本例展現的內容是把按鈕的點按事件電訊號註冊成鍵盤的key事件,這樣按鈕就變成一個鍵盤了,可以點選和長按。註冊成系統事件後可以在應用程式的各個元件中進行使用了。
演示效果:

Android Things 開發入門

硬體安裝圖:

Android Things 開發入門

看下程式碼:

package com.androfarmer.button;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
import android.view.KeyEvent;

import com.google.android.things.pio.Gpio;
import com.google.android.things.pio.GpioCallback;
import com.google.android.things.pio.PeripheralManager;
import com.google.android.things.userdriver.UserDriverManager;
import com.google.android.things.userdriver.input.InputDriver;
import com.google.android.things.userdriver.input.InputDriverEvent;
import java.io.IOException;

public class KeyCodeDriverService extends Service {

    private InputDriver mDriver;
    private Gpio mButtonGpio;
    private static final int KEY_CODE = KeyEvent.KEYCODE_A;

    @Override
    public void onCreate() {
        super.onCreate();

        //建立輸入驅動,並設定驅動的基本資訊
        mDriver = new InputDriver.Builder()
                .setName("Button2Keyboard")
                .setSupportedKeys(new int[]{KEY_CODE})
                .build();

        // 通過 UserDriverManager註冊上面建立的驅動
        UserDriverManager manager = UserDriverManager.getInstance();
        manager.registerInputDriver(mDriver);

        PeripheralManager peripheralManager = PeripheralManager.getInstance();
        try {
            mButtonGpio = peripheralManager.openGpio("BCM21");
           //設定電平方向為輸入
            mButtonGpio.setDirection(Gpio.DIRECTION_IN);
            //設定啟用型別
            mButtonGpio.setActiveType(Gpio.ACTIVE_LOW);
            //設定監聽事件為:電平中斷變化事件,Gpio.EDGE_BOTH
            //意味著電平從低到高中斷以及從高到低中斷都會觸發回撥
            mButtonGpio.setEdgeTriggerType(Gpio.EDGE_BOTH);
            //設定電平變化的監聽器
            mButtonGpio.registerGpioCallback(new GpioCallback() {
                @Override
                public boolean onGpioEdge(Gpio gpio) {
                    try {
                        Log.d("-------------button",gpio.getValue()+"");
                        boolean pressed=gpio.getValue();
                        InputDriverEvent event = new InputDriverEvent();
                        event.setKeyPressed(KEY_CODE, pressed);
                        mDriver.emit(event);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    //返回true代表一直監聽,false代表監聽一次
                    return true;
                }
            });
        } catch (IOException e) {
            e.printStackTrace();
        }
    }



    @Override
    public void onDestroy() {
        super.onDestroy();
        //接觸註冊
        UserDriverManager manager = UserDriverManager.getInstance();
        manager.unregisterInputDriver(mDriver);
        //關閉gpio埠
        try {
            mButtonGpio.close();
            mButtonGpio = null;
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}

複製程式碼

程式碼的註釋寫的已經很詳細了,就不過多解釋了。
這裡簡要介紹下步驟

  1. 通過InputDriver建立驅動基本資訊物件
  2. 通過UserDriverManager註冊驅動
  3. PeripheralManager處理具體外設硬體的電訊號
  4. 通過InputDriver的emit方法將InputDriverEvent事件發射出去,這樣系統各個元件就能相應這個事件了
  5. 別忘了不再使用時解除註冊和關閉埠

使用者驅動型別

主要分為四類:

  1. Location 位置驅動
  2. Input 使用者輸入事件驅動
  3. Sensor 感測器驅動
  4. LoWPAN

對使用者驅動的進一步說明

ats的很多場景我們可以像開發app一樣,對於做過android開發的同學們來說這很easy,但是使用者驅動這個概念我們可能第一次聽說。ats的設計是模組化的,一個ats硬體板只會包含基礎的硬體模組如:網路模組,cpu,記憶體等。我們拿到這個執行ats系統的基礎板後,如果要開發成具體的物聯網產品,可能需要接外設感測器去實現具體功能:比如溫度感測器,煙霧報警器等。

市面上感測器門類複雜,如果我們開發時將業務邏輯與硬體感測器的操作雜糅在一起,很顯然這樣的話我們的程式碼很脆弱並且不具備可移植性,換個同類別的其他型號感測器就無法使用了。因此使用者驅動的出現很好的解決了這個問題,不管外設硬體同類別的型號有多少種,我們只需要寫相應的使用者驅動將其註冊成framework已經實現的感測器事件,這樣業務邏輯只需要跟標準的framework api打交道,而不用管具體用了哪一種感測器。

附上一個開源專案,這裡面實現了很多市面上普遍使用的硬體的使用者驅動
https://github.com/androidthings/contrib-drivers
有興趣的同學可以去研究下不同型別的使用者驅動是如何編寫

ps:本文很多內容和案例來自於官網,更多案例請檢視
https://developer.android.com/samples/?technology=iot&language=java

Android Things 開發入門
歡迎關注微信公眾號,接收第一手技術乾貨

相關文章