6. Software - dviracachas/Safe-and-Sound GitHub Wiki

Implementación de Software

Una parte importante a escoger del proyecto es el lenguaje en el que se iba a programa el microcontrolador ESP32, siendo esta una decisión importante ya que el lenguaje permite y limita a su vez el alcance del proyecto. Como principales opciones se tenían Micropython y ESP IDF. Evaluando las capacidades de cada uno, se terminó eligiendo el lenguaje nativo de la ESP32, ESP IDF, ya que Micropython al tener una sintaxis más sencilla, no cuenta con la capacidad de programar Bluetooth BLE (Bluetooth Low Energy), siendo esta una de las principales características del proyecto por no decir que la fundamental.

De esta forma, se procede a investigar el uso de este lenguaje nativo basado en C, y el cual guarda muchas características similares de este. Las ventajas que incluye el programar el microcontrolador en este lenguaje es la gran cantidad de librerías existentes ya listas para esta, ya que estas fueron creadas específicamente por Espressif. Además usa el sistema operativo FreeRTOS, el cual permite la realización de diferentes tareas al mismo tiempo para aprovechar el doble núcleo de la ESP32.

De destacar se trabajó la librería de Classic BT, la cual permite la comunicación entre un dispositivo móvil y el chip ESP32 implementado en la placa PCB, siendo este tipo de comunicación la más básica y ,como su nombre lo dice, clásica que se puede encontrar permitiendo la transferencia de archivos y datos en general, además que es más simple de usar que la librería BLE, la cual incluye más componentes a tener en cuenta.

Esta sección de software la distribuimos en tres grandes partes; Audio y comunicación, Detección de accidentes mediante acelerómetro y funcionalidades de los botones.

Detección de accidentes mediante acelerómetro.

#include <stdio.h>
#include "driver/gpio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "freertos/timers.h"
#include "driver/adc.h"
#define led1 2
#define ledR 33
#define ledG 25
#define ledB 26
uint8_t led_level = 0;
static const char *tag = "Main";
TimerHandle_t xTimers;
int interval = 100;
int timerId = 1;
int posx = 0;
int posy = 0;
int posz = 0;

esp_err_t init_led(void);
esp_err_t blink_led(void);
esp_err_t set_timer(void);
esp_err_t set_adc(void);

void vTimerCallback(TimerHandle_t pxTimer)
{

    blink_led();
    posx = adc1_get_raw(ADC1_CHANNEL_4);
    posy = adc1_get_raw(ADC1_CHANNEL_7);
    posz = adc1_get_raw(ADC1_CHANNEL_6);

    ESP_LOGI(tag, "Pos x: %i Pos y: %i Pos z: %i", posx, posy, posz);

    if ((posx > 2400 || posx < 1800)&&(posy > 2400 || posy < 1800)&&(posz > 2000 || posz < 1400))
    
    {

            gpio_set_level(ledR, 1);
           
    
    }
    else
    {

        gpio_set_level(ledR, 0);
        gpio_set_level(ledG, 0);
        gpio_set_level(ledB, 0);
    }

}
void app_main(void)
{
    init_led();
    set_timer();
}

esp_err_t init_led(void)
{
    gpio_reset_pin(led1);
    gpio_set_direction(led1, GPIO_MODE_OUTPUT);

    gpio_reset_pin(ledR);
    gpio_set_direction(ledR, GPIO_MODE_OUTPUT);

    gpio_reset_pin(ledG);
    gpio_set_direction(ledG, GPIO_MODE_OUTPUT);

    gpio_reset_pin(ledB);
    gpio_set_direction(ledB, GPIO_MODE_OUTPUT);
    return ESP_OK;
}
esp_err_t blink_led(void)
{
    led_level = !led_level;
    gpio_set_level(led1, led_level);
    return ESP_OK;
}
esp_err_t set_timer(void)
{

    xTimers = xTimerCreate("Timer",                   // Just a text name, not used by the kernel.
                           (pdMS_TO_TICKS(interval)), // The timer period in ticks.
                           pdTRUE,                    // The timers will auto-reload themselves when they expire.
                           (void *)timerId,           // Assign each timer a unique id equal to its array index.
                           vTimerCallback             // Each timer calls the same callback when it expires.
    );

    if (xTimers == NULL)
    {
    }
    else
    {
        if (xTimerStart(xTimers, 0) != pdPASS)
        {
        }
    }
    return ESP_OK;
}
esp_err_t set_adc(void)
{
    return ESP_OK;

    adc1_config_channel_atten(ADC1_CHANNEL_4, ADC_ATTEN_DB_11);
    adc1_config_width(ADC_WIDTH_BIT_12);

    adc1_config_channel_atten(ADC1_CHANNEL_7, ADC_ATTEN_DB_11);
    adc1_config_width(ADC_WIDTH_BIT_12);

    adc1_config_channel_atten(ADC1_CHANNEL_6, ADC_ATTEN_DB_11);
    adc1_config_width(ADC_WIDTH_BIT_12);
}

Implementación de botones

A continuación se muestra el conteo de segundos en que un botón esta presionado y las acciones que este genera cuando cierta cantidad de segundos pasa.

#include <stdio.h>
#include "driver/gpio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
 
#define ESP_INTR_FLAG_DEFAULT 0
 
#define BLINK_LED 12
#define GPIO_INPUT_IO_0 25
int buttonCount = 0;
int i = 0;
 
SemaphoreHandle_t xSemaphore = NULL;
 
TaskHandle_t printVariableTask = NULL;
 
void printVariable(void *pvParameter) {
 
    int a = (int) pvParameter;
    while (1) {
 
        printf("A is a: %d \n", a++);
        vTaskDelay(1000 / portTICK_RATE_MS);
        
    }
}
// interrupt service routine, called when the button is pressed
void IRAM_ATTR button_isr_handler(void* arg) {
 
    // notify the button task
    xSemaphoreGiveFromISR(xSemaphore, NULL);
 
}
// task that will react to button clicks
void button_task(void* arg) {
 
    // infinite loop
    for(;;) {
 
        // wait for the notification from the ISR
        if(xSemaphoreTake(xSemaphore,portMAX_DELAY) == pdTRUE) {
            int buttonState = gpio_get_level(GPIO_INPUT_IO_0);
 
            while(gpio_get_level(GPIO_INPUT_IO_0) == 1) { //code stucks here!!!!
                
                buttonCount++;
                printf("GPIO_INPUT_IO_0 %d\n", buttonState);
                printf("Button pressed! %d \n", buttonCount);
                vTaskDelay(1000 / portTICK_RATE_MS);
                
                if (buttonCount>=5){
                    buttonCount=0;
                }
                else if (buttonCount==2)
                {
                    gpio_set_level(BLINK_LED, 1);
                    vTaskDelay(200 / portTICK_RATE_MS);
                    gpio_set_level(BLINK_LED, 0);
                    vTaskDelay(200 / portTICK_RATE_MS);
                    gpio_set_level(BLINK_LED, 1);
                    vTaskDelay(200 / portTICK_RATE_MS);
                    gpio_set_level(BLINK_LED, 0);
                    vTaskDelay(200 / portTICK_RATE_MS);
                    gpio_set_level(BLINK_LED, 1);
                    vTaskDelay(200 / portTICK_RATE_MS);
                    gpio_set_level(BLINK_LED, 0);
                    buttonCount=0;
                }
                
                }
            //buttonCount=0; aqui sirve a medias (random) / mejor acá
        }
         //buttonCount=0; aqui sirve a medias (random)
    }
    
}
 
void app_main()
{
    // create the binary semaphore
    xSemaphore = xSemaphoreCreateBinary();
 
    // configure button and led pins as GPIO pins
    gpio_pad_select_gpio(GPIO_INPUT_IO_0);
    gpio_pad_select_gpio(BLINK_LED);
 
    // set the correct direction
    gpio_set_direction(GPIO_INPUT_IO_0, GPIO_MODE_INPUT);
    gpio_set_direction(BLINK_LED, GPIO_MODE_OUTPUT);
 
    // enable interrupt on falling (1->0) edge for button pin
    gpio_set_intr_type(GPIO_INPUT_IO_0, GPIO_INTR_POSEDGE);
 
    // start the task that will handle the button
    xTaskCreate(button_task, "button_task", 2048, NULL, 10, NULL);
    // install ISR service with default configuration
    gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);
 
    // attach the interrupt service routine
    gpio_isr_handler_add(GPIO_INPUT_IO_0, button_isr_handler, NULL);
 
    int pass = 25;
    xTaskCreate(&printVariable, "printVariable", 2048, (void*) pass, 5, &printVariableTask);
 
}

Implementación de Sistema de audio mediante Bluetooth

// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "nvs.h"
#include "nvs_flash.h"
#include "esp_system.h"
#include "esp_log.h"

#include "esp_bt.h"
#include "bt_app_core.h"
#include "bt_app_av.h"
#include "esp_bt_main.h"
#include "esp_bt_device.h"
#include "esp_gap_bt_api.h"
#include "esp_a2dp_api.h"
#include "esp_avrc_api.h"
#include "driver/i2s.h"

/* event for handler "bt_av_hdl_stack_up */
enum {
    BT_APP_EVT_STACK_UP = 0,
};

/* handler for bluetooth stack enabled events */
static void bt_av_hdl_stack_evt(uint16_t event, void *p_param);


void app_main()
{
    /* Initialize NVS — it is used to store PHY calibration data */
    esp_err_t err = nvs_flash_init();
    if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
        ESP_ERROR_CHECK(nvs_flash_erase());
        err = nvs_flash_init();
    }
    ESP_ERROR_CHECK(err);

    i2s_config_t i2s_config = {
#ifdef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
        .mode = I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN,
#else
        .mode = I2S_MODE_MASTER | I2S_MODE_TX,                                  // Only TX
#endif
        .sample_rate = 44100,
        .bits_per_sample = 16,
        .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,                           //2-channels
        .communication_format = I2S_COMM_FORMAT_I2S_MSB,
        .dma_buf_count = 6,
        .dma_buf_len = 60,
        .intr_alloc_flags = 0,                                                  //Default interrupt priority
        .tx_desc_auto_clear = true                                              //Auto clear tx descriptor on underflow
    };


    i2s_driver_install(0, &i2s_config, 0, NULL);
#ifdef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
    i2s_set_dac_mode(I2S_DAC_CHANNEL_BOTH_EN);
    i2s_set_pin(0, NULL);
#else
    i2s_pin_config_t pin_config = {
        .bck_io_num = CONFIG_EXAMPLE_I2S_BCK_PIN,
        .ws_io_num = CONFIG_EXAMPLE_I2S_LRCK_PIN,
        .data_out_num = CONFIG_EXAMPLE_I2S_DATA_PIN,
        .data_in_num = -1                                                       //Not used
    };

    i2s_set_pin(0, &pin_config);
#endif


    ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_BLE));

    esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
    if ((err = esp_bt_controller_init(&bt_cfg)) != ESP_OK) {
        ESP_LOGE(BT_AV_TAG, "%s initialize controller failed: %s\n", __func__, esp_err_to_name(err));
        return;
    }

    if ((err = esp_bt_controller_enable(ESP_BT_MODE_CLASSIC_BT)) != ESP_OK) {
        ESP_LOGE(BT_AV_TAG, "%s enable controller failed: %s\n", __func__, esp_err_to_name(err));
        return;
    }

    if ((err = esp_bluedroid_init()) != ESP_OK) {
        ESP_LOGE(BT_AV_TAG, "%s initialize bluedroid failed: %s\n", __func__, esp_err_to_name(err));
        return;
    }

    if ((err = esp_bluedroid_enable()) != ESP_OK) {
        ESP_LOGE(BT_AV_TAG, "%s enable bluedroid failed: %s\n", __func__, esp_err_to_name(err));
        return;
    }

    /* create application task */
    bt_app_task_start_up();

    /* Bluetooth device name, connection mode and profile set up */
    bt_app_work_dispatch(bt_av_hdl_stack_evt, BT_APP_EVT_STACK_UP, NULL, 0, NULL);

#if (CONFIG_BT_SSP_ENABLED == true)
    /* Set default parameters for Secure Simple Pairing */
    esp_bt_sp_param_t param_type = ESP_BT_SP_IOCAP_MODE;
    esp_bt_io_cap_t iocap = ESP_BT_IO_CAP_IO;
    esp_bt_gap_set_security_param(param_type, &iocap, sizeof(uint8_t));
#endif

    /*
     * Set default parameters for Legacy Pairing
     * Use fixed pin code
     */
    esp_bt_pin_type_t pin_type = ESP_BT_PIN_TYPE_FIXED;
    esp_bt_pin_code_t pin_code;
    pin_code[0] = '1';
    pin_code[1] = '2';
    pin_code[2] = '3';
    pin_code[3] = '4';
    esp_bt_gap_set_pin(pin_type, 4, pin_code);

}

void bt_app_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param)
{
    switch (event) {
    case ESP_BT_GAP_AUTH_CMPL_EVT: {
        if (param->auth_cmpl.stat == ESP_BT_STATUS_SUCCESS) {
            ESP_LOGI(BT_AV_TAG, "authentication success: %s", param->auth_cmpl.device_name);
            esp_log_buffer_hex(BT_AV_TAG, param->auth_cmpl.bda, ESP_BD_ADDR_LEN);
        } else {
            ESP_LOGE(BT_AV_TAG, "authentication failed, status:%d", param->auth_cmpl.stat);
        }
        break;
    }

#if (CONFIG_BT_SSP_ENABLED == true)
    case ESP_BT_GAP_CFM_REQ_EVT:
        ESP_LOGI(BT_AV_TAG, "ESP_BT_GAP_CFM_REQ_EVT Please compare the numeric value: %d", param->cfm_req.num_val);
        esp_bt_gap_ssp_confirm_reply(param->cfm_req.bda, true);
        break;
    case ESP_BT_GAP_KEY_NOTIF_EVT:
        ESP_LOGI(BT_AV_TAG, "ESP_BT_GAP_KEY_NOTIF_EVT passkey:%d", param->key_notif.passkey);
        break;
    case ESP_BT_GAP_KEY_REQ_EVT:
        ESP_LOGI(BT_AV_TAG, "ESP_BT_GAP_KEY_REQ_EVT Please enter passkey!");
        break;
#endif

    default: {
        ESP_LOGI(BT_AV_TAG, "event: %d", event);
        break;
    }
    }
    return;
}
static void bt_av_hdl_stack_evt(uint16_t event, void *p_param)
{
    ESP_LOGD(BT_AV_TAG, "%s evt %d", __func__, event);
    switch (event) {
    case BT_APP_EVT_STACK_UP: {
        /* set up device name */
        char *dev_name = "Safe&SoundTest";
        esp_bt_dev_set_device_name(dev_name);

        esp_bt_gap_register_callback(bt_app_gap_cb);

        /* initialize AVRCP controller */
        esp_avrc_ct_init();
        esp_avrc_ct_register_callback(bt_app_rc_ct_cb);
        /* initialize AVRCP target */
        assert (esp_avrc_tg_init() == ESP_OK);
        esp_avrc_tg_register_callback(bt_app_rc_tg_cb);

        esp_avrc_rn_evt_cap_mask_t evt_set = {0};
        esp_avrc_rn_evt_bit_mask_operation(ESP_AVRC_BIT_MASK_OP_SET, &evt_set, ESP_AVRC_RN_VOLUME_CHANGE);
        assert(esp_avrc_tg_set_rn_evt_cap(&evt_set) == ESP_OK);

        /* initialize A2DP sink */
        esp_a2d_register_callback(&bt_app_a2d_cb);
        esp_a2d_sink_register_data_callback(bt_app_a2d_data_cb);
        esp_a2d_sink_init();

        /* set discoverable and connectable mode, wait to be connected */
        esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE);
        break;
    }
    default:
        ESP_LOGE(BT_AV_TAG, "%s unhandled evt %d", __func__, event);
        break;
    }
}
⚠️ **GitHub.com Fallback** ⚠️