Node.js Barcode Scanner with UVC Camera for Raspberry Pi

Raspberry Pi has been widely used in industrial environments. For instance, you can make a barcode scanner with Raspberry Pi and camera. This article will be useful for developers who want to create Node.js barcode reader applications for IoT devices. I will share how to use Dynamsoft barcode SDK for ARM, build Node.js C++ addon, as well as create a simple barcode application in JavaScript.

Barcode SDK for ARM

Dynamsoft released C++ barcode SDK for ARM on labs page. The SDK can be used on Raspberry Pi and BeagleBone.

Download and unzip dbr-V4.2.0-linux-armv7l.zip.

Create a symlink for libDynamsoftBarcodeReader.so:

sudo ln –s /home/pi/dbr-V4.2.0-linux-armv7l/lib/libDynamsoftBarcodeReader.so /usr/lib/libDynamsoftBarcodeReader.so

Build and run the sample code:

cd ~/dbr-V4.2.0-linux-armv7l/samples/c
make
./BarcodeReaderDemo

C++ Addon for Node.js

To use a C++ SDK with Node.js, you need to create an addon.

Install node-gyp:

npm install -g node-gyp

Create binding.gyp. Add paths of header files and libraries:

{
  "targets": [
    {
      'target_name': "dbr",
      'sources': [ "dbr.cc" ],
      'conditions': [
          ['OS=="linux"', {
            'defines': [
              'LINUX_DBR',
            ],
            'include_dirs': [
                "/home/pi/dbr-V4.2.0-linux-armv7l/include"
            ],
            'libraries': [
                "-lDynamsoftBarcodeReader", "-L/home/pi/dbr-V4.2.0-linux-armv7l/lib"
            ]
          }]
      ]
    }
  ]
}

Create dbr.cc to implement native C++ interfaces.

Build the addon:

node-gyp configure
node-gyp build

Import the addon in JavaScript:

var dbr = require('./build/Release/dbr');

Node.js Barcode Reader with Camera

Connect a UVC camera to Raspberry Pi.

Install Node.js module v4l2camera:

npm install v4l2camera

Print the formats that your camera supported:

var v4l2camera = require("v4l2camera");

var cam = new v4l2camera.Camera("/dev/video0");

// list all supported formats
console.log(cam.formats);

Here is mine:

v4l2 camera configuration

The format of camera frame is YUYV:

YUYV

To read barcodes from the frame, we just need to allocate a byte array with all Y values and convert it to DIB buffer:

bool ConvertCameraGrayDataToDIBBuffer(unsigned char* psrc, int size, int width, int height, unsigned char** ppdist, int *psize)
{
	BITMAPINFOHEADER bitmapInfo;
	memset(&bitmapInfo, 0, sizeof(BITMAPINFOHEADER));

	bitmapInfo.biBitCount = 8;
	bitmapInfo.biWidth = width;
	bitmapInfo.biHeight = height;
	bitmapInfo.biSize = sizeof(BITMAPINFOHEADER);

	int iStride = ((width * 8 + 31) >> 5) << 2;
	int iImageSize = iStride * height;
	if (size < iImageSize)
		return false;

	bitmapInfo.biSizeImage = iImageSize;

	*psize = iImageSize + bitmapInfo.biSize + 1024;
	*ppdist = new unsigned char[*psize];

	//1. copy BITMAPINFOHEADER
	memcpy(*ppdist, &bitmapInfo, sizeof(BITMAPINFOHEADER));

	//2. copy gray color map
	char rgba[1024] = { 0 };
	for (int i = 0; i < 256; ++i)
	{
		rgba[i * 4] = rgba[i * 4 + 1] = rgba[i * 4 + 2] = rgba[i * 4 + 3] = i;
	}
	memcpy(*ppdist + sizeof(BITMAPINFOHEADER), rgba, 1024);

	//3. copy gray data (should be fliped)
	unsigned char* srcptr = psrc + (height - 1)*width;
	unsigned char* dstptr = *ppdist + sizeof(BITMAPINFOHEADER) + 1024;

	for (int j = 0; j < height; ++j, srcptr -= width, dstptr += iStride)
	{
		memcpy(dstptr, srcptr, width);
	}

	return true;
}

unsigned char* pdibdata = NULL;
int dibsize = 0;
int width = worker->width, height = worker->height;
int size = width * height;
int index = 0;
unsigned char* data = new unsigned char[size];
// get Y from YUYV
for (int i = 0; i < size; i++)
{
	data[i] = worker->buffer[index];
	index += 2;
}
// gray conversion
ConvertCameraGrayDataToDIBBuffer(data, size, width, height, &pdibdata, &dibsize);
// read barcode
ret = DBR_DecodeBuffer(pdibdata, dibsize, &ro, &pResults);
// release memory
delete []data, data=NULL;
delete []pdibdata, pdibdata=NULL;

Only YUYV frame data is supported! If you want to support other camera formats, add interfaces as what I did.

Keep capturing frames from camera and invoking barcode detection API:

cam.start();

function capture() {
    cam.capture(function(success) {
        var frame = cam.frameRaw();

        dbr.decodeYUYVAsync(frame, format.width, format.height, barcodeTypes,
            function(msg) {
                var result = null;
                for (index in msg) {
                    result = msg[index]
                    console.log("Format: " + result['format']);
                    console.log("Value : " + result['value']);
                    console.log("##################");
                }
                setTimeout(capture, 0);
            });
    });
}

setTimeout(capture, 0);

Run camera_barcode_reader.js:

node.js barcode scanner for raspberry pi

Source Code

https://github.com/dynamsoftlabs/raspberry-pi-nodejs-barcode-reader-