Back to All
Project

QCA402X based Smoke Detector


Skill Level Area of Focus Operating System Platform/Hardware
Intermediate Bluetooth, IoT, Sensors, Smart Home RTOS DragonBoard 410c, QCA 402x WiFi/BLE/ZigBee

This project is designed to interface the QCA4020 development board with a smoke detector and DragonBoard™ 410c development platform from Arrow Electronics.

Objective

The main objective of this project is to use the QCA4020 development board to detect smoke in a home environment and generate alerts in case of emergencies.

Materials Required / Parts List / Tools

Source Code / Source Examples / Application Executable

Additional Resources

Build / Assembly Instructions

This demo is based on the QCLI_demo available in the QCA4020 SDK. In this demo, there is a Peripherals module which includes code to read ADC data from various sensors on the development board. The main idea of this smoke alarm is to use the peripheral module in QCLI_demo, and use its ADC interface to read the value of the smoke sensor. If the value of the smoke alarm is higher than a certain voltage range, set the voltage value of a GPIO on the board to a high level.

1. Modifying the QCLI_demo settings

By default, the peripherals module is not enabled in the QCLI_demo application, so code modification is required to open and use the peripherals module. The module is opened as follows:

1.1 Modify build.batModify build.bat found at the following path:

<path>/target/quartz/demo/QCLI_demo/build/gcc/build.bat
Change
IF /I "%CFG_FEATURE_PERIPHERALS%" == "" (SET CFG_FEATURE_PERIPHERALS=false)
To
IF /I "%CFG_FEATURE_PERIPHERALS%" == "" (SET CFG_FEATURE_PERIPHERALS=true)

1.2 Modify env.configModify env.config found at the following path:

<path>/target/quartz/demo/QCLI_demo/build/gcc/env.config
Change
CFG_FEATURE_PERIPHERALS=false
To
CFG_FEATURE_PERIPHERALS=true

1.3 Build and FlashFollow instructions in the QCLI_demo to build and flash the application on the QCA4020 development board. After programming, remove the programming jumper, reset the board and open the serial port and the following directory appears:

Startup

The "16. Peripherals" option seen above shows that the compilation was successful.

2. Read the ADC data value of the smoke sensor

2.1 Connections

Smoke Sensor PinQCA4020 Development Kit Pin
1 Vcc (+5V)Jumper J1 pin 2 5V
2 GroundJumper J1 Pin 7 AGND
3 Digital OutNot Connected
4 Analog OutJumper J2 Pin 5 Analog Sense

Connecting

2.2 Single reading of ADC dataThe value of the sensor can be read directly by using the command, by entering "16", and the resulting display interface is as follows:

Peripherals

Enter command "4" to read the smoke sensor data as shown:

Reading

This method of operation can only read the data of the ADC once, and cannot read the data of the ADC continuously, so the code needs to be modified accordingly.

2.3 Continuous reading of ADC dataThe continuous reading of values can be realized by the continuous instruction input. Queried in the file

qal.c
The following thread implements the instruction for receiving and processing.
QCLI_Thread
The following function is responsible for receiving and processing related instructions.
QCLI_Process_Input_Data()
Therefore, the modification is mainly performed in this thread, and the modifications are as follows:

First, the following function will be encapsulated, and the data read by the ADC demo will be put into the thread, and the interface for the future smoke value judgment will be written.

QCLI_Adc
The modifications are as follows:

Modify in

adc_demo.c
add interface:
Qcli_Adc
Add print interface


uint32_t Qcli_Adc()
{
   qapi_ADC_Read_Result_t *result;
uint32_t i;
//for (i=0; i < MAX_CHANNELS; i++)
   //{
           result = &chan_result[7].chan_result;
           QCLI_Printf(qcli_adc_group, "ADC[7] = %ddmV \", result->microvolts/1000);
           return result->microvolts/1000;
   //}
}
//Add a print interface:
void printf_a(uint32_t result_adc)
{
   QCLI_Printf(qcli_adc_group, "ADC = %ddmV \", result_adc);
}

Add an interface function in

adc_demo.h
and reference it in
pal.c


        diff --git a/src/adc/adc_demo.h b/src/adc/adc_demo.h
        index 60d9812..26e2869 100644
        --- a/src/adc/adc_demo.h
        +++ b/src/adc/adc_demo.h
        @@ -15,6 +15,8 @@ typedef struct _adc_result {
         } adc_result_t;
         extern adc_result_t chan_result[MAX_CHANNELS];
        +extern uint32_t Qcli_Adc();
        +extern void printf_a(uint32_t result_adc);
         /**
            @brief This function registers the ADC demo commands with QCLI.
        diff --git a/src/qcli/pal.c b/src/qcli/pal.c
        index f8c833d..bd18e6c 100644
        --- a/src/qcli/pal.c
        +++ b/src/qcli/pal.c
        @@ -45,6 +45,7 @@
         #include "ecosystem_demo.h"
         #include "json_demo.h"
         #include "kpi_demo.h"
        +#include "adc_demo.h"
         #ifdef CONFIG_QMESH_DEMO
         #include "qmesh_demo_menu.h"

A while loop, such as shown below, can be used to continuously read the smoke sensor value every 200ms:


        while(1) {
          QCLI_Process_Input_Data(2, "16");
          Process_Command();
          clear_buffer();
          //QCLI_Process_Input_Data(1, " ");
          QCLI_Process_Input_Data(1, "4");
          Process_Command();
          //QCLI_Process_Input_Data(1, " ");
          QCLI_Process_Input_Data(1, "4");
          Process_Command();
          ADC=Qcli_Adc();
          printf_a(ADC);
          //QCLI_Process_Input_Data(1, " ");
          QCLI_Process_Input_Data(1, "3");
          Process_Command();
          qurt_thread_sleep(200);
          };

2.4 Control the high and low levels of GPIO by the value of the smoke sensorThe ADC value read for the smoke sensor is generally 850 mv, so this value is set to be the alarm threshold. GPIO4 is set to high if the value is above this threshold and low if the value is below this threshold. The code is as follows:


        if(ADC > 850)
        {
        QCLI_Process_Input_Data(1, "3");
        Process_Command();
        clear_buffer();
        QCLI_Process_Input_Data(2, "16");
        Process_Command();
        clear_buffer();
        QCLI_Process_Input_Data(2, "11");
        Process_Command();
        clear_buffer();
        QCLI_Process_Input_Data(20, "gpio_tlmm 4 10 1 2 1");
        Process_Command();
        clear_buffer();
        } else {
        QCLI_Process_Input_Data(1, "3");
        Process_Command();
        clear_buffer();
        QCLI_Process_Input_Data(2, "16");
        Process_Command();
        clear_buffer();
        QCLI_Process_Input_Data(2, "11");
        Process_Command();
        clear_buffer();
        QCLI_Process_Input_Data(20, "gpio_tlmm 4 10 1 1 0");
        Process_Command();
        clear_buffer();
        }

3. Receiving data on DragonBoard 410c

The QCA4020 monitors the status of the smoke sensor. When the monitored value is greater than a certain threshold, the level value of GPIO is set to indicate that an event has occurred.

The below steps aim to achieve:

  1. The Dragonboard 410c development board monitors the state change of the GPIO port to which the QCA4020 is connected, thereby generating an interrupt.
  2. Provide a node for the upper layer to view the status of the smoke sensor. One of them indicates that an event has occurred. You can set the node value to 0 by writing to this node.

3.1 Connection Diagram

Connecting

Configure the device treeAdd node on the DragonBoard 410c at the following location:


        /*arch/arm64/boot/dts/qcom/apq8016-sbc.dtsi*/
        qca4020 {
                    compatible = "linux,QCA4020";
                    interrupt-parent = <&msmgpio>;
                    interrupts = <0 36 2>;
                    gpio_int = <&msmgpio 36 0>;
        };

 

3.2 Configuring Makefile

Location of the Makefile:

*/drivers/misc/Makefile*/

Add the following line:

obj-y += qca4020.o

3.3 Drive Implementation


        #include <linux/module.h>
        #include <linux/kernel.h>
        #include <linux/init.h>
        #include <linux/sysfs.h>
        #include <linux/delay.h>
        #include <linux/platform_device.h>
        #include <linux/err.h>
        #include <linux/device.h>
        #include <linux/interrupt.h>
        #include <linux/irq.h>
        #include <linux/of_gpio.h>
        #include <asm/uaccess.h>
        #include <linux/kdev_t.h>
        #include <linux/slab.h>
        #include <linux/workqueue.h>
        struct qca4020_data {
            struct platform_device *pdev;
            int flag;
            int gpio_int;
            int irq;
        };
        static struct qca4020_data* data;
        static irqreturn_t qca4020_interrupt_handler(int irq, void *ptr)
        {
            data->flag = 1;
            return IRQ_HANDLED;
        }
        static ssize_t qca4020_value_store(struct device *dev, struct device_attribute* attr,
                                                    const char *buf, size_t len)
        {
            data->flag = 0;
            return len;
        }
        static ssize_t qca4020_value_show(struct device *dev, struct device_attribute* attr,
                                                    char *buf)
        {
            ssize_t ret = sprintf(buf, "%d\", data->flag);
            return ret;
        }
        static DEVICE_ATTR(value, 0664, qca4020_value_show, qca4020_value_store);
        static int QCA4020_probe(struct platform_device *pdev)
        {
            int result;
            struct device_node* node = pdev->dev.of_node;
            printk("qca4020 probe enter\");
            data = devm_kzalloc(&pdev->dev, sizeof(struct qca4020_data), GFP_KERNEL);
            if (!data) {
                pr_err("%s kzalloc error\", __FUNCTION__);
                return -ENOMEM;
            }
            dev_set_drvdata(&pdev->dev, data);
            data->gpio_int = of_get_named_gpio(node, "gpio_int", 0);
            if (!gpio_is_valid(data->gpio_int)) {
                pr_err("gpio_int not specified\");
                goto err;
            } else {
                result = gpio_request(data->gpio_int, "qca_gpio");
                if (result < 0) {
                    pr_err("Unable to request qca_gpio\");
                    goto err;
                } else {
                    gpio_direction_input(data->gpio_int);
                }
            }
            data->irq = gpio_to_irq(data->gpio_int);
            result = request_irq(data->irq, qca4020_interrupt_handler,
                                                         IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "qca4020_intr", data);
            if (result < 0) {
                pr_err("Unable to request irq\");
                goto err;
            }
            result = sysfs_create_file(&pdev->dev.kobj, &dev_attr_value.attr);
            if (result < 0) {
                printk("sysfs create file failed\");
                goto err;
            }
            printk(KERN_INFO "QCA4020 probe success\");
            return 0;
            err:
            kfree(data);
            printk(KERN_ERR "QCA4020 probe failed\");
            return -EINVAL;
        }
        static int QCA4020_remove(struct platform_device *pdev)
        {
                gpio_free(data->gpio_int);
                kfree(data);
                return 0;
        }
        static struct of_device_id mach_match_table[] = {
            { .compatible = "linux,QCA4020"},
            { },
        };
        static struct platform_driver QCA4020_driver = {
            .probe = QCA4020_probe,
            .remove = QCA4020_remove,
            .driver = {
                .owner = THIS_MODULE,
                .name = "QCA4020GPIO",
                .of_match_table = mach_match_table,
            },
        };
        module_platform_driver(QCA4020_driver);
        MODULE_AUTHOR("[email protected]");
        MODULE_LICENSE("GPL");

Now that you understand how the code is implemented, follow the below steps to build the project.

  1. Download the repository from the GitHub link provided above.
  2. Build and flash the QCA4020 development board with the QCA4020 application.
  3. Set up the DragonBoard 410c, and make all the required software changes.
  4. Connect the smoke detector to the QCA4020 development board as shown above
  5. Connect the QCA4020 development board to the DragonBoard 410c
  6. Turn on the QCA4020 board and the DragonBoard 410c

Usage Instructions

The QCA4020 board is designed to read the signal from the smoke detector. If the value is above a threshold, the board sends the signal to the DragonBoard 410c via the GPIO connection. The DragonBoard 410c is programmed to reads the change in GPIO value and trigger an alert.

Opinions expressed in the content posted here are the personal opinions of the original authors, and do not necessarily reflect those of Qualcomm Incorporated or its subsidiaries ("Qualcomm"). The content is provided for informational purposes only and is not meant to be an endorsement or representation by Qualcomm or any other party. This site may also provide links or references to non-Qualcomm sites and resources. Qualcomm makes no representations, warranties, or other commitments whatsoever about any non-Qualcomm sites or third-party resources that may be referenced, accessible from, or linked to this site.

Project Authors
Thunder Software Technology (Shenzhen) Co., Ltd.
Shenzhen
Scott
Qiliang

Sign up for the Developer Newsletter.

Get software and hardware tool resources to help optimize your development delivered to your inbox weekly.

Qualcomm relentlessly innovates to deliver intelligent computing everywhere, helping the world tackle some of its most important challenges. Our leading-edge AI, high performance, low-power computing, and unrivaled connectivity deliver proven solutions that transform major industries. At Qualcomm, we are engineering human progress.

Stay connected

Get the latest Qualcomm and industry information delivered to your inbox.

Subscribe
Manage your subscription

© Qualcomm Technologies, Inc. and/or its affiliated companies.

Snapdragon and Qualcomm branded products are products of Qualcomm Technologies, Inc. and/or its subsidiaries. Qualcomm patented technologies are licensed by Qualcomm Incorporated.

Note: Certain services and materials may require you to accept additional terms and conditions before accessing or using those items.

References to "Qualcomm" may mean Qualcomm Incorporated, or subsidiaries or business units within the Qualcomm corporate structure, as applicable.

Qualcomm Incorporated includes our licensing business, QTL, and the vast majority of our patent portfolio. Qualcomm Technologies, Inc., a subsidiary of Qualcomm Incorporated, operates, along with its subsidiaries, substantially all of our engineering, research and development functions, and substantially all of our products and services businesses, including our QCT semiconductor business.

Materials that are as of a specific date, including but not limited to press releases, presentations, blog posts and webcasts, may have been superseded by subsequent events or disclosures.

Nothing in these materials is an offer to sell or license any of the services or materials referenced herein.