Introduction: 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€)
Step 1: 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.
Step 2: 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.
Step 3: 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();
}
}
Step 4: 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 }}'
Step 5: 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

