KOSAKA LABORATORY->Tips

秋月VFD RSSやTwitterの内容を表示

秋月VFDにRSSやTwitterの内容を表示させます。
電車内の広告のような物が作れます。

TwitterはTwitterAPIを使うことで、ぶつやきの内容をRSSとして配信することができます。


http://search.twitter.com/search.atom?q=IVRC つぶやきをIVRCで検索した結果
http://search.twitter.com/search.atom?q=from%3AIVRC ユーザーIVRCのつぶやき一覧
http://search.twitter.com/search.atom?q=%23IVRC ハッシュタグ#IVRCのつぶやき一覧

1.プロジェクトの作成
Visual C#で「VFDTest」というWindowsフォームアプリケーションの新規プロジェクトを作成します。

2.フォームの作成
フォーム上に、以下のようにコントロールを配置します



フォーム、ラジオボタン、ボタンをダブルクリックし、イベントを登録します

3.文字表示プログラム
form1.csに以下のプログラムを貼り付けます

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO.Ports;
using System.Collections;

using System.Net;
using System.Xml;

namespace VFDTest
{
    public partial class Form1 : Form
    {
        private SerialPort serialPort = new SerialPort();
        private Boolean is58 = false;
        private Timer timer = new Timer();

        public Form1()
        {
            InitializeComponent();
            this.serialPort = new SerialPort();
        }

        //フォームロード
        private void Form1_Load(object sender, EventArgs e)
        {
            this.comboBox1.DropDownStyle = ComboBoxStyle.DropDownList;

            string[] ports = SerialPort.GetPortNames();

            foreach (string port in ports)
            {
                this.comboBox1.Items.Add(port);
            }

            this.comboBox1.SelectedIndex = 0;

            //ラジオボタン
            this.radioButton1.Checked = true;
            this.is58 = false;

            //ボタン無効
            this.button2.Enabled = false;
            this.button3.Enabled = false;

            //URLを追加
            this.listBox1.Items.Add("http://www.kosaka-lab.com/kosaka_laboratory/atom.xml");
            this.listBox1.SelectedIndex = 0;

            //他スレッドからのコントロール呼び出し許可
            Control.CheckForIllegalCrossThreadCalls = false;
        }

        //接続
        private void button1_Click(object sender, EventArgs e)
        {
            try
            {
                this.serialPort.BaudRate = 19200;//通信速度 19200bps
                this.serialPort.PortName = (string)this.comboBox1.SelectedItem;//ポート番号はコンボボックスから選択
                this.serialPort.DataReceived += new SerialDataReceivedEventHandler(serialPort_DataReceived);
                this.serialPort.Open();//ポートオープン

                //GP1058の場合
                if (this.is58 == true)
                {
                    //画素数336x24
                    this.send_commandFrame(0x25, 0x00);
                }
            }
            catch (Exception ex)
            {
                //ポートオープンに失敗
                MessageBox.Show(ex.ToString());
                return;
            }

            //ボタン各種有効、無効
            this.radioButton1.Enabled = false;
            this.radioButton2.Enabled = false;

            this.comboBox1.Enabled = false;

            this.button1.Enabled = false;
            this.button2.Enabled = true;
            this.button3.Enabled = true;

            //タイマー
            this.timer.Interval = 1000;
            this.timer.Tick += new EventHandler(timer_Tick);
            this.timer.Start();
        }

        //VFDからの返答
        void serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            byte[] byteRcv = new byte[7];
            if (this.serialPort.BytesToRead >= 7)//7バイト以上バッファが溜まったら
            {
                this.serialPort.Read(byteRcv, 0, 7);//7バイトづつ読み込む

                if (byteRcv[2] == 0xC0)//スクロール停止中なら
                {
                    string sendMessage = "";

                    XmlReader reader = XmlReader.Create(this.listBox1.Items[this.listBox1.SelectedIndex] as String);
                    XmlDocument xd = new XmlDocument();
                    xd.Load(reader);
                    XmlNodeList Nodes = xd.GetElementsByTagName("title");

                    for (int i = 1; i < Nodes.Count; i++)//サイト名を表示する場合はi = 0から
                    {
                        if (i > 10) break;//10個以上は表示しない
                        sendMessage = sendMessage + "  " + Nodes[i].InnerXml;
                    }
                    this.send_Message(sendMessage);


                    //次に表示するアイテムを変更
                    if (this.listBox1.SelectedIndex == this.listBox1.Items.Count - 1)
                    {
                        this.listBox1.SelectedIndex = 0;//最初から
                    }else{
                        this.listBox1.SelectedIndex++;
                    }
                }
            }
        }

        //切断
        private void button2_Click(object sender, EventArgs e)
        {
            //ポートクローズ
            this.serialPort.Close();

            //ボタン各種有効、無効
            this.radioButton1.Enabled = true;
            this.radioButton2.Enabled = true;
            this.comboBox1.Enabled = true;

            this.button1.Enabled = true;
            this.button2.Enabled = false;

            //タイマ-ストップ
            this.timer.Stop();
        }

        //コマンドフレーム送信
        private void send_commandFrame(byte command, byte frame)
        {
            byte[] send = new byte[7];
            byte[] byteSum = new byte[2];
            send[2] = command;
            send[3] = frame;

            int nSum = send[0] + send[1] + send[2] + send[3];

            byteSum = BitConverter.GetBytes(nSum);

            send[4] = byteSum[1];
            send[5] = byteSum[0];

            send[6] = 0xEF;
            if (this.serialPort.IsOpen == true)
            {
                try
                {
                    this.serialPort.Write(send, 0, send.Length);
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.ToString());
                }
            }
        }

        //テキスト送信
        private void send_Message(String message)
        {
            byte[] sendMessage = new byte[256];

            //シフトjis->jis
            byte[] byteMessage = Encoding.GetEncoding(50220).GetBytes(message);
            //jis->vfd漢字ロムコード
            byte[] vfdCode = toVFDCode(byteMessage);

            int nFrame = vfdCode.Length / 256 + 1;

            if (vfdCode.Length > 4096)
            {
                Console.WriteLine("エラー:文字数オーバー");
                return;
            }

            this.send_commandFrame(0x04, (byte)nFrame);

            //FFHで埋める
            for (int i = 0; i < 256; i++)
            {
                sendMessage[i] = 0xff;
            }

            int frameCount = 0;
            int byteCount = 0;

            for (int i = 0; i < vfdCode.Length; i++)
            {
                sendMessage[byteCount] = vfdCode[i];

                byteCount++;
                if (byteCount == 256 || i == vfdCode.Length - 1)
                {
                    frameCount++;
                    byteCount = 0;

                    this.send_dataFrame(sendMessage, (byte)frameCount);

                    //FFHで埋める
                    for (int j = 0; j < 256; j++)
                    {
                        sendMessage[j] = 0xff;
                    }
                }
            }
        }

        //データフレーム送信
        private void send_dataFrame(byte[] message, byte frame)
        {
            byte[] send = new byte[262];
            byte[] byteSum = new byte[2];

            //アドレス
            send[0] = 0x00;
            //フレーム番号
            send[1] = frame;
            //コマンド
            send[2] = 0x00;

            //メッセージ
            for (int i = 3; i < 256 + 3; i++)
            {
                send[i] = message[i - 3];
            }

            //サム計算
            int nSum = 0;
            for (int i = 0; i < 256 + 3; i++)
            {
                nSum += send[i];
            }

            byteSum = BitConverter.GetBytes(nSum);

            //SUM
            send[259] = byteSum[1];
            send[260] = byteSum[0];

            //END
            send[261] = 0xEF;
            if (this.serialPort.IsOpen == true)
            {
                this.serialPort.Write(send, 0, send.Length);
            }
        }

        //文字コード変換 jis->VFD独自コード
        private byte[] toVFDCode(byte[] jisCode)
        {
            ArrayList arrayVFDCode = new ArrayList();
            byte[] byteVFDCode;
            Boolean isAscii = true;
            for (int i = 0; i < jisCode.Length; i++)
            {
                //アスキー、jis切り替え
                if (jisCode.Length - i >= 2)
                {
                    if (jisCode[i] == 0x1b && jisCode[i + 1] == 0x24 && jisCode[i + 2] == 0x42)
                    {
                        isAscii = false;
                        i += 2;
                        continue;
                    }
                    else if (jisCode[i] == 0x1b && jisCode[i + 1] == 0x28 && jisCode[i + 2] == 0x42)
                    {
                        isAscii = true;
                        i += 2;
                        continue;
                    }
                }

                if (isAscii)
                {
                    //アスキーコード
                    short tmp1, tmp2;
                    tmp1 = jisCode[i];
                    tmp2 = (short)(tmp1 & 0xE0);
                    tmp2 = (short)(tmp2 >> 5);
                    tmp2--;
                    tmp2 = (short)(tmp2 << 7);

                    tmp1 = (short)(tmp1 & 0x1f);
                    tmp2 = (short)(tmp2 | tmp1);

                    tmp2 = (short)(tmp2 | 0x4000);
                    tmp1 = (short)(tmp2 & 0x00FF);
                    tmp2 = (short)(tmp2 >> 8);
                    arrayVFDCode.Add((byte)tmp1);
                    arrayVFDCode.Add((byte)tmp2);
                }
                else
                {
                    short tmp1 = jisCode[i], tmp2 = jisCode[i + 1], tmp4 = 0;
                    int flag = jisCode[i] >> 4;
                    switch (flag)
                    {
                        case 2:
                            //非漢字                            
                            tmp1 = (short)(tmp1 & 0x7F);
                            tmp4 = (short)(tmp1 & 0x7);
                            tmp1 = (short)(tmp2 >> 2);
                            tmp1 = (short)(tmp1 & 0x18);
                            tmp1 = (short)(tmp1 | tmp4);
                            tmp1 = (short)(tmp1 << 7);
                            tmp1 = (short)(tmp1 & 0xF9F);

                            tmp2 = (short)(tmp2 & 0x1F);
                            tmp2 = (short)(tmp2 | tmp1);
                            tmp2 = (short)(tmp2 & 0xFFF);
                            break;
                        case 3:
                        case 4:
                            //3:漢字第一水準1   4:漢字第一水準2
                            tmp1 = (short)(tmp1 & 0x7F);
                            tmp4 = (short)(tmp1 & 0xF);
                            tmp1 = (short)(tmp1 >> 2);
                            tmp1 = (short)(tmp1 & 0x30);
                            tmp1 = (short)(tmp1 | tmp4);
                            tmp1 = (short)(tmp1 << 7);

                            tmp2 = (short)(tmp2 & 0x7F);
                            tmp2 = (short)(tmp2 | tmp1);
                            tmp2 = (short)(tmp2 & 0xFFF);
                            break;
                        case 5:
                        case 6:
                            //5:漢字第二水準1   6:漢字第二水準2
                            tmp1 = (short)(tmp1 & 0x7F);
                            tmp4 = (short)(tmp1 & 0xF);
                            tmp1 = (short)(tmp1 >> 1);
                            tmp1 = (short)(tmp1 & 0x10);
                            tmp1 = (short)(tmp1 | tmp4);
                            tmp1 = (short)(tmp1 << 7);

                            tmp2 = (short)(tmp2 & 0x7F);
                            tmp2 = (short)(tmp2 | tmp1);
                            tmp2 = (short)(tmp2 | 0x1000);
                            break;
                        case 7:
                            //漢字第二水準3
                            tmp1 = (short)(tmp1 & 0x7F);
                            tmp4 = (short)(tmp1 & 0x7);
                            tmp1 = (short)(tmp2 >> 2);
                            tmp1 = (short)(tmp1 & 0x18);
                            tmp1 = (short)(tmp1 | tmp4);
                            tmp1 = (short)(tmp1 << 7);

                            tmp2 = (short)(tmp2 & 0x1F);
                            tmp2 = (short)(tmp2 | tmp1);
                            tmp2 = (short)(tmp2 | 0x1000);
                            break;
                        default:
                            //その他
                            //スルー
                            continue;
                    }

                    tmp1 = (short)(tmp2 & 0x00ff);
                    tmp2 = (short)(tmp2 >> 8);

                    //リストに追加
                    arrayVFDCode.Add((byte)tmp1);
                    arrayVFDCode.Add((byte)tmp2);

                    //2バイト分進めるために、先に+1しておく
                    i++;
                }
            }
            byteVFDCode = new byte[arrayVFDCode.Count];
            for (int i = 0; i < arrayVFDCode.Count; i++)
            {
                byteVFDCode[i] = (byte)arrayVFDCode[i];
            }

            return byteVFDCode;
        }

        //ラジオボタン
        private void radioButton1_CheckedChanged(object sender, EventArgs e)
        {
            this.is58 = false;
        }

        //ラジオボタン
        private void radioButton2_CheckedChanged(object sender, EventArgs e)
        {
            this.is58 = true;
        }

        //巡回リスト追加
        private void button3_Click(object sender, EventArgs e)
        {
            this.listBox1.Items.Add(textBox1.Text);
            this.textBox1.Text = "";//テキストボックスクリア
        }

        //タイマーイベント
        void timer_Tick(object sender, EventArgs e)
        {
            this.send_commandFrame(0x1c, 0x00);//スクロール中かどうか判断する
        }
    }
}


実行
1 VFDに電源を入れ、PCに繋ぎます。
2 F5ボタンを押し、プログラムを実行します。
3 GP1022かGP1058、自分が使用しているVFDの型番を選択します。
4 VFDが接続されているCOMポートを選び、接続ボタンを押します。



5 小坂研究室のニュース一覧がVFDに表示されます。最後まで表示するとまた最初から表示しなおします。
6 「http://twitter.com/search.atom=%23okachan_sorry」をテキストボックスに貼り付け、追加ボタンをおします








7 小坂研究室のニュース表示が終わると、Twitterハッシュタグ#okachan_sorryの一覧を表示し始めます。





8 Twitterの表示が終わると、また小坂研究室のニュース表示を開始します。
9 切断を押して終了します。

解説
VFDにRSSの文字を表示します。

タイマ
接続ボタンを押すと、1秒間隔でタイマ割り込みを発生させます

this.timer.Interval = 1000;
this.timer.Tick += new EventHandler(timer_Tick);
this.timer.Start();


また、シリアルVFDからデータを受信すると、割り込みを発生させるようにします。
this.serialPort.DataReceived += new SerialDataReceivedEventHandler(serialPort_DataReceived);

タイマ割り込みが入ると、コマンド0x1Cを送信します。
コマンド0x1CはVFDの表示動作を確認するコマンドです。
スクロール中であればVFDから0xC2,スクロール停止中ならC0が送られてきます

コマンド応答

VFDから送られてくる応答フレームは



のような構成になっています。
バッファが7バイト以上溜まったら、7バイトづつ読み込んで、3バイト目をチェックしています
void serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
 byte[] byteRcv = new byte[7];
 if (this.serialPort.BytesToRead >= 7)//7バイト以上バッファが溜まったら
 {
  this.serialPort.Read(byteRcv, 0, 7);//7バイトづつ読み込む

  if (byteRcv[2] == 0xC0)//スクロール停止中なら


XML
選択されているlistBoxのURLをXMLとして読み込みます

XmlReader reader = XmlReader.Create(this.listBox1.Items[this.listBox1.SelectedIndex] as String);
XmlDocument xd = new XmlDocument();
xd.Load(reader);


XML内の<title>タグ内で囲まれた項目をリストアップしています
XmlNodeList Nodes = xd.GetElementsByTagName("title");

XMLの項目を1つのStringに格納して、表示します
for (int i = 1; i < Nodes.Count; i++)//サイト名を表示する場合はi = 0から
{
 if (i > 10) break;//10個以上は表示しない
 sendMessage = sendMessage + " " + Nodes[i].InnerXml;
}
this.send_Message(sendMessage);