文件系統靈活用——智能家居DIY連載教程3

2021-02-24 RTThread物聯網作業系統

Hi,各位小夥伴,DIY 活動已經來到了第三周!前兩周的任務大家都完成了嗎?本周將會迎來新的挑戰——文件系統。本文將從 SPI Flash 和 SD Card 兩方面給大家講解如何使用文件系統,以及針對本次 DIY 做出的一些優化,會大大增強系統性能,一起來看看吧~

我們來看一下第三周的任務:

了解 RT-Thread 文件系統,在接收節點中使用文件系統,存放來自發送節點發送過來的數據。

上述任務比較單一,只是文件系統而已。不過,能巧妙靈活的把文件系統用好用對,可不是一件輕鬆的事情。

DFS 是 RT-Thread 提供的虛擬文件系統組件,全稱為 Device File System,即設備虛擬文件系統,文件系統的名稱使用類似 UNIX 文件、文件夾的風格。

RT-Thread DFS 組件的主要功能特點有:

為應用程式提供統一的 POSIX 文件和目錄操作接口:read、write、poll/select 等。

支持多種類型的文件系統,如 FatFS、RomFS、DevFS 等,並提供普通文件、設備文件、網絡文件描述符的管理。

支持多種類型的存儲設備,如 SD Card、SPI Flash、Nand Flash 等。

DFS 的層次架構如下圖所示,主要分為 POSIX 接口層、虛擬文件系統層和設備抽象層。如下圖:

DFS 的更多內容,請在 RT-Thread 文檔中心中查看:https://www.rt-thread.org/document/site/ (由於微信無法插入外部連結,請將以上連結複製至外部瀏覽器打開)
3.1 準備工作

以正點原子的潘多拉開發板 (Iot Board) 為例,教大家在 SPI Flash 上使用文件系統。

值得一提的是,RT-Thread 已經將 libc 那套文件系統接口對接到 DSF 上了,在 env 工具中開啟 libc 和 DFS 即可,本次教程使用 libc 的那套接口進行文件的打開/關閉、讀取/寫入。

menuconfig 中開啟 libc

1RT-Thread Components  --->
2    POSIX layer and C standard library  --->
3        [*] Enable libc APIs from toolchain

在 meunconfig 中開啟 DFS,本教程使用 elmfatfs 文件系統,需要將 elmfatfs 掛載到 RT-Thread 的 DFS 上,所以 elmfatfs 也要開啟:

1RT-Thread Components  --->
2    Device virtual file system  --->
3        [*] Using device virtual file system
4        [*]   Enable elm-chan fatfs

當然,不要忘記在 meunconfig 中開啟 SPI Flash

1Hardware Drivers Config  --->
2    Onboard Peripheral Drivers  --->
3        [*] Enable QSPI FLASH (W25Q128 qspi1)

潘多拉開發板上的 SPI Flash 使用的是 QSPI 接口,還需要在 meunconfig 中把 QSPI 接口開啟:

1Hardware Drivers Config  --->
2    On-chip Peripheral Drivers  --->
3        [*] Enable QSPI BUS

退出 menuconfig 後需要輸入 「scons --target=mdk5」 更新工程。

3.2 文件系統的掛載

本次 DIY 使用的文件系統是 elmfatfs,elmfatfs 需要在塊設備上才能進行文件操作。潘多拉板子上的 SPI Flash 是 W25Q128,我們需要將 W25Q128 註冊成塊設備,才能使用 elmfatfs 進行文件操作。如下示例代碼:

1static int rt_hw_qspi_flash_with_sfud_init(void)
2{
3    stm32_qspi_bus_attach_device("qspi1", "qspi10", RT_NULL, 4, w25qxx_enter_qspi_mode, RT_NULL);
4    if (RT_NULL == rt_sfud_flash_probe("W25Q128", "qspi10"))
5        return -RT_ERROR;
6    return RT_EOK;
7}
8INIT_COMPONENT_EXPORT(rt_hw_qspi_flash_with_sfud_init);

在 FinSH 中輸入 「list_decive」,即可看到 W25Q128 註冊成了塊設備了,並掛載在 QSPI 上:

W25Q128 註冊成了塊設備後,就能將 elmfatfs  這個文件系統掛載到 RT-Thread 的 DFS 上了,如下示例代碼:

1dfs_mount("W25Q128", "/", "elm", 0, 0)


3.3 文件操作

到此為止,我們就可以使用 libc 的接口進行文件操作了,將接收到的數據以文件方式存放到 W25Q128 裡面去,舉個簡單的例子,如下示例代碼:

1FILE *recvdata_p0;
2recvdata_p0 = fopen("recvdata_p0.csv", "a+");
3if (recvdata_p0 != RT_NULL)
4{
5    fputs((char *)RxBuf_P0, recvdata_p0);
6    fputs("\n", recvdata_p0);
7    fclose(recvdata_p0);
8}

在 Finsh 中輸入 「ls」 可以查看當前文件系統中的文件目錄,如下圖:

輸入 「cat XXX」 可以查看文件內容,如下圖:

簡單的幾步就可以進行文件操作了,RT-Thread 的文件系統還是相當易用的。

4.1 準備工作

以正點原子的潘多拉開發板 (Iot Board) 為例,教大家在 SD Card 上使用文件系統。

和上面的 SPI Flash 一樣,在 menuconfig 中開啟相關選項:SD Card,SPI(潘多拉板子的 SD 卡是用 SPI 驅動的而不是 SDIO),libc,DFS,elmfatfs。

4.2 文件系統的掛載

與 SPI Flash 一樣,需要將 SD Card 註冊成塊設備,才能掛載文件系統。如下示例代碼:

1static int rt_hw_spi1_tfcard(void)
2{
3    __HAL_RCC_GPIOC_CLK_ENABLE();
4    rt_hw_spi_device_attach("spi1", "spi10", GPIOC, GPIO_PIN_3);
5    return msd_init("sd0", "spi10");
6}
7INIT_DEVICE_EXPORT(rt_hw_spi1_tfcard);

在 FinSH 中輸入 「list_decive」,即可看到 SD Card 註冊成了塊設備了,並掛載在 SPI 上:

SD Card 註冊成了塊設備後,就能將 elmfatfs  這個文件系統掛載到 RT-Thread 的 DFS 上了,如下示例代碼:

1dfs_mount("sd0", "/", "elm", 0, 0)

需要注意的是,如果大家手頭的板子是使用 SDIO 接口來驅動 SD Card 的,那麼將 SD Card 註冊成塊設備將不用我們操心,RT-Thread 源碼中的 「...rt-thread\components\drivers\sdio\block_dev.c」 文件中,會將 SD Card 註冊成塊設備的。當然,文件系統的掛載還是需要我們手動敲代碼去實現的。4.3 文件操作

與 SPI Flash 一樣,可以直接使用 libc 的接口進行文件操作,如下示例代碼:

1FILE *recvdata_p0;
2recvdata_p0 = fopen("recvdata_p0.csv", "a+");
3if (recvdata_p0 != RT_NULL)
4{
5    fputs((char *)RxBuf_P0, recvdata_p0);
6    fputs("\n", recvdata_p0);
7    fclose(recvdata_p0);
8}

5.1 優化1

將文件系統用起來,進行文件操作,是一件相對比較容易的事情。不過當將文件系統運用到實際項目中的時候,往往會因為一些需求或者說是其他因素,導致事情不那麼好辦。就拿這個 DIY 來說,如果就像上面的示例代碼這麼用文件系統,雖然系統能正常工作,但是會帶來一些問題:

眾所周知,文件的操作是需要佔用大量時間和資源的,通俗來說就是慢,像文件的讀,寫,打開,創建等,都是比較慢的。如果發送節點發數據過來,接收節點每收到一條數據,就用文件系統記錄這個數據,這樣會導致系統性能下降。如何保證減少文件操作次數,提高系統性能,又能保證每條數據都不丟失呢?

這裡使用 ringbuffer 來避免這個問題。

ringbuffer 是一種先進先出的 FIFO 環形緩衝區,DIY 的接收節點工程中,我們創建了兩個線程去工作,一個是 nrf24l01_thread 線程,用於接收來自發送節點的數據,另一個是 DFS_thread 線程,用於利用文件系統保存數據的。並且創建一個 4KB 大小的一個 ringbuffer

1static struct rt_ringbuffer *recvdatabuf;
2recvdatabuf = rt_ringbuffer_create(4069); 

每當 nrf24l01_thread 線程接收到一條數據,就存放到 ringbuffer 中去:

1rt_ringbuffer_put(recvdatabuf, (rt_uint8_t *)str_data, strlen(str_data));

在DFS_thread 線程中,我們設置一個 ringbuffer 的閾值,這裡我將閾值設置成了 ringbuffer 大小的一半,當寫入的數據達到了 ringbuffer 的閾值之後,就將 ringbuffer 中所有的數據統統寫入文件中去:

1
2if (rt_ringbuffer_data_len(recvdatabuf) > (4096 / 2))
3{
4    
5    recvdatafile_p0 = fopen("recvdata_p0.csv", "ab+");
6    if (recvdatafile_p0 != RT_NULL)
7    {
8        while(rt_ringbuffer_data_len(recvdatabuf))
9        {
10            size = rt_ringbuffer_get(recvdatabuf, (rt_uint8_t *)writebuffer, (4096 / 2));
11            fwrite(writebuffer, 1, size, recvdatafile_p0);
12        }
13        fclose(recvdatafile_p0);
14    }
15}

這麼做,就可以儘可能的減少了文件的操作,提高了系統的性能,同時又保證每一條數據都不會丟失。

5.2 優化2

但是,還有一個問題:

如果發送節點很久很久才發數據過來,或者說是接收節點很久很久才收到數據,那麼 ringbuffer 要很久很久才能到閾值。如果這時候,已經寫了整整一天的數據進 ringbuffer 中了,只差一點點就要到閾值了,很快就可以將數據寫入到文件中去了,這時候偏偏斷電了!整整一天的數據白白丟失了,心痛嗎?

當然,掉電丟數據這種情況是不可以避免的,但是我們可以通過一些算法優化(姑且叫它算法吧),儘可能的減少丟失數據的可能。

解決思路是:定個固定時間,計時,如果時間一到,此時數據還沒寫滿 ringbuffer 的閾值,這時候就不管數據到沒到閾值了,直接將 ringbuffer 裡的數據全部寫入文件中去。要實現這個思路需要搭配事件集 (event) 使用。

在 nrf24l01_thread 線程中,每收到一個數據,就發送一個事件:

1while (1)
2{
3    if (!rx_pipe_num_choose())
4    {
5        
6        if(sscanf((char *)RxBuf_P0, "%d,+%f", &buf.timestamp, &buf.temperature) != 2)
7        {
8            
9            if(sscanf((char *)RxBuf_P0, "%d,-%f", &buf.timestamp, &buf.temperature) != 2)
10            {
11                continue;
12            }
13            buf.temperature = -buf.temperature;
14        }
15        sprintf(str_data, "%d,%f\n", buf.timestamp, buf.temperature);
16        
17        rt_ringbuffer_put(recvdatabuf, (rt_uint8_t *)str_data, strlen(str_data));
18        
19        rt_event_send(recvdata_event, WRITE_EVENT);
20    }
21    rt_thread_mdelay(30);
22}

在DFS_thread 線程中,通過接收兩次事件,並設置接收事件的超時時間,達到計時的目的:

1\while (1)
2{
3    
4    if (rt_event_recv(recvdata_event, WRITE_EVENT, RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, RT_WAITING_FOREVER, &set) != RT_EOK)
5        continue;
6    do
7    {
8        
9        if (rt_event_recv(recvdata_event, WRITE_EVENT, RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, rt_tick_from_millisecond(1000), &set) == RT_EOK)
10        {
11            
12            if (rt_ringbuffer_data_len(recvdatabuf) > THRESHOLD)
13            {
14                
15                recvdatafile_p0 = fopen("recvdata_p0.csv", "ab+");
16                if (recvdatafile_p0 != RT_NULL)
17                {
18                    while(rt_ringbuffer_data_len(recvdatabuf))
19                    {
20                        size = rt_ringbuffer_get(recvdatabuf, (rt_uint8_t *)writebuffer, THRESHOLD);
21                        fwrite(writebuffer, 1, size, recvdatafile_p0);
22                    }
23                    fclose(recvdatafile_p0);
24                }
25            }
26            
27            continue;
28        }
29        
30        recvdatafile_p0 = fopen("recvdata_p0.csv", "ab+");
31        if (recvdatafile_p0 != RT_NULL)
32        {
33            while(rt_ringbuffer_data_len(recvdatabuf))
34            {
35                size = rt_ringbuffer_get(recvdatabuf, (rt_uint8_t *)writebuffer, THRESHOLD);
36                fwrite(writebuffer, 1, size, recvdatafile_p0);
37            }
38            fclose(recvdatafile_p0);
39        }
40    } while(0);
41}

這樣就盡最大力度的解決了掉電丟失數據的可能了。當然,第二次接收事件的超時時間可以根據自己需求設定長短。
為了更進一步便於大家學習,第三周任務的代碼已經開源啦~項目地址:https://github.com/willianchanlovegithub/DIY_projects_base_on_RT-Thread(由於微信無法插入外部連結,請將以上連結複製至外部瀏覽器打開)

第三周的源碼中,只上傳了兩個 demo 工程,均是本次 DIY 中接收節點的代碼

發送節點的代碼,在第二周的 demo 工程中有,這裡不再重複上傳相同 demo 工程

SPI Flash 的 sector 大小為 4096 字節,需要在 menuconfig 中修改:

SD Card 的 sector 大小為 512 字節,需要在 menuconfig 中修改:

 

#題外話# 喜歡RT-Thread不要忘了在GitHub上留下你的STAR哦,你的star對我們來說非常重要!連結地址:https://github.com/RT-Thread/rt-thread

你可以添加微信18917005679為好友,註明:公司+姓名,拉進 RT-Thread 官方微信交流群

RT-Thread


讓物聯網終端的開發變得簡單、快速,晶片的價值得到最大化發揮。Apache2.0協議,可免費在商業產品中使用,不需要公布源碼,無潛在商業風險。

點擊閱讀原文進入GitHub

相關焦點

  • 智能家居系統中家用安防監控系統解密
    3.網絡存儲圖像 家庭無線視頻監控系統能夠通過網絡保存監控視頻。在無警或撤防狀態下,可按設定時間間隔定時在網絡硬碟上保存監控場所視頻;在發生報警情況下能連續在網絡硬碟上保存圖像.直到解除報警為止。用戶隨時可以通過客戶端軟體觀看監控錄像回放。
  • 智能家居系統構建實例之家庭wifi組建
    電力貓即「電力線通訊數據機」,是通過電力線進行寬帶上網的Modem的俗稱。使用家庭或辦公室現有電力線和插座組建成網絡,來傳輸數據。它具有即插即用的特點,能通過普通家庭電力線傳輸網絡信號。但是目前電力貓的穩定性及速度受到電力線的限制,工作起來並不穩定,一般情況下僅用於應急使用,不建議正常情況下用它進行組網。wifi構建的基本方法1、單個路由僅使用一臺無線路由器。
  • WIFI作品DIY教程07-《wifi家居網關》家居控制中心zigbee/nrf24l01等
    》,那麼我們只要在完成:WIFI作品DIY教程05-《家居伺服器》web server(php+mysql+uhttpd)WIFI作品DIY教程06-《openwrt後臺程序》控制mysql、串口通信等再編寫arduino+nrf24l01(或zigbee或315或433或藍牙等無線模塊),那麼家居控制中心就被我們用最低的成本搭建起來。
  • 製作一個簡單的DIY Arduino智能家居系統
    描述在這個Arduino IoT項目中,我將向您展示如何製作一個簡單的DIY Arduino智能家居系統,該系統可以控制電器,例如燈,風扇,門等,從世界各地使用我們的手機。您需要的是具有網絡連接的Arduino開發板,一些繼電器和android手機。讓我們開始吧。
  • 用手機自己做DIY教程(3):修改圖片和壓縮包
    接上篇文章:用手機自己做DIY教程二:視頻轉圖片今天更新自己製作DIY時,怎麼修改圖片和壓縮包:我們將GIF
  • 如何基於 Android Things 構建一個智能家居系統?
    我們用 mount 命令查看開發板的分區,發現總共掛載了 5 個分區,其中 gapps、 _type、oem、data 主要是 Android Things 使用 PRIBOOT 用於樹莓派 3 啟動時的一些運行和配置文件的分區。用 ADB Shell 命令進入終端之後,我們可以看到,整個根文件系統與 Android 的根文件系統沒有太大的區別。
  • 【案例】木道家居的整廠智能物流系統
    在智能製造和智能物流快速發展的趨勢下,木道家居努力開展自動化升級,尤其是在新建的智能化家居工廠內,通過智能物流系統的規劃建設,使其生產和物流能力得到大大提升。由江蘇高科為木道家居承建的整廠智能物流系統項目,使智能化、信息化生產在家居行業得到充分展現,從單一的智能存儲功能向涵蓋物料搬運、區域配送、自動拆垛、自動存儲為一體的智能化物流系統延伸,是「工業4.0」在木工家居行業的典型應用。
  • 智能家居遠程集控系統方案思考
    本文完成了智能家居監控系統的整體設計,採用硬體結合,軟體設計和系統整體調試。根據開發的需要,各部分功能協調,交叉的進行,實現一種高效穩定的物聯網智能家居監控系統的解決方案。1.1 研究意義及背景隨著網絡技術和各種智能設備技術的不斷發展,智能家居也與隨著時代的變化而不斷發展進步,尤其是萬物互聯使得智能家居進入千家萬戶成為可能。
  • 我正在用的智能家居系統
    此文最早寫在兩年前,那時候我新換上一套智能家居系統,也算是開始了一些小折騰。
  • 巧用樹莓派 手把手教你DIY一個智能家居伺服器!
    樹莓派(Raspberry Pi)是大家熟知的迷你計算機系統,看上去像一塊電路板,但具有完整的硬體結構,價格非常便宜。通過樹莓派,可以實現很多低成本的自動化應用形式,比如連接傳感器昨晚門窗警報器、或是把一臺傳統音箱無線化,通過軟體也可實現統一的方便管理,實現基本的智能家居體驗。樹莓派的作業系統 Raspbian 的開發者是麥克·湯姆森和彼得·格林。
  • 智能家居不再DIY:2019年是整合期?
    隨著越來越多的消費者採用智能家居,供應商希望創建一個為消費者提供一站式服務的生態系統。因為將會更加有利於智能家居的普及,吸引更多的用戶,通過整合將會形成「一些清晰的生態系統」,這將有助於擴大智能家居設備的滲透率,提升「新的市場進入者,如保險公司和房地產投資者/建築商等等「。
  • 英特視界 | InCare智能系統APP使用教程
    智能照明系統是智能家居的重要組成部分,是終端用戶體驗家居智能化的首選途徑。
  • 全民智能時代,智能家居產業鏈全解析
    - 2015年3月 【海爾、蘇寧】雙方全面升級戰略合作,在智能家居領域展開深度合作,共同推薦C2B 反向定製產品,U+平臺創客產品、PPTV電視開發計劃,共享會員資源。- 2015年4月 【聯想、百度】合作推出雲路由,後續將合作更多智能硬體。- 2015年4月 【TCL、騰訊】TCL與騰訊、未來電視達成平臺、內容、牌照三方資源整合。
  • 教你用樹莓派 + IFTTT 實現 HomeKit 智能家居自動化(二)
    概念HomeKit:蘋果於 2015 年 5 月發布的智能家居平臺。HomeBridge:一個開源程序,用於讓 HomeKit 支持一些原生不支持 HomeKit 的設備(如米家家居)。HomeAssistant:一個第三方的智能家居平臺。0.2. 關係
  • 引領智能家居新浪潮 走進雲圖全場景智能家居數字照明
    作為全場景智能家居的重要組成,雲圖發布"家天下"全新數字照明品牌,旗下的數字照明系統,據說刷新了用戶對家庭燈光照明的認知,極具顛覆性和革新性。為了一探究竟,本人來到雲圖全場景智能家居體驗展廳,親身感受並全方位深入了解了一下雲圖數字照明系統。
  • 智能家居燈光控制系統的智能開關的接線方法
    為了讓開關與開關間組成一個網絡,就需要用信號線把所有的開關連接起來。信號線屬於弱電線,同樣需要開槽布管等操作,需遵守弱電布線原則。以最流行的485通訊協議的智能開關布線來說,其實布線非常簡單,只要用信號線把各個開關底盒串接起來就可以了,連接方式非常靈活,如圖一。
  • 享受智能生活,網友教你如何DIY一套完整的智能家居
    2、BroadLink 的各種智能組件,目前在用的有一個智能遙控RM-pro、一個環境控制器e-Air、一個大功率智能插座SP2、一個安防套件S1(帶一套門磁和一套紅外)、智能插座SPmini若干。硬體如下
  • Google Nest引爆智能家居 那麼國內家居哪家強?
    2014/07/03 極路由獲聯發科、KPCB千萬美元融資,將布局智能家居和穿戴設備平臺。2014/07/16 傳三星正以2億美金的價格洽購智能家居平臺公司SmartThings。從平臺到入口乃至攝像頭之類,智能家居的每個環節都是投資熱點,即使再麻木的人也足以被明示,這個行業被重度看好。Google 以33億美元之巨收購了小小恆溫器製造商Nest,它在下一個多大的棋?Nest是其整合全球的利器,而Google從智能家居的數據開始。
  • 智能家居入門(3)—— HomeAssistant 安裝
    Home Assistant 簡介HomeAssistant[1]是一款基於 Python 的智能家居開源系統,支持眾多品牌的智能家居設備
  • 谷歌用「垃圾桶」玩轉智能家居
    一個路由器的誕生———————————————————————————————谷歌公司於在當地時間周二推出了一款WiFi路由器OnHub,這貨一推出就收到了廣泛的關注,因為它承載了智能設備未來的重要的概念「智能家居」。