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:
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