diff --git a/README.md b/README.md index de1c533..0f63819 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# SekiPOS v1.6 🍫🥤 +# SekiPOS v2.0 🍫🥤 A reactive POS inventory system for software engineers with a snack addiction. Features real-time UI updates, automatic product discovery via Open Food Facts, and local image caching. diff --git a/SekiScaleESP/.gitignore b/ScaleHardware/IOT_Scale/.gitignore similarity index 100% rename from SekiScaleESP/.gitignore rename to ScaleHardware/IOT_Scale/.gitignore diff --git a/SekiScaleESP/.vscode/extensions.json b/ScaleHardware/IOT_Scale/.vscode/extensions.json similarity index 100% rename from SekiScaleESP/.vscode/extensions.json rename to ScaleHardware/IOT_Scale/.vscode/extensions.json diff --git a/SekiScaleESP/include/README b/ScaleHardware/IOT_Scale/include/README similarity index 100% rename from SekiScaleESP/include/README rename to ScaleHardware/IOT_Scale/include/README diff --git a/SekiScaleESP/lib/README b/ScaleHardware/IOT_Scale/lib/README similarity index 100% rename from SekiScaleESP/lib/README rename to ScaleHardware/IOT_Scale/lib/README diff --git a/SekiScaleESP/platformio.ini b/ScaleHardware/IOT_Scale/platformio.ini similarity index 74% rename from SekiScaleESP/platformio.ini rename to ScaleHardware/IOT_Scale/platformio.ini index 5e22d5f..2bc502e 100644 --- a/SekiScaleESP/platformio.ini +++ b/ScaleHardware/IOT_Scale/platformio.ini @@ -8,9 +8,9 @@ ; Please visit documentation for the other options and examples ; https://docs.platformio.org/page/projectconf.html -[env:d1] -platform = espressif8266 -board = d1 +[env:waveshare_rp2040_zero] +platform = https://github.com/maxgerhardt/platform-raspberrypi.git +board = waveshare_rp2040_zero framework = arduino monitor_speed = 115200 -upload_port = /dev/ttyUSB1 \ No newline at end of file +lib_archive = no \ No newline at end of file diff --git a/ScaleHardware/IOT_Scale/src/main.cpp b/ScaleHardware/IOT_Scale/src/main.cpp new file mode 100644 index 0000000..0a31654 --- /dev/null +++ b/ScaleHardware/IOT_Scale/src/main.cpp @@ -0,0 +1,89 @@ +#include + +const int SCLK_PIN = 18; +const int DOUT_PIN = 19; +const int TARE_BUTTON_PIN = 4; + +long offset = 0; +float scaleFactor = 1.0; // Update this after your calibration test + +void setup() { + Serial.begin(115200); + + pinMode(SCLK_PIN, OUTPUT); + pinMode(DOUT_PIN, INPUT); + pinMode(TARE_BUTTON_PIN, INPUT_PULLUP); + + digitalWrite(SCLK_PIN, LOW); + + Serial.println("Initializing SD10819 ADC..."); + delay(500); // Wait for chip stabilization + calibrateZero(); +} + +long readADC() { + // 1. Wait for DRDY to go LOW + // Timeout added to prevent infinite loop if ADC is disconnected + unsigned long startWait = millis(); + while (digitalRead(DOUT_PIN) == HIGH) { + if (millis() - startWait > 500) return 0; + } + + long value = 0; + + // 2. Read 24 bits of data + for (int i = 0; i < 24; i++) { + digitalWrite(SCLK_PIN, HIGH); + delayMicroseconds(1); // pulse width t3 > 100ns + value = (value << 1) | digitalRead(DOUT_PIN); + digitalWrite(SCLK_PIN, LOW); + delayMicroseconds(1); + } + + // 3. Configuration Pulses: 27 pulses = Channel A, 128x Gain + for (int i = 0; i < 3; i++) { + digitalWrite(SCLK_PIN, HIGH); + delayMicroseconds(1); + digitalWrite(SCLK_PIN, LOW); + delayMicroseconds(1); + } + + // Handle 24-bit two's complement + if (value & 0x800000) { + value |= 0xFF000000; + } + return value; +} + +void calibrateZero() { + Serial.println("Taring... stay still."); + long sum = 0; + for(int i = 0; i < 20; i++) { + sum += readADC(); + delay(10); + } + offset = sum / 20; + Serial.print("New Offset: "); + Serial.println(offset); +} + +void loop() { + // Check Tare Button + if (digitalRead(TARE_BUTTON_PIN) == LOW) { + calibrateZero(); + while(digitalRead(TARE_BUTTON_PIN) == LOW); // Wait for release + } + + // Read and Filter + long raw = readADC(); + float weight = (float)(raw - offset) * scaleFactor; + + // Serial Output for Debugging / Plotting + Serial.print("Raw:"); + Serial.print(raw); + Serial.print("\tWeight:"); + Serial.print(weight, 2); + Serial.println("g"); + + delay(100); // 10Hz sampling rate +} \ No newline at end of file diff --git a/SekiScaleESP/test/README b/ScaleHardware/IOT_Scale/test/README similarity index 100% rename from SekiScaleESP/test/README rename to ScaleHardware/IOT_Scale/test/README diff --git a/ScaleHardware/display_driver/.gitignore b/ScaleHardware/display_driver/.gitignore new file mode 100644 index 0000000..89cc49c --- /dev/null +++ b/ScaleHardware/display_driver/.gitignore @@ -0,0 +1,5 @@ +.pio +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json +.vscode/ipch diff --git a/ScaleHardware/display_driver/.vscode/extensions.json b/ScaleHardware/display_driver/.vscode/extensions.json new file mode 100644 index 0000000..080e70d --- /dev/null +++ b/ScaleHardware/display_driver/.vscode/extensions.json @@ -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" + ] +} diff --git a/ScaleHardware/display_driver/include/README b/ScaleHardware/display_driver/include/README new file mode 100644 index 0000000..49819c0 --- /dev/null +++ b/ScaleHardware/display_driver/include/README @@ -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 diff --git a/ScaleHardware/display_driver/lib/README b/ScaleHardware/display_driver/lib/README new file mode 100644 index 0000000..9379397 --- /dev/null +++ b/ScaleHardware/display_driver/lib/README @@ -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 +#include + +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 diff --git a/ScaleHardware/display_driver/platformio.ini b/ScaleHardware/display_driver/platformio.ini new file mode 100644 index 0000000..88617bd --- /dev/null +++ b/ScaleHardware/display_driver/platformio.ini @@ -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 \ No newline at end of file diff --git a/ScaleHardware/display_driver/src/TM1621_Config.h b/ScaleHardware/display_driver/src/TM1621_Config.h new file mode 100644 index 0000000..d5b1245 --- /dev/null +++ b/ScaleHardware/display_driver/src/TM1621_Config.h @@ -0,0 +1,50 @@ +#ifndef TM1621_CONFIG_H +#define TM1621_CONFIG_H + +#include + +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 \ No newline at end of file diff --git a/ScaleHardware/display_driver/src/main.cpp b/ScaleHardware/display_driver/src/main.cpp new file mode 100644 index 0000000..7b2056b --- /dev/null +++ b/ScaleHardware/display_driver/src/main.cpp @@ -0,0 +1,180 @@ +#include +#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(); +// } +// } +// } \ No newline at end of file diff --git a/ScaleHardware/display_driver/test/README b/ScaleHardware/display_driver/test/README new file mode 100644 index 0000000..9b1e87b --- /dev/null +++ b/ScaleHardware/display_driver/test/README @@ -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 diff --git a/ScannerGO/main.go b/ScannerGO/main.go index e1998d6..615a70f 100644 --- a/ScannerGO/main.go +++ b/ScannerGO/main.go @@ -17,17 +17,19 @@ import ( ) type Config struct { - Port string `json:"port"` - URL string `json:"url"` - BaudRate int `json:"baud"` - Delimiter string `json:"delimiter"` + 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", + Port: "/dev/ttyACM0", + URL: "https://scanner.sekidesu.xyz/scan", + BaudRate: 115200, + Delimiter: "\n", + FlowControl: "none", } const configPath = "config.json" @@ -39,6 +41,7 @@ func main() { 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() @@ -46,6 +49,7 @@ func main() { cfg.URL = *endpoint cfg.BaudRate = *baudRate cfg.Delimiter = *delim + cfg.FlowControl = *flow if *save { saveConfig(cfg) @@ -55,9 +59,16 @@ func main() { serialConfig := &serial.Config{ Name: cfg.Port, Baud: cfg.BaudRate, - ReadTimeout: 0, + 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) @@ -65,11 +76,10 @@ func main() { } defer port.Close() - fmt.Printf("Listening on %s (Baud: %d, Delim: %q)...\n", cfg.Port, cfg.BaudRate, cfg.Delimiter) + fmt.Printf("Listening on %s (Baud: %d, Flow: %s)...\n", cfg.Port, cfg.BaudRate, cfg.FlowControl) scanner := bufio.NewScanner(port) - // Custom split function to handle the configurable delimiter scanner.Split(func(data []byte, atEOF bool) (advance int, token []byte, err error) { if atEOF && len(data) == 0 { return 0, nil, nil @@ -84,7 +94,11 @@ func main() { }) for scanner.Scan() { - content := strings.TrimSpace(scanner.Text()) + rawContent := scanner.Text() + content := strings.TrimFunc(rawContent, func(r rune) bool { + return r < 32 || r > 126 + }) + if content != "" { sendToEndpoint(cfg.URL, content) } @@ -112,10 +126,12 @@ func loadConfig() Config { if err := decoder.Decode(&cfg); err != nil { return defaultConfig } - // Handle case where JSON exists but field is missing if cfg.Delimiter == "" { cfg.Delimiter = "\n" } + if cfg.FlowControl == "" { + cfg.FlowControl = "none" + } return cfg } diff --git a/SekiScaleESP/src/main.cpp b/SekiScaleESP/src/main.cpp deleted file mode 100644 index fbd9447..0000000 --- a/SekiScaleESP/src/main.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include - -const int triggerPin = D2; -bool lastState = HIGH; - -void setup() { - Serial.begin(115200); - randomSeed(analogRead(0)); - - pinMode(LED_BUILTIN, OUTPUT); - pinMode(triggerPin, INPUT_PULLUP); - - // Flash LED to signal boot - digitalWrite(LED_BUILTIN, LOW); - delay(500); - digitalWrite(LED_BUILTIN, HIGH); -} - -void loop() { - bool currentState = digitalRead(triggerPin); - - // Detect when D4 touches GND (Falling Edge) - if (currentState == LOW && lastState == HIGH) { - int randomWeight = random(100, 5000); - - // Output formatted for easy parsing - Serial.print("WEIGHT:"); - Serial.println(randomWeight); - - // Visual feedback - digitalWrite(LED_BUILTIN, LOW); - delay(100); - digitalWrite(LED_BUILTIN, HIGH); - - delay(250); // Debounce - } - - lastState = currentState; -} \ No newline at end of file diff --git a/templates/checkout.html b/templates/checkout.html index 2c5d804..f09af38 100644 --- a/templates/checkout.html +++ b/templates/checkout.html @@ -397,6 +397,15 @@ handleProductScan(product); }); + socket.on('scale_update', function (data) { + const weightInput = document.getElementById('weight-input'); + const weightModal = document.getElementById('weightModal'); + + if (weightModal.classList.contains('show')) { + weightInput.value = data.grams; + } + }); + function goToInventory() { if (missingProductData) { window.location.href = `/?barcode=${missingProductData.barcode}`; diff --git a/templates/inventory.html b/templates/inventory.html index 0a0f708..fd7f8f2 100644 --- a/templates/inventory.html +++ b/templates/inventory.html @@ -287,6 +287,13 @@ permanentemente', 'executeBulkDelete()') %} } }); + socket.on('scale_update', function (data) { + const unitType = document.getElementById('form-unit-type').value; + if (unitType === 'kg') { + document.getElementById('form-stock').value = data.kilograms; + } + }); + // Replace your existing updateForm function with this one function updateForm(b, n, p, i, t, stock, unit) { dismissPrompt();