2012年9月30日日曜日

C言語でシリアル通信



ANDROID,LINUX,WINDOWS,MAC
それぞれの環境でUSBシリアル通信(組み込み的にはUART通信)のプログラムを
つくったのでその感想を述べたい。

まず、androidだが、これはLINUXの一部なのだが、ドライバがほとんど対応してない。だからLINUXで/dev/ttyUSBのファイルができるのを確認しているドングル、ケーブルなどをUSBミニとUSBとの変換器経由で接続しても、ほとんど/dev/ttyUSBのファイルができない。これはUSBの変換器の不具合ではなく、androidのカーネルにドライバがないため。これをクリアするためにカーネルのフラグを立ててビルドしなおしてもだめ。理由はandroid appからアクセスするときアクセス権で拒否られるため。結局カーネル、シェル両方書き換えてOSごとインストールする必要あり。それでデバイスファイルができアクセス権も取れ、アプリからアクセス可能となる。

このあたりの顛末は
”google nexusにandroidを再インストール”
でかきましたんで興味がある人は読んで

 ただBluetooth用のUARTのチップを使った装置との通信なら上のようなことをする必要ないらしい。(したことないので分からないが)

次LINUX
一番素直なシリアル通信コードがかける。さすがC言語オリジナルという感じ
QTでコーデイングすればそのままMACでも同じコーデイングが使用可能。上の処理をすればそのままandroidでも動作する。

やり方。まずケーブル、ドングルなどシリアル通信するハードを接続。
ここで /dev/ttyUSB? というファイルが出来るのを確認。
何もほかに差してないなら、/dev/ttyUSB0 です。たぶん。

でC言語コーディングの話に移ります。
QTで私は作成しましたがなんでも同じです。
①ポートオープン

# include <stdio.h>
# include <stdlib.h>
# include <termio.h>
# include <unistd.h>
# include <fcntl.h>
# include <getopt.h>
# include <time.h>
# include <errno.h>
# include <string.h>
#include <sys/time.h>
 #include <unistd.h>

    struct termios TtyAttr;

    int DeviceSpeed = B9600;
    int ByteBits = CS8;
    char DeviceName[32];//="/dev/ttyUSB0";
    fd_set ReadSetFD;

int ncommFd=open(DeviceName, O_RDWR, 0);
    if (fcntl(ncommFd F_SETFL, O_NONBLOCK) < 0)
    {
            Error("Unable set to NONBLOCK mode");
            return ERROR_FCNTL;
    }
    memset(&TtyAttr, 0, sizeof(struct termios));
    TtyAttr.c_iflag = IGNPAR;
    TtyAttr.c_cflag = DeviceSpeed | HUPCL | ByteBits | CREAD|CLOCAL;
    TtyAttr.c_cc[VMIN] = 1;

    if (tcsetattr(ncommFd, TCSANOW, &TtyAttr) < 0)
    {
         Warning("Unable to set comm port");
    }
    FD_ZERO(&g_ReadSetFD);
    FD_SET(ncommFd, ReadSetFD);
    return 0;

②send
write(ncommFd, バッファ, 長さ);
③recv
read(ncommFd, バッファ, 長さ);

最後Windows
WindowsでC言語で上のコードを通しても失敗する(コンパイルエラー)
さらに/dev/ttyUSB0というファイルもできないし。
WINDOWSでシリアルポートの情報は実はレジストリからとりそのあとCreateFileでオープンするのだがそんなことはやってられない。
そこでQtでコーディングする方法をしらべると
qextserialportというものが公開されているのが分かる。
これは超便利なクラスでQtのシリアルアクセスをカプセル化します。
http://code.google.com/p/qextserialport/
そこでこれを使って上記コードを書き換えます

環境整備
上記からファイルを落とし展開
C:\qextserialport-1.2beta1\
に展開したとする。
プロジェクトの*.proに
include(C:\qextserialport-1.2beta1\src\qextserialport.pri)
を一行追加して保存

①ポートオープン
#include "qextserialenumerator.h"
//検索
    QList<QextPortInfo> ports = QextSerialEnumerator::getPorts();
    QString enumName;
    char ch_enumName[20];
    char ch_portname[20];
    char ch_com9[20]="COM9";
    printf("List of ports:\n");
    for (int i = 0; i < ports.size(); i++) {
            printf("port name: %s\n", ports.at(i).portName.toLocal8Bit().constData());
            printf("friendly name: %s\n", ports.at(i).friendName.toLocal8Bit().constData());
            printf("physical name: %s\n", ports.at(i).physName.toLocal8Bit().constData());
            printf("enumerator name: %s\n", ports.at(i).enumName.toLocal8Bit().constData());
            printf("===================================\n\n");
            sprintf(ch_enumName,"%s",ports.at(i).enumName.toLocal8Bit().constData());
            if(strcmp(ch_enumName,"USB")==0)
            {
                sprintf(ch_portname,"%s",ports.at(i).portName.toLocal8Bit().constData());
                if(strcmp(ch_portname,ch_com9))
                {
                    sprintf(portname,"\\\\.\\%s",ch_portname);
                }
                break;
            }else{
                sprintf(portname,"%s",ports.at(i).portName.toLocal8Bit().constData());
            }
    }
//open
    QextSerialPort *port = new QextSerialPort(portname);
    port->open(QIODevice::ReadWrite | QIODevice::Unbuffered);
    qDebug("is open: %d", port->isOpen());
    if(port->isOpen() != 1)
    {
        return;
    }
    port->setBaudRate(BAUD9600);
    port->setFlowControl(FLOW_OFF);
    port->setParity(PAR_NONE);
    port->setDataBits(DATA_8);
    port->setStopBits(STOP_1);
    //set timeouts to 500 ms
    port->setTimeout(0);

②send
port->write(バッファ, ながさ);
③recv
port->read(バッファ, ながさ);

ZIP圧縮のプログラム

今回は再びプログラミングの話

ZIP圧縮ルーチンをDLLなどつかわず自力で書きました。
だからその覚書をのこしておきたい。
以下はvisual studio 2010で行いました。

参考にしたのはこれ
http://hp.vector.co.jp/authors/VA016379/cpplib/zlib.htm
http://www.tnksoft.com/reading/zipfile/arczip.php

まず必要なものとしてZLIB(http://www.zlib.net/)があります。
最初にこれをダウンロード(現在zlib127.zip)します。

次に適当なLIBプロジェクトを作成してその中にzlib127.zipを解凍して
できた中身のルートのファイルを(サブフォルダをの中のもの以外)すべて追加

最後にコンパイルしてLIBをを得ます。このときプリコンパイルヘッダをOFFでしないとエラーになります。
ここではこれをzlib.lib(release),zlibd.lib(debug)とします。

次にこれを使用するアプリを作成します。

適当なDLGベースのアプリを作成、ボタンを一個置きます。

次にzlib.libをこのプロジェクトに追加します。
ファイルをプロジェクトにコピー後
プロジェクトの設定で追加してもいいですが
プロジェクトのファイルに
#ifdef _DEBUG
#pragma comment(lib,"zlibd.lib")
#else
#pragma comment(lib,"zlib.lib")
#endif
と書いたほうが、うざくなくて私はすきです。

次にヘッダー類のコピーですが、
zconf.h
zlib.h
をコピーします。
そのまま使ってもいいわけですが、
http://www.tnksoft.com/reading/zipfile/arczip.php
から拝借した
ziputil.cpp
ziputil.h
を経由して使いました。
結局プロジェクトには
#include "ziputil.h"
とかけばいいです。

さらにZIPのファイル情報を管理するMFCクラスを作成しました

class CZipFileInfo : public CObject
{
public:
    CZipFileInfo();
    virtual ~CZipFileInfo();
    CStringA strpath;
    int buflen;
    unsigned char  *buff;
    __time64_t filetime;
};

typedef CTypedPtrArray< CPtrArray, CZipFileInfo* > CZipFileInfoArray;


これでようやく環境が整ったのでコーデイング出来ます。
2関数作成しました
//ZIPファイル検索
void SearchPath(CString strpath,CString strCur,CZipFileInfoArray &  ZipInfoArry){
    CFileFind FileFind;
    BOOL FndEndJug;


    //-----------------------------
    //検索ファイル名文字列を生成
    CString strSearchFile = strpath + _T("\\*.*") ;
   
    //----------------
    //検索実行
    if(!FileFind.FindFile(strSearchFile))
        return;
   
    FndEndJug = TRUE;
    while(FndEndJug){
       
        //-------
        //検索
        FndEndJug = FileFind.FindNextFile();
       
        // "." , ".."を無視
        if(FileFind.IsDots())
            continue;
       
        //検索結果の判定
        if(FileFind.IsDirectory())
        {
            // サブ・ディレクトリ内を検索するための再帰呼び出し
            SearchPath(FileFind.GetFilePath(),strCur,ZipInfoArry);
        }
        else {
            // 検索結果をリスト・ボックスへ出力
            CString fullpath=FileFind.GetFilePath();
            //TRACE(fullpath+L"\n");
            TCHAR RelativePath[MAX_PATH];
            PathRelativePathTo(RelativePath,strCur, FILE_ATTRIBUTE_DIRECTORY,fullpath,FILE_ATTRIBUTE_ARCHIVE);


            //ファイルデータ取り出し
            struct _stat64i32 st0;
            _wstat(fullpath,&st0);
            FILE * fp0=NULL;
            _tfopen_s(&fp0,fullpath,L"rb");
            unsigned char * buff=new unsigned char [st0.st_size];
            fread(buff,1,st0.st_size,fp0);
            fclose(fp0);


            CZipFileInfo * pInfo=new CZipFileInfo;
            pInfo->strpath=RelativePath;
            pInfo->buflen=st0.st_size;
            pInfo->buff=buff;
            pInfo->filetime=st0.st_mtime;
            ZipInfoArry.Add(pInfo);
        }
    }
}

//圧縮
void CreateZip(CString strpath,CString strZip){
    TCHAR chCur[MAX_PATH];
    GetCurrentDirectory(MAX_PATH,chCur);

    //ファイルをサーチ
    CZipFileInfoArray  ZipInfoArry;
    SearchPath(strpath,chCur,ZipInfoArry);
    //ファイル数
    int nLen=ZipInfoArry.GetCount();
    if(nLen<=0)return;
    // ヘッダ情報の作成
    ZipHeader * zipheader=new ZipHeader  [nLen];
    CentralDirHeader * archeader=new CentralDirHeader [nLen];
    memset(zipheader, 0, sizeof(ZipHeader) * nLen);
    memset(archeader, 0, sizeof(CentralDirHeader) * nLen);


    for(int i=0;i<nLen;i++){
        TRACE(ZipInfoArry[i]->strpath+"\n");
        zipheader[i].signature = ZIPSIG_CENTDIR;    // PK0304
        zipheader[i].needver = 20;                    // Deflate圧縮を利用できるVer
        zipheader[i].comptype = 8;                    // Deflate圧縮

        // ファイル時刻を取得
        __time64_t time64=ZipInfoArry[i]->filetime;
        tm nowtm;
        _localtime64_s(&nowtm,&time64);

        zipheader[i].filedate = GetDosDate(nowtm.tm_year + 1900, nowtm.tm_mon+1, nowtm.tm_mday);
        zipheader[i].filetime = GetDosTime(nowtm.tm_hour, nowtm.tm_min, nowtm.tm_sec);

        // 展開時に用いるファイル名の設定
        zipheader[i].fnamelen = ZipInfoArry[i]->strpath.GetLength();
        zipheader[i].filename = ZipInfoArry[i]->strpath.GetBuffer();

        // ファイルサイズの設定
        zipheader[i].uncompsize = ZipInfoArry[i]->buflen;


        archeader[i].signature = ZIPSIG_ARCHIVEFILE;    // PK0102
        archeader[i].madever = 20;



    }

    FILE *fp=NULL;
    _tfopen_s(&fp,strZip, _T("wb"));
    if(fp == NULL){
        printf("ファイルが開けませんでした。");
        return;
    }


    // 圧縮関連変数の初期化
    const uInt tempsize = 16384*0x50;
    Byte * tempbuf=new Byte [tempsize];
    uInt compsize;

    z_stream zs;
    memset(&zs, 0, sizeof(z_stream));

    deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY);

    // ファイルの位置を一時的に格納する変数
    unsigned int current;


    // アーカイブデータの書き込み
    InitCRC32();
    for(int i=0;i<nLen;i++){
        // データの位置を格納
        archeader[i].headerpos = ftell(fp);

        // zipヘッダの書き込み
        WriteZipHeader(fp, &zipheader[i]);

        // 現在のファイル位置を一時的に取得
        current = ftell(fp);
   
        // zlib構造体をメモリの開放を行わず再初期化する
        deflateReset(&zs);

        // crc32の初期値は0xFFFFFFFFにする
        zipheader[i].crc32 = CRC32_DEFAULT;

        // データの圧縮と書き込み
        compsize = 0;
        zs.avail_in = zipheader[i].uncompsize;
        zs.next_in = (Bytef*)ZipInfoArry[i]->buff;
        do{
            zs.avail_out = tempsize;
            zs.next_out = tempbuf;

            // CRC32値の更新
            //zipheader[i].crc32 = GetCRC32(zs.next_in, zs.avail_in, zipheader[i].crc32);
            zipheader[i].crc32 = GetCRC32(ZipInfoArry[i]->buff,ZipInfoArry[i]->buflen);

            deflate(&zs, Z_FINISH);

            fwrite(tempbuf, 1, tempsize - zs.avail_out, fp);
        }while(zs.avail_out == 0);


        // 圧縮サイズデータの更新
        zipheader[i].compsize = ftell(fp) - current;

        current = ftell(fp);

        // ファイルデータの書き換え
        fseek(fp, archeader[i].headerpos + 14, SEEK_SET);
        fwrite(&zipheader[i].crc32, 1, sizeof(unsigned int), fp);
        fwrite(&zipheader[i].compsize, 1, sizeof(unsigned int), fp);
        fseek(fp, 0, SEEK_END);


        // ヘッダ内容のコピー
        CopyToCentralDirHeader(&archeader[i], &zipheader[i]);
    }

    // zlibメモリの開放
    deflateEnd(&zs);


    // 中央ディレクトリ情報開始位置の格納
    current = ftell(fp);

    // 中央ディレクトリの書き込み
    for(int i = 0; i < nLen; i++){
        WriteCentralDirHeader(fp, &archeader[i]);
    }

    // 終端ヘッダの書き込み
    EndCentDirHeader endheader;
    memset(&endheader, 0, sizeof(EndCentDirHeader));
    endheader.signature = ZIPSIG_ENDCENTDIR;    // PK0506
    endheader.direntry = nLen;
    endheader.diskdirentry = endheader.direntry;
    endheader.startpos = current;
    endheader.dirsize = ftell(fp) - current;
    WriteEndCentDirHeader(fp, &endheader);

    fclose(fp);
    for(int i=0;i<nLen;i++){
        ZipInfoArry[i]->strpath.ReleaseBuffer();
        delete [] ZipInfoArry[i]->buff;
        delete ZipInfoArry[i];
    }

    delete [] tempbuf;
    delete [] zipheader;
    delete [] archeader;
}

で最後にこれを使う部分をボタンの応答関数に書きます。
void CZIPsampleDlg::OnBnClickedZipRun()
{
    //workのフルパスを作成
    TCHAR cbuff[MAX_PATH];
    ZeroMemory(cbuff,sizeof(cbuff));
    GetCurrentDirectory(MAX_PATH,cbuff);
    lstrcat(cbuff,L"\\work");

    CTime tm=CTime::GetTickCount();
    CString filename=tm.Format("%Y-%m-%d %H-%M.zip");
    CreateZip(cbuff,filename);

}
これでカレントのworkフォルダの中身が現在時刻のファイルに圧縮されます。
ここまで読んでくれた人のためにプロジェクトを添付します。
サンプルプロジェクト

2012年9月25日火曜日

ubuntu でリモートデスクトップ

前回”自宅のマシンに外部からアクセスする”
http://kokawa2003.blogspot.jp/2012/09/linux-yahoo-bb-nttyahoobbpc-ipifconfig.html
の時につづきを話すと書いたので、今回はその続きで自宅マシンをリモートデスクトップサーバーにする話をしたい。使うのはfreenx。

何も分からない人のために補足するとこれからどこかの(どこでもいい)Wndows、Linux,MACのパソコンで自宅のlinuxのデスクトップを表示する方法を説明します。

読む必要はないが一応資料は
https://help.ubuntu.com/community/FreeNX

このソフトubuntu maverickの頃まではよく使ってたのだがその後更新が途絶えたため使用をやめてた。
がしかし、ubuntu preciseになって密かに更新再開されてたので、使ってみたら、いい感じなのでここで紹介します。

サーバー側のインストールはこんな感じ
sudo add-apt-repository ppa:freenx-team/ppa
sudo apt-get update
sudo apt-get install freenx
これだけでsshのサーバーも一緒にインストールされてサーバー機能が立ち上がります。

クライアント側
クライアントソフトはWebにあります。
http://www.nomachine.com/download.php

この真ん中近辺の
    NX Client for Windows    
    NX Client for Linux    
    NX Client for Mac OSX    
    NX Client for Solaris

のあたりがそれ。それぞれ自分の環境のものをインストールします。またこれとは別にopennx
というものもあります。(http://opennx.net/

以下にNX Client for Windowsをダウンロードしてインストールしたとして、
 セットアップを述べます。
まずそのクライアントマシンで自宅マシンのURLにアクセスできることをpingなどで確認。
次にサーバ―のURL、ログイン名、パスワードを思い出してください。
で設定は

こんな感じです。上のウインドウのconfigureボタンクリックで下の画面が出ます。このウインドウのhostのところにサーバーのURLを書きます。

ただこのままするとデスクトップの設定がgnomeなのでそれがやな人はcustum->settingでコマンドを/usr/bin/gnome-session --session ubuntu-2d
に変えてください。
でうまく行くと下の絵のような感じで画面が出ます。
windowとデスクトップのサイズが一致しない場合alt+ctrl+F/alt+ctrl+Rを交互に押すと直ります。

あとSSHも一緒にインストールされるので、teratermなどでリモートからSSHでアクセスすることもできます。



※なおこれとほぼ同じ操作感覚のx2go
http://www.x2go.org/
というものもあります。
利点はfreenxと違って音が聞けるということ 。使いやすいほうでどうぞ



2012年9月19日水曜日

ubuntu でevernoteにアクセス

UbuntuでEvernoteにアクセスする方法がわかったので紹介しておきたい。
この手のソフトとしてはnevernoteて言うのがあったのだが、このソフトが
PPAに対応してnixnoteという名前に変わっていた。PPAに対応してるので紹介したい。
まずインストール方法は

 sudo add-apt-repository ppa:vincent-c/nevernote
 sudo apt-get update
 sudo apt-get install nixnote

て感じ。Evernote派は活用されたい。
起動はそのものズバリ nixnoteです。

2012年9月17日月曜日

自宅のマシンに外部からアクセスする

世の中ではLinuxといえばサーバーなのでネットワーク系の記事。
自宅のマシンに外部からアクセスするにはどうすればいいかについて書きたい。
今自宅のマシンはyahoo BBで
「NTTの装置」ー「YAHOO BBのルータ」ー「PC」という接続です。
マシンのIPをifconfigでしらべると
 eth0      Link encap:イーサネット  ハードウェアアドレス 00:1c:c4:2d:3d:34 
          inetアドレス:192.168.3.2  ブロードキャスト:192.168.3.255  マスク:255.255.255.0
          inet6アドレス: 2001:c90:8242:25df:8822:bac6:4b56:b9d1/64 範囲:グローバル
          inet6アドレス: 2001:c90:8242:25df:24f9:b444:9767:2ef3/64 範囲:グローバル
          inet6アドレス: 2001:c90:8242:25df:21c:c4ff:fe2d:3d34/64 範囲:グローバル
          inet6アドレス: fe80::21c:c4ff:fe2d:3d34/64 範囲:リンク
          UP BROADCAST RUNNING MULTICAST  MTU:1500  メトリック:1
          RXパケット:1448780 エラー:0 損失:0 オーバラン:0 フレーム:0
          TXパケット:1073784 エラー:0 損失:0 オーバラン:0 キャリア:0
          衝突(Collisions):0 TXキュー長:1000
          RXバイト:1509515899 (1.5 GB)  TXバイト:141055699 (141.0 MB)
          割り込み:17
で192.168..ではじまるからグローバルIPでないことがわかる
この場合ネットーワークを遡ってどこでグローバルIPがついているか?とその装置をいじれるか?
がカギなのだが、私の場合ルーターがそれに当たる。
これの簡単な調べ方はたとえば
確認君(http://www.ugtop.com/spill.shtml)
で自機のグローバルIPを調べてそのIPを実際にブラウザで開くとルーター設定が開くので
そこからグローバルIPがルータのIPと分かる。

分かったら次にルーターの機能をもちいてPCを外部アクセス可能にする。
これについてはアドレス変換をもちいるべきだろうが、うざいので
ルータのDMZで自分のPCのアドレスを指定した。(セキュリテイ的には最悪)

ここまででとりあえず確認君のIPで自機に辿りつけますが、
いちいちIPを書きとめるのが面倒なので次に無料ダイナミックDNS(DDNS)サービスで名前をつけました。
使ったのはこれ http://ieserver.net/
これの便利なところは再起動するたびに自動的に登録しなおすスクリプトがあるところ
http://ieserver.net/tools.html

これでいちいちIPを覚えなくてもいつも決まった名前(XXXX.dip.jp)で自機にアクセス
出来るようになります。つまり簡易サーバになった。(自機が起動している限り)

一円も使わずにここまで出来るのだからいい世の中になったものだ。
次回からこれを使っていろいろ遊びましょう。
ただメールサーバとかWebサーバとかセキュリテイがどうたらというくだらない話はかんべんしてください。

注意)自機を公開するってことは絶え間なく外部アクセスできるのでDenyHostとかrkHunterとかでやばい穴を
ふさいでおいてください。

2012年9月16日日曜日

UbuntuをUSB-HDDにインストール

このブログはLINUX系のブログを目指しているので(一応)
それ系のまともな記事を書くことがたまにあります。

今回、知り合いがUbuntuをちょっとインストールしたいというので手伝いました。
PCにインストールするのですがちょっとやり方を工夫しましたです。

そのポイントは外付けUSB-HDDを買ってきて、そこにUBUNTUをインストールしたということ。
LINUXは外付けUSB(含USBメモリ)から起動できるってのを知ってたので。確かWindowsはできないはず。
このやりかたのメリットはUSBを外すと無傷の既存OS環境に即座に戻ること。
やはり人間、何も考えたくないときとか全て疑ってしまうときとかあるからね。
完全に切り離せるのが重要だと思うわけですよ。
あとUSB-HDDなので環境を持ち運べるメリットもあります。
だから何かあっても、もってきてもらえるし。
注意点は
①ブートオーダーで外付けUSBを内蔵HDDより上位にすること
(だから出来ない古いPCではこのやり方はだめ)
②GRUBをインストールする場所を外付けUSBにすること
https://wiki.ubuntulinux.jp/UbuntuTips/Install/InstallUbuntuPrecise
https://wiki.ubuntulinux.jp/UbuntuTips/Install/InstallUbuntuPrecise?action=AttachFile&do=get&target=Step3_grub_install.png
の写真の”ブートローダーをインストールするデバイス”をUSB-HDDにすること
の2点くらいでしょうか?
これでUSBを差したらLINUX、USBをはずしたらもとの無傷の環境となります。


次の問題として無線LANのドライバが認識されないってのが、ありましたが
有線LANにて接続後、sudo apt-get update && sudo apt-get upgrade
ののち”追加のドライバ”で解決。

2012年9月7日金曜日

ubuntu のアプリをwebからインストール

ubuntu のアプリをmacやスマートフォンのようにWebから
インストールするサイトがあるのを今日知った。
明らかに狙った作りだ
https://apps.ubuntu.com/cat/