cash2ecash

cash2ecash: cash acceptor that issues digital cash (experimental)
Log | Files | Refs | README | LICENSE

commit be3f1df99af1106c212e3f4548b3c178e1be34d4
parent 5cbe64eda6492526fe30f11aa7da18e2335fca13
Author: Tellenbach Reto <tellr1@bfh.ch>
Date:   Tue, 16 Jun 2026 22:24:25 +0200

[new] State-logic: AcceptCash state

Diffstat:
Msrc/taler-digitizer.c | 169+++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
Msrc/uart/ca_uart.c | 30+++++++++++++++++++++++-------
Msrc/uart/ca_uart.h | 5+++--
Mtaler-digitizer.conf | 2+-
4 files changed, 133 insertions(+), 73 deletions(-)

diff --git a/src/taler-digitizer.c b/src/taler-digitizer.c @@ -37,7 +37,9 @@ #include "digitizer_display.h" #include "gpio/gpiod_wrapper.h" #include "uart/ca_uart.h" - +#include <termios.h> +#include <sys/ioctl.h> +#include <stdio.h> /** * Time unit for PERSON_WITHDRAL_PERIOD config. @@ -169,8 +171,6 @@ static struct TALER_Amount cfg_ba_max_denomination; */ static struct TALER_Amount cfg_ba_min_denomination; - - /** * Handle to the context for http requests */ @@ -214,7 +214,7 @@ static struct gpiod_line_request *rl_ca_inhibit; /** * uart handle for Coin Acceptor signals */ -static int filestream_ca; +static struct GNUNET_NETWORK_Handle *filestream_ca; @@ -523,12 +523,76 @@ on_post_accounts_withdrawal_done(void *cls, return; } + +/** + * When UART signal from Coin Acceptor + * recived, or timeout of ACCEPT_CASH_TIMEOUT_SECONDS + */ +static void +on_coin_recived(void *cls) +{ + (void) cls; + struct TALER_Amount inserted; + ssize_t n; + uint8_t buff[32] = {0}; //only on byte read per accept cycle, but check if more than one was read + state = TALER_amount_is_zero(&state_ctx->digitizer_user_balance)? + DIGITIZER_STATE_CANCEL_WITHDRAWAL:DIGITIZER_STATE_CASHING_UP; + if (GNUNET_SCHEDULER_REASON_TIMEOUT == + GNUNET_SCHEDULER_get_task_context()->reason) + { + TALER_LOG_DEBUG ("Timeout of Accept Coin\n"); + GNUNET_SCHEDULER_add_now(state_controller_task,NULL); + return; + } + n = read (GNUNET_NETWORK_get_fd (filestream_ca), buff, sizeof (buff)); + TALER_LOG_DEBUG ("read n=%zd",n); + if(0 < n) + { + for(ssize_t i=0;i<n;i++) + { + TALER_LOG_DEBUG ("Coin byte received: 0x%02x (%d)\n", buff[i], buff[i]); + if(i>0) + TALER_LOG_WARNING("Multiple Coins inserted!\n"); + strcpy(inserted.currency,cfg_currency); + inserted.value = buff[i] / 10; + inserted.fraction = (buff[i] % 10) * (TALER_AMOUNT_FRAC_BASE/10); + if(0 > TALER_amount_add(&state_ctx->digitizer_user_balance, + &state_ctx->digitizer_user_balance, + &inserted)) + { + TALER_LOG_ERROR("adding amount failed\n"); + GNUNET_SCHEDULER_add_now(state_controller_task,NULL); + return; + } + } + state = DIGITIZER_STATE_COUNT_MONEY; + GNUNET_SCHEDULER_add_now(state_controller_task,NULL); + return; + } + else if(n == 0) + { + TALER_LOG_INFO("No Coin inserted in time\n"); + GNUNET_SCHEDULER_add_now(state_controller_task,NULL); + return; + } + else + { + perror ("read uart"); + TALER_LOG_INFO("Signal Error UART Coin Acceptor\n"); + GNUNET_SCHEDULER_add_now(state_controller_task,NULL); + return; + } +return; +} + + /** * Get withdrawal request for repeated requesting */ static void get_withdrawal_task(void *cls); + /** * callback of accounts withdrawal request * used in the QR Create state @@ -584,7 +648,7 @@ get_withdrawal_task(void *cls) * used in the ScanQR state */ static void -timeout_task(void *cls) +timeout_ScanQR_task(void *cls) { TALER_LOG_DEBUG ("Timeout of ScanQR\n"); @@ -605,7 +669,8 @@ shutdown_task (void *cls) { (void)cls; gpiod_line_request_release(rl_ca_inhibit); - close(filestream_ca); + GNUNET_NETWORK_socket_close (filestream_ca); + filestream_ca = NULL; if (NULL != get_withdrawal_handle) { @@ -1019,9 +1084,17 @@ static void Init_state_task(void *cls) global_ret = EXIT_FAILURE; return; } - filestream_ca = ca_uart_init(cfg_ca_sig_device,10*ACCEPT_CASH_TIMEOUT_SECONDS); + int fd = ca_uart_init (cfg_ca_sig_device); + if (-1 == fd) + { + TALER_LOG_ERROR ("UART init failed\n"); + global_ret = EXIT_FAILURE; + return; + } + filestream_ca = GNUNET_NETWORK_socket_box_native (fd); } + state_ctx->min_insertion_amount = ((1 == TALER_amount_cmp(&cfg_ba_min_denomination, &cfg_ca_min_denomination))? @@ -1142,7 +1215,7 @@ static void ScanQR_state_task(void *cls) poll = GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MILLISECONDS, SCAN_QR_CHECK_STATUS_INTERVAL_MILISECONDS); timeout_handle = GNUNET_SCHEDULER_add_delayed(delay, - timeout_task, + timeout_ScanQR_task, NULL); start_qr_show(SCAN_QR_TIMEOUT_SECONDS); @@ -1167,22 +1240,28 @@ static void CountMoney_state_task(void *cls) if(0 != gpiod_line_request_set_value(rl_ca_inhibit, (unsigned int)cfg_ca_inhibit_pin, GPIOD_LINE_VALUE_INACTIVE)) + { + GNUNET_free(temp_amount); + if(TALER_amount_is_zero(&state_ctx->digitizer_user_balance)) { - GNUNET_free(temp_amount); - if(TALER_amount_is_zero(&state_ctx->digitizer_user_balance)) - { - TALER_LOG_ERROR("failed to set GPIO inactive"); - state = DIGITIZER_STATE_CANCEL_WITHDRAWAL; - GNUNET_SCHEDULER_add_now(state_controller_task,NULL); - } - else - { - TALER_LOG_ERROR("failed to set GPIO active"); - state = DIGITIZER_STATE_CASHING_UP; - GNUNET_SCHEDULER_add_now(state_controller_task,NULL); - } + TALER_LOG_ERROR("failed to set GPIO inactive"); + state = DIGITIZER_STATE_CANCEL_WITHDRAWAL; + GNUNET_SCHEDULER_add_now(state_controller_task,NULL); } - + else + { + TALER_LOG_ERROR("failed to set GPIO active"); + state = DIGITIZER_STATE_CASHING_UP; + GNUNET_SCHEDULER_add_now(state_controller_task,NULL); + } + } + if (1 != TALER_amount_cmp (&state_ctx->withdrawal_limit, + &state_ctx->digitizer_user_balance)) + { + state = DIGITIZER_STATE_CASHING_UP; + GNUNET_SCHEDULER_add_now (state_controller_task, NULL); + return; + } TALER_amount_subtract(temp_amount, &state_ctx->withdrawal_limit, &state_ctx->digitizer_user_balance); @@ -1204,6 +1283,7 @@ static void CountMoney_state_task(void *cls) state = DIGITIZER_STATE_CASHING_UP; GNUNET_SCHEDULER_add_now(state_controller_task,NULL); } + tcflush (GNUNET_NETWORK_get_fd (filestream_ca), TCIFLUSH); state = DIGITIZER_STATE_ACCEPT_CASH; GNUNET_SCHEDULER_add_now(state_controller_task,NULL); } @@ -1213,47 +1293,10 @@ static void AcceptCash_state_task(void *cls) { TALER_LOG_DEBUG("AcceptingCash state"); (void)cls; - struct TALER_Amount inserted; - ssize_t n; - uint8_t buff[32]; //only on byte read per accept cycle, but check if more than one was read - n = read(filestream_ca,buff,sizeof(buff)); - state = TALER_amount_is_zero(&state_ctx->digitizer_user_balance)? - DIGITIZER_STATE_CANCEL_WITHDRAWAL:DIGITIZER_STATE_CASHING_UP; - if(0 < n) - { - for(ssize_t i=0;i<n;i++) - { - if(i>0) - TALER_LOG_WARNING("Multiple Coins inserted!\n"); - strcpy(inserted.currency,cfg_currency); - inserted.value = buff[i] / 10; - inserted.fraction = buff[i] % 10 * TALER_AMOUNT_FRAC_BASE; - if(0 > TALER_amount_add(&state_ctx->digitizer_user_balance, - &state_ctx->digitizer_user_balance, - &inserted)) - { - TALER_LOG_ERROR("adding amount failed\n"); - GNUNET_SCHEDULER_add_now(state_controller_task,NULL); - return; - } - } - state = DIGITIZER_STATE_COUNT_MONEY; - GNUNET_SCHEDULER_add_now(state_controller_task,NULL); - return; - } - else if(n == 0) - { - TALER_LOG_INFO("No Coin inserted in time\n"); - GNUNET_SCHEDULER_add_now(state_controller_task,NULL); - return; - } - else - { - TALER_LOG_INFO("Signal Error UART Coin Acceptor\n"); - GNUNET_SCHEDULER_add_now(state_controller_task,NULL); - return; - } - return; + struct GNUNET_TIME_Relative delay; + delay = GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, + ACCEPT_CASH_TIMEOUT_SECONDS); + GNUNET_SCHEDULER_add_read_net(delay,filestream_ca,on_coin_recived,NULL); } /** diff --git a/src/uart/ca_uart.c b/src/uart/ca_uart.c @@ -28,20 +28,22 @@ #include <stdlib.h> #include <unistd.h> #include "ca_uart.h" +#include <stdio.h> int -ca_uart_init(const char *dev,const unsigned char vtime) +ca_uart_init(const char *dev) { int uart_filestream; uart_filestream = 0; // Config UART0 - uart_filestream = open(dev, O_RDONLY | O_NOCTTY); + uart_filestream = open(dev, O_RDONLY | O_NOCTTY | O_NDELAY); // O_RDONLY: read only // O_NOCTTY: The port never becomes the controlling terminal of the process // O_NDELAY: Use non-blocking I/O if (-1 == uart_filestream) { - return -1; + perror ("open uart"); + return -1; } struct termios options; if(0 != tcgetattr(uart_filestream, @@ -50,18 +52,27 @@ ca_uart_init(const char *dev,const unsigned char vtime) close(uart_filestream); return -1; } - if(0 != cfsetispeed(&options, B9600)) + cfmakeraw(&options); + if(0 != cfsetspeed(&options, B9600)) { close(uart_filestream); return -1; } options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // RAW input - options.c_cflag |= PARENB; // Parity bit enabled - options.c_iflag |= (INPCK | ISTRIP); // check and strip paryty bit + options.c_cflag &= ~CSTOPB; // Clear stop field, only one stop bit + //options.c_cflag |= PARENB; // Parity even bit enabled + options.c_cflag &= ~PARENB; /* disable parity */ + options.c_cflag &= ~PARODD; // clear Parity odd bit + //options.c_iflag |= (INPCK); // check and paryty bit + options.c_iflag &= ~(INPCK); + options.c_iflag |= IGNPAR; /* ignore parity errors */ + options.c_iflag &= ~ISTRIP; // parity bit is 9th bit therfor cleared this option + options.c_iflag |= (IGNBRK); // ignore BREAK condition input options.c_cflag |= CS8; // Size: 8 Bits + options.c_cflag &= ~CRTSCTS; // Disable RTS/CTS hardware flow control (most common) options.c_cflag |= (CREAD | CLOCAL); // These will ensure that your program does not become the 'owner' of the port subject to sporatic job control and hangup signals, and also that the serial interface driver will read incoming data bytes. options.c_iflag &= ~(IXON | IXOFF | IXANY); // disable software flow controll - options.c_cc[VTIME] = vtime; + options.c_cc[VTIME] = 0; options.c_cc[VMIN] = 0; //Flush buffers and apply setti @@ -70,6 +81,11 @@ ca_uart_init(const char *dev,const unsigned char vtime) close(uart_filestream); return -1; } + if(0 != tcflush (uart_filestream, TCIFLUSH)) + { + close(uart_filestream); + return -1; + } return uart_filestream; } diff --git a/src/uart/ca_uart.h b/src/uart/ca_uart.h @@ -21,12 +21,13 @@ #define CA_UART_H /** + * Configures UART for Coin Acceptor, + * sets speed, length, parity, readmode * @brief init UART port for Coin Acceptor signal * @param dev device file of uart - * @param vtime wait time for signal * @return -1 on error */ int -ca_uart_init(const char *dev,const unsigned char vtime); +ca_uart_init(const char *dev); #endif diff --git a/taler-digitizer.conf b/taler-digitizer.conf @@ -27,7 +27,7 @@ FRAMEBUFFER_BACKLIGHT = /sys/class/backlight/10-0045/brightness ENABLE = YES INHIBIT_DEVICE = /dev/gpiochip0 INHIBIT_PIN = 16 -SIGNAL_DEVICE = /dev/gpiochip0 +SIGNAL_DEVICE = /dev/serial0 MAX_DENOMINATION = 5 MIN_DENOMINATION = 0.1