fallback VID PID + hex json + RE info, keypad WIP
This commit is contained in:
BIN
extensions/go/ScannerHID/icon.ico
Normal file
BIN
extensions/go/ScannerHID/icon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.9 KiB |
@@ -1,12 +1,15 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
_ "embed"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"image/color"
|
"image/color"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"fyne.io/fyne/v2"
|
"fyne.io/fyne/v2"
|
||||||
@@ -17,10 +20,35 @@ import (
|
|||||||
"github.com/google/gousb"
|
"github.com/google/gousb"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//go:embed icon.ico
|
||||||
|
var iconData []byte
|
||||||
|
|
||||||
|
type HexUint16 uint16
|
||||||
|
|
||||||
|
func (h HexUint16) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(fmt.Sprintf("0x%04X", h))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *HexUint16) UnmarshalJSON(data []byte) error {
|
||||||
|
var s string
|
||||||
|
if err := json.Unmarshal(data, &s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
s = strings.TrimPrefix(s, "0x")
|
||||||
|
val, err := strconv.ParseUint(s, 16, 16)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*h = HexUint16(val)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
TargetURL string `json:"target_url"`
|
TargetURL string `json:"target_url"`
|
||||||
VendorID uint16 `json:"vendor_id"`
|
VendorID HexUint16 `json:"vendor_id"`
|
||||||
ProductID uint16 `json:"product_id"`
|
ProductID HexUint16 `json:"product_id"`
|
||||||
|
FallbackVID HexUint16 `json:"fallback_vendor_id"`
|
||||||
|
FallbackPID HexUint16 `json:"fallback_product_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var hidMap = map[byte]string{
|
var hidMap = map[byte]string{
|
||||||
@@ -49,15 +77,16 @@ func (b *BridgeApp) saveConfig() {
|
|||||||
|
|
||||||
func loadConfig() Config {
|
func loadConfig() Config {
|
||||||
conf := Config{
|
conf := Config{
|
||||||
TargetURL: "https://scanner.sekidesu.xyz/scan",
|
TargetURL: "https://scanner.sekidesu.xyz/scan",
|
||||||
VendorID: 0xFFFF,
|
VendorID: 0xFFFF,
|
||||||
ProductID: 0x0035,
|
ProductID: 0x0035,
|
||||||
|
FallbackVID: 0x04B3,
|
||||||
|
FallbackPID: 0x3107,
|
||||||
}
|
}
|
||||||
file, err := os.ReadFile("config.json")
|
file, err := os.ReadFile("config.json")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
json.Unmarshal(file, &conf)
|
json.Unmarshal(file, &conf)
|
||||||
} else {
|
} else {
|
||||||
// Create default config if missing
|
|
||||||
data, _ := json.MarshalIndent(conf, "", " ")
|
data, _ := json.MarshalIndent(conf, "", " ")
|
||||||
os.WriteFile("config.json", data, 0644)
|
os.WriteFile("config.json", data, 0644)
|
||||||
}
|
}
|
||||||
@@ -82,6 +111,8 @@ func main() {
|
|||||||
|
|
||||||
a := app.New()
|
a := app.New()
|
||||||
w := a.NewWindow("POS Hardware Bridge (Go)")
|
w := a.NewWindow("POS Hardware Bridge (Go)")
|
||||||
|
w.SetIcon(fyne.NewStaticResource("icon.ico", iconData))
|
||||||
|
|
||||||
bridge.window = w
|
bridge.window = w
|
||||||
bridge.urlEntry = widget.NewEntry()
|
bridge.urlEntry = widget.NewEntry()
|
||||||
bridge.urlEntry.SetText(conf.TargetURL)
|
bridge.urlEntry.SetText(conf.TargetURL)
|
||||||
@@ -90,18 +121,18 @@ func main() {
|
|||||||
|
|
||||||
bridge.logList = widget.NewList(
|
bridge.logList = widget.NewList(
|
||||||
func() int { return len(bridge.logs) },
|
func() int { return len(bridge.logs) },
|
||||||
func() fyne.CanvasObject { return widget.NewLabel("template") },
|
func() fyne.CanvasObject { return widget.NewLabel("template") },
|
||||||
func(i widget.ListItemID, o fyne.CanvasObject) {
|
func(i widget.ListItemID, o fyne.CanvasObject) {
|
||||||
o.(*widget.Label).SetText(bridge.logs[i])
|
o.(*widget.Label).SetText(bridge.logs[i])
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
content := container.NewBorder(
|
content := container.NewBorder(
|
||||||
container.NewVBox(
|
container.NewVBox(
|
||||||
widget.NewLabel("Target POS Endpoint:"),
|
widget.NewLabel("Target POS Endpoint:"),
|
||||||
bridge.urlEntry,
|
bridge.urlEntry,
|
||||||
bridge.status,
|
bridge.status,
|
||||||
widget.NewLabel("Activity Log:"),
|
widget.NewLabel("Activity Log:"),
|
||||||
),
|
),
|
||||||
nil, nil, nil,
|
nil, nil, nil,
|
||||||
bridge.logList,
|
bridge.logList,
|
||||||
@@ -129,18 +160,18 @@ func (b *BridgeApp) addLog(msg string) {
|
|||||||
ts := time.Now().Format("15:04:05")
|
ts := time.Now().Format("15:04:05")
|
||||||
formatted := fmt.Sprintf("[%s] %s", ts, msg)
|
formatted := fmt.Sprintf("[%s] %s", ts, msg)
|
||||||
|
|
||||||
if b.isCLI {
|
if b.isCLI {
|
||||||
fmt.Println(formatted)
|
fmt.Println(formatted)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
fyne.DoAndWait(func() {
|
fyne.DoAndWait(func() {
|
||||||
b.logs = append([]string{formatted}, b.logs...)
|
b.logs = append([]string{formatted}, b.logs...)
|
||||||
if len(b.logs) > 15 {
|
if len(b.logs) > 15 {
|
||||||
b.logs = b.logs[:15]
|
b.logs = b.logs[:15]
|
||||||
}
|
}
|
||||||
b.logList.Refresh()
|
b.logList.Refresh()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *BridgeApp) updateStatus(msg string, col color.Color) {
|
func (b *BridgeApp) updateStatus(msg string, col color.Color) {
|
||||||
@@ -179,13 +210,17 @@ func (b *BridgeApp) usbListenLoop() {
|
|||||||
for {
|
for {
|
||||||
dev, err := ctx.OpenDeviceWithVIDPID(gousb.ID(b.config.VendorID), gousb.ID(b.config.ProductID))
|
dev, err := ctx.OpenDeviceWithVIDPID(gousb.ID(b.config.VendorID), gousb.ID(b.config.ProductID))
|
||||||
|
|
||||||
|
if (err != nil || dev == nil) && (b.config.FallbackVID != 0) {
|
||||||
|
dev, err = ctx.OpenDeviceWithVIDPID(gousb.ID(b.config.FallbackVID), gousb.ID(b.config.FallbackPID))
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil || dev == nil {
|
if err != nil || dev == nil {
|
||||||
b.updateStatus("Scanner unplugged. Waiting...", color.NRGBA{200, 0, 0, 255})
|
b.updateStatus("Scanner unplugged. Waiting...", color.NRGBA{200, 0, 0, 255})
|
||||||
time.Sleep(2 * time.Second)
|
time.Sleep(2 * time.Second)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
b.updateStatus("Scanner Locked & Ready", color.NRGBA{0, 180, 0, 255})
|
b.updateStatus(fmt.Sprintf("Scanner Ready (0x%04X)", dev.Desc.Vendor), color.NRGBA{0, 180, 0, 255})
|
||||||
|
|
||||||
intf, done, err := dev.DefaultInterface()
|
intf, done, err := dev.DefaultInterface()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
BIN
extensions/platformio/DISPLAY_SEGMENTS.xcf
Normal file
BIN
extensions/platformio/DISPLAY_SEGMENTS.xcf
Normal file
Binary file not shown.
BIN
extensions/platformio/WIRING.xcf
Normal file
BIN
extensions/platformio/WIRING.xcf
Normal file
Binary file not shown.
@@ -1,48 +1,76 @@
|
|||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
|
||||||
const int BUZZER_PIN = 6;
|
const int BUZZER_PIN = 6;
|
||||||
const int allPins[] = {0, 1, 2, 3, 4, 5, 7};
|
const int pins[] = {0, 1, 2, 3, 4, 5, 7};
|
||||||
const int numPins = 7;
|
const int numPins = 7;
|
||||||
|
|
||||||
unsigned long lastDebounceTime = 0;
|
struct KeyMap {
|
||||||
const int debounceDelay = 250;
|
int strobe;
|
||||||
|
int target;
|
||||||
|
char key;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Your discovered mapping
|
||||||
|
KeyMap keypad[] = {{7, 1, '1'}, {7, 2, '2'}, {3, 7, '3'}, {5, 0, 'C'},
|
||||||
|
{7, 0, 'T'}, {4, 1, 'm'}, {1, 2, 'Y'}, {5, 3, '6'},
|
||||||
|
{1, 0, '+'}, {2, 0, 'Z'}, {1, 5, '7'}, {5, 2, 'U'},
|
||||||
|
{2, 5, '8'}, {3, 5, '9'}, {2, 4, 'M'}, {3, 1, '4'},
|
||||||
|
{0, 2, '5'}, {3, 4, 'P'}, {4, 0, '$'}, {0, 4, 'L'}};
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
Serial.begin(115200);
|
Serial.begin(115200);
|
||||||
for (int i = 0; i < numPins; i++) {
|
for (int p : pins) {
|
||||||
pinMode(allPins[i], INPUT_PULLUP);
|
pinMode(p, INPUT_PULLUP);
|
||||||
}
|
}
|
||||||
|
// Keep buzzer pin high-impedance so it doesn't sink the matrix
|
||||||
|
pinMode(BUZZER_PIN, INPUT_PULLUP);
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void playBeep() {
|
||||||
// Only scan if we aren't in the middle of a "hit" lockout
|
analogWrite(BUZZER_PIN, 10);
|
||||||
if (millis() - lastDebounceTime < debounceDelay) return;
|
|
||||||
|
delay(50); // Beep duration
|
||||||
|
|
||||||
|
analogWrite(BUZZER_PIN, 0); // Turn it off
|
||||||
|
pinMode(BUZZER_PIN, INPUT_PULLUP); // Reset to input for the matrix
|
||||||
|
}
|
||||||
|
|
||||||
|
char getKeyPressed() {
|
||||||
for (int s = 0; s < numPins; s++) {
|
for (int s = 0; s < numPins; s++) {
|
||||||
int strobe = allPins[s];
|
int strobe = pins[s];
|
||||||
|
|
||||||
pinMode(strobe, OUTPUT);
|
pinMode(strobe, OUTPUT);
|
||||||
digitalWrite(strobe, LOW);
|
digitalWrite(strobe, LOW);
|
||||||
delayMicroseconds(100); // Slightly longer for stability
|
delayMicroseconds(50);
|
||||||
|
|
||||||
for (int i = 0; i < numPins; i++) {
|
for (int i = 0; i < numPins; i++) {
|
||||||
int target = allPins[i];
|
int target = pins[i];
|
||||||
if (target == strobe) continue;
|
if (target == strobe)
|
||||||
|
continue;
|
||||||
|
|
||||||
if (digitalRead(target) == LOW) {
|
if (digitalRead(target) == LOW) {
|
||||||
// WE FOUND ONE!
|
for (int k = 0; k < 20; k++) {
|
||||||
Serial.print("SINK:");
|
if (keypad[k].strobe == strobe && keypad[k].target == target) {
|
||||||
Serial.print(strobe);
|
|
||||||
Serial.print(" READ:");
|
|
||||||
Serial.println(target);
|
|
||||||
|
|
||||||
//tone(BUZZER_PIN, 2000, 50);
|
// Visual and Audio feedback
|
||||||
lastDebounceTime = millis();
|
Serial.print("Pressed: ");
|
||||||
|
Serial.println(keypad[k].key);
|
||||||
pinMode(strobe, INPUT_PULLUP);
|
playBeep();
|
||||||
return; // Jump out of the whole loop to reset the lockout
|
|
||||||
|
while (digitalRead(target) == LOW)
|
||||||
|
; // Wait for release
|
||||||
|
pinMode(strobe, INPUT_PULLUP);
|
||||||
|
return keypad[k].key;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pinMode(strobe, INPUT_PULLUP);
|
pinMode(strobe, INPUT_PULLUP);
|
||||||
}
|
}
|
||||||
}
|
return '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
getKeyPressed();
|
||||||
|
delay(10);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user