Barcode Scanner for Bring! Shopping List

by DavidE281 in Circuits > Arduino

10997 Views, 77 Favorites, 0 Comments

Barcode Scanner for Bring! Shopping List

Barcode reader
producto_cocacola.jpg
producto_aquarius.jpg
esquema.png
m5stick.jpg
coke.jpg
bring.jpg
yk-e1005.jpg
Lector_2.jpg

This project aims to create a gadget that reads the 2D barcodes of the products I want to add to my shopping list. (Bring! Shopping List App).

The gadget is built with an M5StickC + a 2D barcode reader with a serial port (YK-E1005-OEM) + a box built with a 3D printer to integrate them.

To communicate with the Bring! application I took advantage of my Raspberry PI running Home Assistant. The barcodes are sent through MQTT to the MQTT server (Mosquitto) and once received in HomeAssistant a code programmed in Python is launched. This program looks for the barcode in an excel sheet where you can find the list of all the products. Once the product is identified it is sent to the Bring! application through an unofficial API and by MQTT the product name is returned to show it on the barcode reader screen.

Supplies

M5Stick C (10€)

CCD Barcode scanner 1D - 3,3Vcc (15€)

Ribbon cable to PCB board (5€)

Schematics

esquema_yk-e1005.jpg
pcb.png
pins_m5stickc.png

Only four signals are required to connect the barcode reader to the ESP32 (M5StickC): Vcc, GND, TX and Trigger.

YK-E1005 <----- > M5StickC

Vcc <------> 3V3

GND <------> GND

TX <------> G36

Trigger <-------> G26

To make the connection I used an intermediate plate to avoid having to perform complicated soldering.

Printing Case

carcasa.png
Lector_1.jpg
Lector_2.jpg

The box is designed in Tinkercad and is a beta version. For now it is not the ideal box but for testing it is enough.

The base contains a connector with a magnet so you can quickly connect and disconnect the charger.

Program M5StickC

To program the M5StickC I used the Arduino IDE using the basic libraries.

The operation is as follows:

-Once the A button is pressed the capture trigger of the reader is activated and it waits to receive data through the serial port for a maximum time of 10 seconds.

-When the information is received through the serial port, the barcode is sent to the MQTT server.

-If a product name is received from the MQTT server, it is shown on the screen.

-The display is turned off, in order not to damage it, when the charger is connected. The input voltage is checked.

//
// Lector codigo barras para M5Stack Stick-C
//
// Mayo-2020
//
// daekka LNX

#include <M5StickC.h>
#include <WiFi.h>
#include <PubSubClient.h>

//WiFi credentials
char* ssid       = "xxxxxx";
char* password   = "xxxxxx";
const char* mqtt_server = "192.168.1.200";
const char* mqttUser = "xxxxxx";
const char* mqttPassword = "xxxxxx";
WiFiClient espClient;
PubSubClient client(espClient);

// LED
#define ledPin 10
// Pin trigger lector
#define triggerPin 26

bool usb_power = false;
unsigned long previus_millis_captura = millis();
const int timeThreshold_captura = 10000;
String codigo_leido="";


// the setup routine runs once when M5StickC starts up
void setup() {

  // initialize the M5StickC object
  M5.begin();

  // Trigger del lector
  pinMode (triggerPin, OUTPUT);
  digitalWrite (triggerPin, HIGH);

  // LED
  pinMode (ledPin, OUTPUT);
  digitalWrite (ledPin, HIGH);  // turn off the LED

  // Puerto serie
  Serial2.begin(9600,SERIAL_8N1,36,0);

  // Activa el wifi y MQTT
  activa_wifi();
  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);

  // Ajusto el brillo
  //M5.Lcd.setBrightness(80);

  // Rota la pantalla
  M5.Lcd.setRotation(3);
  M5.Axp.ScreenBreath(10);
  // Lcd display
  M5.Lcd.fillScreen(WHITE);
  delay(500);
  M5.Lcd.fillScreen(RED);
  delay(500);
  M5.Lcd.fillScreen(GREEN);
  delay(500);
  M5.Lcd.fillScreen(BLUE);
  delay(500);
  M5.Lcd.fillScreen(BLACK);
  delay(500);

  if (M5.Axp.GetVBusVoltage() > 4) {
    usb_power = false;
  }
  else {
    usb_power = true;
  }

}

void activa_wifi() {
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
  }
}

void callback(char* topic, byte* message, unsigned int length) {
  String messageTemp;

  if (String(topic) == "lector_codigo_barras/in") {
      for (int i = 0; i < length; i++) {
        messageTemp += (char)message[i];
      }
      for (int i = 0; i < 10; i++) {
        messageTemp += " ";
      }
      display_producto (messageTemp);
  }
}

void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    // Attempt to connect
    if (client.connect("M5stickC-Codigo_barras")) {
      // Subscribe
      client.subscribe("lector_codigo_barras/in");
    } else {
      delay(1000);
    }
  }
}

void display_titulo (String titulo) {
  M5.Lcd.setTextColor(WHITE,BLACK);
  M5.Lcd.drawString(titulo + "              ", 11, 8, 1);

}
void display_producto (String producto){
  M5.Lcd.setTextColor(GREEN,BLACK);
  M5.Lcd.drawString(producto + "         ", 10, 30, 4);
}

void display_codigo_barras (String codigo) {
  M5.Lcd.setTextColor(RED,BLACK);
  M5.Lcd.drawString(codigo + "         ", 10, 60, 2);
}

void enciende_pantalla (){
  M5.Lcd.writecommand(ST7735_DISPON);
  M5.Axp.ScreenBreath(10);
  M5.Lcd.fillScreen(BLACK);
  display_titulo ("LECTOR CODIGO BARRAS");
  display_producto ("Producto.....");
  display_codigo_barras ("Esperando.....");
}

void apaga_pantalla (){
  M5.Lcd.writecommand(ST7735_DISPOFF);
  M5.Axp.ScreenBreath(0);
}

void comprobacion_usb_conectado() {
  // Apaga la pantalla
  if ((M5.Axp.GetVBusVoltage() > 4) && (usb_power == false) ){
      usb_power = true;
      apaga_pantalla();
  }
  if ((M5.Axp.GetVBusVoltage() <= 4) && (usb_power == true) ){
      usb_power = false;
      enciende_pantalla();
  }
}

void captura_codigo () {

     digitalWrite (ledPin, LOW);  // turn on the LED
     digitalWrite (triggerPin, LOW); // Activa lector
     previus_millis_captura = millis();
     while (Serial2.available() == 0 && (millis()-previus_millis_captura<timeThreshold_captura)) {
        delay (10);
     }

     if (millis()-previus_millis_captura<timeThreshold_captura) {
        M5.Lcd.fillScreen(GREEN);
        delay (100);
        codigo_leido = Serial2.readString();
        M5.Lcd.fillScreen(BLACK);
        M5.Axp.ScreenBreath(10);
        display_titulo ("LECTOR CODIGO BARRAS");
        int str_len = codigo_leido.length()-1;
        char char_array[str_len];
        codigo_leido.toCharArray(char_array, str_len);
        client.publish("lector_codigo_barras/out", char_array);
        display_codigo_barras (codigo_leido);
     }
     else {
        display_codigo_barras ("TIME OUT.....");
     }
     digitalWrite (triggerPin, HIGH); // Apaga lector
     digitalWrite (ledPin, HIGH);  // turn on the LED
}


// the loop routine runs over and over again forever
void loop(){

  if (!client.connected()) {
    reconnect();
  }
  client.loop();

  // Comprueba si esta el USB cargador conectado y apaga o enciende la pantalla
  comprobacion_usb_conectado();
  // Actualiza estado botones
  M5.update();
  if (M5.BtnA.wasPressed()) {
      captura_codigo();
  }

}

HomeAssistant

Integrations to be implemented:

-MQTT sensor: receives the barcode from the reader.

-Shell Command: executes the Python code

-Automation: launches the Python code once data is received at the MQTT sensor.

############################################################
#
# Home Assistant - Lector codigo de barras
#
############################################################
 
sensor:
  - platform: mqtt
    name: lector_codigo_barras 
    state_topic: "lector_codigo_barras/out"
    expire_after: 2
    
shell_command: 
    insert_producto_lector_codigo_barras: '/usr/bin/python /home/homeassistant/.homeassistant/python/bring/insert_producto_lector_codigo_barras.py "{{ producto }}"'
          
automation:
  - alias: recibido_codigo_barras
    initial_state: true
    trigger:
      - platform: state
        entity_id: sensor.lector_codigo_barras
    action:
      - service: shell_command.insert_producto_lector_codigo_barras
        data_template:
            producto: '{{ states.sensor.lector_codigo_barras.state }}'

Python Script

excel.png

The Python program does the following:

-Searches for the received code (sys.argv[1]) in the excel sheet where the products are stored.

-Sends by MQTT the name of the product.

-Add the product to the Bring! application using the unofficial BringApi library.

#!/usr/bin/env python
# coding: utf8

# Find barcodes from excel sheet
# daekka LNX

import requests
import json
import sys
import os
import xlrd
from collections import OrderedDict
import bringapi
import time

# Open the workbook and select the worksheet (first=0)
path_excel_productos = '/home/homeassistant/.homeassistant/python/bring'
file_name_excel_productos = 'barcode_productos.xlsx'
wb = xlrd.open_workbook(os.path.join(path_excel_productos, file_name_excel_productos))
sh = wb.sheet_by_index(0)

#Iterate through each row in worksheet and fetch values into dict
for rownum in range(1, sh.nrows):
    row_values = sh.row_values(rownum)
    if row_values[3] == sys.argv[1]:
       producto = OrderedDict()   
       producto['id_aleman'] = row_values[0]
       producto['id_traduccion'] = row_values[1]
       #producto['id_categoria'] = row_values[2]
       #producto['id_codigo'] = row_values[3]
       
       #MQTT
       os.system ('mosquitto_pub -h 192.168.1.200 -m "' + producto['id_traduccion'] +'" -t lector_codigo_barras/in')

       #BRING!
       bring = bringapi.BringApi("user","password",True)
       bring.purchase_item(producto['id_aleman'],"")

       #Finaliza buble
       break

The excel sheet has 4 columns:

1º Original name of the product in the application. This is the name to be used in the APIBRING so that no new products are created. If you use a name that is not in Bring! the product will be created and you will have to modify the category and the icon in the application.

2º Translation to my language. This is the name that I show in the M5Stick screen.

3º Category to which the product belongs (not used)

4º Bar code. When there are several brands of the same product, the row must be duplicated with the same data