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:
The format of camera frame is 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: