JS: Flipboard
This commit is contained in:
61
js/flipboard/modules/js_infrared.c
Normal file
61
js/flipboard/modules/js_infrared.c
Normal file
@@ -0,0 +1,61 @@
|
||||
#include "../js_modules.h"
|
||||
#include <infrared.h>
|
||||
#include <infrared_transmit.h>
|
||||
|
||||
static void js_infrared_send_protocol(struct mjs* mjs) {
|
||||
size_t num_args = mjs_nargs(mjs);
|
||||
if(num_args != 3 || !mjs_is_string(mjs_arg(mjs, 0)) || !mjs_is_number(mjs_arg(mjs, 1)) ||
|
||||
!mjs_is_number(mjs_arg(mjs, 2))) {
|
||||
mjs_prepend_errorf(
|
||||
mjs, MJS_BAD_ARGS_ERROR, "Invalid args (protocolName, address, command)");
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
return;
|
||||
}
|
||||
|
||||
mjs_val_t protocol_arg = mjs_arg(mjs, 0);
|
||||
const char* protocol_name = mjs_get_string(mjs, &protocol_arg, NULL);
|
||||
uint32_t address = mjs_get_int(mjs, mjs_arg(mjs, 1));
|
||||
uint32_t command = mjs_get_int(mjs, mjs_arg(mjs, 2));
|
||||
|
||||
InfraredMessage message;
|
||||
message.protocol = infrared_get_protocol_by_name(protocol_name);
|
||||
if(message.protocol == InfraredProtocolUnknown) {
|
||||
mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "Invalid protocol (%s)", protocol_name);
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
return;
|
||||
}
|
||||
int transmit_count = 1;
|
||||
message.address = address;
|
||||
message.command = command;
|
||||
message.repeat = transmit_count != 1;
|
||||
infrared_send(&message, transmit_count);
|
||||
|
||||
mjs_return(mjs, mjs_mk_boolean(mjs, true));
|
||||
}
|
||||
|
||||
static void* js_infrared_create(struct mjs* mjs, mjs_val_t* object) {
|
||||
mjs_val_t infrared_obj = mjs_mk_object(mjs);
|
||||
mjs_set(mjs, infrared_obj, "sendProtocol", ~0, MJS_MK_FN(js_infrared_send_protocol));
|
||||
*object = infrared_obj;
|
||||
return (void*)1;
|
||||
}
|
||||
|
||||
static void js_infrared_destroy(void* inst) {
|
||||
UNUSED(inst);
|
||||
}
|
||||
|
||||
static const JsModuleDescriptor js_infrared_desc = {
|
||||
"infrared",
|
||||
js_infrared_create,
|
||||
js_infrared_destroy,
|
||||
};
|
||||
|
||||
static const FlipperAppPluginDescriptor plugin_descriptor = {
|
||||
.appid = PLUGIN_APP_ID,
|
||||
.ep_api_version = PLUGIN_API_VERSION,
|
||||
.entry_point = &js_infrared_desc,
|
||||
};
|
||||
|
||||
const FlipperAppPluginDescriptor* js_infrared_ep(void) {
|
||||
return &plugin_descriptor;
|
||||
}
|
||||
213
js/flipboard/modules/js_rgbleds/js_rgbleds.c
Normal file
213
js/flipboard/modules/js_rgbleds/js_rgbleds.c
Normal file
@@ -0,0 +1,213 @@
|
||||
#include <core/common_defines.h>
|
||||
#include "../../js_modules.h"
|
||||
#include <furi_hal.h>
|
||||
|
||||
#include "rgbleds.h"
|
||||
|
||||
typedef struct {
|
||||
RgbLeds* leds;
|
||||
} JsRgbledsInst;
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
static const GpioPin* get_gpio_pin(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].pin;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void js_rgbleds_setup(struct mjs* mjs) {
|
||||
mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0);
|
||||
JsRgbledsInst* rgbleds = mjs_get_ptr(mjs, obj_inst);
|
||||
furi_assert(rgbleds);
|
||||
|
||||
if(mjs_nargs(mjs) != 1 || !mjs_is_object(mjs_arg(mjs, 0))) {
|
||||
mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "");
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
return;
|
||||
}
|
||||
|
||||
mjs_val_t pin_obj = mjs_get(mjs, mjs_arg(mjs, 0), "pin", ~0);
|
||||
mjs_val_t count_obj = mjs_get(mjs, mjs_arg(mjs, 0), "count", ~0);
|
||||
mjs_val_t spec_obj = mjs_get(mjs, mjs_arg(mjs, 0), "spec", ~0);
|
||||
|
||||
if(!mjs_is_string(pin_obj)) {
|
||||
mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "pin must be a string");
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!mjs_is_number(count_obj)) {
|
||||
mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "count must be a number");
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!mjs_is_string(spec_obj)) {
|
||||
mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "spec must be a string");
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
return;
|
||||
}
|
||||
|
||||
const GpioPin* pin = get_gpio_pin(mjs_get_string(mjs, &pin_obj, NULL));
|
||||
if(!pin) {
|
||||
mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "invalid pin");
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
return;
|
||||
}
|
||||
|
||||
if(rgbleds->leds) {
|
||||
rgbleds_free(rgbleds->leds);
|
||||
}
|
||||
|
||||
uint16_t count = mjs_get_int(mjs, count_obj);
|
||||
rgbleds->leds = rgbleds_alloc(count, pin);
|
||||
}
|
||||
|
||||
static void js_rgbleds_set(struct mjs* mjs) {
|
||||
mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0);
|
||||
JsRgbledsInst* rgbleds = mjs_get_ptr(mjs, obj_inst);
|
||||
furi_assert(rgbleds);
|
||||
|
||||
if(!rgbleds->leds) {
|
||||
mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, "LEDs not setup");
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t color;
|
||||
if(mjs_nargs(mjs) == 2) {
|
||||
if(mjs_is_number(mjs_arg(mjs, 0)) && mjs_is_number(mjs_arg(mjs, 1))) {
|
||||
color = mjs_get_int(mjs, mjs_arg(mjs, 1));
|
||||
} else if(mjs_is_number(mjs_arg(mjs, 0)) && mjs_is_object(mjs_arg(mjs, 1))) {
|
||||
mjs_val_t red_obj = mjs_get(mjs, mjs_arg(mjs, 1), "red", ~0);
|
||||
mjs_val_t green_obj = mjs_get(mjs, mjs_arg(mjs, 1), "green", ~0);
|
||||
mjs_val_t blue_obj = mjs_get(mjs, mjs_arg(mjs, 1), "blue", ~0);
|
||||
if(!mjs_is_number(red_obj) || !mjs_is_number(green_obj) || !mjs_is_number(blue_obj)) {
|
||||
mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "");
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
return;
|
||||
}
|
||||
color = (mjs_get_int(mjs, red_obj) << 16) | (mjs_get_int(mjs, green_obj) << 8) |
|
||||
mjs_get_int(mjs, blue_obj);
|
||||
} else {
|
||||
mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "");
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
return;
|
||||
}
|
||||
} else if(mjs_nargs(mjs) == 4) {
|
||||
if(!mjs_is_number(mjs_arg(mjs, 0)) || !mjs_is_number(mjs_arg(mjs, 1)) ||
|
||||
!mjs_is_number(mjs_arg(mjs, 2)) || !mjs_is_number(mjs_arg(mjs, 3))) {
|
||||
mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "");
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
return;
|
||||
}
|
||||
|
||||
color = (mjs_get_int(mjs, mjs_arg(mjs, 1)) << 16) |
|
||||
(mjs_get_int(mjs, mjs_arg(mjs, 2)) << 8) | mjs_get_int(mjs, mjs_arg(mjs, 3));
|
||||
} else {
|
||||
mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "");
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t index = mjs_get_int(mjs, mjs_arg(mjs, 0));
|
||||
|
||||
uint16_t original_color = rgbleds_get(rgbleds->leds, index);
|
||||
rgbleds_set(rgbleds->leds, index, color);
|
||||
mjs_return(mjs, mjs_mk_number(mjs, original_color));
|
||||
}
|
||||
|
||||
static void js_rgbleds_get(struct mjs* mjs) {
|
||||
mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0);
|
||||
JsRgbledsInst* rgbleds = mjs_get_ptr(mjs, obj_inst);
|
||||
furi_assert(rgbleds);
|
||||
|
||||
if(!rgbleds->leds) {
|
||||
mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, "LEDs not setup");
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
return;
|
||||
}
|
||||
|
||||
if(mjs_nargs(mjs) != 1 || !mjs_is_number(mjs_arg(mjs, 0))) {
|
||||
mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "");
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t index = mjs_get_int(mjs, mjs_arg(mjs, 0));
|
||||
mjs_return(mjs, mjs_mk_number(mjs, rgbleds_get(rgbleds->leds, index)));
|
||||
}
|
||||
|
||||
static void js_rgbleds_update(struct mjs* mjs) {
|
||||
mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0);
|
||||
JsRgbledsInst* rgbleds = mjs_get_ptr(mjs, obj_inst);
|
||||
furi_assert(rgbleds);
|
||||
|
||||
if(!rgbleds->leds) {
|
||||
mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, "LEDs not setup");
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
return;
|
||||
}
|
||||
|
||||
rgbleds_update(rgbleds->leds);
|
||||
}
|
||||
|
||||
static void* js_rgbleds_create(struct mjs* mjs, mjs_val_t* object) {
|
||||
JsRgbledsInst* rgbleds = malloc(sizeof(JsRgbledsInst));
|
||||
rgbleds->leds = NULL;
|
||||
mjs_val_t rgbleds_obj = mjs_mk_object(mjs);
|
||||
mjs_set(mjs, rgbleds_obj, INST_PROP_NAME, ~0, mjs_mk_foreign(mjs, rgbleds));
|
||||
mjs_set(mjs, rgbleds_obj, "setup", ~0, MJS_MK_FN(js_rgbleds_setup));
|
||||
mjs_set(mjs, rgbleds_obj, "set", ~0, MJS_MK_FN(js_rgbleds_set));
|
||||
mjs_set(mjs, rgbleds_obj, "get", ~0, MJS_MK_FN(js_rgbleds_get));
|
||||
mjs_set(mjs, rgbleds_obj, "update", ~0, MJS_MK_FN(js_rgbleds_update));
|
||||
*object = rgbleds_obj;
|
||||
return rgbleds;
|
||||
}
|
||||
|
||||
static void js_rgbleds_destroy(void* inst) {
|
||||
JsRgbledsInst* rgbleds = inst;
|
||||
if(rgbleds->leds) {
|
||||
rgbleds_free(rgbleds->leds);
|
||||
}
|
||||
free(rgbleds);
|
||||
}
|
||||
|
||||
static const JsModuleDescriptor js_rgbleds_desc = {
|
||||
"rgbleds",
|
||||
js_rgbleds_create,
|
||||
js_rgbleds_destroy,
|
||||
};
|
||||
|
||||
static const FlipperAppPluginDescriptor plugin_descriptor = {
|
||||
.appid = PLUGIN_APP_ID,
|
||||
.ep_api_version = PLUGIN_API_VERSION,
|
||||
.entry_point = &js_rgbleds_desc,
|
||||
};
|
||||
|
||||
const FlipperAppPluginDescriptor* js_rgbleds_ep(void) {
|
||||
return &plugin_descriptor;
|
||||
}
|
||||
299
js/flipboard/modules/js_rgbleds/led_driver.c
Normal file
299
js/flipboard/modules/js_rgbleds/led_driver.c
Normal file
@@ -0,0 +1,299 @@
|
||||
#include "led_driver_i.h"
|
||||
|
||||
#define TAG "led_driver"
|
||||
|
||||
struct LedDriver {
|
||||
LL_DMA_InitTypeDef dma_gpio_update;
|
||||
LL_DMA_InitTypeDef dma_led_transition_timer;
|
||||
|
||||
const GpioPin* gpio;
|
||||
uint32_t gpio_buf[2]; // On/Off for GPIO
|
||||
|
||||
uint16_t timer_buffer[LED_DRIVER_BUFFER_SIZE + 2];
|
||||
uint32_t write_pos;
|
||||
uint32_t read_pos;
|
||||
|
||||
uint32_t count_leds;
|
||||
uint32_t* led_data;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Initializes the DMA for GPIO pin toggle via BSRR.
|
||||
* @param led_driver The led driver to initialize.
|
||||
* @param gpio The GPIO pin to toggle.
|
||||
*/
|
||||
static void led_driver_init_dma_gpio_update(LedDriver* led_driver, const GpioPin* gpio) {
|
||||
led_driver->gpio = gpio;
|
||||
|
||||
// Memory to Peripheral
|
||||
led_driver->dma_gpio_update.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH;
|
||||
// Peripheral (GPIO - We populate GPIO port's BSRR register)
|
||||
led_driver->dma_gpio_update.PeriphOrM2MSrcAddress = (uint32_t)&gpio->port->BSRR;
|
||||
led_driver->dma_gpio_update.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT;
|
||||
led_driver->dma_gpio_update.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD;
|
||||
// Memory (State to set GPIO)
|
||||
led_driver->dma_gpio_update.MemoryOrM2MDstAddress = (uint32_t)led_driver->gpio_buf;
|
||||
led_driver->dma_gpio_update.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT;
|
||||
led_driver->dma_gpio_update.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD;
|
||||
// Data
|
||||
led_driver->dma_gpio_update.Mode = LL_DMA_MODE_CIRCULAR;
|
||||
led_driver->dma_gpio_update.NbData = 2; // We cycle between two (HIGH/LOW)values
|
||||
// When to perform data exchange
|
||||
led_driver->dma_gpio_update.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP;
|
||||
led_driver->dma_gpio_update.Priority = LL_DMA_PRIORITY_VERYHIGH;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initializes the DMA for the LED timings via ARR.
|
||||
* @param led_driver The led driver to initialize.
|
||||
*/
|
||||
static void led_driver_init_dma_led_transition_timer(LedDriver* led_driver) {
|
||||
// Timer that triggers based on user data.
|
||||
led_driver->dma_led_transition_timer.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH;
|
||||
// Peripheral (Timer - We populate TIM2's ARR register)
|
||||
led_driver->dma_led_transition_timer.PeriphOrM2MSrcAddress = (uint32_t)&TIM2->ARR;
|
||||
led_driver->dma_led_transition_timer.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT;
|
||||
led_driver->dma_led_transition_timer.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD;
|
||||
// Memory (Timings)
|
||||
led_driver->dma_led_transition_timer.MemoryOrM2MDstAddress =
|
||||
(uint32_t)led_driver->timer_buffer;
|
||||
led_driver->dma_led_transition_timer.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT;
|
||||
led_driver->dma_led_transition_timer.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_HALFWORD;
|
||||
// Data
|
||||
led_driver->dma_led_transition_timer.Mode = LL_DMA_MODE_NORMAL;
|
||||
led_driver->dma_led_transition_timer.NbData = LED_DRIVER_BUFFER_SIZE;
|
||||
// When to perform data exchange
|
||||
led_driver->dma_led_transition_timer.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP;
|
||||
led_driver->dma_led_transition_timer.Priority = LL_DMA_PRIORITY_HIGH;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Allocate and initialize LedDriver structure.
|
||||
* @details This function allocate and initialize LedDriver structure.
|
||||
* @return Pointer to allocated LedDriver structure.
|
||||
*/
|
||||
LedDriver* led_driver_alloc(int count_leds, const GpioPin* gpio) {
|
||||
furi_assert(gpio);
|
||||
furi_assert(count_leds && count_leds <= MAX_LED_COUNT);
|
||||
|
||||
LedDriver* led_driver = malloc(sizeof(LedDriver));
|
||||
led_driver_init_dma_gpio_update(led_driver, gpio);
|
||||
led_driver_init_dma_led_transition_timer(led_driver);
|
||||
led_driver->led_data = malloc(MAX_LED_COUNT * sizeof(uint32_t));
|
||||
|
||||
led_driver->count_leds = count_leds;
|
||||
|
||||
return led_driver;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Frees a led driver.
|
||||
* @details Frees a led driver.
|
||||
* @param led_driver The led driver to free.
|
||||
*/
|
||||
void led_driver_free(LedDriver* led_driver) {
|
||||
furi_assert(led_driver);
|
||||
|
||||
furi_hal_gpio_init_simple(led_driver->gpio, GpioModeAnalog);
|
||||
free(led_driver->led_data);
|
||||
free(led_driver);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the LED at the given index to the given color.
|
||||
* @note You must still call led_driver_transmit to actually update the LEDs.
|
||||
* @param led_driver The led driver to use.
|
||||
* @param index The index of the LED to set.
|
||||
* @param rrggbb The color to set the LED to (0xrrggbb format).
|
||||
* @return The previous color of the LED (0xrrggbb format).
|
||||
*/
|
||||
uint32_t led_driver_set_led(LedDriver* led_driver, uint32_t index, uint32_t rrggbb) {
|
||||
furi_assert(led_driver);
|
||||
if(index >= led_driver->count_leds) {
|
||||
return 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
uint32_t previous = led_driver->led_data[index];
|
||||
led_driver->led_data[index] = rrggbb;
|
||||
return previous;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the LED at the given index.
|
||||
* @param led_driver The led driver to use.
|
||||
* @param index The index of the LED to get.
|
||||
* @return The color of the LED (0xrrggbb format).
|
||||
*/
|
||||
uint32_t led_driver_get_led(LedDriver* led_driver, uint32_t index) {
|
||||
furi_assert(led_driver);
|
||||
if(index >= led_driver->count_leds) {
|
||||
return 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
return led_driver->led_data[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initializes the DMA for GPIO pin toggle and led transititions.
|
||||
* @param led_driver The led driver to initialize.
|
||||
*/
|
||||
static void led_driver_start_dma(LedDriver* led_driver) {
|
||||
furi_assert(led_driver);
|
||||
|
||||
LL_DMA_Init(DMA1, LL_DMA_CHANNEL_1, &led_driver->dma_gpio_update);
|
||||
LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &led_driver->dma_led_transition_timer);
|
||||
|
||||
LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1);
|
||||
LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Stops the DMA for GPIO pin toggle and led transititions.
|
||||
* @param led_driver The led driver to initialize.
|
||||
*/
|
||||
static void led_driver_stop_dma() {
|
||||
LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1);
|
||||
LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2);
|
||||
LL_DMA_ClearFlag_TC1(DMA1);
|
||||
LL_DMA_ClearFlag_TC2(DMA1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Starts the timer for led transitions.
|
||||
* @param led_driver The led driver to initialize.
|
||||
*/
|
||||
static void led_driver_start_timer() {
|
||||
furi_hal_bus_enable(FuriHalBusTIM2);
|
||||
|
||||
LL_TIM_SetCounterMode(TIM2, LL_TIM_COUNTERMODE_UP);
|
||||
LL_TIM_SetClockDivision(TIM2, LL_TIM_CLOCKDIVISION_DIV1);
|
||||
LL_TIM_SetPrescaler(TIM2, 0);
|
||||
// Updated by led_driver->dma_led_transition_timer.PeriphOrM2MSrcAddress
|
||||
LL_TIM_SetAutoReload(TIM2, LED_DRIVER_TIMER_SETINEL);
|
||||
LL_TIM_SetCounter(TIM2, 0);
|
||||
|
||||
LL_TIM_EnableCounter(TIM2);
|
||||
LL_TIM_EnableUpdateEvent(TIM2);
|
||||
LL_TIM_EnableDMAReq_UPDATE(TIM2);
|
||||
LL_TIM_GenerateEvent_UPDATE(TIM2);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Stops the timer for led transitions.
|
||||
* @param led_driver The led driver to initialize.
|
||||
*/
|
||||
static void led_driver_stop_timer() {
|
||||
LL_TIM_DisableCounter(TIM2);
|
||||
LL_TIM_DisableUpdateEvent(TIM2);
|
||||
LL_TIM_DisableDMAReq_UPDATE(TIM2);
|
||||
furi_hal_bus_disable(FuriHalBusTIM2);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Waits for the DMA to complete.
|
||||
* @param led_driver The led driver to use.
|
||||
*/
|
||||
static void led_driver_spin_lock(LedDriver* led_driver) {
|
||||
const uint32_t prev_timer = DWT->CYCCNT;
|
||||
const uint32_t wait_time = LED_DRIVER_SETINEL_WAIT_MS * SystemCoreClock / 1000;
|
||||
|
||||
do {
|
||||
/* Make sure it's started (allow 100 ticks), but then check for sentinel value. */
|
||||
if(TIM2->ARR == LED_DRIVER_TIMER_SETINEL && DWT->CYCCNT - prev_timer > 100) {
|
||||
break;
|
||||
}
|
||||
|
||||
// We should have seen it above, but just in case we also have a timeout.
|
||||
if((DWT->CYCCNT - prev_timer > wait_time)) {
|
||||
FURI_LOG_E(
|
||||
TAG,
|
||||
"0x%04x not found (ARR 0x%08lx, read %lu)",
|
||||
LED_DRIVER_TIMER_SETINEL,
|
||||
TIM2->ARR,
|
||||
led_driver->read_pos);
|
||||
led_driver->read_pos = led_driver->write_pos - 1;
|
||||
break;
|
||||
}
|
||||
} while(true);
|
||||
}
|
||||
|
||||
static void led_driver_add_period_length(LedDriver* led_driver, uint32_t length) {
|
||||
led_driver->timer_buffer[led_driver->write_pos++] = length;
|
||||
led_driver->timer_buffer[led_driver->write_pos] = LED_DRIVER_TIMER_SETINEL;
|
||||
}
|
||||
|
||||
static void led_driver_add_period(LedDriver* led_driver, uint16_t duration_ns) {
|
||||
furi_assert(led_driver);
|
||||
|
||||
uint32_t reload_value = duration_ns / LED_DRIVER_TIMER_NANOSECOND;
|
||||
|
||||
if(reload_value > 255) {
|
||||
FURI_LOG_E(TAG, "reload_value: %ld", reload_value);
|
||||
}
|
||||
furi_check(reload_value > 0);
|
||||
furi_check(reload_value < 256 * 256);
|
||||
|
||||
led_driver_add_period_length(led_driver, reload_value - 1);
|
||||
}
|
||||
|
||||
static void led_driver_add_color(LedDriver* led_driver, uint32_t rrggbb) {
|
||||
UNUSED(rrggbb);
|
||||
|
||||
uint32_t ggrrbb = (rrggbb & 0xFF) | ((rrggbb & 0xFF00) << 8) | ((rrggbb & 0xFF0000) >> 8);
|
||||
|
||||
for(int i = 23; i >= 0; i--) {
|
||||
if(ggrrbb & (1 << i)) {
|
||||
led_driver_add_period(led_driver, LED_DRIVER_T0L);
|
||||
led_driver_add_period(led_driver, LED_DRIVER_T1L);
|
||||
} else {
|
||||
led_driver_add_period(led_driver, LED_DRIVER_T0H);
|
||||
led_driver_add_period(led_driver, LED_DRIVER_T1H);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send the LED data to the LEDs.
|
||||
* @param led_driver The led driver to use.
|
||||
*/
|
||||
void led_driver_transmit(LedDriver* led_driver) {
|
||||
furi_assert(led_driver);
|
||||
|
||||
furi_assert(!led_driver->read_pos);
|
||||
furi_assert(!led_driver->write_pos);
|
||||
|
||||
furi_hal_gpio_init(led_driver->gpio, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
|
||||
furi_hal_gpio_write(led_driver->gpio, false);
|
||||
|
||||
const uint32_t bit_set = led_driver->gpio->pin << GPIO_BSRR_BS0_Pos;
|
||||
const uint32_t bit_reset = led_driver->gpio->pin << GPIO_BSRR_BR0_Pos;
|
||||
|
||||
// Always start with HIGH
|
||||
led_driver->gpio_buf[0] = bit_set;
|
||||
led_driver->gpio_buf[1] = bit_reset;
|
||||
|
||||
for(size_t i = 0; i < LED_DRIVER_BUFFER_SIZE; i++) {
|
||||
led_driver->timer_buffer[i] = LED_DRIVER_TIMER_SETINEL;
|
||||
}
|
||||
for(size_t i = 0; i < led_driver->count_leds; i++) {
|
||||
led_driver_add_color(led_driver, led_driver->led_data[i]);
|
||||
}
|
||||
led_driver_add_period(led_driver, LED_DRIVER_TDONE);
|
||||
led_driver->dma_led_transition_timer.NbData = led_driver->write_pos + 1;
|
||||
|
||||
FURI_CRITICAL_ENTER();
|
||||
|
||||
led_driver_start_dma(led_driver);
|
||||
led_driver_start_timer();
|
||||
|
||||
led_driver_spin_lock(led_driver);
|
||||
|
||||
led_driver_stop_timer();
|
||||
led_driver_stop_dma();
|
||||
|
||||
FURI_CRITICAL_EXIT();
|
||||
|
||||
memset(led_driver->timer_buffer, LED_DRIVER_TIMER_SETINEL, LED_DRIVER_BUFFER_SIZE);
|
||||
led_driver->read_pos = 0;
|
||||
led_driver->write_pos = 0;
|
||||
}
|
||||
42
js/flipboard/modules/js_rgbleds/led_driver.h
Normal file
42
js/flipboard/modules/js_rgbleds/led_driver.h
Normal file
@@ -0,0 +1,42 @@
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
typedef struct LedDriver LedDriver;
|
||||
|
||||
/**
|
||||
* @brief Allocate and initialize LedDriver structure.
|
||||
* @details This function allocate and initialize LedDriver structure.
|
||||
* @return Pointer to allocated LedDriver structure.
|
||||
*/
|
||||
LedDriver* led_driver_alloc(int count_leds, const GpioPin* gpio);
|
||||
|
||||
/**
|
||||
* @brief Frees a led driver.
|
||||
* @details Frees a led driver.
|
||||
* @param led_driver The led driver to free.
|
||||
*/
|
||||
void led_driver_free(LedDriver* led_driver);
|
||||
|
||||
/**
|
||||
* @brief Sets the LED at the given index to the given color.
|
||||
* @note You must still call led_driver_transmit to actually update the LEDs.
|
||||
* @param led_driver The led driver to use.
|
||||
* @param index The index of the LED to set.
|
||||
* @param rrggbb The color to set the LED to.
|
||||
* @return The previous color of the LED.
|
||||
*/
|
||||
uint32_t led_driver_set_led(LedDriver* led_driver, uint32_t index, uint32_t rrggbb);
|
||||
|
||||
/**
|
||||
* @brief Gets the LED at the given index.
|
||||
* @param led_driver The led driver to use.
|
||||
* @param index The index of the LED to get.
|
||||
* @return The color of the LED (0xrrggbb format).
|
||||
*/
|
||||
uint32_t led_driver_get_led(LedDriver* led_driver, uint32_t index);
|
||||
|
||||
/**
|
||||
* @brief Send the LED data to the LEDs.
|
||||
* @param led_driver The led driver to use.
|
||||
*/
|
||||
void led_driver_transmit(LedDriver* led_driver);
|
||||
26
js/flipboard/modules/js_rgbleds/led_driver_i.h
Normal file
26
js/flipboard/modules/js_rgbleds/led_driver_i.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <stm32wbxx_ll_dma.h>
|
||||
#include "led_driver.h"
|
||||
|
||||
#define MAX_LED_COUNT 16
|
||||
|
||||
// We store the HIGH/LOW durations (2 values) for each color bit (24 bits per LED)
|
||||
#define LED_DRIVER_BUFFER_SIZE (MAX_LED_COUNT * 2 * 24)
|
||||
|
||||
// We use a setinel value to figure out when the timer is complete.
|
||||
#define LED_DRIVER_TIMER_SETINEL 0xFFFFU
|
||||
|
||||
/** 64 transitions per us @ 64MHz. Our timing is in NANO_SECONDS */
|
||||
#define LED_DRIVER_TIMER_NANOSECOND (1000U / (SystemCoreClock / 1000000U))
|
||||
|
||||
// Timings for WS2812B
|
||||
#define LED_DRIVER_T0H 400U
|
||||
#define LED_DRIVER_T1H 800U
|
||||
#define LED_DRIVER_T0L 850U
|
||||
#define LED_DRIVER_T1L 450U
|
||||
#define LED_DRIVER_TRESETL 55 * 1000U
|
||||
#define LED_DRIVER_TDONE 2000U
|
||||
|
||||
// Max wait for the DMA to complete. NOTE: 4000 leds*(850ns+450ns)*24 = 124.8ms + 50ms blanking = 174.8ms
|
||||
#define LED_DRIVER_SETINEL_WAIT_MS 200
|
||||
131
js/flipboard/modules/js_rgbleds/rgbleds.c
Normal file
131
js/flipboard/modules/js_rgbleds/rgbleds.c
Normal file
@@ -0,0 +1,131 @@
|
||||
#include <furi_hal.h>
|
||||
|
||||
#include "rgbleds.h"
|
||||
#include "led_driver.h"
|
||||
|
||||
#define LED_COUNT 16
|
||||
|
||||
struct RgbLeds {
|
||||
uint16_t num_leds;
|
||||
uint32_t* color;
|
||||
uint16_t brightness;
|
||||
LedDriver* led_driver;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Allocates a RgbLeds struct.
|
||||
* @details This method allocates a RgbLeds struct. This is used to
|
||||
* control the addressable LEDs.
|
||||
* @param num_leds The number of LEDs to allocate.
|
||||
* @param leds_pin The GPIO pin to use for the LEDs. (&gpio_ext_pc3)
|
||||
* @return The allocated RgbLeds struct.
|
||||
*/
|
||||
RgbLeds* rgbleds_alloc(uint16_t num_leds, const GpioPin* const leds_pin) {
|
||||
RgbLeds* leds = malloc(sizeof(RgbLeds));
|
||||
leds->num_leds = num_leds;
|
||||
leds->color = malloc(sizeof(uint32_t) * leds->num_leds);
|
||||
leds->brightness = 255;
|
||||
leds->led_driver = led_driver_alloc(leds->num_leds, leds_pin);
|
||||
|
||||
rgbleds_reset(leds);
|
||||
return leds;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Frees a RgbLeds struct.
|
||||
* @param leds The RgbLeds struct to free.
|
||||
*/
|
||||
void rgbleds_free(RgbLeds* leds) {
|
||||
if(leds->led_driver) {
|
||||
led_driver_free(leds->led_driver);
|
||||
}
|
||||
free(leds->color);
|
||||
free(leds);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Resets the LEDs to their default color pattern (off).
|
||||
* @details This method resets the LEDs data to their default color pattern (off).
|
||||
* You must still call rgbleds_update to update the LEDs.
|
||||
* @param leds The RgbLeds struct to reset.
|
||||
*/
|
||||
void rgbleds_reset(RgbLeds* leds) {
|
||||
for(int i = 0; i < leds->num_leds; i++) {
|
||||
leds->color[i] = 0x000000;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the color of the LEDs.
|
||||
* @details This method sets the color of the LEDs.
|
||||
* @param leds The RgbLeds struct to set the color of.
|
||||
* @param led The LED index to set the color of.
|
||||
* @param color The color to set the LED to (Hex value: RRGGBB).
|
||||
* @return True if the LED was set, false if the LED was out of range.
|
||||
*/
|
||||
bool rgbleds_set(RgbLeds* leds, uint16_t led, uint32_t color) {
|
||||
if(led > leds->num_leds) {
|
||||
return false;
|
||||
}
|
||||
|
||||
leds->color[led] = color;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the color of the LEDs.
|
||||
* @details This method gets the color of the LEDs.
|
||||
* @param leds The RgbLeds struct to get the color of.
|
||||
* @param led The LED index to get the color of.
|
||||
* @return The color of the LED (Hex value: RRGGBB).
|
||||
*/
|
||||
uint32_t rgbleds_get(RgbLeds* leds, uint16_t led) {
|
||||
if(led > leds->num_leds) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return leds->color[led];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the brightness of the LEDs.
|
||||
* @details This method sets the brightness of the LEDs.
|
||||
* @param leds The RgbLeds struct to set the brightness of.
|
||||
* @param brightness The brightness to set the LEDs to (0-255).
|
||||
*/
|
||||
void rgbleds_set_brightness(RgbLeds* leds, uint8_t brightness) {
|
||||
leds->brightness = brightness;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adjusts the brightness of a color.
|
||||
* @details This method adjusts the brightness of a color.
|
||||
* @param color The color to adjust.
|
||||
* @param brightness The brightness to adjust the color to (0-255).
|
||||
* @return The adjusted color.
|
||||
*/
|
||||
static uint32_t adjust_color_brightness(uint32_t color, uint8_t brightness) {
|
||||
uint32_t red = (color & 0xFF0000) >> 16;
|
||||
uint32_t green = (color & 0x00FF00) >> 8;
|
||||
uint32_t blue = (color & 0x0000FF);
|
||||
|
||||
red = (red * brightness) / 255;
|
||||
green = (green * brightness) / 255;
|
||||
blue = (blue * brightness) / 255;
|
||||
|
||||
return (red << 16) | (green << 8) | blue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Updates the LEDs.
|
||||
* @details This method changes the LEDs to the colors set by rgbleds_set.
|
||||
* @param leds The RgbLeds struct to update.
|
||||
*/
|
||||
void rgbleds_update(RgbLeds* leds) {
|
||||
for(int i = 0; i < leds->num_leds; i++) {
|
||||
uint32_t color = adjust_color_brightness(leds->color[i], leds->brightness);
|
||||
led_driver_set_led(leds->led_driver, i, color);
|
||||
}
|
||||
|
||||
led_driver_transmit(leds->led_driver);
|
||||
}
|
||||
63
js/flipboard/modules/js_rgbleds/rgbleds.h
Normal file
63
js/flipboard/modules/js_rgbleds/rgbleds.h
Normal file
@@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
|
||||
#include <furi_hal.h>
|
||||
|
||||
typedef struct RgbLeds RgbLeds;
|
||||
|
||||
/**
|
||||
* @brief Allocates a RgbLeds struct.
|
||||
* @details This method allocates a RgbLeds struct. This is used to
|
||||
* control the addressable LEDs.
|
||||
* @param num_leds The number of LEDs to allocate.
|
||||
* @param leds_pin The GPIO pin to use for the LEDs. (&gpio_ext_pc3)
|
||||
* @return The allocated RgbLeds struct.
|
||||
*/
|
||||
RgbLeds* rgbleds_alloc(uint16_t num_leds, const GpioPin* const leds_pin);
|
||||
|
||||
/**
|
||||
* @brief Frees a RgbLeds struct.
|
||||
* @param leds The RgbLeds struct to free.
|
||||
*/
|
||||
void rgbleds_free(RgbLeds* leds);
|
||||
|
||||
/**
|
||||
* @brief Resets the LEDs to their default color pattern (off).
|
||||
* @details This method resets the LEDs data to their default color pattern (off).
|
||||
* You must still call rgbleds_update to update the LEDs.
|
||||
* @param leds The RgbLeds struct to reset.
|
||||
*/
|
||||
void rgbleds_reset(RgbLeds* leds);
|
||||
|
||||
/**
|
||||
* @brief Sets the color of the LEDs.
|
||||
* @details This method sets the color of the LEDs.
|
||||
* @param leds The RgbLeds struct to set the color of.
|
||||
* @param led The LED index to set the color of.
|
||||
* @param color The color to set the LED to (Hex value: RRGGBB).
|
||||
* @return True if the LED was set, false if the LED was out of range.
|
||||
*/
|
||||
bool rgbleds_set(RgbLeds* leds, uint16_t led, uint32_t color);
|
||||
|
||||
/**
|
||||
* @brief Gets the color of the LEDs.
|
||||
* @details This method gets the color of the LEDs.
|
||||
* @param leds The RgbLeds struct to get the color of.
|
||||
* @param led The LED index to get the color of.
|
||||
* @return The color of the LED (Hex value: RRGGBB).
|
||||
*/
|
||||
uint32_t rgbleds_get(RgbLeds* leds, uint16_t led);
|
||||
|
||||
/**
|
||||
* @brief Sets the brightness of the LEDs.
|
||||
* @details This method sets the brightness of the LEDs.
|
||||
* @param leds The RgbLeds struct to set the brightness of.
|
||||
* @param brightness The brightness to set the LEDs to (0-255).
|
||||
*/
|
||||
void rgbleds_set_brightness(RgbLeds* leds, uint8_t brightness);
|
||||
|
||||
/**
|
||||
* @brief Updates the LEDs.
|
||||
* @details This method changes the LEDs to the colors set by rgbleds_set.
|
||||
* @param leds The RgbLeds struct to update.
|
||||
*/
|
||||
void rgbleds_update(RgbLeds* leds);
|
||||
210
js/flipboard/modules/js_speaker.c
Normal file
210
js/flipboard/modules/js_speaker.c
Normal file
@@ -0,0 +1,210 @@
|
||||
#include "../js_modules.h"
|
||||
#include <furi_hal.h>
|
||||
|
||||
typedef struct {
|
||||
bool acquired;
|
||||
} JsSpeakerInst;
|
||||
|
||||
static void js_speaker_acquire(struct mjs* mjs) {
|
||||
mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0);
|
||||
JsSpeakerInst* speaker = mjs_get_ptr(mjs, obj_inst);
|
||||
furi_assert(speaker);
|
||||
|
||||
if(mjs_nargs(mjs) != 1) {
|
||||
mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "Invalid args (timeoutMs)");
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!mjs_is_number(mjs_arg(mjs, 0))) {
|
||||
mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "Invalid numeric arg (timeoutMs)");
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t timeout = mjs_get_int(mjs, mjs_arg(mjs, 0));
|
||||
|
||||
if(!speaker->acquired) {
|
||||
speaker->acquired = furi_hal_speaker_acquire(timeout);
|
||||
}
|
||||
|
||||
mjs_return(mjs, mjs_mk_boolean(mjs, speaker->acquired));
|
||||
}
|
||||
|
||||
static void js_speaker_release(struct mjs* mjs) {
|
||||
mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0);
|
||||
JsSpeakerInst* speaker = mjs_get_ptr(mjs, obj_inst);
|
||||
furi_assert(speaker);
|
||||
|
||||
if(mjs_nargs(mjs) != 0) {
|
||||
mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "No arguments expected");
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
return;
|
||||
}
|
||||
|
||||
if(speaker->acquired) {
|
||||
furi_hal_speaker_stop();
|
||||
furi_hal_speaker_release();
|
||||
speaker->acquired = false;
|
||||
}
|
||||
|
||||
mjs_return(mjs, mjs_mk_boolean(mjs, true));
|
||||
}
|
||||
|
||||
static void js_speaker_start(struct mjs* mjs) {
|
||||
mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0);
|
||||
JsSpeakerInst* speaker = mjs_get_ptr(mjs, obj_inst);
|
||||
furi_assert(speaker);
|
||||
|
||||
size_t num_args = mjs_nargs(mjs);
|
||||
float frequency;
|
||||
float volume;
|
||||
|
||||
if(num_args == 1) {
|
||||
if(!mjs_is_number(mjs_arg(mjs, 0))) {
|
||||
mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "Invalid numeric arg (freq [, volume])");
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
return;
|
||||
}
|
||||
volume = 1.0;
|
||||
} else if(num_args == 2) {
|
||||
if(!mjs_is_number(mjs_arg(mjs, 0)) || !mjs_is_number(mjs_arg(mjs, 1))) {
|
||||
mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "Invalid numeric arg (freq [, volume])");
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
return;
|
||||
}
|
||||
volume = mjs_get_double(mjs, mjs_arg(mjs, 1));
|
||||
if(volume < 0 || volume > 1) {
|
||||
mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "Invalid volume (0 <= volume <= 1)");
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "Invalid args (freq [, volume])");
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!speaker->acquired) {
|
||||
mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "Speaker must be acquired first");
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
return;
|
||||
}
|
||||
|
||||
frequency = mjs_get_double(mjs, mjs_arg(mjs, 0));
|
||||
|
||||
furi_hal_speaker_start(frequency, volume);
|
||||
|
||||
mjs_return(mjs, mjs_mk_boolean(mjs, true));
|
||||
}
|
||||
|
||||
static void js_speaker_stop(struct mjs* mjs) {
|
||||
mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0);
|
||||
JsSpeakerInst* speaker = mjs_get_ptr(mjs, obj_inst);
|
||||
furi_assert(speaker);
|
||||
|
||||
if(mjs_nargs(mjs) != 0) {
|
||||
mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "No arguments expected");
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!speaker->acquired) {
|
||||
mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "Speaker must be acquired first");
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
return;
|
||||
}
|
||||
|
||||
furi_hal_speaker_stop();
|
||||
|
||||
mjs_return(mjs, mjs_mk_boolean(mjs, true));
|
||||
}
|
||||
|
||||
static void js_speaker_play(struct mjs* mjs) {
|
||||
mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0);
|
||||
JsSpeakerInst* speaker = mjs_get_ptr(mjs, obj_inst);
|
||||
furi_assert(speaker);
|
||||
|
||||
size_t num_args = mjs_nargs(mjs);
|
||||
float frequency;
|
||||
float volume;
|
||||
uint32_t duration;
|
||||
uint32_t timeout = 1000;
|
||||
bool acquired_in_play = false;
|
||||
|
||||
if(num_args != 3) {
|
||||
mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "Invalid args (freq, volume, duration)");
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!mjs_is_number(mjs_arg(mjs, 0)) || !mjs_is_number(mjs_arg(mjs, 1)) ||
|
||||
!mjs_is_number(mjs_arg(mjs, 2))) {
|
||||
mjs_prepend_errorf(
|
||||
mjs, MJS_BAD_ARGS_ERROR, "Invalid numeric arg (freq, volume, duration)");
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
return;
|
||||
}
|
||||
|
||||
frequency = mjs_get_double(mjs, mjs_arg(mjs, 0));
|
||||
volume = mjs_get_double(mjs, mjs_arg(mjs, 1));
|
||||
duration = mjs_get_int(mjs, mjs_arg(mjs, 2));
|
||||
|
||||
if(!speaker->acquired) {
|
||||
acquired_in_play = true;
|
||||
speaker->acquired = furi_hal_speaker_acquire(timeout);
|
||||
}
|
||||
|
||||
if(speaker->acquired) {
|
||||
furi_hal_speaker_start(frequency, volume);
|
||||
furi_delay_ms(duration);
|
||||
furi_hal_speaker_stop();
|
||||
if(acquired_in_play) {
|
||||
furi_hal_speaker_release();
|
||||
speaker->acquired = false;
|
||||
}
|
||||
mjs_return(mjs, mjs_mk_boolean(mjs, true));
|
||||
} else {
|
||||
mjs_return(mjs, mjs_mk_boolean(mjs, false));
|
||||
}
|
||||
}
|
||||
|
||||
static void* js_speaker_create(struct mjs* mjs, mjs_val_t* object) {
|
||||
JsSpeakerInst* speaker = malloc(sizeof(JsSpeakerInst));
|
||||
speaker->acquired = false;
|
||||
mjs_val_t speaker_obj = mjs_mk_object(mjs);
|
||||
mjs_set(mjs, speaker_obj, INST_PROP_NAME, ~0, mjs_mk_foreign(mjs, speaker));
|
||||
mjs_set(mjs, speaker_obj, "acquire", ~0, MJS_MK_FN(js_speaker_acquire));
|
||||
mjs_set(mjs, speaker_obj, "release", ~0, MJS_MK_FN(js_speaker_release));
|
||||
mjs_set(mjs, speaker_obj, "start", ~0, MJS_MK_FN(js_speaker_start));
|
||||
mjs_set(mjs, speaker_obj, "stop", ~0, MJS_MK_FN(js_speaker_stop));
|
||||
mjs_set(mjs, speaker_obj, "play", ~0, MJS_MK_FN(js_speaker_play));
|
||||
*object = speaker_obj;
|
||||
return speaker;
|
||||
}
|
||||
|
||||
static void js_speaker_destroy(void* inst) {
|
||||
JsSpeakerInst* speaker = (JsSpeakerInst*)inst;
|
||||
if(speaker->acquired) {
|
||||
furi_hal_speaker_stop();
|
||||
furi_hal_speaker_release();
|
||||
speaker->acquired = false;
|
||||
}
|
||||
free(speaker);
|
||||
}
|
||||
|
||||
static const JsModuleDescriptor js_speaker_desc = {
|
||||
"speaker",
|
||||
js_speaker_create,
|
||||
js_speaker_destroy,
|
||||
};
|
||||
|
||||
static const FlipperAppPluginDescriptor plugin_descriptor = {
|
||||
.appid = PLUGIN_APP_ID,
|
||||
.ep_api_version = PLUGIN_API_VERSION,
|
||||
.entry_point = &js_speaker_desc,
|
||||
};
|
||||
|
||||
const FlipperAppPluginDescriptor* js_speaker_ep(void) {
|
||||
return &plugin_descriptor;
|
||||
}
|
||||
Reference in New Issue
Block a user