diff --git a/applications/system/js_app/examples/apps/Scripts/js_examples/adc.js b/applications/system/js_app/examples/apps/Scripts/js_examples/adc.js new file mode 100644 index 000000000..0506d348c --- /dev/null +++ b/applications/system/js_app/examples/apps/Scripts/js_examples/adc.js @@ -0,0 +1,42 @@ +// This is an example of how to use the analog pins (ADC) on the Flipper Zero. +// The example uses a reference voltage of 2048mV (2.048V), but you can also use 2500mV (2.5V). +// The example reads the values of the analog pins A7, A6, and A4 and prints them to the console. +// The example also checks if the value of A7 is twice the value of A6 and breaks the loop if it is. +// The example uses the analog pins A7, A6, and A4, but you can also use PC3, PC1, and PC0. + +let gpio = require("gpio"); + +// initialize pins A7, A6, A4 as analog (you can also use PC3, PC1, PC0) +gpio.init("PA7", "analog", "no"); // pin, mode, pull +gpio.init("PA6", "analog", "no"); // pin, mode, pull +gpio.init("PA4", "analog", "no"); // pin, mode, pull + +gpio.startAnalog(2048); // vRef = 2.048V (you can also use 2500 for a 2.5V reference voltage) + +while (true) { + let pa7_value = gpio.readAnalog("PA7"); + let pa6_value = gpio.readAnalog("PA6"); + let pa4_value = gpio.readAnalog("PA4"); + print("A7: " + to_string(pa7_value) + " A6: " + to_string(pa6_value) + " A4: " + to_string(pa4_value)); + delay(100); + if (pa7_value === pa6_value * 2) { + break; + } +} +print("A7 is twice A6!"); + +gpio.stopAnalog(); + +// possible analog pins https://docs.flipper.net/gpio-and-modules#miFsS +// "PA7" aka 2 +// "PA6" aka 3 +// "PA4" aka 4 +// "PC3" aka 7 +// "PC1" aka 15 +// "PC0" aka 16 + +// possible modes +// "analog" + +// possible pull +// "no" diff --git a/applications/system/js_app/modules/js_gpio.c b/applications/system/js_app/modules/js_gpio.c index 8f6158418..ce65837b1 100644 --- a/applications/system/js_app/modules/js_gpio.c +++ b/applications/system/js_app/modules/js_gpio.c @@ -3,25 +3,30 @@ #include #include +typedef struct { + FuriHalAdcHandle* handle; +} JsGpioInst; + typedef struct { const GpioPin* pin; const char* name; + const FuriHalAdcChannel channel; } 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 + {.pin = &gpio_ext_pa7, .name = "PA7", .channel = FuriHalAdcChannel12}, // 2 + {.pin = &gpio_ext_pa6, .name = "PA6", .channel = FuriHalAdcChannel11}, // 3 + {.pin = &gpio_ext_pa4, .name = "PA4", .channel = FuriHalAdcChannel9}, // 4 + {.pin = &gpio_ext_pb3, .name = "PB3", .channel = FuriHalAdcChannelNone}, // 5 + {.pin = &gpio_ext_pb2, .name = "PB2", .channel = FuriHalAdcChannelNone}, // 6 + {.pin = &gpio_ext_pc3, .name = "PC3", .channel = FuriHalAdcChannel4}, // 7 + {.pin = &gpio_swclk, .name = "PA14", .channel = FuriHalAdcChannelNone}, // 10 + {.pin = &gpio_swdio, .name = "PA13", .channel = FuriHalAdcChannelNone}, // 12 + {.pin = &gpio_usart_tx, .name = "PB6", .channel = FuriHalAdcChannelNone}, // 13 + {.pin = &gpio_usart_rx, .name = "PB7", .channel = FuriHalAdcChannelNone}, // 14 + {.pin = &gpio_ext_pc1, .name = "PC1", .channel = FuriHalAdcChannel2}, // 15 + {.pin = &gpio_ext_pc0, .name = "PC0", .channel = FuriHalAdcChannel1}, // 16 + {.pin = &gpio_ibutton, .name = "PB14", .channel = FuriHalAdcChannelNone}, // 17 }; bool js_gpio_get_gpio_pull(const char* pull, GpioPull* value) { @@ -92,6 +97,15 @@ const GpioPin* js_gpio_get_gpio_pin(const char* name) { return NULL; } +FuriHalAdcChannel js_gpio_get_gpio_channel(const char* name) { + for(size_t i = 0; i < COUNT_OF(js_gpio_pins); i++) { + if(strcmp(js_gpio_pins[i].name, name) == 0) { + return js_gpio_pins[i].channel; + } + } + return FuriHalAdcChannelNone; +} + 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); @@ -232,18 +246,127 @@ static void js_gpio_read(struct mjs* mjs) { mjs_return(mjs, mjs_mk_boolean(mjs, value)); } +static void js_gpio_read_analog(struct mjs* mjs) { + mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0); + JsGpioInst* gpio = mjs_get_ptr(mjs, obj_inst); + furi_assert(gpio); + + if(gpio->handle == NULL) { + mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, "Analog mode not started"); + mjs_return(mjs, MJS_UNDEFINED); + return; + } + + 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; + } + + FuriHalAdcChannel channel = js_gpio_get_gpio_channel(pin_name); + if(channel == FuriHalAdcChannelNone) { + mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "Invalid pin name"); + mjs_return(mjs, MJS_UNDEFINED); + return; + } + + uint16_t adc_value = furi_hal_adc_read(gpio->handle, channel); + float adc_mv = furi_hal_adc_convert_to_voltage(gpio->handle, adc_value); + + mjs_return(mjs, mjs_mk_number(mjs, adc_mv)); +} + +static void js_gpio_start_analog(struct mjs* mjs) { + mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0); + JsGpioInst* gpio = mjs_get_ptr(mjs, obj_inst); + furi_assert(gpio); + + FuriHalAdcScale scale = FuriHalAdcScale2048; + if(mjs_nargs(mjs) > 0) { + mjs_val_t scale_arg = mjs_arg(mjs, 0); + + if(!mjs_is_number(scale_arg)) { + mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "Argument must be a number"); + mjs_return(mjs, MJS_UNDEFINED); + return; + } + + int32_t scale_num = mjs_get_int32(mjs, scale_arg); + if(scale_num == 2048 || scale_num == 2000) { // 2 volt reference + scale = FuriHalAdcScale2048; + } else if(scale_num == 2500) { // 2.5 volt reference + scale = FuriHalAdcScale2500; + } else { + mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "Invalid scale"); + mjs_return(mjs, MJS_UNDEFINED); + return; + } + } + + if(gpio->handle != NULL) { + mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, "Analog mode already started"); + mjs_return(mjs, MJS_UNDEFINED); + return; + } + + gpio->handle = furi_hal_adc_acquire(); + furi_hal_adc_configure_ex( + gpio->handle, + scale, + FuriHalAdcClockSync64, + FuriHalAdcOversample64, + FuriHalAdcSamplingtime247_5); +} + +static void js_gpio_stop_analog(struct mjs* mjs) { + mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0); + JsGpioInst* gpio = mjs_get_ptr(mjs, obj_inst); + furi_assert(gpio); + + if(gpio->handle == NULL) { + mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, "Analog mode not started"); + mjs_return(mjs, MJS_UNDEFINED); + return; + } + + furi_hal_adc_release(gpio->handle); + gpio->handle = NULL; +} + static void* js_gpio_create(struct mjs* mjs, mjs_val_t* object) { + JsGpioInst* gpio = malloc(sizeof(JsGpioInst)); + gpio->handle = NULL; mjs_val_t gpio_obj = mjs_mk_object(mjs); + mjs_set(mjs, gpio_obj, INST_PROP_NAME, ~0, mjs_mk_foreign(mjs, gpio)); 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)); + mjs_set(mjs, gpio_obj, "readAnalog", ~0, MJS_MK_FN(js_gpio_read_analog)); + mjs_set(mjs, gpio_obj, "startAnalog", ~0, MJS_MK_FN(js_gpio_start_analog)); + mjs_set(mjs, gpio_obj, "stopAnalog", ~0, MJS_MK_FN(js_gpio_stop_analog)); *object = gpio_obj; - return (void*)1; + return (void*)gpio; } static void js_gpio_destroy(void* inst) { - UNUSED(inst); + if(inst != NULL) { + JsGpioInst* gpio = (JsGpioInst*)inst; + if(gpio->handle != NULL) { + furi_hal_adc_release(gpio->handle); + gpio->handle = NULL; + } + free(gpio); + } // loop through all pins and reset them to analog mode for(size_t i = 0; i < COUNT_OF(js_gpio_pins); i++) {