Vol.929 24.Oct.2025

ネットワークブリッジ 簡易CANバスモニター

M ミニPC(2) 〜Maxmini B3(New)でネットワークブリッジ

by fjk

 Amazonプライム感謝祭に、Maxmini B3(New)[BMAX]を14,899円で入手した。この価格で、新品のWindowss11proマシンを入手できるのは驚きです。
 マシンの主な仕様は、
CPU:
Intel Celeron N5095(TDP 15W)
OS:
Windows 11 Pro プリインストール
グラフィック:
Intel UHD、4K@60Hz
メモリ:
DDR4_8GB(最大16GB)
SSD:
256GB_M.2×1
内部ベイ:
2.5インチHDD/SSD×1(最大2T)
ビデオ出力:
HDMI 2.0×2(2画面同時出力)
LAN:
1000Mbpsイーサネット、RJ45×1
Wi-Fi:
2.4GHz/5GHzデュアルバンドWi-Fi
インターフェース:
USB 3.0×4
オーディオ:
3.5mmAudio端子×1
ケース:
クリエイティブケースデザイン
重量:
290g
サイズ:
125×112×41mm

Maxmin_B3_Newの主な仕様
BMAX   Maxmin_B3_New

 N5095(Jasper Lake)は2021年頃に発売されたCPUで、第7世代のCore-i5と同等の性能で、メールやネットサーフィン、YouTube動画再生、簡単な文書編集などの軽作業なら、ビジネス用途でも使えるといわれています。abc858で紹介したミニPCのNucBox5(N5105、24EUs)よりは、さらにグラフィック性能(16EUs)などが劣るので、3Dゲームや動画編集には不向きです。N5095の詳細はここ などを参照。
 今回は、主にネット接続サーバ管理用に使っているabc806で紹介したwin10マシン(i5-9400F)の後継用Win11マシンとして準備したのだが、同マシンがabc928でwin11にアップできたので、他の用途にも使えるよう、DDR4_8GBメモリ(4,000円、CFD)とSSD(2.5インチ、1TB、10,000円、crucial BX500)を追加した。
 そして、Maxmini_B3(New)の有線/無線の2つのアダプターを利用して、2つのLANを繋ぐネットワークブリッジを構成し、異なるLANに繋がるマシン間でファイルの共有が出来るようにした。ネットワークブリッジの設定はWindows 11でネットワークブリッジを設定する方法などを参照。

 

Maxmini_B3(New)の外観

Maxmini内部(メモリ、SSD取付)

ネットワークブリッジ
接続中


P PICでCAN通信(3) 〜簡易CANバスモニター

by fjk

 前報(abc928)のノード4のハードを使って、簡易なCANバスモニター(BUSクロック=100kbps)を作成した[ノード5]。
 ハードはabc928をほぼそのまま利用。なお、IDは11ビットのみに対応。
【主な改造点】

  1.  CANバスを流れるデータを全て取得し、OLEDに16進文字等で表示
     (16進文字の表示データは書込開始位置がスクルールする手抜きスクロール)
  2.  MCP2515モジュールのJ1ジャンパー外し解放(終端抵抗を無効)し、バスの途中でモニター。
  3.  以下の表示モードを選択可能(スクロール画面は5x7ドット文字、最下行は半角文字)
    @ 整数モード[abc928と同じ、Default]  
     (データが2バイト整数)
     XXX: NNNNN
    A アスキーモード [最大8文字]
     (表示可能なアスキー文字以外は '・' )
     XXX: AAAAAAAA
    B HEXモード [最大8バイト]
     (16進文字で表示、最下行の半角表示無し)
     XXX: H0H1H2H3H4H5H5H7
    ● リモートフレーム
     XXX: --Remote

    CANバスモニターテスト状況

【abc928から具体的な変更箇所】

  1.  MCP2515モジュールのJ1ジャンパー外し解放(終端抵抗を無効)
  2.  abc927のノード1(コンデンサ不要のPIC18F26K83を使用)とノード2の間に、ノード5をCAN接続
  3.  skMCP25xx.cの行番号171と178のMCP_RXB_RX_STDEXTをMCP_RXB_RX_ANYに変更し、「全ての通信を受信」に設定
    (受信バッファーモードの詳細は、後述のMCP2515データシートを参考)
  4.  mainの以下を変更
    @ 関数内でノード5のフィルターを設定(id=124)しているが、上述の設定で無視される
    A リモートフレーム受信も表示できるように変更(Rコマンドも使えるが・・)
    B スイッチ入力で表示モード(整数/アスキー/16進数)を変更可(Mコマンドでも変更可)

CANバスモニタ−(整数)

CANバスモニター(アスキー)

CANバスモニター(16進数)

【テスト結果】
 ノード1,2(id=123)のCAN通信を、ノード5(RXM=ANY[11])でCANバスを監視(モニター)することができた。
 しかし、ノード5でフレーム送信はできたが、自分が出すフレームのモニターはできなかった。

★ USE_ASC_FONTをコメントにすると、英数フォントが選択され、PIC16F18325が使える。
★ USE_RA3_PORTを有効にすると、スイッチはSW0ではなくRA3になる(abc928を参考に)

CANバスモニタ(ノード5)のプログラムリスト


【参考】MCP2515の受信レジスタの一部 (MCP2515Manualより)


RXM : 受信バッファモードの設定を行う
    11 = 全てのメッセージを受信する(エラーも含む)、
         フィルタ基準は無視される
    10 = 予約済み)使用不可
    01 = 予約済み)使用不可
    00 = RXFnSIDL.EXIDEN 
        ビットごとに全ての有効なメッセージを受信する

RXRTR : リモート転送要求受信ビット
    1 = リモート転送要求を受信
    0 = リモート転送要求を受信なし

BUKT : ロールオーバー有効ビット
    1 = RXB0が満杯の場合、メッセージはRXB1に書き込まれる
    0 = ロールオーバーは無効

FILHIT0-2 : フィルタヒットビット(どのフィルタが受信したか)





SID[2:0] : 標準識別子ビット

SRR : 標準フレームリモート送信要求ビット
        (IDEビットが0の場合のみ有効)
    1 = 標準フレームリモート送信要求を受信
    0 = 標準データフレームを受信

IDE : 拡張識別子フラグビット
        (受信メッセージが標準フレームか拡張フレームかを示す)
    1 = 受信メッセージは拡張フレーム
    0 = 受信メッセージは標準フレーム

EID[17:16] : 拡張識別子ビット


≪≪ ノード5 ≫≫   ▼abc929-18326.c(zip)

/*****************************(abc929-18326.c)***********
 *  簡易CANバスモニター(MCF2515モジュール)       *
 *********************************************************/
#include "myProject.h"
#include "myFunction.h"
#include "mcp_can_dfs.h"
#include "skMCP25xx.h"
#include "i2c_SSD1306.h"

#define USE_ASC_FONT
#ifdef USE_ASC_FONT
    #include "Font_8x16ASC.h"
#else
    #include "Font_8x16NA.h"
#endif

//#define USE_RA3_PORT
#ifdef USE_RA3_PORT 
    #define GET_SW()    RA3_GetValue() 
#else
    #define GET_SW()    SW0_GetValue()  
#endif

// 共通変数の宣言
char RBuf[EU_BFSIZE];           // BFSIZEはmyProject.hで宣言
uint8_t SFlg 	= 0;            // シリアル受信フラグ
uint8_t CrtLn 	= 0;            // OLED表示現在行
uint8_t H_Chr 	= 0;            // 整数表示モード
uint8_t Mx_Ln 	= 4;            // 最終行は5行目

unsigned char flagRecv  = 0;    // CAN受信フラグ
unsigned char len       = 0;    // CAN受信文字数
unsigned char RcvBuf[8];        // CAN受信データ

/*==========================================================*
 *   MCP2515のINTピン割り込み処理                           *
 *----------------------------------------------------------*/
void IOCAF2_Process(void) {
    flagRecv = 1;         // CAN受信(有り)フラグセット
}

/*==========================================================*
 *  OLED表示用関数 & 表示テスト関数                        *
 *----------------------------------------------------------*/
//---- スクロール付き表示(5x7ドット文字)
//      (スクロール範囲は[0 - Mx_Ln])
void i2cOLED_Prt(char *str) {
    i2cOLED_posPX(CrtLn, 0);
    i2cOLED_str(str);
    if (CrtLn < Mx_Ln)  CrtLn++;
     else CrtLn = 0;
    i2cOLED_Clr(CrtLn, CrtLn, 0);
}

//---- 8x16ドット半角文字表示
uint8_t i2cPrint_Hchr(uint8_t py, uint8_t x,char ch){
   #ifdef USE_ASC_FONT
    if((0x20 <= ch)&(ch <= 0x7F)){
        x = i2cOLED_Ptn(py,x,2,8,(char *)F_asc[ch-0x20]);
   #else
    if((0x30 <= ch)&(ch <= 0x3A)){
        x = i2cOLED_Ptn(py,x,2,8,(char *)F_hn[ch-0x30]);
    }else if((0x41 <= ch)&(ch <= 0x5A)){
        x = i2cOLED_Ptn(py,x,2,8,(char *)F_af[ch-0x41]);
    }else if(ch == 0x20){
        x = i2cOLED_Ptn(py,x,2,8,(char *)F_sp);
    }else if((0x2A <= ch)&(ch <= 0x2F)){
        x = i2cOLED_Ptn(py,x,2,8,(char *)F_op[ch-0x2A]);
    }else if((0x61 <= ch)&(ch <= 0x7A)){
        x = i2cOLED_Ptn(py,x,2,8,(char *)F_af[ch-0x61]);
   #endif
    }else{
        x = i2cOLED_Ptn(py,x,2,8,(char *)F_dt);
    }
    return x;
}

//---- 半角文字列の表示
uint8_t i2cPrint_Hstr(uint8_t py, uint8_t x, char *str) {
    uint8_t n;
    while(*str){
        x = i2cPrint_Hchr(py,x,*str++);
    }
    return x;
}

//---- 半角文字の表示テスト
void test_HanK(void) {
    uint8_t i, j;
    for(j = 0; j < 4; j++){
        for(i = 0; i < 16; i++){
            i2cPrint_Hchr(j*2, i*8, 0x20+j*16+i);
//            i2cPrint_Hchr(j*2, i*8, 0x40+j*16+i);
        }
    }
}

//----  配列データを16進数文字列に
void set_hex_data(char *bf, uint8_t *ary, uint8_t n){
    uint8_t i;
    for(i = 0; i < n; i++){
        my_xtoa(bf, ary[i], 2);
        bf += 2;
    }
    *bf = 0;
}

//--- 印刷可能な文字に変換して文字列を連結
void cv_prtcat(char *bf, char *str, uint8_t n){
    uint8_t i;
    for(i = 0; i < n; i++){
       if(isprint(*str))    *bf++ = *str;
         else               *bf++ = 0x7F;
        str++;
    }
    *bf = 0;
}

//---- OLEDとEUに文字列を出力
void OLED_EU_Prt(char *str){
     i2cOLED_Prt(str);
     EU_Puts(str);
}

//---- OLEDの最下行に半角文字表示
void OLED_Low_Hprt(char *str){
    i2cOLED_Clr(6, 7, 0);
    i2cPrint_Hstr(6, 0, str);       // 半角文字表示
}

//----  表示モードの変更
uint8_t chg_dspMd(void) {
    i2cOLED_Clr(0, 7, 0); CrtLn = 0;
    switch (H_Chr) {
        case 0:	H_Chr = 1;	Mx_Ln = 4;
            #ifdef USE_ASC_FONT
            OLED_EU_Prt("** Ascii Mode");
            #else
            OLED_EU_Prt("** N-Alfa Mode");
            #endif
            break;
        case 1:	H_Chr = 2;	Mx_Ln = 7;
            OLED_EU_Prt("** HexNum Mode");
            break;
        case 2:	H_Chr = 0;	Mx_Ln = 4; 
            OLED_EU_Prt("** Integer Mode");
            break;
    }
    return H_Chr;
}

/*==========================================================*
 *  CAN受信の処理                                           *
 *----------------------------------------------------------*/
void CheckReceiveProcess(void) {
    union {
        unsigned char c[2];
        unsigned int i;
        } data;
    unsigned long id;
    char buf[16];

    // INTピン割り込みが有れば処理する
    if (flagRecv) {
        flagRecv = 0;           // 割り込みフラグをクリア
        while (CAN_MSGAVAIL == mcp_checkReceive()) {
            // 受信したメッセージを読み込む,
            mcp_readMsgBuf(&len, RcvBuf);
            id = mcp_getCanId();
            my_xtoa(buf, (uint16_t)id, 3); 
            buf[3] = ':';  buf[4] = ' ';

            // データフレームなら
            if (mcp_isRemoteRequest() == 0) {
                if(H_Chr == 0){             // 整数表示
                    data.c[0] = RcvBuf[0];
                    data.c[1] = RcvBuf[1];
                    my_utoa(buf + 5, data.i);
                    OLED_EU_Prt(buf);
                    OLED_Low_Hprt(buf);     // 半角文字表示
                }else if(H_Chr == 1){       // アスキー表示
                    cv_prtcat(buf+5, (char *)RcvBuf, len);
                    OLED_EU_Prt(buf);
                    OLED_Low_Hprt(buf);     // 半角文字表示
                }else{                      // 16進数表示
                    set_hex_data(buf+5,RcvBuf,len);
                    OLED_EU_Prt(buf);
                }
            }else{                          // リモートフレーム
                buf[5] = 0;
                strcat(buf," -- Remote");
                OLED_EU_Prt(buf);
                if(H_Chr < 2){
                    OLED_Low_Hprt(buf);     // 半角文字表示
                }
            }
        }
    }
}

/*==========================================================*
 *  CAN送信テスト                                           *
 *----------------------------------------------------------*/
void CAN_Send(char *str, uint8_t *bf) {
    uint16_t id;
    uint8_t n, *p;
    n = (uint8_t)strlen(str) - 1;
    if(n == 0){                         // パラメータ無し
        id = 0x123;
        // リモートフレーム送信
        mcp_sendMsgBuf(id, CAN_STDID, CAN_RMTFRM, 0, bf, 1);
    }else{
        if(n > 8) n = 8;                // 最大8文字まで
        p = (uint8_t *)(str + 1);
        id = 0x124;	
        // データーフレーム送信
        mcp_sendMsgBuf(id, CAN_STDID, CAN_DTFRM, n, p, 1);
    }
}

/************************************************************
 *       Main application                                   *
 ************************************************************/
void main(void) {
    uint8_t cmd;                        // コマンド
    uint8_t buf[8];                     // CANデータ用
    uint8_t sw;                         // スイッチ情報

    SYSTEM_Initialize();
    SSP1CON1bits.SSPEN = 1;             //SPI1を有効に

    IOCAF2_SetInterruptHandler(IOCAF2_Process);

    INTERRUPT_GlobalInterruptEnable();
    INTERRUPT_PeripheralInterruptEnable();

    i2cOLED_init();
    i2cOLED_Clr(0, 7, 0);
    OLED_EU_Prt("ready!");

    // MCP2515によるCAN通信の初期化
    // CANバス通信速度=100Kbps  MCP2515のクロック=8MHz
    while (CAN_OK != mcp_begin(CAN_100KBPS, MCP_8MHz)) {
        OLED_EU_Prt("InitFail");
        while (1);                      // 終了
    }
    OLED_EU_Prt("Init OK!");

    // MASK0->Filter0->RXB0(オーバフローでRXB1)のみ使用
    mcp_init_Mask(0, 0, 0x3ff);         // 全て受付る
    mcp_init_Filt(0, 0, 0x124);         // ID:0x124のみ受け取る
    // RXM = MCP_PXB_RX_ANY に設定するとフィルター基準は無視

    // MCP2515のINTピン割り込みの設定
    IOCAN2 = 1;     // RA2をIOCで設定(立下げエッジで割込み発生)
    IOCAF2 = 0;             // フラグをクリアする
    IOCIF = 0;              // 状態変化割込フラグをクリア
    IOCIE = 1;              // 状態変化割り込みを有効にする

    while (1) {
        if(GET_SW()==0){                // スイッチONか
            __delay_ms(50);             // チャタリング対策
            if(GET_SW()==0){            // やはりスイッチON?
                chg_dspMd();
                while(GET_SW());        // スイッチがOFFまで待つ
                __delay_ms(100);        // チャタリング対策
            }
        }

        // 受信しているメッセージデータを調べる
        CheckReceiveProcess();

        if (SFlg) {                     // シリアル入力があれば
            cmd = RBuf[0];
            switch (cmd) {
                case 'C': i2cOLED_Clr(0, 7, 0);
                          CrtLn = 0;                break;
                case 'H': test_HanK();              break;
                case 'R': CAN_Send(RBuf,buf);       break;
                case 'M': chg_dspMd();              break;
            }
            SFlg = 0;
        }
    }
}

/****************** End of File *****************************/

【その他のプログラム】

mcp_can_dsf.h(zip)・・・abc928と同じ
skMCP25xx.c/h(zip)・・・abc928と同じ
myProject.h(zip)・・・abc928と同じ
myFunction.c/h(zip)・・・abc928と同じ
i2c_SSD1306.c/.h+Font_5x7.h(zip)・・・abc928と同じ
Font_8x16ASC.h+Font_8x16NA.h(zip)
※ EUSARTの設定についてはabc917等を参照

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


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


ネットワークブリッジ 簡易CANバスモニター