From d947fa749aa115a9cddbd03918e1c60ae64a50bc Mon Sep 17 00:00:00 2001 From: Sil333033 <94360907+Sil333033@users.noreply.github.com> Date: Mon, 11 Mar 2024 20:58:14 +0100 Subject: [PATCH] JS: added GPIO module --- applications/system/js_app/application.fam | 9 + .../js_app/examples/apps/Scripts/gpio.js | 62 +++++ applications/system/js_app/modules/js_gpio.c | 237 ++++++++++++++++++ 3 files changed, 308 insertions(+) create mode 100644 applications/system/js_app/examples/apps/Scripts/gpio.js create mode 100644 applications/system/js_app/modules/js_gpio.c diff --git a/applications/system/js_app/application.fam b/applications/system/js_app/application.fam index 1da65ee1d0..67931216f5 100644 --- a/applications/system/js_app/application.fam +++ b/applications/system/js_app/application.fam @@ -94,6 +94,7 @@ App( requires=["js_app"], sources=["modules/js_keyboard.c"], ) + App( appid="js_subghz", apptype=FlipperAppType.PLUGIN, @@ -101,3 +102,11 @@ App( requires=["js_app"], sources=["modules/js_subghz/*.c"], ) + +App( + appid="js_gpio", + apptype=FlipperAppType.PLUGIN, + entry_point="js_gpio_ep", + requires=["js_app"], + sources=["modules/js_gpio.c"], +) diff --git a/applications/system/js_app/examples/apps/Scripts/gpio.js b/applications/system/js_app/examples/apps/Scripts/gpio.js new file mode 100644 index 0000000000..02b55a3adc --- /dev/null +++ b/applications/system/js_app/examples/apps/Scripts/gpio.js @@ -0,0 +1,62 @@ +let gpio = require("gpio"); + +// initialize pins +gpio.init("PC3", "outputPushPull", "up"); // pin, mode, pull +print("PC3 is initialized as outputPushPull with pull-up"); + +gpio.init("PC1", "input", "down"); // pin, mode, pull +print("PC1 is initialized as input with pull-down"); + +// let led on PC3 blink +gpio.write("PC3", true); // high +delay(1000); +gpio.write("PC3", false); // low +delay(1000); +gpio.write("PC3", true); // high +delay(1000); +gpio.write("PC3", false); // low + +// read value from PC1 and write it to PC3 +while (true) { + let value = gpio.read("PC1"); + gpio.write("PC3", value); + + value ? print("PC1 is high") : print("PC1 is low"); + + delay(100); +} + + +// possible pins https://docs.flipper.net/gpio-and-modules#miFsS +// "PA7" aka 2 +// "PA6" aka 3 +// "PA4" aka 4 +// "PB3" aka 5 +// "PB2" aka 6 +// "PC3" aka 7 +// "PA14" aka 10 +// "PA13" aka 12 +// "PB6" aka 13 +// "PB7" aka 14 +// "PC1" aka 15 +// "PC0" aka 16 +// "PB14" aka 17 + +// possible modes +// "input" +// "outputPushPull" +// "outputOpenDrain" +// "altFunctionPushPull" +// "altFunctionOpenDrain" +// "analog" +// "interruptRise" +// "interruptFall" +// "interruptRiseFall" +// "eventRise" +// "eventFall" +// "eventRiseFall" + +// possible pull +// "no" +// "up" +// "down" \ No newline at end of file diff --git a/applications/system/js_app/modules/js_gpio.c b/applications/system/js_app/modules/js_gpio.c new file mode 100644 index 0000000000..18021c64ef --- /dev/null +++ b/applications/system/js_app/modules/js_gpio.c @@ -0,0 +1,237 @@ +#include "../js_modules.h" +#include +#include + +typedef struct { + const GpioPin* pin; + const char* name; +} GpioPinCtx; + +static const GpioPinCtx js_gpio_pins[] = { + {.pin = &gpio_ext_pa7, .name = "PA7"}, // 2 + {.pin = &gpio_ext_pa6, .name = "PA6"}, // 3 + {.pin = &gpio_ext_pa4, .name = "PA4"}, // 4 + {.pin = &gpio_ext_pb3, .name = "PB3"}, // 5 + {.pin = &gpio_ext_pb2, .name = "PB2"}, // 6 + {.pin = &gpio_ext_pc3, .name = "PC3"}, // 7 + {.pin = &gpio_swclk, .name = "PA14"}, // 10 + {.pin = &gpio_swdio, .name = "PA13"}, // 12 + {.pin = &gpio_usart_tx, .name = "PB6"}, // 13 + {.pin = &gpio_usart_rx, .name = "PB7"}, // 14 + {.pin = &gpio_ext_pc1, .name = "PC1"}, // 15 + {.pin = &gpio_ext_pc0, .name = "PC0"}, // 16 + {.pin = &gpio_ibutton, .name = "PB14"}, // 17 +}; + +GpioPull js_gpio_get_gpio_pull(const char* pull) { + if(strcmp(pull, "no") == 0) { + return GpioPullNo; + } else if(strcmp(pull, "up") == 0) { + return GpioPullUp; + } else if(strcmp(pull, "down") == 0) { + return GpioPullDown; + } else { + return GpioPullNo; + } +} + +GpioMode js_gpio_get_gpio_mode(const char* mode) { + if(strcmp(mode, "input") == 0) { + return GpioModeInput; + } else if(strcmp(mode, "outputPushPull") == 0) { + return GpioModeOutputPushPull; + } else if(strcmp(mode, "outputOpenDrain") == 0) { + return GpioModeOutputOpenDrain; + } else if(strcmp(mode, "altFunctionPushPull") == 0) { + return GpioModeAltFunctionPushPull; + } else if(strcmp(mode, "altFunctionOpenDrain") == 0) { + return GpioModeAltFunctionOpenDrain; + } else if(strcmp(mode, "analog") == 0) { + return GpioModeAnalog; + } else if(strcmp(mode, "interruptRise") == 0) { + return GpioModeInterruptRise; + } else if(strcmp(mode, "interruptFall") == 0) { + return GpioModeInterruptFall; + } else if(strcmp(mode, "interruptRiseFall") == 0) { + return GpioModeInterruptRiseFall; + } else if(strcmp(mode, "eventRise") == 0) { + return GpioModeEventRise; + } else if(strcmp(mode, "eventFall") == 0) { + return GpioModeEventFall; + } else if(strcmp(mode, "eventRiseFall") == 0) { + return GpioModeEventRiseFall; + } else { + return GpioModeInput; + } +} + +const GpioPin* js_gpio_get_gpio_pin(const char* name) { + for(int i = 0; i < 12; i++) { + if(strcmp(js_gpio_pins[i].name, name) == 0) { + return js_gpio_pins[i].pin; + } + } + return NULL; +} + +static void js_gpio_init(struct mjs* mjs) { + mjs_val_t pin_arg = mjs_arg(mjs, 0); + mjs_val_t mode_arg = mjs_arg(mjs, 1); + mjs_val_t pull_arg = mjs_arg(mjs, 2); + + if(!mjs_is_string(pin_arg)) { + mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "Argument must be a string"); + mjs_return(mjs, MJS_UNDEFINED); + return; + } + + const char* pin_name = mjs_get_string(mjs, &pin_arg, NULL); + if(!pin_name) { + mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "Failed to get pin name"); + mjs_return(mjs, MJS_UNDEFINED); + return; + } + + if(!mjs_is_string(mode_arg)) { + mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "Argument must be a string"); + mjs_return(mjs, MJS_UNDEFINED); + return; + } + + const char* mode_name = mjs_get_string(mjs, &mode_arg, NULL); + if(!mode_name) { + mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "Failed to get mode name"); + mjs_return(mjs, MJS_UNDEFINED); + return; + } + + if(!mjs_is_string(pull_arg)) { + mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "Argument must be a string"); + mjs_return(mjs, MJS_UNDEFINED); + return; + } + + const char* pull_name = mjs_get_string(mjs, &pull_arg, NULL); + if(!pull_name) { + mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "Failed to get pull name"); + mjs_return(mjs, MJS_UNDEFINED); + return; + } + + const GpioPin* gpio_pin = js_gpio_get_gpio_pin(pin_name); + const GpioMode gpio_mode = js_gpio_get_gpio_mode(mode_name); + const GpioPull gpio_pull = js_gpio_get_gpio_pull(pull_name); + + if(gpio_pin == NULL) { + mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "Invalid pin name"); + mjs_return(mjs, MJS_UNDEFINED); + return; + } + + furi_hal_gpio_init(gpio_pin, gpio_mode, gpio_pull, GpioSpeedVeryHigh); + + mjs_return(mjs, MJS_UNDEFINED); +} + +static void js_gpio_write(struct mjs* mjs) { + mjs_val_t pin_arg = mjs_arg(mjs, 0); + mjs_val_t value_arg = mjs_arg(mjs, 1); + + if(!mjs_is_string(pin_arg)) { + mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "Argument must be a string"); + mjs_return(mjs, MJS_UNDEFINED); + return; + } + + const char* pin_name = mjs_get_string(mjs, &pin_arg, NULL); + if(!pin_name) { + mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "Failed to get pin name"); + mjs_return(mjs, MJS_UNDEFINED); + return; + } + + if(!mjs_is_boolean(value_arg)) { + mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "Argument must be a boolean"); + mjs_return(mjs, MJS_UNDEFINED); + return; + } + + bool value = mjs_get_bool(mjs, value_arg); + + const GpioPin* gpio_pin = js_gpio_get_gpio_pin(pin_name); + + if(gpio_pin == NULL) { + mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "Invalid pin name"); + mjs_return(mjs, MJS_UNDEFINED); + return; + } + + furi_hal_gpio_write(gpio_pin, value); + + mjs_return(mjs, MJS_UNDEFINED); +} + +static void js_gpio_read(struct mjs* mjs) { + mjs_val_t pin_arg = mjs_arg(mjs, 0); + + if(!mjs_is_string(pin_arg)) { + mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "Argument must be a string"); + mjs_return(mjs, MJS_UNDEFINED); + return; + } + + const char* pin_name = mjs_get_string(mjs, &pin_arg, NULL); + if(!pin_name) { + mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "Failed to get pin name"); + mjs_return(mjs, MJS_UNDEFINED); + return; + } + + const GpioPin* gpio_pin = js_gpio_get_gpio_pin(pin_name); + + if(gpio_pin == NULL) { + mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "Invalid pin name"); + mjs_return(mjs, MJS_UNDEFINED); + return; + } + + bool value = furi_hal_gpio_read(gpio_pin); + + mjs_return(mjs, mjs_mk_boolean(mjs, value)); +} + +static void* js_gpio_create(struct mjs* mjs, mjs_val_t* object) { + mjs_val_t gpio_obj = mjs_mk_object(mjs); + mjs_set(mjs, gpio_obj, "init", ~0, MJS_MK_FN(js_gpio_init)); + mjs_set(mjs, gpio_obj, "write", ~0, MJS_MK_FN(js_gpio_write)); + mjs_set(mjs, gpio_obj, "read", ~0, MJS_MK_FN(js_gpio_read)); + *object = gpio_obj; + + return (void*)1; +} + +static void js_gpio_destroy(void* inst) { + UNUSED(inst); + + // loop through all pins and reset them to analog mode + for(int i = 0; i < 12; i++) { + furi_hal_gpio_write(js_gpio_pins[i].pin, false); + furi_hal_gpio_init(js_gpio_pins[i].pin, GpioModeAnalog, GpioPullNo, GpioSpeedVeryHigh); + } +} + +static const JsModuleDescriptor js_gpio_desc = { + "gpio", + js_gpio_create, + js_gpio_destroy, +}; + +static const FlipperAppPluginDescriptor plugin_descriptor = { + .appid = PLUGIN_APP_ID, + .ep_api_version = PLUGIN_API_VERSION, + .entry_point = &js_gpio_desc, +}; + +const FlipperAppPluginDescriptor* js_gpio_ep(void) { + return &plugin_descriptor; +} \ No newline at end of file