5

Based on my understanding* of the check digit calculation for Code128 barcodes (which differs radically/drastically from most of the other standard barcode types), my code below is correct. However, I would appreciate a "sanity check" by those who may know better/have some empirical observations/mud from the trenches attached to their trench (or lab) coats.

  • This is my understanding:

Each character in the barcode, from the left, is first converted into its ASCII code (IOW, a barcode character of "1" is to be viewed as its ASCII Code of 49, etc.) and then that value is multiplied by its ordinal position in the array of chars.

e.g. for the fabricated barcode number "123456789" the "1" equates to 49, the "2" to 50, ... the "9" to 57.

Then, the ordinal position of each character is multiplied by its ASCII value. e.g., 1*49 == 49, 2*50==100, ... 9*57==513. You then sum all these up, to arrive at (in this case) 2,445.

The next step is to divide that number by the Code128 "magic number" of 103; the value you're interested in is the modulus. So, in this case, 2445 % 103 == 76.

Penultimate-finally, you convert that value (76), considering it to be an ASCII code, back the other direction to its "presentation" value, namely "L".

Finally, you append that calculated character to the original barcode. Verily and thus (notwithstanding further ado or adieux), you end up with a value of "123456789L"

If this is not the correct value, then I'm understanding something incorrectly.

Here are a few barcodes:

0) 123456789
1) 12345678
2) 1234567
3) 123456

...and how they should appear with their calculated check digits (and do, with the code below):

0) 123456789L
1) 12345678N
2) 1234567*
3) 123456E

Last but not least, here is the code I used to compute the Code128 check digits:

private void buttonAppendCode128CheckDigit_Click(object sender, EventArgs e)
{
    const int CODE_128_DIVISOR = 103;
    string barCode = textBoxRawCode128.Text.Trim();
    int runningTotal = 0;

    for (int i = barCode.Length - 1; i > -1; i--)
    {
        char valToConvertToASCII = Convert.ToChar(barCode[i]);
        int valToMultiply = ConvertToASCIIInt(valToConvertToASCII);
        runningTotal += (valToMultiply*(i + 1));
    }

    int code128Modulus = runningTotal%CODE_128_DIVISOR;
    textBoxCode128WithCheckDigit.Text = barCode + ConvertToASCIIChar(code128Modulus);
}

private char ConvertToASCIIChar(int code128Modulus)
{
    return (char) code128Modulus;
}

private int ConvertToASCIIInt(char valToConvertToASCII)
{
    return valToConvertToASCII;
}

UPDATE

I'm not quite grokking Brian Anderson's answer; he may be right (probably is), but I would think the start and stop bits would be ignored as far as the check digit calculation goes. Aren't they there just so that the barcode scanner knows from which point to pay attention and thereafter from which point it can resume its electronic snoozing?

And then the math (provided Brian is correct about needing to subtract 32 from the ASCII vals) would be:

(17*1)+(18*2)+(19*3)+(20*4)+(21*5)+(22*6)+(23*7)+(24*8)+(25*9)
-or:
17 + 36 + 57 + 80 + 105 + 132 + 161 + 192 + 225 == 1005

Why would the start character be included in the calculation, but not the stop character?

Since 1005 % 103 == 78, the check digit would be ... "N" ... or would it be (78-32 == 46) "-"?

If both the stop and start characters were included, then of course that would change the solution, too...

UPDATE 2

I admit I'm not exactly an ogler of barcodes, so I may have seen such and just not noticed/paid attention, but can barcodes have such check digits as "-" and ""? It seems bizarre; I would expect them to always be alphanumerics if not just numerics. If my suspicion is correct, what is done when a calculation does end up with a squirrely check digit such as "-" or "" or "~", &c?

UPDATE 3

So, if I understand Brian and other sources I'v read correctly, I will read from the barcode scanner what semantically decodes to:

[startChar]123456789[checkDigit][stopChar]

...but which would logically strip out the stop char --since it is not part of the check digit calculation -- and (not quite so logically) also strip out the check digit, so what I actually get would be:

[startChar]123456789

...and massage it and calculate the check digit to to display a human-readable representation of the barcode:

123456789[checkDigit]

And since the start char has obviously (?) been seen if the barcode has scanned, I can just pre-add it to the runningTotal value to be computed; thus, my code is now:

private void buttonAppendCode128CheckDigit_Click(object sender, EventArgs e)
{
    const int CODE_128_DIVISOR = 103;
    string barCode = textBoxRawCode128.Text.Trim();
    int runningTotal = ConvertToASCIIInt(barcode[0]); // Start with the value of the start char; this should always be either 103 (Code128A), 104 (Code128B), or 105 (Code128C); 106 is the stop char

    for (int i = barCode.Length - 1; i > 0; i--) // now disregarding already calculated first element by ignoring element 0
    {
        char valToConvertToASCII = Convert.ToChar(barCode[i]);
        int valToMultiply = ConvertToASCIIInt(valToConvertToASCII);
        runningTotal += (valToMultiply*(i + 1));
    }

    int code128Modulus = runningTotal%CODE_128_DIVISOR;
    textBoxCode128WithCheckDigit.Text = barCode + ConvertToASCIIChar(code128Modulus);
}

private char ConvertToASCIIChar(int code128Modulus)
{
    return (char) code128Modulus;
}

private int ConvertToASCIIInt(char valToConvertToASCII)
{
    const int ASCII_ADJUSTMENT_VAL = 32;
    return valToConvertToASCII-ASCII_ADJUSTMENT_VAL;
}
B. Clay Shannon
  • 1,055
  • 124
  • 399
  • 759
  • From a quick look the code seems match your description of the algorithm. Be aware that since you're getting this from a textbox, there is no guarantee that the characters in the string are part of the ASCII range. It won't crash or error out, but will produce invalid data without noticing the user. I can't verify that your checksum description is correct, unfortunately. – C.Evenhuis Sep 10 '13 at 21:30
  • @C.Evenhuis: Thanks for czeching it out; no worries about the textbox value - that's just a utility for testing. In reality, the value will come from an actual scanned barcode. – B. Clay Shannon Sep 10 '13 at 21:33

1 Answers1

3

It looks like you might be missing the start character for code 128A (103), 128B (104) or 128C (105). This number is weighted with a '*1', just like the first character in your barCode string. I also think you have to do the math with the Code 128 values (i.e. ASCII_value_of_character - 32). So for the string "123456789" I get "104, 17, 18, 19, 20, 21, 22, 23, 24, 25, 79, 106", including the Start 128B (104), Stop (106) and a Checksum of 79 (1109 % 103).

Brian Anderson
  • 1,556
  • 1
  • 16
  • 25
  • 1
    I know it seems kind of squirrelly, but the start character is not fixed and determines the interpretation of the bar. What I thought was strange when I was learning about this was the fact that the Start character and the first data character both get a '1' rating. I mean, why not start the first data character off as multiplied by 2? You'll find an Excel app toward the bottom of the page on this website: http://notionovus.com/blog/barcodes/html/code-128-barcode/ that generates HTML Code 128 barcodes and it uses the "ASCII - 32" and "include the Start character" methods. They scan. – Brian Anderson Sep 11 '13 at 04:21
  • Okay, great; that will be a good resource for checking my calcs. – B. Clay Shannon Sep 11 '13 at 15:03
  • So it seems to me that when "I" (the app I'm maintaining) scans a barcode, I will have to use that value to calculate the check digit, but ignore/disregard the last character (the "stop bit"). Correct? So the weightization of the barcode will be: 1,1,2,3,...len-1,0 – B. Clay Shannon Sep 11 '13 at 16:28
  • 1
    Correct. The Stop byte, which is always 106 is used by the scanner for orientation. It is only used to signal the right side of a barcode, in case the operator scans it upside-down. The unique set of bars and lines before the quiet zone is not found anywhere else in the symbology, so a scan cannot be accidentally terminated simply by having the bars and lines for 106 appear anywhere else. The byte immediately to the left of the Stop is the chksum, so that's how the hardware knows where the rest of the data is. The scanner then works from the left side back to the last byte before the chksum. – Brian Anderson Sep 11 '13 at 17:14
  • If interested, see my new, related post at http://stackoverflow.com/questions/18749044/why-is-my-ascii-char-to-int-conversion-failing – B. Clay Shannon Sep 11 '13 at 18:52