本文適用於在沒有紅外發射器的手機上,利用音頻接口連接相關發射器進行信號發射。效果展示: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