2021年3月18日木曜日

Lazarus IOT

 http://m-and-i.cocolog-nifty.com/freetalk/2017/02/raspberry-pilaz.html 参照

Raspberry piでLazarusを使ってIoT

Raspberry pi/RaspbianでのC#/mono実行環境ではデータ入力と送信のサイクル限界が200ms程度とあまりよろしくありませんでした。Raspbian上のmonoが遅いのか、MCP3208アクセスライブラリが遅いのか原因はよくわかりこれはませんが、何とか処理スピードを上げられないかと、ネイティブコンパイラであるLazarusを使用してみました。
ADコンバータMCP3208用のライブラリは、トランジスタ技術のダウンロードサービスから2014年7月号の「特集 第6章 HDMIテレビにグラフ表示! 8チャネル・ロング・メモリ・データ・ロガー」をダウンロードして、この中のspi_mcp3208.pasを使用させていただきました。
また、MQTTライブラリにはmqtt-free-pascal-masterを使用しています。

尚、そのままではLaspbian上でコンパイルした実行ファイルがMQTTに接続する時点で、Semaphore init failed (possibly too many concurrent threads)エラーが出て停止してしまうため、プロジェクトソースに以下の変更を加えています(Web上で見つけた解決策です)。

1
2
3
4
5
6
7
8
9
10
program publish;
  
{$mode objfpc}{$H+}
  
uses
  {$DEFINE UseCThreads} ←この一行を追加
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}

Lazarusで作成したアプリケーションでは、データ入力と送信のサイクル限界が30msまで上がりました。この性能が出ると、50msサイクル程度までは安定した出力が保証できそうです。8チャンネル分の入力データをいちいちフォーム上に表示させていてこのスピードですから、さすがはネイティブコンパイラといった感じでしょうか。入力データの表示を行わなければ、もっとスピードが上がるかもしれません。

Delphi使いの私としては、Lazarusは言語仕様がDelphi互換でフォームデザインも同じ感覚で行うことが出来るので、使っていてまったく違和感がありません。また、LazarusはWindows上でも使用できるためフォームデザインから基本的なプログラミングまでをWindows上で行ってRaspbian上にプロジェクトをコピーして仕上げることが出来ます。Visual Studio/C#ではWindows上で作成した実行ファイルがRaspbian/mono上でそのまま動くという利点はありますが、GPIO処理などのRaspberry pi依存部分のデバッグは遅くて貧弱なRaspbian上のMonoDevelopで行う必要がありますので、Lazarusの方が優位と行った感じでしょうか。

Rasplaz

Lazarus/Raspbianでの主な処理は以下の通りです。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
unit Unit1;
  
{$mode objfpc}{$H+}
  
interface
  
uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
  ExtCtrls, Spin, DateUtils, IniFiles, MQTT, spi_mcp3208;
  
// スタート
procedure TForm1.StartBtnClick(Sender: TObject);
var
  URL: string;
  i: integer;
begin
  Topic := PTopic.Text;
  URL := BURL.Text;
  MQTTClient := TMQTTClient.Create(URL, 1883);
  MQTTClient.Connect;
  Sleep(500);
  if MQTTClient.isConnected then
  begin
    IdleTimer1.Interval := Interval.Value;
    IdleTimer1.Enabled:= True;
    StopBtn.Enabled:= True;
    StartBtn.Enabled:= False;
    BURL.Enabled := False;
    PTopic.Enabled:= False;
    for i := 1 to 8 do
      CB[i].Enabled:= False;
    Pdt := 0;
  end else begin
    MQTTClient.ForceDisconnect;
    MQTTClient.Free;
    MessageDlg('MQTT connection was failed.', mtError, [mbOK], 0);
  end;
end;
  
// データ入力・Publish処理
procedure TForm1.IdleTimer1Timer(Sender: TObject);
var
  InData: array[0..7] of single;
  i, tgap: integer;
  tdt: TDateTime;
  pub, tmp: ansistring;
begin
  // 出力フォーマット
  // YYYY/MM/DD h:min:sec.msec, interval, data1, data2, .... ,data8
  // intervalは前回データ取得から今回までの間の時間(msec)
  tdt := Now;
  if Pdt > 0 then
    tgap := MilliSecondsBetween(Pdt, tdt) //前回から今回までの時間をmsecで取得
  else
    tgap := 0;
  Pdt := tdt;
  DateTimeToString(pub, 'yyyy/mm/dd hh:nn:ss.z', tdt);
  tmp := IntToStr(tgap);
  pub := pub + ',' + tmp;
  ADC8.Read(InData);
  for i:= 1 to 8 do
  begin
    if CB[i].Checked then
    begin
      tmp := FloatToStr(InData[i-1]);
      if MonitorStat.Checked then
        DT[i].Text:= tmp;
      pub := pub + ',' + tmp;
    end else
      pub := pub + ',';
    end;
  MQTTClient.Publish(Topic, pub);
end;

0 件のコメント:

コメントを投稿