cash2ecash

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

commit 46069084f068a73be72b9c68d9b7ad33b03675e8
parent ff3c47bbe8768d4f63dc431c5abd3af80cf8138c
Author: Tellenbach Reto <tellr1@bfh.ch>
Date:   Mon,  8 Jun 2026 18:13:00 +0200

[new] State-logic: CountMoney state

Diffstat:
Msrc/CMakeLists.txt | 3++-
Asrc/gpio/CMakeLists.txt | 7+++++++
Asrc/gpio/gpiod_wrapper.c | 166+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/gpio/gpiod_wrapper.h | 76++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/taler-digitizer.c | 128+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Mtaler-digitizer.conf | 2+-
6 files changed, 377 insertions(+), 5 deletions(-)

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt @@ -3,8 +3,9 @@ find_package(PkgConfig REQUIRED) add_subdirectory(lib) +add_subdirectory(gpio) # Manage libs target_link_libraries(taler-digitizer - PRIVATE bank + PRIVATE bank gpio ) \ No newline at end of file diff --git a/src/gpio/CMakeLists.txt b/src/gpio/CMakeLists.txt @@ -0,0 +1,7 @@ +# Librarys +## Defining Librarys + +add_library(gpio gpiod_wrapper.c) + +## Linking Targets +target_link_libraries(gpio PUBLIC gpiod.a) diff --git a/src/gpio/gpiod_wrapper.c b/src/gpio/gpiod_wrapper.c @@ -0,0 +1,165 @@ +/* + This file is part of TALER cash2ecash + Copyright (C) 2026 GNUnet e.V. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. +*/ + +#include "gpiod_wrapper.h" + +struct gpiod_chip *gpiod_chip_open_by_name(const char *name) +{ + struct gpiod_chip *chip; + char *path; + int ret; + + ret = asprintf(&path, "/dev/%s", name); + if (ret < 0) + { + perror("asprint() memory-allocation failed"); + return NULL; + } + + + chip = gpiod_chip_open(path); + free(path); + + return chip; +} + +struct gpiod_line_settings* gpiod_make_settings(enum gpiod_line_direction direction,enum gpiod_line_bias bias, enum gpiod_line_drive drive, enum gpiod_line_drive active_low) +{ + int ret = 0; + struct gpiod_line_settings* settings = gpiod_line_settings_new(); + if (!settings) + { + perror("gpiod_line_settings_new() failed"); + return NULL; + } + + ret |= gpiod_line_settings_set_direction(settings, direction); + ret |= gpiod_line_settings_set_bias(settings, bias); + ret |= gpiod_line_settings_set_drive(settings, drive); + gpiod_line_settings_set_active_low(settings, active_low); + if (ret) + { + perror("gpiod_line_settings_set....() failed"); + return NULL; + } + + + return settings; +} + +struct gpiod_line_request* gpiod_make_line_request_by_name(const char* chipname, const char* linename, struct gpiod_line_settings* settings) +{ + struct gpiod_request_config* request_cfg = NULL; //for kernel settings, not used here + struct gpiod_line_request* request = NULL; + struct gpiod_line_config* line_cfg; + struct gpiod_chip* chip; + int ret = 0; + + + chip = gpiod_chip_open_by_name(chipname); + if (!chip) + { + perror("gpiod_chip_open_by_name() failed"); + goto free_settings; + } + + + const int line_offset = gpiod_chip_get_line_offset_from_name(chip, linename); + if(line_offset<0) + { + perror("gpiod_chip_get_line_offset_from_name() failed"); + goto close_chip; + } + + const unsigned int u_line_offset = (unsigned int)line_offset; + + line_cfg = gpiod_line_config_new(); + if (!line_cfg) + { + perror("gpiod_line_config_new() failed"); + goto close_chip; + } + + + ret = gpiod_line_config_add_line_settings(line_cfg, &u_line_offset, 1, settings); + if (ret) + { + perror("gpiod_line_config_add_line_settings() failed"); + goto free_line_cfg; + } + + + request = gpiod_chip_request_lines(chip, request_cfg, line_cfg); + +free_line_cfg: + gpiod_line_config_free(line_cfg); + +close_chip: + gpiod_chip_close(chip); + +free_settings: + gpiod_line_settings_free(settings); + + return request; //NULL on error +} + +struct gpiod_line_request* gpiod_make_line_request(const char* chip_path, const unsigned int line_offset, struct gpiod_line_settings* settings) +{ + struct gpiod_line_request* request = NULL; + struct gpiod_line_config* line_cfg; + struct gpiod_chip* chip; + int ret = 0; + + chip = gpiod_chip_open(chip_path); + if (!chip) + { + perror("gpiod_chip_open_by_name() failed"); + goto free_settings; + } + + + line_cfg = gpiod_line_config_new(); + if (!line_cfg) + { + perror("gpiod_line_config_new() failed"); + goto close_chip; + } + + + ret = gpiod_line_config_add_line_settings(line_cfg, &line_offset, 1, settings); + if (ret) + { + perror("gpiod_line_config_add_line_settings() failed"); + goto free_line_cfg; + } + + + request = gpiod_chip_request_lines(chip, NULL, line_cfg); + +free_line_cfg: + gpiod_line_config_free(line_cfg); + +close_chip: + gpiod_chip_close(chip); + +free_settings: + gpiod_line_settings_free(settings); + + return request; //NULL on error +} +\ No newline at end of file diff --git a/src/gpio/gpiod_wrapper.h b/src/gpio/gpiod_wrapper.h @@ -0,0 +1,75 @@ +/* + This file is part of TALER cash2ecash + Copyright (C) 2026 GNUnet e.V. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. +*/ + +#ifndef GPIOD_WRAPPER_H +#define GPIOD_WRAPPER_H + +/* Use GPIO to enable coin insert*/ +//heavily inspired from gpiod source example of Gent Gibson toggle_line_value.c + +#include <gpiod.h> +#include <stdlib.h> +#include <stdio.h> + +/** + * @param name + * @return + */ +struct gpiod_chip * +gpiod_chip_open_by_name(const char *name); + +/** + * Init settings for line-request + * @param direction + * @param bias + * @param drive + * @param active_los + * @return NULL on errors + */ +struct gpiod_line_settings* +gpiod_make_settings(enum gpiod_line_direction direction, + enum gpiod_line_bias bias, + enum gpiod_line_drive drive, + enum gpiod_line_drive active_low); + +/** + * + * @param chipname + * @param linename + * @param settings + * @return must release line request with gpiod_line_request_release() + */ +struct gpiod_line_request* +gpiod_make_line_request_by_name(const char* chipname, + const char* linename, + struct gpiod_line_settings* settings); + +/** + * + * @param chip_path + * @param line_offset + * @param settings + * @return + */ +struct gpiod_line_request* +gpiod_make_line_request(const char* chip_path, + const unsigned int line_offset, + struct gpiod_line_settings* settings); + +#endif +\ No newline at end of file diff --git a/src/taler-digitizer.c b/src/taler-digitizer.c @@ -34,6 +34,7 @@ #include "lib/bank_api_get_withdrawals.h" #include "lib/bank_api_post_accounts_withdrawals_confirm.h" #include "digitizer_display.h" +#include "gpio/gpiod_wrapper.h" /** @@ -192,6 +193,17 @@ static struct TALER_BANK_PostCreateWithdrawalHandle *post_accounts_withdrawal_ha */ static FILE *fp; +/** + * used to init gpio + */ +static struct gpiod_line_settings *gpio_settings; + +/** + * gpio request to enable coin acceping + */ +static struct gpiod_line_request *rl_ca_enable; + + enum DIGITIZER_states { @@ -285,13 +297,18 @@ struct DIGITIZER_StateContext * is updated according to bank balance and bank withdrawal-limits * in init state of each withdrawal process */ - struct TALER_Amount withdrawal_balance; + struct TALER_Amount withdrawal_limit; /** * withdrawal operation information * url and id */ struct TALER_BANK_CreateWithdrawalInformatio wi; + + /** + * amount of current prozessing withdrawal + */ + struct TALER_Amount digitizer_user_balance; }; static enum DIGITIZER_states state; @@ -467,10 +484,11 @@ on_get_accounts_done(void *cls, GNUNET_SCHEDULER_add_now(state_controller_task,NULL); return; } - state_ctx->withdrawal_balance = + state_ctx->withdrawal_limit = ((-1 == TALER_amount_cmp(&account_max_withdrawal, &state_ctx->bank_max_wire_transfer_amount))? account_max_withdrawal : state_ctx->bank_max_wire_transfer_amount); + TALER_amount_min(&state_ctx->withdrawal_limit,&state_ctx->withdrawal_limit,&cfg_user_withdrawallimit), @@ -576,6 +594,7 @@ shutdown_task (void *cls) (void)cls; GNUNET_free(state_ctx); GNUNET_free(display_ctx); + gpiod_line_request_release(rl_ca_enable); if (NULL != get_config_handle) { @@ -953,6 +972,37 @@ static void Init_state_task(void *cls) global_ret = EXIT_FAILURE; return; } + //CA Init + gpio_settings = + gpiod_make_settings(GPIOD_LINE_DIRECTION_OUTPUT, + GPIOD_LINE_BIAS_PULL_UP, + GPIOD_LINE_DRIVE_OPEN_DRAIN,1); + if (NULL == gpio_settings) + { + TALER_LOG_ERROR("GPIO Init failed"); + gpiod_line_settings_free(gpio_settings); + global_ret = EXIT_FAILURE; + return; + } + rl_ca_enable = + gpiod_make_line_request(cfg_ca_device, + cfg_ca_en_pin, + gpio_settings); + if (NULL == rl_ca_enable) + { + TALER_LOG_ERROR("GPIO Init failed"); + gpiod_line_request_release(rl_ca_enable); + global_ret = EXIT_FAILURE; + return; + } + if(0 != gpiod_line_request_set_value(rl_ca_enable, + (unsigned int)cfg_ca_en_pin, + GPIOD_LINE_VALUE_INACTIVE)) + { + TALER_LOG_ERROR("failed to set GPIO active"); + global_ret = EXIT_FAILURE; + return; + } } state_ctx->min_insertion_amount = @@ -978,7 +1028,6 @@ static void Init_state_task(void *cls) //BA Init - //CA Init curl_ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, &reschedule_ctx); @@ -1014,6 +1063,7 @@ static void Idle_state_task(void *cls) { TALER_LOG_DEBUG("Idle state\n"); (void)cls; + TALER_amount_set_zero(cfg_currency,&state_ctx->digitizer_user_balance); get_accounts_handle = TALER_BANK_get_accounts (curl_ctx, cfg_bank_base_url, cfg_bank_account_username, @@ -1086,6 +1136,68 @@ static void ScanQR_state_task(void *cls) return; } +static void CountMoney_state_task(void *cls) +{ + char* balance_str; + struct TALER_Amount *temp_amount; + temp_amount = GNUNET_new(struct TALER_Amount); + balance_str = TALER_amount_to_string(&state_ctx->digitizer_user_balance); + TALER_LOG_DEBUG ("CountMoney: %s\n",balance_str); + GNUNET_free(balance_str); + (void)cls; + if(0 != gpiod_line_request_set_value(rl_ca_enable, + (unsigned int)cfg_ca_en_pin, + GPIOD_LINE_VALUE_INACTIVE)) + { + 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_amount_subtract(temp_amount, + &state_ctx->withdrawal_limit, + &state_ctx->digitizer_user_balance); + if(-1 == TALER_amount_cmp(temp_amount, + &state_ctx->max_insertion_amount)) + { + GNUNET_free(temp_amount); + TALER_LOG_INFO("withdrawal limit reached"); + state = DIGITIZER_STATE_CASHING_UP; + GNUNET_SCHEDULER_add_now(state_controller_task,NULL); + }else + { + GNUNET_free(temp_amount); + if(0 != gpiod_line_request_set_value(rl_ca_enable, + (unsigned int)cfg_ca_en_pin, + GPIOD_LINE_VALUE_ACTIVE)) + { + TALER_LOG_ERROR("failed to set GPIO active"); + state = DIGITIZER_STATE_CASHING_UP; + GNUNET_SCHEDULER_add_now(state_controller_task,NULL); + } + state = DIGITIZER_STATE_ACCEPT_CASH; + GNUNET_SCHEDULER_add_now(state_controller_task,NULL); + } +} + +static void AcceptCash_state_task(void *cls) +{ + (void)cls; + TALER_LOG_DEBUG("AcceptingCash state"); + state = DIGITIZER_STATE_ACCEPT_CASH; + GNUNET_SCHEDULER_add_now(state_controller_task,NULL); +} + /** * Switch between State tasks */ @@ -1120,6 +1232,16 @@ static void state_controller_task(void *cls) GNUNET_SCHEDULER_add_now(ScanQR_state_task,NULL); return; } + case DIGITIZER_STATE_COUNT_MONEY: + { + GNUNET_SCHEDULER_add_now(CountMoney_state_task,NULL); + return; + } + case DIGITIZER_STATE_ACCEPT_CASH: + { + GNUNET_SCHEDULER_add_now(AcceptCash_state_task,NULL); + return; + } default: { diff --git a/taler-digitizer.conf b/taler-digitizer.conf @@ -6,7 +6,7 @@ BANK_ACCOUNT = bank_acc_tellr # Withdrawl limitation per person, for a limited time period. # This can only be enforced with KYC functionality -USER_WITHDRAWALLIMIT = 200 +USER_WITHDRAWALLIMIT = 15 USER_WITHDRAWAL_PERIOD = 10