基於android開發手機控制空調的程序(硬件+軟件)

本文適用於在沒有紅外發射器的手機上,利用音頻接口連接相關發射器進行信號發射。效果展示:http://t.cn/RLhOuCf

2016年2月28日更新:

使用音頻還是要給手機插音頻線,所以具有相當大的侷限性。而且在開發過程中還不容易採樣,容易受各種因素干擾。用聲波模擬脈衝信號也具有一定難度。最近在玩Arduino,發現這個開發板很適合用來做採樣和發射。

通過Arduino錄製紅外與播放紅外信號的相關信息寫在了這篇博文中:

http://blog.csdn.net/luhanglei/article/details/50762325

2016年06月07日更新

關於“正弦波”的作用:

通過在Arduino上實驗就能知道,傳感器接收紅外信號的時候,並不是只要紅外發光二極管亮起就是1,滅掉就是0。

紅外接收器其實只接收頻率爲38KHz的信號(不是光的頻率,就是通斷電而閃爍的頻率),因此當需要輸出爲1的信號的時候,發出的實際是一個20KHz(因爲其實20KHz也可以被接收到)的信號,而不是常亮。



硬件:

作者的手機型號是nexus4,使用直接連接紅外發射管(直接無反應)和DIY使用三極管,進行信號放大的方式(光太暗),均告失敗。。。

在製作硬件的過程中,可以使用①用手機攝像頭觀察和②暫時用可見光發光二極管替代的方法查看效果。

最終解決方法:jd買了一個usb小音響(19RMB左右),把喇叭全都拆掉,將紅外二極管串聯相應大小的電阻(電阻大小多少合適,有公式請百度),焊接到原來喇叭的位置上。

效果圖:


原理:手機輸出的音頻信號太弱,勢必要進行放大才夠我們使用,三極管放大的效果還是太弱。這時候小音箱就成了救命稻草了。。。它很便宜,關鍵是有着很成熟的音頻信號放大電路!

以上內容完成以後可以使用現有的遙控類軟件進行測試。

軟件:

①首先查找要目標設備的遙控器編碼文檔。

②現在網上已經可以下到很多可以讓手機當遙控器的軟件了,他們不開源,但是可以獲取他們輸出的信號參考一下。

(下圖是用一根兩端都是公頭的音頻線,一端連接手機,另一端連接電腦麥克口,使用錄音軟件,用44100採樣率錄下來的,再用cooledit打開分析的。線也可以在jd買到,不到10塊)

根據我空調的文檔,前面9000μs高電平+4500μs低電平是起始碼,後面的01都是高低電平組合,一高一低爲一位,低電平短的是0,長的是1。放大以後可以看到,生成的是很漂亮的正弦波。

③自己根據需要生成帶有高低電平的聲波。

根據網上搜索到的正弦波生成算法,作者仿造了上面的正弦波,但發現再次用電腦聲卡捕獲以後,高電平全體在低電平一側,而且低電平的座標出現很大的偏移。

圖中內容爲低電平出現偏移,一開始認爲是在播放一開始就發聲會導致偏移,圖中已經採取相關措施,看來這個猜想不正確。即使文件開始很久之後纔開始發聲,還是會偏移。

後來經過多次測試,發現問題出在“低電平”的處理上。在使用8bit pcm編碼的情況下,一直認爲正弦波與橫軸交點的縱座標意味着0,上爲正,下爲負,於是作者簡單的將低電平置爲了0x00,後來發現問題就出在這裏。正弦波的與橫軸的焦點,是整個寬度的中間值,而不是一個負值。

生成高低電平波代碼如下(HEIGHT爲振幅):

/**
     * @param waveLen 週期,每個週期佔用多少次採樣率
     * @param length  長度,單位同樣是佔用了多少次採樣率。例如採樣率爲44100,需要1秒長,則length值爲44100/waveLen
     * @return
     */
    public static byte[] getHigh(int waveLen, int length) {
        byte[] wave = new byte[length];
        for (int i = 0; i < length; i++) {
            wave[i] = (byte) (HEIGHT * (1 - Math.sin(2 * Math.PI
                    * ((i % waveLen) * 1.00 / waveLen))));
        }
        return wave;
    }

    public static byte[] getLow(int waveLen, int length) {
        byte[] wave = new byte[length];
        for (int i = 0; i < length; i++) {
            wave[i] = (byte) (HEIGHT);
        }
        return wave;
    }

生成0,1的代碼如下:

private byte[] get0() {
        int highLength = (int) Math.round(sampleRate / 1000000.0 * HIGHTIME / WAVELEN) * WAVELEN;//採樣率/1000000.0*高電平時間=高電平佔用的採樣次數,先除後乘是想得到一個整數倍的週期,具體有沒有益處未證實
        byte[] high = getHigh(WAVELEN, highLength);
        int lowLength = (int) Math.round(sampleRate / 1000000.0 * LOWTIMEOF0 / WAVELEN) * WAVELEN;
        byte[] low = getLow(WAVELEN, lowLength);
        byte[] res = new byte[highLength + lowLength];
        System.arraycopy(high, 0, res, 0, high.length);
        System.arraycopy(low, 0, res, high.length, low.length);
        return res;
    }

    private byte[] get1() {
        int highLength = (int) Math.round(sampleRate / 1000000.0 * HIGHTIME / WAVELEN) * WAVELEN;
        byte[] high = getHigh(WAVELEN, highLength);
        int lowLength = (int) Math.round(sampleRate / 1000000.0 * LOWTIMEOF1 / WAVELEN) * WAVELEN;
        byte[] low = getLow(WAVELEN, lowLength);
        byte[] res = new byte[highLength + lowLength];
        System.arraycopy(high, 0, res, 0, high.length);
        System.arraycopy(low, 0, res, high.length, low.length);
        return res;
    }
將生成好的數據按照編碼格式拼接起來,連接到發射器播放,即可控制空調等設備。

 AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate,
                AudioFormat.CHANNEL_OUT_MONO,
                AudioFormat.ENCODING_PCM_8BIT, wave.length, AudioTrack.MODE_STATIC);
        if (audioTrack != null) {
            audioTrack.write(wave, 0, wave.length);
            audioTrack.play();
        }

作者將以上功能放到了一個service中來控制,實現了安卓手錶(android wear)控制空調的功能,效果:http://t.cn/RLhOuCf

建議在開發過程中遇到問題的時候多抓取音頻分析,發現問題所在。

如果出現開頭丟失的問題,可以在數據之前加入一定的中低電平和低電平信號,來當炮灰。


感謝

http://blog.csdn.net/kangear/article/details/39376105

http://m.blog.csdn.net/blog/caoshichao520326/8646913

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章