Barcode displayer (replacement for Pebble's Skunk)

Posted on
  • So, I've been working for the last 15-20 minutes on a barcode displayer for the Bangle JS 2 (since my pebble died and I miss the Skunk app). I've started with implementing EAN-13 barcodes first.

    It works but on 1:1 scaling it is too small and due to the resolution of the screen I have to depend on the parity bit to fix the errors that occur when scaling it up to the full size of the screen.
    An EAN-13 barcode has 95 lines and with a resolution of 176 I can't do perfect scaling.
    The alternative would be to split it into 2 sections and depend on the decoding algorithms of barcode scanners. This would exclude non-laser barcode scanners though.
    Are people interested in this kind of app? If so I'll probably develop it further and put it in the app store.
    If not then I'll just make it functional for my barcodes and leave it at that.
    This is the code so far:

    // Function to generate EAN-13 barcode
    function generateEAN13(barcode) {
      const lineWidth = 1.8; // Adjust as needed. 1.8 fills the screen but causes errors that need to be fixed with the parity check.
      // Table for encoding the first digit
    var table_encoding = [
      ["000000", 0], ["001011", 1], ["001101", 2], ["001110", 3], ["010011", 4],
      ["011001", 5], ["011100", 6], ["010101", 7], ["010110", 8], ["011010", 9]
    // Encoding dictionary for L, G, and R patterns
    var dict = [
      ["0001101", "0011001", "0010011", "0111101", "0100011", "0110001", "0101111", "0111011", "0110111", "0001011"], // L
      ["0100111", "0110011", "0011011", "0100001", "0011101", "0111001", "0000101", "0010001", "0001001", "0010111"], // G
      ["1110010", "1100110", "1101100", "1000010", "1011100", "1001110", "1010000", "1000100", "1001000", "1110100"] // R
      if (barcode.length !== 13) {
        console.error("Barcode must be a string of length 13");
        return "";
      var firstDigit = parseInt(barcode[0]);
      var encoding = table_encoding[firstDigit][0];
      var ean13 = "101"; // Start marker
      // Encode the first 6 digits using the determined encoding
      for (var j = 1; j < 7; j++) {
        var usageIndex = parseInt(encoding[j-1]);
        ean13 += dict[usageIndex][parseInt(barcode[j])];
        if (j === 6) {
          ean13 += "01010"; // Center marker
      // Encode the last 6 digits using the R pattern
      for (var k = 7; k < 13; k++) {
        var rIndex = parseInt(barcode[k]);
        ean13 += dict[2][rIndex];
      ean13 += "101"; // End marker
      for (let i = 0; i < ean13.length; i++) {
        const binaryDigit = ean13[i];
        const x = i * lineWidth+3;
        if (binaryDigit === "1") {
          const startY = 10; // Starting y-coordinate
          var height = 100; // Height of the line, adjust as needed
          if (i<4 || i>91) {
            height = 110; // height of start and end marker, adjust as needed
          g.fillRect(x, startY, x+lineWidth-1, startY + height);
    g.drawString(barcode, 10, 115);
    // Render the graphics on the display
    // Example usage
    var barcodeDigits = "4055334874045"; // Replace with desired 13-digit EAN-13 barcode
  • @Akisame, checkout @MaBe 's conversations and contributions and start talking to him. - ao

  • Thanks. I see he's taking the pre-generated image approach. With the bangle js 2's resolution I foresee issues with misinterpreting the R encoded portion for EAN-13 barcodes.
    But I don't see anything new on this topic from him since 2 years ago when he was working on a code 128 barcode generator for the Bangle 1. I don't think we can get code 128 working on the Bangle 2 with the resolution it provides and the decreased error correcting bit.

  • I don't know anything about scanning or encoding bar codes, but could you maybe get this working by displaying the bar code diagonally on the display to get 2*95 pixels without scaling? It might throw off the scanners when slightly misaligned but could be worth a shot.

  • Ooh, it works for EAN-13. It just uses the build in error checking. Code 128 just doesn't work. It needs a higher resolution.
    Diagonal works if we have anti aliassing. I would have to look. I don't know if bangle has that. I don't think it has.

  • If the display is to small for barcode than think about qrcode.

  • @Ganblejs yes. Bangle 1 has AA support. I thought bangle 2 doesn't.
    @MaBe qrcode only does qrcodes right? The goal is to be able to emulate loyalty cards, employee cards and other barcode systems that required you to carry barcodes with you.
    I have a set of 6 cards that I used to carry in my pebble.
    So far EAN-13 works but it requires the build in error checking to do so. This is because the scaling can't be 1:1 with the resolution of the bangle 2. It works perfectly on the bangle 1 though without using the error checking.

  • @Ganblejs yes. Bangle 1 has AA support. I thought bangle 2 doesn't.

    You may very well be right! :) But the link is specifically to the Bangle.js 2 reference. If AA is only on Bangle 1 maybe it should be taken out of there.

  • EAN-13 has a checksum digit, is that what you mean? What barcode types are typically used for those cards?

  • @MaBe yeah, EAN is actually quite clever in its error correction with the L, G and R encoding.
    The barcodes used differs per country. In Europe you need EAN-13, EAN-8, code 39, code 128 or a QR code. In my and my wife's wallet I found mainly code 128 and EAN-13 cards.
    Code 128 and 39 are usually only numbers with rarely an asterisk symbol in there so the look up table should be doable. Still big though with 106 unique items.
    Longest code 128 I was able to find was 19 characters in 128C. That should fit on the bangle 2 but it will be small as there isn't any option to scale it
    The smallest code 128 I found is 11 symbols and can also not be scaled.
    Hmm. I will have to do a field test to see if the scanners are able to scan these tiny code 128 barcodes.
    If they can then there's no issue.

  • I believe Bangle.js 2 does have all the AA functionality but the screen is only 3 bits so it's of limited use unless you dither.

    If you did want to have some antialiasing maybe consider using one of the colours as a 'grey' - for example White,Red,Black

    ... but whether that would be useful with a normal red-light barcode reader I don't know.

    Having said that, the barcode reader probably won't be pin-sharp so I imagine that dithering may well be good enough

  • Okay. I had a bit of time today so I added the code 128 support.
    You can find the code here:­2-Barcode
    Next time I have some time to kill I'll create a settings page where you can add your barcodes.
    support Bangle 1
    Make it actually usable (allow switching of barcodes)
    Settings page
    Scaling of everything
    Display name of barcodes

    The code is a bit long (12.8 kb, 295 lines of code) and can probably be optimized further.
    For Code-128 the max length varies based on how many times you switch between codeset. If you only use numbers you can fit 24 characters (since every 2 numbers is 1 set), If you only use letters and symbols you can fit 12 characters. Every switch between the two codesets will cost you a character. If you go below 4 sets (so 8 numbers or 4 characters) it will automatically scale up.

  • Just a FYI if your not aware of Cards app by @glemco, PR here. Seems pretty adjacent, maybe it makes sense for you to collaborate?

  • Nice, thanks for sharing

  • I was actually not aware of the Cards app. Yeah, would be cool to have it all in one place.
    @glemco 's code seems to follow similar principles. I take a bit more advantage of the checksum for error checking, thus allowing for "bad" scaling but otherwise pretty similar.

  • @Akisame the cards app is roughly a proof of concept. It works fine with gadgetbridge (catima sync) but the rendering of codes is way far from being perfect. I tried some open source libraries but feel free to fork/enhance the app with your own renderers.

  • @glemco thank you. I don't have much free time but I'll have a look when I have time.

  • I tried it some days ago and I can sync my Catima cards. Unfortunately when I try to display them, the watch reboots. Any idea why?

  • That is because I haven't had the time to make it fully functional yet.
    In 1.5 months (when I'm done with my phd) I'll have time to finish this and combine it with the cards app.

  • @fparri keep in mind that (as I wrote in the readme) the app is mostly a proof of concept for now, as you noticed sync works, but code generation is done on the device and not in the best way.
    Complex QR codes give a hard time to the device and it reboots (didn't try with complex barcodes but those would likely not render fine).
    As many said, the best thing would be to have the codes generated by the phone (Catima perhaps), but that is not how the gadgetbridge integration currently works.
    Any help is more than welcome, at the moment I don't have much time to work on that (and I don't have much knowledge of android and barcode/QR generation to make the algorithms smarter)

  • Great! Thanks for your good work :)

  • Understood. You're right, I tried it and many of the simpler QRs work :)
    Even as it is now, I find your app very useful, thanks a lot :)

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview

Barcode displayer (replacement for Pebble's Skunk)

Posted by Avatar for Akisame @Akisame