2014年12月29日月曜日

ラズベリーパイとしりとりで遊ぶよ

もう年末で、今日は早く仕事が終わったので、
すこしラズベリーパイで遊んでみた。

以前にダウンロードしたことがあった、Wikipediaのデータを使って
なにかできないかと思い、
巨大なWikipediaデータからタイトルとそのよみがな、概要だけを抽出して、データベース化。
それをラズベリーパイに覚えてもらって、「しりとり」でもやってみようと思う。


Wikipediaのデータはここにあって
http://dumps.wikimedia.org/jawiki/latest/

以下のURLに説明があった。
http://www.mwsoft.jp/programming/munou/wikipedia_data_list.html

入手したのは「jawiki-latest-abstract.xml」というもので、
タイトルや概要が記載れている1.5GB程度のファイル。
 xml形式になっており、ちょっと困った。

xml2csv的なものはいろいろありそうだけど、phpの勉強を兼ねて自作。
(その他に、いくつかフィルタしたいこともあったので)

先ほどのファイルはこんな感じ
<title>Wikipedia: 言語</title>
<url>http://ja.wikipedia.org/wiki/%E8%A8%80%E8%AA%9E</url>
<abstract>言語(げんご)とは、コミュニケーションのための記号の体系である。</abstract>
<links> …省略

 これの、<title>のWikipedia:以降〜</title>までを名称として、
<abstract>内を概要とした。
よみがなは、以下のルールで生成した。
1.タイトルがすべてひらがな→タイトルをよみがな
2.タイトルがすべてカタカナ→ひらがなに変換してよみがなとする
3.アブストラクトにタイトル(説明) があれば、説明をひらがなにしてみる
3−1.そのさい、スペースや-・などの区切り文字があったら、そこまでで切り取る

そのほか「〜の一覧」などのまとめページは除外した。
全容がこれ
<>
<?php
    mb_language('Japanese');
    mb_internal_encoding('UTF-8');
    mb_regex_encoding("UTF-8");
    
    //タイトル
    $title_start_string="Wikipedia: ";
    $title_start_string_len = mb_strlen($title_start_string);
    $title_end_string="";
    $title_end_string_len   = mb_strlen($title_end_string);
    //アブスト
    $abst_start_string="";
    $abst_start_string_len = mb_strlen($abst_start_string);
    $abst_end_string="";
    $abst_end_string_len   = mb_strlen($abst_end_string);
    //
    $mode = 0;
    $testval_tit = '';
    $testval_abs = '';
    $testval_kana = '';
    $cnt_k = 0;
    $cnt_r = 0;
    $cnt_ng = 0;
    if($argc > 1){
        try {
            $pdo = new PDO('mysql:dbname=test;host=192.168.4.135', 'username', 'password',array(PDO::MYSQL_ATTR_LOCAL_INFILE => true));
        } catch (PDOException $e) {
            exit('データベースに接続できませんでした。' . $e->getMessage());
        }
        
        $stmt = $pdo->query('SET NAMES utf8');
        if (!$stmt) {
            $info = $pdo->errorInfo();
            exit($info[2]);
        }
        $stmt = $pdo->query('set character_set_database=utf8');
        if (!$stmt) {
            $info = $pdo->errorInfo();
            exit($info[2]);
        }
        
        echo "読み取り開始・・・\n";
        $time_start = microtime(true);
        $fp = fopen($argv[1], "r");
        $temp_file = tempnam(sys_get_temp_dir(), 'Tux');
        $fpw = fopen($temp_file, "w");
        while ($line = fgets($fp)) {
            if($mode == 0){
                //タイトル探し
                $testval_tit = searchStr($line,$title_start_string,$title_start_string_len,$title_end_string,$title_end_string_len);
                if($testval_tit !== ''){
                    //echo "->".$line;
                    //echo "名称=".$testval_tit."\n";
                    $cnt_k = $cnt_k + 1;
                    $mode = 1;
                }
            }elseif($mode == 1){
                //アブスト探し
                $testval_abs = searchStr($line,$abst_start_string,$abst_start_string_len,$abst_end_string,$abst_end_string_len);
                if($testval_abs !== ''){
                    //echo "->".$line;
                    //echo " 概要=".$testval_abs."\n";
                    
                        $testval_kana = seikei2($testval_abs,$testval_tit);
                        if($testval_kana !== ''){
                            /*echo "==============================\n";
                            echo "名称=".$testval_tit."\n";
                            echo " 概要=".$testval_abs."\n";
                            echo " よみがな=".$testval_kana."\n";
                            echo "==============================\n";*/
                            fwrite($fpw,$pdo->quote($testval_kana).",".$pdo->quote($testval_tit).",".$pdo->quote($testval_abs)."\n");
                            $cnt_r = $cnt_r + 1;
                            //if($cnt_r > 1000) break;
                        }else{
                        
                            /*echo "【NG】名称=".$testval_tit."\n";
                            echo "【NG】よみがな=".$testval_kana."\n";
                            echo "【NG】概要=".$testval_abs."\n";*/
                            $cnt_ng = $cnt_ng + 1;
                        }
                    
                    $mode = 0;
                }
            }
            

        }
        fclose($fpw);
        fclose($fp);
        
        $timelimit = microtime(true) - $time_start;
        echo "\n".$timelimit." seconds\n";
        echo "総数:".$cnt_k." 有効数:".$cnt_r." 無効数:".$cnt_ng." ".(100*$cnt_r/$cnt_k)."%\n";
        
        echo "DBに書き込み開始・・・"."'".$temp_file."'\n";
        $time_start = microtime(true);
        
        $stmt = $pdo->query("LOAD DATA LOCAL INFILE ".
                            "'".$temp_file."' REPLACE INTO TABLE `test`.`wiki` ".
                            "FIELDS TERMINATED BY ',' ENCLOSED BY '\'' LINES TERMINATED BY '\n' (`kana`, `name`, `gaiyo`);");
        if (!$stmt) {
            $info = $pdo->errorInfo();
            exit($info[2]);
        }
        
        $timelimit = microtime(true) - $time_start;
        echo "\n".$timelimit." seconds\n";
        
        $pdo = null;

    }
    function seikei2($abs,$title){
        $retval = "";
        //〜の一覧ってやつはいらない
        if(!endsWith($title,"一覧")){
            $t_title = mb_convert_kana(seikei($title),"c");
            if(preg_match("/^[ぁ-ゞー]+$/u",$t_title)){
                //タイトルが全部ひらがなだった
                $retval = $t_title;
            //}elseif(preg_match("/^[ァ-ヶー]+$/u",$t_title)){
            //    //タイトルが全部カタカタだった→ひらがなに変換
            //   $retval = $t_title;
                
            }else{
                $testval_k = searchStr($abs,$title."(",mb_strlen($title)+1,")",1);
                    if($testval_k !== ''){
                    //ひらがなにしてみる
                    $testval_kana = mb_convert_kana(seikei($testval_k),"c");
                    if(preg_match("/^[ぁ-ゞー・]+$/u",$testval_kana)){
                        $retval = $testval_kana;
                    }
                }
            }
        }
        return $retval;
    }
    function seikei($str){
        //空白や-を削除、区切り文字があれば先頭を返す
        $retval = "";
        $str_s = mb_ereg_replace("[-\s\u00A0\u3000・]","",$str);
        //$str_s = mb_ereg_replace("・","",$str_s);
        //$str_s = mb_ereg_replace("-","",$str_s);
        //$str_s = mb_ereg_replace(" ","",$str_s);
        //$str_s = mb_ereg_replace("ー",ー",$str_s);
        $ary = mb_split("[、, (,]",$str_s);
        if(count($ary) > 0){
            $retval = $ary[0];
        }else{
            $retval = $str_s;
        }
        
        return $retval;
    }
    function searchStr($str,$start_str,$start_str_len,$end_str,$end_str_len){
        $retval ="";
        $pos_s = mb_strpos($str, $start_str);
        $pos_e = mb_strpos($str, $end_str);
        if(($pos_s === false) || ($pos_e === false)){
            //みつからず
        }else{
            //echo mb_strlen($str)."->s:".$pos_s."->e:".$pos_e;
            //$retval =  mb_substr($str,$pos_s+$start_str_len,mb_strlen($str) - $start_str_len - $end_str_len-1);
            $retval =  mb_substr($str, $pos_s+$start_str_len, $pos_e - $pos_s - $start_str_len);
        }
        return $retval;
    }
    /**
    * endsWith
    * http://blog.anoncom.net/2009/02/20/124.html
    * 
    * @param string $haystack
    * @param string $needle
    * @return boolean
    */
    function endsWith($haystack, $needle){
        $length = (strlen($haystack) - strlen($needle));
        if( $length <0 data-blogger-escaped-false="" data-blogger-escaped-haystack="" data-blogger-escaped-length="" data-blogger-escaped-needle="" data-blogger-escaped-return="" data-blogger-escaped-strpos="">
これを実行すると・・・
php wikicsv.php jawiki-latest-abstract.xml
読み取り開始・・・
29.402565956116 seconds
総数:943216 有効数:408613 無効数:534603 43.321254092382%
DBに書き込み開始・・・'/tmp/TuxUHAuGL'
768.0825881958 seconds

PCで実行しているので、やはり生成は29秒と早い。
でも、ラズベリーパイに転送するのは、768秒・・・なんとか改善できないかな。
で、中身はこんな感じ


んでもって、サーバー側のスクリプトは、
<>
<?php
        mb_language('Japanese');
        mb_internal_encoding('UTF-8');
        mb_regex_encoding("UTF-8");
        
        $s_talk = htmlspecialchars($_GET['talk'], ENT_QUOTES, "utf-8");
        $result = "";
        
        try {
                $pdo = new PDO('mysql:dbname=test;host=192.168.4.135', 'username', 'password');
        } catch (PDOException $e) {
                talk("データベースに接続できませんでした。");
                exit('データベースに接続できませんでした。' . $e->getMessage());
        }
        
        $stmt = $pdo->query('SET NAMES utf8');
        if (!$stmt) {
            $info = $pdo->errorInfo();
            exit($info[2]);
        }
        $stmt = $pdo->query('set character_set_database=utf8');
        if (!$stmt) {
            $info = $pdo->errorInfo();
            exit($info[2]);
        }
        
        if(strlen($s_talk) == 0){
                talk("WEBサーバーを起動しました。");
        }else{
                $result = "あなたの言葉は、".$s_talk."です。";
                if(preg_match("/^[ぁ-ゞー]+$/u",$s_talk)){
                        //ひらがなだった
                        $stmt = $pdo->prepare("select * from wiki where wiki.kana like :word and wiki.kana not like '%ん' order by rand() limit 1");
                        $stmt->bindValue(':word', mb_substr($s_talk,-1).'_%');
                        $flag = $stmt->execute();
                        if (!$flag) {
                                $info = $stmt->errorInfo();
                                exit($info[2]);
                        }
                        while ($data = $stmt->fetch(PDO::FETCH_ASSOC)) {
                                $result=$result."
私の言葉は".$data['name']."(".$data['kana'].")です。

".$data['gaiyo'];
                                talk("あなたの言葉は、".$s_talk."です。そして、私の言葉は、".$data['kana']."です。");
                        }
                }else{
                        $result=$result."
申し訳ありませんが、すべて「ひらがなで」入力してください。";
                        talk("あなたの言葉は、".$s_talk."です。申し訳ありませんが、すべて「ひらがなで」入力してください。");
                }
                

        }
        
        $pdo = null;
        
        function talk($kotoba){
                exec('echo "'.$kotoba.'" | /home/pi/aquestalkpi/AquesTalkPi -v -f1 -f - | aplay > /dev/null &');
        }
        echo <<< EOD



    
    簡易しりとり
    

 
 

    

RasPiと「しりとり」で遊ぶ!

これは、Wikipediaのダウンロード可能なデータを利用した簡易的な「しりとり」です。
あなたの言葉: {$result}
EOD; ?>
そして、ブラウザからアクセス!

ちょっと楽しいかも。

2014年12月21日日曜日

ラズベリーパイがweb経由でしゃべるよ

しばらくラズベリーパイで遊んでいなかったので、
色々と忘れてしまっている。

年末はいろいろあるね。そして仕事もプライベートも停滞気味・・・
よく、仕事と家庭と趣味の3本柱で、どれか1つでも充実していれば、
やっていけるというけれど、なんとかならないかな。

と、現実逃避なのだけど、最近Mysql+PHP+Javascriptの構成で遊んでいる。
直接仕事には関係ないけど、いつか役立つ日が来るはず。

今日やることは、WEB経由でRasPiを喋らせること
node.jsとphpでそれぞれやってみた。



node.js
ar http = require('http');
http.createServer(
    function(req,res){
        res.writeHead(200,{'Content-Type':'text/html'});
        res.end('Raspberry Pi & Node.js');
    }
).listen(8000);


----------------------------------------------------
var http = require('http');
var fs = require('fs');
var url = require('url');
var exec = require("child_process").exec;

var server = http.createServer();
server.on('request', doRequest);
server.listen(8000);
console.log('Server running!');

var cmd = "echo 'ウェブサーバーを起動しました。' | ./aquestalkpi/AquesTalkPi  -v f1 -f - | aplay";
exec(
    cmd,
    function(err, stdout, stderr) {}
);

 
// リクエストの処理
function doRequest(req, res) {
    fs.readFile('./hello.html', 'UTF-8',
        function(err, data) {
            res.writeHead(200, {'Content-Type': 'text/html'});
            res.write(data);
            res.end();
        });
    if(req.method=='GET') {
        var url_parts = url.parse(req.url,true);
        console.log(url_parts.query);
        if('talk' in url_parts.query){
            cmd = "echo '要求" + url_parts.query['talk']+"' | ./aquestalkpi/AquesTalkPi  -v f1 -f - | aplay";
            exec(
                cmd,
                function(err, stdout, stderr) {}
            );
        }
    }
}

----------------------------------------------------



    
    sample
    

 
 

    

Sample Page

これはNode.jsのサンプルページです。
しゃべる内容:

php
 /dev/null &');
 }else{
  exec('echo '.$s_talk.' | /home/pi/aquestalkpi/AquesTalkPi -v -f1 -f - | aplay > /dev/null &');
 }
 echo <<< EOD


 
  
  AquesTalkのテスト
  
 
 
     

WebからRaspiを喋らせる

WebからAquesTalkPiを制御するサンプル。
しゃべる内容:
EOD; ?>

やっていて困ったこと、phpで外部プログラムを実行しようとしたら、
ALSA lib confmisc.c:768:(parse_card) cannot find card '0'
ALSA lib conf.c:4241:(_snd_config_evaluate) function snd_func_card_driver returned error: No such file or directory
ALSA lib confmisc.c:392:(snd_func_concat) error evaluating strings
ALSA lib conf.c:4241:(_snd_config_evaluate) function snd_func_concat returned error: No such file or directory
ALSA lib confmisc.c:1251:(snd_func_refer) error evaluating name
ALSA lib conf.c:4241:(_snd_config_evaluate) function snd_func_refer returned error: No such file or directory
ALSA lib conf.c:4720:(snd_config_expand) Evaluate error: No such file or directory
ALSA lib pcm.c:2217:(snd_pcm_open_noupdate) Unknown PCM default
aplay: main:682: audio open error: No such file or directory

とエラーが出てしまった。
どうやらapacheの権限では、aplayを実行できないような気がする。
そこで、apacheの実行ユーザーを一般ユーザー(pi)に変えてみた
(参考)http://j-caw.co.jp/blog/?p=1407

/etc/apache2/envvars の
export APACHE_RUN_USER=www-data これをpiに変更

これで動いた!


2014年12月13日土曜日

iModela+eagleでプリント基板作成(動作確認)

ようやく、パターンのチェックが終わり想定外のショート・断線が無いことを確認した。
すぐにでも電源を入れてみたかったけど、
肝心のソフトウェアがまだ・・・。

この基板に載っているのはPIC16F818で、I2Cのスレーブにもなれる。
これをラズベリーパイから操作したいと思っている。
その前段階として、PIC自身がカウントアップするプログラムを書いて、
動作確認を行うことにした。

やはり、Linux(X86_64)環境では開発環境が遅すぎるので、
32bit環境で開発を行った。

/* 
 * File:   main.c
 * Author: adeno
 *
 * Created on 2014/12/13
 */

#include 
#include 
#include 
#include "nana_seg_ptn.h"

/*
 * 
 */
// CONFIG
#pragma config FOSC = INTOSCIO  // Oscillator Selection bits (INTRC oscillator; port I/O function on both RA6/OSC2/CLKO pin and RA7/OSC1/CLKI pin)
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF      // Power-up Timer Enable bit (PWRT disabled)
#pragma config MCLRE = ON       // RA5/MCLR/VPP Pin Function Select bit (RA5/MCLR/VPP pin function is MCLR)
#pragma config BOREN = ON       // Brown-out Reset Enable bit (BOR enabled)
#pragma config LVP = ON         // Low-Voltage Programming Enable bit (RB3/PGM pin has PGM function, Low-Voltage Programming enabled)
#pragma config CPD = OFF        // Data EE Memory Code Protection bit (Code protection off)
#pragma config WRT = OFF        // Flash Program Memory Write Enable bits (Write protection off)
#pragma config CCPMX = RB2      // CCP1 Pin Selection bit (CCP1 function on RB2)
#pragma config CP = OFF         // Flash Program Memory Code Protection bit (Code protection off)

#define _XTAL_FREQ 4000000      //delay用に必要(クロック4MHzを指定)
#define TMR0_CNT 0        //TMR0カウント値設定

static void seg_sel(unsigned char n);

unsigned char keta; //7segの桁
unsigned int num;
unsigned char gcnt;
/*
 *
 */
int main(int argc, char** argv) {

    OSCCON = 0b01100010;
    //PORTA 3-0は7segのd-a 不論理
    //RA4は1桁目、RA6は2桁目 不論理
    //PORTB 3-0は7segのh-e 不論理
    
    //PORTA 0,1,2,3,4,6,7
    //PORTB 0,2,6,7 桁 5
    PORTA = 0b11011111; //PORTAの中身をきれいにする
    TRISA = 0b00000000; //PORTAは 1:入力 0:出力

    PORTB = 0b11100101; //PORTBの中身をきれいにする
    TRISB = 0b00000000; //PORTBは 1:入力 0:出力

    keta = 0;
    num = 0;
    gcnt = 0;

    //TMR0の割り込み初期設定
    //1000ms=0.25us(4MHz)*4*プリスケーラ−256*256
    OPTION_REG = 0b0111; //プリスケーラ値設定0b0111(=256回)
    TMR0 = TMR0_CNT; //TMR0カウント値設定

    INTCONbits.TMR0IE =1; //タイマ割込み許可
    INTCONbits.GIE = 1; //全体割込み許可

    unsigned char ra_bak;

    while(1) {
        //表示を消す
        seg_sel(0);
        //PORTA = 0b01011111;
        __delay_ms(1);

        //表示
        seg_sel(keta);
        //PORTA = 0b01001001;
        //PORTB = 0b00001111;
        //NOP();
        //RA4 = 0;
        __delay_ms(3);
        //桁移動
        keta++;
        if(keta > 5){keta = 1;}



    }


    return (EXIT_SUCCESS);
}


static void interrupt isr(void) { //割り込み関数
    if(INTCONbits.TMR0IF == 1) {  //割込み種がTimer0割込みの場合
        INTCONbits.TMR0IF = 0; //Timer0割り込みフラグクリアー
        TMR0 = TMR0_CNT; //TMR0カウント値設定
        gcnt++;
        if(gcnt > 5){//15){
            num++;
            if(num > 9999){num = 0;}
            gcnt = 0;
        }
    }
}
// =============== セグメントセレクタ    ============
static void seg_sel(unsigned char n){
    //PORTA 0,1,2,3,4,6,7
    //PORTB 0,2,6,7 桁 5

    unsigned char ptn,ra_bak,rb_bak;
    ra_bak = PORTA & 0b00100000;
    rb_bak = PORTB & 0b00011010;

    switch(n){
        default:{ }
        case 0: {
            //初期化
            PORTA = 0b11011111;
            PORTB = 0b11100101;
            break;
        }
        case 1:{
            //セグメント1
            ptn = show_number(num % 10,0);
            if((ptn & 0b00100000) > 0 ){
                PORTB = 0b01100101; //7
            }else{
                PORTB = 0b01000101; //5to7
            }
            
            //PORTB = rb_bak | (0b00001111 & (ptn >> 4));
            //PORTA = 0b01010000 | (0b00001111 & ptn);    //ptn
            //NOP();
            //PORTA = 0b01000000 | (0b00001111 & ptn);    //RA4をL + ptn
            PORTA = ra_bak | (0b11011111 & ptn);
            //PORTA = 0b11000000;
            break;
        }
        case 2:{
            //セグメント2
            ptn = show_number((num / 10)%10,0);
            if((ptn & 0b00100000) > 0 ){
                PORTB = 0b10100101; //6
            }else{
                PORTB = 0b10000101; //5to6
            }
            //PORTB = rb_bak | (0b00001111 & (ptn >> 4));
            //PORTA = 0b01010000 | (0b00001111 & ptn);    //ptn
            //PORTB = 0b11000001; //5と2
            //NOP();
            //PORTA = 0b00010000 | (0b00001111 & ptn);    //RA6をL + ptn
            //PORTA = 0b01000000;
            PORTA = ra_bak | (0b11011111 & ptn);
            break;
        }
        case 3:{
            //セグメント3
            ptn = show_number((num / 100)%10,0);
            if((ptn & 0b00100000) > 0 ){
                PORTB = 0b11100001; //2
            }else{
                PORTB = 0b11000001; //5to2
            }
            //PORTB = rb_bak | (0b00001111 & (ptn >> 4));
            //PORTA = 0b01010000 | (0b00001111 & ptn);    //ptn
            //PORTB = 0b11000001; //5と2
            //NOP();
            //PORTA = 0b00010000 | (0b00001111 & ptn);    //RA6をL + ptn
            //PORTA = 0b01000000;
            PORTA = ra_bak | (0b11011111 & ptn);
            break;
        }
        case 4:{
            //セグメント4
            ptn = show_number((num / 1000)%10,0);
            if((ptn & 0b00100000) > 0 ){
                PORTB = 0b11100100; //0
            }else{
                PORTB = 0b11000100; //5to0
            }
            //PORTB = rb_bak | (0b00001111 & (ptn >> 4));
            //PORTA = 0b01010000 | (0b00001111 & ptn);    //ptn
            //PORTB = 0b11000001; //5と2
            //NOP();
            //PORTA = 0b00010000 | (0b00001111 & ptn);    //RA6をL + ptn
            //PORTA = 0b01000000;
            PORTA = ra_bak | (0b11011111 & ptn);
            break;
        }
    }

}

/*
 * File:   nana_seg_pth.c
 * Author: adeno
 *
 * Created on 2014/11/13
 */

#include "nana_seg_ptn.h"


// =============== 表示パターン ====================
unsigned char show_number(unsigned char n,unsigned char dot){
    static unsigned char ptn = 0b11111111;
    /*
     * 76543210
     * hecagfbd
     */
    switch(n){
        case 0: { //abcdef
            ptn = 0b10001000;
            break;
        }
        case 1: { //bc
            ptn = 0b11011101;
            break;
        }
        case 2: { //abdeg
            ptn = 0b10100100;
            break;
        }
        case 3: { //abcdg
            ptn = 0b11000100;
            break;
        }//hecagfbd
        case 4: { //bcfg
            ptn = 0b11010001;
            break;
        }
        case 5: { //acdfg
            ptn = 0b11000010;
            break;
        }
        case 6: { //acdefg
            ptn = 0b10000010;
            break;
        }
        case 7: { //abc
            ptn = 0b11001101;
            break;
        }
        case 8: { //abcdefg
            ptn = 0b10000000;
            break;
        }
        case 9: { //abcdfg
            ptn = 0b11000000;
            break;
        }
    }

    return ptn;
}

<次回改善>
・数値→7セグパターン出力は汎用的に使えるようにしておく
 今は配線の都合だけでレイアウトしてしまったので、
  RA: 76543210
     7seg:hecagfbd
 みたいなことになっている。。。
 パターンを作る人と出力のために変換する人が必要。

動作確認結果がこちら。
スモーク色のアクリル板を上に載っけておくと表示が見やすい
(というか、板が無いととても見づらい。。。)
カメラで撮るとチラツキが目立つね。



次はI2Cの確認だ−!

2014年12月7日日曜日

iModela+eagleでプリント基板作成(部品実装完了)

朝、会社に行く前とか、帰ってきてからの少しの時間で、少しづつ部品の載せてきた。
今日ようやく完了した。

実装してきて思ったことは、
・銅箔面を素手で触るとすぐに変色してしまう→要手袋
・作業が中断する場合は、密封して極力外気に触れさせない→チャック付きの袋

右側切るの忘れてた・・・。
まだ通電確認していないけど、
 裏側はこんな感じ
き、汚いOrz

次作るときは、
・GNDは熱が逃げやすいので、工夫が必要
・ジャンパーは直線がいい、極力少なくしたい
・チップ部品は意外と楽ちん (今回は1か所しか使っていないけど)
・GND以外の島は削っておきたい:加工時間が長くなってしまうけど、
  はんだが移っちゃうし・・・。

コーティングスプレーしたからもう机上に置きっぱなしで大丈夫なはず・。


2014年11月29日土曜日

iModela+eagleでプリント基板作成(2/2)

前回の続き

ガーバーデータの出力は、いろいろなところで紹介されているので、
キーワードだけ。
gerb274x.cam → パターンデータ
excellon.cam   → ドリルデータ

らしい

生成したガーバーデータを「WINSTAR PCB for iModela」にて読み込む。
切削に使うエンドミルが一覧に無いので、「詳細設定」を選ぶ。
このあたりは、トライアンドエラーで行き着いた値なので、正しくは無いかもしれない。。。
でも大丈夫そうだから、まぁいいか。

「了解」を押すと「切削設定」が表示される
パターン:0.3mm
ドリル穴、カット:0.5mm
剥離面:0.5mm
芯ブレ補正値:0.04mm






「V」をクリック「Vカッター計算」というのが開く

このあたりの数値もトライアンドエラー、でも動くからこれでいいやOrz

もろもろ確認して、OKであれば切削を開始する。
 また「Vカッター計算」
切削速度は10mm/s程度までなら大丈夫みたい。



うりゃー
ガーガーうるせーーー
パターンの切り出しに20分くらい
穴あけに15分くらい

 ところどころ削りきれていないものがあるので、
カッターで微修正。
 今日はここまで。
久しぶりにやったので、一日がかりだ。やれやれ。
でも達成感があるね。

iModela+eagleでプリント基板作成(1/2)

ユニバーサル基板では大変だけど、基板を発注するほど大量には作らない時、
自分でプリント基板を作りたい衝動に駆られた。

感光基板をエッチング液でうんたらかんたらは、
綺麗に作れるのだけど、廃液の処理とか感光時間の 調整とか
ドリルで穴あけとか、ちょっと心が折れたので、
もう少し楽ができないかなと思った。
(もう1年以上前の話だけど。。)

その時に衝動買いしたのが、iModelaである。
http://icreate.rolanddg.com/iModela/DGJ/Japanese/index.html/

iModelaは非金属の加工ができる個人向けの切削加工マシーンなのだけど、
エンドミルをつければ、プリント基板を加工できるのだ。
http://www.pcbmilling.biz/

なかなか値が張るのだけど、いろいろできそうと心が踊り、
衝動買いしたのはもうかなり前。

しばらく厳重に保管しすぎてホコリがかぶっていたが、
これではいけないと思い、再び始めたのである。

前に作ったときは、FT232RLのSSOPのピッチまで作れたので、
今回のデザインルールもコレと同じでクリアランス0.35mmとした。

eagleでデザインルールを指定
チェックすると大量のNG項目がでて、作り直し。。。


その後、
太さを変えるのを忘れていた。
電源は16mil、信号線は12milとした。
直角曲がりは気持ち悪いので、変更した。
GNDのベタパターンを作成し、最終的にはこうなった。

まずはここまで。

2014年11月24日月曜日

Canon MG6330をLinuxで使用する(2/2)

前回は、同一セグメントからの利用だったので、
今回は異なるセグメントからの利用に挑戦する。

ルーターとしてdd-wrt化したあいつがいるので、
なんとか工夫したら使えないかぁ・・・。


  1. 調査
一体、このプリンターはどんなプロトコルで通信しているのか。
そして、install.shで勝手にプリンターを探してくれるけど
何をやっているのか?

まぁググったら一撃なんだけれども、それじゃあつまらないので、

先ほどインストールに成功したノートPCで再度install.shを動かしてみて、パケットキャプチャしてみた。

ARP投げまくってる。
プリンターの電源入れ忘れてた。。
電源を入れて再度キャプチャー

おぉとれた!
Canon BJNPというポート8611のやつですな!
このキーワードをもとにググって(やっぱり)みると
tcpudp8611を使用するみたい。
また、スキャナーの場合には、8612を使うとか使わないとか。


  1. ルーターの設定
    仕組みがわかれば、それに対応してルーターの設定を変更する。
# 192.168.4.1:8611に届いたパケットを192.168.2.6:8611に転送
iptables -t nat -A PREROUTING -m tcp -p tcp --dst 192.168.4.1 --dport 8611 -j DNAT --to-destination 192.168.2.6:8611
iptables -t nat -A PREROUTING -m udp -p udp --dst 192.168.4.1 --dport 8611 -j DNAT --to-destination 192.168.2.6:8611

# 192.168.2.6:8611宛のパケットの送信元は192.168.2.5にする
iptables -t nat -A POSTROUTING -m tcp -p tcp --dst 192.168.2.6 --dport 8611 -j SNAT --to-source 192.168.2.5
iptables -t nat -A POSTROUTING -m udp -p udp --dst 192.168.2.6 --dport 8611 -j SNAT --to-source 192.168.2.5

# 戻りのパケットは許可
iptables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT

上記設定にて動きました!!
あとは、設定の永続化をしないといけないのだけど、それはまた今度。。
今はプリンターが使えた喜びに浸りたい・・・。

Canon MG6330をLinuxで使用する(1/2)

Canon MG6330Linuxで使用する(1/2)


  1. はじまり
我が家のプリンターはCanon MG6330というやつで、
ネットワークプリント対応しており、
WindowsPCやスマホから印刷していた。

そういえば、Linuxから印刷したことがなかったので、今回チャレンジしてみた。
というのも、たまたまLinux向けのドライバーが公開されているのを知ったからだ。


  1. ネットワーク構成
我が家のネットワーク構成は以下のような感じで、
Wimaxルーターに無線LAN機能のついたものを使用している。
この無線LANにスマホやらWindowsPCやらがぶら下がっている。



そして、ルーター代わりにdd-wrt化したルーターをかませてセグメントを分けている。
同じセグメントのノートPCから先述のドライバーを使うのは何も困ることはなかったが、
違うセグメント(192.168.4.0/24系)からプリンターを利用するのがどうにもハマってしまった。



  1. 同一セグメントのノートPCにて
まずは、同一セグメントのノートPCからプリンターを使う場合
先述のリンクからドライバーをダウンロードする。
  
PIXUS MG6330 IJ Printer Driver Ver. 3.80 for Linux
cnijfilter-mg6300series-3.80-1-rpm.tar.gz

rootになって./install.shを実行してみる。
==================================================

Canon Inkjet Printer Driver
Version 3.80
Copyright CANON INC. 2001-2012
All Rights Reserved.

==================================================
実行コマンド = rpm -Uvh ./packages/cnijfilter-common-3.80-1.i386.rpm
エラー: 依存性の欠如:
cups cnijfilter-common-3.80-1.i386 に必要とされています

怒られた。そうだよね。
cupsのインストールして、再度チャレンジ!

==================================================

Canon Inkjet Printer Driver
Version 3.80
Copyright CANON INC. 2001-2012
All Rights Reserved.

==================================================
実行コマンド = rpm -Uvh ./packages/cnijfilter-common-3.80-1.i386.rpm
準備しています... ################################# [100%]
更新中 / インストール中...
1:cnijfilter-common-3.80-1 ################################# [100%]
実行コマンド = rpm -Uvh ./packages/cnijfilter-mg6300series-3.80-1.i386.rpm
エラー: 依存性の欠如:
libpangox-1.0.so.0 cnijfilter-mg6300series-3.80-1.i386 に必要とされています
libpng12.so.0 cnijfilter-mg6300series-3.80-1.i386 に必要とされています
libtiff.so.3 cnijfilter-mg6300series-3.80-1.i386 に必要とされています
実行コマンド = rpm -e cnijfilter-common
警告: ファイル /usr/lib64/cups/filter/pstocanonij: 削除に失敗しました: そのようなファイルやディレクトリはありません
警告: ファイル /usr/lib64/cups/backend/cnijusb: 削除に失敗しました: そのようなファイルやディレクトリはありません
警告: ファイル /usr/lib64/cups/backend/cnijnet: 削除に失敗しました: そのようなファイルやディレクトリはありません

また怒られた。怒られた内容に従い以下の追加インストール。
libpangox-devel
libpangox1.0_0
libpng12-devel
libpng12_0
libtiff-devel

すると[libtiff.so.3 cnijfilter-mg6300series-3.80-1.i386 に必要とされています]とまたまた怒られてしまった。

ググるとlibtiff.so.3が必要な場合には、libtiff.so.4などからシンボリックリンクを張っちゃうのが常套手段らしいので、やってみたが、まだ怒られる。

ムキー!!
心が折れたので、libtiff.so.3をネットから拾ってきた。。

==================================================

Canon Inkjet Printer Driver
Version 3.80
Copyright CANON INC. 2001-2012
All Rights Reserved.

==================================================
実行コマンド = rpm --test -U ./packages/cnijfilter-common-3.80-1.i386.rpm
パッケージ cnijfilter-common-3.80-1.i386 は既にインストールされています。
実行コマンド = rpm --test -U ./packages/cnijfilter-mg6300series-3.80-1.i386.rpm
パッケージ cnijfilter-mg6300series-3.80-1.i386 は既にインストールされています。

#=========================================================#
# プリンターの登録
#=========================================================#
続いて、プリンターの登録を行います。
プリンターを接続して、電源を入れてください。
ネットワーク接続で使用する場合は、プリンターをネットワークに接続してください。
準備が整ったら、Enterキーを押してください。
>

きたー!!
このまま指示に従っていくと無事にプリンターが使えるようになりました!

次は異なるセグメントから使えるようにするぞ!

2014年11月22日土曜日

Mageia4(64bit)+MPLABXでハマる・・・。

久しぶりにeagleで回路図を作成した。
片面+ジャンパー構成を考えていたので、
狙ったサイズに入らなくて苦戦すること4時間。
熱中してしまったので、後に引けなくなったのか・・・。



そして久しぶりにPICマイコンを使った。
今まではWindows環境でMPLABを使用していたが、
Windows環境は仮想化したので、今回はLinux環境にMPLABXをインストールした。
環境:Mageia 4 x86_64

MicrochipのサイトからIDEとXC8をダウンロード

MPLAB® X IDE v2.26
http://www.microchip.com/pagehandler/en-us/family/mplabx/

MPLAB® XC8 Compiler v1.33B
http://www.microchip.com/pagehandler/en-us/devtools/mplabxc/home.html

と、インストールなどは上記リンク先に丁寧に書いてあるのだけど、
困ったことに、遅い。
起動からプロジェクトが開くまでも1分くらいかかるし、
プロジェクトのビルドにも1〜2分かかる。

正直ちょっと待てない。

試しに、Windows7にインストールしてみると超速!
ならばと、32bit版のMageiaで試したら、そっちも超速だった・・・Orz
64bitの所為なのかーーー。

この問題も2.25で対策されていたので関係無かった。
http://microchip.wikidot.com/install:mplabx-lin64

javaのバージョンが古いかと思い、アップデート。
変わんねー。

まてよ・・・。こいつJavaアンインストールしても動いてる!!
ググると、独自で持ってるらしい。
/opt/microchip/mplabx/mplab_ide/etc/mplab_ide.conf

jdkhome="/opt/microchip/mplabx/sys/java/jre1.7.0_67/"

jdkhome="/usr/java/jre1.8.0_25/"
に書き換えてみた。
起動しない。。

ちなみにアンインストールの方法は
http://denethor.wlu.ca/cp316/software/Readme_for_MPLABX_IDE.htm#_Toc324949034
/opt/microchip/mplabx/Uninstall\ MPLAB\ X\ IDE にあるらしい。

うーん。 困った。。。

2014年11月15日土曜日

Buffalo WZR-600DHP2のdd-wrt化 その3

WZR-600DHP2を触る機会があったので、dd-wrtを導入してみた(のつづき)

定番プログラムを実行させてみた。

1.準備

ncursesの入手
生成
CC=arm-none-linux-gnueabi-gcc ./configure --prefix=/home/[自分の作業場所]/usr --host=arm-none-linux-gnueabi --with-shared

そしてmake, make install
usr内のbinと libをコピー

そして、定番プログラム・・・slの入手!
http://www.tkl.iis.u-tokyo.ac.jp/~toyoda/
https://github.com/mtoyoda/sl
すごいよねこれ。

CC=arm-none-linux-gnueabi-gcc
CFLAGS=-O -I/home/[自分の作業場所]/usr/include/ncurses -I/home/[自分の作業場所]/usr/include -L/home/[自分の作業場所]/usr/lib

これでmakeするとslが生成される。
slもbinにコピー

2.いざ実行!


sshからログインすると、環境変数が読み込まれない?ようなので、
手動で・・・(なんか良い方法ないかな)
export TERMINFO=/lib/terminfo

するとこんな感じに実行できる!




Buffalo WZR-600DHP2のdd-wrt化 その2

WZR-600DHP2を触る機会があったので、dd-wrtを導入してみた(のつづき)

前回はdd-wrtのインストールまでを行った。
今回は、簡単なプログラムを実行させてみた。

クロスコンパイルをやってみる。

1.準備

ツールチェーンを入手
http://www.mentor.com/embedded-software/sourcery-tools/sourcery-codebench/editions/lite-edition/

ツールチェーンはいろいろ種類があるみたいだけど、
 有名どころを使った。まずは動くことが大事。

2.スタティックリンクで生成してみる

いきなり難しいことはできないので、
スタティックリンクでやってみる
hello.c

#include <stdio.h>
int main(void)
{
 printf("Hello Wold\n");
 return 0;
}

いざコンパイル!!
arm-none-linux-gnueabi-gcc hello.c -static -o hello.exe

出来上がったhello.exeを600DHP2に転送して試してみる。

できた!

3.ダイナミックリンクで生成してみる

スタティックだけではつまらないので、ダイナミックリンクでもやってみる。
 
そのためには、ライブラリを保存できる環境が欲しい。
前回、SDカードが/jffsに自動マウントされるようにしたので、その領域を使う。

共有ライブラリの 保存先libを自由に変更できるようにするために、
SDカードに一式を保存して、それをメイン(/lib)として利用するようにする。

まずは、
/jffs内にclone/libを作成して、現在のlibの中身を全部コピー!!
ついでにclone/bin も作成して、全部コピー!!

そして、ツールチェーンの共有ライブラリもコピーする
CodeSourcery/Sourcery_CodeBench_Lite_for_ARM_GNU_Linux/arm-none-linux-gnueabi/libc/armv4t/lib
これで、下ごしらえはできた。

dd-wrtの「管理」から 「コマンド実行」を選択し、
起動スクリプトを作成する。
内容としては、
#!/bin/sh
sleep 20
if [ -f /jffs/optware.enable ]; then
mount -o bind /jffs/clone/lib /lib
mount -o bind /jffs/clone/bin /bin
else
exit
fi

があれば大丈夫。
20秒待つのはおまじない。。
 マウントしたSDカードが意図しているものかの簡易的なチェックのために、
/jffs/にoptware.enableをという空のファイルを作っておく。
このファイルがある場合にSDカードのlibとbinを使用するしくみ。

 一度再起動して、下準備完了。

 早速、ダイナミックリンクで生成してみる。

arm-none-linux-gnueabi-gcc hello.c -o hello_d.exe

どや!

次は定番プログラムの実行に挑戦

Buffalo WZR-600DHP2のdd-wrt化 その1

WZR-600DHP2を触る機会があったので、dd-wrtを導入してみた。


どうやらdd-wrt化は実績があるみたいだ。 
USBポートにext3フォーマットのSDをつなげて、そこに各種アプリをインストールしてみる。

※以下自己責任+無線出力はOFFにすること。

1.dd-wrtの入手



動作確認したのは以下のバージョン
Path: Downloads › betas › 2014 › 11-11-2014-r25309 › buffalo_wzr-600dhp2 
公式版ではなくbetaなのか。。

2.インストール


<デバッグ画面からファームアップを行う>
詳細は以下のリンク先が詳しい

インストール後、192.168.1.1にアクセス
ここまではとても簡単にできる!



3.SDを外部ストレージ化


SDカードはext3にてフォーマットした。
600dhp2に刺してみると、自動でマウントされる。
されない場合は「ネットワーク」→「USB」にある
「自動ドライブマウント」を確認する。
表示を「日本語」にしていると、レイアウトが崩れるみたいたので、
「英語」と切り替えながら確認すると良さそう。
自動マウントされたSDカードが/dev/sda1として認識された。
このUUIDを「自動ドライブマウント」の下にある
Mount this Partition to /jffsにコピペする。
これでこのSDカードが常に/jffsにマウントされるようになる。


まずはここまで

2014年11月10日月曜日

ラズベリーパイがICカードの残高をしゃべるよ

ICカードの残高を喋ってくれたら面白いと思ったので、早速やってみた。

ICカードのリーダーにはパソリPaSoRi(RC-S320)を使用した。

1.下準備
 a)libusb-devの インストール
apt-get install libusb-dev

 b)libpafeの インストール
wget http://homepage3.nifty.com/slokar/pasori/libpafe-0.0.8.tar.gz
tar xzvf libpafe-0.0.8.tar.gz
cd libpafe-0.0.8/
./configure
make
make install

 c)環境設定
 このままではrootでしか使えないので
/lib/udev/rules.d/60-libpafe.rulesを作成する

ACTION!="add", GOTO="pasori_rules_end"
SUBSYSTEM=="usb_device", GOTO="pasori_rules_start"
SUBSYSTEM!="usb", GOTO="pasori_rules_end"
LABEL="pasori_rules_start"

ATTRS{idVendor}=="054c", ATTRS{idProduct}=="006c", MODE="0664", GROUP="plugdev"
ATTRS{idVendor}=="054c", ATTRS{idProduct}=="01bb", MODE="0664", GROUP="plugdev"
ATTRS{idVendor}=="054c", ATTRS{idProduct}=="02e1", MODE="0664", GROUP="plugdev"

LABEL="pasori_rules_end"


そして、
udevadm control --reload-rules
これで、下準備はおしまい。

2.自作プログラムの実行
 動作確認として/libpafe-0.0.8/tests/内にsuica_zandaka.cを作成。

#include 
#include 
#include "libpafe.h"

int main(void){
 /*スイカの残高を表示する*/
 pasori* p;
 felica* f;
 int i;
 int m;
 int wk, wk2;
 uint8 b[16];
 uint8 idm[16];
 
 felica_block_info fbi;
 fbi.service = 0x090f;
 fbi.mode = 0;
 fbi.block = 0;
 
 printf("ICカードの残高は、");
 p = pasori_open();
 if(!p){
  fprintf(stderr,"error\n");
  exit(-1);
 }
 pasori_init(p);
 f = felica_polling(p,FELICA_POLLING_ANY,0,0);
 //printf("read test\n");

 felica_get_idm(f, idm);
  
        // IDm文字列を作成stdlib
 //printf("IDm = %02x%02x%02x%02x%02x%02x%02x%02x\n", idm[0], idm[1], idm[2], idm[3], idm[4], idm[5], idm[6], idm[7]);

 for(i=0;i!=32;i++){
  if(!felica_read(f,&i,&fbi,b)){
   /*printf("[%02d] : ",i);
                 printf("%02X ",b[0]);
   printf("%02X ",b[1]);
                 printf("%02X",b[2]);
                 printf("%02X",b[3]);
                 printf("%02X",b[4]);
                 printf("%02X ",b[5]);
                 printf("%02X-%02X -> ",b[6],b[7]);
                 printf("%02X-%02X      ",b[8],b[9]);
                 printf("%02X%02X - % 5d Yen        ",b[10],b[11],b[11]*256+b[10]);
                 printf("%02X%02X%02X%02X\n",b[12],b[13],b[14],b[15]);*/
   //printf("[%02d] : ",i);
   //printf("%d ",b[0]);
   if(((int)b[1] > 0) || ((int)b[2] > 0)){
     /*printf("%02X ",b[0]);
     printf("%02X ",b[1]);
     printf("%02X",b[2]);
     printf("%02X ",b[3]);
     wk=b[4]>>1;
     wk=wk&0x7F;
     printf("%02d/",wk);
     wk=b[4]&0x01;
     wk=wk<<3 data-blogger-escaped-wk2="b[5]">>5;
     wk2=wk2&0x07;
     wk=wk+wk2;
     printf("%02d/",wk);
     wk=b[5]&0x1F;
     printf("%02d ",wk);
     printf("%02X-%02X -> ",b[6],b[7]);
     printf("%02X-%02X      ",b[8],b[9]);*/
     printf("%6d円です。\n",b[11]*256+b[10]);
     pasori_close(p);
     return 0;
     //printf("%02X%02X%02X%02X\n",b[12],b[13],b[14],b[15]);
   }
  }
 }
 printf("読み取れませんでした。\n");
 pasori_close(p);
 return 0;
}



実行ファイルの作成はこんな感じ
gcc -DHAVE_CONFIG_H -I. -I..    -I../src -I../ -g -O2 -MT suica_zandaka-suica_zandaka.o -MD -MP -MF .deps/suica_zandaka-suica_zandaka.Tpo -c -o suica_zandaka-suica_zandaka.o `test -f 'suica_zandaka.c' || echo './'`suica_zandaka.c
mv -f .deps/suica_zandaka-suica_zandaka.Tpo .deps/suica_zandaka-suica_zandaka.Po
/bin/bash ../libtool --tag=CC   --mode=link gcc -I../src -I../ -g -O2   -o suica_zandaka suica_zandaka-suica_zandaka.o ../src/libpafe.la


プログラムの出力をAquesTalkPiにしゃべってもらう

./suica_zandaka | ../../aquestalkpi/AquesTalkPi  -v f1 -f - | aplay

こんな感じ


参考