moved stuff + scanner com/hid edits

This commit is contained in:
2026-03-17 17:25:14 -03:00
parent 9cf0792866
commit 0fcb8ce473
1228 changed files with 382 additions and 151555 deletions

View File

@@ -0,0 +1,2 @@
config.json
imageTools-*

View File

@@ -0,0 +1,42 @@
#!/bin/bash
# Define binary names
LINUX_BIN="imageTools-linux"
LINUX_ARM_BIN="imageTools-linuxARMv7"
WINDOWS_BIN="imageTools-windows.exe"
echo "Starting build process..."
# Build for Linux (64-bit)
echo "Building for Linux..."
GOOS=linux GOARCH=amd64 go build -o "$LINUX_BIN" main.go
if [ $? -eq 0 ]; then
echo "Successfully built: $LINUX_BIN"
else
echo "Failed to build Linux binary"
exit 1
fi
# Build for Windows (64-bit)
echo "Building for Windows..."
GOOS=windows GOARCH=amd64 go build -o "$WINDOWS_BIN" main.go
if [ $? -eq 0 ]; then
echo "Successfully built: $WINDOWS_BIN"
else
echo "Failed to build Windows binary"
exit 1
fi
# Build for Linux ARM (ARMv7)
echo "Building for Linux ARMv7..."
GOOS=linux GOARCH=arm GOARM=7 go build -o "$LINUX_ARM_BIN" main.go
if [ $? -eq 0 ]; then
echo "Successfully built: $LINUX_ARM_BIN"
else
echo "Failed to build Linux ARMv7 binary"
exit 1
fi
echo "Build complete."

View File

@@ -0,0 +1,5 @@
module dataTools
go 1.25.7
require github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646

View File

@@ -0,0 +1,2 @@
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=

View File

@@ -0,0 +1,84 @@
package main
import (
"flag"
"fmt"
"image"
"image/jpeg"
"os"
"path/filepath"
"strings"
"github.com/nfnt/resize"
)
func main() {
// Command line arguments
dirPath := flag.String("dir", "./", "Directory containing images")
maxWidth := flag.Uint("width", 1000, "Maximum width for resizing")
quality := flag.Int("quality", 75, "JPEG quality (1-100)")
flag.Parse()
files, err := os.ReadDir(*dirPath)
if err != nil {
fmt.Printf("Error reading directory: %v\n", err)
return
}
fmt.Printf("Processing images in %s (Max Width: %d, Quality: %d)\n", *dirPath, *maxWidth, *quality)
for _, file := range files {
if file.IsDir() {
continue
}
ext := strings.ToLower(filepath.Ext(file.Name()))
if ext != ".jpg" && ext != ".jpeg" && ext != ".png" {
continue
}
filePath := filepath.Join(*dirPath, file.Name())
processImage(filePath, *maxWidth, *quality)
}
fmt.Println("Done. Your storage can finally breathe again.")
}
func processImage(path string, maxWidth uint, quality int) {
file, err := os.Open(path)
if err != nil {
fmt.Printf("Failed to open %s: %v\n", path, err)
return
}
img, _, err := image.Decode(file)
file.Close()
if err != nil {
fmt.Printf("Failed to decode %s: %v\n", path, err)
return
}
// Only resize if original is wider than maxWidth
bounds := img.Bounds()
var finalImg image.Image
if uint(bounds.Dx()) > maxWidth {
finalImg = resize.Resize(maxWidth, 0, img, resize.Lanczos3)
fmt.Printf("Resized and compressed: %s\n", filepath.Base(path))
} else {
finalImg = img
fmt.Printf("Compressed (no resize needed): %s\n", filepath.Base(path))
}
// Overwrite the original file
out, err := os.Create(path)
if err != nil {
fmt.Printf("Failed to create output file %s: %v\n", path, err)
return
}
defer out.Close()
err = jpeg.Encode(out, finalImg, &jpeg.Options{Quality: quality})
if err != nil {
fmt.Printf("Failed to encode %s: %v\n", path, err)
}
}

View File

@@ -0,0 +1,8 @@
module MPPOS
go 1.25.7
require (
github.com/google/uuid v1.6.0
github.com/joho/godotenv v1.5.1
)

View File

@@ -0,0 +1,4 @@
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=

View File

@@ -0,0 +1,143 @@
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"time"
"github.com/google/uuid"
"github.com/joho/godotenv"
)
// Request structures based on MP documentation
type PrintRequest struct {
Type string `json:"type"`
ExternalReference string `json:"external_reference"`
Config PrintConfig `json:"config"`
Content string `json:"content"`
}
type PrintConfig struct {
Point PointSettings `json:"point"`
}
type PointSettings struct {
TerminalID string `json:"terminal_id"`
Subtype string `json:"subtype"`
}
func main() {
err := godotenv.Load("../.env")
if err != nil {
fmt.Println("Error loading .env file")
}
// Example receipt using supported tags: {b}, {center}, {br}, {s}
receiptContent := "{center}{b}SEKIPOS VENTA{/b}{br}" +
"--------------------------------{br}" +
"{left}Producto: Choripan Premium{br}" +
"{left}Total: $5.500{br}" +
"--------------------------------{br}" +
"{center}{s}Gracias por su compra{/s}{br}"
resp, err := SendPrintAction(receiptContent)
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
fmt.Printf("Response: %s\n", resp)
}
func SendPrintAction(content string) (string, error) {
apiURL := "https://api.mercadopago.com/terminals/v1/actions"
accessToken := os.Getenv("MP_ACCESS_TOKEN")
terminalID := os.Getenv("MP_TERMINAL_ID")
payload := PrintRequest{
Type: "print", // Required
ExternalReference: fmt.Sprintf("ref_%d", time.Now().Unix()),
Config: PrintConfig{
Point: PointSettings{
TerminalID: terminalID,
Subtype: "custom", // For text with tags
},
},
Content: content, // Must be 100-4096 chars
}
jsonData, _ := json.Marshal(payload)
req, _ := http.NewRequest("POST", apiURL, bytes.NewBuffer(jsonData))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+accessToken)
// Mandatory Unique UUID V4
req.Header.Set("X-Idempotency-Key", uuid.New().String())
client := &http.Client{Timeout: 10 * time.Second}
resp, err := client.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
if resp.StatusCode != http.StatusCreated && resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("MP Error %d: %s", resp.StatusCode, string(body))
}
return string(body), nil
}
func PartialRefund(orderID string, paymentID string, amount string) (string, error) {
// Endpoint para reembolsos según la referencia de API
apiURL := fmt.Sprintf("https://api.mercadopago.com/v1/orders/%s/refund", orderID)
token := os.Getenv("MP_ACCESS_TOKEN")
payload := map[string]interface{}{
"transactions": []map[string]string{
{
"id": paymentID,
"amount": amount, // Debe ser un string sin decimales
},
},
}
body, _ := json.Marshal(payload)
req, _ := http.NewRequest("POST", apiURL, bytes.NewBuffer(body))
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-Idempotency-Key", uuid.New().String())
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
resBody, _ := io.ReadAll(resp.Body)
return string(resBody), nil
}
func GetOrderStatus(orderID string) (string, error) {
apiURL := fmt.Sprintf("https://api.mercadopago.com/v1/orders/%s", orderID)
token := os.Getenv("MP_ACCESS_TOKEN")
req, _ := http.NewRequest("GET", apiURL, nil)
req.Header.Set("Authorization", "Bearer "+token)
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
resBody, _ := io.ReadAll(resp.Body)
return string(resBody), nil
}

View File

@@ -0,0 +1,2 @@
config.json
COMScannerGO-*

View File

@@ -0,0 +1,42 @@
#!/bin/bash
# Define binary names
LINUX_BIN="COMScannerGO-linux"
LINUX_ARM_BIN="COMScannerGO-linuxARMv7"
WINDOWS_BIN="COMScannerGO-windows.exe"
echo "Starting build process..."
# Build for Linux (64-bit)
echo "Building for Linux..."
GOOS=linux GOARCH=amd64 go build -o "$LINUX_BIN" main.go
if [ $? -eq 0 ]; then
echo "Successfully built: $LINUX_BIN"
else
echo "Failed to build Linux binary"
exit 1
fi
# Build for Windows (64-bit)
echo "Building for Windows..."
GOOS=windows GOARCH=amd64 go build -o "$WINDOWS_BIN" main.go
if [ $? -eq 0 ]; then
echo "Successfully built: $WINDOWS_BIN"
else
echo "Failed to build Windows binary"
exit 1
fi
# Build for Linux ARM (ARMv7)
echo "Building for Linux ARMv7..."
GOOS=linux GOARCH=arm GOARM=7 go build -o "$LINUX_ARM_BIN" main.go
if [ $? -eq 0 ]; then
echo "Successfully built: $LINUX_ARM_BIN"
else
echo "Failed to build Linux ARMv7 binary"
exit 1
fi
echo "Build complete."

View File

@@ -0,0 +1,7 @@
module ScannerGO
go 1.24.0
require github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07
require golang.org/x/sys v0.41.0 // indirect

View File

@@ -0,0 +1,4 @@
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07 h1:UyzmZLoiDWMRywV4DUYb9Fbt8uiOSooupjTq10vpvnU=
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=

View File

@@ -0,0 +1,175 @@
package main
import (
"bufio"
"bytes"
"encoding/json"
"flag"
"fmt"
"io"
"net/http"
"net/url"
"os"
"strings"
"time"
"github.com/tarm/serial"
)
type Config struct {
Port string `json:"port"`
URL string `json:"url"`
BaudRate int `json:"baud"`
Delimiter string `json:"delimiter"`
FlowControl string `json:"flow_control"`
}
var defaultConfig = Config{
Port: "/dev/ttyACM0",
URL: "https://scanner.sekidesu.xyz/scan",
BaudRate: 115200,
Delimiter: "\n",
FlowControl: "none",
}
const configPath = "config.json"
func main() {
cfg := loadConfig()
portName := flag.String("port", cfg.Port, "Serial port name")
endpoint := flag.String("url", cfg.URL, "Target URL endpoint")
baudRate := flag.Int("baud", cfg.BaudRate, "Baud rate")
delim := flag.String("delim", cfg.Delimiter, "Line delimiter")
flow := flag.String("flow", cfg.FlowControl, "Flow control: none, hardware, software")
save := flag.Bool("save", false, "Save current parameters to config.json")
flag.Parse()
cfg.Port = *portName
cfg.URL = *endpoint
cfg.BaudRate = *baudRate
cfg.Delimiter = *delim
cfg.FlowControl = *flow
if *save {
saveConfig(cfg)
fmt.Println("Settings saved to", configPath)
}
serialConfig := &serial.Config{
Name: cfg.Port,
Baud: cfg.BaudRate,
ReadTimeout: time.Millisecond * 500,
}
// tarm/serial uses boolean flags for flow control if available in the version used
// If your version doesn't support these fields, you may need to update the package
// or manage the lines manually via the file descriptor.
/* Note: tarm/serial usually requires specific fork or version
for full RTS/CTS hardware flow control support.
*/
port, err := serial.OpenPort(serialConfig)
if err != nil {
fmt.Printf("Error opening port %s: %v\n", cfg.Port, err)
os.Exit(1)
}
defer port.Close()
fmt.Printf("Listening on %s (Baud: %d, Flow: %s)...\n", cfg.Port, cfg.BaudRate, cfg.FlowControl)
scanner := bufio.NewScanner(port)
scanner.Split(func(data []byte, atEOF bool) (advance int, token []byte, err error) {
if atEOF && len(data) == 0 {
return 0, nil, nil
}
if i := bytes.Index(data, []byte(cfg.Delimiter)); i >= 0 {
return i + len(cfg.Delimiter), data[0:i], nil
}
if atEOF {
return len(data), data, nil
}
return 0, nil, nil
})
for scanner.Scan() {
rawContent := scanner.Text()
content := strings.TrimFunc(rawContent, func(r rune) bool {
return r < 32 || r > 126
})
if content != "" {
sendToEndpoint(cfg.URL, content)
}
}
if err := scanner.Err(); err != nil {
fmt.Printf("Scanner error: %v\n", err)
}
}
func loadConfig() Config {
if _, err := os.Stat(configPath); os.IsNotExist(err) {
saveConfig(defaultConfig)
return defaultConfig
}
file, err := os.Open(configPath)
if err != nil {
return defaultConfig
}
defer file.Close()
var cfg Config
decoder := json.NewDecoder(file)
if err := decoder.Decode(&cfg); err != nil {
return defaultConfig
}
if cfg.Delimiter == "" {
cfg.Delimiter = "\n"
}
if cfg.FlowControl == "" {
cfg.FlowControl = "none"
}
return cfg
}
func saveConfig(cfg Config) {
file, err := os.Create(configPath)
if err != nil {
fmt.Printf("Failed to create/save config: %v\n", err)
return
}
defer file.Close()
encoder := json.NewEncoder(file)
encoder.SetIndent("", " ")
encoder.Encode(cfg)
}
func sendToEndpoint(baseURL, content string) {
client := &http.Client{
Timeout: 5 * time.Second,
}
fullURL := fmt.Sprintf("%s?content=%s", baseURL, url.QueryEscape(content))
resp, err := client.Get(fullURL)
if err != nil {
fmt.Printf("Network Error: %v\n", err)
return
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Printf("Error reading response: %v\n", err)
return
}
fmt.Printf("Data: [%s] | Status: %s\n", content, resp.Status)
if len(body) > 0 {
fmt.Printf("Response: %s\n", string(body))
}
fmt.Println(strings.Repeat("-", 30))
}

View File

@@ -0,0 +1,2 @@
config.json
HIDScannerGO-*

View File

@@ -0,0 +1,18 @@
#!/bin/bash
LINUX_BIN="HIDScannerGO-linux"
WINDOWS_BIN="HIDScannerGO-windows.exe"
echo "Starting build process..."
# 1. Build for Linux (Host)
echo "Building for Linux (64-bit)..."
CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -o "$LINUX_BIN" .
# 2. Build for Windows (Needs MinGW)
echo "Building for Windows (64-bit)..."
# We must point to the mingw gcc and enable CGO
CGO_ENABLED=1 GOOS=windows GOARCH=amd64 CC=x86_64-w64-mingw32-gcc \
go build -ldflags="-H=windowsgui" -o "$WINDOWS_BIN" .
echo "Build complete."

View File

@@ -0,0 +1,46 @@
module ScannerGO
go 1.24.0
require (
fyne.io/fyne/v2 v2.7.3
gioui.org v0.9.0
github.com/google/gousb v1.1.3
)
require (
fyne.io/systray v1.12.0 // indirect
gioui.org/shader v1.0.8 // indirect
github.com/BurntSushi/toml v1.5.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fredbi/uri v1.1.1 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/fyne-io/gl-js v0.2.0 // indirect
github.com/fyne-io/glfw-js v0.3.0 // indirect
github.com/fyne-io/image v0.1.1 // indirect
github.com/fyne-io/oksvg v0.2.0 // indirect
github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71 // indirect
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a // indirect
github.com/go-text/render v0.2.0 // indirect
github.com/go-text/typesetting v0.3.3 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/hack-pad/go-indexeddb v0.3.2 // indirect
github.com/hack-pad/safejs v0.1.0 // indirect
github.com/jeandeaual/go-locale v0.0.0-20250612000132-0ef82f21eade // indirect
github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
github.com/nicksnyder/go-i18n/v2 v2.5.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rymdport/portal v0.4.2 // indirect
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c // indirect
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef // indirect
github.com/stretchr/testify v1.11.1 // indirect
github.com/yuin/goldmark v1.7.8 // indirect
golang.org/x/exp/shiny v0.0.0-20250408133849-7e4ce0ab07d0 // indirect
golang.org/x/image v0.26.0 // indirect
golang.org/x/net v0.35.0 // indirect
golang.org/x/sys v0.41.0 // indirect
golang.org/x/text v0.24.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

View File

@@ -0,0 +1,93 @@
eliasnaur.com/font v0.0.0-20230308162249-dd43949cb42d h1:ARo7NCVvN2NdhLlJE9xAbKweuI9L6UgfTbYb0YwPacY=
eliasnaur.com/font v0.0.0-20230308162249-dd43949cb42d/go.mod h1:OYVuxibdk9OSLX8vAqydtRPP87PyTFcT9uH3MlEGBQA=
fyne.io/fyne/v2 v2.7.3 h1:xBT/iYbdnNHONWO38fZMBrVBiJG8rV/Jypmy4tVfRWE=
fyne.io/fyne/v2 v2.7.3/go.mod h1:gu+dlIcZWSzKZmnrY8Fbnj2Hirabv2ek+AKsfQ2bBlw=
fyne.io/systray v1.12.0 h1:CA1Kk0e2zwFlxtc02L3QFSiIbxJ/P0n582YrZHT7aTM=
fyne.io/systray v1.12.0/go.mod h1:RVwqP9nYMo7h5zViCBHri2FgjXF7H2cub7MAq4NSoLs=
gioui.org v0.9.0 h1:4u7XZwnb5kzQW91Nz/vR0wKD6LdW9CaVF96r3rfy4kc=
gioui.org v0.9.0/go.mod h1:CjNig0wAhLt9WZxOPAusgFD8x8IRvqt26LdDBa3Jvao=
gioui.org/cpu v0.0.0-20210808092351-bfe733dd3334/go.mod h1:A8M0Cn5o+vY5LTMlnRoK3O5kG+rH0kWfJjeKd9QpBmQ=
gioui.org/shader v1.0.8 h1:6ks0o/A+b0ne7RzEqRZK5f4Gboz2CfG+mVliciy6+qA=
gioui.org/shader v1.0.8/go.mod h1:mWdiME581d/kV7/iEhLmUgUK5iZ09XR5XpduXzbePVM=
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/felixge/fgprof v0.9.3 h1:VvyZxILNuCiUCSXtPtYmmtGvb65nqXh2QFWc0Wpf2/g=
github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw=
github.com/fredbi/uri v1.1.1 h1:xZHJC08GZNIUhbP5ImTHnt5Ya0T8FI2VAwI/37kh2Ko=
github.com/fredbi/uri v1.1.1/go.mod h1:4+DZQ5zBjEwQCDmXW5JdIjz0PUA+yJbvtBv+u+adr5o=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/fyne-io/gl-js v0.2.0 h1:+EXMLVEa18EfkXBVKhifYB6OGs3HwKO3lUElA0LlAjs=
github.com/fyne-io/gl-js v0.2.0/go.mod h1:ZcepK8vmOYLu96JoxbCKJy2ybr+g1pTnaBDdl7c3ajI=
github.com/fyne-io/glfw-js v0.3.0 h1:d8k2+Y7l+zy2pc7wlGRyPfTgZoqDf3AI4G+2zOWhWUk=
github.com/fyne-io/glfw-js v0.3.0/go.mod h1:Ri6te7rdZtBgBpxLW19uBpp3Dl6K9K/bRaYdJ22G8Jk=
github.com/fyne-io/image v0.1.1 h1:WH0z4H7qfvNUw5l4p3bC1q70sa5+YWVt6HCj7y4VNyA=
github.com/fyne-io/image v0.1.1/go.mod h1:xrfYBh6yspc+KjkgdZU/ifUC9sPA5Iv7WYUBzQKK7JM=
github.com/fyne-io/oksvg v0.2.0 h1:mxcGU2dx6nwjJsSA9PCYZDuoAcsZ/OuJlvg/Q9Njfo8=
github.com/fyne-io/oksvg v0.2.0/go.mod h1:dJ9oEkPiWhnTFNCmRgEze+YNprJF7YRbpjgpWS4kzoI=
github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71 h1:5BVwOaUSBTlVZowGO6VZGw2H/zl9nrd3eCZfYV+NfQA=
github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71/go.mod h1:9YTyiznxEY1fVinfM7RvRcjRHbw2xLBJ3AAGIT0I4Nw=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a h1:vxnBhFDDT+xzxf1jTJKMKZw3H0swfWk9RpWbBbDK5+0=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-text/render v0.2.0 h1:LBYoTmp5jYiJ4NPqDc2pz17MLmA3wHw1dZSVGcOdeAc=
github.com/go-text/render v0.2.0/go.mod h1:CkiqfukRGKJA5vZZISkjSYrcdtgKQWRa2HIzvwNN5SU=
github.com/go-text/typesetting v0.3.3 h1:ihGNJU9KzdK2QRDy1Bm7FT5RFQoYb+3n3EIhI/4eaQc=
github.com/go-text/typesetting v0.3.3/go.mod h1:vIRUT25mLQaSh4C8H/lIsKppQz/Gdb8Pu/tNwpi52ts=
github.com/go-text/typesetting-utils v0.0.0-20250618110550-c820a94c77b8 h1:4KCscI9qYWMGTuz6BpJtbUSRzcBrUSSE0ENMJbNSrFs=
github.com/go-text/typesetting-utils v0.0.0-20250618110550-c820a94c77b8/go.mod h1:3/62I4La/HBRX9TcTpBj4eipLiwzf+vhI+7whTc9V7o=
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/google/gousb v1.1.3 h1:xt6M5TDsGSZ+rlomz5Si5Hmd/Fvbmo2YCJHN+yGaK4o=
github.com/google/gousb v1.1.3/go.mod h1:GGWUkK0gAXDzxhwrzetW592aOmkkqSGcj5KLEgmCVUg=
github.com/google/pprof v0.0.0-20211214055906-6f57359322fd h1:1FjCyPC+syAzJ5/2S8fqdZK1R22vvA0J7JZKcuOIQ7Y=
github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg=
github.com/hack-pad/go-indexeddb v0.3.2 h1:DTqeJJYc1usa45Q5r52t01KhvlSN02+Oq+tQbSBI91A=
github.com/hack-pad/go-indexeddb v0.3.2/go.mod h1:QvfTevpDVlkfomY498LhstjwbPW6QC4VC/lxYb0Kom0=
github.com/hack-pad/safejs v0.1.0 h1:qPS6vjreAqh2amUqj4WNG1zIw7qlRQJ9K10eDKMCnE8=
github.com/hack-pad/safejs v0.1.0/go.mod h1:HdS+bKF1NrE72VoXZeWzxFOVQVUSqZJAG0xNCnb+Tio=
github.com/jeandeaual/go-locale v0.0.0-20250612000132-0ef82f21eade h1:FmusiCI1wHw+XQbvL9M+1r/C3SPqKrmBaIOYwVfQoDE=
github.com/jeandeaual/go-locale v0.0.0-20250612000132-0ef82f21eade/go.mod h1:ZDXo8KHryOWSIqnsb/CiDq7hQUYryCgdVnxbj8tDG7o=
github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25 h1:YLvr1eE6cdCqjOe972w/cYF+FjW34v27+9Vo5106B4M=
github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25/go.mod h1:kLgvv7o6UM+0QSf0QjAse3wReFDsb9qbZJdfexWlrQw=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
github.com/nicksnyder/go-i18n/v2 v2.5.1 h1:IxtPxYsR9Gp60cGXjfuR/llTqV8aYMsC472zD0D1vHk=
github.com/nicksnyder/go-i18n/v2 v2.5.1/go.mod h1:DrhgsSDZxoAfvVrBVLXoxZn/pN5TXqaDbq7ju94viiQ=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/pkg/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA=
github.com/pkg/profile v1.7.0/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDjvNo=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rymdport/portal v0.4.2 h1:7jKRSemwlTyVHHrTGgQg7gmNPJs88xkbKcIL3NlcmSU=
github.com/rymdport/portal v0.4.2/go.mod h1:kFF4jslnJ8pD5uCi17brj/ODlfIidOxlgUDTO5ncnC4=
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c h1:km8GpoQut05eY3GiYWEedbTT0qnSxrCjsVbb7yKY1KE=
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c/go.mod h1:cNQ3dwVJtS5Hmnjxy6AgTPd0Inb3pW05ftPSX7NZO7Q=
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef h1:Ch6Q+AZUxDBCVqdkI8FSpFyZDtCVBc2VmejdNrm5rRQ=
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef/go.mod h1:nXTWP6+gD5+LUJ8krVhhoeHjvHTutPxMYl5SvkcnJNE=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic=
github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM=
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8=
golang.org/x/exp/shiny v0.0.0-20250408133849-7e4ce0ab07d0 h1:tMSqXTK+AQdW3LpCbfatHSRPHeW6+2WuxaVQuHftn80=
golang.org/x/exp/shiny v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:ygj7T6vSGhhm/9yTpOQQNvuAUFziTH7RUiH74EoE2C8=
golang.org/x/image v0.26.0 h1:4XjIFEZWQmCZi6Wv8BoxsDhRU3RVnLX04dToTDAEPlY=
golang.org/x/image v0.26.0/go.mod h1:lcxbMFAovzpnJxzXS3nyL83K27tmqtKzIJpctK8YO5c=
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -0,0 +1,246 @@
package main
import (
"encoding/json"
"flag"
"fmt"
"image/color"
"net/http"
"os"
"time"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/app"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/widget"
"github.com/google/gousb"
)
type Config struct {
TargetURL string `json:"target_url"`
VendorID uint16 `json:"vendor_id"`
ProductID uint16 `json:"product_id"`
}
var hidMap = map[byte]string{
4: "a", 5: "b", 6: "c", 7: "d", 8: "e", 9: "f", 10: "g", 11: "h", 12: "i",
13: "j", 14: "k", 15: "l", 16: "m", 17: "n", 18: "o", 19: "p", 20: "q",
21: "r", 22: "s", 23: "t", 24: "u", 25: "v", 26: "w", 27: "x", 28: "y", 29: "z",
30: "1", 31: "2", 32: "3", 33: "4", 34: "5", 35: "6", 36: "7", 37: "8", 38: "9", 39: "0",
44: " ", 45: "-", 46: "=", 55: ".", 56: "/",
}
type BridgeApp struct {
urlEntry *widget.Entry
status *canvas.Text
logList *widget.List
logs []string
window fyne.Window
config Config
isCLI bool
}
func (b *BridgeApp) saveConfig() {
b.config.TargetURL = b.urlEntry.Text
data, _ := json.MarshalIndent(b.config, "", " ")
_ = os.WriteFile("config.json", data, 0644)
}
func loadConfig() Config {
conf := Config{
TargetURL: "https://scanner.sekidesu.xyz/scan",
VendorID: 0xFFFF,
ProductID: 0x0035,
}
file, err := os.ReadFile("config.json")
if err == nil {
json.Unmarshal(file, &conf)
} else {
// Create default config if missing
data, _ := json.MarshalIndent(conf, "", " ")
os.WriteFile("config.json", data, 0644)
}
return conf
}
func main() {
cliMode := flag.Bool("cli", false, "Run in CLI mode without GUI")
flag.Parse()
conf := loadConfig()
bridge := &BridgeApp{
config: conf,
isCLI: *cliMode,
}
if *cliMode {
fmt.Println("Running in CLI mode...")
bridge.usbListenLoop()
return
}
a := app.New()
w := a.NewWindow("POS Hardware Bridge (Go)")
bridge.window = w
bridge.urlEntry = widget.NewEntry()
bridge.urlEntry.SetText(conf.TargetURL)
bridge.status = canvas.NewText("Status: Booting...", color.Black)
bridge.status.TextSize = 14
bridge.logList = widget.NewList(
func() int { return len(bridge.logs) },
func() fyne.CanvasObject { return widget.NewLabel("template") },
func(i widget.ListItemID, o fyne.CanvasObject) {
o.(*widget.Label).SetText(bridge.logs[i])
},
)
content := container.NewBorder(
container.NewVBox(
widget.NewLabel("Target POS Endpoint:"),
bridge.urlEntry,
bridge.status,
widget.NewLabel("Activity Log:"),
),
nil, nil, nil,
bridge.logList,
)
w.SetContent(content)
w.Resize(fyne.NewSize(500, 400))
w.SetOnClosed(func() {
if !bridge.isCLI {
bridge.config.TargetURL = bridge.urlEntry.Text
data, err := json.MarshalIndent(bridge.config, "", " ")
if err == nil {
_ = os.WriteFile("config.json", data, 0644)
fmt.Println("Configuration saved.")
}
}
})
go bridge.usbListenLoop()
w.ShowAndRun()
}
func (b *BridgeApp) addLog(msg string) {
ts := time.Now().Format("15:04:05")
formatted := fmt.Sprintf("[%s] %s", ts, msg)
if b.isCLI {
fmt.Println(formatted)
return
}
fyne.DoAndWait(func() {
b.logs = append([]string{formatted}, b.logs...)
if len(b.logs) > 15 {
b.logs = b.logs[:15]
}
b.logList.Refresh()
})
}
func (b *BridgeApp) updateStatus(msg string, col color.Color) {
if b.isCLI {
fmt.Printf("STATUS: %s\n", msg)
return
}
fyne.DoAndWait(func() {
b.status.Text = msg
b.status.Color = col
b.status.Refresh()
})
}
func (b *BridgeApp) sendToPos(barcode string) {
url := b.config.TargetURL
if !b.isCLI {
url = b.urlEntry.Text
}
b.addLog(fmt.Sprintf("Captured: %s. Sending to %s", barcode, url))
client := http.Client{Timeout: 3 * time.Second}
resp, err := client.Get(url + "?content=" + barcode)
if err != nil {
b.addLog("HTTP Error: Backend unreachable")
return
}
defer resp.Body.Close()
b.addLog(fmt.Sprintf("Success: POS returned %d", resp.StatusCode))
}
func (b *BridgeApp) usbListenLoop() {
ctx := gousb.NewContext()
defer ctx.Close()
for {
dev, err := ctx.OpenDeviceWithVIDPID(gousb.ID(b.config.VendorID), gousb.ID(b.config.ProductID))
if err != nil || dev == nil {
b.updateStatus("Scanner unplugged. Waiting...", color.NRGBA{200, 0, 0, 255})
time.Sleep(2 * time.Second)
continue
}
b.updateStatus("Scanner Locked & Ready", color.NRGBA{0, 180, 0, 255})
intf, done, err := dev.DefaultInterface()
if err != nil {
b.addLog("Error claiming interface")
dev.Close()
time.Sleep(2 * time.Second)
continue
}
var inEp *gousb.InEndpoint
for _, epDesc := range intf.Setting.Endpoints {
if epDesc.Direction == gousb.EndpointDirectionIn {
inEp, _ = intf.InEndpoint(epDesc.Number)
break
}
}
if inEp == nil {
b.addLog("No IN endpoint found")
done()
dev.Close()
continue
}
currentBarcode := ""
buf := make([]byte, inEp.Desc.MaxPacketSize)
for {
n, err := inEp.Read(buf)
if err != nil {
break
}
if n < 3 || buf[2] == 0 {
continue
}
modifier := buf[0]
keycode := buf[2]
isShift := (modifier == 2 || modifier == 32)
if keycode == 40 {
if currentBarcode != "" {
go b.sendToPos(currentBarcode)
currentBarcode = ""
}
} else if val, ok := hidMap[keycode]; ok {
if isShift && len(val) == 1 && val[0] >= 'a' && val[0] <= 'z' {
val = string(val[0] - 32)
}
currentBarcode += val
}
}
done()
dev.Close()
b.addLog("Hardware connection lost. Reconnecting...")
}
}

View File

@@ -0,0 +1,5 @@
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch

View File

@@ -0,0 +1,10 @@
{
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [
"platformio.platformio-ide"
],
"unwantedRecommendations": [
"ms-vscode.cpptools-extension-pack"
]
}

View File

@@ -0,0 +1,37 @@
This directory is intended for project header files.
A header file is a file containing C declarations and macro definitions
to be shared between several project source files. You request the use of a
header file in your project source file (C, C++, etc) located in `src` folder
by including it, with the C preprocessing directive `#include'.
```src/main.c
#include "header.h"
int main (void)
{
...
}
```
Including a header file produces the same results as copying the header file
into each source file that needs it. Such copying would be time-consuming
and error-prone. With a header file, the related declarations appear
in only one place. If they need to be changed, they can be changed in one
place, and programs that include the header file will automatically use the
new version when next recompiled. The header file eliminates the labor of
finding and changing all the copies as well as the risk that a failure to
find one copy will result in inconsistencies within a program.
In C, the convention is to give header files names that end with `.h'.
Read more about using header files in official GCC documentation:
* Include Syntax
* Include Operation
* Once-Only Headers
* Computed Includes
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html

View File

@@ -0,0 +1,46 @@
This directory is intended for project specific (private) libraries.
PlatformIO will compile them to static libraries and link into the executable file.
The source code of each library should be placed in a separate directory
("lib/your_library_name/[Code]").
For example, see the structure of the following example libraries `Foo` and `Bar`:
|--lib
| |
| |--Bar
| | |--docs
| | |--examples
| | |--src
| | |- Bar.c
| | |- Bar.h
| | |- library.json (optional. for custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
| |
| |--Foo
| | |- Foo.c
| | |- Foo.h
| |
| |- README --> THIS FILE
|
|- platformio.ini
|--src
|- main.c
Example contents of `src/main.c` using Foo and Bar:
```
#include <Foo.h>
#include <Bar.h>
int main (void)
{
...
}
```
The PlatformIO Library Dependency Finder will find automatically dependent
libraries by scanning project source files.
More information about PlatformIO Library Dependency Finder
- https://docs.platformio.org/page/librarymanager/ldf.html

View File

@@ -0,0 +1,17 @@
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env:waveshare_rp2040_zero]
platform = https://github.com/maxgerhardt/platform-raspberrypi.git
board = waveshare_rp2040_zero
framework = arduino
monitor_speed = 115200
lib_archive = no
lib_deps = bogde/HX711@^0.7.5

View File

@@ -0,0 +1,32 @@
#include <Arduino.h>
#include "HX711.h"
const int LOADCELL_SCK_PIN = 3;
const int LOADCELL_DOUT_PIN = 2;
const long LOADCELL_OFFSET = 0;
const long LOADCELL_DIVIDER = 0;
HX711 scale;
void setup() {
Serial.begin(115200);
while(!Serial);
Serial.println("Remove all weight from the scale.");
delay(2000);
scale.set_scale();
scale.tare();
Serial.println("Tare complete. Place a known weight on the scale.");
}
void loop() {
if (scale.is_ready()) {
long reading = scale.get_units(10);
Serial.print("Raw Value: ");
Serial.println(reading);
} else {
Serial.println("HX711 not found.");
}
delay(1000);
}

View File

@@ -0,0 +1,11 @@
This directory is intended for PlatformIO Test Runner and project tests.
Unit Testing is a software testing method by which individual units of
source code, sets of one or more MCU program modules together with associated
control data, usage procedures, and operating procedures, are tested to
determine whether they are fit for use. Unit testing finds problems early
in the development cycle.
More information about PlatformIO Unit Testing:
- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html

View File

@@ -0,0 +1,5 @@
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch

View File

@@ -0,0 +1,10 @@
{
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [
"platformio.platformio-ide"
],
"unwantedRecommendations": [
"ms-vscode.cpptools-extension-pack"
]
}

View File

@@ -0,0 +1,37 @@
This directory is intended for project header files.
A header file is a file containing C declarations and macro definitions
to be shared between several project source files. You request the use of a
header file in your project source file (C, C++, etc) located in `src` folder
by including it, with the C preprocessing directive `#include'.
```src/main.c
#include "header.h"
int main (void)
{
...
}
```
Including a header file produces the same results as copying the header file
into each source file that needs it. Such copying would be time-consuming
and error-prone. With a header file, the related declarations appear
in only one place. If they need to be changed, they can be changed in one
place, and programs that include the header file will automatically use the
new version when next recompiled. The header file eliminates the labor of
finding and changing all the copies as well as the risk that a failure to
find one copy will result in inconsistencies within a program.
In C, the convention is to give header files names that end with `.h'.
Read more about using header files in official GCC documentation:
* Include Syntax
* Include Operation
* Once-Only Headers
* Computed Includes
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html

View File

@@ -0,0 +1,46 @@
This directory is intended for project specific (private) libraries.
PlatformIO will compile them to static libraries and link into the executable file.
The source code of each library should be placed in a separate directory
("lib/your_library_name/[Code]").
For example, see the structure of the following example libraries `Foo` and `Bar`:
|--lib
| |
| |--Bar
| | |--docs
| | |--examples
| | |--src
| | |- Bar.c
| | |- Bar.h
| | |- library.json (optional. for custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
| |
| |--Foo
| | |- Foo.c
| | |- Foo.h
| |
| |- README --> THIS FILE
|
|- platformio.ini
|--src
|- main.c
Example contents of `src/main.c` using Foo and Bar:
```
#include <Foo.h>
#include <Bar.h>
int main (void)
{
...
}
```
The PlatformIO Library Dependency Finder will find automatically dependent
libraries by scanning project source files.
More information about PlatformIO Library Dependency Finder
- https://docs.platformio.org/page/librarymanager/ldf.html

View File

@@ -0,0 +1,17 @@
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env:waveshare_rp2040_zero]
platform = https://github.com/maxgerhardt/platform-raspberrypi.git
board = waveshare_rp2040_zero
framework = arduino
monitor_speed = 115200
lib_archive = no
lib_deps = bogde/HX711@^0.7.5

View File

@@ -0,0 +1,50 @@
#ifndef TM1621_CONFIG_H
#define TM1621_CONFIG_H
#include <Arduino.h>
const uint8_t digitMap[] = {
0b11111100, // 0
0b00000110, // 1
0b01011011, // 2
0b01001111, // 3
0b01100110, // 4
0b01101101, // 5
0b01111101, // 6
0b00000111, // 7
0b01111111, // 8
0b01101111 // 9
};
const int arrows[] = {283,363,183,203}; //tare,?,?,Zero
const int battery[] = {63,83,103}; //LOW / MED / FULL
// Following your pattern: {A, B, C, D, E, F, G}
const int row1_d1[] = {300, 301, 302, 313, 312, 310, 311}; // Addresses 30 & 31
const int row1_d2[] = {280, 281, 282, 293, 292, 290, 291}; // Addresses 28 & 29
const int row1_d3[] = {260, 261, 262, 273, 272, 270, 271}; // Addresses 26 & 27
const int row1_d4[] = {240, 241, 242, 253, 252, 250, 251}; // Addresses 24 & 25
const int row1_d5[] = {220, 221, 222, 233, 232, 230, 231}; // Addresses 22 & 23
const int row1_decimal[] = {263,232,223}; //XX.X.X.X
const int row2_d1[] = {200, 201, 202, 213, 212, 210, 211}; // Addresses 20 & 21
const int row2_d2[] = {180, 181, 182, 193, 192, 190, 191}; // Addresses 18 & 19
const int row2_d3[] = {160, 161, 162, 173, 172, 170, 171}; // Addresses 16 & 17
const int row2_d4[] = {140, 141, 142, 153, 152, 150, 151}; // Addresses 14 & 15
const int row2_d5[] = {120, 121, 122, 133, 132, 130, 131}; // Addresses 12 & 13
const int row2_decimal[] = {163,143,123}; //XX.X.X.X
const int row3_d1[] = {100, 101, 102, 113, 112, 110, 111}; // Addresses 10 & 11
const int row3_d2[] = {80, 81, 82, 93, 92, 90, 91}; // Addresses 8 & 9
const int row3_d3[] = {60, 61, 62, 73, 72, 70, 71}; // Addresses 6 & 7
const int row3_d4[] = {40, 41, 42, 53, 52, 50, 51}; // Addresses 4 & 5
const int row3_d5[] = {20, 21, 22, 33, 32, 30, 31}; // Addresses 2 & 3
const int row3_d6[] = {0, 1, 2, 13, 12, 10, 11}; // Addresses 0 & 1
const int row3_decimal[] = {43,23,3}; //XX.X.X.X
const int* digitsRow1[] = {row1_d1, row1_d2, row1_d3, row1_d4, row1_d5};
const int* digitsRow2[] = {row2_d1, row2_d2, row2_d3, row2_d4, row2_d5};
const int* digitsRow3[] = {row3_d1, row3_d2, row3_d3, row3_d4, row3_d5, row3_d6};
const int* decimals[] = {row1_decimal, row2_decimal, row3_decimal};
#endif

View File

@@ -0,0 +1,180 @@
#include <Arduino.h>
#include "TM1621_Config.h"
#define LCD_DATA 29
#define LCD_WR 28
#define LCD_CS 27
// Tracking variables
int currentAddr = 0;
int currentBit = 0;
void writeBits(uint32_t data, uint8_t count) {
for (int8_t i = count - 1; i >= 0; i--) {
digitalWrite(LCD_WR, LOW);
digitalWrite(LCD_DATA, (data >> i) & 0x01);
digitalWrite(LCD_WR, HIGH);
}
}
void sendCmd(uint8_t cmd) {
digitalWrite(LCD_CS, LOW);
writeBits(0x04, 3); // Binary 100 [cite: 432]
writeBits(cmd, 8); // Command [cite: 413, 534]
writeBits(0, 1); // X bit [cite: 416]
digitalWrite(LCD_CS, HIGH);
}
void writeAddr(uint8_t addr, uint8_t data) {
digitalWrite(LCD_CS, LOW);
uint16_t header = (0x05 << 6) | (addr & 0x3F); // Mode 101 [cite: 475]
writeBits(header, 9);
writeBits(data & 0x0F, 4); // 4 bits of data [cite: 475]
digitalWrite(LCD_CS, HIGH);
}
void updateDisplay() {
// Clear all segments first
for (int i = 0; i < 32; i++) {
writeAddr(i, 0x00);
}
// Light up only the current bit
writeAddr(currentAddr, (1 << currentBit));
Serial.print(">>> CURRENT - Address: ");
Serial.print(currentAddr);
Serial.print(" | Bit (COM): ");
Serial.println(currentBit);
Serial.println("Enter 'n' for Next, 'p' for Prev:");
}
void setup() {
pinMode(LCD_DATA, OUTPUT);
pinMode(LCD_WR, OUTPUT);
pinMode(LCD_CS, OUTPUT);
digitalWrite(LCD_CS, HIGH); // Initialize serial interface [cite: 443]
Serial.begin(9600);
// while (!Serial);
Serial.println("--- TM1621 Interactive Mapper ---");
sendCmd(0x01); // SYS EN [cite: 534]
sendCmd(0x29); // BIAS 1/3, 4 COM [cite: 420, 544]
sendCmd(0x03); // LCD ON [cite: 534]
updateDisplay();
}
uint8_t shadowRAM[32] = {0};
void writeMappedSegment(int aab, bool state) {
int addr = aab / 10;
int bit = aab % 10;
if (state) shadowRAM[addr] |= (1 << bit);
else shadowRAM[addr] &= ~(1 << bit);
writeAddr(addr, shadowRAM[addr]);
}
void displayDigit(const int segments[], int number) {
uint8_t bits = digitMap[number % 10];
for (int i = 0; i < 7; i++) {
// We check Bit 0, then Bit 1, etc.
// This maps i=0 to Segment A, i=1 to Segment B...
bool state = (bits >> i) & 0x01;
writeMappedSegment(segments[i], state);
}
}
void printToRow(int row, long value, int decimalPos = 0) {
const int** currentRow;
int numDigits;
// Select row configuration
switch(row) {
case 1: currentRow = digitsRow1; numDigits = 5; break;
case 2: currentRow = digitsRow2; numDigits = 5; break;
case 3: currentRow = digitsRow3; numDigits = 6; break;
default: return;
}
// Display the number right-aligned
long tempValue = value;
for (int i = numDigits - 1; i >= 0; i--) {
if (tempValue > 0 || i == numDigits - 1) { // Show at least one digit
displayDigit(currentRow[i], tempValue % 10);
tempValue /= 10;
} else {
// Clear leading zeros (all segments off)
for (int s = 0; s < 7; s++) writeMappedSegment(currentRow[i][s], false);
}
}
// Handle Decimal Points (if applicable for that row)
if (decimalPos > 0 && decimalPos <= 3) {
writeMappedSegment(decimals[row-1][decimalPos-1], true);
}
}
int counter1 = 0;
int counter2 = 0;
int counter3 = 0;
void loop() {
printToRow(1, counter1);
printToRow(2, counter2);
printToRow(3, counter3);
counter1 = (counter1 + 1) % 1000;
counter2 = (counter2 + 2) % 1000;
counter3 = (counter3 + 3) % 1000;
delay(100);
}
// void loop() {
// writeAddr(0, 0x00);
// writeAddr(1, 0x00);
// writeAddr(2, 0x00);
// writeAddr(3, 0x00);
// displayDigit(row1_d1, counter);
// Serial.println(counter);
// counter++;
// if (counter > 9) {
// counter = 0;
// }
// delay(1000);
// }
// void loop() {
// if (Serial.available() > 0) {
// char input = Serial.read();
// // Ignore newline/carriage return characters
// if (input == '\n' || input == '\r') return;
// if (input == 'n' || input == 'N') {
// currentBit++;
// if (currentBit > 3) {
// currentBit = 0;
// currentAddr++;
// }
// if (currentAddr > 31) currentAddr = 0;
// updateDisplay();
// }
// else if (input == 'p' || input == 'P') {
// currentBit--;
// if (currentBit < 0) {
// currentBit = 3;
// currentAddr--;
// }
// if (currentAddr < 0) currentAddr = 31;
// updateDisplay();
// }
// }
// }

View File

@@ -0,0 +1,11 @@
This directory is intended for PlatformIO Test Runner and project tests.
Unit Testing is a software testing method by which individual units of
source code, sets of one or more MCU program modules together with associated
control data, usage procedures, and operating procedures, are tested to
determine whether they are fit for use. Unit testing finds problems early
in the development cycle.
More information about PlatformIO Unit Testing:
- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html

Binary file not shown.

View File

@@ -0,0 +1,164 @@
import os
import json
import requests
import barcode
import urllib3
import re
from barcode.writer import ImageWriter
from PIL import Image, ImageDraw, ImageFont
from io import BytesIO
# --- SETTINGS ---
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
JSON_FILE = os.path.join(os.getcwd(), 'curated_list.json')
CARD_DIR = os.path.join(os.getcwd(), 'keychain_cards')
IMG_CACHE_DIR = os.path.join(os.getcwd(), 'image_cache')
OUTPUT_PDF = os.path.join(os.getcwd(), 'keychain_3x3_perfect.pdf')
# A4 at 300 DPI
PAGE_W, PAGE_H = 2480, 3508
COLS, ROWS = 3, 3
PAGE_MARGIN = 150
# Ensure directories exist
for d in [CARD_DIR, IMG_CACHE_DIR]:
os.makedirs(d, exist_ok=True)
def clean_filename(name):
return re.sub(r'[\\/*?:"<>|]', "_", name)
def get_ean_from_plu(plu):
return f"000000{str(plu).zfill(4)}00"
def get_cached_image(url, plu):
cache_path = os.path.join(IMG_CACHE_DIR, f"{plu}.jpg")
# Si el archivo ya existe en el cache, lo usamos sin importar la URL
if os.path.exists(cache_path):
return cache_path
# Si no existe y la URL es un placeholder, no podemos descargar nada
if url == "URL_PLACEHOLDER":
print(f"⚠️ {plu} tiene placeholder y no se encontró en {IMG_CACHE_DIR}")
return None
# Lógica de descarga original para URLs reales
try:
headers = {'User-Agent': 'Mozilla/5.0'}
res = requests.get(url, headers=headers, timeout=10, verify=False)
if res.status_code == 200:
with open(cache_path, 'wb') as f:
f.write(res.content)
return cache_path
except Exception as e:
print(f"❌ Error descargando {plu}: {e}")
return None
def generate_card(item):
name = item['name']
plu = item['plu']
img_url = item['image']
safe_name = clean_filename(name).replace(' ', '_')
final_path = os.path.join(CARD_DIR, f"PLU_{plu}_{safe_name}.png")
if os.path.exists(final_path):
return final_path
local_img_path = get_cached_image(img_url, plu)
card = Image.new('RGB', (300, 450), color='white')
draw = ImageDraw.Draw(card)
draw.rectangle([0, 0, 299, 449], outline="black", width=3)
if local_img_path:
try:
img = Image.open(local_img_path).convert("RGB")
w, h = img.size
size = min(w, h)
img = img.crop(((w-size)//2, (h-size)//2, (w+size)//2, (h+size)//2))
img = img.resize((200, 200), Image.Resampling.LANCZOS)
card.paste(img, (50, 40))
except:
draw.text((150, 140), "[IMG ERROR]", anchor="mm", fill="red")
else:
draw.text((150, 140), "[NOT FOUND]", anchor="mm", fill="red")
try:
f_name = ImageFont.truetype("arialbd.ttf", 22)
f_plu = ImageFont.truetype("arial.ttf", 18)
except:
f_name = f_plu = ImageFont.load_default()
draw.text((150, 260), name.upper(), fill="black", font=f_name, anchor="mm")
draw.text((150, 295), f"PLU: {plu}", fill="#333333", font=f_plu, anchor="mm")
EAN = barcode.get_barcode_class('ean13')
ean = EAN(get_ean_from_plu(plu), writer=ImageWriter())
tmp = f"tmp_{plu}"
ean.save(tmp, options={'module_height': 12.0, 'font_size': 10, 'text_distance': 4})
if os.path.exists(f"{tmp}.png"):
b_img = Image.open(f"{tmp}.png")
b_img = b_img.resize((280, 120))
card.paste(b_img, (10, 320))
os.remove(f"{tmp}.png")
card.save(final_path)
print(f" - Card created: {name} ({plu})")
return final_path
def create_pdf():
all_files = sorted([f for f in os.listdir(CARD_DIR) if f.endswith('.png')])
if not all_files:
print("❌ No cards found to put in PDF.")
return
available_w = PAGE_W - (PAGE_MARGIN * 2)
available_h = PAGE_H - (PAGE_MARGIN * 2)
slot_w, slot_h = available_w // COLS, available_h // ROWS
target_w = int(slot_w * 0.9)
target_h = int(target_w * (450 / 300))
if target_h > (slot_h * 0.9):
target_h = int(slot_h * 0.9)
target_w = int(target_h * (300 / 450))
pages = []
current_page = Image.new('RGB', (PAGE_W, PAGE_H), 'white')
print(f"📄 Organizing {len(all_files)} cards into {COLS}x{ROWS} grid...")
for i, filename in enumerate(all_files):
item_idx = i % (COLS * ROWS)
if item_idx == 0 and i > 0:
pages.append(current_page)
current_page = Image.new('RGB', (PAGE_W, PAGE_H), 'white')
row, col = item_idx // COLS, item_idx % COLS
img_path = os.path.join(CARD_DIR, filename)
card_img = Image.open(img_path).convert('RGB')
card_img = card_img.resize((target_w, target_h), Image.Resampling.LANCZOS)
x = PAGE_MARGIN + (col * slot_w) + (slot_w - target_w) // 2
y = PAGE_MARGIN + (row * slot_h) + (slot_h - target_h) // 2
current_page.paste(card_img, (x, y))
pages.append(current_page)
pages[0].save(OUTPUT_PDF, save_all=True, append_images=pages[1:], resolution=300.0, quality=100)
print(f"✅ Created {OUTPUT_PDF}. Now go print it and stop crying.")
if __name__ == "__main__":
if not os.path.exists(JSON_FILE):
print(f"❌ Missing {JSON_FILE}")
else:
with open(JSON_FILE, 'r', encoding='utf-8') as f:
data = json.load(f)
print(f"Step 1: Processing {len(data)} cards...")
for entry in data:
generate_card(entry)
print("\nStep 2: Generating PDF...")
create_pdf()

View File

@@ -0,0 +1,51 @@
import pandas as pd
import json
import os
file_path = os.path.join(os.getcwd(), 'PLU+FSMA+list+v1.0.xlsx')
sheet_name = 'Non FTL'
new_url_base = "https://server-ifps.accurateig.com/assets/commodities/"
def get_one_of_each():
if not os.path.exists(file_path):
print("❌ Excel file not found.")
return
# 1. Load Excel
df = pd.read_excel(file_path, sheet_name=sheet_name)
# 2. Drop rows missing the essentials
df = df.dropna(subset=['IMAGE', 'PLU', 'COMMODITY'])
# 3. CRITICAL: Drop duplicates by COMMODITY only
# This ignores Variety and Size, giving us exactly one row per fruit type.
df_unique = df.drop_duplicates(subset=['COMMODITY'], keep='first')
data_output = []
for _, row in df_unique.iterrows():
# Extract filename from the messy URL in Excel
original_link = str(row['IMAGE'])
filename = original_link.split('/')[-1]
# Build the final working URL
image_url = f"{new_url_base}{filename}"
# Get the clean Commodity name
commodity = str(row['COMMODITY']).title()
plu_code = str(row['PLU'])
data_output.append({
"name": commodity,
"plu": plu_code,
"image": image_url
})
# 4. Save to JSON
with open('one_of_each.json', 'w', encoding='utf-8') as f:
json.dump(data_output, f, indent=4, ensure_ascii=False)
print(f"✅ Success! Generated 'one_of_each.json' with {len(data_output)} unique commodities.")
if __name__ == "__main__":
get_one_of_each()

View File

@@ -0,0 +1,41 @@
import serial
import requests
import time
# --- CONFIGURATION ---
COM_PORT = 'COM5' # Change to /dev/ttyUSB0 on Linux
BAUD_RATE = 115200
# The IP of the PC running your Flask WebUI
SERVER_URL = "https://scanner.sekidesu.xyz/scan" # Change to your server's URL
def run_bridge():
try:
# Initialize serial connection
ser = serial.Serial(COM_PORT, BAUD_RATE, timeout=0.1)
print(f"Connected to {COM_PORT} at {BAUD_RATE} bauds.")
print("Ready to scan. Try not to break it.")
while True:
# Read line from scanner (most scanners send \r or \n at the end)
if ser.in_waiting > 0:
barcode = ser.readline().decode('utf-8').strip()
if barcode:
print(f"Scanned: {barcode}")
try:
# Send to your existing Flask server
# We use the same parameter 'content' so your server doesn't know the difference
resp = requests.get(SERVER_URL, params={'content': barcode})
print(f"Server responded: {resp.status_code}")
except Exception as e:
print(f"Failed to send to server: {e}")
time.sleep(0.01) # Don't melt your CPU
except serial.SerialException as e:
print(f"Error opening {COM_PORT}: {e}")
except KeyboardInterrupt:
print("\nBridge stopped by user. Quitter.")
if __name__ == "__main__":
run_bridge()

View File

@@ -0,0 +1,36 @@
import serial
import requests
import time
import argparse
import sys
def run_bridge():
parser = argparse.ArgumentParser(description="Scanner Bridge for the technically impaired")
parser.add_argument('--port', default='COM5', help='Serial port (default: COM5)')
parser.add_argument('--baud', type=int, default=115200, help='Baud rate (default: 115200)')
parser.add_argument('--url', default='https://scanner.sekidesu.xyz/scan', help='Server URL')
args = parser.parse_args()
try:
ser = serial.Serial(args.port, args.baud, timeout=0.1)
print(f"Connected to {args.port} at {args.baud} bauds.")
while True:
if ser.in_waiting > 0:
barcode = ser.readline().decode('utf-8', errors='ignore').strip()
if barcode:
print(f"Scanned: {barcode}")
try:
resp = requests.get(args.url, params={'content': barcode}, timeout=5)
print(f"Server responded: {resp.status_code}")
except Exception as e:
print(f"Failed to send to server: {e}")
time.sleep(0.01)
except serial.SerialException as e:
print(f"Error opening {args.port}: {e}")
except KeyboardInterrupt:
print("\nBridge stopped. Finally.")
if __name__ == "__main__":
run_bridge()

View File

@@ -0,0 +1,155 @@
import tkinter as tk
from tkinter import ttk
import threading
import requests
import usb.core
import usb.util
import usb.backend.libusb1
import os
import time
VENDOR_ID = 0xFFFF
PRODUCT_ID = 0x0035
HID_MAP = {
4: 'a', 5: 'b', 6: 'c', 7: 'd', 8: 'e', 9: 'f', 10: 'g', 11: 'h', 12: 'i',
13: 'j', 14: 'k', 15: 'l', 16: 'm', 17: 'n', 18: 'o', 19: 'p', 20: 'q',
21: 'r', 22: 's', 23: 't', 24: 'u', 25: 'v', 26: 'w', 27: 'x', 28: 'y', 29: 'z',
30: '1', 31: '2', 32: '3', 33: '4', 34: '5', 35: '6', 36: '7', 37: '8', 38: '9', 39: '0',
44: ' ', 45: '-', 46: '=', 55: '.', 56: '/'
}
class POSBridgeApp:
def __init__(self, root):
self.root = root
self.root.title("POS Hardware Bridge")
self.root.geometry("500x320")
self.running = True
# UI Setup
ttk.Label(root, text="Target POS Endpoint:").pack(pady=(15, 2))
self.url_var = tk.StringVar(value="https://scanner.sekidesu.xyz/scan")
self.url_entry = ttk.Entry(root, textvariable=self.url_var, width=60)
self.url_entry.pack(pady=5)
self.status_var = tk.StringVar(value="Status: Booting...")
self.status_label = ttk.Label(root, textvariable=self.status_var, font=("Segoe UI", 10, "bold"))
self.status_label.pack(pady=10)
ttk.Label(root, text="Activity Log:").pack()
self.log_listbox = tk.Listbox(root, width=70, height=8, bg="#1e1e1e", fg="#00ff00", font=("Consolas", 9))
self.log_listbox.pack(pady=5, padx=10)
# Bind the close button to kill threads cleanly
self.root.protocol("WM_DELETE_WINDOW", self.on_close)
# Fire up the USB listener in a background thread
self.usb_thread = threading.Thread(target=self.usb_listen_loop, daemon=True)
self.usb_thread.start()
def log(self, message):
# Tkinter requires GUI updates to happen on the main thread
self.root.after(0, self._append_log, message)
def _append_log(self, message):
self.log_listbox.insert(0, time.strftime("[%H:%M:%S] ") + message)
if self.log_listbox.size() > 15:
self.log_listbox.delete(15)
def update_status(self, text, color="black"):
self.root.after(0, self._set_status, text, color)
def _set_status(self, text, color):
self.status_var.set(f"Status: {text}")
self.status_label.config(foreground=color)
def on_close(self):
self.running = False
self.root.destroy()
def send_to_pos(self, barcode):
url = self.url_var.get()
self.log(f"Captured: {barcode}. Sending...")
try:
resp = requests.get(url, params={'content': barcode}, timeout=3)
self.log(f"Success: POS returned {resp.status_code}")
except requests.RequestException as e:
self.log(f"HTTP Error: Backend unreachable")
def usb_listen_loop(self):
import sys
# PyInstaller extracts files to a temp _MEIPASS folder at runtime
if getattr(sys, 'frozen', False):
base_path = sys._MEIPASS
else:
base_path = os.path.dirname(os.path.abspath(__file__))
dll_path = os.path.join(base_path, "libusb-1.0.dll")
if not os.path.exists(dll_path):
self.update_status(f"CRITICAL: DLL missing at {dll_path}", "red")
return
backend = usb.backend.libusb1.get_backend(find_library=lambda x: dll_path)
while self.running:
# Reconnect loop
dev = usb.core.find(idVendor=VENDOR_ID, idProduct=PRODUCT_ID, backend=backend)
if dev is None:
self.update_status("Scanner unplugged. Waiting...", "red")
time.sleep(2)
continue
try:
dev.set_configuration()
cfg = dev.get_active_configuration()
intf = cfg[(0,0)]
endpoint = usb.util.find_descriptor(
intf,
custom_match=lambda e: usb.util.endpoint_direction(e.bEndpointAddress) == usb.util.ENDPOINT_IN
)
self.update_status("Scanner Locked & Ready", "green")
current_barcode = ""
# Active reading loop
while self.running:
try:
data = dev.read(endpoint.bEndpointAddress, endpoint.wMaxPacketSize, timeout=1000)
keycode = data[2]
modifier = data[0]
is_shift = modifier == 2 or modifier == 32
if keycode == 0:
continue
if keycode == 40: # Enter key signifies end of scan
if current_barcode:
# Spawn a micro-thread for the HTTP request so we don't block the next scan
threading.Thread(target=self.send_to_pos, args=(current_barcode,), daemon=True).start()
current_barcode = ""
elif keycode in HID_MAP:
char = HID_MAP[keycode]
if is_shift and char.isalpha():
char = char.upper()
current_barcode += char
except usb.core.USBError as e:
# 10060/110 are normal timeouts when no barcode is being actively scanned
if e.args[0] in (10060, 110):
continue
else:
self.log(f"Hardware interrupt lost. Reconnecting...")
break # Breaks inner loop to trigger outer reconnect loop
except Exception as e:
self.log(f"USB Error: {e}")
time.sleep(2) # Prevent rapid crash loops
if __name__ == '__main__':
# You must run pip install requests if you haven't already
root = tk.Tk()
app = POSBridgeApp(root)
root.mainloop()

Binary file not shown.

View File

@@ -0,0 +1,25 @@
import usb.core
import usb.backend.libusb1
import os
# Grab the exact path to the DLL in your current folder
current_dir = os.path.dirname(os.path.abspath(__file__))
dll_path = os.path.join(current_dir, "libusb-1.0.dll")
if not os.path.exists(dll_path):
print(f"I don't see the DLL at: {dll_path}")
exit(1)
# Force pyusb to use this specific file
backend = usb.backend.libusb1.get_backend(find_library=lambda x: dll_path)
print("Scanning with forced local DLL backend...")
devices = usb.core.find(find_all=True, backend=backend)
found = False
for d in devices:
found = True
print(f"Found Device -> VID: {hex(d.idVendor)} PID: {hex(d.idProduct)}")
if not found:
print("Python is still blind. The DLL might be the wrong architecture (32-bit vs 64-bit).")

View File

@@ -0,0 +1,85 @@
import usb.core
import usb.util
import usb.backend.libusb1
import os
import sys
# Your exact scanner IDs
VENDOR_ID = 0xFFFF
PRODUCT_ID = 0x0035
# Basic HID to ASCII translation dictionary
HID_MAP = {
4: 'a', 5: 'b', 6: 'c', 7: 'd', 8: 'e', 9: 'f', 10: 'g', 11: 'h', 12: 'i',
13: 'j', 14: 'k', 15: 'l', 16: 'm', 17: 'n', 18: 'o', 19: 'p', 20: 'q',
21: 'r', 22: 's', 23: 't', 24: 'u', 25: 'v', 26: 'w', 27: 'x', 28: 'y', 29: 'z',
30: '1', 31: '2', 32: '3', 33: '4', 34: '5', 35: '6', 36: '7', 37: '8', 38: '9', 39: '0',
44: ' ', 45: '-', 46: '=', 55: '.', 56: '/'
}
def main():
# Force the local DLL backend
current_dir = os.path.dirname(os.path.abspath(__file__))
dll_path = os.path.join(current_dir, "libusb-1.0.dll")
if not os.path.exists(dll_path):
print(f"Error: Missing {dll_path}")
sys.exit(1)
backend = usb.backend.libusb1.get_backend(find_library=lambda x: dll_path)
# Find the scanner using the forced backend
dev = usb.core.find(idVendor=VENDOR_ID, idProduct=PRODUCT_ID, backend=backend)
if dev is None:
print("Scanner not found. Check Zadig driver again.")
sys.exit(1)
# Claim device
dev.set_configuration()
cfg = dev.get_active_configuration()
intf = cfg[(0,0)]
endpoint = usb.util.find_descriptor(
intf,
custom_match=lambda e: usb.util.endpoint_direction(e.bEndpointAddress) == usb.util.ENDPOINT_IN
)
print("Scanner locked. Waiting for barcodes...")
current_barcode = ""
while True:
try:
# Read 8 bytes from the scanner
data = dev.read(endpoint.bEndpointAddress, endpoint.wMaxPacketSize, timeout=1000)
keycode = data[2]
modifier = data[0]
is_shift = modifier == 2 or modifier == 32
if keycode == 0:
continue
if keycode == 40: # Enter key
print(f"Captured Barcode: {current_barcode}")
current_barcode = ""
elif keycode in HID_MAP:
char = HID_MAP[keycode]
if is_shift and char.isalpha():
char = char.upper()
current_barcode += char
except usb.core.USBError as e:
if e.args[0] == 10060 or e.args[0] == 110:
continue
else:
print(f"USB Error: {e}")
break
except KeyboardInterrupt:
print("\nExiting and releasing scanner...")
usb.util.dispose_resources(dev)
break
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,29 @@
import sqlite3
DB_FILE = 'db/pos_database.db'
def upgrade_db():
try:
with sqlite3.connect(DB_FILE) as conn:
<<<<<<< HEAD
=======
# Add stock column
# conn.execute("ALTER TABLE products ADD COLUMN stock REAL DEFAULT 0")
# print("Successfully added 'stock' column.")
# # App.py also expects unit_type, adding it to prevent future headaches
# conn.execute("ALTER TABLE products ADD COLUMN unit_type TEXT DEFAULT 'unit'")
# print("Successfully added 'unit_type' column.")
>>>>>>> 1a048a0e074ee26bd45dda9731c78c2ecef42fba
conn.execute("ALTER TABLE dicom ADD COLUMN image_url TEXT;")
print("Successfully added 'image_url' column.")
conn.commit()
print("Migration complete. Your data is intact.")
except sqlite3.OperationalError as e:
print(f"Skipped: {e}. (This usually means the columns already exist, so you're fine).")
if __name__ == '__main__':
upgrade_db()