勝手な電子工作・・

勝手なオリジナル電子工作に関する記事を書きます

マイコンとPCのデータ授受ーSmallbasicなら簡単 (2020.12.05 LiquidCrystal_I2Cについて補足しました)

f:id:a-tomi:20200823175731g:plain

ArduinoのデータをPCで受けて表示した例

マイコンのデータをPCで受けて自由にグラフィック処理したり、逆にPCからデータをマイコンへ渡したりするには、PC側にそれなりの言語環境が必要。ところがちゃんと維持していないと、Anacondaなどのバージョン不整合を起こし、まるで王手飛車とりで攻めまくられたりして・・・^^; 思わぬことで手こずりたくないものです。

私はマイクロSDカードを介したりすることが多いですが、リアルタイムで処理したい場合はそうはいきません。そんなとき、単純・簡単で便利なのがマイクロソフトが無償提供している Small Basic という教育用言語です。

ここではマイコンとの間のインターフェイスを、単純で特別な機器の要らないシリアル通信でつなぎます(それなりのデバイスを用意すれば、BlueToothなど他の方法ももちろん可能ですが)。

使うにはまず、Small BasicをPCにインストールします。バージョンアップの頻度は少なくとても安定しています。今は次のサイトが本家になっています。

https://smallbasic-publicwebsite.azurewebsites.net/

 英語版でとくに困ることはありませんが、日本語版(各国言語版)を入れたいときは次からインストールします。

https://www.microsoft.com/ja-jp/download/details.aspx?id=46392

 Small Basic自体はいちじるしく単純ですが、便利な拡張APIが沢山出ています。なかでも英国の団体が運営するLitDevには豊富な関数がそろっていて世界中で多く使われています。LitDevの最新版(現時点は LitDev 1.2.23.0)を次からインストールします。

http://litdev.co.uk/

LitDevのZipを解凍してすべてを次のフォルダーに入れます。C:\Program Files (x86)\Microsoft\Small Basic\Lib

これで後はプログラミングが自由自在です。めったにバージョンアップがないのは却ってありがたいことです^^

Small Basicの使い方は、非常にわかりやすい日本語サイトも沢山でていますので必要に応じてご参照下さい。LitDevで使える多数の関数については上のサイトで、英語ではありますがわかりやすく説明されています。ここではそのなかのLDCommPortだけを使いますが、他に便利なAPIは多数あります。LitDevは、SmallBasicの開発環境で言語そのものに交じって同じに使えます。

 

まずは、ArduinoからPCのSmall Basicへ送る場合の例です。

f:id:a-tomi:20200823210916j:plain

次がこのArduinoスケッチです。

/******************************************************************
    Sending data to Microsoft-Smallbasic via Serial port, example.
     Initial version V.00 Aug.22, 2020  (c) Akira Tominaga
     Function:
      Send data of Lissage graph to PC Smallbasic.
  *****************************************************************/
#include "Wire.h"
#include "LiquidCrystal_I2C.h"
//                    addr, en,rw,rs,d4,d5,d6,d7,bl,blpol
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);
#define cPl 20                    // characters per line
#define lMax 4                    // number of lines
#define wSize 854                 // graphics window width
#define hSize 480                 // graphics window height
uint8_t Line = 0;                 // set LCD initial line
byte EOT = 0xFE;

void setup()  {  // ***** Arduino Setup *****
  Serial.begin(9600);
  lcd.begin(20, 4);        // initialize 20 x 4 LCD turn on backlight
  lcd.backlight();
  lcd.setCursor(0, 0);
  lcd.print("Talk with SmallBasic");
Hajime:
  lcd.setCursor(0, 1);
  lcd.print("Start Smallbasic pgm");
  while (Serial.available() < 1) {}
  String s = Serial.readString(); // read message from Smallbasic pgm
  uint8_t nPads=20-s.length();    // # of blanks req'd to pad
  lcd.setCursor(0, 1);
  lcd.print(s);
  for (uint8_t i=0;i<nPads;i++){
    lcd.print(" ");
  }
  delay(10);
  lcd.setCursor(0, 1);
  lcd.print("Lissage data sent");
  for (float t = 0; t < 2.1 * 3.1416; t = t + 0.01) {
    uint16_t px = (float)((wSize/2) * sin(3 * t) + wSize/2);
    uint16_t py = (float)((hSize/2) * sin(4 * t) + hSize/2);
    uint8_t xh=px>>8;             // get x-high byte
    uint8_t xl=px-256*xh;         // get x-low byte
    uint8_t yh=py>>8;             // get y-high byte
    uint8_t yl=py-256*yh;         // get y-low byte
    Serial.write(xh);             // send x-high byte
    Serial.write(xl);             // send x-low byte
    Serial.write(yh);             // send y-high byte
    Serial.write(yl);             // send y-low byte
    delay(1);
  }
  for (uint8_t i=1;i<4;i++){      // LCD lines from 1 to 3 
    lcd.setCursor(0,i);           // set cursor 
    for (uint8_t j=0;j<20;j++){   // and clear each line  
      lcd.print(" ");
    }
  }
  Serial.write(EOT);              // send End of Transimission
  Serial.write(0x00);             // of which low byte to add
  goto Hajime;                    // return to Hajime and wait
}

void loop() { // ***** Arduino Loop *****
}             // not used in this example

 

この記事のいちばん最初に掲げた動画は、これをSmallBasicの次のプログラムで処理したものです。

myTitle="Receiving data from Arduino  Aug.22, 2020 (c)Akira Tominaga"
TextWindow.Title=myTitle
TextWindow.Write("Open CommPort..")
status = LDCommPort.OpenPort("COM5",9600)
TextWindow.WriteLine(status)
gw = 854
gh = 480
margin = 50
GraphicsWindow.Width = gw+margin
GraphicsWindow.Height = gh+margin
GraphicsWindow.Title=myTitle
GraphicsWindow.BackgroundColor="Green"
GraphicsWindow.PenColor="White"
GraphicsWindow.PenWidth=3
EOT=254*256 ' define 0xFE.. as End of Transmission from Arduino
xs=gw/2 ' initial x position in Graphics window
ys=gh/2 ' initial y position in Graphics window
LDCommPort.TXString("SB started.")

Loop1:
xh=LDCommPort.RXChar()
xl=LDCommPort.RXChar()
xx=xh*256+xl+Margin/2  ' Set x position
'TextWindow.Write("XX=")
'TextWindow.WriteLine(Text.GetCharacterCode(XX))
If XX>=EOT Then
  Goto Lpe
EndIf
yh=LDCommPort.RXChar()
yl=LDCommPort.RXChar()
yy=yh*256+yl+Margin/2  ' Set y position
GraphicsWindow.DrawLine(xs, ys,XX,YY)
xs=xx ' save xx
ys=yy ' save yy
Goto Loop1

Lpe:
LDCommPort.ClosePort()
GraphicsWindow.BrushColor="Yellow"
GraphicsWindow.FontSize=30
GraphicsWindow.DrawText(gw/2-60,gh/2,"Completed!")
Sound.PlayBellRing()

 グラフはSmallBasicのGraphicsWindowでごく簡単にかいていますが、かなり高度の事も色々できます。一例:https://a-tomi.hatenablog.com/entry/2018/09/04/162615

LDCommPortのうち、OpenPort()、TXString()、RXChar()、ClosePort()しか使っていませんが、他に、RXByte()、RXAll()、TXByte()などの関数も使えます。

他の命令は全てSmallBasicそのものですが、変数に型の概念がないこと(つまり宣言不要)や、ファイルやイメージなどの扱いも含めて楽ちんそのものです^^

 

次に、SmallBasicが非常に長い文章をArduinoへ送り、Arduinoで液晶に次々に表示する例です。

f:id:a-tomi:20200823211138j:plain

 このSmallBasic側プログラムは次です。

myTitle="Sending text to Arduino   Aug.22, 2020 (c)Akira Tominaga"
TextWindow.Title=myTitle
TextWindow.Write("Open CommPort..")
status = LDCommPort.OpenPort("COM5",9600)
TextWindow.WriteLine(status)

Bun=File.ReadContents("D:\SmallBasic\SBtxt\Test00.txt")
Bl=Text.GetLength(Bun)
If Bl=0 Then
  TextWindow.WriteLine("*** File not found")
EndIf

tLen=20 ' text length to be sent at one time
sP=1    ' start pposition of  Text reading
LF=Text.GetCharacter(10) ' ending char sent by Arduino

While(sP<Bl)
  Bpart=Text.GetSubText(Bun,sP,tLen)
  TextWindow.Write("SB sent: ")
  TextWindow.WriteLine(Bpart)
  LDCommPort.TXString(Bpart)
  sP=sP+tLen
  TextWindow.Write("Arduino: ")
  Resp=""
    While(Resp <> LF)
      Resp=Text.GetCharacter(LDCommPort.RXByte())
      TextWindow.Write(Resp)
    EndWhile 
EndWhile
status=LDCommPort.ClosePort()
Sound.PlayBellRing()

 ここでは簡単のため、テキストファイルを開いて、全量を読み込んでそれを送っていますが、Webの特定場所から読み込んで送るなども、もちろんできます。

SmallBasicのTextWIndowに、この送受の内容が次のように順次表示されます。

f:id:a-tomi:20200823212742j:plain

  次がこれを受けて表示するArduinoのスケッチです。

実行順序としてはArduinoを先に接続して動かしてから、次にSmallBasicのプログラムを立ち上げます。なお接続Port番号が一定しないマイコンの場合は、SmallBasicでのOpenPortの処理方法をこの記事の最後にかいておきます。

/*****************************************************************
    communications with MS Smallbasic via Serial port, example.
     Initial version V.00 Aug.22, 2020  (c) Akira Tominaga
     Function:
      Receive text from Smallbasic and display to LCD 20x4.
     Major revisions:
 *****************************************************************/
#include "Wire.h"
#include "LiquidCrystal_I2C.h"
//                    addr, en,rw,rs,d4,d5,d6,d7,bl,blpol
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);
#define cPl 20                    // characters per line
#define lMax 4                    // number of lines
uint8_t Line = 0;                 // set LCD initial line

void setup()  { // ***** Arduino Setup *****
  lcd.begin(cPl, lMax);           // initialize I2C and LCD
  lcd.backlight();                // and turn on backlight
  lcd.setCursor(0, 0);            // LCD home position
  lcd.print("Receive text from PC");
  Serial.begin(9600);             // the same bps with PC
}

void loop() {  // ***** Arduino Loop *****
  while (Serial.available() < 1) {}
  String s = Serial.readString(); // receive text string
  lcd.setCursor(0, Line);         // point line to show text
  lcd.print(s);                   // and show text to LCD
  int j = Line + 1;               // point next line to use
  if (j >= lMax) j = 0;
  lcd.setCursor(0, j);
  for (int k = 0; k < cPl; k++) { // and clear it
    lcd.print(" ");
  }
  delay(1000);                    // time to read text
  Serial.write("OK!");            // send message OK! to PC
  Serial.write(0x0A);             // LF as end of message
  Line++;                         // point next line of LCD
  if (Line >= lMax) Line = 0;
}

 上の例は電光掲示板のように表示するだけですが、適度な速度でやれば結構よみやすいものです。

 

以上での留意点は

①先にマイコンをシリアルポート(今ではUSBシリアル)につなぎ、そのあとでSmallBasicプログラムを立ち上げる

②COMポート番号が接続のつど一定にならないマイコン接続の場合は、LDCommPort.AvailablePorts()関数でポート番号を得てからOpenPort処理するように組むようになっていますが、これは文字の操作が結構面倒です。しかしポートを1つしか使わないことが圧倒的に多いでしょうから、その場合は次のコーディングでPort番号を得てからOpenPortをするコーディングにすれば大丈夫です。

kekka="x"
for i=1 to 16
  portName="COM"+i
  kekka=LDCommPort.OpenPort(portName,9600)
  If kekka="SUCCESS" Then 
    Goto Out
  EndIf
EndFor
Out:
TextWindow.Write("CommPort is ")
TextWindow.WriteLine(portName)

 

では今回はこのへんで。

一番簡単な言語のひとつであるSmall Basicを使い始めていただき、プログラミングする習慣が少しでも広まるきっかけになればと思います。

 

2020.12.05追記:

I2Cシリアル変換モジュールで接続する液晶文字ディスプレイは種類が複数あり、ライブラリーが混乱しがちです。ここではArduinoのLiquidCrystal_I2C.hライブラリー(New-liquidCrystal-master)を使う方法を補足させていただきます。

元祖の日立1602LCDモジュール(HD44780)と同じ仕様のLCDディスプレイをI2Cシリアル変換モジュール(PCF8574ボード)で4ビットデータを使って動かす価格の安い(海外ネットで16文字x2行は200円台、20文字x4行は400円台で購入できた)ものが圧倒的に多く出ている感じです。次の写真で、各LCDモジュール裏の右上にはんだ付けされているのがその変換モジュールです。

f:id:a-tomi:20201205121018j:plain

その便利な変換ICは同じでもボードには複数種があるため、LCDへのピン接続の明示的な指定の必要性があるわけです。ピン接続部を表から見ればどれでも、元祖の1602LCDモジュール(HD44780)と同じで海外製なのにカタカナを含む中身のフォントも全く同じものが多いです。

f:id:a-tomi:20201205121404j:plain

ところが、同じに見えても4ビット接続の場合は8ビット接続と異なり、データの授受にD0~D3を使わないためI2Cプロトコルは煩雑になります。

このため、現時点ではArduinooのLiquidCrystal_I2Cライブラリーをスーパーセットである次のものへ入れ替え、他のLiquidCrystal_I2Cライブラリーを削除することで問題なく使えるようになっています。16桁2行でも20桁4行でも上のスケッチ例のように指定できます。

GitHub - fmalpartida/New-LiquidCrystal: Clone of the new liquid crystal library from: https://bitbucket.org/fmalpartida/new-liquidcrystal

名称が重なる他のLiquidCrystal_I2Cライブラリーは以後使わないかと思います。万一必要になれば、そのときに元のを特定スケッチのフォルダーに改めて再導入すればよいかと思われますし、もし心配なら上記を入れる前に古いのをどこか別フォルダーにバックアップしておくとか(参照先の名前が重なるのを防ぐために)。

今やOLEDが多く使われるようになりましたが、そちらはメモリーの必要量が多いためこういう安価で楽なI2C接続LCDはまだまだ長く使われるのではないかと思います。

以上、ご自分がお使いになる同様な液晶文字ディスプレイについてご判断の上、自己責任にてお願いしますね。

 

 

©2020 Akira Tominaga, All rights reserved.