Barcode Scanner for Bring! Shopping List
by DavidE281 in Circuits > Arduino
10997 Views, 77 Favorites, 0 Comments
Barcode Scanner for Bring! Shopping List
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
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
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
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