BrotherHL2140用プリントサーバーの構築
Brotherのプリンタ HL2140をネットワークを介して利用したかったので,プリントサーバーを構築してみました.
サーバー側のOSはLinux(CentOS 5.5)を利用しています.
まず,yumを使ってcupsをインストールします.
$ su - パスワード:(rootのパスワード) # yum -y install cups # /etc/rc.d/init.d/cups start # chkconfig cups on
次にメーカーが提供しているプリンタドライバ等をインストールします.
こちらからrpmファイルを得ることができます.必ずリンク先の同意をした後,次のコマンドを実行してください.
# mkdir -p /root/rpm # cd /root/rpm # wget "http://solutions.brother.co.jp/support/os/linux/dlf_lpr/brhl2140lpr-2.0.2-1.i386.rpm" # wget "http://solutions.brother.co.jp/support/os/linux/dlf_cupswrapper/cupswrapperHL2140-2.0.2-1.i386.rpm" # rpm -ivh brhl2140lpr-2.0.2-1.i386.rpm # rpm -ivh cupswrapperHL2140-2.0.2-1.i386.rpm
後はcupsのWebインタフェイスから作業ができますが,localhostからのアクセスしか許可されていません.
そこで,SSHポートフォワーディング機能を利用して設定を行います.
TeraTermの場合は,上のメニューバーから,設定→SSH転送を選択し,「追加」ボタンを押して次のようにダイアログを埋めます.

※その他のターミナルソフトを利用している場合は,各自調べてみてください.
ポートフォワーディングを有効にした後は,次のURLにブラウザからアクセスできると思います.
http://localhost:631/
早速設定を行います.
下図のように管理タブから,チェックボックス「このシステムに接続されている公開済みプリンタを共有」,「リモート管理を許可」をオンにします.

「設定の変更」ボタンを押すとサーバーが再起動されると思います.
サーバー再起動後は,ポートフォワーディングなしでアクセスできるようになります.
http://[サーバーIPアドレス]:631/
ここからはプリンタの追加を適当にすれば良いだけです.
Webインタフェイスなので簡単だと思います.
認証を求められた場合は,rootの情報を入力してください.
設定後はプリンタタブから以下のように確認できると思います.

テストページを印刷してみます. Printer Test Page というタイトルの紙が印刷されれば成功です.
次にWindows側の設定をします.
あらかじめ,HL2140のドライバをインストールしておく必要があると思います.
(今まではUSBを利用して印刷していたため記憶が曖昧です)
コントロールパネルから「プリンタとFAX」を選択し,左のメニューから「プリンタのインストール」を選択して,ウィザードを開きます.
プリンタの指定で,http://[サーバーIPアドレス]:631/printers/(プリンタ名) とします.
後は製造元を「Brother」, プリンタを「Brother HL-2140 series」として選択し,OKを押すことで設定が完了します.
以上でプリンタをネットワークを介して利用できるようになると思います.
念のため,Windows上からもテスト印刷してみてください.
Windows環境でのシリアル通信
シリアル通信を使うことで手軽にパソコンとマイコン間で通信が行えます.
パソコン側の環境ではTeraTermなどのターミナルソフトウェアを利用するのも手軽で良いですが,
通信データがテキストデータとなってしまうため,処理速度やメモリー節約の観点からは不利です.
そのような時は専用のモニタリングソフトウェアなどの開発も検討するべきでしょう.
ということで,WindowsのRS232C通信ソフトウェアを書いてみました.
このソフトウェアは通信にスレッドを用いることによって受信待ちブロッキングを回避しています.
serial.h
#ifndef _SERIAL_H_ #define _SERIAL_H_ typedef struct _TAG_SERIAL* serial_t; /* 関数プロトタイプ */ serial_t serial_create(char *pname, unsigned int baud); void serial_delete(serial_t obj); unsigned int serial_send(serial_t obj, unsigned char *buf, unsigned int size); unsigned int serial_recv(serial_t obj, unsigned char *buf, unsigned int size); unsigned int serial_recv_length(serial_t obj); #endif /* _SERIAL_H_ */
serial.c
#include <windows.h> #include <stdlib.h> #include "fifo.h" #include "serial.h" /* 1つのシリアル通信に関するデータ構造 */ struct _TAG_SERIAL { // 通信関係 HANDLE handle; DCB dcb; // スレッドに関して HANDLE thread_handle; DWORD thread_id; BOOL thread_active; CRITICAL_SECTION cs_send; CRITICAL_SECTION cs_recv; // FIFO fifo_t *q_recv; fifo_t *q_send; // その他 char *pname; char *msg; }; /* プロトタイプ */ DWORD WINAPI serial_thread(LPVOID param); /* 1次バッファ */ #define SERIAL_TMP_BUFSIZE 128 /* シリアル通信を開始 */ serial_t serial_create(char *pname, unsigned int baud) { serial_t obj; COMMTIMEOUTS timeout; // インスタンスメモリ確保 obj = (serial_t) malloc(sizeof(struct _TAG_SERIAL)); if ( obj == NULL ) return NULL; ZeroMemory(obj,sizeof(struct _TAG_SERIAL)); obj->pname = pname; // COMポートのハンドルを取得 obj->handle = CreateFile(pname,GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL/*|FILE_FLAG_OVERLAPPED*/,NULL); if ( obj->handle == INVALID_HANDLE_VALUE ) { free(obj); return NULL; } // COMポートの通信設定 GetCommState(obj->handle, &obj->dcb); obj->dcb.BaudRate = baud; if ( SetCommState(obj->handle, &obj->dcb) == FALSE ) { free(obj); return NULL; } // COMポートのタイムアウト設定 ZeroMemory(&timeout,sizeof(COMMTIMEOUTS)); timeout.ReadIntervalTimeout = MAXDWORD; if ( SetCommTimeouts(obj->handle, &timeout) == FALSE ) { free(obj); return NULL; } // FIFOメモリ確保 obj->q_send = fifo_create(); obj->q_recv = fifo_create(); if ( obj->q_send == NULL || obj->q_recv == NULL ) { fifo_delete(obj->q_send); fifo_delete(obj->q_recv); free(obj); return NULL; } // スレッド開始 InitializeCriticalSection(&obj->cs_recv); InitializeCriticalSection(&obj->cs_send); obj->thread_active = TRUE; obj->thread_handle = CreateThread(NULL,0,serial_thread,(LPVOID *)obj,0,&obj->thread_id); if ( obj->thread_handle == NULL ) { DeleteCriticalSection(&obj->cs_recv); DeleteCriticalSection(&obj->cs_send); fifo_delete(obj->q_send); fifo_delete(obj->q_recv); free(obj); return NULL; } return obj; } /* シリアル通信を停止 */ void serial_delete(serial_t obj) { DWORD thread_state; // スレッドを停止 obj->thread_active = FALSE; do { Sleep(1); GetExitCodeThread(obj->thread_handle,&thread_state); } while (thread_state == STILL_ACTIVE); DeleteCriticalSection(&obj->cs_send); DeleteCriticalSection(&obj->cs_recv); // 通信ポートを閉じる CloseHandle(obj->handle); // メモリー領域の解放 fifo_delete(obj->q_send); fifo_delete(obj->q_recv); free(obj); } /* シリアル通信スレッド */ DWORD WINAPI serial_thread(LPVOID param) { serial_t obj = (serial_t) param; BYTE recv_buf[SERIAL_TMP_BUFSIZE]; BYTE send_buf[SERIAL_TMP_BUFSIZE]; DWORD recv_len; DWORD send_len,send_size; BOOL ret; BOOL recv_hold = FALSE; BOOL send_hold = FALSE; while ( obj->thread_active ) { // 受信 if ( recv_hold == FALSE ) { ret = ReadFile(obj->handle, recv_buf, sizeof(recv_buf), &recv_len, NULL); if ( ret == FALSE ) { obj->msg = "ReadFile failed."; break; } if ( recv_len ) recv_hold = TRUE; } else if ( TryEnterCriticalSection(&obj->cs_recv) ) { recv_len -= fifo_write(obj->q_recv, recv_buf, recv_len); LeaveCriticalSection(&obj->cs_recv); if ( recv_len != 0 ) obj->msg = "q_recv is fully filled. (>_<)"; recv_hold = FALSE; } // 送信 if ( send_hold ) { ret = WriteFile(obj->handle, send_buf, send_size, &send_len, NULL); if ( ret == FALSE ) { obj->msg = "WriteFile failed."; break; } if ( send_size != send_len ) obj->msg = "WriteFile spilled some of q_send."; send_hold = FALSE; } else if ( TryEnterCriticalSection(&obj->cs_send) ) { send_size = fifo_read(obj->q_send, send_buf, sizeof(send_buf)); LeaveCriticalSection(&obj->cs_send); send_hold = TRUE; } Sleep(1); } obj->thread_active = FALSE; ExitThread(TRUE); return 0; } /* 送信する */ unsigned int serial_send(serial_t obj, unsigned char *buf, unsigned int size) { unsigned int ret; EnterCriticalSection(&obj->cs_send); ret = fifo_write(obj->q_send, buf, size); LeaveCriticalSection(&obj->cs_send); return ret; } /* 受信する */ unsigned int serial_recv(serial_t obj, unsigned char *buf, unsigned int size) { unsigned int ret; EnterCriticalSection(&obj->cs_recv); ret = fifo_read(obj->q_recv, buf, size); LeaveCriticalSection(&obj->cs_recv); return ret; } /* 受信したバイト数を取得 */ unsigned int serial_recv_length(serial_t obj) { return fifo_length(obj->q_recv); }
以上が通信部です.
バッファリングのためにFIFOを実現するコードも用います.
fifo.h
#ifndef _FIFO_H_ #define _FIFO_H_ #define FIFO_BUFSIZE 1024 /* FIFOデータ構造 */ typedef struct _TAG_FIFO { unsigned char buf[FIFO_BUFSIZE]; unsigned int size; unsigned int read; unsigned int write; }fifo_t; /* プロトタイプ */ fifo_t *fifo_create(void); void fifo_delete(fifo_t *obj); unsigned int fifo_write(fifo_t *obj, unsigned char *buf, unsigned int size); unsigned int fifo_read(fifo_t *obj, unsigned char *buf, unsigned int size); unsigned int fifo_length(fifo_t *obj); #endif /* _FIFO_H_ */
fifo.c
#include <stdlib.h> #include "fifo.h" /* インスタンス生成 */ fifo_t *fifo_create(void) { fifo_t *obj = (fifo_t *) malloc(sizeof(fifo_t)); if ( obj == NULL ) return NULL; obj->read = obj->write = 0; obj->size = FIFO_BUFSIZE; return obj; } /* インスタンス消去 */ void fifo_delete(fifo_t *obj) { free(obj); } /* データを書き込む. 戻り値:書き込めたバイト数 */ unsigned int fifo_write(fifo_t *obj, unsigned char *buf, unsigned int size) { unsigned int ret = 0; unsigned int next = (obj->write + 1) % obj->size; while ( next != obj->read && ret < size ) { obj->buf[obj->write] = buf[ret]; obj->write = next; next = (obj->write + 1) % obj->size; ret++; } return ret; } /* データを読み込む.戻り値:読み込めたバイト数 */ unsigned int fifo_read(fifo_t *obj, unsigned char *buf, unsigned int size) { unsigned int ret = 0; while ( obj->read != obj->write && ret < size ) { buf[ret] = obj->buf[obj->read]; obj->read = (obj->read + 1) % obj->size; ret++; } return ret; } /* 格納されているデータ数を取得する */ unsigned int fifo_length(fifo_t *obj) { return (obj->size + obj->write - obj->read ) % obj->size; }
以上のコードでシリアル通信を実現できます.
PC側でエコーを実現するのであれば,以下のようなコードで実現できると思います.
#include <stdio.h> #include <windows.h> #include <conio.h> #include <stdlib.h> #include "serial.h" int main (void) { serial_t obj = serial_init("COM1",9600); unsigned char buf[128], len; if ( obj == NULL ) { fprintf(stderr,"オブジェクト生成に失敗"); return EXIT_FAILURE; } while (1) { len = serial_recv(obj,buf,sizeof(buf)); if (len) serial_send(obj,buf,len); Sleep(1); if ( kbhit() ) break; } serial_delete(obj); return EXIT_SUCCESS; }
AVRマイコンを使ったシリアル通信
マイコンとPCでシリアル通信をする実験をしました.
PCとAVRマイコンの接続はUSB-RS232C変換ケーブルを利用し,
レベル変換IC(max232等)を通じてマイコンのUSART(UART)ポートに接続します.
変換ケーブル,IC共に秋月電子で手に入ります.
今回の実験に利用したマイコンはAtMega644というものですが,C言語を利用してソフトウェアを作成したため,
USARTを内蔵しているAVRマイコンであれば,ほぼ同じコードで動作すると思います.
開発環境としてWinAVRとAVRlibCを利用しました.
エコープログラムは以下に示すように記述することができると思います.
メインループ内でビジーループを負荷としているのは,
serial_send, serial_recv関数はアトミック操作を行っているため,
間を空けず絶えずアクセスするとデータが送受信できないなどの問題が生じてしまうためです.
main.c
#include <avr/io.h> #include <util/delay.h> #include "serial.h" int main (void) { uint8_t buf[16], n; serial_init(9600); while (1) { n = serial_recv(buf,sizeof(buf)); if (n) serial_send(buf,n); _delay_ms(1); // 仮想負荷 } serial_end(); return 0; }
serial.h
#ifndef _SERIAL_H_ #define _SERIAL_H_ void serial_init(uint16_t baud); void serial_end(void); uint8_t serial_send(uint8_t *buf, uint8_t size); uint8_t serial_recv(uint8_t *buf, uint8_t size); #endif /* _SERIAL_H_ */
serial.c
#include <avr/io.h> #include <avr/interrupt.h> /* 設定 */ #define SERIAL_SYS_CLOCK 20e6 #define SERIAL_BAUDRATE 9600 #define SERIAL_RX_BUFSIZE 64 #define SERIAL_TX_BUFSIZE 64 /* 割り込み制御マクロ */ #define SERIAL_TXIE_ENABLE() UCSR0B |= _BV(UDRIE0) #define SERIAL_TXIE_DISABLE() UCSR0B &=~_BV(UDRIE0) #define SERIAL_RXCIE_ENABLE() UCSR0B |= _BV(RXCIE0) #define SERIAL_RXCIE_DISABLE() UCSR0B &=~_BV(RXCIE0) /* 受信FIFO */ volatile struct { uint8_t buf[SERIAL_RX_BUFSIZE+1]; uint8_t read; uint8_t write; } serial_rx; /* 送信FIFO */ volatile struct { uint8_t buf[SERIAL_TX_BUFSIZE+1]; uint8_t read; uint8_t write; } serial_tx; /* シリアル通信の開始 */ void serial_init(uint16_t baud) { // キューのリセット serial_rx.read = serial_rx.write = 0; serial_tx.read = serial_tx.write = 0; // USARTレジスタの設定 UCSR0A = 0; UCSR0B = _BV(RXCIE0) | _BV(RXEN0) | _BV(TXEN0); UCSR0C = _BV(UCSZ01) | _BV(UCSZ00); UBRR0 = (SERIAL_SYS_CLOCK / 16) / baud - 1; // 全割り込み許可 sei(); } /* シリアル通信の停止 */ void serial_end(void) { UCSR0A = UCSR0B = UCSR0C = 0; } /* データを送信キューに追加する. 戻り値:追加できた数 */ uint8_t serial_send(uint8_t *buf, uint8_t size) { uint8_t next, ret; // 排他制御のようなもの SERIAL_TXIE_DISABLE(); ret = 0; next = (serial_tx.write + 1) % SERIAL_TX_BUFSIZE; while ( next != serial_tx.read && ret < size ) { serial_tx.buf[serial_tx.write] = buf[ret]; serial_tx.write = next; next = (serial_tx.write + 1) % SERIAL_TX_BUFSIZE; ret++; } // 送信キューが空で無いときに割り込み再開 if ( serial_tx.write != serial_tx.read ) { SERIAL_TXIE_ENABLE(); } return ret; } /* 受信キューにあるデータを受け取る. 戻り値:受け取ったデータ数 */ uint8_t serial_recv(uint8_t *buf, uint8_t size) { uint8_t ret = 0; SERIAL_RXCIE_DISABLE(); while ( serial_rx.read != serial_rx.write && ret < size ) { buf[ret] = serial_rx.buf[serial_rx.read]; serial_rx.read = (serial_rx.read + 1) % SERIAL_RX_BUFSIZE; ret++; } SERIAL_RXCIE_ENABLE(); return ret; } /* データ受信割り込み */ ISR (USART0_RX_vect) { uint8_t next = (serial_rx.write + 1) % SERIAL_RX_BUFSIZE; if ( next == serial_rx.read ) { next = UDR0; return; } serial_rx.buf[serial_rx.read] = UDR0; serial_rx.write = next; } /* データ送信可能割り込み */ ISR (USART0_UDRE_vect) { UDR0 = serial_tx.buf[serial_tx.read]; serial_tx.read = (serial_tx.read + 1) % SERIAL_TX_BUFSIZE; if ( serial_tx.read == serial_tx.write ) { SERIAL_TXIE_DISABLE(); } }
TeraTerm用いてエコー動作を確認できました.

C言語FIFOデータ構造
前々回の記事でFIFOデータ構造をC++で書いていましたが,デフォルトのライブラリにFIFOデータ構造は存在していたようです.
今回はC言語で書いたライブラリを紹介します. とはいいつつ,ソースコードを晒すだけ^^;
メモリ確保を静的にすることで,マイコンなどでも扱いやすくなると思います.
この実装では,リングバッファと呼ばれる考え方を用いています.
fifo.hは以下
#ifndef _FIFO_H_ #define _FIFO_H_ /* FIFOに格納するデータ型 */ typedef unsigned long fifo_data_t; /* FIFO構造体 */ struct TAG_FIFO { fifo_data_t *buf; unsigned int size; unsigned int read; unsigned int write; }; /* 型定義 */ typedef struct TAG_FIFO fifo_t; /* 関数プロトタイプ */ fifo_t *fifo_create(unsigned int size); void fifo_delete(fifo_t *inst); unsigned int fifo_how_fill(fifo_t *inst); unsigned int fifo_how_empty(fifo_t *inst); int fifo_add(fifo_t *inst, fifo_data_t d); int fifo_add_block(fifo_t *inst, fifo_data_t *ptr, unsigned int size); int fifo_get_block(fifo_t *inst, fifo_data_t *ptr, unsigned int size); #endif /* _FIFO_H_ */
fifo.cは以下
#include <stdlib.h> #include <string.h> #include "fifo.h" /* FIFOの作成 */ fifo_t *fifo_create(unsigned int size) { fifo_t *inst; inst = (fifo_t *) malloc(sizeof(fifo_t)); if ( inst == NULL ) return NULL; memset(inst,0,sizeof(fifo_t)); inst->buf = (fifo_data_t *) malloc(size+1); if ( inst->buf == NULL ) { free(inst); return NULL; } inst->size = size + 1; return inst; } /* FIFOの削除 */ void fifo_delete(fifo_t *inst) { free(inst->buf); free(inst); } /* FIFOのデータ要素数を取得する */ unsigned int fifo_how_fill(fifo_t *inst) { unsigned int val; val = (inst->size + inst->write - inst->read) % inst->size; return val; } /* FIFOの空き要素数を取得する */ unsigned int fifo_how_empty(fifo_t *inst) { unsigned int val; val = (inst->size + inst->read - inst->write - 1) % inst->size; return val; } /* データを追加する */ int fifo_add(fifo_t *inst, fifo_data_t d) { unsigned int next = (inst->write + 1) % inst->size; if ( next == inst->read ) { return 0; } *(inst->buf+inst->write) = d; inst->write = next; return 1; } /* データブロックを追加する */ int fifo_add_block(fifo_t *inst, fifo_data_t *ptr, unsigned int size) { int n = fifo_how_empty(inst); int i; if ( size < n ) { n = size; } for (i=0;i<n;i++) { inst->buf[(inst->write+i)%inst->size] = *(ptr+i); } inst->write = (inst->write + n) % inst->size; return n; } /* データを読み取る */ int fifo_get_block(fifo_t *inst, fifo_data_t *ptr, unsigned int size) { int n = fifo_how_fill(inst); int i; if ( n > size ) n = size; for (i=0;i<n;i++) { *(ptr+i) = *(inst->buf + (inst->read+i)%inst->size); } inst->read = (inst->read + n) % inst->size; return n; }
C++で汎用FIFOを実現してみた.
C++の勉強を少しづつしています.
僕はFIFOデータ構造を使う機会が多いので,汎用のFIFOクラスをC++を使って作ってみました.
まだ勉強を始めたばかりであるため,もっと良い書き方はあると思います.
#include <iostream> using namespace std; class Queue { public: // コンストラクタ Queue(unsigned int, unsigned int); // デストラクタ ~Queue(); // エンキューする void enqueue(uint8_t *); // デキューする void dequeue(uint8_t *); // 保持しているデータ数を返す. unsigned int number(); private: uint8_t **buf; unsigned int qsize; unsigned int esize; unsigned int read; unsigned int write; }; // コンストラクタ. esize:1要素のバイト数, qsize:キューの長さ. Queue::Queue(unsigned int esize, unsigned int qsize) { qsize = qsize + 1; this->qsize = qsize; this->esize = esize; buf = new uint8_t*[qsize]; for (int i=0;i<qsize;i++) { buf[i] = new uint8_t[esize]; } read = write = 0; } // デストラクタ Queue::~Queue() { for (int i=0;i<qsize;i++) { /* 2011/02/21修正 iの初期化がしてありませんでした */ delete [] buf[i]; } delete [] buf; } // エンキューする. ptr:書き込みデータへのポインタ. void Queue::enqueue(uint8_t *ptr) { int next = (write + 1) % qsize; if ( next == read ) { std::cerr << "Queue overflow." << endl; return; } for (int i=0; i<esize; i++) { buf[write][i] = ptr[i]; } write = next; } // デキューする. ptr:読み取りデータへのポインタ. void Queue::dequeue(uint8_t *ptr) { if ( read == write ) { std::cerr << "Queue underflow" << endl; return; } for (int i=0; i<esize; i++) { ptr[i] = buf[read][i]; } read = (read + 1) % qsize; } // 保持しているデータ数を取得する. inline unsigned int Queue::number(void) { return (qsize + write - read) % qsize; } /* Main */ int main (int argc, char **argv) { int i; Queue q(sizeof(int),10); // Intサイズを格納できる,長さ10のキューを作成. for (i=1;i<argc;i++) { int n = atoi(argv[i]); // 引数に与えられた数字の読み取り. q.enqueue((uint8_t *)&n); // 変数が保存されたアドレスをキャストして渡す. } while ( q.number() ) { // キューにデータが残っている間繰り返す. int n; q.dequeue((uint8_t *)&n); // デキュー♪ std::cout << n << endl; // デキューされたものを標準出力へ出力. } return 0; }
簡易ケーブルタグの作り方
今日はあんまりテクニカルでない内容.
最近発見したちょっと便利な方法です.
一般的に,サーバやルータなどのネットワーク機器はスイッチングハブと共に一箇所に集約されることが多いと思います.
そんな環境の中,ネットワーク構成の変更の必要が生じた場合,大量のネットワークケーブルの中から必要なケーブルを発見し,作業を必要があります.
そこで,ファイバータグCA-TAG137の販売商品 |通販ならサンワダイレクトのような便利な製品が発売されていますが,今回は身近にある物で,その代用品を自作するという内容です.
必要なもの
・付箋
・ホチキス
・ペン
制作方法
- ケーブルに付箋を巻く
- ホチキスでとめる
- ペンで必要事項を記入する
写真のように簡易的なケーブルタグを自作することができます.
紙を使用しているため,一般的な製品と比較して耐久性に問題があるかもしれませんが,そもそも慎重に扱われるべき機材なので,そこまで心配はいらないと思います.

