This commit is contained in:
2026-03-17 16:06:53 -03:00
20 changed files with 495 additions and 57 deletions

View File

@@ -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.

View File

@@ -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
lib_archive = no

View File

@@ -0,0 +1,89 @@
#include <Arduino.h>
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
}

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

View File

@@ -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
}

View File

@@ -1,39 +0,0 @@
#include <Arduino.h>
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;
}

View File

@@ -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}`;

View File

@@ -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();