2

C++ コードを使用して、USB 経由で接続されたモデムに AT コマンドを送信しようとしています。この特定のモデムで電話番号を返す「AT+MDN」を使用してテストしており、シリアルポートに接続するパテを介してテストすると正しく動作します。GetComPort は、モデムが接続されている COM ポート番号を決定し、そのハンドルを作成します。sendCommand() メソッドはポートを開き、コマンドを送信し、バッファをフラッシュしてから結果を読み取ろうとしますが、送信されたコマンドしか受信しないため、この例では「AT+MDN」を受信します。この問題に対して見つけたさまざまな修正 (バッファをフラッシュし、書き込みと読み取りの間にスリープを追加する) をすべて試しましたが、どれも機能しませんでした。

SerialATDT.cpp

#include "SerialATDT.h"
#include "EventSem.h"
#include "Traces.h"
#include <setupapi.h>
#include <devguid.h>
#include <regstr.h>

SerialATDT::SerialATDT(String ident) : mComPortIdentifier(ident)
{
}

SerialATDT::~SerialATDT(void)
{
}

String SerialATDT::getComPortId() 
{
  HDEVINFO hDevInfo;
  SP_DEVINFO_DATA DeviceInfoData;
  int devOffset = 0;
  DWORD propertyDataType;
  HKEY devKey;
  DWORD portNameSize;
  DWORD result;
  String comPort = "";
  BYTE friendlyName[4096];
  TCHAR devName[4096];
  TCHAR portName[4096];

  // Create a HDEVINFO with all present devices.
  hDevInfo = SetupDiGetClassDevs(&GUID_DEVCLASS_MODEM, 0, 0, DIGCF_PRESENT);
  if (hDevInfo == INVALID_HANDLE_VALUE)
  {
    TRACE_L(TEXT("\nSetupDiGetClassDevs() failed: %d\n"), GetLastError());
    return "";
  }

  // Enumerate through all devices in Set.
  DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
  while (SetupDiEnumDeviceInfo(hDevInfo, devOffset++, &DeviceInfoData))
  {

    if (!SetupDiGetDeviceRegistryProperty(hDevInfo, &DeviceInfoData, SPDRP_FRIENDLYNAME,
      &propertyDataType, friendlyName, sizeof(friendlyName), NULL))
    {
      TRACE_L(TEXT("\nSetupDiEnumDeviceInfo() failed: %d\n"), GetLastError());
      continue;
    }

    // Look for identifying info in the name
    if ( mComPortIdentifier.size() > 0 ) {
      const char *temp = strstr((const char*)friendlyName, mComPortIdentifier.c_str());

      if ( temp == 0 ) {
        continue;
      }
    }

    // Get the device name.
    if (!SetupDiGetDeviceInstanceId(hDevInfo, &DeviceInfoData, devName, MAX_PATH, NULL))
    {
      TRACE_L(TEXT("\nSetupDiGetDeviceInstanceId() failed: %d\n"), GetLastError());
      continue;
    }

    // Open the registry key associated with the device.
    devKey = SetupDiOpenDevRegKey(hDevInfo, &DeviceInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ);
    if (devKey == INVALID_HANDLE_VALUE)
    {
      TRACE_L(TEXT("\nSetupDiOpenDevRegKey() failed: %d\n"), GetLastError());
      continue;
    }

    // Read the PortName registry key.
    portNameSize = sizeof(portName);
    result = RegQueryValueEx(devKey, TEXT("PortName"), NULL, NULL, (LPBYTE) portName, &portNameSize);
    if(result != ERROR_SUCCESS)
    {
      TRACE_L(TEXT("\nRegQueryValueEx() failed: %d\n"), result);
      continue;
    }

    // We are not guaranteed a null terminated string from RegQueryValueEx, so explicitly NULL-terminate it.
    portName[portNameSize / sizeof(TCHAR)] = TEXT('\0');

    // Close the registry key.
    result = RegCloseKey(devKey);
    if (result != ERROR_SUCCESS)
    {
      TRACE_L(TEXT("\nRegCloseKey() failed: %d\n"), result);
      continue;
    }

    // Try to open the COM port.
    comPort = portName;
  }

  SetupDiDestroyDeviceInfoList(hDevInfo);

  return comPort;
}

bool SerialATDT::getComPort(HANDLE *hFile)
{
  String comPort = getComPortId();

  *hFile = INVALID_HANDLE_VALUE;

  if ( comPort.size() > 0 ) {
    String comPortStr;

    comPortStr.Format("\\\\.\\%s", comPort.c_str());

    *hFile = ::CreateFile( comPortStr.c_str(),
      GENERIC_READ | GENERIC_WRITE,
      0,
      NULL,
      OPEN_EXISTING,
      0,
      NULL );

    if ( *hFile == INVALID_HANDLE_VALUE ) {
      TRACE_L("AT file open error %ld", GetLastError());
    }
  }

  return *hFile != INVALID_HANDLE_VALUE;
}

bool SerialATDT::sendCommand(String command, String &response)
{
  bool retFlag = true;
  HANDLE hFile = NULL;

  response = "";

  if ( !getComPort(&hFile) ) {
    TRACE("SerialATDT-Unable to get comport");
    return false;
  }

  ::SetupComm( hFile, 2048, 2048 );

  DCB dcb = { 0 };
  dcb.DCBlength = sizeof(DCB);
  ::GetCommState( hFile, &dcb );

  dcb.BaudRate = 9600;

  // set additional parameters for 8-bit, no parity, one stop bit, no flow control
  dcb.fRtsControl     = RTS_CONTROL_DISABLE;
  dcb.ByteSize        = 8;
  dcb.Parity          = NOPARITY;
  dcb.StopBits        = ONESTOPBIT;
  dcb.fOutxCtsFlow    = FALSE;
  dcb.fOutxDsrFlow    = FALSE;
  dcb.fDtrControl     = DTR_CONTROL_DISABLE;
  dcb.fDsrSensitivity = FALSE;
  dcb.fOutX           = FALSE;
  dcb.fInX            = FALSE;

  ::SetCommState( hFile, &dcb );

  // Retrieve the timeout parameters for all read and write operations on the port. 
  COMMTIMEOUTS CommTimeouts;
  ::GetCommTimeouts (hFile, &CommTimeouts);

#define BUFFER_LENGTH 256

  // Change the COMMTIMEOUTS structure settings.
  CommTimeouts.ReadIntervalTimeout = 50;  
  CommTimeouts.ReadTotalTimeoutMultiplier = (2000/BUFFER_LENGTH);  // Only wait 2 seconds (2000ms).
  CommTimeouts.ReadTotalTimeoutConstant = 50;    
  CommTimeouts.WriteTotalTimeoutMultiplier = 50;  
  CommTimeouts.WriteTotalTimeoutConstant = 50;    

  // Set the timeout parameters for all read and write operations on the port. 
  ::SetCommTimeouts (hFile, &CommTimeouts);

  char buffer[BUFFER_LENGTH+1];
  OVERLAPPED m_ReadSync;
  unsigned error = 0;
  EventSem readWait;
  bool finished = false;


  memset(&m_ReadSync, 0, sizeof(OVERLAPPED));

  m_ReadSync.hEvent = readWait.GetHandle();
  m_ReadSync.Pointer = (void*)buffer;

  static int index = 0;

  // Edited here
  command += "\r";

  // First write the command out to the serial port
  UInt32 numBytesWritten = 0;
  UInt32 totalBytesWritten = 0;
  do {
    if ( !::WriteFile(hFile, command.c_str(), command.length(), &numBytesWritten, NULL) ) {
      TRACE("SerialATDT-sendCommand failed on sending.");
      retFlag = false;
      break;
    }

    totalBytesWritten += numBytesWritten;
  } while (totalBytesWritten < command.length() );

  ::FlushFileBuffers(hFile);

  // Read in the response
  if ( retFlag ) {
    Sleep(1000);

    while ( !finished ) {
      UInt32 numBytesRead = 0;

      buffer[0] = '\0';

      if ( !::ReadFile(hFile, buffer, BUFFER_LENGTH, &numBytesRead, &m_ReadSync ) )
        {
        if ( ( error = ::GetLastError() ) == ERROR_IO_PENDING )
          {
          error = 0;
          if ( !::GetOverlappedResult( hFile, &m_ReadSync, &numBytesRead, true ) )
            {
            error = ::GetLastError();
            }
          }
        }

      // We read something
      if ( numBytesRead > 0 ) {
        buffer[numBytesRead] = '\0';
        response += buffer;
      } else { // We timed out so just drop out
        finished = true;
      }
    }
  }

  // Remove the command from what we read in the response
  if ( response.find(command) != std::string::npos ) {
    response = response.substr(command.length());
  }

  if ( hFile != INVALID_HANDLE_VALUE ) {
    ::CloseHandle( hFile );
  }

  return retFlag;
}

および SerialATDT.h

#ifndef SERIALATDT_H
#define SERIALATDT_H

#pragma once

#include "SgiString.h"
#include "FileSystem.h"

using namespace sgi;

class SerialATDT
{
private:
  String mComPortIdentifier;

  bool getComPort(HANDLE *hFile);
  String getComPortId();

public:
  SerialATDT(String ident);
  ~SerialATDT(void);

  bool sendCommand(String command, String &response);
};

#endif
4

1 に答える 1

4

書き込んだ文字列が戻ってきた場合、それはモデムからの意図的なエコーである可能性があります (これは、Putty で入力した内容を確認できる 1 つの方法です)。もう一度読んでみましたか?また、書き込み文字列の行末が異なることを確認しましたか。改行 "\n" のみが必要なモデムもあれば、復帰改行 "\r\n" が必要なモデムもあります。行末が正しくないと、モデムが AT コマンドを実行していない可能性があります。

于 2013-02-20T20:45:49.117 に答える