Question

I have a bar-code reader which is connected to PC on RS232 serial port. I am writing a C++ code to send command to barcode scanner and get the response back to the PC. Currently program can send data to the device correctly but it is failed to read the response from barcode scanner. In this case once we send a command to barcode reader it will response back with a positive or negative acknowledgement.

e.g:- Send BEEP command.
 1. Host(PC) send a BEEP command to barcode scanner
 2. Barcode scanner make a beep sound and send the acknowledgement back
    to host (PC)
 3. Host (PC) read the acknowledgement

in below code first 2 step are working properly but I couldn't write third one correctly. Please somebody help me to correct my source code to read the response from barcode scanner asynchronously.

main.cpp

#include <iostream>
extern "C"
{
    #include <stdlib.h>
    #include <stdio.h>
    #include <unistd.h>
}
#include "DeviceRS232.h"
#include "Message.h"


int main()
{
    unsigned char recvBuffer[257];
    unsigned char ledOn[] = {0x05, 0xE7, 0x04, 0x00, 0x0D, 0x00};
    unsigned char SSIBuffer[] = {0x00, 0xC6, 0x04, 0x08, 0x11, 0xEE, 0x01};
    unsigned char requestRevision[] = {0x00, 0x04, 0xA3, 0x04, 0x00};
    unsigned char sendBeep[] = {0x00, 0xE6, 0x04, 0x00, 0x05};

    Message beepCommand(sendBeep, sizeof(sendBeep)/sizeof(sendBeep[0]));

    std::cout << "*********************************************************" << std::endl << std::endl;
    DeviceRS232 dev_rs232;
    dev_rs232.setDefaultAttributes();
    dev_rs232.openSerialPort();


    // Send BEEP command several times.
    std::cout << "---Start sending beep---" << std::endl;
    for(int x=0; x<1; x++)
    {
        int sizeSent = dev_rs232.sendDataBuffer(beepCommand.getCommandData(), beepCommand.getLen());
        if( sizeSent > 0)
        {
            std::cout << "Data sent: " <<  sizeSent << std::endl;
        }

        memset(recvBuffer, 0, sizeof(recvBuffer));
        int recvSize = dev_rs232.receiveDataBuffer(recvBuffer, sizeof(recvBuffer));
        std::cout << "Date Received, Data: " <<  recvBuffer << " Size: " << recvSize << std::endl;
        sleep(2);
        /**
        while(true)
        {
            memset(recvBuffer, 0, sizeof(recvBuffer));
            int recvSize = dev_rs232.receiveDataBuffer(recvBuffer, sizeof(recvBuffer));
            if(recvSize > 0)
                std::cout << "Date Received, Data: " <<  recvBuffer << " Size: " << recvSize << std::endl;
            sleep(2);
        }*/
    }
    std::cout << "---End sending beep-----\n" << std::endl;


    dev_rs232.closeSerialPort();
    std::cout << "*********************************************************" << std::endl;


    return 0;
}

Message.h

#ifndef MESSAGE_H
#define MESSAGE_H

#include <iostream>
#include <string>
#include <numeric>
extern "C"
{
    #include <stdlib.h>
    #include <stdio.h>
    #include <unistd.h>
}

class Message
{
    public:
        Message();              //  default constructor
        virtual ~Message();     //  destructor


        Message(const std::basic_string<unsigned char> msg) : commandMsg(msg)
        {
            printf("msg[0]:%x\n", msg[4]);
            std::cout << "length: " << commandMsg.length() << std::endl;

            //commandMsg[0] = commandMsg.length();
            appendChecksum();
        };

        Message(const unsigned char *msg, int msglen) : commandMsg(msg, msglen)
        {
            commandMsg[0] = commandMsg.length();
            appendChecksum();
        };

        const unsigned char *getCommandData() const
        {
            return commandMsg.c_str();
        }

        int getLen() const
        {
            return commandMsg.length();
        }


    protected:
    private:
        int appendChecksum();
        std::basic_string<unsigned char> commandMsg;
};

#endif // MESSAGE_H

Message.cpp

#include "Message.h"

Message::Message()
{
    //ctor
}

Message::~Message()
{
    //dtor
}

int Message::appendChecksum()
{
    int sum = -std::accumulate(commandMsg.begin(), commandMsg.end(), 0);

    commandMsg.push_back(0xFF & (sum >> 8));
    commandMsg.push_back(0xFF & sum);
}

DeviceRS232.h

#ifndef DEVICERS232_H
#define DEVICERS232_H

extern "C"
{
#include <stdio.h>
#include <string.h>
#include <termios.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <limits.h>
#include <stdlib.h>
}

#include <string>

#define MAX_SERIAL_PORT_NO  30



class DeviceRS232
{
    public:
        DeviceRS232();
        virtual ~DeviceRS232();

        int fdRS232;                    //  file descriptor for the serial port

        void setSerialPort(std::string sp);
        void setBaudRate(long baud);
        void setDataBits(int dataBit);
        void setStopBits(int stopBit);
        void setNumberOfParityBits(int nparityBits);
        void setDefaultAttributes();
        long getBaudRate();
        std::string getSerialPort();
        int openSerialPort();
        int readUserConfiguration();
        int sendDataBuffer(const unsigned char *dataBuffer, size_t bufferSize);
        int receiveDataBuffer(unsigned char *dataBuffer, size_t bufferSize);
        void closeSerialPort();


    protected:
        std::string serialPort;         //  Serial port like /dev/ttyS0
        long baudRate;                  //  Scanner baud rate
        int dataBits;                   //  data bits
        int stopBits;                   //  stop bits
        int numberOfParityBits;         //  number of parity bits
        termios oldSerialPortSetting;   //  Current values of termios structure for /dev/ttyS0
        termios newSerialPortSetting;   //  new termios attributes for /dev/ttyS0


    private:
};

#endif // DEVICERS232_H

DeviceRS232.cpp

#include "DeviceRS232.h"

DeviceRS232::DeviceRS232()
{
    //ctor
}

DeviceRS232::~DeviceRS232()
{
    //dtor
}

void DeviceRS232::setSerialPort(std::string sp)
{
    serialPort = sp;
}

void DeviceRS232::setBaudRate(long baud)
{
    baudRate = baud;
}

void DeviceRS232::setDataBits(int dataBit)
{
    dataBits = dataBit;
}

void DeviceRS232::setStopBits(int stopBit)
{
    stopBits = stopBit;
}

void DeviceRS232::setNumberOfParityBits(int nparityBits)
{
    numberOfParityBits = nparityBits;
}

void DeviceRS232::setDefaultAttributes()
{
    std::string sp = "/dev/ttyS0";
    long baud = 9600;
    int dataBit = 1;
    int stopBit = 1;
    int nparityBits = 0;

    setSerialPort(sp);
    setBaudRate(baud);
    setDataBits(dataBit);
    setStopBits(stopBit);
    setNumberOfParityBits(nparityBits);
}

long DeviceRS232::getBaudRate()
{
    return baudRate;
}

std::string DeviceRS232::getSerialPort()
{
    return serialPort;
}

int DeviceRS232::openSerialPort()
{
    int fd, baudr, status, portStatus;
    setDefaultAttributes();

    switch(getBaudRate())
    {
        case      50 : baudr = B50;
                       break;
        case      75 : baudr = B75;
                       break;
        case     110 : baudr = B110;
                       break;
        case     134 : baudr = B134;
                       break;
        case     150 : baudr = B150;
                       break;
        case     200 : baudr = B200;
                       break;
        case     300 : baudr = B300;
                       break;
        case     600 : baudr = B600;
                       break;
        case    1200 : baudr = B1200;
                       break;
        case    1800 : baudr = B1800;
                       break;
        case    2400 : baudr = B2400;
                       break;
        case    4800 : baudr = B4800;
                       break;
        case    9600 : baudr = B9600;
                       break;
        case   19200 : baudr = B19200;
                       break;
        case   38400 : baudr = B38400;
                       break;
        case   57600 : baudr = B57600;
                       break;
        case  115200 : baudr = B115200;
                       break;
        case  230400 : baudr = B230400;
                       break;
        case  460800 : baudr = B460800;
                       break;
        case  500000 : baudr = B500000;
                       break;
        case  576000 : baudr = B576000;
                       break;
        case  921600 : baudr = B921600;
                       break;
        case 1000000 : baudr = B1000000;
                       break;
        default      : printf("invalid baudrate\n");
                       return(1);
                       break;
    }

    //  Open serial port
    fd = open(getSerialPort().c_str(),  O_RDWR | O_NOCTTY | O_NDELAY);
    if(fd == -1)
    {
        printf("Unable to open serial port...\n");
        perror(getSerialPort().c_str());
        return 1;
    }

    fdRS232 = fd;

    fcntl(fdRS232, F_SETFL, FNDELAY);
    status = tcgetattr(fdRS232, &oldSerialPortSetting);
    if(status == -1)
    {
        close(fdRS232);
        printf("Unable to get serial port attributes...\n");
        return 1;
    }

    memset(&newSerialPortSetting, 0, sizeof(newSerialPortSetting));
    newSerialPortSetting.c_cflag = baudr | CS8 | CLOCAL | CREAD; //
    newSerialPortSetting.c_iflag = IGNPAR;
    newSerialPortSetting.c_oflag = 0;
    newSerialPortSetting.c_lflag = 0;
    newSerialPortSetting.c_cc[VMIN] = 0;
    newSerialPortSetting.c_cc[VTIME] = 0;

    status = tcsetattr(fdRS232, TCSANOW, &newSerialPortSetting);
    if(status==-1)
    {
        close(fdRS232);
        perror("unable to adjust portsettings ");
        return 1;
    }

    //  Get the status of opened serial port
    if(ioctl(fdRS232, TIOCMGET, &portStatus) == -1)
    {
        perror("Unable to get port status");
        return 1;
    }

    //  Tern on DTR and RTS
    portStatus |= TIOCM_DTR;
    portStatus |= TIOCM_RTS;

    //  Set the status of the port with new DTR, RTS values
    if(ioctl(fdRS232, TIOCMSET, &portStatus) == -1)
    {
        perror("Unable to set port status...");
        return 1;
    }

  return 0;
}

int DeviceRS232::sendDataBuffer(const unsigned char *dataBuffer, size_t bufferSize)
{
    return write(fdRS232, dataBuffer, bufferSize);
}

int DeviceRS232::receiveDataBuffer(unsigned char *dataBuffer, size_t bufferSize)
{
    /**int recvSize = 0;
    recvSize = read(fdRS232, dataBuffer, bufferSize);
    return recvSize;*/

    unsigned char recvBuffer[255];
    unsigned char *ptrChar;
    int nBytes;

    ptrChar = recvBuffer;
    memset(recvBuffer, 0x00, sizeof(recvBuffer));
    while((nBytes = read(fdRS232, ptrChar, recvBuffer+sizeof(recvBuffer) - ptrChar -1)) > 0)
    {
        ptrChar += nBytes;
        //printf("while - %d\n", nBytes);
    }

    //printf("recvBuffer : %x\n", recvBuffer[0]);
    //printf("recvBuffer : %x\n", recvBuffer[1]);
    //printf("recvBuffer : %x\n", recvBuffer[2]);
    //printf("recvBuffer : %x\n", recvBuffer[3]);
    //printf("recvBuffer : %x\n", recvBuffer[4]);
    dataBuffer = recvBuffer;

    return nBytes;
}

void DeviceRS232::closeSerialPort()
{
    int portStatus;

    if(ioctl(fdRS232, TIOCMGET, &portStatus) == -1)
    {
        perror("Unable to get the port status");
    }

    //  Tern off DTR and RTS
    portStatus &= ~TIOCM_DTR;
    portStatus &= ~TIOCM_RTS;

    //  Set the status of the port with new DTR, RTS values
    if(ioctl(fdRS232, TIOCMSET, &portStatus) == -1)
    {
        perror("Unable to set port status...");
    }

    close(fdRS232);
}

my bad method is int DeviceRS232::receiveDataBuffer(unsigned char *dataBuffer, size_t bufferSize)

Below is the console output:

*********************************************************

---Start sending beep---
Data sent: 7
Date Received, Data:  Size: 0
---End sending beep-----

*********************************************************

Process returned 0 (0x0)   execution time : 2.004 s
Press ENTER to continue.
Was it helpful?

Solution 2

/**
 * Receive responses from the decoder
 */
int DeviceRS232::receiveDecodedData(unsigned char *dataBuffer, size_t bufferSize)
{
    unsigned char recvBuffer[251];
    unsigned char *ptrChar;
    int nBytes, portStatus;
    int inputBufSize = 0;

    ChangeCTS(fdRS232, 0);
    ChangeRTS(fdRS232, 0);

    while(inputBufSize <= 0)
    {
        ioctl(fdRS232, FIONREAD, &inputBufSize);
        usleep(1);
    }


    if(inputBufSize > 0)
    {
        int decodePacketLen = 0;
        //unsigned char
        memset(recvBuffer, 0x00, sizeof(recvBuffer));
        nBytes = 0;

        //usleep(100000);
        while(nBytes < ((int)recvBuffer[0] + 2))
        {
            int index = 0;
            int recvDataLen = 0;
            if(nBytes != 0)
                index = nBytes - 1;

            recvDataLen = read(fdRS232, &recvBuffer[index], 251);
            if(recvDataLen < 0)
            {
                std::cout << "[INFO@DeviceRS232::receiveDecodedData]File read error: " << strerror(errno) << std::endl;
                //sleep(1);
            }

            nBytes += recvDataLen;
            if(nBytes == ((int)recvBuffer[0] + 2))
                break;

        }

        if(recvBuffer[1] == DECODE_DATA)
            sendCommandToDecoder(OPCODE_ACK);

        std::cout << "[INFO @ DeviceRS232::receiveDecodedData]Data Lenght (without CheckSum) : " << (int)recvBuffer[0] << std::endl;

        for(int i=0; i<nBytes; i++)
        {
            std::cout << "recvBuffer[" << i << "]: ";
            printf("%x\n", recvBuffer[i]);
        }
        std::cout << "-----------------------------------" << std::endl;

        ChangeRTS(fdRS232, 1);
        ChangeCTS(fdRS232, 1);
        //sleep(1);
    }

    //strcpy((char *)dataBuffer, (char *)recvBuffer);
    memcpy((char *)dataBuffer, recvBuffer, sizeof(recvBuffer)/sizeof(recvBuffer[0]));
    inputBufSize = 0;

    return nBytes;

}


/**
 * Send commands to the decoder.
 */
int DeviceRS232::sendCommandToDecoder(unsigned int opCode)
{
    unsigned char *commandBuffer;
    int commandLength;

    switch(opCode)
    {
    case OPCODE_ACK:
        {
            unsigned char ackString[] = {0x00, 0xD0, 0x04, 0x00};
            commandLength = sizeof(ackString);
            commandBuffer = ackString;
        }
        break;
    case OPCODE_PARAM_SEND:
        {
            unsigned char paramSendString[] = {0x00, 0xC6, 0x04, 0x08, 0x00, 0xEE, 0x01};
            commandLength = sizeof(paramSendString);
            commandBuffer = paramSendString;
        }
        break;
    default:
        break;
    }

    Message msgCommand(commandBuffer, commandLength);

    return sendDataBuffer(msgCommand.getCommandData(), msgCommand.getLen());
}

Required constants defined in DeviceRS232.h header file.

OTHER TIPS

As others have noted, one suspect area is the number of bytes you're sending. Instead of always sending 257 bytes, the barcode reader is probably expecting just the number of bytes in the command and no more.

Also, your code has a number of repeated operations for calculating the checksum at the end of the message. That suggests a class would help streamline the design. Here, then, is a Message class for that purpose:

#include <vector>
#include <numeric>
#include <string>

class Message 
{
public:
    Message(const std::basic_string<unsigned char> msg) : mymsg(msg) { 
        mymsg[0] = mymsg.length(); appendChecksum(); };
    Message(const unsigned char *msg, int msglen) : mymsg(msg, msglen) { 
        mymsg[0] = mymsg.length(); appendChecksum(); };
    const unsigned char *getData() const { return mymsg.c_str(); }
    size_t getLen() const { return mymsg.length(); }

private:
        int appendChecksum();
        std::basic_string<unsigned char> mymsg;
};


int Message::appendChecksum()
{
    int sum = -std::accumulate(mymsg.begin(), mymsg.end(), 0);
    mymsg.push_back(0xff & (sum >> 8));
    mymsg.push_back(0xff & sum);
    return sum;
}

Now within your main routine, you can eliminate several dozen lines of code and use these instead (if you're using C++11):

    Message setparams{{0x00, 0xc6, 0x04, 0x08, 0x11, 0xee, 0x01}};
    Message beep{{0x00, 0xe6, 0x04, 0x00, 0x05}};
    Message getrevision{{0x00, 0xA3, 0x04, 0x00}};
    Message ledOn{{0x00, 0xe7, 0x04, 0x00, 0x0d, 0x00}};

If you're not using C++11 (which would be a shame!) you can use this slightly less clean style instead:

unsigned char parms[] = {0x00, 0xc6, 0x04, 0x08, 0x11, 0xee, 0x01};
Message setparams(parms,sizeof(parms)/sizeof(parms[0]));

Note that the first byte is set to zero instead of the length. This is done because the constructor automatically calculates and sets the proper length in that byte just before it calculates and appends the checksum. There are other ways to do this, of course, but I'll leave that to you.

Finally, with your loop, you can now use this line:

int sizeSent = dev_rs232.sendDataBuffer(beep.getData(), beep.getLen());

That may or may not actually solve the problem, but it will assist you to have a cleaner program to start with.

Also, a few pieces of style and design advice:

  1. get out of the habit of using namespace std
  2. use iostream instead of printf
  3. instead of having to call setDefaultAttributes() immediately after creating the device, have the constructor set up sane defaults
  4. eliminate "magic numbers" such as 4096 for the receive buffer size. Instead, use named static const. It will make the program more understandable and maintainable.
  5. consider using an existing library such as boost::asio instead of rolling your own

Good luck!

Edit: Based on your keen (and correct!) observation that the Message constructor doesn't work properly when fed a plain unsigned char *, I've added a second constructor and modified the non C++11 version of the code. Sorry for any inconvenience, and thanks for keeping me honest.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top