Vol.869 12.May.2023

電子バックミラー Wordのフィールドコード (R-8)バーチャルIRリモコン

D 電子バックミラー (ルームミラー型ドライブレコーダ)

by fjk

 最近、ルームミラー型の電子バックミラー(レコーダ付)が多く見受けられるようになってきた。荷物を積んでいて、後方が見えないときに便利である。シュピーレンも後方視界は良くなく、abc775で取り付けた後方カメラは解像度が130万画素で、後続車のナンバープレートも近づくとやっと読める程度だった。
 そこで、200万画素カメラのChagerV28(\10,593)を2020年に秋に店頭で見つけ、購入していたが、取り付ける暇が無く放置していた。購入時、ミラーとテレビのアンテナが近いので、電波干渉が起きないかという問題が気になり、「電波干渉なし」と記載があるものを選んだ。主な仕様は、([]はBS01)

・カメラ解像度:
1920x1080(27.5/25fps)、F2.0  [前:3840x2160、F1.8、後:1920x1080]
・録画解像度:
1296P/1080P/720P(H.264、MP4)  [2160P、1440P、1080P(リア)、MPEG2-TS]
・表 示:
11.88型液晶(タッチスクリーン)  [11inch、表示エリア、輝度、画面調整]
・音 声:
録音ON/OFF、音声コマンド対応  [音声記録・音声認識無し]
・GPS:
緯度/経度、スピード/進路、受信状態、信号強度、GPS位置記録
・センサ:
Gセンサ、駐車監視(要別売り電源ケーブル)のイベント録画
・SDカード:
8〜128GB(マイクロSD、class10以上推奨) [32〜128GB、32GB付属]

 リアカメラケーブルについている赤いリード線は、バックランプの電源に接続すると、バック時に自動でリアカメラに切り替わる。自動切り替えやバックガイドを使わないのであれば配線しなくても良い。
 リアカメラ用コードが思っていたより短く(6m)、別途「ドライブレコーダ用4pinリアカメラ延長コード10m」(\1,300)を入手した。と、同時に、AmazonのタイムセールでHDR/WDR機能付き4KドライブレコーダBS01(WFK)が\9,000(40%off、2022model)とあり、音声認識機能が無い以外はほぼ同じ機能だったので、ついポチリ。V28は女房の車にトレードした。
 なお、両機種ともTV、GPSへの電波の干渉はない。後方視界が悪いキャンピングカーには必需品かも。また、前方/後方画像の2画面同時表示も可。


ルームミラーに取り付けた状態
(前方カメラ表示)

バックミラー状態
(後方カメラ表示、メニュー表示中)

夜間後方カメラ
(ライトが強くハレーションぎみ)


F Wordのフィールドコード

by fjk

 Wordで使われるフィールドコードは、図、文字、数式に対してどのような処理を実行するのかをWordに命令するための専用のコードで、Excelのマクロのようなもの。「頁番号」や「差し込み印刷」「条件付き書式」など、いろいろな応用に利用できる。
 フィールドコードを使うには、挿入したいところで、「挿入/テキスト」の「クイックパーツの表示」から「フィールド」を選ぶと、使用できるフィールドコードが表示され、入力できる。

  

【フィールドのショートカットキー】
フィールドショートカットキー記述例実行結果
・DATEフィールド
・PAGEフィールド
・TIMEフィールド
・LISTNUMフィールド
Alt+Shift+D
Alt+Shift+P
Alt+Shift+T
Ctrl+Alt+L

・次のフィールドへ移動 Alt+F1
・前のフィールドへ移動 Shit+F11
・空のフィールドを挿入 Ctrl+F9 [MS-IME時](挿入後フィールドコードを手入力)
・フィールドのリンクの解除 Ctrl+Shift+F9 (ハイパーリンクの設定はCtrl+K)
・フィールドのロック Ctrl+F11
・フィールドロックの解除 Ctrl+Shift+F11

※詳細は、Microsoftサポートの「Wordのフィールドコード一覧」を参照。


R 赤外線リモコン(8)  〜バーチャルリモコン

by fjk

 前報(abc868)でパソコンから赤外線リモコンを制御できたので、パソコン上でProcessinngを使ってグラフィカルなリモコンを実現した。
 前報では、1文字でリモコンを操作したので、そのまま文字を増やす方法もあるが、拡張性も考慮し、文字列で送信する方法を用いた。文字列は赤外線コードそのものでもよいが、4x3キーに対応する1〜51の番号を送ることにした。これに伴い、受信するPIC側のソフトも修正した。。

  1. ターミナルソフト(テラターム)で文字列のPICシリアル通信テスト
     PICのハードはabc868のままで、ソフトは受信割り込みを使うので、eusart1の割り込み処理でデータを受信し、受信データのエコーと受信文字を受信バッファーに保存するコードを追加した。さらに、改行コード(0x0Aまたは0x0D)を受信するとsFlg=1とし、データ受信が完了したことが判るようにした。
     メインでは、sFlgをチェックし、受信データがあれば、受信文字列を数値に変換し、この番号に対応した赤外線コードの発出とそのコードをパソコンにも送信したところ、リモコンが正常に動作した。
      

     
  2. Processinngで作成したGUI画面からリモコン操作
    シリアル接続するだけであれば、import processing.serial.*; 宣言だけで良いが、接続ポートリストサイズも取得したいので import java.util.*; も宣言し、portList = Arrays.asList(Serial.list()); でリスト、portList.size()で接続USBポート数を取得し、最後に接続したUSBポートに115200bpsで自動接続した。もし、接続USBポートが既に判っているなら、直接指定もできる。
          portList = Arrays.asList(Serial.list());
          portMax = portList.size()-1;
          myPort = new Serial(this, Serial.list()[portMax], 115200);
    
    ボタンデータ用にBtnクラスを作成し、ボタン配列を使用。
        class Btn{  String nam;	// ボタンの名称
                    float x;	// ボタンの表示X位置
                    float y;	// ボタンの表示Y位置
                    int w;		// ボタンの横長さ
                    int h;		// ボタンの縦長さ
                    String cap;	// ボタンのキャプション(表示文字)
                    int val;	// 送信する赤外線コード用数値
            void draw(){・・}	// ボタンを表示するメソッド
            int getVal(){・・}	// ボタンのコード数値を返すメソッド
            String getNam(){・・}	// ボタンの名称を返すメソッド
          }
    
      

 ボタンイベントが発生したら、contorlEvent(・・)で発生したイベント(theEvent)とボタン(コントロール)の名前を比較し、一致したら、対応するリモコンコード用数値をPICにシリアル送信している。
 同時に、シリアル受信データ(エコー・赤外線送信コードデータ)があれば、IDEコンソールに出力している。
 デフォルトのフォントでは文字サイズが小さいので、漢字フォントでサイズと合わせて font=createFont("MS-Gothic-48",20) と宣言し、 .getCaptionLabel().setFont(font) でボタンのキャプションフォントを指定した。
 PICリモコンをUSB接続し、パソコン上のLCD-DTVRC4リモコン画面で、ボタンをマウスでクリックすることで、IOデータのテレビをリモコン操作できるようになった(PICの4x3キーも同時使用可)。

●PICのmain.c  (赤字はabc868から変更・追加したところ)

【プログラム】 abc869.c(zip)
/*=== Ir Remote controler for IO-Data  (abc869) ===*/

#include "mcc_generated_files/mcc.h"

#define MARK()      __delay_us(562)
#define SPACE_1()   __delay_us(1686)
#define SPACE_0()   __delay_us(562)
#define READER_1()  __delay_us(8992)
#define READER_0()  __delay_us(4496)
#define GYAP_0()    __delay_ms(43)

#define DSM_1()     (MD1CON0bits.MD1BIT = 1)
#define DSM_0()     (MD1CON0bits.MD1BIT = 0)
#define IO_CUSTOM   0xE880
#define SCN_DELAY() __delay_ms(30)
#define KEY_DELAY() __delay_ms(500)

uint8_t IO[][8] ={{0x80,0xE8,0x84,0x7B,0x80,0xE8,0x10,0xCF},
                  {0x80,0xE8,0x84,0x7B,0x80,0xE8,0x20,0xCF},
                  {0x80,0xE8,0x84,0x7B,0x80,0xE8,0x30,0xCF}};
			
uint16_t IO2[]  = {0x1084,0x4084,0x7084,0x7024,	//  1, 4, 7,DG [Md=0}
                   0x2084,0x5084,0x8084,0xB084,	//  2, 5, 8,11
                   0x3084,0x6084,0x9084,0x0094,	//  3, 6, 9,BS 
                   0x3014,0x4024,0x0014,0x1014,	// Mn,Lf,C+,C- [Md=1]
                   0x2024,0x4014,0x3024,0xD084,	// Up,Cr,Dw,Rt
                   0x2014,0x5024,0xE004,0xF004,	// Sl,Rt,V+,V-
                   0xE014,0x0094,0x0000,0xA024,	// Vd,BS,  ,Ps [Md=2]
                   0x9024,0x0000,0x8014,0x0000,	// HD,  ,Cd,In
                   0xD014,0x7024,0x0000,0x9014,	// PC,DG,  ,So
                   0xC014,0xF024,0xE024,0xE084,	// Dp,Pr,Dc,Sb [Md=3]
                   0x1024,0xC024,0xD024,0x5014,	// Md,Wp,Nw,Sa
                   0x6014,0x0084,0xF084,0x7014,	// Sz,Nx,pC,Tm
                   0xD004,0xA084,0xC084};     	// power,10,12

uint16_t nco[] = {0xFFFF,0x0043,0x0086,0x010c}; // 0.5s,1s,2s,4s

uint8_t  sft;
uint16_t wsft;
uint16_t keyM;
uint8_t  keyP
uint8_t  IrMd = 0;

char     sBuf[BFSIZE];
char     rBuf[BFSIZE];
uint8_t  sFlg = 0;
uint8_t  sIdx = 0;
 
/*---- IOdata Leader ---*/
void IO_Leader(){
    DSM_1();                // "Leader" Start
    READER_1();
    DSM_0();
    READER_0();
}

/*---- IOdata stop ---*/
void IO_Stop(){
    DSM_1();
    MARK();                 // "Stop"
    DSM_0();
}

/*-------- Send word of IRcode ------*/
void Ir_Send2(uint16_t wd){
    wsft = 0x0001;
    for(int i=0;i<16;i++){
        DSM_1();
        MARK();
        DSM_0();
        if(wd & wsft)  SPACE_1();
         else          SPACE_0();
        wsft <<= 1;
    }
}

/*-------- Send IOdata2 IRcode  ----*/
void IO_DATA2(uint8_t c){
    uint16_t w = IO2[c];
    uint16_t wLo=(w&0x00FF)|((~w<<8)&0xFF00);
    uint16_t wHi=((w>>8)&0x00FF)|(~w&0xFF00);
    IO_Leader();            	// "Leader" Start
    Ir_Send2(IO_CUSTOM);    	// Send "Custom" 
    Ir_Send2(wLo);          	// Send "Data1"
    IO_Stop();              	// stop
    GYAP_0();               	// Gyap time
    IO_Leader();            	// "Leader" Start
    Ir_Send2(IO_CUSTOM);    	// Send "Custom" 
    Ir_Send2(wHi);          	// Send "Data2"
    IO_Stop();              	// stop
    printf(" %04X\n\r",w);  	// serial print
}

uint16_t keyScan(){
    uint8_t k;
    uint16_t ky;
    X_A0_SetLow();
    SCN_DELAY();
    ky = ~PORTC & 0x0F;
    X_A0_SetHigh();    

    Y_A1_SetLow();
    SCN_DELAY();
    k = ~PORTC & 0x0F;
    ky |= ((uint16_t)k << 4);
    Y_A1_SetHigh();    

    Z_A2_SetLow();     
    SCN_DELAY();
    k = ~PORTC & 0x0F;
    ky |= ((uint16_t)k << 8);
    Z_A2_SetHigh();    
    return ky; 
}

uint8_t bitCount(uint16_t w){
    uint16_t wsft = 1;
    uint8_t i;
    for (i = 0; i < 12; i++){
        if(w & wsft)   return (i);
        wsft <<= 1;
    }
    return 0;
}

void Power_Mode(){
    uint8_t i;
    __delay_ms(10);
    if(PORTAbits.RA3) return;
    for(i=0;i<20;i++){
        __delay_ms(100);
        if(PORTAbits.RA3){
            IrMd = (IrMd + 1) % 4;
            NCO1CON  = 0x00;                    //NCO stop();
            NCO1INCH = (nco[IrMd] >> 8) & 0xFF;
            NCO1INCL =  nco[IrMd] & 0xFF;
            NCO1CON  = 0x80;                    //NCO start
            return;
        }
    }
    IO_DATA2(48);                               // Power
    while(!PORTAbits.RA3) ;
}

/* =====  Main application ====== */

void main(void)
{
    uint8_t n;

    SYSTEM_Initialize();
    INTERRUPT_PeripheralInterruptEnable();
    INTERRUPT_GlobalInterruptEnable();	
       
    X_A0_SetHigh();
    Y_A1_SetHigh();
    Z_A2_SetHigh();
    
    puts("Hellow\n\r");
 
    while (1){
        if(!PORTAbits.RA3) Power_Mode();
        keyM = keyScan();
        if(keyM){
            keyP = bitCount(keyM) + (IrMd * 12);  
            IO_DATA2(keyP);
            KEY_DELAY();
        }
        if(sFlg){
            n = atoi(rBuf);
            if((n > 0)&&(n < 52)){
              IO_DATA2(n-1);
            }
            sFlg = 0;
        }
    }
}
/* ===== End of File ===== */
※ eusart1.hにも以下の追加・修正が必要
/***	eusart1.h 	***/
#define BFSIZE 18  		// シリアル受信バーファーサイズ
 ・・・
※ eusart1.cにも以下の追加・修正が必要
/***	eusart1.c	***/
#define EUSART1_TX_BUFFER_SIZE 8
#define EUSART1_RX_BUFFER_SIZE 8
extern char rBuf[];		// シリアル受信バッファー
extern uint8_t sFlg;		// 受信完了フラグ
extern uint8_t sIdx; 		// 受信文字列インデックス
 ・・・
void EUSART1_Receive_ISR(void)
{
    char ch;
    eusart1RxStatusBuffer[eusart1RxHead].status = 0;
    if(RC1STAbits.FERR){
        eusart1RxStatusBuffer[eusart1RxHead].ferr = 1;
        EUSART1_FramingErrorHandler();
    }
    if(RC1STAbits.OERR){
        eusart1RxStatusBuffer[eusart1RxHead].oerr = 1;
        EUSART1_OverrunErrorHandler();
    }    
    if(eusart1RxStatusBuffer[eusart1RxHead].status){
        EUSART1_ErrorHandler();
    } else {
        EUSART1_RxDataHandler();
    }
    // or set custom function using EUSART1_SetRxInterruptHandler()
    ch = getch();
    putch(ch);
    if((ch == 0x0a)||(ch == 0x0d)){
        sFlg = 1;
        rBuf[sIdx] = 0;
        sIdx = 0;
    }else{
        if(sIdx < BFSIZE) rBuf[sIdx++] = ch;
    }
}
 
●Processingのプログラムコード
【プログラム】 abc869.pde(zip)
class Btn{			// ボタンオブジェクト(abc869_btn)
    String nam;
    float  x;
    float  y;
    int    w;
    int    h;
    String cap;
    int    val;

    Btn(String tmpS,float tmpX,float tmpY,int tmpW,int tmpH,String tmpC,int tmpV){
        nam = tmpS;
        x = tmpX;
        y = tmpY;
        w = tmpW;
        h = tmpH;
        cap = tmpC;
        val = tmpV;
    }
    void draw(){
        cp5.addButton(nam)
           .setPosition(x,y)
           .setSize(w,h)
           .setCaptionLabel(cap)
           .getCaptionLabel().setFont(font);
    }
    int getbVal(){
        return val;
    }
    String getbNam(){
        return nam;
    }
}


// --- abc869_main ---
import controlP5.*;
import java.util.*;
import processing.serial.*;

ControlP5 cp5;
Serial myPort = null;
PFont  font;
int    btMax = 46;
Btn[]  b = new Btn[btMax];
String bNam;
List   portList;

void setup(){
    font=createFont("MS-Gothic-48",20);
    textFont(font);
    printArray(Serial.list());
    portList = Arrays.asList(Serial.list());
    int portMax = portList.size()-1;
    myPort = new Serial(this, Serial.list()[portMax], 115200);
    size(330,820);
    cp5 = new ControlP5(this);
    b[0] = new Btn("btn1",  20,140, 90,40,"1",     1);
    b[1] = new Btn("btn2", 120,140, 90,40,"2",     5);
    b[2] = new Btn("btn3", 220,140, 90,40,"3",     9);
    b[3] = new Btn("btn4",  20,190, 90,40,"4",     2);
    b[4] = new Btn("btn5", 120,190, 90,40,"5",     6);
    b[5] = new Btn("btn6", 220,190, 90,40,"6",    10);
    b[6] = new Btn("btn7",  20,240, 90,40,"7",     3);
    b[7] = new Btn("btn8", 120,240, 90,40,"8",     7);
    b[8] = new Btn("btn9", 220,240, 90,40,"9",    11);
    b[9] = new Btn("btn10", 20,290, 90,40,"10",    50);
    b[10]= new Btn("btn11",120,290, 90,40,"11",     8);
    b[11]= new Btn("btn12",220,290, 90,40,"12",    51);
    b[12]= new Btn("btn13",170, 20, 65,35,"BS",    26);
    b[13]= new Btn("btn14",245, 20, 65,35,"地D",   34);
    b[14]= new Btn("btn15", 95, 70, 65,35,"ビデオ",25);
    b[15]= new Btn("btn16",170, 70, 65,35,"HDMI",  29);
    b[16]= new Btn("btn17",245, 70, 65,35,"PC",    33);
    b[17]= new Btn("btn18", 20, 20, 50,35,"電源",  49);
    b[18]= new Btn("btn19",120,350, 90,40,"▲",    17);
    b[19]= new Btn("btn20", 20,400, 90,40,"<",    14);
    b[20]= new Btn("btn21",220,400, 90,40,">",    22);
    b[21]= new Btn("btn22",120,450, 90,40,"▼",    19);
    b[22]= new Btn("btn23",130,400, 70,40,"決定",  18);
    b[23]= new Btn("btn24", 20,450, 70,40,"Menu",  13);
    b[24]= new Btn("btn25",260,450, 50,40,"戻る",  20);
    b[25]= new Btn("btn26", 95,500, 50,48,"c▲",   15);
    b[26]= new Btn("btn27", 95,550, 50,48,"c▼",   16);
    b[27]= new Btn("btn28",175,500, 50,48,"v+",    23);
    b[28]= new Btn("btn29",175,550, 50,48,"V-",    24);
    b[29]= new Btn("btn30",260,550, 50,40,"消音",  21);
    b[30]= new Btn("btn31", 20,620, 65,35,"画表示",49);
    b[31]= new Btn("btn32", 95,620, 65,35,"画Mod", 41);
    b[32]= new Btn("btn33",170,620, 65,35,"画Siz", 45);
    b[33]= new Btn("btn34",245,620, 65,35,"前ch",  47);
    b[34]= new Btn("btn35", 20,670, 65,35,"前番組",38);
    b[35]= new Btn("btn36", 95,670, 65,35,"週番組",42);
    b[36]= new Btn("btn37",170,670, 65,35,"翌番組",46);
    b[37]= new Btn("btn38",245,670, 65,35,"現番組",43);
    b[38]= new Btn("btn39", 20,720, 65,35,"番説明",39);
    b[39]= new Btn("btn40", 95,720, 65,35,"字幕",  40);
    b[40]= new Btn("btn41",170,720, 65,35,"音声",  44);
    b[41]= new Btn("btn42",245,720, 65,35,"タイマ",48);
    b[42]= new Btn("btn43", 20,770, 65,35,"子画面",31);
    b[43]= new Btn("btn44", 95,770, 65,35,"子位置",28);
    b[44]= new Btn("btn45",170,770, 65,35,"子入力",32);
    b[45]= new Btn("btn46",245,770, 65,35,"子音声",36);

    for (int i = 0;i < btMax;i++){
        b[i].draw();
        println(b[i].getbVal(), b[i].getbNam());
    }
}

void draw(){
    background(200);
}

void controlEvent(ControlEvent theEvent) {
    for(int i = 0;i < btMax;i++){
        bNam = b[i].getbNam();
        if (theEvent.isFrom(bNam)){ 
            int n = b[i].getbVal();
            println(bNam," ",n);
            myPort.write(nf(n)+"\n");
        }
    }
}

void serialEvent(Serial myPort) {
    if ( myPort.available() > 0 ) {
        String str = myPort.readStringUntil('\n');
        if ( str != null ) print(str);
    }
}


※※ Processing のおまけ(1) ※※
processingではversion3からSIZE関数で変数が使えなくなった(変数を使うとエラーとなる)。代わりにsettings関数を使うように薦められている(settings関数はsetup関数より前に置くこと!)
int sizeW = 300;
int sizeH = 200;

void settings(){
    size(sizeW,sizeH);
}

void setup(){
   ・・・

 
※※ Processing のおまけ(2) ※※
また、surfaceオブジェクトを使うと、processing実行時にサイズ変更が容易。
●size関数で画像サイズを宣言後、surfaceでサイズを変更する例(赤字を前述リモコンに追加・変更
 パソコン上のリモコンのサイズを変更できるように、SZ変数に倍率(初期値=1.0)を設定すると、ウィンドウサイズも含めて大きさを変更できる(フォントサイズも合わせて変更している)。
  void draw(){
    cp5.addButton(nam)
       .setPosition(x*SZ,y*SZ)                            // position()の引数はfloat
       .setSize((int)(w*SZ),(int)(h*SZ))                  // size()の引数は整数
       .setCaptionLabel(cap)
       .getCaptionLabel().setFont(font);
  }
            ・・・

            ・・・
float SZ = 1.0;
        ・・・

void setup(){
    font=createFont("MS-Gothic-48",(int)(20*SZ));        // font()サイズの引数は整数
    textFont(font);
    printArray(Serial.list());
    portList = Arrays.asList(Serial.list());
    int portMax = portList.size()-1;
    myPort = new Serial(this, Serial.list()[portMax], 115200);
    size(330,820);
    surface.setResizable(true);                         // サイズ変更を有効にする
    surface.setSize((int)(width*SZ),(int)(height*SZ));  // 引数は整数(現在のサイズを元に)
//  surface.setSize((int)(330*SZ),(int)(820*SZ));       // 事前にsize()関数を使わない場合
    cp5 = new ControlP5(this);

         ・・・
★size()関数で事前に宣言をしなくてもsurface.setSize()関数のみでもOK。


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


電子バックミラー Wordのフィールドコード (R-8)バーチャルIRリモコン