Tech Note 09: Bar Code Scanning

February 25, 2009

© NSB Corporation. All rights reserved.



NS Basic/Palm can be used with a variety of different scanners. Here are some tips on using different ones. If you have information to contribute, please let us know!
  1. Symbol
  2. Visor Symbol
  3. Janam
  4. Aceeca
  5. Socket

Symbol Devices

NS Basic/Palm provides support for Symbol devices starting with version 2.0. The necessary libraries and samples are installed along with NS Basic/Palm: there are no extra files to download.

The Symbol routines work on all Palm OS devices from Symbol, as well as the Symbol Springboard for Visor. Please note that there are a few wrinkles with the Visor version: they are documented further on in this document. If you have a Symbol SPT15xx, it's a good idea to flash Palm OS 3.5 to the device. With older ROM versions, there is a potential error when using the STOP statement.

The routines are very simple to add into a program. Check out the sample called "Symbol" to see how it is done.

The Easy Way

1. In your startup code, include the line

LoadLibrary "NSBSymbolLib"

This lets NSBasic know that you will be using the library.

2. Somewhere in the initialization, possibly in the After code of your main form, call the function

NSBSymbolLib.IsPalmSymbolUnit()

This will return 1 if the program is running on a Symbol unit and 0 otherwise. If it returns 0, do not try to use any further Symbol functions.

3. In the Events code of your main form, when you receive an event type 34815, call this string function to get the scanned value:

scannedData = NSBSymbolLib.GetLastScanData()

The Events code will be called whenever the user pushes the yellow Scan button on the Symbol device. NSBSymbolLib.GetLastScanData() returns a string giving the last barcode scanned. Do whatever is appropriate with this bar code.

4. Compile your NSBasic program and download it plus the NSBSymbolLib.prc directory to your Symbol device. Your program should behave appropriately according to the last settings of the scanner.

The Symbol.prj sample does this. It also gets the bar code type using the integer function NSBSymbolLib.GetBarType().

The Harder Way

1. Events

The Symbol device produces three separate events. Each of these has two possible values. It's best to check for both in your program so your program will run on all devices.

34815 or -30721 A scan has been decoded.
34816 or -30720 The battery is low.
34817 or -30719 When a scan starts.

You can display a dialog box when you recieve event 34816. Event 34817 can normally be ignored, as you will get an event 34815 after a second or two anyway. If no bar code was successfully scanned, NSBSymbolLib.GetLastScanData() returns "NR" and NSBSymbolLib.GetBarType() returns 0.

2. Functions

The NSBasic Symbol library provides a number of procedures and functions that you can use to customize the operation of the scanner.
IsPalmSymbolUnit() This function returns 1 if this is a symbol unit, 0 otherwise
CmdScanEnable() This procedure enables the scanner. Normally, the scanner is always enabled. You can call this to make absolutely sure, or if you also use CmdScanDisable()
CmdScanDisable() This procedure disables the scanner.
CmdScanSetTriggeringModes(modes) This procedure sets the triggering modes of the scanner. For more information, consult the Symbol documentation.
CmdScanSetBarcodeEnabled(type, whether) This procedure enables or disables a particular barcode. The type of barcode is given in type as an integer. If whether is 1, the barcode is enable. If it is 0, it is disabled. See table below for values.
CmdSendParams(beep) (obsolete) Call this procedure after a series of CmdScanSetTriggeringModes(modes) and CmdScanSetBarcodeEnabled(type, whether). If the integer beep is 1, the Symbol device will beep when this is called. If it is 0, the device will not beep. The CmdScanSetTriggeringModes() and CmdScanSetBarcodeEnabled() call this function automatically.
CmdStartDecode() This procedure starts a decode. The effect is the same as pressing the yellow button. To use this, you must set the triggering mode to 8. Normally, there is no reason to use this, as the yellow button is the standard way of starting a scan.
CmdStopDecode() This procedure stops a decode in progress. Normally, it is not used.
GetDecodedData() This function returns a string that contains the raw decoded data. It is only provided for debugging purposes.
GetLastScanData() This function returns a string that contains that last bar code scanned. You will normally call this as a response to an event type 34815. However, you can call it any time up to when the next barcode is scanned. If the last barcode was invalid, this will return "NR" as the barcode.
GetBarType() This function returns the type of the last scanned barcode as an integer. If the last barcode was invalid, it returns 0. See table below for return values.
CmdScanLedOn() This procedure turns the green LED on. Because the LED normally goes on when a scan is started, this is provided just for fun.
CmdScanLedOff() This procedure turns the green LED off. Because the LED normally goes on when a scan is completed, this is provided just for fun.

3. Barcode Types

The following table gives the numerical values of the barcode types. These numbers are defined by Symbol.
GetBarType()CmdScanSetBarcodeEnabled()
NOT_APPLICABLE 0  
CODE39 10
CODABAR 27
CODE128 38
D2OF5 45
IATA2OF5 5  
I2OF5 66
CODE93 79
UPCA 81
UPCE0 92
EAN8 104
EAN13 113
MSI_PLESSEY 1411
EAN128 1514
UPCE1 1612
CODE39_FULL_ASCII 19  
TRIOPTIC_CODE39 2113
BOOKLAND_EAN 2283
COUPON_CODE 2385
ISBT128 2584
CODE32 32  
UPCA_2SUPPLEMENTALS 72  
UPCE0_2SUPPLEMENTALS 73  
EAN8_2SUPPLEMENTALS 74  
EAN13_2SUPPLEMENTALS 75  
UPCE1_2SUPPLEMENTALS 80  
UPCA_5SUPPLEMENTALS 136  
UPCE0_5SUPPLEMENTALS 137  
EAN8_5SUPPLEMENTALS 138  
EAN13_5SUPPLEMENTALS 139  
UPCE1_5SUPPLEMENTALS 144  
PDF 417   17

Visor Symbol Devices

While the Visor/CSM 150 combo is similar to Symbol devices (see above), there are differences. Special thanks are due to Doug Handy and Ron Glowka for working this out.

1. Event codes differ on the CSM150 compared to the SPTxxxx

  Use -30721 instead of 34815
  Use -30720 instead of 34816
  Use -30719 instead of 34817
If you are trying to support both the SPTxxxx and the CSM150 devices, you will need to test for both event codes. This is caused by a bug in Symbol's code.

2. Avoid making library calls in the project startup and termination

During project startup and termination, the normal program "event loop" processing is not performed. While it may seem natural to perform some library calls such as CmdScanEnable in the startup code after the LoadLibrary, in practice is causes events to get queued but not processed and a fatal error results.

If it is necessary to perform a Symbol library call in the project startup or termination, immediately follow the call with the following code:

  Dim EventType as Variant
  SysTrapSub 285, 2, EventType, 1	'EvtGetEvent
  SysTrapSub 169, 1, EventType		'SysHandleEvent

The ScanConfig Utility

ScanConfig is a small Palm OS application for configuring attributes of a Symbol brand Palm OS based scanner such as the SPTxxxx series or the CSM150 springboard module. It is intended to aid developers of NSB/Palm applications to allow setting of values not currently exposed by calls in NSBSymbolLib.

ScanConfig is run by using AppLaunch() from within your NSB/Palm program, and supplying both a cmd integer to designate the attribute to be changed plus a data string with the associated desired value(s).

ScanConfig (and its documentation) is available in the Files section of the NS Basic web board. The URL is http://groups.yahoo.com/group/nsbasic-palm/files/ScanConfig.zip

Janam Devices

Janam devices are designed to be compatible with Symbols. You can use the same shared library and functions.

The critical code is slightly different than in the Symbol: here is how it looks:

Startup Code:

   LoadLibrary "NSBSymbolLib.inf", "NSBSymbolLib"
   NSBSymbolLib.cmdScanEnable()

Form Event Code

   Dim foo as String
   Dim bar as Integer
   
   If sysInfo(9)=8459 Then 'Janam Scan Button hit
	   foo = NSBSymbolLib.GetLastScanData()
	   fldBarCode.Text = foo  
	   bar = NSBSymbolLib.GetBarType()
	   fldCodeType.Text = str(bar)
   End If
Note: We have mixed reports whether this works properly.

Aceeca Devices

Working with an Aceeca Ltd. ID::Verifi PDA API for Scanned Data
Contributed by Michael Assia SY-CON Systems, Inc.

We found the Aceeca Palm 4.1-based PDA to be an alternative to the Symbol SPT series units. Included from Aceeca is a very capable "wedge" scanner program to make entering barcoded data into any application an easy matter. Why then would you need anything else for your application?

The Aceeca wedge software reads a barcode and completes the current field where focus remains on the field. This occurs for any input field regardless of how the data is entered including the Symbol SPT series with the NSBSymbolLib. The user must tap into the next field before entering or scanning the next value. It was our desire, however, to make barcode input an intuitive process with little to no tapping, just scan to complete the field and automatically move to the next input field ready for the next scan. You may also want to perform a different operation or display messages based on the data obtained from the scan immediately after it is received. You can also get the desired results using the Aceeca wedge API by intercepting the events and characters generated by the software as it decodes the barcode characters read.

The behavior of the Aceeca API within an NSBasic written application differs from that described in the BCS2 write-up and C/C++ example. Within an NSBasic application you will need to use the GetEvent call from the NSSystemLib to detect the KeyDown event (4) that occurs for each character scanned. The GetEvent must be called in the form's Event code to handle this process. The actual decoded character is obtained by calling another NSSystemLib function, KeyEventChr.

The Aceeca API continues to generate the KeyDown event for each character it decodes until the barcode is fully decoded. However programmatically there is no way to know how many characters are to be received. To signify the end of a barcode string, you will need to use the Advanced setup for the Aceeca wedge software on the PDA and add a CR LF (carriage return & line feed) or some other special characters (2) to the end of the character string representing the barcode data read. Make sure the characters you choose are not found in any barcode symbology you are using.

Project Startup

' Define several key fields so their value exists thorough out the program
'
Global dataread as String         'to hold scanned characters until all are received
Global Cr as String	   
Global Lf as String
.
.
	Cr = Chr(13)
	Lf = Chr(10)
	LoadLibrary "NSBSystemLib", "NSL"	
Form Code (Event) for any forms with barcode input
Sub Form1047_Event()
Dim evnt as Integer
'
    evnt = NSL.GetEvent(500,0,0)
    If evnt <> 4 Then Exit Sub
'
'   Event was keydown from Aceeca PDA
'   Get each character one-by-one read by scanner 
'
    keychr = NSL.KeyEventChr()
    If chr(keychr) <> Cr And chr(keychr) <> Lf Then 
       dataread = dataread + Chr(keychr)
       Else
       If chr(keychr) = Lf Then Exit Sub
'
' After getting Cr, put string into appropriate input field.
' 
       Select Case statecode
             Case 1
                statecode = 2
                in1.text = dataread
                In2.Setfocus
                in2.text = ""
            Case 2
                statecode = 3
                in2.text = dataread
                In3.Setfocus
                in3.text = ""
            Case 3
                in3.text = dataread
                statecode = 4
                In4.SetFocus
                in4.text = ""
            Case 4
                in4.text = dataread
                statecode = 1
                in1.SetFocus
                in1.text = ""
            Case Else
            End Select 
            dataread = "" 
    End If             
End Sub

Socket Scanners

Contributed by Doug Handy

In my opinion the 3E model is junk -- it is a CMOS scanner with a very slow processing speed and horrid success rate. Nearly unusable in my book.

But I do use the 3M model now -- it is a class 1 laser and works pretty well. Not quite as nice as the Symbol scanners, but WAY better than the 3E and still quite a bit better than the various Grabba models.

I've not tried the 3P model, which is a class 2 laser and should be even better.
http://www.socketmobile.com/products/handheld-computer/accessories/series3/

I think probably all of them work the same from an application perspective though. So I do know something about using the 3M from code, and the 3E probably works the same way.

In my case, I load their shared library, set various configuration options from my code, and disable their trigger button capture and control it myself from my app. My app intercepts all the hardware butons, and when the one assigned to the scanner is pushed, I make calls to their shared library to initiate the scan.

But that is at least in part because my app is ultra flexible. Users have a page where they can assign any of long list of functions to any of the hardware keys. So they can change which key triggers a scan, and my app has to accomodate it dynamically. I also support the symbol scanners (or Visor with CSM150 springboard), the Grabba line of scanners, and the SDSC 3M scanner.

Each of which requires different code to trigger the scan, accept / process a completed scan, etc (not to mention startup and shutdown changes, what library to load, etc).

Of course, I have the advantage that I am doing all of it from C, not NSB/Palm (sorry -- I need the flexibility and performance...).

Sending a keystroke from a program

Use EvtEnqueueKey, which is SysTrapSub 301 in NSB/Palm parlance.

Dim keyChar as Short
Dim keyCode as Short
Dim keyModifiers as Short

keyChar = 516   'vchrHard1; 517-519 for buttons 2-4 instead
keyCode = 0
keyModifiers = 8 'commandKeyMask
'EvtEnqueueKey
SysTrapSub 301, 3, keyChar, keyCode, keyModifiers
The above should mimic the press of the first hard key (vchrHard1). The other 3 on the bottom of the Palm should be 517, 518, and 519.

Note that I have not tried this from NSB/Palm for triggering a scan. But the above is the general technique for inserting a "press" of those buttons, and since you are putting it into the system event queue, I think the scan library will probably detect it and trigger a scan.