From 793501d62d6ac3ce4d5bca80b3988f06ce9fd6d6 Mon Sep 17 00:00:00 2001 From: Samuel Yvon Date: Mon, 4 Jul 2022 11:32:06 -0400 Subject: [PATCH] Add GPIO control through RPC (#1282) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add GPIO control through RPC * Assets: sync protobuf to 0.10 * Assets: update protobuf to fixed v10 Co-authored-by: あく --- applications/rpc/rpc.c | 5 +- applications/rpc/rpc_gpio.c | 221 ++++++++++++++++++++++++++++++++++++ applications/rpc/rpc_i.h | 2 + assets/compiled/gpio.pb.c | 33 ++++++ assets/compiled/gpio.pb.h | 183 +++++++++++++++++++++++++++++ assets/protobuf | 2 +- 6 files changed, 444 insertions(+), 2 deletions(-) create mode 100644 applications/rpc/rpc_gpio.c create mode 100644 assets/compiled/gpio.pb.c create mode 100644 assets/compiled/gpio.pb.h diff --git a/applications/rpc/rpc.c b/applications/rpc/rpc.c index 4832afe3..ce063b8e 100644 --- a/applications/rpc/rpc.c +++ b/applications/rpc/rpc.c @@ -51,7 +51,10 @@ static RpcSystemCallbacks rpc_systems[] = { .alloc = rpc_system_gui_alloc, .free = rpc_system_gui_free, }, -}; + { + .alloc = rpc_system_gpio_alloc, + .free = NULL, + }}; struct RpcSession { Rpc* rpc; diff --git a/applications/rpc/rpc_gpio.c b/applications/rpc/rpc_gpio.c new file mode 100644 index 00000000..614e775a --- /dev/null +++ b/applications/rpc/rpc_gpio.c @@ -0,0 +1,221 @@ +#include "flipper.pb.h" +#include "rpc_i.h" +#include "gpio.pb.h" +#include +#include + +static const GpioPin* rpc_pin_to_hal_pin(PB_Gpio_GpioPin rpc_pin) { + switch(rpc_pin) { + case PB_Gpio_GpioPin_PC0: + return &gpio_ext_pc0; + case PB_Gpio_GpioPin_PC1: + return &gpio_ext_pc1; + case PB_Gpio_GpioPin_PC3: + return &gpio_ext_pc3; + case PB_Gpio_GpioPin_PB2: + return &gpio_ext_pb2; + case PB_Gpio_GpioPin_PB3: + return &gpio_ext_pb3; + case PB_Gpio_GpioPin_PA4: + return &gpio_ext_pa4; + case PB_Gpio_GpioPin_PA6: + return &gpio_ext_pa6; + case PB_Gpio_GpioPin_PA7: + return &gpio_ext_pa7; + } + + __builtin_unreachable(); +} + +static GpioMode rpc_mode_to_hal_mode(PB_Gpio_GpioPinMode rpc_mode) { + switch(rpc_mode) { + case PB_Gpio_GpioPinMode_OUTPUT: + return GpioModeOutputPushPull; + case PB_Gpio_GpioPinMode_INPUT: + return GpioModeInput; + } + + __builtin_unreachable(); +} + +static GpioPull rpc_pull_mode_to_hall_pull_mode(PB_Gpio_GpioInputPull pull_mode) { + switch(pull_mode) { + case PB_Gpio_GpioInputPull_UP: + return GpioPullUp; + case PB_Gpio_GpioInputPull_DOWN: + return GpioPullDown; + case PB_Gpio_GpioInputPull_NO: + return GpioPullNo; + } + + __builtin_unreachable(); +} + +static void rpc_system_gpio_set_pin_mode(const PB_Main* request, void* context) { + furi_assert(request); + furi_assert(context); + furi_assert(request->which_content == PB_Main_gpio_set_pin_mode_tag); + + RpcSession* session = context; + furi_assert(session); + + PB_Gpio_SetPinMode cmd = request->content.gpio_set_pin_mode; + const GpioPin* pin = rpc_pin_to_hal_pin(cmd.pin); + GpioMode mode = rpc_mode_to_hal_mode(cmd.mode); + + furi_hal_gpio_init_simple(pin, mode); + if(mode == GpioModeOutputPushPull) { + furi_hal_gpio_write(pin, false); + } + + rpc_send_and_release_empty(session, request->command_id, PB_CommandStatus_OK); +} + +static void rpc_system_gpio_write_pin(const PB_Main* request, void* context) { + furi_assert(request); + furi_assert(context); + furi_assert(request->which_content == PB_Main_gpio_write_pin_tag); + + RpcSession* session = context; + furi_assert(session); + + PB_Gpio_WritePin cmd = request->content.gpio_write_pin; + const GpioPin* pin = rpc_pin_to_hal_pin(cmd.pin); + uint8_t value = !!(cmd.value); + + PB_Main* response = malloc(sizeof(PB_Main)); + response->command_id = request->command_id; + response->has_next = false; + + if(LL_GPIO_MODE_OUTPUT != LL_GPIO_GetPinMode(pin->port, pin->pin)) { + response->command_status = PB_CommandStatus_ERROR_GPIO_MODE_INCORRECT; + } else { + response->command_status = PB_CommandStatus_OK; + furi_hal_gpio_write(pin, value); + } + + rpc_send_and_release(session, response); + + free(response); +} + +static void rpc_system_gpio_read_pin(const PB_Main* request, void* context) { + furi_assert(request); + furi_assert(context); + furi_assert(request->which_content == PB_Main_gpio_read_pin_tag); + + RpcSession* session = context; + furi_assert(session); + + PB_Gpio_ReadPin cmd = request->content.gpio_read_pin; + const GpioPin* pin = rpc_pin_to_hal_pin(cmd.pin); + + PB_Main* response = malloc(sizeof(PB_Main)); + response->command_id = request->command_id; + response->has_next = false; + + if(LL_GPIO_MODE_INPUT != LL_GPIO_GetPinMode(pin->port, pin->pin)) { + response->command_status = PB_CommandStatus_ERROR_GPIO_MODE_INCORRECT; + } else { + response->command_status = PB_CommandStatus_OK; + response->which_content = PB_Main_gpio_read_pin_response_tag; + response->content.gpio_read_pin_response.value = !!furi_hal_gpio_read(pin); + } + + rpc_send_and_release(session, response); + + free(response); +} + +void rpc_system_gpio_get_pin_mode(const PB_Main* request, void* context) { + furi_assert(request); + furi_assert(context); + furi_assert(request->which_content == PB_Main_gpio_get_pin_mode_tag); + + RpcSession* session = context; + furi_assert(session); + + PB_Gpio_GetPinMode cmd = request->content.gpio_get_pin_mode; + const GpioPin* pin = rpc_pin_to_hal_pin(cmd.pin); + + PB_Main* response = malloc(sizeof(PB_Main)); + response->command_id = request->command_id; + response->has_next = false; + + uint32_t raw_pin_mode = LL_GPIO_GetPinMode(pin->port, pin->pin); + + PB_Gpio_GpioPinMode pin_mode; + if(LL_GPIO_MODE_INPUT == raw_pin_mode) { + pin_mode = PB_Gpio_GpioPinMode_INPUT; + response->command_status = PB_CommandStatus_OK; + } else if(LL_GPIO_MODE_OUTPUT == raw_pin_mode) { + pin_mode = PB_Gpio_GpioPinMode_OUTPUT; + response->command_status = PB_CommandStatus_OK; + } else { + pin_mode = PB_Gpio_GpioPinMode_INPUT; + response->command_status = PB_CommandStatus_ERROR_GPIO_UNKNOWN_PIN_MODE; + } + + response->which_content = PB_Main_gpio_get_pin_mode_response_tag; + response->content.gpio_get_pin_mode_response.mode = pin_mode; + + rpc_send_and_release(session, response); + + free(response); +} + +void rpc_system_gpio_set_input_pull(const PB_Main* request, void* context) { + furi_assert(request); + furi_assert(context); + furi_assert(request->which_content == PB_Main_gpio_set_input_pull_tag); + + RpcSession* session = context; + furi_assert(session); + + PB_Gpio_SetInputPull cmd = request->content.gpio_set_input_pull; + const GpioPin* pin = rpc_pin_to_hal_pin(cmd.pin); + const GpioPull pull_mode = rpc_pull_mode_to_hall_pull_mode(cmd.pull_mode); + + PB_Main* response = malloc(sizeof(PB_Main)); + response->command_id = request->command_id; + response->has_next = false; + + PB_CommandStatus status; + if(LL_GPIO_MODE_INPUT != LL_GPIO_GetPinMode(pin->port, pin->pin)) { + status = PB_CommandStatus_ERROR_GPIO_MODE_INCORRECT; + } else { + status = PB_CommandStatus_OK; + furi_hal_gpio_init(pin, GpioModeInput, pull_mode, GpioSpeedLow); + } + + rpc_send_and_release_empty(session, request->command_id, status); + + free(response); +} + +void* rpc_system_gpio_alloc(RpcSession* session) { + furi_assert(session); + + RpcHandler rpc_handler = { + .message_handler = NULL, + .decode_submessage = NULL, + .context = session, + }; + + rpc_handler.message_handler = rpc_system_gpio_set_pin_mode; + rpc_add_handler(session, PB_Main_gpio_set_pin_mode_tag, &rpc_handler); + + rpc_handler.message_handler = rpc_system_gpio_write_pin; + rpc_add_handler(session, PB_Main_gpio_write_pin_tag, &rpc_handler); + + rpc_handler.message_handler = rpc_system_gpio_read_pin; + rpc_add_handler(session, PB_Main_gpio_read_pin_tag, &rpc_handler); + + rpc_handler.message_handler = rpc_system_gpio_get_pin_mode; + rpc_add_handler(session, PB_Main_gpio_get_pin_mode_tag, &rpc_handler); + + rpc_handler.message_handler = rpc_system_gpio_set_input_pull; + rpc_add_handler(session, PB_Main_gpio_set_input_pull_tag, &rpc_handler); + + return NULL; +} diff --git a/applications/rpc/rpc_i.h b/applications/rpc/rpc_i.h index d0c6cf76..e512ad39 100644 --- a/applications/rpc/rpc_i.h +++ b/applications/rpc/rpc_i.h @@ -32,6 +32,8 @@ void* rpc_system_app_alloc(RpcSession* session); void rpc_system_app_free(void* ctx); void* rpc_system_gui_alloc(RpcSession* session); void rpc_system_gui_free(void* ctx); +void* rpc_system_gpio_alloc(RpcSession* session); +void rpc_system_gpio_free(void* ctx); void rpc_print_message(const PB_Main* message); void rpc_cli_command_start_session(Cli* cli, string_t args, void* context); diff --git a/assets/compiled/gpio.pb.c b/assets/compiled/gpio.pb.c new file mode 100644 index 00000000..a47c928d --- /dev/null +++ b/assets/compiled/gpio.pb.c @@ -0,0 +1,33 @@ +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.4.5 */ + +#include "gpio.pb.h" +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +PB_BIND(PB_Gpio_SetPinMode, PB_Gpio_SetPinMode, AUTO) + + +PB_BIND(PB_Gpio_SetInputPull, PB_Gpio_SetInputPull, AUTO) + + +PB_BIND(PB_Gpio_GetPinMode, PB_Gpio_GetPinMode, AUTO) + + +PB_BIND(PB_Gpio_GetPinModeResponse, PB_Gpio_GetPinModeResponse, AUTO) + + +PB_BIND(PB_Gpio_ReadPin, PB_Gpio_ReadPin, AUTO) + + +PB_BIND(PB_Gpio_ReadPinResponse, PB_Gpio_ReadPinResponse, AUTO) + + +PB_BIND(PB_Gpio_WritePin, PB_Gpio_WritePin, AUTO) + + + + + + diff --git a/assets/compiled/gpio.pb.h b/assets/compiled/gpio.pb.h new file mode 100644 index 00000000..a8a08df7 --- /dev/null +++ b/assets/compiled/gpio.pb.h @@ -0,0 +1,183 @@ +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.4.5 */ + +#ifndef PB_PB_GPIO_GPIO_PB_H_INCLUDED +#define PB_PB_GPIO_GPIO_PB_H_INCLUDED +#include + +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +/* Enum definitions */ +typedef enum _PB_Gpio_GpioPin { + PB_Gpio_GpioPin_PC0 = 0, + PB_Gpio_GpioPin_PC1 = 1, + PB_Gpio_GpioPin_PC3 = 2, + PB_Gpio_GpioPin_PB2 = 3, + PB_Gpio_GpioPin_PB3 = 4, + PB_Gpio_GpioPin_PA4 = 5, + PB_Gpio_GpioPin_PA6 = 6, + PB_Gpio_GpioPin_PA7 = 7 +} PB_Gpio_GpioPin; + +typedef enum _PB_Gpio_GpioPinMode { + PB_Gpio_GpioPinMode_OUTPUT = 0, + PB_Gpio_GpioPinMode_INPUT = 1 +} PB_Gpio_GpioPinMode; + +typedef enum _PB_Gpio_GpioInputPull { + PB_Gpio_GpioInputPull_NO = 0, + PB_Gpio_GpioInputPull_UP = 1, + PB_Gpio_GpioInputPull_DOWN = 2 +} PB_Gpio_GpioInputPull; + +/* Struct definitions */ +typedef struct _PB_Gpio_GetPinMode { + PB_Gpio_GpioPin pin; +} PB_Gpio_GetPinMode; + +typedef struct _PB_Gpio_GetPinModeResponse { + PB_Gpio_GpioPinMode mode; +} PB_Gpio_GetPinModeResponse; + +typedef struct _PB_Gpio_ReadPin { + PB_Gpio_GpioPin pin; +} PB_Gpio_ReadPin; + +typedef struct _PB_Gpio_ReadPinResponse { + uint32_t value; +} PB_Gpio_ReadPinResponse; + +typedef struct _PB_Gpio_SetInputPull { + PB_Gpio_GpioPin pin; + PB_Gpio_GpioInputPull pull_mode; +} PB_Gpio_SetInputPull; + +typedef struct _PB_Gpio_SetPinMode { + PB_Gpio_GpioPin pin; + PB_Gpio_GpioPinMode mode; +} PB_Gpio_SetPinMode; + +typedef struct _PB_Gpio_WritePin { + PB_Gpio_GpioPin pin; + uint32_t value; +} PB_Gpio_WritePin; + + +/* Helper constants for enums */ +#define _PB_Gpio_GpioPin_MIN PB_Gpio_GpioPin_PC0 +#define _PB_Gpio_GpioPin_MAX PB_Gpio_GpioPin_PA7 +#define _PB_Gpio_GpioPin_ARRAYSIZE ((PB_Gpio_GpioPin)(PB_Gpio_GpioPin_PA7+1)) + +#define _PB_Gpio_GpioPinMode_MIN PB_Gpio_GpioPinMode_OUTPUT +#define _PB_Gpio_GpioPinMode_MAX PB_Gpio_GpioPinMode_INPUT +#define _PB_Gpio_GpioPinMode_ARRAYSIZE ((PB_Gpio_GpioPinMode)(PB_Gpio_GpioPinMode_INPUT+1)) + +#define _PB_Gpio_GpioInputPull_MIN PB_Gpio_GpioInputPull_NO +#define _PB_Gpio_GpioInputPull_MAX PB_Gpio_GpioInputPull_DOWN +#define _PB_Gpio_GpioInputPull_ARRAYSIZE ((PB_Gpio_GpioInputPull)(PB_Gpio_GpioInputPull_DOWN+1)) + + +#ifdef __cplusplus +extern "C" { +#endif + +/* Initializer values for message structs */ +#define PB_Gpio_SetPinMode_init_default {_PB_Gpio_GpioPin_MIN, _PB_Gpio_GpioPinMode_MIN} +#define PB_Gpio_SetInputPull_init_default {_PB_Gpio_GpioPin_MIN, _PB_Gpio_GpioInputPull_MIN} +#define PB_Gpio_GetPinMode_init_default {_PB_Gpio_GpioPin_MIN} +#define PB_Gpio_GetPinModeResponse_init_default {_PB_Gpio_GpioPinMode_MIN} +#define PB_Gpio_ReadPin_init_default {_PB_Gpio_GpioPin_MIN} +#define PB_Gpio_ReadPinResponse_init_default {0} +#define PB_Gpio_WritePin_init_default {_PB_Gpio_GpioPin_MIN, 0} +#define PB_Gpio_SetPinMode_init_zero {_PB_Gpio_GpioPin_MIN, _PB_Gpio_GpioPinMode_MIN} +#define PB_Gpio_SetInputPull_init_zero {_PB_Gpio_GpioPin_MIN, _PB_Gpio_GpioInputPull_MIN} +#define PB_Gpio_GetPinMode_init_zero {_PB_Gpio_GpioPin_MIN} +#define PB_Gpio_GetPinModeResponse_init_zero {_PB_Gpio_GpioPinMode_MIN} +#define PB_Gpio_ReadPin_init_zero {_PB_Gpio_GpioPin_MIN} +#define PB_Gpio_ReadPinResponse_init_zero {0} +#define PB_Gpio_WritePin_init_zero {_PB_Gpio_GpioPin_MIN, 0} + +/* Field tags (for use in manual encoding/decoding) */ +#define PB_Gpio_GetPinMode_pin_tag 1 +#define PB_Gpio_GetPinModeResponse_mode_tag 1 +#define PB_Gpio_ReadPin_pin_tag 1 +#define PB_Gpio_ReadPinResponse_value_tag 2 +#define PB_Gpio_SetInputPull_pin_tag 1 +#define PB_Gpio_SetInputPull_pull_mode_tag 2 +#define PB_Gpio_SetPinMode_pin_tag 1 +#define PB_Gpio_SetPinMode_mode_tag 2 +#define PB_Gpio_WritePin_pin_tag 1 +#define PB_Gpio_WritePin_value_tag 2 + +/* Struct field encoding specification for nanopb */ +#define PB_Gpio_SetPinMode_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UENUM, pin, 1) \ +X(a, STATIC, SINGULAR, UENUM, mode, 2) +#define PB_Gpio_SetPinMode_CALLBACK NULL +#define PB_Gpio_SetPinMode_DEFAULT NULL + +#define PB_Gpio_SetInputPull_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UENUM, pin, 1) \ +X(a, STATIC, SINGULAR, UENUM, pull_mode, 2) +#define PB_Gpio_SetInputPull_CALLBACK NULL +#define PB_Gpio_SetInputPull_DEFAULT NULL + +#define PB_Gpio_GetPinMode_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UENUM, pin, 1) +#define PB_Gpio_GetPinMode_CALLBACK NULL +#define PB_Gpio_GetPinMode_DEFAULT NULL + +#define PB_Gpio_GetPinModeResponse_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UENUM, mode, 1) +#define PB_Gpio_GetPinModeResponse_CALLBACK NULL +#define PB_Gpio_GetPinModeResponse_DEFAULT NULL + +#define PB_Gpio_ReadPin_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UENUM, pin, 1) +#define PB_Gpio_ReadPin_CALLBACK NULL +#define PB_Gpio_ReadPin_DEFAULT NULL + +#define PB_Gpio_ReadPinResponse_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, value, 2) +#define PB_Gpio_ReadPinResponse_CALLBACK NULL +#define PB_Gpio_ReadPinResponse_DEFAULT NULL + +#define PB_Gpio_WritePin_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UENUM, pin, 1) \ +X(a, STATIC, SINGULAR, UINT32, value, 2) +#define PB_Gpio_WritePin_CALLBACK NULL +#define PB_Gpio_WritePin_DEFAULT NULL + +extern const pb_msgdesc_t PB_Gpio_SetPinMode_msg; +extern const pb_msgdesc_t PB_Gpio_SetInputPull_msg; +extern const pb_msgdesc_t PB_Gpio_GetPinMode_msg; +extern const pb_msgdesc_t PB_Gpio_GetPinModeResponse_msg; +extern const pb_msgdesc_t PB_Gpio_ReadPin_msg; +extern const pb_msgdesc_t PB_Gpio_ReadPinResponse_msg; +extern const pb_msgdesc_t PB_Gpio_WritePin_msg; + +/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ +#define PB_Gpio_SetPinMode_fields &PB_Gpio_SetPinMode_msg +#define PB_Gpio_SetInputPull_fields &PB_Gpio_SetInputPull_msg +#define PB_Gpio_GetPinMode_fields &PB_Gpio_GetPinMode_msg +#define PB_Gpio_GetPinModeResponse_fields &PB_Gpio_GetPinModeResponse_msg +#define PB_Gpio_ReadPin_fields &PB_Gpio_ReadPin_msg +#define PB_Gpio_ReadPinResponse_fields &PB_Gpio_ReadPinResponse_msg +#define PB_Gpio_WritePin_fields &PB_Gpio_WritePin_msg + +/* Maximum encoded size of messages (where known) */ +#define PB_Gpio_GetPinModeResponse_size 2 +#define PB_Gpio_GetPinMode_size 2 +#define PB_Gpio_ReadPinResponse_size 6 +#define PB_Gpio_ReadPin_size 2 +#define PB_Gpio_SetInputPull_size 4 +#define PB_Gpio_SetPinMode_size 4 +#define PB_Gpio_WritePin_size 8 + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/assets/protobuf b/assets/protobuf index 6c1b8ae6..d9e34366 160000 --- a/assets/protobuf +++ b/assets/protobuf @@ -1 +1 @@ -Subproject commit 6c1b8ae66a85bcd7e79e993a0b5573c38c302db5 +Subproject commit d9e343661dd36cfab792b78be1dea4e5950cb4dd