Общение по стандарту RS232 на примере китайского кардридера

в 14:22, , рубрики: C#, RS232 VB PASCAL Ms-DOS C#, Программирование, метки:

В этом топике я хотел бы поделиться своим опытом «борьбы» с одним китайским девайсом. На работе поступил приказ и было принято поменять старые кардридеры на новые. Суть в том, что старые ридеры были активные, т.е. в бесконечном цикле пытались считать карту, а вот новые уже были пассивными — работать не будут, пока не подашь на них команду. Модель аппарата CR501AU V3 (вдруг кому пригодиться). Китайский девайс, документации никакой нет (только на китайском). И всё что у меня было на руках это коробочка с этикеткой модели и кое-какой исходник. Исходник, к слову, был из другого нашего отдела, который был написан на 1С. Но, так как штат программистов у нас в конторе обновился, не осталось тех людей, которые работали над этими старыми исходниками, поэтому пришлось разбираться. Поначалу всё было просто. В исходный код 1С был вставлен кусок скрипта на Visual Basic, который отвечал полностью за работу с ридером. Ничего под рукой для работы с VB не было, поэтому воспользовался Excel-евский компилятором.

Function ReedCard()
MSComm1.CommPort = 1 'Выставляем порт
MSComm1.Settings = "19200,N,8,1" 'Выставляем настройки
MSComm1.InputLen = 0 
On Error Resume Next
MSComm1.PortOpen = True

MSComm1.Output = Chr(&HAA) & Chr(&HBB) & Chr(&H6) & Chr(&H0) & Chr(&H0) & Chr(&H0) & Chr(&H1) & Chr(&H2) & Chr(&H52) & Chr(&H51)  'Вот это строка для общения с ридером. Первая нужна для "пробуждения" ридера. Отправляем символы в hex виде
sleep (0.1) 'Спим немного, чтоб не бомбить девайс(иногда не работает корректно без сна)
Answer = MSComm1.Input
MSComm1.Output = Chr(&HAA) & Chr(&HBB) & Chr(&H5) & Chr(&H0) & Chr(&H0) & Chr(&H0) & Chr(&H2) & Chr(&H2) & Chr(&H0) 'Вторая команда пришлет нам ответ. Это то, что нам нужно — код карты. Правда, пока только ASCII-символами
sleep (0.1) 
Answer = MSComm1.Input

Dim ReadPort As String

If Len(Answer) > 10 Then 'Если при попытке считать карта была на ридере, то ответом на предыдущую команду будет строка из 14-ти символов. Если на ридер были отправлены команды на считывание, но карты не было на месте, то в ответ будет возвращено 5 символов. Поэтому, если символов >10, то обрабатываем карту

Dim a: a = s2a(Answer) 'StringToArray-функция. Просто переводит нашу строку из 14 символов в массив[14]
Dim i
For i = 0 To UBound(a)
    a(i) = Right(0 & Hex(Asc(a(i))), 2) 'В каждом элементе массива "а" у нас хранился какой-то ASCII-символ. Этот цикл пробегается по каждому символу, берет его ascii-код и переводит его в hex
Next
    ReadPort = Join(a)
    MSComm1.Output = Chr(170) & Chr(187) & Chr(6) & Chr(0) & Chr(0) & Chr(0) & Chr(6) & Chr(1) & Chr(7) & Chr(0) 'А эта команда нужна только для того, чтобы "пикнуть" :)  Т.е. ридер просто издаст звук(это для того, чтоб было понятно, что ридер считал карту)
    
'''' Тут дальше идет мой код, который просто переводит хексовую строку в число. Например: AA BB CC DD -> 2864434397. Тут много кода, но вам он не нужен — вы и сами сможете его написать.
End If
MSComm1.PortOpen = False
End Sub

 Function s2a(s)
        ReDim a(Len(s) - 1)
        Dim i
        For i = 0 To UBound(a)
            a(i) = Mid(s, i + 1, 1)
        Next
        s2a = a
    End Function
    
    Sub sleep(sk)
        PauseTime = sk
        Start = Timer
        Do While Timer < Start + PauseTime
        Loop
    End Sub

Не так уж и сложно. Но проблема заключалась в том, что этот код на VB мне нужно было перенести на Pascal в MS-DOS. Несколько дней бился с этим, но скармливая эти команды в Com-порт ридер не подавал признаков жизни. Тогда я решил сначала реализовать это на своём «родном» языке — C#. Так же пришлось попотеть, но в итоге понял в чем кроется разгадка!

serialPort1.Open();
serialPort1.Write(((char)0xAA) + "" + ((char)0xBB) + "" + ((char)0x6) + "" + ((char)0x0) + "" + ((char)0x0) + "" + ((char)0x0) + "" + ((char)0x6) + "" + ((char)0x1) + "" + ((char)0x7) + "" + ((char)0x0));
serialPort1.Close();

Код выше неправильный. Я какое-то время над ним экспериментировал, но он никак не хотел работать. И кавычки убирал. И .ToString() добавлял. И посимвольно писал в порт. Ничего. И потом как вдруг дошло:

serialPort1.Open();
byte[] ar = new byte[9];
ar[0] = 170;
ar[1] = 187;
ar[2] = 6;
ar[3] = 0;
ar[4] = 0;
ar[5] = 0;
ar[6] = 6;
ar[7] = 1;
ar[8] = 7;
ar[8] = 0;
serialPort1.Write(ar, 0, ar.Length);
serialPort1.Close();

И всё заработало! Осталось только перенести это в паскаль:

var
ar: array of Byte[10];
begin
OpenCom (ComNo, B_19200, Bits_8+Stops_1+Parity_No, 2048);
ar[0]=170;
ar[1]=187;
ar[2]=6;
ar[3]=0;
ar[4]=0;
ar[5]=0;
ar[6]=6;
ar[7]=1;
ar[8]=7;
ar[9]=0;

for i:=0 to 10 do begin
WriteCom(ComNo,ar[i]);
end;

Код выше заставляет ридер «пикнуть». Дальше переделать всё под себя очень просто.

P.S. Информации по этой проблеме в ру-сегменте не нашел совсем, на англоязычных же есть, но крупицы. От себя информацию по работе с RS232 на примере китайского кардридера собрал. Надеюсь кому-нибудь поможет. Удачи!

Автор: Votming

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js