RS232C データ受信のタイミングでプログラムを動かす への返答

投稿で使用できる特殊コードの説明。(別タブで開きます。)
本名は入力しないようにしましょう。
投稿した後で削除するときに使うパスワードです。返答があった後は削除できません。
返答する人が目安にします。相手が小学生か社会人かで返答の仕方も変わります。
最初の投稿が質問の場合、質問者が解決時にチェックしてください。(以降も追加書き込み・返信は可能です。)
※「過去ログ」について書くときはその過去ログのURLも書いてください。

以下の返答は逆順(新しい順)に並んでいます。

投稿者 ザボン  (社会人) 投稿日時 2014/12/10 10:46:25
遅くなって申し訳ありませんでした
投稿者 ザボン  (社会人) 投稿日時 2014/12/10 10:45:17
返答ありがとうございます!

application.ontimeで1時間毎に読み込むように変更しました。

まだテストしていないですがおそらくいけると思います。

ありがとうございました!
投稿者 (削除されました)  () 投稿日時 2014/12/10 10:30:05
(削除されました)
投稿者 daive  (社会人) 投稿日時 2014/12/1 19:40:33
お役にたつかどうか解りませんが、

1.Windows API による 232 通信でも、受信バッファに受信されたデータ数を
  検出する事が可能です。
  ⇒やさしく書いた過去サイトが、無いか検索してみました。
    例えば、
    WindowsでRS232Cを使う
    http://members.jcom.home.ne.jp/0434383301/vc10.htm
    受信されたデータ数を実際にReadFile()を呼び出す前に知りたい場合があります。
      省略    
  ⇒データが固定長であれば、決まった長さになるまで、受信データ数を、定期的にチェックする。
    ↑説明通りであれば、固定長の筈ですよね?
    可変長であれば、ターミネータ(C/R、L/F、EOT etc)で判別、
    固定長でもなく、ターミネータでも判定出来ない、適当通信であれば、
    データ数の増加がなくなったら、1回分受信終了と判定するなど。

  現状のコード構成に近いままで行うには、
>    Do While CommInput(hCom, buf, Len(buf)) = 0    ' 受信する(Module1内の受信命令を実行)
>          DoEvents  
>     Loop
  固定長データなのですから、
  上記のコードの後に、決まったデータ数に達するまで、
  データを追加で読込むコードを追加すれば良いのでは?

2.イベント駆動型プログラミングでは、
 >'23時のデータを受信するまで常にデータを受信する
 >Do While nipo <> 2300    
 >     DoEvents      
 >  :中略
 > Loop
 の様な、無限ループ的プログラムは、書かない事が前提です。
 DoEventsは、現在の環境では、DoEventsの利点よりも、欠点の方が多く、
  安易な使用は避けるべきものです。
 EXCEL-VBAでは、標準でインターバルタイマが実装されていないので、
 application.ontime(検索ワード:vba application.ontime)の使い方を工夫して、
 定周期に、受信バッファのデータ数を調べる方法へ、変更をお勧めします。
 (データ受信間隔:1時間に1回程度ですので、対応可能。)
 または、1分以上に1回程度チェックするのであれば、テーブルクエリのデータ更新間隔を
 イベントに使う事も可能。
3.苦労して、Windows API から 232 を使わなくても、VB.NET であれば、
  232 通信は、簡単にできるのですが、敷居が高いようであれば、
  受信バッファに受信されたデータ数を検出する事
  からかも。

  VB.NET から EXCEL を使う方法もありますが、
  慣れていないと混乱するかもしれません。
  EXCEL-DNA という手段もあります。
投稿者 ザボン  (社会人) 投稿日時 2014/12/1 14:19:33

シート3に以下のプログラムを

Private DCB As DCBstructure
Private Settings As String

Sub Worksheet_Activate()
'↑シート3がアクティブ(選択状態)になると実行される

'シートを保護するが、マクロではセルの値変更可能になる
Sheet2.Protect UserInterfaceOnly:=True

'シート3を隠す
Worksheets("Sheet3").Visible = False

'受信データを書き込むシート2を選択する
Sheet2.Select

'セルを選択できない用にロックする
ActiveSheet.EnableSelection = xlUnlockedCells
' スクロール範囲を設定する
ActiveSheet.ScrollArea = "$A$1:$R$26"

Dim cto As COMMTIMEOUTS                       
Dim Last_Row As Long                          
Dim today, niti, getumatu, tuki As String     
Dim buf As String * 256                       
Dim deta1, deta2 As Variant                  
Dim i, j, k, o, nipo As Integer     
          
' COMポートを開く
hCom = CommOpen(Sheet1.Range("C3"))  'シート1のC3にCOMポート番号
Call InitializeComm

If hCom = INVALID_HANDLE_VALUE Then
    Call MsgBox("COMポートが開けません", vbOKOnly Or vbCritical)
    Call CommClose(hCom)  'COMポートを閉じる
End If

' タイムアウトを設定
cto.ReadTotalTimeoutConstant = 1000 'タイムアウト時間(ms)
Call SetCommTimeout(hCom, cto)  'Module1内のタイムアウト命令を呼び出す

'シート2の測定値を書き込む空白行はBの何行目かを探す
Last_Row = ActiveSheet.Cells(27, 2).End(xlUp).Row
j = Last_Row + 1


'23時のデータを受信するまで常にデータを受信する
Do While nipo <> 2300
    
    DoEvents      
    k = 2  'シート2の2列目から受信データを書き込む
    
    buf = ""
    deta1 = ""
    deta2 = ""
        
    'この部分を変更したいです。ループでずっと読み込むんじゃなくてbufにデータが入ったら読み込む様に
    Do While CommInput(hCom, buf, Len(buf)) = 0    ' 受信する(Module1内の受信命令を実行)
          DoEvents  
    Loop
      
    deta1 = Split(buf, vbCrLf)
    For o = 0 To UBound(deta1) - 1
            deta2 = Split(deta1(o), ",")

        For i = 0 To UBound(deta2)
             Sheet2.Cells(j, k) = deta2(i)
             k = k + 1
        Next i
    
    Next o
    
  nipo = Sheet2.Cells(j, 4)

    j = j + 1

Loop

    Call CommClose(hCom)  'Module1内のCOMポートを閉じる命令を実行

End Sub

Private Function InitializeComm() As Boolean          。
   Settings = Sheet1.Range("C4")        '9600,N,7,1" ”ボーレート,パリティーチェックなし,データ長,ストップビット
   If hCom > 0 Then
      BuildCommDCB Settings, DCB
      DCB.Flags = DCB.Flags Or 1         ' Binary Mode ON
      DCB.Flags = DCB.Flags Or &H1010    ' DTR and RTS Enable
      SetCommState hComm, DCB
      InitializeComm = True
   Else
      MsgBox "COMポートが開けません" , vbOKOnly + vbCritical + vbApplicationModal
      InitializeComm = False
   End If
End Function

投稿者 ザボン  (社会人) 投稿日時 2014/12/1 14:18:03
標準モジュール
Public Const GENERIC_READ           As Long = &H80000000
Public Const GENERIC_WRITE          As Long = &H40000000
Public Const OPEN_EXISTING          As Long = 3
Public Const INVALID_HANDLE_VALUE   As Long = -1            

Public hCom As Long

Public Declare Function GetCommState Lib "kernel32" (ByVal hCom As Long, ByRef lpDCB As DCBstructure) As Long

Public Declare Function BuildCommDCB Lib "kernel32" Alias "BuildCommDCBA" (ByVal lpDef As String, ByRef lpDCB As DCBstructure) As Long

Public Declare Function SetCommState Lib "kernel32" (ByVal hCom As Long, ByRef lpDCB As DCBstructure) As Long

Public Type DCBstructure 
        DCBlength As Long
        BaudRate As Long 
        Flags As Long 
        XonLim As Integer
        XoffLim As Integer
        ByteSize As Byte
        Parity As Byte
        StopBits As Byte
        XonChar As Byte
        XoffChar As Byte
        ErrorChar As Byte
        EofChar As Byte
        EvtChar As Byte
        wReserved2 As Integer
End Type
' Comm Baud Rate indices
Public Const CBR_110 = 110
Public Const CBR_300 = 300
Public Const CBR_600 = 600
Public Const CBR_1200 = 1200
Public Const CBR_2400 = 2400
Public Const CBR_4800 = 4800
Public Const CBR_9600 = 9600
Public Const CBR_14400 = 14400
Public Const CBR_19200 = 19200
Public Const CBR_38400 = 38400
Public Const CBR_56000 = 56000
Public Const CBR_57600 = 57600
Public Const CBR_115200 = 115200
Public Const CBR_128000 = 128000
Public Const CBR_256000 = 256000

Public Const DTR_CONTROL_DISABLE = &H0
Public Const DTR_CONTROL_ENABLE = &H1
Public Const DTR_CONTROL_HANDSHAKE = &H2

Public Const RTS_CONTROL_DISABLE = &H0
Public Const RTS_CONTROL_ENABLE = &H1
Public Const RTS_CONTROL_HANDSHAKE = &H2
Public Const RTS_CONTROL_TOGGLE = &H3

Public Type COMMTIMEOUTS
    ReadIntervalTimeout         As Long
    ReadTotalTimeoutMultiplier  As Long
    ReadTotalTimeoutConstant    As Long
    WriteTotalTimeoutMultiplier As Long
    WriteTotalTimeoutConstant   As Long
End Type

Public Declare Function CreateFile Lib "kernel32" Alias "CreateFileA" ( _
    ByVal lpFileName As String, _
    ByVal dwDesiredAccess As Long, _
    ByVal dwShareMode As Long, _
    ByVal lpSecurityAttributes As Long, _
    ByVal dwCreationDisposition As Long, _
    ByVal dwFlagsAndAttributes As Long, _
    ByVal hTemplateFile As Long _
) As Long

Public Declare Function ReadFile Lib "kernel32" ( _
    ByVal hFile As Long, _
    lpBuffer As Any, _
    ByVal nNumberOfBytesToRead As Long, _
    lpNumberOfBytesRead As Long, _
    ByVal lpOverlapped As Long _
) As Long

Public Declare Function WriteFile Lib "kernel32" ( _
    ByVal hFile As Long, _
    lpBuffer As Any, _
    ByVal nNumberOfBytesToWrite As Long, _
    lpNumberOfBytesWritten As Long, _
    ByVal lpOverlapped As Long _
) As Long

Public Declare Function SetCommTimeouts Lib "kernel32" ( _
    ByVal hFile As Long, _
    lpCommTimeouts As COMMTIMEOUTS _
) As Long

Public Declare Function CloseHandle Lib "kernel32" ( _
    ByVal hObject As Long _
) As Long

Public Function CommOpen(ByVal strPort As String) As Long
CommOpen = CreateFile(strPort, GENERIC_READ Or GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0)
End Function

Public Function CommInput(ByVal hCom As Long, strBuffer As String, ByVal lngLen As Long) As Long
Dim lngRead As Long
Call ReadFile(hCom, ByVal strBuffer, lngLen, lngRead, 0)
CommInput = lngRead
End Function

Public Function CommOutput(ByVal hCom As Long, ByVal strBuffer As String, ByVal lngLen As Long) As Long
Dim lngWrite As Long
Call WriteFile(hCom, ByVal strBuffer, lngLen, lngWrite, 0)
CommOutput = lngWrite
End Function

Public Function CommClose(ByVal hCom As Long) As Boolean
CommClose = CloseHandle(hCom)
End Function

Public Function SetCommTimeout(ByVal hCom As Long, typTimeout As COMMTIMEOUTS) As Boolean
SetCommTimeout = SetCommTimeouts(hCom, typTimeout)
End Function
投稿者 ザボン  (社会人) 投稿日時 2014/12/1 14:17:37
質問しなれてないので申し訳ありませんでした。

1、エクセル2010 32ビット
2、Windows 7 Professional SP1 32ビット
3、API使ってます。kernel32?下のプログラムを見ていただいたらわかりますでしょうか?
4、PC本体の232ポート(9600、7ビット、1ストップ、パリティなし、単方向、非同期、無手順)
5、プログラムを載せます。
  受信データは「ON_MEAS2,141201,0100,S#1,TN,1.23,F%%,456.7,890CRLFON_MEAS2,141201,0100,S#1,TP,1.23,F%%,456.7,890CRLF」
  「固定文、日付、時間、固定文、固定文、値、固定文、値、値改行コード固定文、日付・・・」
  このように2行1時間毎に出力されてきます。
投稿者 daive  (社会人) 投稿日時 2014/11/29 18:56:07
>VBA7.0でデータを受信しシートに印字するプログラムを自作しています。
と書かれても、何をしているか、掲示板では解りません。
1.OFFICEの何を使っているのか?EXCEL 20xx SPx , ACCESS 20xx SPx など具体的に。
  32ビット(x32 or x86)、64ビット版どちらを使っているのか?
  VBAは、OFFICE製品内でも、同一仕様ではありません。
2.OSは何を使っているのか、具体的に
  Windows 7 Professional SP1 32ビット など
3.232通信は、Windows API で行っているのか?
  何らかのコンポーネント(DLL等)を使って行っているのか?(具体的な製品名など)
4.232通信は何で行っているのか?通信設定は、しかじか。
  PC本体の232ポート、USB-シリアルアダプタ(メーカーは、、、、)
  9600、8ビット、1ストップ、パリティ無
  など
5.出来れば回答者が再現可能であるような、最低限のコード(プログラム)やら、
  コードが出せないのであれば、
  質問者さんが、回答者に解るように、何時何処で誰が何をどの様に如何する如何したを
  書かれないと伝わらないかと。
'
Visual Studio Cmmunity 2013 
が、使用許諾範囲で無償使用できるように
なりました。
(VS2013 Professiona 相当)
 http://www.microsoft.com/ja-jp/dev/products/community.aspx

処理内容次第ですが、VBAで無理無理作るより、
1.通信系部分: VB.NET / C# で作成、データをファイル化
2.データ処理部分:VBA7で
という事の方が簡単では?

内容によっては、VB.NET / C# で完結するかもしれません。

232通信コード例、検索ワード
nonsoft 232 通信
VBA 232 通信
VBA 232 Windows API
VBA 232 ドライバー

VB6 のランタイム、モジュールを使う場合は、使用許諾に十分に注意してください。
VB6用アプリの一部としてのみ使用許諾される事が前提の筈です。
(VBA用としては使用許諾されない可能性を、要検討)
対して、.NET Framework は、使用許諾範囲が緩いです。
投稿者 ザボン  (社会人) 投稿日時 2014/11/29 15:43:46
ある機械とRS232Cシリアル通信でデータをパソコンで一方的に受け取っています。

VBA7.0でデータを受信しシートに印字するプログラムを自作しています。

データは1時間毎に機械から送信されてきます。

プログラムはループで常に受信データ格納先を確認し、データが入ったらループを抜け次の処理をするプログラムです。

この方法ではたまにデータを取りこぼしたりおかしなデータになってしまいます。(24回に1回か2回)

原因の予想ですが
非同期で通信しているため、データを少しでも格納したらReadFileが受信完了!みたいに思って次の 動作に移ってしまうのではないかと。

理想はループを使用せず、受信データ格納先にデータがすべて入ったらイベントが発生?プログラムが動作するようにしたいのですが、可能でしょうか?

ついでにですが、
ネットで調べまくって何とかここまでできたのですが、基礎をあんまり勉強していないので標準モジュールとかReadFileやCreateFileが完全に理解できていません。
標準モジュールに作ったプログラムは常に実行されているんでしょうか?
ポートを開きReadFileを常に実行しているからデータを受信できるのでしょうか?

質問のしかたが下手で申し訳ないですがご指導よろしくお願いいたします。