前回のATコマンドで位置情報を獲得するコードは、何度か起動してみると
たまにうまくいく程度のしょぼいものでした。
なので、今回はもう少しまじめに作成です。
起動して、ほったらかしとくと緯度、経度が表示されます。
コードは「続きを読む」から。
でも、このコード。位置情報の取得を一発目は失敗するんだよなぁ。
腹たったので、gotoでループして再度取得をしています。
エラー処理以外でgotoが活躍するCソースを見る機会はそんなにないですよ。必見!(駄目駄目)
■ボーレート(BAUDRATE)の変更
38400よりは115200で動いてほしい・・・。
と言った訳で、BAUDRATEを
#define BAUDRATE B38400から
#define BAUDRATE B115200 へ
変更してみました。
うーん、体感的には早くなったようには思えないです・・・。
■Cソースからコマンド起動
これは普通にsystem()で起動できますね。
Windowsならこんな感じ。
#include
void main()
{
system("notepad");
}
manによるとlinuxでは、
system()は、/bin/sh -c command の形で実行するらしい。
あと、エラー時は-1、/bin/shが実行できなかった場合は127を返す。
つまり、こんな感じかな?
int err;
err = system("/sbin/cardctl resume");
if ( ( err == 127 ) || ( err < 0 ) ) {
perror("resumeに失敗した・・");
exit(-1);
}
busyの時(ioctl(): Device or resource busy)の時は256が帰ってきてますね。
でも、通信でモデムを使っているときは、
次のopen()でfdを獲得できずにexit()されるので、
今回は特に気にしない方針で進みます。
■perror()
システムエラーメッセージ (strerror() 関数) を取得して、エラー出力してくれる。
printf( "%s\n", strerror( errno ) );と同じ意味のよう。
便利なので採用。
■少しの間、眠っていて欲しい。
"/sbin/cardctl resume"した後、間髪いれずにopen()するのはちょっと無謀。
ちょっとの間でいいので待機していて欲しい・・・。
そんなときは、sleep()。
括弧の中身は秒を指定。
unsigned int sleep(unsigned int seconds)
今回は、いたるところで活躍しております。
■モデム以外の時は落ちてほしい。
HDさしていても、何も刺さっていなくても、open()に成功してしまう・・・。
これは困ったと探していると、ManにTIOCMGETというものを見つける。
意味は不明だけど「モデムビット列の状態を取得する。」とあるので
モデムじゃなかったらエラーを返すかなぁとおもって試してみた。
見事に成功。
でも、俺の環境以外でも動くのだろうか・・・。
まぁ、とりあえずこんな感じ。
if ((err=ioctl (fd, TIOCMGET, &err)) < 0)
{
perror("モデムが見つからないよー:");
close(fd);
exit(-1);
}
■select()でタイムアウト
open時にO_NONBLOCKを使っていないので、read()出来ないときにブロックされて
フリーズしてしまいます。
でも、O_NONBLOCKを使うと、今度は訳の分からない文字列を拾って、
僕たちを困らせます。
そんな僕らを助けるのがselect()関数。
ファイルディスクリプタを監視して、read()が出来るまで待ってくれます。
read()で出来なかったら、タイムアウトしてくれます。
まず必要なのは、タイムアウト時間を決めるtimeval構造体。
struct timeval Timeout;
これに時間をいれて上げましょう。一応タイムアウトは5秒で。
Timeout.tv_usec = 0; /* マイクロ秒 */
Timeout.tv_sec = 5; /* 秒 */
※ここでJFにまちがいを発見!
Timeout.tv_usec = 0;はusecですよ。msecではないですよ。
次に必要なのはディスクリプションの集合体fd_set。
fd_set readfs;
select()は複数のディスクリプタを監視できる。
だから集合体に登録してまとめて監視をする。たとえ、1つしかディスクリプタがなくても・・。
まずは、ディスクリプションの集合体を初期化。
FD_ZERO(&readfs);
次に、今回は1つしかないファイルディスクリプタfdをわざわざ登録してあげます。
FD_SET(fd, &readfs);
これで準備完了。めんどくさいです。
準備したものをselect()の引数に入れていきます。
select(fd + 1, &readfs, NULL, NULL, &Timeout)
fd+1となっているところには、ファイルディスクリプタの最大値をいれます。
値は一個しかないのでfd。。数字は0から始まるので+1。合計 fd+1。
selectの戻り値はfd_setに残っているディスクリプタの数が出てくる。
今回はfdを一個しか使っていないので、fdの値がそのまま返ってくる。
ちなみに0が返ってきたときはタイムアウト、0より小さいときはエラー。
まとめると、こんな感じ。
int err;
struct timeval Timeout;
/* 入力ループでのタイムアウト値を設定 */
Timeout.tv_usec = 0; /* ミリ秒 */
Timeout.tv_sec = 5; /* 秒 */
/* select()に使用 */
FD_ZERO(&readfs);
FD_SET(fd, &readfs);
if((err=select(fd + 1, &readfs, NULL, NULL, &Timeout)) <= 0)
{
perror("selectタイムアウト");
close(fd);
exit (-1);
}
■これからの予定
とりあえず、関数に分けようかと。
さすがにgoto文はかっこ悪い。
■問題点
なんか、安定しない。
いろいろと遊んでいると、read()がブロッキングされてコマンドを受け付けなくなる事が・・・。
なのに、cuでつないでみるとうまく動くし。
なぜ?
お分かりの方、情報ください。
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>
#include <sys/ioctl.h>
/* <asm/termbits.h> で定義されているボーレートの設定.これは
<termios.h>からインクルードされる. */
////////////////->修正
//#define BAUDRATE B38400
#define BAUDRATE B115200
/* 適切なシリアルポートを指すように,この定義を変更する.*/
//#define MODEMDEVICE "/dev/ttyS1"
#define MODEMDEVICE "/dev/ttyS3"
////////////////<-修正
#define _POSIX_SOURCE 1 /* POSIX 準拠のソース */
#define FALSE 0
#define TRUE 1
volatile int STOP=FALSE;
main()
{
int fd,c, res;
struct termios oldtio,newtio;
char buf[255];
///////////////////->追加
int err; /*いわゆるエラー用*/
int flag=FALSE; /*無理やりループ用のフラグ*/
struct timeval Timeout; /* select() の タイムアウト用 */
fd_set readfs; /* select() の引数用 */
/*カードをアクティブにする*/
err = system("/sbin/cardctl resume");
if ( ( err == 127 ) || ( err < 0 ) ) {
perror("resumeに失敗した・・");
exit(-1);
}
/*アクティブになるまで、気持ち待機*/
sleep(1);
///////////////////<-追加
/*
読み書きのためにモデムデバイスをオープンする.ノイズによって CTRL-C
がたまたま発生しても接続が切れないように,tty 制御はしない.
*/
fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY );
if (fd <0) {perror(MODEMDEVICE); exit(-1); }
///////////////////->追加
/*モデムかどうか判定している気分*/
if ((err=ioctl (fd, TIOCMGET, &err)) < 0)
{
perror("モデムが見つからないよー");
exit(-1);
}
/* select()のタイムアウト値を設定 */
Timeout.tv_usec = 0; /* ミリ秒 */
Timeout.tv_sec = 5; /* 秒 */
/* select()に使用 */
FD_ZERO(&readfs);
FD_SET(fd, &readfs);
///////////////////-<追加
tcgetattr(fd,&oldtio); /* 現在のシリアルポートの設定を待避させる*/
bzero(&newtio, sizeof(newtio)); /* 新しいポートの設定の構造体をクリアする */
/*
BAUDRATE: ボーレートの設定.cfsetispeed と cfsetospeed も使用できる.
CRTSCTS : 出力のハードウェアフロー制御 (必要な結線が全てされているケー
ブルを使う場合のみ.Serial-HOWTO の7章を参照のこと)
CS8 : 8n1 (8 ビット,ノンパリティ,ストップビット 1)
CLOCAL : ローカル接続,モデム制御なし
CREAD : 受信文字(receiving characters)を有効にする.
*/
newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;
/*
IGNPAR : パリティエラーのデータは無視する
ICRNL : CR を NL に対応させる(これを行わないと,他のコンピュータで
CR を入力しても,入力が終りにならない)
それ以外の設定では,デバイスは raw モードである(他の入力処理は行わない)
*/
newtio.c_iflag = IGNPAR | ICRNL;
/*
Raw モードでの出力
*/
newtio.c_oflag = 0;
/*
ICANON : カノニカル入力を有効にする
全てのエコーを無効にし,プログラムに対してシグナルは送らせない
*/
newtio.c_lflag = ICANON;
/*
全ての制御文字を初期化する
デフォルト値は /usr/include/termios.h を見れば分かるが,コメントに書
いてあるので,ここでは見る必要はない.
*/
newtio.c_cc[VINTR] = 0; /* Ctrl-c */
newtio.c_cc[VQUIT] = 0; /* Ctrl-\ */
newtio.c_cc[VERASE] = 0; /* del */
newtio.c_cc[VKILL] = 0; /* @ */
newtio.c_cc[VEOF] = 4; /* Ctrl-d */
newtio.c_cc[VTIME] = 0; /* キャラクタ間タイマを使わない */
newtio.c_cc[VMIN] = 1; /* 1文字来るまで,読み込みをブロックする */
newtio.c_cc[VSWTC] = 0; /* '\0' */
newtio.c_cc[VSTART] = 0; /* Ctrl-q */
newtio.c_cc[VSTOP] = 0; /* Ctrl-s */
newtio.c_cc[VSUSP] = 0; /* Ctrl-z */
newtio.c_cc[VEOL] = 0; /* '\0' */
newtio.c_cc[VREPRINT] = 0; /* Ctrl-r */
newtio.c_cc[VDISCARD] = 0; /* Ctrl-u */
newtio.c_cc[VWERASE] = 0; /* Ctrl-w */
newtio.c_cc[VLNEXT] = 0; /* Ctrl-v */
newtio.c_cc[VEOL2] = 0; /* '\0' */
/*
モデムラインをクリアし,ポートの設定を有効にする
*/
tcflush(fd, TCIFLUSH);
tcsetattr(fd,TCSANOW,&newtio);
////////////////->追加
/* goto文用のフラグ。またはループの開始 */
at_start:
/*ATコマンドを入力:位置情報開始*/
write(fd , "AT@LBC1\r\n" , 9);
while (1){
if((err=select(fd + 1, &readfs, NULL, NULL, &Timeout)) <= 0)
{
perror("selectタイムアウト");
exit (-1);
}
res = read(fd,buf,255);
buf[res]=0; /* printf で使うため,文字列の終端をセットする */
printf(":%s:%d:\n", buf, res);
if (!strcmp("OK\n", buf)) {
break;
}
}
/*経験的に3秒待機が早く取得できる気がした*/
sleep(3);
/*ATコマンドを入力:位置情報取得*/
write(fd , "AT@LBC?\r\n" , 9);
////////////////<-追加
/*
端末の設定終了.入力を処理するできるようになった.
例では,行の先頭に 'z' を入力することでプログラムを終了させる
*/
while (STOP==FALSE) { /* 終了条件が満たされるまでループ */
/*
255文字以上入力された場合でも,行終端文字が入力されるまでは,プロ
グラムの実行は read でブロックされる.読み込んだ文字数が,実際に入
力されている文字数より少ない場合には,次回の read で残りの文字が読
み込まれる.変数 res には実際に読み込まれた文字数がセットされる.
*/
/////////////////////->追加
/* スパゲティ */
if((err = select(fd + 1, &readfs, NULL, NULL, &Timeout)) <= 0)
{
perror("selectタイムアウト");
exit(-1);
}
res = read(fd,buf,255);
buf[res]=0; /* printf で使うため,文字列の終端をセットする */
printf(":%s:%d\n", buf, res);
if (buf[0] == 'N') flag = TRUE;
if (!strcmp("OK\n", buf)){
if (flag == TRUE){
break;
}else{
goto at_start;
}
}
if(!(strcmp("ERROR\n", buf)))
{
goto at_start;
}
/////////////////////<-追加
}
/* ポートの設定をプログラム開始時のものに戻す */
tcsetattr(fd,TCSANOW,&oldtio);
//////////////////////->追加
/*カードをサスペンドする*/
err = system("/sbin/cardctl suspend");
if ( ( err == 127 ) || ( err < 0 ) ) {
perror("suspendに失敗した・・");
exit(-1);
}
/////////////////////<-追加
}

