By the end of this tutorial, you will be able to detect motion and its direction, as well as trigger an interrupt based on detected motion.
The BGT60LTR11AIP shieldOur shield is using 60GHz radar technology to detect motion and its direction in the range of 7m. It offers a small form factor of only 20x6.25mm and a low power consumption, creating innovative, intuitive sensing capabilities for many applications. The small motion sensor integrates antennas and processes the raw radar signal internally which enables operation of the device without any external microcontroller.
Interfacing with the shield is super easy using only four pins:
- Gnd
- Vin (1.5V - 5V)
- P out (= motion direction)
- T out (= motion detection)
The power supply with GND and Vin (1.5V to 5V) can get connected to microcontoller Pins Vcc and GND. The phase detect out (P out) pin represents the direction while the target detect out (T out) pin indicates detected motion from the radar sensor. The BGT60 can easily be plugged onto headers of the Arduino MKR1000 Board (Vcc, GND, T out - A1 and P out - A2).
The library we developed for our 60GHz Radar shield is called "radar-bgt60". Using the Arduino IDE, install the Radar library by going to Sketch -> Include library -> Library Manager. The radar library includes eight basic API functions, which we use later in various examples.
- Bgt60() - Constructor of the Arduino Bgt60 object
- ~Bgt60() - Destructor of the Arduino Bgt60 object
- init() - Initialize the Bgt60 class object
- deinit() - Deinitialize the Bgt60 class object
- getMotion() - Read out target-detect-pin
- getDirection() - Read out phase-detect-pin
- enableInterrupt() - Enables the hardware interrupt
- disableInterrupt()- Disables the hardware interrupt
Find more detailed information in our Arduino API description.
To upload the sketch on your board, you have to choose the desired platform first. In this exampel we use the Arduino MKR1000 baord. Go to Tools -> Board -> Board Manager. There you search for your "Arduino MKR1000" and you find the package "Arduino SAMD Boards" which needs to be installed.
In order to upload the sketch now, you also have to choose the right COM-Port. There is a really easy way to do this. Unplug your connected Arduino and then check the available COM-Ports under Tool -> Port. Now connect your Arduino and check the ports again. Now you should see a new one, that was not there before. This is the right one, please select it.
MotionIn the first easy example we want to use the getMotion() function to recognize motion in the environment of the sensor. Take the code from File -> Example -> radar-bgt60 -> motionDetection.
/*!
* \name motionDetection
* \author Infineon Technologies AG
* \copyright 2021 Infineon Technologies AG
* \brief This example detects the motion of an object
* \details This example demonstrates how to detect a moving object while the
* BGT60LTR11AIP shield is connected to Arduino compatible
* boards using polling method.
*
* Connection details:
* --------------------------------------------------
* Pin on shield Connected to pin on Arduino
* --------------------------------------------------
* TD depends on Arduino board
* PD depends on Arduino board
* GND GND
* Vin VCC (3.3V or 5V - depends on Arduino board)
* --------------------------------------------------
*
* Decoding on-board LED output of BGT60LTR11AIP shield:
*
* - Red LED indicates the output of direction of motion once target is detected (PD)
* ---------------------------------------------
* LED State Output explanation
* ---------------------------------------------
* Red ON Departing target
* OFF Approaching target
* ---------------------------------------------
*
* - Green LED indicates the output of target in motion detection (TD)
* ---------------------------------------------
* LED State Output explanation
* ---------------------------------------------
* Green ON Moving target detected
* OFF No target detected
* ---------------------------------------------
*
* SPDX-License-Identifier: MIT
*/
#include <Arduino.h>
/* Include library main header */
#include <bgt60-ino.hpp>
/* Include Arduino platform header */
#include <bgt60-platf-ino.hpp>
/*
* In case no supported platform is defined, the
* PD and TD pin will be set to the values below.
*/
#ifndef TD
#define TD 15
#endif
#ifndef PD
#define PD 16
#endif
/* Create radar object with following arguments:
* TD : Target Detect Pin
* PD : Phase Detect Pin */
Bgt60Ino radarShield(TD, PD);
/* Begin setup function - takes care of initializations and executes only once post reset */
void setup()
{
/* Set the baud rate for sending messages to the serial monitor */
Serial.begin(9600);
// Configures the GPIO pins to input mode
Error_t init_status = radarShield.init();
/* Check if the initialization was successful */
if (OK != init_status) {
Serial.println("Init failed.");
}
else {
Serial.println("Init successful.");
}
}
/* Begin loop function - this part of code is executed continuously until external termination */
void loop()
{
/* Initialize the variable to NO_MOTION to be able to record new events */
Bgt60::Motion_t motion = Bgt60::NO_MOTION;
/* The getMotion() API does two things:
1. Returns the success or failure to detect moving object as a message of type Error_t.
Any value other than OK indicates failure
2. Sets recent event in "motion" variable. Events can be: NO_MOTION or MOTION */
Error_t err = radarShield.getMotion(motion);
/* Check if API execution is successful */
if(err == OK)
{
/* Cases based on value set in motion variable */
switch (motion)
{
/* Variable "motion" is set to MOTION when moving target is detected */
case Bgt60::MOTION:
Serial.println("Target in motion detected!");
break;
/* Variable "motion" is set to NO_MOTION when moving target is not present */
case Bgt60::NO_MOTION:
Serial.println("No target in motion detected.");
break;
}
}
/* API execution returned error */
else {
Serial.println("Error occurred!");
}
/* Reducing the frequency of the measurements */
delay(500);
}
Without comments, the code has only 50 lines. After we have discussed it step by step we will upload the example.
#include <Arduino.h>
#include <bgt60-ino.hpp>
#include <bgt60-platf-ino.hpp>
First of all, we included the main library from Arduino and the used libraries for the BGT60 radar sensor.
#ifndef TD
#define TD 15
#endif
#ifndef PD
#define PD 16
#endif
Bgt60Ino radarShield(TD, PD);
The if condition is checking if TD and PD are defined. They represent our pins phase detect and target detect from the hardware. In our case they are already set, because we defined the supported platform, the Arduino MKR1000, before. If we choose another microcontroller (not from Arduino), this step is necessary to specify the unknown pins. The line "Bgt60Ino radarShield(TD, PD);" uses the instructor to create an radar object with the two pins.
void setup()
{
Serial.begin(9600);
// Configures the GPIO pins to input mode
Error_t init_status = radarShield.init();
if (OK != init_status) {
Serial.println("Init failed.");
}
else {
Serial.println("Init successful.");
}
}
In the function "setup" are the initializations for our program. It is executed only once when we upload the sketch. The "Serial.begin()" function sets the baud rate, which defines the transmission speed for sending messages to the monitor. Now we use one of the library's API functions "intit()" to initialize the Bgt60 class object "radarShield" from before. Therefore, the pins will be set on input mode. If the initialization was successful, the function returns "OK". To see as a user if the initialization worked, we print the outcome of the init-function.
void loop()
{
Bgt60::Motion_t motion = Bgt60::NO_MOTION;
Error_t err = radarShield.getMotion(motion);
The “loop()” function is the part of the code, which is repeated over and over again. First, we decline a variable “motion” to save the detected movement. There are two possible states. At the beginning we set it on “Bgt60::NO_MOTION”.
In the next line we use the function “getMotion()" on our “radarShield” object. The passing parameter for the function is the motion variable. When the sensor detects a motion, this function sets the variable as “Bgt60::MOTION”. If the whole function was a success, "OK" will be saved in the “Error_t” variable.
if(err == OK)
{
switch (motion)
{
case Bgt60::MOTION:
Serial.println("Target in motion detected!");
break;
case Bgt60::NO_MOTION:
Serial.println("No target in motion detected.");
break;
}
}
else {
Serial.println("Error occurred!");
}
delay(500);
}
In the last part we check if the function was successful. When that’s the case we start a switch case condition, if the variable "motion" is “Bgt60::MOTION” or “Bgt60::NO_MOTION” and print the solution. At last we start a delay, which represents the pause between two loop passages.
Now we understand the whole code and are ready to upload it. If not done so far, you can compile the code on Button 1. With 2 you upload the sketch on your board. To see what we printed and if the sensor detectet motion, you have to open the monitor on 3.
On the monitor you can read “No target in motion detected” when everything around it is completely standing still. But if you have a movement in up to 5 meters away from the sensor your output is “Target in motion detected!”.
DirectionFollowing to the successful detection of a movement, we also want to know the direction of this movement. Therefore, the librarys, object creation and the setup function are exactly the same.
Bgt60::Direction_t direction = Bgt60::NO_DIR;
Error_t err = radarShield.getDirection(direction);
The code in the loop is also similar to before, but now we do it for the direction. First we need a variable to store the direction. Second, we use the API function "getDirection()".
if (err == OK)
{
switch (direction)
{
case Bgt60::APPROACHING:
Serial.println("Target is approaching!");
break;
case Bgt60::DEPARTING:
Serial.println("Target is departing!");
break;
case Bgt60::NO_DIR:
Serial.println("Direction cannot be determined since no motion was detected!");
break;
}
}
else{
Serial.println("Error occurred!");
}
There are three possible states for the direction variable. We have two different directions: approaching and departing. It is also possible, that we have no movement at all and therefore no direction. If the API function worked, it gives back a “OK” for the error variable. Only if it was successful, we start a switch-case condition, where we decided between departing, approaching and no direction. The current state is printed after every delay.
A possible monitor output is on the picture above. To create it, you first move your hand towards the sensor and then away again.
InterruptAs a final example, let's show how to use the interrupt function. When you enable an interrupt, the processor stops its current activities and saves its state. Instead, it executes a function called Interrupt Service Routine (ISR) in the meantime. In our example, the interrupt is triggered when the state of the detected motion or direction changes. Therefore the librarys and object creation in the code are still the same as in the last two examples.
init_status = radarShield.enableInterrupt(cBackFunct);
if (OK != init_status)
Serial.println("Interrupt init failed.");
else
Serial.println("Interrupt init successful.");
}
In the setup function we set the baud rate and initialize our object, as before. Furthermore we activate the interrupt now. The function "enableInterrupt()" switches the hardware interrupt on. The “cBackFunct” function is starting with the interrupt. Also, we get a status variable, with which we can check if the API function to enable the interrupt worked.
/* Definition and initialization of the interrupt active flag */
volatile static bool intActive = false;
/* User defined callback function */
void cBackFunct(void)
{
if ( ! intActive ) {
/* Set the interrupt active flag to avoid parallel execution of this function multiple times. */
intActive = true;
/* Create variables to store the state of the motion as well as the direction */
Bgt60::Motion_t motion = Bgt60::NO_MOTION;
Bgt60::Direction_t direction = Bgt60::NO_DIR;
/* Now check what happend, first check if a motion was detected or is
not detected anymore */
Error_t err = radarShield.getMotion(motion);
/* Check if API execution is successful */
if(OK == err)
{
/* In case motion is detected */
if(Bgt60::MOTION == motion){
Serial.println("Target in motion was detected!");
/* Check the direction of the detected motion */
err = radarShield.getDirection(direction);
if(OK == err)
{
/* In case the target is approaching */
if(Bgt60::APPROACHING == direction){
Serial.println("The target is approaching!");
}
/* In case the target is departing */
else{
Serial.println("The target is departing!");
}
}
/* API execution returned error */
else{
Serial.println("Error has occurred during the determination of the direction!");
}
}
/* No motion is detected */
else{
Serial.println("No target in motion detected!");
}
}
/* API execution returned errord */
else {
Serial.println("Error has occurred during the determination of the direction!");
}
Serial.println("\n--------------------------------------\n");
/* Release the interrupt active flag to allow a new call of this callback function. */
intActive = false;
}
}
“cBackFunct” represents the interrupt service routine (ISR). We write it in a separate function before the setup function. Also. we have a local variable, which is the active flag of the interrupt. Therefore, it is set active in the beginning of ISR and inactive at the end of the function. To avoid parallel execution of this function multiple times, the “cBackFunct” checks if the active flag was inactive before. Everything else in the function is similar to the direction detection. We use the known functions first to check for a movement and then separate for the two directions of the movement. We print the corresponding state of motion and direction. After that, we print a visual border to keep an overview.
void loop()
{
// Here you can do something else in parallel while waiting for an interrupt.
delay(1000);
}
In our loop function we have now only the delay. There we could put any code, which should be executed while no change in motion or direction is happening. To understand the way the interrupt works easier, you can print something in your loop too.
The monitor output changes after each boundary. This is because we start the interrupt only when changes occur. The time between printouts is no longer as regular as before, now it depends on the interrupt.
If we don't want the interrupt any more for example after a period of time, we can disable it with the function "disableInterrupt()". Afterwards only our code in the loop will be executed.
Comments