Vol.921 27.Jun.2025

電動エアーダスター プチFATでデータ書込

A 電動エアーダスター&ハンディクリーナー(コードレス)

by fjk

 電動工具の使用後の清掃に、その都度コンプレサーのあるところに行って、エアーダスターで埃を吹き飛ばしていた。そこで、どこでも使えるものがないか、ネットで調べていると「1台で吹き飛ばしや吸引ができるコードレス充電式エアダスターF72」(5,989円、Amazon[79%引?])を見つけ、ついポチり。
 購入したダスターは、「吸引モード」でホコリを取り、「隙間掃除モード」で届きにくい場所も快適に、「真空圧縮モード」で衣類をコンパクトに収納し、「エアブロー」で粉塵を一掃、「空気注入」でエアマットや浮き輪も素早く膨らませれる5つのモードがあり、車の洗車時に水滴の吹き飛ばしもできるとある。
 使ってみると、工具のブロワーとしては風速が少し弱いが何とか使える。車の水滴は確かに飛ばすことができたが、車全体の水滴を吹き飛ばすのは・・・。水滴が溜まる所やガラス窓の水滴飛ばしには有効。

【主な仕様】
1.モーター:
最大180,000RPMの高速モーター
2.風 速:
最大75m/s(調整は無段階、吸引は最大15,000Pa)
3.電 池:
10000mAh(最長150分、充電はUSB Type-C)
4.重 量:
280g(サイズは、9×14cm)
5.付属品:
ノズル5種、USB充電ケーブル

梱包状態(本体+パーツ、USBケーブル)

吸引モード(+フィルター、ノズル)

ブロワーモード(ノズルは2種類)

車屋根の水滴を吹き飛ばし中


P PICでSDカードを使う(8)  〜プチFATでSDにデータ書込

by fjk

 abc920まで、SDカードからデータを読み出すことができたが、プチFATは書込に制限があった。そこで、FAT管理データを読み出せるテストプログラムを作成し、書き込みを行うとどうなるかを調べた。
 そして、SDカード(FAT32、32Gb以下)上の既存ファイルにテキストデータの書込(書き換え)ができる方法を確認した。

【ハード】
abc919の回路をそのまま利用した。
 
【ソフト】
事前に256〜2048バイトのテキストファイル(zip)等を準備し、SDカードに書き込んだ。
・printf()等を利用するので、Redirect_STDIO_to_USARTにチェック。
・ppfconf.hの設定は以下の通り

/*---------------------------------------------------------------------------/
/ Function Configurations (0:Disable, 1:Enable)
/---------------------------------------------------------------------------*/

#define	PF_USE_READ    1   /* pf_read() function */
#define	PF_USE_DIR     1   /* pf_opendir() and pf_readdir() function */
#define	PF_USE_LSEEK   1   /* pf_lseek() function */
#define	PF_USE_WRITE   1   /* pf_write() function */

#define PF_FS_FAT12    0   /* FAT12 */
#define PF_FS_FAT16    1   /* FAT16 */
#define PF_FS_FAT32    1   /* FAT32 */


 
1.プチFATの動作検証
 テスト用に以下のコマンドを追加(全て小文字)
m
SDマウント
o
ファイルのオープン(Fコマンドで指定された)
r
データ読出し(オフセット場所から)
i
SD情報表示
s ofs
SDシーク、ofs:オフセット[10進数]
w str
データ書込、str:書き込むテキストデータ
 −− その他のコマンド (大文字)−−
R[filname]
txtファイルを読み出し・表示
F[filname]
ファイル名filnamをセット

iコマンド表示例
 ・Rコマンド以外を実行するには、事前にSD_Open(またはRコマンド)を一度は実行しておくこと。
 ・Rコマンドは常にofs =0となるので、's'コマンドでシーク後は、'r'コマンドを使用すること。
 

s、rコマンドで、読込・シーク例
 

書込テスト回路

wコマンドで書込例(上:書込前、下:書込後)
(最初のセクタデータが「*****」だけになっている
 データは先頭に書込まれ、以後0で埋められている
 ファイルサイズは変わらず、1024バイトのまま)
2.SDカードへの文字列データの書込
前項で調べた結果を踏まえ、以下の手順でSDカードへの書込を行うことにした。
  @ 512バイトのセクター単位でバッファーに読込む
  A バッファーの一部を変更したい文字列に置き換える
  B 再び同じセクターに512バイトを書き込む
なお、文字列の書込(変更)がセクター境界にまたがる場合にも対応できるようにした。
 
[ 追加したSD書込コマンド](Wは大文字)
W[ofs,]str
文字列strを書込、ofs:書込開始位置
ofs指定無しは、前書込終了位置から続けて書込)

512バイトのセクター内での書込例
(指定位置にチャンと書き込まれている
そして、他のデータには影響なし)
 
 
 
 
 
 
セクターをまたがる書込例  →→→→
(512バイトの2つのセクターにまたがり
 チャンとデータが書き込まれている) 

【おまけ】
 SDカードの(ディレクトリー)ファイルリストを表示するDコマンドを追加した。
 (サブディレクトリのファイルをフルパスで指定できるが、30文字を超えないこと)
D[path]
リスト表示、path:ディレクトリ名
(path無しは'/' [root]と同じ)
<注意>
なお、pff.cのpf_readdir()で、以下の(赤字の)所をコメントに
              ・・・
1161    res = dir_read(dj, dir);	/* Get current directory item */
1162 //   if (res == FR_NO_FILE) res = FR_OK;
1163    if (res == FR_OK) {	
              ・・・   

ファイルリスト例

【abc921-18326.c】 abc921-18326.c + myFunction.c+myProject.h(zip)


/*****************************(abc921-18326.c)*******
 *   Test Petit FatFS
 ****************************************************/
#include "mcc_generated_files/mcc.h"
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "pff.h"
#include "myProject.h"

#define SD_BFSIZE      512
#define PATH_SIZE       32

enum { NON = 0, TXT, CSV, BIN = 8, BMP };

static char     Dlm[] = ",";                // 文字列区切りデリミタ

//--- タイマー・汎用変数
char     Msg[40];                           // コメント等送信用文字列
uint8_t  TFlg;                              // タイマーフラグ

//---- SDカード用変数
FATFS  	Fs;                                 // ファイルシステム変数
FILINFO Fno;                                // ファイルシステム情報
DIR     Dir;                                // ディレクトリー情報
BYTE   	Buf[SD_BFSIZE + 1];                 // ファイルデータ用バッファー
UINT    Br;                                 // 送受信完了データ数
char    FilNam[PATH_SIZE] = "TEST00.TXT";   // ファイル名(8.3))
uint8_t F_ext = TXT;                        // 1:TXT, 2:CSV, 8:BIN, 9:BMP
DWORD   Wof;                                // 書込オフセット

//---- EUSART用変数
char     RBuf[EU_BFSIZE];                   // シリアル受信バッファー
uint8_t  SFlg;                              // シリアル受信フラグ

/***************************************
 *  タイマ0 Callback関数 (1秒周期割り込み)
 ***************************************/
void TMR0_Process(void){
    TFlg = 1;                               // タイマーフラグセット
}

/***************************************
 *  SDカード用汎用関数
 ***************************************/
//--- SDカードのマウント
FRESULT SD_Mount(void) {
    uint8_t i = 0;
    FRESULT res;
    
    do{
        res = pf_mount(&Fs);
        i++;
        EUSART_Write('.');
    }while((res != FR_OK) && (i < 10));     // 10回まで試みる

    if(res != FR_OK){                       // マウント失敗なら
        EU_Puts("** SD Not Mounted !!");
    }else{
        EU_Puts("SD Mount OK!");
    }
    return res;	
}

//--- ファイルのオープン(SDアクセステスト用)
FRESULT SD_Open(void){
    FRESULT res;
    
    res = SD_Mount();
    if(res != FR_OK) return res;
    res = pf_open(FilNam);
    if(res != FR_OK){                       // オープン失敗なら
        EU_Puts("** SD Not Open !!");
    }else{
        EU_Puts("File Open OK!");
    }
    return res;
}

//---  データの読み出し(SDアクセステスト用)
FRESULT SD_Read(void){
    FRESULT res;
    
    res = pf_read(Buf,SD_BFSIZE,&Br);
    if(res != FR_OK){                       // 読出し失敗なら
        EU_Puts("** File Not Read !!");
    }else{
        Buf[Br] = 0;
        EU_Puts((char *)Buf);
        my_itoa(Msg,Br);
        EU_Puts(Msg);
    }
    return res;
}

//--- ファイル名をセットするコマンド   F[filename]
/*    *str:指定するファイル名
 *    (strが無い場合、現在ファイル名を表示)   */
void SD_cmd_F(char *str) {
    size_t i,n;
    char   *p;
    
    n = strlen(str);
    if(n > 1){
        if(n > PATH_SIZE) n = PATH_SIZE - 1;
        for(i = 0; i < n; i++)
            FilNam[i] = (char)toupper(str[i+1]);
        FilNam[n]=0;
    } 
    p = strchr(FilNam,'.');
    if     (strncmp(p+1,"TXT",3)==0) F_ext = TXT;
    else if(strncmp(p+1,"CSV",3)==0) F_ext = CSV;
    else if(strncmp(p+1,"BIN",3)==0) F_ext = BIN;
    else if(strncmp(p+1,"BMP",3)==0) F_ext = BMP;
    else                             F_ext = NON;
    sprintf(Msg,"NewFileName = %s: %d\n",FilNam,F_ext);
    EU_Puts(Msg);
}

//--- ファイルシステム(FATFS)データの表示
void SD_Info(void){
    printf("FAT_Type = %d\n", Fs.fs_type);    // FATタイプ
    printf("FS_Flag  = %d\n", Fs.flag);       // File フラグ
    printf("Cls_Size = %d\n", Fs.csize);      // セクター数
    printf("Pad1     = %d\n", Fs.pad1);       // (BYTE)
    printf("Root_Dir = %d\n", Fs.n_rootdir);  // Rootエントリ
    printf("Cls_Num  = %ld\n",Fs.n_fatent);   // FATエントリ
    printf("FAT_Sec  = %ld\n",Fs.fatbase);    // startセクタ
    printf("Root_Sec = %ld\n",Fs.dirbase);    // Rootセクタ
    printf("Data_Sec = %ld\n",Fs.database);   // Dataセクタ
    printf("F_Point  = %ld\n",Fs.fptr);       // Fileポインタ
    printf("F_Size   = %ld\n",Fs.fsize);      // Fileサイズ
    printf("F_Clst   = %ld\n",Fs.org_clust);  // Fileクラスタ
    printf("F_Cr_Cls = %ld\n",Fs.curr_clust); // 現在クラスタ
    printf("F_Cr_Sec = %ld\n",Fs.dsect);      // 現在セクタ
}

//--- SDカードから読込コマンド    R[filename.TXT]
/*    strファイルから読み出す(TXTファイルのみ)
 *    (strが無い場合は現在ファイルから読み出す)   */
void SD_cmd_R(char *str){
    FRESULT res;
    uint16_t sz = 0;
    
    if(strlen(str)>1)
        SD_cmd_F(str);    
    if(F_ext != 1){
        EU_Puts("Not Text File!");
        return;
    }
    EU_Puts("Start Command R");
    if(SD_Mount() != FR_OK) return;
    res = pf_open(FilNam);                  // ファイルオープン
    if(res == FR_OK){
        sprintf(Msg,"Open %s",FilNam);
        EU_Puts(Msg);
        do{
            pf_read(Buf, SD_BFSIZE, &Br);   // SDから読込む
            Buf[Br] = 0;
            EU_Puts((char *)Buf);
            sz += Br;
        }while(Br!=0);
    }else{
        sprintf(Msg,"** Can't open %s !!",FilNam);
        EU_Puts(Msg);
    }
    sprintf(Msg,"Size = %d",sz);
    EU_Puts(Msg);
}

//---  文字列s1のn1番目からs2文字列のn2バイト分を置き換える
void strNRepl(char *s1, uint16_t p1, char *s2, uint16_t n2){
    uint16_t n1;                            // 置換される文字列の文字数
    uint16_t i;                             // ワーク用変数
    char     *p;                            // ワーク用ポインタ

    if(n2 == 0) n2 = strlen(s2);            // 置換文字数が0なら全文字
    n1 = strlen(s1);
    if(p1 + n2 > n1) n2 = n1 - p1;
    p = s1 + p1;
    for(i = 0; i < n2; i++){
        *p++ = *s2++;
    }
}

//---   セクター単位でSDカードから読込・変更・カードに書込
UINT Sec_Write(DWORD ofs, uint16_t sof, char *str, uint16_t bt){
    FRESULT  res;
    UINT     rw;                            // 読込バイト数
    UINT     bw;                            // 書込バイト数
    DWORD  cr_of;
    
    cr_of = ofs & 0xFFFFFE00;               // オフセットはセクタ境界
    res = pf_lseek(cr_of);                  // セクター境界にシーク
//    if(res != FR_OK) return 0;            // エラーチェック
    res = pf_read(Buf, 512, &rw);           // セクター単位で読出す
//    if(res != FR_OK) return 0;            // エラーチェック
    if(sof + bt > 512) bt = 512 - sof;      // セクター境界まで置換
    if(sof + bt > rw)  bt = rw - sof;       // 読込データ数まで置換
    strNRepl((char *)Buf,sof,str,bt);       // 文字列の置換
    res = pf_lseek(cr_of);                  // セクター境界に再シーク
//    if(res != FR_OK) return 0;            // エラーチェック
    res = pf_write(Buf,rw,&bw);             // セクターデータを書込
//    if(res != FR_OK) return 0;            // エラーチェック
    res = pf_write(0,0,&bw);                // ファイナライズ
    return bt;
}

///--- SDカードにデータを書き込む
UINT SD_WriteP(DWORD ofs, char *str){
    UINT    sof;                            // セクター内オフセット
    UINT    rmb;                            // 文字列の残りバイト数
    UINT    btw;                            // 書込済み総バイト数
    UINT    bw;                             // 書込済みバイト数
    UINT    sln;                            // 書込文字列のサイズ
    char    *pts;                           // 書込文字列ポインタ

    sof = ofs % 512;                        // 最初セクタ内の書込位置
    sln = strlen(str);
//    printf("ofs=%ld, sof=%d, rmb=%d\n",ofs,sof,sln);
    //--- 書き出し開始
    btw = Sec_Write(ofs,sof,str,sln);       // 最初のセクターに書き込む
    if(sof + sln > 512){                    // セクター境界を超える?
        pts = str + btw;                    // 文字列ポインタを進める
        rmb = sln - btw;                    // 文字列の残りバイト
        ofs = (ofs + 512) & 0xFFFFFE00;     // 次セクターの先頭に
//        printf("ofs=%ld, rmb= %d, btw=%d\n",ofs,rmb,btw);
        while((sln > btw)&(ofs < Fs.fsize)){
            bw = Sec_Write(ofs,0,pts,rmb);  // 次セクターに書込
            btw += bw;                      // 書込済み文字数を加える
            ofs += 512;                     // 次セクターの先頭に
            pts += bw;                      // 文字列のポインタを進める
            rmb -= bw;                      // 文字列の残りバイトを減らす
//            printf("ofs=%ld, sln= %d, btw= %d\n",ofs,sln,btw);
        };                                  // 文字列が残っているなら継続
    }
    return btw;                             // 書込済み文字数を返す
}

//--- SDカードへの書込コマンド     W[ofs,]str
void SD_cmd_W(char *str){
    char    *pts;                           // 文字列ポインタ
    
    if(strlen(str)==1) return;              // 'W'のみなら
    if(strchr(str,',')){                    // コンマがあれば?ofs有り
        pts = strtok(str+1,Dlm);            // コマンドの次文字から
        if(pts != NULL){                    // オフセットデータがある
            Wof = (DWORD)atol(pts);         // オフセット値取得
        }
        pts = strtok(NULL,Dlm);             // 書込文字列指定
    }else{
        pts = str + 1;                      // 書込文字列指定
    }
//    printf("Wof = %ld, %s\n",Wof,pts);
    if(pts != NULL){                        // 文字列データ有り
        SD_WriteP(Wof,pts);
        Wof += strlen(pts);
    }
}

//--- SDカードにデータを書き込む
void Tst_Write(char *str){
    FRESULT res;
    size_t btw;
    UINT   bw;
    
    btw = strlen(str+1);
    if(btw){
        res = pf_write(str+1,btw,&bw);
        res = pf_write(0,0,&bw);
        printf("Write_SD: %s\n",str+1);
    }else{
        res = pf_write("++XXXX++",8,&bw);
        res = pf_write(0,0,&bw);
        puts("Write_SD: ++XXXX++");
    }
}

//--- 読み書きポインター移動
void Tst_Seek(char *str){
    FRESULT res;
    DWORD   ofs;
    
    if(strlen(str)==1){
        puts("Seek Error!");
        return;
    }
    ofs = (DWORD)atol(str+1);
    res = pf_lseek(ofs);
    Wof = ofs;
    if(res) puts("Seek Error!");
    else    printf("Seek at %ld\n",ofs);
}

//----  ディレクトリーデータ表示
void Prt_Dir(void){
    if(AM_SYS & Fno.fattrib)
      printf("%-13s  -<sys>-  %2X\n", Fno.fname, Fno.fattrib);
    else if(AM_DIR & Fno.fattrib)
      printf("%-13s  -<dir>-  %2X\n", Fno.fname, Fno.fattrib);
    else
      printf("%-13s  %7ld  %2X\n", Fno.fname, Fno.fsize, Fno.fattrib);
}

//----  ディレクトリーのオープン・取得・表示
FRESULT List_Dir(char *str){
    FRESULT res;
    char    path[PATH_SIZE];
    size_t  n;
    uint8_t i;
    
    if(SD_Mount() != FR_OK) return FR_NOT_READY;
    n = strlen(str+1);
    if(n > 0){
        if(n >= PATH_SIZE) n = PATH_SIZE - 1;
        for(i = 0; i < n; i++)
            path[i] = (char)toupper(str[i+1]);
    }
    path[n] = 0;      
    res = pf_opendir(&Dir,path);
    if(n == 0)  printf("OpenDir = root  %d\n", res);
    else        printf("OpenDir = %s  %d\n",path, res);
    if(res != FR_OK) return res;
    while(pf_readdir(&Dir,&Fno) == FR_OK){
        Prt_Dir();
    }
    puts("--- dir end ---");
    return res;
}

/***************************************
 *    Main application
 ****************************************/
void main(void) {
    FRESULT res;
    char    cmd;                            // 受信コマンド文字
    
    SYSTEM_Initialize();

  // タイマ0 Callback関数定義
    TMR0_SetInterruptHandler(TMR0_Process);
    
    INTERRUPT_GlobalInterruptEnable();
    INTERRUPT_PeripheralInterruptEnable();

    SSP1CON1bits.SSPEN = 1;
    
    EU_Puts("--- test ---");
    LED_SetHigh();
    
    while (1) {
      // 1秒周期の処理
      if(TFlg == 1){                        // フラグオンの場合
        LED_Toggle();
        TFlg = 0;                           // フラグリセット
      }
      // コマンド処理
      if(SFlg){                             // 受信ありの場合
        cmd = RBuf[0];  
        sprintf(Msg,"Input = %s\n",RBuf);
        EU_Puts(Msg);
        switch(cmd){
          case 'R': SD_cmd_R(RBuf); break;  // SDからデータ読出
          case 'F': SD_cmd_F(RBuf); break;  // ファイル名セット
          case 'W': SD_cmd_W(RBuf); break;  // SDにデータ書込 
          case 'D': List_Dir(RBuf); break;  // ディレクトリ
          //----  Test Putit_FatFS  ----
          case 'm': SD_Mount();     break;  // SDマウント
          case 'o': SD_Open();      break;  // FileOpen 
          case 'r': SD_Read();      break;  // データ読出し
          case 'i': SD_Info();      break;  // SD情報表示
          case 's': Tst_Seek(RBuf); break;  // SDシーク
          case 'w': Tst_Write(RBuf);break;  // データ書込
 
          default:  EU_Puts("???"); break;  // 無効なコマンド
        }
        SFlg = 0;
      }
    }
}
/******   End of File   *****/

【その他のライブラリー】

・プチFAT:
pff.c/h+pffconf.h(zip) [修正済み]
・diskio:
diskio.c/h(zip)


※プログラムのリストをハイライト無しのスタイルで見る場合はここをクリック


※ 本レポートの参考・利用は、あくまでも自己責任でお願いします。


電動エアーダスター プチFATでデータ書込