MJHD

エモさ駆動開発

ZOOMERのスピードメータを交換する[2]

前回より…

さて,燃料計の無いズーマーに燃料計付きのデジタルメーターを付けたいという話でしたが,
今日ついにSP武川のマルチ汎用メーターが届きました.

開封の儀

f:id:wait0000:20151227155950j:plainf:id:wait0000:20151229020901j:plainf:id:wait0000:20151229020904j:plain

さっそく取り付けにかかる…がしかし

f:id:wait0000:20151228090207j:plain

なんと,フロント部分のボルトが硬すぎて,手持ちの工具では緩めることができませんでした.

そこで,レンチセットを購入.使ってみて気づきましたが,これすっごい便利です.
めっちゃ楽に力を込めて外すことができました. f:id:wait0000:20151229014314j:plain

ここで友人K氏に助けを依頼し,
友人とバンバン取り外しにかかります. f:id:wait0000:20151228184148j:plainf:id:wait0000:20151228184450j:plain

フューエルセンダの交換

前回も説明したとおり,ズーマー純正のフューエルセンダはアレです.1L以下になった場合のみ反応します.
前回の記事で説明したとおり,AF57のフロートセンサと入れ替え作業を行います. f:id:wait0000:20151228185449j:plainf:id:wait0000:20151228185212j:plain f:id:wait0000:20151228185209j:plain
これがズーマー純正のフューエルセンダ.シンプル…

f:id:wait0000:20151228190458j:plain
バンバン取り外していきます.スターウォーズのロボットみたいな奴が現れます.

f:id:wait0000:20151229020951j:plain
もう素っ裸です.初めてこいつの真の姿を見た….
ケーブル類がかなり奥まった部分に格納されているため,電気系統をいじりたければ前のパネルを外す必要があります.

f:id:wait0000:20151228210035j:plain
さっそく,メータの説明書にしたがってバンバン配線をしていきます.
基本的には,純正メータに接続されている端子を付け替えるだけです.
ただ,純正メータはカプラで接続されているので,これをキボシに張り替える作業が大変でした.

別にカプラ,キボシどっちでも良いと思うんですけど,今回はメータに合わせてキボシに変更しちゃいました.
カプラは全て根本で切断です.(この時友人はひたすら「パイプカット!!パイプカット!!」と叫んでいました.) 追記: 基本、線は切断しない方がいいです。細長い棒でカプラの端子を押しながら引き抜くことで切らずに付け替えられます。

いや,多分もっといい方法あるので各自がんばりましょう.

動作テスト

電源,アース,速度メータ,RPMメータ,などを取り付けた時点で,一度キーをIGNITIONまで回してみます.
f:id:wait0000:20151228210042j:plain

うまく行きましたね.まだ燃料計には接続していないので非表示です.

燃料計を接続

f:id:wait0000:20151228231309j:plain
交換したフューエルセンダからもともと存在するフューエルセンダのカプラを切断(ダメ)し,そこにまさかのハンダ付けしました. (黄色を青/白へ,緑をアース(緑)へ接続します.)
ハンダ付けのゴリ押し感大好きです.(ダメ)
いつガソリンが爆発するか怖かったけどね.

このままだと,燃料警告灯に接続されているだけなので,新メータの燃料計端子,黄色の端子に先ほどハンダ付けした端子を接続します.
中々長いケーブルが必要になります.注意です.

f:id:wait0000:20151229014753j:plain
そしてデジタルメータにて510 Ωの抵抗値を設定することで,正常に燃料の表示が行えました. その他の設定項目は少し曖昧だったので,みなさん適当に設定してください…

問題点

スイッチをオンにした瞬間,一回警告灯が光るのがちょっと怖い.
燃料警告灯もう要らないので切断して別のメッセージにしようかな?

次回,果たしてエンジンはかかるのか・・・?速度が表示されるのか…?

ZOOMERのスピードメータを交換する[1]

f:id:wait0000:20151226022737p:plain
Honda|バイク|ズーマー|フォトライブラリー

ホンダのZOOMER,愛好者も多い点で原付の中では珍しいバイクなのだが,実は1つ大きな問題点がある.

燃料計がない.
なんと,5L入るタンクが残り1Lになった時点でランプが点灯するだけ,という遠出したい時など,かなり怖い仕組みなのだ.
実際,何度もガス欠に見舞われてガソリンスタンドまで押して歩いた…(しんどかった.)

慣れてくると,オドメータと燃費(km/L)で計算できるようになってくるのだが,給油する度にメータの値をメモるのも面倒.
そこで,燃料計をつけるついでに,多機能メータに総取替えすることにした.

俺のZOOMERについて

2006年製のキャブ車,ZOOMERの白です.(中古なので,以前の持ち主がフレームを赤に交換した模様)
走行距離14000kmでございます.まだまだ乗ります.

フューエルセンダの入手

本当にアレだと思うのだが,まず純正の燃料センサー自体が燃料の残量を測ることができない.
1L未満になった時のみ,抵抗値が変化する?(この辺は詳しく調べてない)ようなセンサーらしいのだ.
そこで,一般的には専用の燃料計キット(参考:Amazon.co.jp: キタコ(KITACO) フュエルメーターキット ズーマー 752-1125200: 車&バイク)を使用してセンサーまるごと取り換え,メーターを追加する.

しかし,今回はせっかくなので純正スピードメータも取り外し,汎用多機能スピードメータに取り替えてしまうことにした.
そのためには,まずは燃料の残量に応じて抵抗値の変わるまともなフューエルセンダを入手する必要がある.

基本的にホンダの原付バイクはAF56(Dio)~AF58(ZOOMER)まで共通の形状らしい.
よって,AF56, AF57のフューエルセンダを流用することが出来るらしい.

というわけで,AF57(スマートDio Z4)の中古ガソリンタンクをポチりました. f:id:wait0000:20151222192849j:plain

きったねぇ…
ヤフオクで1500円です….

センサー取り外しf:id:wait0000:20151222193147j:plain

栓が伸びてる蓋部分を押さえて,グリっと回します.
左方向にしかまわらんかった.
少しだけ力が入ります. f:id:wait0000:20151222193233j:plain こんなんで上の蓋が外れます.

f:id:wait0000:20151222193302j:plain

ニュルッとセンサーが出てきます.
新しいタンクに入れる際,サビなどが付着しているとタンクに良くないので,今のうちにフキフキしときましょう.

抵抗値測定

ちゃんと動くのかどうか,テストも兼ねて簡単な回路に接続してみます.

f:id:wait0000:20151222225352j:plain

センサのフロート(黒い部分)の動きに合わせてLEDの明るさが変化しているのが確認できました.

また, 電圧,電流を測定することで最大510Ωの可変抵抗であることもわかりました.

次回,メータの取り付け&センサの交換

次回,スペシャルパーツ武川 スクエアLCD S&Tメーター SQLST1 05-05-0006が届き次第,交換作業を始めようと思います.

YAUC2015メモ

YAUC

YAUC 2015
会津大学宇都宮大学の合同発表会.宇都宮大学,UU-KISS(宇都宮大学感性情報科学研究会,韓国の6人グループではない)主催.

2015/12/19日(土).

13:00より開始

  • 伊藤先生によるUU-KISSの紹介
    年に数回このような研究会を開いているらしく,今回もその1つらしい.
    情報と人間の感覚・感性について研究交流している様子.

13:15より,及川さんご講演

グーグルでChrome開発に関わった及川卓也氏が「Qiita」開発元Incrementsの14人目の社員に | TechCrunch Japan
及川卓也さんが,ご自身の経験から大学生へのメッセージを発表された.

  • 進路はいくらでも変えられる(日本はアメリカに比べて進路を早く決めすぎ)
  • 点と点は繋がるというお話
  • 嫌だからやめるのではなく,新しいことをしたいからやめるという理由であるべき
  • 開発者支援の道へ Qiita
  • スタートアップでは自動化が大事(SlackのBot,Qiitan( DocomoruでBOTと雑に会話する - Qiita参照),CIなど)
  • 日本はなぜか質問回答サイトでは盛り上がらない.よって知識共有サイトへ

14:00あたりより,LT大会

  • 機械学習まとめ.顔認識(識別),Exileの前の方に同級生がいるお話
  • DBF(Database Firewall)を作成したお話,色んな対策法があるらしい
  • Where WareというSNSサイトを,他学科の人が作ったお話
  • 建築業界,製薬分野でも情報系の人材が重宝されているというお話
  • WebGLとは何かというお話,シェーダーのお話
  • スマートハウスをRasPiなどで作成するお話,会津は寒いみたい
  • HSP部屋,実はGolangについても少し語りたかった(タイマーを見たら残り40秒だった)
  • 連鎖ゲームを解く競技プログラミングで6位とったお話
  • Kotlinかわいい.Scalaなども試したけどメソッド数に上限があったらしい.Android開発.
  • FPGAのお話,開発コストのお話
  • デザインのお話,地元の事務所と提携したり雑誌出したり
  • 授業でAV女優をランキングするWebサービスを作ったお話
  • モンゴルのお話,モンゴルではIT分野のスタートアップが増えてる
  • Sketchアプリのすすめ
  • 車の診断情報などを読み取ると楽しいお話
  • 蔵を工場化するお話,ホットプレートは偉大らしい
  • 及川さんの追加ご講演

17:30より懇親会

  • 乾杯
  • 寿司,ローストビーフ,スナック菓子3~4種,ジュース各種

それでも夜は星を連れて

Capo: 1

A E F#m C#m
夕暮れの道を 並んで歩く

D A D E
酔いにまかせて 次の店まで

A E F#m C#m
口をすべらせ 君の瞳が曇る

D A D E
そんなつもりじゃ なかったんだけれど

A E F#m C#m
愛してるなんて 言い訳 みたいだね

D A D E
君がいないと まるでダメなのに

A E F#m C#m
そばにいて欲しい なんて かっこわるいね

D A D E
飲みすぎただけさ 気に留めないで


A E Em
それでも夜は星をつれて なんでもない顔して 旅に誘うよ

D Dm A F#m
トランクの中には 四つ葉のクローバー

D E A
この風が吹くならば

D E A
君と歩いてゆける

D E A
この風が吹くならば

D E F#m
いつの日も何度でも

D E A
君と歩いて行ける

HSPにおいてモジュール内部でbuttonを配置して分岐先でthismodを参照する

よくわからないタイトルになってしまった。

例えば

#module mainScreen id, text

#modinit int screenId
  screen screenId, 640, 480
  button gosub "テスト", *button_click@mainScreen

  text = "Hello World!!"
return

#modcfunc get_text
return text

*button_click
  // mes text とはできない
  mes get_text(thismod) // しかし、これも当然エラー
return

#global

こんな感じのコードを動かしたい。
そこで、若干無理矢理ではあるがマクロで真っ黒な黒魔術を使うことにした。

#module mainScreen id, text

#define global new_mainScreen(%1, %2) %tnew_mainScreen \
    if 0 : *%i {\
        _thismod@mainScreen = %1 :\
        gosub *button_click@mainScreen :\
        return } :\
    %i@mainScreen = *%p1 :\
    newmod %1, mainScreen, %2, %o %o0

#modinit int screenId, var dispatcher
  screen screenId, 640, 480
  button gosub "テスト", dispatcher

  text = "Hello World!!"
return

#modcfunc get_text
return text

*button_click
  mes get_text(_thismod@mainScreen) // 参照できる
return

#global

new_mainScreenマクロにより、button命令で分岐すると自動的に_thismod変数へモジュール変数が代入されるようになる。
ただこのままだと、全てのボタンの処理が同じになってしまうため、それぞれのボタンに対して別々の処理を割り当てられるよう、以下のように書き換えた。

#module mainScreen id, text, events

#define global new_mainScreen(%1, %2) %tnew_mainScreen \
    if 0 : *%i {\
        _thismod@mainScreen = %1 :\
        gosub *button_click@mainScreen :\
        return } :\
    %i@mainScreen = *%p1 :\
    newmod %1, mainScreen, %2, %o@mainScreen %o0

#define ctype register_event(%1) %tregister_event \
    events(%1) = *%i :\
    if 0 : *%o


#modinit int screenId, var dispatcher

    dimtype events, vartype("label"), 64

    screen screenId, 640, 480

    button gosub "mes", dispatcher
    register_event(stat) {
        mes get_text(_thismod@mainScreen)
        
        return
    }

    button gosub "dialog", dispatcher
    register_event(stat) {
        dialog get_text(_thismod@mainScreen)

        return
    }

    text = "Hello World!!"
return

#modcfunc get_text
return text

#modfunc dispatch int objid
    gosub events(objid)
return

*button_click
    dispatch _thismod@mainScreen, stat
return

#global

new_mainScreen main, 0

events変数にオブジェクトIDに対応する処理を登録していく。そして、button_clickラベルにおいてそれぞれの処理を呼び出す関数dispatchを実行する。
何かもっといい方法があったらコメントしてください。

簡単な正規表現エンジンを書いた

学校の課題が「検索,置換を行うプログラムを作成しなさい.オプションとして,独自の機能を盛り込んでもよし」とあったので,簡単な正規表現のエンジンを書いてみた.

対応する文法は以下の3つ.

記号 意味
* 直前の文字の0回以上の繰り返し
x|y xまたはy
xy xとyの連結

具体的な文法規則は以下のサイトにある,「文脈自由文法」(CFG: Context-free Grammer)による定義を使用した.
正規表現エンジンを作ろう (3) (2/3):CodeZine(コードジン)(要会員登録)

CFGはその変数名に対応する関数を作り,それぞれの関数の中で再帰的に他のヘンス名に対応する関数を呼び出すことで,構文木を作成することができる.

a -> 'A' b | c

という定義があったら,


Node* parse(const char* text) {
  char* it = text;
  return a(&it);
}

Node* a(char** it) {
  Node* node;

  if (**it == 'A') {
    node = b();
    (*it)++;
  } else {
    node = c();
  }

  return node;
}

のように書いていけば良い.多分.

先ほど示したサイトではこの後,構文木からNFAを,NFAからDFAを作成することになると思うのだが,今回は以下のサイトを参考にしてVM正規表現エンジンにした.

Mokkosu - VM型の正規表現エンジンを実装する - Qiita

Regular Expression Matching: the Virtual Machine Approach

VM型とは,正規表現を一度専用のバイトコードコンパイルし,そのバイトコードVM(仮想マシン)によって実行することで,判定を行う方法.
以下に今回のVMの構造を示す.

名前 意味
mem メモリ.コンパイルしたバイトコードを格納.
pc プログラムカウンタ.これから実行するメモリ上の命令の位置を表す.
sp 文字列ポインタ.判定を行う文字を示す.

今回はスレッドを分ける必要があったため,pcとspをまとめてContextという構造体に入れた.
その他詳しい定義は先ほど示したリンクを参照してください.


#include <stdio.h>
#include <stdlib.h>

/****************************************
 * 木構造
 ****************************************/

typedef enum {
    ND_Union,
    ND_Star,
    ND_Char,
    ND_Concat,
    ND_Empty
} NodeType;

typedef struct Node {
    NodeType type;
    char value;
    struct Node** chd;
    int chd_count;
} Node;

Node* new_node();
void add_chd(Node*, Node*);
void free_tree(Node*);
void print_tree(Node*, int);

Node* new_node() {
    Node* node = (Node*)malloc(sizeof(Node));
    node->value = 0;
    node->chd = NULL;
    node->chd_count = 0;
    return node;
}

void add_chd(Node* t, Node* p) {
    if (t->chd == NULL) t->chd = (Node* *)malloc(0);
    t->chd = (Node* *)realloc(t->chd, 1 * sizeof(Node*));
    t->chd[t->chd_count++] = p;
}

void print_tree(Node* t, int level) {
    char indent[256];
    int  cnt = -1;

    sprintf(indent, "%% %ds", level);
    printf(indent, "");
    printf("Type:");
    switch(t->type) {
        case ND_Concat:
            printf("Concat");
            break;
        case ND_Union:
            printf("Union");
            break;
        case ND_Star:
            printf("Star");
            break;
        case ND_Char:
            printf("Char");
            break;
        case ND_Empty:
            printf("Epsilon");
            break;
    }
    printf(",Val:%c, Chd:%d\n", t->value, t->chd_count);

    while (++cnt < t->chd_count)
        print_tree(t->chd[cnt], level+1);
}

void free_tree(Node* t) {
    int cnt = -1;

     while (++cnt < t->chd_count)
         free_tree(t->chd[cnt]);
    free(t);
}

/****************************************
 * コンパイラ
 ****************************************/
typedef enum {
    OP_Char,
    OP_Match,
    OP_Jump,
    OP_Split
} Opecode;

typedef struct {
    char opecode;
    char operand1;
    char operand2;
    char dummy;
}__attribute__((packed)) Inst;

void print_code(Inst* code, int len) {
    int pc;
    for (pc = 0; pc < len; pc++) {
        printf("%4d | %02x %02x %02x %02x | ", pc, code[pc].opecode, code[pc].operand1, code[pc].operand2, code[pc].dummy);

        switch (code[pc].opecode) {
            case OP_Jump:
                printf("JMP   %4d", code[pc].operand1);
                break;
            case OP_Split:
                printf("SPLIT %4d, %4d", code[pc].operand1, code[pc].operand2);
                break;
            case OP_Char:
                printf("CHAR  %4c", code[pc].operand1);
                break;
            case OP_Match:
                printf("MATCH");
                break;
        }
        printf("\n");
    }
}

int compile(Node*, Inst**);
void compile_node(Node*, Inst**, int*);

int compile(Node* ast, Inst** out) {
    int pc = 0;

    if (*out != NULL) free(*out);
    *out = (Inst*)malloc(0);

    compile_node(ast, out, &pc);

    *out = (Inst*)realloc(*out, sizeof(Inst) * (pc + 1));
    (*out)[pc].opecode = OP_Match;

    pc++;

    return pc;
}

void compile_node(Node* node, Inst** out, int* pc) {
    int tmp1, tmp2;
    int cnt = -1;

    switch(node->type) {
        case ND_Char:
            *out = (Inst*)realloc(*out, sizeof(Inst) * (*pc + 1));
            (*out)[*pc].opecode  = OP_Char;
            (*out)[*pc].operand1 = node->value;
            (*out)[*pc].operand2 = 0;
            (*pc)++;
            break;
        case ND_Union:
            *out = (Inst*)realloc(*out, sizeof(Inst) * (*pc + 1));
            (*out)[*pc].opecode  = OP_Split;
            (*out)[*pc].operand1 = *pc + 1;

            tmp1 = *pc;
            (*pc)++;

            compile_node(node->chd[0], out, pc);

            *out = (Inst*)realloc(*out, sizeof(Inst) * (*pc + 1));
            (*out)[*pc].opecode  = OP_Jump;
            (*out)[*pc].operand2 = 0;

            tmp2 = *pc;
            (*pc)++;

            compile_node(node->chd[1], out, pc);

            (*out)[tmp1].operand2 = tmp2 + 1;
            (*out)[tmp2].operand1 = *pc;

            break;
        case ND_Star:
            *out = (Inst*)realloc(*out, sizeof(Inst) * (*pc + 1));
            (*out)[*pc].opecode = OP_Split;
            (*out)[*pc].operand1 = *pc + 1;

            tmp1 = *pc;
            (*pc)++;

            compile_node(node->chd[0], out, pc);

            *out = (Inst*)realloc(*out, sizeof(Inst) * (*pc + 1));
            (*out)[*pc].opecode  = OP_Jump;
            (*out)[*pc].operand1 = tmp1;
            (*out)[*pc].operand2 = 0;

            (*pc)++;

            (*out)[tmp1].operand2 = *pc;

            break;
        case ND_Concat:
            compile_node(node->chd[0], out, pc);
            compile_node(node->chd[1], out, pc);
            break;
        case ND_Empty:
            break;
    }
}


/****************************************
 * 構文解析器
 ****************************************/
void parse(char[], Node**);
Node* parse_exp(char**);
Node* parse_subexp(char**);
Node* parse_factor(char**);
Node* parse_seq(char**);
Node* parse_subseq(char**);
Node* parse_star(char**);

void parse(char regexp[], Node** out) {
    char* it;

    if (*out != NULL) free(*out);

    it = regexp;

    *out = parse_exp(&it);
}

Node* parse_exp(char** it) {
    Node* node;

    node = parse_subexp(it);

    if (**it != '\0') printf("unexpected %c, expected EOF\n", **it);

    return node;
}

Node* parse_subexp(char** it) {
    Node* node;
    Node* node1;
    Node* node2;

    node = parse_subseq(it);

    if (**it == '|') {
        (*it)++;
        node1 = node;
        node2 = parse_subexp(it);
        node  = new_node();
        node->type = ND_Union;
        add_chd(node, node1);
        add_chd(node, node2);
    }

    return node;
}

Node* parse_factor(char** it) {
    Node* node;

    if (**it == '(') {
        (*it)++;
        node = parse_subexp(it);
    } else {
        node = new_node();
        node->type = ND_Char;
        node->value = **it;
    }

    (*it)++;

    return node;
}

Node* parse_seq(char** it) {
    Node* node;

    if (**it == '(' || (**it != '|' && **it != ')' && **it != '*' && **it != '\0')) {
        return parse_subseq(it);
    }

    node = new_node();
    node->type = ND_Empty;

    return node;
}

Node* parse_subseq(char** it) {
   Node* node;
   Node* node1;
   Node* node2;

   node = parse_star(it);
   if (**it == '(' || (**it != '|' && **it != ')' && **it != '*' && **it != '\0')) {
       node1 = node;
       node2 = parse_subseq(it);
       node  = new_node();
       node->type = ND_Concat;
       add_chd(node, node1);
       add_chd(node, node2);
   }

    return node;
}

Node* parse_star(char** it) {
    Node* node;
    Node* tmp;

    node = parse_factor(it);

    if (**it == '*') {
        tmp = node;
        node = new_node();
        node->type = ND_Star;
        add_chd(node, tmp);

        (*it)++;
    }

    return node;
}


/****************************************
 * VM
 ****************************************/

typedef struct {
    int pc;
    char* sp;
} Context;

typedef struct {
    Context ctx;
    Inst* mem;
} VM;

VM* new_vm();
int run(VM*);

VM* new_vm() {
    VM* vm = (VM*)malloc(sizeof(VM));

    vm->ctx.pc = 0;
    vm->ctx.sp = 0;
    vm->mem = NULL;

    return vm;
}

int run(VM* vm) {
    Context ctx;
    Inst* inst = vm->mem + vm->ctx.pc;

    switch(inst->opecode) {
        case OP_Jump:
            vm->ctx.pc = inst->operand1 - 1;
            break;
        case OP_Split:
            ctx = vm->ctx;
            vm->ctx.pc = inst->operand1;
            if (run(vm)) return 1;

            vm->ctx = ctx;
            vm->ctx.pc = inst->operand2;
            if (run(vm)) return 1;
            return 0;
        case OP_Char:
            if (*vm->ctx.sp == inst->operand1) {
                vm->ctx.sp++;
            } else {
                return 0;
            }
            break;
        case OP_Match:
            return 1;
    }

    vm->ctx.pc++;

    return run(vm);
}

/****************************************
 * main関数 
 ****************************************/

int main(int argc, char** argv) {
    Node* ast  = NULL;
    Inst* code = NULL;
    VM vm;
    int size;

    vm.ctx.sp = argv[1];
    
    printf("Text: %s, Pattern: %s\n\n", argv[1], argv[2]);

    parse(argv[2], &ast);

    printf("SYNTAX TREE:\n");
    print_tree(ast, 0);

    size = compile(ast, &code);

    printf("COMPILED CODE:\n");
    print_code(code, size);

    vm.mem = code;

    printf("TEST:\n");
    if (run(&vm)) {
        printf("Match!\n");
    }

    free_tree(ast);
    free(code);
}

Roundcubeでsmtps(またはimaps)接続できない

Roundcubeにて,

SMTP Error: SMTP error: Connection failed: Failed to connect socket: fsockopen(): unable to connect to ~~~ (Unknown error)」

と出て接続できない問題が発生した.

どうやら,PHPのあるバージョンからSSL接続のチェックが厳しくなり,self-signedの証明書で問題が起こるらしい.

解決策として,roundcubeのディレクトリ/config/config.inc.phpを編集し,以下のようにする.

$config['smtp_server'] = 'ssl://ホスト';

~~~省略~~~

$config['smtp_conn_options'] = array(
  'ssl' => array(
    'verify_peer' => false,
    'verify_peer_name' => false,
  ),
);

imapsの場合は,smtp_serverではなくdefault_hostの方にssl://をつけ,smtp_conn_optionsimap_conn_optionsに置き換えればOK.