BLE implementation causing GPIO reset pin to stay High

I am using a GPIO pin (P1.05) as a reset signal for an external CPU component. The pin functions correctly and stays low when tested in a basic GPIO application. However, as soon as I implement the BLE stack, the pin behavior changes: it stays High by default and only toggles Low for a few milliseconds when triggered, before immediately returning High.

It appears something in the BLE initialization or a hardware conflict is pulling this pin High. I have defined the pin in my overlay as GPIO_ACTIVE_HIGH. Is there a known conflict with Port 1 Pin 5 when using the BLE controller, or is my initialization order incorrect?

  • 4251.aram.overlay

    /* src/main.c */
    #include <zephyr/kernel.h>
    #include <zephyr/drivers/gpio.h>
    #include <zephyr/logging/log.h>
    #include <zephyr/drivers/uart.h>
    #include <zephyr/bluetooth/bluetooth.h>
    #include <zephyr/bluetooth/hci.h>
    #include <zephyr/bluetooth/gatt.h>
    #include <zephyr/bluetooth/uuid.h>
    #include <string.h>
    #include <stdio.h>
    
    #include "../inc/uart.h"
    
    LOG_MODULE_REGISTER(aram_app, LOG_LEVEL_INF);
    
    #define VER_STRING  "Ver: Dev 007 -RC007"
    #define HELP_STRING "Commands: Ver, Id, LedOn, LedOff, Reset, Help\r\n"
    
    /* GPIO Setup */
    #define LED0_NODE DT_ALIAS(led0)
    static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(LED0_NODE, gpios);
    
    /* Varicite reset pin - P1.04 on gpio1, via zephyr,user node */
    //static const struct gpio_dt_spec varicite_reset_pin =
    //    GPIO_DT_SPEC_GET(DT_PATH(zephyr_user), varicite_reset_gpios);
    
    static const struct gpio_dt_spec varicite_reset_pin =
       GPIO_DT_SPEC_GET_OR(DT_NODELABEL(varicite_reset_pin), gpios, {0});
    
    extern struct k_msgq uart3_msgq;
    extern const struct bt_gatt_service_static nus_svc;
    
    /* NUS UUIDs */
    #define BT_UUID_NUS_VAL    BT_UUID_128_ENCODE(0x6e400001, 0xb5a3, 0xf393, 0xe0a9, 0xe50e24dcca9e)
    #define BT_UUID_NUS_TX_VAL BT_UUID_128_ENCODE(0x6e400003, 0xb5a3, 0xf393, 0xe0a9, 0xe50e24dcca9e)
    #define BT_UUID_NUS_RX_VAL BT_UUID_128_ENCODE(0x6e400002, 0xb5a3, 0xf393, 0xe0a9, 0xe50e24dcca9e)
    
    static struct bt_uuid_128 nus_tx_uuid = BT_UUID_INIT_128(BT_UUID_NUS_TX_VAL);
    static struct bt_uuid_128 nus_rx_uuid = BT_UUID_INIT_128(BT_UUID_NUS_RX_VAL);
    static bool notify_enabled;
    
    /* ---------------------------------------------------------------
     * Shared command handler - called from both BLE and UART3.
     * response[] is filled with the reply string (null-terminated).
     * Returns the length of the response string.
     * --------------------------------------------------------------- */
    static int  handle_command(const char *cmd_in, char *response, size_t resp_size)
    {
        // 1. Create a mutable pointer and skip leading whitespace
        const char *cmd = cmd_in;
        while (isspace((unsigned char)*cmd)) {
            cmd++;
        }
    
        // 2. Handle trailing CR/LF/Spaces by creating a local copy or 
        // using a temporary buffer if cmd_in needs to stay immutable.
        // For simplicity, let's assume we can work with a local stack copy:
        char clean_cmd[64]; 
        strncpy(clean_cmd, cmd, sizeof(clean_cmd) - 1);
        clean_cmd[sizeof(clean_cmd) - 1] = '\0';
    
        // Trim trailing whitespace/CR/LF
        char *end = clean_cmd + strlen(clean_cmd) - 1;
        while (end >= clean_cmd && isspace((unsigned char)*end)) {
            *end = '\0';
            end--;
        }
    
        // --- Now use 'clean_cmd' for your logic ---
    
        if (strcmp(clean_cmd, "Ver") == 0) {
            snprintf(response, resp_size, VER_STRING "\r\n");
    
        } else if (strcmp(clean_cmd, "Id") == 0) {
            snprintf(response, resp_size, "Id:%s\r\n", CONFIG_BOARD);
    
        } else if (strcmp(clean_cmd, "LedOn") == 0) {
            gpio_pin_set_dt(&led, 1);
            snprintf(response, resp_size, "LED:ON\r\n");
    
        } else if (strcmp(clean_cmd, "LedOff") == 0) {
            gpio_pin_set_dt(&led, 0);
            snprintf(response, resp_size, "LED:OFF\r\n");
    
        } else if (strcmp(clean_cmd, RESET_CMD_ASC) == 0) {
            snprintf(response, resp_size, "Reset:%s", CONFIG_BOARD);
            return 99; 
    
        } else if (strcmp(clean_cmd, "Help") == 0) {
            snprintf(response, resp_size, HELP_STRING);
    
        } else {
            snprintf(response, resp_size, "Unknown:%s\r\n", clean_cmd);
        }
    
        return strlen(response);
    }
    
    /* ---------------------------------------------------------------
     * Hardware reset sequence - hold reset pin for 5 s
     * --------------------------------------------------------------- */
    void trigger_varicite_reset(const char *source)
    {
        LOG_INF("Reset triggered by %s", source);
        gpio_pin_set_dt(&led, 1);
        gpio_pin_set_dt(&varicite_reset_pin, 1);
        k_msleep(5000);
        gpio_pin_set_dt(&led, 0);
        gpio_pin_set_dt(&varicite_reset_pin, 0);
        LOG_INF("Reset complete.");
    }
    
    /* ---------------------------------------------------------------
     * BLE NUS callbacks
     * --------------------------------------------------------------- */
    static void nus_ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
    {
        notify_enabled = (value == BT_GATT_CCC_NOTIFY);
        LOG_INF("BLE notifications %s", notify_enabled ? "enabled" : "disabled");
    }
    
    static ssize_t nus_rx_received(struct bt_conn *conn,
                                   const struct bt_gatt_attr *attr,
                                   const void *buf, uint16_t len,
                                   uint16_t offset, uint8_t flags)
    {
        char incoming[32];
        char response[128];
        uint16_t copy_len = MIN(len, sizeof(incoming) - 1);
    
        memcpy(incoming, buf, copy_len);
        incoming[copy_len] = '\0';
        LOG_INF("BLE RX: %s, - %d", incoming,strlen(incoming));
    
        int cmd_result = handle_command(incoming, response, sizeof(response));
    
        if (notify_enabled) {
            bt_gatt_notify(NULL, &nus_svc.attrs[2], response, strlen(response));
            LOG_INF("BLE TX: %s", response);
        }
    
        /* Perform reset after sending the response */
        if (cmd_result == 99) {
            trigger_varicite_reset("BLE");
        }
    
        return len;
    }
    
    BT_GATT_SERVICE_DEFINE(nus_svc,
        BT_GATT_PRIMARY_SERVICE(BT_UUID_DECLARE_128(BT_UUID_NUS_VAL)),
        BT_GATT_CHARACTERISTIC(&nus_tx_uuid.uuid, BT_GATT_CHRC_NOTIFY,
                               BT_GATT_PERM_NONE, NULL, NULL, NULL),
        BT_GATT_CCC(nus_ccc_cfg_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
        BT_GATT_CHARACTERISTIC(&nus_rx_uuid.uuid,
                               BT_GATT_CHRC_WRITE | BT_GATT_CHRC_WRITE_WITHOUT_RESP,
                               BT_GATT_PERM_WRITE, NULL, nus_rx_received, NULL),
    );
    
    /* ---------------------------------------------------------------
     * UART3 command handler thread
     * Reads lines from uart3_msgq (filled by uart3 ISR in uart.c)
     * and sends responses back over UART3.
     * --------------------------------------------------------------- */
    void uart3_handler_thread(void *p1, void *p2, void *p3)
    {
        char cmd[128];
        char response[128];
    
        LOG_INF("UART3 handler thread started");
        int cmd_resp = -1;
    
        while (1) {
            if (k_msgq_get(&uart3_msgq, &cmd, K_FOREVER) == 0) {
                LOG_INF("UART3 RX: %s", cmd);
    
                cmd_resp = handle_command(cmd, response, sizeof(response));
    
                /* Send response over UART3 */
                print_uart3(response);
    
                LOG_INF("UART3 TX ---: %s", response);
    
                /* Perform reset after sending the response */
                if (cmd_resp == 99) {
                    trigger_varicite_reset("UART3");
                }
            }
        }
    }
    
    /* ---------------------------------------------------------------
     * UART2 (modem) polling thread - watches for SMS reset command
     * --------------------------------------------------------------- */
    void uart2_poll_thread(void *p1, void *p2, void *p3)
    {
        while (1) {
            if (send_wait_resp("AT+CMGL=\"REC UNREAD\"", RESET_CMD) == 0) {
                LOG_INF("RESET MESSAGE DETECTED via modem");
                trigger_varicite_reset("Modem");
            }
            k_msleep(2000);
        }
    }
    
    K_THREAD_DEFINE(u2_tid, 1024, uart2_poll_thread,   NULL, NULL, NULL, 7, 0, 0);
    K_THREAD_DEFINE(u3_tid, 1024, uart3_handler_thread, NULL, NULL, NULL, 7, 0, 0);
    
    /* ---------------------------------------------------------------
     * Bluetooth ready callback
     * --------------------------------------------------------------- */
    static void bt_ready(int err)
    {
        if (err) {
            LOG_ERR("Bluetooth init failed (err %d)", err);
            return;
        }
        bt_le_adv_start(BT_LE_ADV_CONN_NAME, NULL, 0, NULL, 0);
        LOG_INF("BLE advertising started");
    }
    
    int io_init(void)
    {
    	if (!gpio_is_ready_dt(&led)) return -1;
    	gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE);
    	gpio_pin_set_dt(&led, 0);
    
    	if (!gpio_is_ready_dt(&varicite_reset_pin)) {
    		LOG_ERR("varicite_reset_pin not ready");
    		return -1;
    	}
    	gpio_pin_configure_dt(&varicite_reset_pin, GPIO_OUTPUT_INACTIVE | GPIO_PULL_DOWN);
    	gpio_pin_set_dt(&varicite_reset_pin, 0);
        
    	LOG_INF("varicite_reset_pin P1.04 configured LOW");
    
    	return 0;
    }
    
    /* ---------------------------------------------------------------
     * main
     * --------------------------------------------------------------- */
    int main(void)
    {
        LOG_INF("Starting ARAM application on %s", CONFIG_BOARD);
    
    	io_init();	
        uart_init();
        bt_enable(bt_ready);
    
        return 0;
    }
    
    #include <zephyr/kernel.h>
    #include <zephyr/drivers/gpio.h>
    #include <zephyr/logging/log.h>
    #include <zephyr/drivers/uart.h>
    #include "../inc/uart.h"
    #include "../inc/gpio_demo.h"
    
    LOG_MODULE_REGISTER(aram_uart, LOG_LEVEL_INF);
    
    /* ---------------------------------------------------------------
     * UART2 - Modem (u-blox SARA)
     * --------------------------------------------------------------- */
    #define UART2_NODE DT_NODELABEL(uart2)
    static const struct device *const uart2_dev = DEVICE_DT_GET(UART2_NODE);
    
    #define MSG_SIZE 128
    K_MSGQ_DEFINE(uart_msgq, MSG_SIZE, 100, 4);
    
    static char uart2_rx_buf[MSG_SIZE];
    static int  uart2_rx_pos;
    
    void serial_cb(const struct device *dev, void *user_data)
    {
        uint8_t c;
        if (!uart_irq_update(uart2_dev))    { return; }
        if (!uart_irq_rx_ready(uart2_dev))  { return; }
    
        while (uart_fifo_read(uart2_dev, &c, 1) == 1) {
            if ((c == '\n' || c == '\r') && uart2_rx_pos > 0) {
                uart2_rx_buf[uart2_rx_pos] = '\0';
                k_msgq_put(&uart_msgq, &uart2_rx_buf, K_NO_WAIT);
                uart2_rx_pos = 0;
            } else if (uart2_rx_pos < (sizeof(uart2_rx_buf) - 1)) {
                uart2_rx_buf[uart2_rx_pos++] = c;
            }
        }
    }
    
    void print_uart(unsigned char *buf)
    {
        int msg_len = strlen(buf);
        for (int i = 0; i < msg_len; i++) {
            uart_poll_out(uart2_dev, buf[i]);
        }
    }
    
    void send_command(uint8_t *cmd)
    {
        print_uart(cmd);
    }
    
    int wait_for_command_response(uint8_t *expected_response, k_timeout_t timeout)
    {
        uint8_t rx_buf[MSG_SIZE];
        int ret;
    
        while (1) {
            ret = k_msgq_get(&uart_msgq, rx_buf, timeout);
            if (ret == 0) {
                if (strstr(rx_buf, expected_response) != NULL) {
                    return 0;
                }
            } else if (ret == -EAGAIN) {
                return -EAGAIN;
            } else {
                return -EINVAL;
            }
        }
        return -1;
    }
    
    int send_wait_resp(uint8_t *cmd, uint8_t *resp)
    {
        send_command(cmd);
        print_uart("\r\n");
        return (wait_for_command_response(resp, K_MSEC(100)) == 0) ? 0 : 1;
    }
    
    int get_msg_nbr(char *str)
    {
        int ret = -1;
        char *token = strtok(str, ":");
        if (token != NULL) {
            token = strtok(NULL, ",");
            sscanf(token, "%d", &ret);
        }
        return ret;
    }
    
    /* ---------------------------------------------------------------
     * UART3 - Console / host command interface
     * --------------------------------------------------------------- */
    #define UART3_NODE DT_NODELABEL(uart3)
    static const struct device *const uart3_dev = DEVICE_DT_GET(UART3_NODE);
    
    #define UART3_MSG_SIZE 128
    K_MSGQ_DEFINE(uart3_msgq, UART3_MSG_SIZE, 10, 4);
    
    static char uart3_rx_buf[UART3_MSG_SIZE];
    static int  uart3_rx_pos;
    
    static void uart3_serial_cb(const struct device *dev, void *user_data)
    {
        uint8_t c;
        if (!uart_irq_update(uart3_dev))   { return; }
        if (!uart_irq_rx_ready(uart3_dev)) { return; }
    
        while (uart_fifo_read(uart3_dev, &c, 1) == 1) {
            if ((c == '\n' || c == '\r') && uart3_rx_pos > 0) {
                uart3_rx_buf[uart3_rx_pos] = '\0';
                LOG_INF("UART3 enqueue: %s", uart3_rx_buf);
                k_msgq_put(&uart3_msgq, &uart3_rx_buf, K_NO_WAIT);
                uart3_rx_pos = 0;
            } else if (uart3_rx_pos < (UART3_MSG_SIZE - 1)) {
                uart3_rx_buf[uart3_rx_pos++] = c;
            }
        }
    }
    
    void print_uart3(const char *buf)
    {
        int msg_len = strlen(buf);
        for (int i = 0; i < msg_len; i++) {
            uart_poll_out(uart3_dev, buf[i]);
        }
    }
    
    /* ---------------------------------------------------------------
     * uart_init - initialise both UART2 and UART3
     * --------------------------------------------------------------- */
    int uart_init(void)
    {
        /* UART2 - modem */
        if (!device_is_ready(uart2_dev)) {
            LOG_ERR("UART2 device not ready");
            return -1;
        }
        int ret = uart_irq_callback_user_data_set(uart2_dev, serial_cb, NULL);
        if (ret < 0) {
            LOG_ERR("UART2 IRQ callback set failed: %d", ret);
            return ret;
        }
        uart_irq_rx_enable(uart2_dev);
        LOG_INF("UART2 (modem) initialised");
    
        /* UART3 - console */
        if (!device_is_ready(uart3_dev)) {
            LOG_ERR("UART3 device not ready");
            return -1;
        }
        ret = uart_irq_callback_user_data_set(uart3_dev, uart3_serial_cb, NULL);
        if (ret < 0) {
            LOG_ERR("UART3 IRQ callback set failed: %d", ret);
            return ret;
        }
        uart_irq_rx_enable(uart3_dev);
        LOG_INF("UART3 (console) initialised");
    
        return 0;
    }
    
    int modem_startup_sequence(void)
    {
        int ret;
        
        // Array of commands to simplify the loop, or call them sequentially:
        const char *cmds[] = {
            CMD_KUSBCOMP,
            CMD_TRACE_QUERY,
            CMD_TRACE_AT,
            CMD_CFUN_RESET,
            CMD_TRACE_CUST,
            CMD_CFUN_RESET
        };
    
        for (int i = 0; i < ARRAY_SIZE(cmds); i++) {
            LOG_INF("Sending: %s", cmds[i]);
            
            // Using a 2-second timeout for modem processing
            ret = send_wait_resp((uint8_t *)cmds[i], (uint8_t *)RESP_OK);
            
            if (ret != 0) {
                LOG_WRN("Command %s failed or timed out", cmds[i]);
                // Depending on requirements, you might want to 'return ret' here to abort
            } else {
                LOG_INF("Command %s successful", cmds[i]);
            }
            
            // Optional: Small delay between commands to allow modem buffer to clear
            k_msleep(100); 
        }
    
        return 0;
    }
    4274.prj.conf

  • Hi,

     

    I am using a GPIO pin (P1.05) as a reset signal for an external CPU component. The pin functions correctly and stays low when tested in a basic GPIO application. However, as soon as I implement the BLE stack, the pin behavior changes: it stays High by default and only toggles Low for a few milliseconds when triggered, before immediately returning High.

    Are you using sysbuild and do multi-image builds for the appcore? If yes, then you need to disable gpio forwarding in the sub-images, such as mcuboot.

     

    Kind regards,

    Håkon

  • Thank you for the reply
    The project is not currently configured to use Sysbuild or perform multi-image builds for the network and application cores.Its a standard, single-image Zephyr application.

  • Hi,

     

    Q1: Could you share the generated zephyr.dts file?

    Q2: Can you try to set/clr the gpio at boot-up and see if this works as intended? For testing purposes, to see if the pin actually toggles.

     

    Kind regards,

    Håkon

Related