
簡介面向嵌入式與Android開發者的CC2530溫度、紅外傳感器控制上位機項目整合Zigbee節點與手機端遠程監控適合學習無線傳感器網絡和物聯網應用開發的工程師。壓縮包共78個文件約4.45MB包含Android工程源碼、XML布局、PNG圖標、JAR依賴庫及多個APK安裝包結構完整可直接導入編譯或安裝體驗。已有944人學習項目展示了從串口通信建立、協議設計、傳感器數據采集到UI實時更新的全鏈路實現能幫助讀者理解CC2530硬件控制、Android USB串口通信以及前后端數據交互。參考其中協議定義和應用層設計可快速移植DS18B20、HC-SR501等傳感器邏輯構建自己的遠程監測方案。1. 項目概述與整體方案選型1.1 需求拆解這個項目到底要做什么這個項目是我幫一個做環境監測的朋友做的核心需求很明確用CC2530作為下位機節點外接DS18B20采集環境溫度外接熱釋電紅外傳感器檢測人體活動兩個傳感器數據要通過串口實時上報給Android手機端App不僅要顯示數據還要能下發指令控制繼電器和蜂鳴器。一句話總結就是傳感器采集加執行控制Android手機當遠程面板。別看功能簡單真正做起來需要打通的東西不少CC2530端要有穩定的傳感器驅動和組幀邏輯Android端要有可靠的串口通信和數據解析兩端之間還要約定一套不會出錯的通信協議。這篇文章把從下位機到上位機的完整鏈路拆開講不光是貼代碼還會說明每一步為什么這么做。適合做課程設計、電子競賽、智能家居類項目的同學參考也適合剛入門嵌入式、想搞清楚設備和App怎么對話的開發者。1.2 方案選型為什么用CC2530又為什么不用Z-Stack用CC2530做主控很多人第一反應是這不就是個Zigbee芯片嗎是不是要用Z-Stack協議棧組網。這恰恰是新手最容易走偏的地方。CC2530本質是一顆8051內核的SoC自帶2.4GHz射頻和豐富的外設它既可以跑Zigbee協議棧也完全可以當一顆普通單片機裸奔使用。這個項目里只有一個采集節點要直接連手機不存在多節點組網需求所以我選擇了裸機編程。原因很實際Z-Stack的OSAL事件調度機制對新手來說學習曲線陡而且協議棧初始化會占掉不少系統資源單純為了GPIO采集和串口發送引入一套協議棧完全是殺雞用牛刀。如果后續要擴展成多個CC2530節點采集匯聚到協調器再轉發給手機那時候再切到Z-Stack重新設計架構也不遲。硬件上我用的是一塊CC2530最小系統板加底板擴展因為板載集成了USB轉串口芯片調試和供電都方便。外接DS18B20在P1.0口紅外傳感器接P1.1口繼電器控制引腳放在P1.2預留一個蜂鳴器在P1.3IO分配盡量錯開避免后來的PCB布線和程序擴展互相干擾。2. 下位機CC2530的采集、控制與通信實現2.1 我使用的開發環境與時鐘配置CC2530的開發環境我用的是IAR for 8051這個IDE是TI官方主推的工程配置里有一點必須注意芯片型號選CC2530F256鏈接器配置里要用到對應的配置文件不然燒錄后程序跑不起來。時鐘選擇是個容易踩坑的點。CC2530內部有16MHz RC振蕩器但RC振蕩器的頻率精度受溫度和電壓影響較大如果直接用內部時鐘做串口波特率長時間通信后累計誤差會導致數據錯位。我的做法是外部32MHz晶振通過寄存器配置將系統主時鐘切到32MHz外部晶振這樣115200波特率才能保證足夠的精度。void system_clock_init(void) { // 切換到32MHz外部晶振確保串口波特率穩定 SET_MAIN_CLOCK_SOURCE(0); // 選擇外部32MHz晶振 CLKCONCMD ~0x40; // 設置系統時鐘源 while (CLKCONSTA 0x40); // 等待切換完成 CLKCONCMD ~0x07; // 系統時鐘設為32MHz while (CLKCONSTA 0x07); }2.2 DS18B20溫度采集單總線時序與代碼實現DS18B20是Dallas出的單總線數字溫度傳感器一根數據線就能完成供電和通信精度能做到12位分辨率0.0625攝氏度。但它對時序要求很苛刻讀時序和寫時序的最小時間差是微秒級的8051在這個地方特別容易被中斷打擾導致時序被拉長。我的做法是在單總線操作的臨界區前關中斷操作完再開。代碼里所有延時函數必須用邏輯分析儀實測校準過不能想當然填幾個空循環就完事。最容易被忽略的是上電后的轉換時間DS18B20在12位分辨率下的轉換時間最長750ms如果你發完啟動轉換指令就立刻讀溫度讀回來的永遠是上一次的舊值。uint16_t ds18b20_read_temperature(void) { uint8_t temp_l, temp_h; int16_t raw; uint16_t result; EA 0; // 關中斷保護單總線時序 ds18b20_reset(); ds18b20_write_byte(0xCC); // 跳過ROM匹配 ds18b20_write_byte(0x44); // 啟動溫度轉換 delay_ms(750); // 等待轉換完成必須等夠 ds18b20_reset(); ds18b20_write_byte(0xCC); ds18b20_write_byte(0xBE); // 讀取暫存器內容 temp_l ds18b20_read_byte(); temp_h ds18b20_read_byte(); ds18b20_reset(); EA 1; // 恢復中斷 raw (temp_h 8) | temp_l; result (uint16_t)(raw * 0.0625 * 10); // 乘10保留一位小數 return result; }上拉電阻一定不能省DS18B20數據線上必須接4.7k歐姆上拉。我之前圖省事用過板載弱上拉結果是溫度偶爾正常偶爾顯示85度這個85度是DS18B20上電后的默認值說明芯片經常復位排查了半天才發現是上拉強度不夠。2.3 紅外傳感器接入與控制輸出紅外部分我選用的是HC-SR501熱釋電紅外模塊它檢測的是人體輻射的紅外線變化模塊上自帶菲涅爾透鏡和信號處理電路輸出直接就是TTL高電平單片機只需要讀GPIO狀態。這里有兩個使用細節要強調。第一HC-SR501上電后需要大約一分鐘預熱穩定期這段時間內輸出會頻繁誤觸發首次上電要在程序里做個初始化延時邏輯或者干脆在校驗階段忽略前60秒的數據。第二模塊背面有兩個可調電位器一個調感應距離大概3到7米一個調輸出延時大概5秒到5分鐘實際部署時要用小螺絲刀反復調到合適的位置不要指望軟件能完全彌補硬件的誤判。采集到紅外狀態后我的程序邏輯是檢測到有人且溫度超過設定閾值則置位繼電器開并觸發蜂鳴器報警同時把狀態打包發送給Android端。養成一個習慣控制輸出前加軟件延時去抖紅外信號持續20ms以上才認為是有效觸發這樣能過濾掉大部分脈寬很窄的干擾信號。2.4 串口數據幀協議給上下位機定好規矩下位機采集到的數據要發給Android端通信協議是我在整個項目里最看重的一部分。串口通信本質是面向字節流的上電后從第一個字節開始接收如果沒有協議約束接收端根本無法判斷一個溫度數據從哪里開始到哪里結束。我定義了一個簡單的幀結構所有字段都用單字節便于單片機處理。幀頭固定為0xAA 0x55設備地址用于將來擴展多設備幀類型0x01表示傳感器數據上行0x02表示控制指令下行后面跟數據長度和數據區最后一字節是前面所有字節累加和的低八位。字段字節數說明幀頭11固定0xAA幀頭21固定0x55設備地址10x01代表1號節點幀類型10x01上行數據0x02下行控制數據長度1數據區字節數數據區N按幀類型定義校驗和1前面所有字節累加取低8位溫度上行幀的數據區固定5字節0x01表示溫度數據類型隨后兩個字節是溫度整數部分和小數部分再一個字節是紅外狀態最后一個字節是繼電器當前狀態。這樣Android端解析邏輯就能寫得很簡單不需要考慮變長數據。校驗和必須加串口在無屏蔽環境很容易被電機、繼電器開關的瞬間干擾打亂數據位沒有校驗就沒法發現壞幀。3. Android上位機串口通信鏈路搭建與界面聯動3.1 通信方式選型USB串口還是藍牙Android端和下位機通信最常見的兩條路是USB轉串口和藍牙串口模塊。前者用OTG線直接連設備穩定可靠插上就通后者走HC-05之類的藍牙模塊免布線但配對和連接狀態管理會多出一堆邏輯。我最終選了USB串口方案因為項目里CC2530底板已經集成了CH340芯片直接用OTG線連接手機就能當串口用開發調試階段少一層無線干擾。如果你手頭的板子沒有USB轉串口或者你想做無線部署可以考慮把設備端換成藍牙模塊Android側用官方藍牙API做SPP連接整體架構不變只是把數據鏈路層換一下。需要注意一個前置條件手機必須有OTG功能Android系統版本建議6.0以上。CH340這類芯片不像標準USB免驅設備那樣系統默認識別需要在App里集成對應的串口驅動庫通過USB權限申請才能訪問。3.2 串口庫集成與打開串口的正確姿勢Android端做USB串口通信我直接用了一個非常好用的開源庫usb-serial-for-android它內部適配了FTDI、CP210x、CH34x以及標準CDC設備CH340這種國產芯片也在支持列表里省掉了很多自己寫驅動的痛苦。集成方式很簡單在Gradle里加一行依賴然后在Manifest里聲明USB設備廣播過濾和權限uses-feature android:nameandroid.hardware.usb.host / uses-permission android:nameandroid.permission.USB_PERMISSION_ATTACHED / activity android:name.MainActivity intent-filter action android:nameandroid.hardware.usb.action.USB_DEVICE_ATTACHED / /intent-filter meta-data android:nameandroid.hardware.usb.action.USB_DEVICE_ATTACHED android:resourcexml/device_filter / /activitydevice_filter.xml里指定廠商ID和產品IDCH340的vendorId通常是0x1A86。App檢測到設備插入后調用requestPermission彈窗申請權限拿到權限再open設備設置波特率115200、8數據位、1停止位、無校驗參數。這里有個經驗打開串口之前先檢查設備是否已被占用用兩個Thread同時讀一個串口設備是必然崩潰的。3.3 數據解析與界面刷新別卡主線程串口數據讀取必須放在后臺線程Android的主線程負責UI如果直接在UI線程做阻塞讀App秒變ANR。我的做法是開一個專門的工作線程用循環從串口讀取數據讀到的字節先放到一個緩沖區里解析出完整幀后再把結果通過Handler或者runOnUiThread發送到主線程刷新界面。粘包和半包是串口通信里的老問題下位機一次發送的數據可能被拆成多段到達也可能兩次數據連在一起到達。我的解析思路是把每次讀到的字節追加到緩沖區尾部然后循環查找幀頭0xAA 0x55找到后根據數據長度字段判斷幀是否完整完整就取幀校驗不完整就等待下一次讀數據。private void parseBuffer() { int index; while ((index findHeader(buffer)) 0) { if (buffer.size() - index 4) { break; // 幀頭不完整等待更多數據 } int len buffer.get(index 4) 0xFF; if (buffer.size() - index len 6) { byte[] frame copyFrame(index, len 6); if (checkSum(frame)) { handleFrame(frame); } removeProcessed(index, len 6); } else { break; // 幀數據不完整等待 } } }這種邊收邊解析的寫法比等滿一包再處理靠譜得多尤其在下位機高頻上報的場景緩沖區滿了也不怕頭部處理完的數據會及時清掉。界面刷新我用了最簡單的Handler機制溫度是一個大的TextView實時更新紅外狀態用一個圓形指示燈View切換紅綠顏色。讀取線程和UI線程之間只傳遞解析好的數據對象不要把原始字節直接拋給UI線程去解析。3.4 控制指令下發與反饋閉環Android端不僅收數據還要發控制指令。界面上放了一個ToggleButton控制繼電器開關點擊事件里組裝下行幀幀頭0xAA 0x55、設備地址0x01、幀類型0x02、數據長度0x01、數據區0x01或者0x00、最后計算校驗和通過串口write方法寫入。單純發送了不管是不夠的我在下位機收到控制指令后會立即給Android端回一幀狀態確認數據區帶上繼電器實際狀態的反饋。Android端在發送指令后的500毫秒內如果沒有收到對應反饋就提示用戶指令發送失敗讓你能區分是串口斷開了還是下位機沒執行。別小看這個閉環設計演示的時候它能幫你省掉大量到底發出去沒有的扯皮。4. 聯調階段的坑與排查經驗4.1 串口亂碼問題多半出在時鐘和電平第一次把CC2530接上Android App最常見的問題就是收到的數據全是亂碼。我先排除了Android側波特率配置錯誤然后才鎖定了CC2530的時鐘問題上面已經說過用內部RC振蕩器跑115200波特率誤差會大到直接亂碼。切換到外部晶振后問題立刻消失。另一個隱藏問題是電平不匹配。CC2530是3.3V供電它的串口IO輸出是3.3V電平大部分USB轉串口模塊兼容3.3V和5V兩檔。如果你的模塊跳線帽撥到了5V檔或者用了老式RS232電平電路數據位會被拉高到錯誤電平表現出來也是亂碼。建議直接用3.3V檔并確認連接線沒有交叉錯位CC2530的TX接對端RX別接成直通。4.2 數據錯幀、粘包協議容錯要提前做聯調中遇到過一個很典型的錯幀問題Android端偶爾顯示溫度變成80多度或者紅外狀態莫名其妙反轉。起初以為是傳感器壞了后來加日志才發現是幀同步出了問題。如果下位機上電瞬間發送了半個幀Android端從錯誤位置開始找幀頭運氣好能自動同步回來運氣不好會把錯位數據當有效數據處理。解決思路有兩層。上層是嚴格校驗校驗和不匹配的幀直接丟棄不丟棄也不能把里面的數據拿來刷新界面。底層是每幀加幀頭并且幀頭連續兩個字節單字節幀頭在數據區隨機出現0xAA時容易誤判雙幀頭加上長度和校驗能把誤判概率壓到極低。實測這個方案跑了三天再沒出現過一次錯幀展示。4.3 紅外傳感器誤報觸發邏輯和設備調參HC-SR501在空調出風口附近會頻繁誤報這個不是模塊壞了是熱釋電紅外對溫度擾動本身就很敏感。我的處理策略是軟件層面對相鄰兩次狀態變化做時間間隔判斷如果紅外信號在小于500毫秒內反復翻轉認定是抖動維持上一次狀態不更新。這類傳感器更適合做區域有人無人判斷不適合做精確的觸發計時產品設計時要有預期。另外提醒一點HC-SR501的塑料透鏡很容易積灰別用濕布擦輕輕用干布或者氣吹清理。室內項目用了半年多輸出狀態越來越不穩定最后發現就是透鏡臟了導致感應距離縮短清理后恢復正常。4.4 供電與穩定性一些容易忽略的硬件細節CC2530在繼電器吸合瞬間會有比較大的電流沖擊如果供電走的是劣質USB線電壓跌落會讓單片機直接復位溫度數據瞬間清零。我的解決辦法是在繼電器控制引腳加三極管驅動和續流二極管并且在下位機供電處并聯一個470uF電解電容加一個0.1uF瓷片電容把瞬態壓降吃掉。還有一個小細節每次繼電器切換指令發出后Android端看到的狀態反饋大約有幾十到一百毫秒的延遲這是正常的。不要在下位機程序里做發送完立即讀取傳感器這種動作傳感器模塊需要幾百微秒的穩定時間這個用條件編譯加一點空延時就能規避。整套系統穩定運行下來我對簡單項目這個詞有了新的理解鏈路越短越要把每一環的細節砸實任何一環松了整體就都松了。本文還有配套的精品資源點擊獲取