N-VAN ルームランプ改造

N-VANのLEDルームランプ回路は下図左のようになっています。LEDモジュールのアノード側はV+(12V電源供給)に接続されています。LEDモジュールのカソード側はルームランプスイッチの位置によって以下のように接続されます。

・ON位置:GND

・中間位置:ドアスイッチ

・OFF位置:NC(接続なし)

しかしイグニッションOFF時はV+の電源供給が15分で切れるので、車中泊の時に天井灯として使うのには不便です。そこで上図右のようにダイオードを追加してサブバッテリーからも電源供給するように改造します。

ルームランプ改造

V+(電源供給元)とLEDモジュールの間を一箇所ニッパーで切断し、そこにダイオード(1N4007)を追加します。さらにそのダイオードのカソードにサブバッテリーからの電源をダイオード経由でパラ付けします。

注意:サブバッテリー直結の場合ルームランプOFFにするのを忘れるとサブバッテリーが過放電します。前記事のサブバッテリーモニターの電源供給回路のようにタイマーで自動OFFするような仕組みを入れておきましょう。Aitendoの長時間タイマーICなどで作ると簡単かもしれません。

自作サブバッテリーモニター

前記事で触れたN-VANで使っている自作サブバッテリーモニターです。SBC-004とSMF31MS-850の間に入れています。機能は以下のとおり。

・サブバッテリー電圧、充電電流表示
・サブバッテリー電源供給回路(ACC OFF後24時間で自動OFF)
インバーターON/OFF用リレーコントロール(30分で自動OFF)

サブバッテリーモニター外観

・起動するとサブバッテリーの電圧と充電電流を表示します。
・サブバッテリー電源供給回路はサブバッテリーからPower MOSFET経由で出力しています。ACC ONで自動ON、サブバッテリーの過放電を防ぐためACC OFF後24時間で自動OFFします。残り時間は液晶右下に表示。ENGELのポータブル冷蔵庫、ルームランプ、車載USB電源などに使っています。電源供給中は液晶のバックライトがONになり、LEDが点滅します。
・中央のプッシュスイッチで12V→100Vインバーター制御用のリレーをON/OFFします。サブバッテリーの過放電を防ぐためON後30分で自動OFFします。残り分数は液晶右上に表示。インバーターにコントロール端子が無い場合はリレー出力をインバーターの電源スイッチと並列にはんだ付けします。リレーON時は液晶のバックライトがONになり、OFFタイマー付電源供給回路もその後24時間ONになり、液晶下のLEDが点灯します。

回路図は以下のとおり。使っている液晶パネルは秋月のAQM0802A-FLW-GBWです。

サブバッテリーモニター回路図

サブバッテリーモニター回路図

制御プログラムは以下のとおり(少し大きい)。消費電力を抑えるためにClockは1MHzで動作。初期化後は125msおきのタイマー割り込みで電源OFFタイマーを減算。フォアグラウンド側で電圧・電流測定、タイマー値を見て電源・リレーON/OFF、ACC電圧とプッシュスイッチON/OFFの確認などを行っています。

#include 

#pragma config FCMEN=OFF
#pragma	config IESO=OFF
#pragma	config CPD=OFF
#pragma	config CP=OFF
#pragma	config BOREN=ON
#pragma	config MCLRE=OFF
#pragma	config PWRTE=OFF
#pragma config LVP=OFF  //
#pragma config WDTE=OFF
#pragma config FOSC=INTOSCIO

#define _XTAL_FREQ 1000000
#define TMR1H_VAL 0x85      // 65536-31250=34286=0x85ee
#define TMR1L_VAL 0xee      // 31250 * 4us = 125ms period
#define RELAY_TIME 30        // relay on time 30min.
#define EXT_POW_TIME 24       // ext. power on time 24H.
#define RELAY RA7
#define ACC RA6
#define PUSH_SW RA5
#define EXT_POW RA1
#define LED_R RB5
#define SCL RB4
#define LED_BL RB2
#define SDA RB1
#define PANEL_POW RB0
#define TRIS_SDA TRISB1
#define LCD_ADDR 0x7c
#define ON 1
#define OFF 0

unsigned char t_rm=0, t_rs=0;   // relay-on counter t_rm:minute, t_rs:second
unsigned char t_ph=0;           // external power-on counter
unsigned int t_ps=0;
unsigned char timer_flag=0, disp_refresh=0;

void pic_init(void)
{
    OSCCON = 0x40;		// INTOSC 1MHz
    ANSEL = 0x09;		// RA3:VSB, RA0:CSNS
    PORTA = 0x00;
    PORTB = 0x12;		// SCL=1, SDA=1
    TRISA = 0x79;		// RA7:RELAY, RA6:ACC, RA5:PUSH-SW, RA4:
                        // RA3:VSB, RA2:, RA1:Ext_Pow, RA0:CSNS
    TRISB = 0x00;		// RB5:LED_R, RB4:SCL, RB1:SDA, RB0:PANAL_POW
    OPTION_REG = 0xff;  // PORT B weak pull up disable

                                // adc setup
    ADCON0 = 0b00000001;	// clock=fosc/2, a/d enable
    ADCON1 = 0b10000000;	// right justified, Vdd/Vss ref

                                // timer1 setup
    T1CON = 0b00000100;		// T1CKPS:1/1 T1OSCEN:F *T1SYNC:F TMR1CS:INT TMR1ON:F
				// timer1 clock freq. is 1MHz/4/1=250KHz
    PEIE = ON;			// all peripheral interrupts enable
    GIE = ON;			// global interrupt enable
    TMR1H = TMR1H_VAL;		// set timer1 interrupt period
    TMR1L = TMR1L_VAL;
    TMR1IE = ON;			// timer1 interrupt enable

    TMR1ON = ON;				// timer1 start
    return;
}

// read adc 'sample' times, return value: 0x0000-0xffff
unsigned int adc_read( unsigned char ch, unsigned char sample )
{
	unsigned int value;	// adc value
	unsigned char i;

	ADCON0 = (unsigned char)( (ch<<3) | 0b00000001);	// right justified, VDD ref, ADON

	// measure 'sample' times for noise reduction
	for( i = 0, value = 0; i < sample; i++ ){
		GO_nDONE = 1;		// ADC start
		while( GO_nDONE )
			continue;
		value += ADRESH*256+ADRESL;
	}
	return value;
}

void i2c_start(){
	SDA = 1;
	SCL = 1;
	SDA = 0;
	SCL = 0;
    return;
}

unsigned char i2c_write(unsigned char d){
	unsigned char i, ack;

 	for (i=0; i<8; i++){
        SDA = (d & 0x80)? 1: 0;
 		SCL = 1;    // send 1bit
 		SCL = 0;
        d <<= 1;    // next bit
	}
	TRIS_SDA = 1;   // SDA input mode
	SCL = 1;
	ack = SDA;
	SCL = 0;
	TRIS_SDA = 0;	// SDA output mode
	return(ack);
}

void i2c_stop(){
	SDA = 0;
	SCL = 1;
    SDA = 1;
    return;
}

void lcd_cmd(unsigned char cmd){
    i2c_start();
    i2c_write(LCD_ADDR);
    i2c_write(0x00);
    i2c_write(cmd);
    i2c_stop();
    __delay_us(30);
    return;
}

void lcd_putc(unsigned char data){
    i2c_start();
    i2c_write(LCD_ADDR);
    i2c_write(0x40);
    i2c_write(data);
    i2c_stop();
    __delay_us(30);
    return;
}

void lcd_puts(unsigned char pos, unsigned char* s){
    lcd_cmd( pos );
    while(*s){
        lcd_putc(*s++);
    }
    return;
}

void lcd_init(){
    TMR1IE = OFF;			// timer1 interrupt disable
    TMR1ON = OFF;			// timer1 stop
    PANEL_POW = 1;      // LCD power on
    __delay_ms(40);
    lcd_cmd(0x38);
    lcd_cmd(0x39);
    lcd_cmd(0x14);
    lcd_cmd(0x70);
    lcd_cmd(0x52);
    lcd_cmd(0x6c);
    __delay_ms(200);
    lcd_cmd(0x38);
    lcd_cmd(0x0c);
    lcd_puts(0x80, (unsigned char*)"SUB BATT");
    lcd_puts(0xc0, (unsigned char*)" MONITOR");
    __delay_ms(1000);
    lcd_cmd(0x01);
    __delay_ms(2);
    lcd_puts(0x84, (unsigned char*)"V");
    lcd_puts(0xc4, (unsigned char*)"A");
    TMR1ON = ON;	// timer1 start
    TMR1IE = ON;			// timer1 interrupt enable
    return;
}

void bin2str4(unsigned int val, unsigned char *b){
    union{
        unsigned long int bd32;
        struct{
            unsigned bd8l : 8;
            unsigned bd8h : 8;
            unsigned bcd0 : 4;
            unsigned bcd1 : 4;
            unsigned bcd2 : 4;
            unsigned bcd3 : 4;
        }bcd;
    }work;
    char i;

    work.bd32 = (unsigned long int)val;
    for(i = 0; i < 16; i++){
        if(work.bcd.bcd0 >= 5)
            work.bcd.bcd0 += 3;
        if(work.bcd.bcd1 >= 5)
            work.bcd.bcd1 += 3;
        if(work.bcd.bcd2 >= 5)
            work.bcd.bcd2 += 3;
        if(work.bcd.bcd3 >= 5)
            work.bcd.bcd3 += 3;
        work.bd32 <<= 1;
    }
    b[0] = (work.bcd.bcd2)? work.bcd.bcd2 + '0': ' ';
    b[1] = work.bcd.bcd1 + '0';
    b[2] = '.';
    b[3] = work.bcd.bcd0 + '0';
    b[4] = 0x00;
    return;
}

void bin2str2(unsigned char val, unsigned char *b){
    union{
        unsigned short int bd16;
        struct{
            unsigned bd8 : 8;
            unsigned bcd0 : 4;
            unsigned bcd1 : 4;
        }bcd;
    }work;
    char i;

    work.bd16 = (unsigned short int)val;
    for(i = 0; i < 8; i++){
        if(work.bcd.bcd0 >= 5)
            work.bcd.bcd0 += 3;
        if(work.bcd.bcd1 >= 5)
            work.bcd.bcd1 += 3;
        work.bd16 <<= 1;
    }
    b[0] = (work.bcd.bcd1)? work.bcd.bcd1 + '0': ' ';
    b[1] = work.bcd.bcd0 + '0';
    b[2] = 0x00;
    return;
}

void __interrupt() timer1_overflow(void)
{
    static unsigned char freerun=0;
    
    TMR1ON = OFF;			// stop timer1
    TMR1IF = OFF;           // clear timer1 interrupt flag
    TMR1H = TMR1H_VAL;      // reset timer1
    TMR1L = TMR1L_VAL;
    TMR1ON = ON;			// restart timer1
    timer_flag = 1;
    freerun++;
    if(!(freerun & 0x07)){        // once per 8 interrupts
        disp_refresh = 1;
        if(t_rm){
            t_rs++;         // count up relay timer
            if(t_rs >= 60){
                t_rs = 0;
                t_rm--;
            }
        }
        if(t_ph){
            t_ps++;         // count up ext.pow timer
            if(t_ps >= 3600){
                t_ps = 0;
                t_ph--;
            }
        }
    }
    return;
}

void main(void) {
    unsigned char state;
    unsigned char s_flag=OFF, s_ctr=16;
    unsigned int batt, csns; // sub battery voltage and charge current
    unsigned char disp[5];  // LCD display buffer

    pic_init();
    lcd_init();

    while(1){
        while(!timer_flag)
            ;
        timer_flag = 0;
        if(disp_refresh){
            disp_refresh = 0;
            batt = (adc_read(3, 72)+0x80)>>8;   //voltage:175mV/V
            bin2str4(batt, disp);
            lcd_puts(0x80, disp);
            csns = adc_read(0, 63)>>6;  //csns:50mV/A
            bin2str4(csns, disp);
            lcd_puts(0xc0, disp);
            bin2str2(t_rm, disp);   // relay on timer
            lcd_puts(0x86, disp);
            bin2str2(t_ph, disp);   // ext.power on timer
            lcd_puts(0xc6, disp);
            if(t_rm){
                RELAY = ON;          // relay power on
                LED_R = ON;
            }else{
                RELAY = OFF;          // relay power off
                LED_R = OFF;
            }
            if(t_ph){
                EXT_POW = ON;   // ext.power on
                LED_BL = ON;    // backlight on
                if(!t_rm){
                    LED_R = ON;           // LED on
                    __delay_ms(10);
                    LED_R = OFF;          // LED off
                }
            }else{
                EXT_POW = OFF;  // ext.power off
                LED_BL = OFF;   // backlight off
            }
        }
        if(!PUSH_SW){                   // switch pushed
            if(s_flag&&s_ctr){          // pushed continuously
                s_ctr--;
            }
            s_flag=ON;
        }else{
            if(s_flag){                 // switch pushed->released
                if(s_ctr){
                    // pushed less than 2 sec.
                    if(!t_rm){  // relay is off?
                        t_rm = RELAY_TIME;       // turn on relay 30min.
                        t_ph = EXT_POW_TIME;     // turn on ext.pow 24h
                    }else{
                        t_rm = 0;               // turn off relay
                    }
                    t_rs = t_ps = 0;
                }else{
                    // pushed more than 2 sec.
                    t_rm = t_rs = t_ph = t_ps = 0; // clear relay & ext_pow. timer
                    lcd_init();         // initialize LCD
                    __delay_ms(100);    // prevent sw. chattering
                }
            }
            s_flag=OFF;
            s_ctr=16;                   // 16*125ms=2sec
        }
        if(ACC){
            t_ph = EXT_POW_TIME;       // hold ext.power 24h after ACC-OFF
            t_ps = 0;
        }
    }

    return;
}

MPLAB X IDE 6と日本語フォルダー名

N-VANの自作サブバッテリーモニターに使っているPIC16F88のブログラムを数年ぶりに修正してMPLAB X IDE6→PICKIT3で書き込もうとしたらBUILD成功後にX IDEが固まってしまいました。

Build後に通常表示されるhexファイルローディング:

Loading code from C:/xxx/dist/default/production/xxx.hex...

この行が表示されずコケているようです。

 

X IDEの各バージョンで試したところX IDE6.00/6.05/6.15/6.20では固まるがX IDE5.50までロールバックすると正常に書き込めました。調べてみると原因は日本語フォルダー名でした。

私はデスクトップPCとノートPCで共有するのに便利なので全てのWindows PCのドキュメントフォルダーをOneDriveに置いているのですが、これが「ドキュメント」という日本語フォルダー名になっており、X IDE6以降では不具合が生じていました。

5.5までは問題無かったので明らかにエンバグなんですが、6.00になって2年以上直っていない所見ると全く直す気ないんでしょうね。また6.20以降はPICKIT3がサポート対象外だそうですから今後のバージョンで直ってもどうしようもない。

しょうがないので「ドキュメント」フォルダーを英字フォルダーへ移動させました。

OneDriveの「ドキュメント」フォルダー

「ドキュメント」フォルダーを右クリックして「プロパティ」を左クリック

「場所」タグ

「場所」タグを開いて、「ドキュメント」の部分を手打ちで「Documents」に修正してOKをクリック

このあと元フォルダーの内容を「Documents」フォルダーへ移動させるか聞いてくるので「はい」を選択:OneDriveが同期するのにしばらくかかります。

Pixel 7aの消費電力問題

5月に買ったPixel 7aが、前の晩にフル充電しても朝になると電池残量75%位まで落ちてしまうようになりました。ネットで検索してみたところ

・フル充電から電池残量0%でシャットダウンするまで放電を2回行うと直る

という書き込みがあったので試してみました。

フル充電してからYoutubeの動画連続再生で放電させたのですが、1回目の放電時は電池残量1%の表示になってからシャットダウンするまで11時間掛かりました。Pixel 7a発売直後のWebレビュー記事ではYoutubeの動画連続再生でフル充電から15時間程度という結果だったので、11時間再生できたという事は電池残量1%の表示でも実際の電池残量は70%位。間違いなく電池残量検出がBugっています。

完全放電を2回行った後は下画像のように待受メインならば1週間以上保つようになりました。

待受時バッテリー消費量

 

OGASAKA SHINとBurtonバインディングの相性問題発生

OGASAKA SHIN 156 2023 / Burton Fish 156 2012

2月に予約したOGASAKA SHIN 156が届きました。Burton Fish 156と並べると

・SHINはFishより少し細い

・実はFish 156は156cmより2~3cm短かった(サバ読んでた?)

・カタログではセットバックがFish:75mm SHIN:10mmだがあまり差がない

さっそくFishで使っていたBurton Cartel X Re:Flex 2020をSHINに移植しようとしたら、ネジの相性問題が発生しました。

Cartel Xに4x4用に添付されているBurton純正M6x16ネジを4本軽く締めた状態ではワッシャーが少し浮いてしまう。

ワッシャーが少し浮いている

ネジを外してみると、どうもインサートホールにネジが底当たりしている。

上:ネジ締め後、底当たり跡あり 下:ネジ締め前

ノギスで測ってみると、OGASAKA SHIN156 のインサートホールの深さは5mmしかないので、ディスクより5mm以上ネジが飛び出ると底当たりします。そこでディスクにM6x16ネジをしっかりとナット止めすると、M6のナット(高さ5mm)から0.8mm位飛び出している。

M6x16ネジをディスクにM6ナット(高さ5mm)で固定

よってこの飛び出した分を削るか、M6x15ネジを使わないと最悪ボードが壊れる。M6x16ネジで0.8mm位飛び出しているので、M6x15.5ではまだ少し当たると思う。

 

<<<ネジを削ります!>>>

まずディスクにM6x16ネジを4本しっかりとナット止めします。このナットから飛び出している部分がなくなればSHINのインサートホールで底当たりしなくなります。

ディスクにM6x16ネジを4本ナット止め

ひっくり返して#120の耐水ペーパーの上に置いて、円を描くようにひたすら削ります。

耐水ペーパーで削る

ナットが少し削れてきたら出来上がり。一部のネジは片当たりしてナットの片側だけが先に削れると思うので、そうなっているネジはドライバーで180度回して再度削ります。1枚(ネジ4本)削るのに15~20分かかったかな。腕が疲れた。年寄りには少々キツい。

ネジ削り完了

軽量化でボードが薄くなっているのは分かるけど、少しは使い勝手やインサートホールの耐久性とかも考えて欲しいところ。現状のインサートホールにネジ単体で回して確認すると最大2.5山しか掛からないみたい。機構部品としてはあと1山は欲しいですね。

N-VAN 車載用ワイヤレスチャージャー自作?

スマホをPixel 7aに変更したのでワイヤレス充電ができるようになりました。そこでN-VANに強引にワイヤレスチャージャーを実装してみました。

通販で1000円で売っていた普通のワイヤレスチャージャーを少し改造して使います。裏返してゴム脚を剥がすと小さなネジが出てきます。

ワイヤレスチャージャー裏:ゴム脚を剥がす

ネジを外して裏蓋を取ると、通販などで良く見かけるワイヤレス充電モジュールが出てきました。充電用コイルは製品の直径の半分位で、その外側は充電コントロール基板部以外は使われておらず空いています。

ワイヤレスチャージャー裏:蓋を外す

この空いている所に100均で売っている「超強力マグネット13mm」をセメダインスーパーXで貼り付けます。磁石を貼り付けた方向が上になります。USBコネクターを左下に出すため磁石貼り付け位置をセンターからずらしています。

チャージャー内にマグネット貼り付け

ダイソーのキッチン収納コーナーで磁石用取り付けパネル(ロング)を買ってきます。これは1mm厚の400系ステンレス:磁性あり ですね。

ダイソー 磁石用取り付けパネル(ロング)

ステンレスパネル(140x30mm)をはさみで縦横とも半分位にカットして(66x15mm位)ヤスリでバリを取り、スマホのケースに貼り付けます。ワイヤレスチャージャーのコイル部にステンレスパネルが掛からないよう注意。磁石で探るとPixel 7aの場合、受電部の中心は背面<G>マークあたり(ほぼスマホ中央)のようです。

ステンレスパネルを貼り付けたスマホケース:Seriaの怪しいストラップ付

スマホ固定用に木枠を作ってワイヤレスチャージャーを裏面からネジ止めし、裏蓋を戻します。ダイソーの水性ペイント(黒)で塗装しています。下の2つの丸い窪みはスピーカー用で、これがないとスマホナビ中に音声案内が聞こえません。その右の切り欠きはストラップ用。

※カーナビとハンズフリーのためにBluetooth接続しているとカーナビの機種によってはナビ音声が聞こえないようです

車載用チャージャー完成

少し長めのステンレスステーでインパネと同じ位の傾きを付けて、適宜ネジ止めして完成。マグネットとステンレスパネルの間に樹脂パネルが挟まっているのでMagSafeのような強力固定ではないですが、周りの木枠があるので通常走行の振動程度では落ちてきません。逆にあまり強く固定されない分スマホの付け外しは楽です。

N-VANに取り付け完了

 

Burtonの節句

Burton Fish 2012 / Fish 3D 2020

端午の節句なのでFish 2012と2020の二本揃です。

2012はとても良かったのですが、2020は3Dという怪しい形状になっていて、パウダーはいいのですが圧雪で全然走らない。コース間の繋ぎの緩斜面で止まりそうになる。2シーズン乗ったのですが今シーズンは2012に戻しました。

来シーズンはOGASAKA SHIN 156なので今から楽しみです。パウダー祈願。