RS232C 付 DMM(デジタルマルチメータ) MS8226 を読んでみました (2017/05/06~12/10)
・2017/12/10 iOS (iPhone) のコード (Delphi 10.1) を追加しました。

MS8226 は MASTECH(中国)のデジタルマルチメータで、標準で RS232C ポートと専用ケーブルが付いています。
アマゾンで、¥4,950. で購入しました。中国から届きます。
TsDMMViewer の KAISE KU-2068 モード で接続すると、PC での計測値の表示、ロギングが可能になります。

通信は、1 秒に 1 回程度の間隔で、LCD 表示の 14 バイトのデータが送られてきます。
計測値は数値ではなく、7 セグメント x 4桁 のデータなので、解析が必要になります。
他にも、PCリンク(RS232C、Bluetooth、BLE ...)の DMM がありますが、通信データの内容は、LCD 表示なのではないかと思います。

■スクリーンショット



これを元に、RS-232C - WiFi 変換器を付けて、スマホ(iPhone、Android)に飛ばしています。
MS8226 は、DTR を ON にしないと、データを送信しません。(DTR をフォトカプラの電源として使用していると思われます。)
使用した RS-232C - WiFi 変換器(ラトックシステム社 REX-WF60) は、DTR には対応していないため、DC5V を MS8226 の 4 番ピンに接続する必要があります。



■Delphi 10.1 + ComPort LIB ソースコード

unit MS8226Unit;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, CPort, CPortCtl;

type
  TForm2 = class(TForm)
    ComPort1: TComPort;
    Button1: TButton;
    Button2: TButton;
    Edit1: TEdit;
    Edit2: TEdit;
    Edit3: TEdit;
    Edit13: TEdit;
    ComComboBox1: TComComboBox;
    Edit4: TEdit;
    Edit5: TEdit;
    Edit6: TEdit;
    Label1: TLabel;
    procedure Button1Click(Sender: TObject);
    procedure ComPort1RxChar(Sender: TObject; Count: Integer);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure Button2Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
    GTicks : Cardinal;
    GStrHex :string;
    GStrBin :string;
    procedure DispStrBin;
  end;

var
  Form2: TForm2;

implementation

{$R *.dfm}

// ComPort オープン
procedure TForm2.Button1Click(Sender: TObject);
begin
  ComPort1.BaudRate := TBaudRate.br2400;
  ComPort1.DataBits := TDataBits.dbEight;
  ComPort1.StopBits := TStopBits.sbOneStopBit;
  ComPort1.FlowControl.FlowControl := TFlowControl.fcNone;
  ComPort1.FlowControl.ControlDTR := TDTRFlowControl.dtrEnable;
  ComPort1.Open;

  GTicks := GetTickCount;
end;

// ComPort クローズ
procedure TForm2.Button2Click(Sender: TObject);
begin
  if Comport1.Connected then begin
    try
      Comport1.Close;
      Label1.Caption := '';
    except
      ;
    end;
  end;
end;

// 1バイト整数を2進文字列に
function ByteToBin(x: Byte) : string;
var
  i : integer;
begin
  result := '';
  for i := 1 to 8 do begin
    if ((x and 1) = 1) then
      result := '1' + result
    else
      result := '0' + result;
    x := x shr 1;
  end;
end;

// 1バイト16進文字列を2進文字列に
function HexToBin(const hex : string) : string;
var
  x : integer;
begin
  result := '';
  x := StrToIntDef('$' + hex, -1);
  if x >= 0 then
    result := ByteToBin(x);
end;

// 7セグメントデータを数値に
function SevenSegToStr(const efadcgb : string): string;
var
  s : string;
begin
  result := '';
  if efadcgb.Length = 7 then begin
    s := efadcgb;
    if      s = '1111101' then result := '0'
    else if s = '0000101' then result := '1'
    else if s = '1011011' then result := '2'
    else if s = '0011111' then result := '3'
    else if s = '0100111' then result := '4'
    else if s = '0111110' then result := '5'
    else if s = '1111110' then result := '6'
    else if s = '0010101' then result := '7'
    else if s = '1111111' then result := '8'
    else if s = '0111111' then result := '9'
    else if s = '1101000' then result := 'L';
  end;
end;

// 2進文字列をDMMデータとして表示
procedure TForm2.DispStrBin;
const
  offset = 4;
var
  s : string;
  AcDc, Range, Tani : string;
begin
  if GStrBin.Length = 14 * 8 then begin
    // AC / DC
    if GStrBin.Substring(offset + 0, 1) = '1' then AcDc := 'AC'
    else if GStrBin.Substring(offset + 1, 1) = '1' then AcDc := 'DC'
    else AcDc := '';
    Edit1.Text := AcDc;

    // Rnage
    if GStrBin.Substring(offset + 2, 1) = '1' then Range := 'AUTO'
    else Range := 'MANUAL';
    Edit2.Text := Range;

    // 単位(Unit)
    if GStrBin.Substring(offset + 82, 1) = '1' then Tani := 'M'
    else if GStrBin.Substring(offset + 81, 1) = '1' then Tani := '%'
    else if GStrBin.Substring(offset + 80, 1) = '1' then Tani := 'm'
    else if GStrBin.Substring(offset + 74, 1) = '1' then Tani := 'K'
    else if GStrBin.Substring(offset + 73, 1) = '1' then Tani := 'n'
    else if GStrBin.Substring(offset + 72, 1) = '1' then Tani := 'u'
    else Tani := '';

    if GStrBin.Substring(offset + 98, 1) = '1' then Tani := Tani + 'Hz'
    else if GStrBin.Substring(offset + 97, 1) = '1' then Tani := Tani + 'V'
    else if GStrBin.Substring(offset + 96, 1) = '1' then Tani := Tani + 'A'
    else if GStrBin.Substring(offset + 89, 1) = '1' then Tani := Tani + 'Ω'
    else if GStrBin.Substring(offset + 88, 1) = '1' then Tani := Tani + 'F'
    else if GStrBin.Substring(offset + 105, 1) = '1' then Tani := Tani + '℃';
    Edit3.Text := Tani;

    if GStrBin.Substring(offset + 83, 1) = '1' then Edit4.Text := 'Beep'
    else if GStrBin.Substring(offset + 75, 1) = '1' then Edit4.Text := 'Diode'
    else Edit4.Text := '';

    if GStrBin.Substring(offset + 91, 1) = '1' then Edit5.Text := 'HOLD'
    else Edit5.Text := '';
    if GStrBin.Substring(offset + 90, 1) = '1' then Edit6.Text := 'REL'
    else Edit6.Text := '';
    {
    if GStrBin.Substring(offset + 3, 1) = '1' then Edit7.Text := 'RS232C'
    else Edit7.Text := '';
    }

    // 測定値
    s := '';
    if GStrBin.Substring(offset + 8, 1) = '1' then s := s + '-' else s := s + ' ';
    s := s + SevenSegToStr(GStrBin.Substring(offset + 9, 3) + GStrBin.Substring(offset + 9 + 7, 4));
    if GStrBin.Substring(offset + 24, 1) = '1' then s := s + '.';
    s := s + SevenSegToStr(GStrBin.Substring(offset + 25, 3) + GStrBin.Substring(offset + 25 + 7, 4));
    if GStrBin.Substring(offset + 40, 1) = '1' then s := s + '.';
    s := s + SevenSegToStr(GStrBin.Substring(offset + 41, 3) + GStrBin.Substring(offset + 41 + 7, 4));
    if GStrBin.Substring(offset + 56, 1) = '1' then s := s + '.';
    s := s + SevenSegToStr(GStrBin.Substring(offset + 57, 3) + GStrBin.Substring(offset + 57 + 7, 4));
    Edit13.Text := s;

  end;
end;

// ComPort から受信
procedure TForm2.ComPort1RxChar(Sender: TObject; Count: Integer);
var
  s : string;
  i : integer;
  Ticks : Cardinal;
begin
  if Count > 0 then begin
    Ticks := GetTickCount;
    ComPort1.ReadStr(s, Count);
    if s.Length > 0 then begin
      if Ticks - GTicks > 200 then  begin
        if GStrHex.Length = 28 then begin
          GStrBin := '';
          // 2進文字列に
          for i := 0 to 28 div 2 -1 do
            GStrBin := GStrBin + HexToBin(GStrHex.Substring(i * 2, 2));
          if Label1.Caption <> '■' then Label1.Caption := '■'
          else Label1.Caption := '';
          //Memo1.Lines.Insert(0, GStrBin);

          // 2進文字列をDMMデータとして表示
          DispStrBin;
        end;
        GTicks := Ticks;
        GStrHex := '';
      end;
      // 一時的に16進文字列に
      for i := 0 to s.Length -1 do
        GStrHex := GstrHex + IntToHex(Ord(s.Chars[i]), 2);
    end;
  end;
end;

// フォームクローズ
procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  if Comport1.Connected then begin
    try
      Comport1.Close;
    except
      ;
    end;
  end;
end;

end.

■Delphi 10.1 + Wi-Fi (Indy) ソースコード

unit Unit2;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
  FMX.Controls.Presentation, FMX.StdCtrls, FMX.Layouts, IdBaseComponent,
  IdComponent, IdTCPConnection, IdTCPClient, IdUDPBase, IdUDPClient, IdUDPServer,
  FMX.Edit, IdGlobal, FMX.ScrollBox,{ FMX.Memo,}
  TTS.iOS,FMX.Platform, FMX.Objects,
  System.Math.Vectors,UIConsts;

type
  TForm2 = class(TForm)
    ScaledLayout1: TScaledLayout;
    Button1: TButton;
    IdTCPClient1: TIdTCPClient;
    Timer1: TTimer;
    StyleBook1: TStyleBook;
    PaintBox1: TPaintBox;
    Label1: TLabel;
    Button3: TButton;
    Rectangle1: TRectangle;
    procedure Button1Click(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure PaintBox1Paint(Sender: TObject; Canvas: TCanvas);
  private
    { private 宣言 }
    function  HandleAppEvent(AAppEvent: TApplicationEvent; AContext: TObject): Boolean;
  public
    { public 宣言 }
    cnt : Integer;
    TTS: TTextToSpeech;
    DMMValue : string;
    AcDcStr : string;
    UnitStr : string;
  end;

var
  Form2: TForm2;

implementation

{$R *.fmx}
{$R *.iPhone47in.fmx IOS}

// -----------------------------------------------------------------------------
var
  SevenSegsData : array [0..43] of array [0..1] of Double =
      (
        // a
        (-7.33526 , 13.9743) ,(-7.066240, 14.2)    ,(-0.892448, 14.2),
        (-0.785713, 14.0728) ,(-2.89844 , 12.3)    ,(-5.93038 , 12.3),
        // b
        (-0.477173, 13.7051) ,(-0.254734, 13.44)   ,(-1.1403  ,  8.4177),
        (-2.30772 , 7.43812) ,(-3.08004 ,  8.35855),(-2.425590, 12.0702),
        // c
        (-2.36688 , 6.76188) ,(-1.59456 , 5.84146) ,(-2.49057 , 0.76),
        (-2.82852 , 0.476423),(-4.18485 , 2.09283) ,(-3.53430 , 5.7823),
        // d
        (-3.19622 , 0.167884),(-3.39629 , 0.0)     ,(-9.57008 , 0.0),
        (-9.73467 , 0.19615) ,(-7.7041  , 1.9)     ,(-4.64964 , 1.9),
        // e
        (-10.0432 , 0.56385) ,(-10.2078 , 0.76)    ,(-9.32223 , 5.7823),
        (-8.15483 , 6.76188) ,(-7.3825  , 5.84146) ,(-8.01264 , 2.2677),
        // f
        (-8.09565 , 7.43813) ,(-8.86797 , 8.35854) ,(-7.97197 , 13.44),
        (-7.70297 , 13.6657) ,(-6.29808 , 11.9915) ,(-6.92823 , 8.4177),
        // g
        (-7.78711 , 7.07042) ,(-6.61969 , 8.05)    ,(-3.44774 , 8.05),
        (-2.67543 , 7.12958) ,(-3.84285 , 6.15)    ,(-7.01480 , 6.15),
        // dp
        (-0.85000 , 0.85000) ,(1.2, 0.0)
      );

function ToSevenSegsData(const src: string): string;
var
  c: string;
begin
  c := src.Substring(0, 1);
       if c = '0' then result  := '1111110'
  else if c = '1' then result  := '0110000'
  else if c = '2' then result  := '1101101'
	else if c = '3' then result  := '1111001'
  else if c = '4' then result  := '0110011'
  else if c = '5' then result  := '1011011'
  else if c = '6' then result  := '1011111'
  else if c = '7' then result  := '1110010'
  else if c = '8' then result  := '1111111'
  else if c = '9' then result  := '1111011'
  else if c = '-' then result  := '0000001'
  else if c = '+' then result  := '0110001'
  else if c = '=' then result  := '0001001'
  else if c = '.' then result  := '.'
  else if c = 'a' then result  := '0011101'//='o'
  else if c = 'b' then result  := '0011111'
  else if c = 'c' then result  := '0001101'
  else if c = 'd' then result  := '0111101'
  else if c = 'e' then result  := '1001111'//='E'
  else if c = 'f' then result  := '1000111'//='F'
  else if c = 'g' then result  := '1111011'
  else if c = 'h' then result  := '0010111'
  else if c = 'i' then result  := '0010000'
  else if c = 'j' then result  := '0111000'
  else if c = 'k' then result  := '0010111'//='h'
  else if c = 'l' then result  := '0110000'//='1'
  else if c = 'm' then result  := '0010101'//='n'
  else if c = 'n' then result  := '0010101'//='m'
  else if c = 'o' then result  := '0011101'//='a'
  else if c = 'p' then result  := '1100111'//='P'
  else if c = 'q' then result  := '1110011'
  else if c = 'r' then result  := '0000101'
  else if c = 's' then result  := '1011011'//='5'
  else if c = 't' then result  := '0001111'
  else if c = 'u' then result  := '0011100'
  else if c = 'v' then result  := '0011100'//='u'
  else if c = 'w' then result  := '0011100'//='u'
  else if c = 'x' then result  := '0011100'//='H'
  else if c = 'y' then result  := '0100111'//='Y'
  else if c = 'z' then result  := '1101101'//='2'
  else if c = 'A' then result  := '1110111'
  //else if c = 'B' then result  := '1111111'//='8'
  else if c = 'B' then result  := '0011111'
  else if c = 'C' then result  := '1001119'
  //else if c = 'D' then result  := '1111110'//='0'
  else if c = 'D' then result  := '0111101'
  else if c = 'E' then result  := '1001111'
  else if c = 'F' then result  := '1000111'
  else if c = 'G' then result  := '1010111'
  else if c = 'H' then result  := '0110111'
  else if c = 'I' then result  := '0110000'
  else if c = 'J' then result  := '0111100'
  else if c = 'K' then result  := '0110110'//='H'
  else if c = 'L' then result  := '0001110'
  else if c = 'M' then result  := '1110110'
  else if c = 'N' then result  := '1110110'
  else if c = 'O' then result  := '1111110'
  else if c = 'P' then result  := '1100111'
  else if c = 'Q' then result  := '1111110'//='0'
  else if c = 'R' then result  := '1110111'//='A'
  else if c = 'S' then result  := '1011011'//='5'
  else if c = 'T' then result  := '1000110'
  else if c = 'U' then result  := '0111110'
  else if c = 'V' then result  := '0111110'
  else if c = 'W' then result  := '0111110'
  else if c = 'X' then result  := '0110111'//='H'
  else if c = 'Y' then result  := '0100111'//='y'
  else if c = 'Z' then result  := '0101101'//='2'
  else result  := '';
end;

procedure DrawSevenSegsElement(const BitStr : string; dispX, dispY, scale: double; cv : TCanvas; co :TAlphaColor);
var
  segdata : string;
  i, j : integer;
  pts : TPolygon ; // uses ,,, System.Math.Vectors;
  dpX, dpY, dpR : double;
begin
  cv.BeginScene;
  try
    // 輪郭
    cv.Stroke.Kind := TBrushKind.Solid ;
    cv.Stroke.Color := co;
    // 塗りつぶし
    cv.Fill.Kind := TBrushKind.Solid;
    cv.Fill.Color := co;

    if BitStr = '.' then begin
      dpR := SevenSegsData[43, 0] * scale * 1.5;
      dpX := SevenSegsData[42, 0] * scale + 10.2 * scale + dispX - dpR;
      dpY := SevenSegsData[42, 1] * scale + 14.1 * scale + dispY - dpR;
      cv.FillEllipse(RectF(dpX, dpY , dpX + dpR, dpY + dpR), 1.0);
      cv.DrawEllipse(RectF(dpX, dpY , dpX + dpR, dpY + dpR), 1.0);
    end
    else begin
      segdata := ToSevenSegsData(BitStr);
      if segdata.Length = 7 then begin
        SetLength(pts, 6);
        for i := 0 to 6 do begin
          if segdata.Substring(i, 1) = '1' then begin
            for j := 0 to 5 do begin
              pts[j] := PointF(
                SevenSegsData[i * 6 + j, 0] * scale +10.2 * scale + dispX,
                - SevenSegsData[i * 6 + j, 1] * scale +14.1 * scale + dispY
              );
            end;
            cv.FillPolygon(pts, 1.0);
            cv.DrawPolygon(pts, 1.0);
          end;
        end;
      end;
    end;
  finally
    cv.EndScene;
  end;
end;
// 左上が基点
// 使用例:
//  src := '-12.34567890';
//  DrawSevenSegsText(src, 0, 0, 5.0, PaintBox1.Canvas, claBlue);
// uses ,,, UIConsts; for TAlphaColor;
procedure DrawSevenSegsText(const src : string;  stX, stY, scale: double; cv : TCanvas; co :TAlphaColor);
var
  s : string;
  i : integer;
  dispX, dispY: double;
begin
  dispY := stY;
  dispX := stX;
  for i := 0 to src.Length -1 do begin
    s := src.SubString(i, 1);
    if s = '.' then begin
      DrawSevenSegsElement(s, dispX- scale * 10, dispY, scale, cv, co);
    end
    else begin
      DrawSevenSegsElement(s, dispX, dispY, scale, cv, co);

      dispX := dispX + scale * 10.4;
    end;
  end;
end;

// -----------------------------------------------------------------------------

// FMX TApplicationEvents
// uses ,, FMX.Platform;
function  TForm2.HandleAppEvent(AAppEvent: TApplicationEvent; AContext: TObject): Boolean;
begin
   // ※ TimeChange,OpenURL は iOS のみサポート
   case AAppEvent of
      TApplicationEvent.FinishedLaunching:
        ;
      TApplicationEvent.BecameActive:
        ;//
      TApplicationEvent.WillBecomeInactive:
        begin
          ;// バックグラウンドになる直前に呼ばれる

          try
            if IdTCPClient1.Connected then begin
              IdTCPClient1.Disconnect;
              Button1.Text := '接続';
            end;
          except
            ;
          end;

        end;
      TApplicationEvent.EnteredBackground:
        ;// バックグラウンドになった直後に呼ばれる
      TApplicationEvent.WillBecomeForeground:
        ;// フォアグラウンドになる直前に呼ばれる
      TApplicationEvent.WillTerminate :
        ;
      TApplicationEvent.LowMemory:
        ;
      TApplicationEvent.TimeChange:
        ;
      TApplicationEvent.OpenURL:
        ;
    end;
    Result := True;
end ;

procedure TForm2.PaintBox1Paint(Sender: TObject; Canvas: TCanvas);
begin
  Canvas.Clear(claBlack);
  //DrawSevenSegsText(DMMValue, 0, 72, 7.5, Canvas, claAqua);
  DrawSevenSegsText(DMMValue, 0, 80, 12, Canvas, claYellow);
  Canvas.BeginScene;
  try
    // 輪郭
    Canvas.Stroke.Kind := TBrushKind.Solid ;
    Canvas.Stroke.Color := claAqua;
    // 塗りつぶし
    Canvas.Fill.Kind := TBrushKind.Solid;
    Canvas.Fill.Color := claAqua;

    Canvas.Font.Size := 60;
    Canvas.FillText(RectF(15, 15, 660, 60), AcDcStr, False, 1.0, [], TTextAlign.Leading, TTextAlign.Center);
    Canvas.FillText(RectF( 0, 15, 640, 60), UnitStr, False, 1.0, [], TTextAlign.Trailing, TTextAlign.Center);
  finally
    Canvas.EndScene;
  end;
end;

// 1バイト整数を2進文字列に
function ByteToBin(x: Byte) : string;
var
  i : integer;
begin
  result := '';
  for i := 1 to 8 do begin
    if ((x and 1) = 1) then
      result := '1' + result
    else
      result := '0' + result;
    x := x shr 1;
  end;
end;

// 1バイト16進文字列を2進文字列に
function HexToBin(const hex : string) : string;
var
  x : integer;
begin
  result := '';
  x := StrToIntDef('$' + hex, -1);
  if x >= 0 then
    result := ByteToBin(x);
end;

// 7セグメントデータを数値に
function SevenSegToStr(const efadcgb : string): string;
var
  s : string;
begin
  result := '';
  if efadcgb.Length = 7 then begin
    s := efadcgb;
    if      s = '1111101' then result := '0'
    else if s = '0000101' then result := '1'
    else if s = '1011011' then result := '2'
    else if s = '0011111' then result := '3'
    else if s = '0100111' then result := '4'
    else if s = '0111110' then result := '5'
    else if s = '1111110' then result := '6'
    else if s = '0010101' then result := '7'
    else if s = '1111111' then result := '8'
    else if s = '0111111' then result := '9'
    else if s = '1101000' then result := 'L';
  end;
end;
procedure TForm2.Button1Click(Sender: TObject);
begin
  if Button1.Text = '接続' then begin

    IdTCPClient1.Host := '1.2.3.4';
    IdTCPClient1.Port := 2000;
    IdTCPClient1.ReadTimeout := 500;
    IdTCPClient1.ConnectTimeout := 1000;
    try
      IdTCPClient1.Connect;
      if IdTCPClient1.Connected then begin
        Button1.Text := '切断';
      end;
    except
      on E: Exception do begin

        ShowMessage( E.ClassName + sLineBreak + E.Message);
      end;
    end;
  end
  else begin
    Button1.Text := '接続';
    if IdTCPClient1.Connected then begin
      try
        IdTCPClient1.Disconnect;
        //Button1.Text := '接続';
      except
        on E: Exception do begin
          ShowMessage( E.ClassName + sLineBreak + E.Message);
        end;
      end;
    end;

  end;
end;

procedure TForm2.Button3Click(Sender: TObject);
begin
  if Button3.Text = '音声OFF' then
    Button3.Text := '音声ON'
  else
    Button3.Text := '音声OFF';
end;

procedure TForm2.FormCreate(Sender: TObject);
var
  SvcEvents : IFMXApplicationEventService;  //uses ... FMX.Platform;
begin
  Label1.Text := '';
  // 画面を縦に固定
  //Application.FormFactor.Orientations :=
  //  [TFormOrientation.Portrait, TFormOrientation.InvertedPortrait];

  // ApplicationEvent
  if TPlatformServices.Current.SupportsPlatformService
      (IFMXApplicationEventService, IInterface(SvcEvents)) then
    SvcEvents.SetApplicationEventHandler(HandleAppEvent);

  // テキストスピーチ
  TTS := TTextToSpeech.Create;
  TTS.Speed := TTS.SpeedMin;

  // インターバルタイマー
  Timer1.Interval := 250;
  Timer1.Enabled := True;
end;

procedure TForm2.FormDestroy(Sender: TObject);
begin
  // テキストスピーチ解放
  TTS.Free;
end;

// 小数点位置
function BinStrToDMMDecimalPoint(const BinStr: string): integer;
const
  offset = 4;
var
  i : integer;
begin
  result := 0;
  for i := 0 to 2 do begin
    if BinStr.Substring(offset + 24 + i * 16, 1) = '1' then begin
      result := 3 - i;
      break;
    end;
  end;
end;

// 単位
function BinStrToDMMUnit(const BinStr: string): string;
const
  offset = 4;
begin
  // 単位(Unit)
       if BinStr.Substring(offset + 82, 1) = '1' then result  := 'M'
  else if BinStr.Substring(offset + 81, 1) = '1' then result  := '%'
  else if BinStr.Substring(offset + 80, 1) = '1' then result  := 'm'
  else if BinStr.Substring(offset + 74, 1) = '1' then result  := 'K'
  else if BinStr.Substring(offset + 73, 1) = '1' then result  := 'n'
  else if BinStr.Substring(offset + 72, 1) = '1' then result  := 'μ'
  else result  := '';

       if BinStr.Substring(offset + 98, 1) = '1' then result  := result  + 'Hz'
  else if BinStr.Substring(offset + 97, 1) = '1' then result  := result  + 'V'
  else if BinStr.Substring(offset + 96, 1) = '1' then result  := result  + 'A'
  else if BinStr.Substring(offset + 89, 1) = '1' then result  := result  + 'Ω'
  else if BinStr.Substring(offset + 88, 1) = '1' then result  := result  + 'F'
  else if BinStr.Substring(offset + 105, 1) = '1' then result  := result  + '℃';
end;

// テキストスピーチ用単位
function BinStrToDMMUnitMessage(const BinStr: string): string;
const
  offset = 4;
begin
  // 単位(Unit)
       if BinStr.Substring(offset + 82, 1) = '1' then result  := 'メガ'
  else if BinStr.Substring(offset + 81, 1) = '1' then result  := 'パーセント'
  else if BinStr.Substring(offset + 80, 1) = '1' then result  := 'ミリ'
  else if BinStr.Substring(offset + 74, 1) = '1' then result  := 'キロ'
  else if BinStr.Substring(offset + 73, 1) = '1' then result  := 'ナノ'
  else if BinStr.Substring(offset + 72, 1) = '1' then result  := 'マイクロ'
  else result  := '';

       if BinStr.Substring(offset + 98, 1) = '1' then result  := result  + 'ヘルツ'
  else if BinStr.Substring(offset + 97, 1) = '1' then result  := result  + 'ボルト'
  else if BinStr.Substring(offset + 96, 1) = '1' then result  := result  + 'アンペア'
  else if BinStr.Substring(offset + 89, 1) = '1' then result  := result  + 'オーム'
  else if BinStr.Substring(offset + 88, 1) = '1' then result  := result  + 'ファラッド'
  else if BinStr.Substring(offset + 105, 1) = '1' then result  := result  + 'ド';
end;

// AC / DC
function BinStrToDMMAcDc(const BinStr: string): string;
const
  offset = 4;
begin
  result := '';
       if BinStr.Substring(offset + 0, 1) = '1' then result := 'AC'
  else if BinStr.Substring(offset + 1, 1) = '1' then result := 'DC';
end;

// 測定値
function BinStrToDMMValue(const BinStr: string; UnitFlag: boolean):string;
const
  offset = 4;
begin
  result := '';
  if BinStr.Length = 14 * 8 then begin
    if BinStr.Substring(offset + 8, 1) = '1' then result := '-' else result := ' ';
    result := result + SevenSegToStr(BinStr.Substring(offset + 9, 3) + BinStr.Substring(offset + 9 + 7, 4));
    if BinStr.Substring(offset + 24, 1) = '1' then result := result + '.';
    result := result + SevenSegToStr(BinStr.Substring(offset + 25, 3) + BinStr.Substring(offset + 25 + 7, 4));
    if BinStr.Substring(offset + 40, 1) = '1' then result := result + '.';
    result := result + SevenSegToStr(BinStr.Substring(offset + 41, 3) + BinStr.Substring(offset + 41 + 7, 4));
    if BinStr.Substring(offset + 56, 1) = '1' then result := result + '.';
    result := result + SevenSegToStr(BinStr.Substring(offset + 57, 3) + BinStr.Substring(offset + 57 + 7, 4));
    if UnitFlag then
      result := result + ' ' + BinStrToDMMUnit(BinStr);
  end;
end;

procedure TForm2.Timer1Timer(Sender: TObject);
var
  res : string;
  binstr : string;
  svalue : string;
  vbuf : TIdBytes;
  i : integer;
  dvalue : double;
  dp : integer;
  ttstr : string;
begin
  try
    if IdTCPClient1.Connected then begin
      IdTCPClient1.IOHandler.ReadTimeout := IdTCPClient1.ReadTimeout;
      try
        IdTCPClient1.IOHandler.ReadBytes(vbuf, 14);
        res := '';
        binstr := '';
        for i := 0 to Length(vbuf) -1 do begin
          res := res + IntToHex(vbuf[i], 2);
          binstr := binstr + ByteToBin(vbuf[i]);
        end;

        //for i := 0 to 28 div 2 -1 do
        //  binstr := binstr + HexToBin(res.Substring(i * 2, 2));
        // 16進表示
        Label1.Text := res;

        // 測定値(表示そのまま)
        svalue := BinStrToDMMValue(binstr, False);
        DMMValue := svalue;

        // 測定値
        dvalue := StrToFloatDef(svalue, 10000);
        dp := BinStrToDMMDecimalPoint(binstr);

        if dvalue < 10000 then
          ttstr := Format('%.' + dp.ToString + 'f', [dvalue])
        else
          ttstr := 'Overflow Lebel';//svalue;//'O.L.'; //'OverflowLebel';

        //ttstr := StringReplace(ttstr, '.', 'テン',[]);

        //DMMValue := '     ' + Label1.Text;
        //DMMValue := DMMValue.Substring(DMMValue.Length-6, 5);
        //PaintBox1.Repaint;

        // 単位
        UnitStr := BinStrToDMMUnit(binstr);

        // AC / DC
        AcDcStr := BinStrToDMMAcDc(binstr);
        //AcDcStr := ttstr;

        PaintBox1.Repaint;
        // テキストスピーチ
        Inc(cnt);
        if (cnt >= 12) then begin
          if Button3.Text = '音声OFF' then
            TTS.Speak(ttstr + ' ' + BinStrToDMMUnitMessage(binstr));
          cnt := 0;
        end;
      except
        on E: Exception do begin
          // Timeout Error
          Label1.Text := E.ClassName;
        end;
      end;
    end;
  except
    on E: Exception do begin
      Label1.Text := E.ClassName;
    end;
  end;
end;

end.