↓Recommended Follow ↓
Introduction
If you ask how useful gesture recognition really is, it’s hard to say; it’s neither too big nor too small. In daily life, we see it being used in various small devices like lamps and kitchen switches. From a practical standpoint, having gesture-controlled switches in the kitchen is quite nice; sometimes when your hands are covered in cooking grease, pressing a switch can make it sticky over time.
Or you could just have a gesture-controlled faucet. However, that said, the current recognition rates aren’t very high. You might say that if you spend a lot of money on a more expensive product, the accuracy will be better, but that’s not always the case. You know, many “high-tech” products are, to put it bluntly, commercial bubbles that trick you into buying them.
They might add a sensor that costs just a few bucks, but they can convince you of its high-end nature and charge you an extra 60 bucks. Some familiar slogans are – “Very expensive, but very worth it”, “Don’t buy XXX unless you’ve seen me”.
There are several chips for gesture sensing; I bought the PAJ7620 from DFRobot (mainly for its nine gesture recognition feature, while some only recognize six gestures).
That said, it’s not cheap; honestly, I should have just bought the one from YABO.
The module from YABO has an advantage: it supports multiple wiring methods; you can use X-pin ribbon connectors, Dupont wires, or crocodile clips.
This is what the module looks like.
Don’t be misled by the picture; when you get it in hand, you’ll find it quite small, just look at this…
When taking pictures with your phone, if the module is in use, you’ll see a bright spot on the screen, which is the infrared emitter on the PAJ7620.
This module communicates via the IIC (I2C) protocol, with the default slave address of 0x73. The operation is done through reading and writing registers. Each register has its own address; as long as you write bytes to the corresponding address, the data will be stored in that register.
1. To read a register: First, write the address of the register you want to read to the slave address 0x73; then read a byte from the module, which is the value of that register.
2. To write to a register: Write two bytes to the slave address 0x73 – the first byte specifies the register address, and the second byte is the value to write.
For example:
a. To write 0x01 to register 0x42, send two bytes to slave 0x73: 0x42, 0x01.
b. To read the value of register 0x23, first send a byte 0x23 to slave 0x73, then read a byte.
PAJ7620 Module
The PAJ7620 module has a limited number of registers, and it’s not too complicated to operate. Some people say the module doesn’t respond; is it broken? That’s hard to say, but generally, it shouldn’t be; you’d have to be unlucky to get a broken module. The most likely reason is that you’re not following the correct operation flow. This module is a bit peculiar (it can save power): after powering on, it defaults to sleep mode, so it won’t recognize gestures.
So I suspect this student probably didn’t wake the module before reading data, and what they read would just be 00 00 00 00.
Alright, enough chit-chat, but I also don’t plan to introduce the registers one by one; that would be too boring, let’s explain it with practical use.
No.1 Select Register Bank (Address:0xEF)
Although the PAJ7620 doesn’t have many registers, it loves partitioning. Its registers are divided into two banks – Bank 0 and Bank 1. Therefore, some registers are in Bank 0, and some are in Bank 1; we must be careful when operating, as we need to switch banks before reading or writing registers, otherwise the values read will be incorrect.
Bank switching method:
* First bank: Write 0x00 to register 0xEF;
* Second bank: Write 0x01 to register 0xEF.
For instance, register address 0x72 is used to enable or disable the PAJ7620 module, and it is located in Bank 1. To read or write this register, you must do it in two steps (0x73 is the slave address).
Step 1: —> 0x73 write 0xEF 0x01
Step 2: —> 0x73 read 0x72
No.2 Enable Register (Address:0x72)
This register has been mentioned before; it is located in Bank 1. Writing 0x00 to this register will disable the PAJ7620 module, while writing 0x01 will enable it.
No.3 Suspend and Wake Up Module
Suspending means the sleep state value is stored in register 0x03, which is located in Bank 0. The value of the register only has the first binary bit useful; 0x00 indicates the module is working, while 0x01 indicates the module is in sleep.
To put the module into sleep mode, follow these steps:
1. Send 0x01 to 0xEF to select Bank 1;
2. Write 0x00 to register 0x72 to disable the module;
3. Write 0x00 to register 0xEF to select Bank 0;
4. Write 0x01 to register 0x03 to enter sleep.
After powering on, the module also defaults to sleep mode, so it won’t recognize gestures at this time; it must be woken up first. Waking up is quite simple, just requires normal IIC signals. According to DFRobot’s documentation, one wake-up method is to read register 0x00, if it returns 0x20, it indicates successful wake-up.
After the module is woken up, it is still in a disabled state, so after waking up, you must write 0x01 to the register at address 0x72 to complete the operation. As for register 0x03 (suspend), there’s no need to worry about it, it will automatically reset to zero.
Some people say the PAJ7620 module doesn’t respond; it’s likely they forgot to enable (write 0x72 register) the module after waking it up.
Thus, we can summarize that the module’s initialization process should be as follows:
1. Continuously read register 0x00 from slave 0x73 until it returns 0x20 to complete the wake-up operation;
2. Write 0x01 to register 0xEF to switch to Bank 1;
3. Write 0x01 to register 0x72 to enable the module to enter normal working state.
No.4 Set Gesture Detection Flags (Register Addresses:0x41 and 0x42)
These two registers are not used to read the detected gestures but to set which gestures the module supports for detection. Each binary bit represents a gesture; if it is 1, it means that gesture can be detected; if it is 0, the module will not detect that gesture. Each register holds one byte, totaling eight bits. As mentioned earlier, the PAJ7620 module supports the recognition of nine gestures, so one byte of eight bits isn’t enough. Register 0x41 holds the flags for the first eight gestures, while register 0x42 holds the remaining gesture. Hence, actually, only the first binary bit of 0x42 is used, and the remaining seven are not used.
No.5 Gesture Detection Results (Register Addresses:0x43 and 0x44)
These two registers are used to read the gesture detection results; similarly, since one byte of eight bits is not enough, two registers are used. If any bit’s value is 1, it indicates that gesture has been detected; otherwise, it’s 0, meaning it hasn’t been detected.
The binary bits in 0x41, 0x42 correspond one-to-one with those in 0x43, 0x44. The default definitions in the documentation are as follows:
The binary bits from low to high: up, down, left, right, forward, backward, clockwise, counterclockwise. The remaining gesture is in the lowest bit of the second byte, which is waving goodbye – the action of waving your hand back and forth in front of the module.
However, this definition is relative, as the module’s installation direction can rotate by X degrees in real-world use. At this time, more testing should be done to redefine which binary bits correspond to which gestures. According to DFRobot’s documentation, the correct placement orientation is as follows. But I placed it like this.
So the direction of the gestures needs to be redefined; in short, one binary bit corresponds to one gesture, and which gesture it represents depends on the direction in which you place the module, so you can try more.
Program Code
Alright, the above content is the core functionality introduction of the module; with this understanding, we can convert it into program code.
To make it more convenient to use, a better solution is to encapsulate it in a class – I wrote a PAJ7620 class that includes the following methods:
* WakeUp: Wake up the module;
* Suspend: Suspend the module;
* SetEnable: Enable/Disable the module;
* GetGesture: Get the detected gesture;
* SelectBank0 and SelectBank1: Switch register banks.
The PAJ7620 module is enabled by default to detect nine gestures, so I didn’t read or write registers 0x41 and 0x42 in my code; interested friends can add that themselves; the operations are the same, just reading and writing registers.
First, let’s declare the register addresses we’ll use as constants for easier reference later.
const byte SELECTE_BANK = 0xEF; //Switch bank
const byte BANK0 = 0x00; //Bank 0
const byte BANK1 = 0x01; //Bank 1
const byte ISENABLE = 0x72; //Enable/Disable module
const byte GES_DETECT = 0x43; //Read gesture
const byte GES_DETECT2 = 0x44; //Read gesture (ninth)
const byte SUSPEND = 0x03; //Suspend module (sleep)
Below is the default slave address of the module – 0x73.
public const int DEFAULT_ADDR = 0x73;
In the constructor of the class, we initialize the connection to the IIC device.
private I2cDevice _device=default;
public Paj7620(int busid = 1, int address = DEFAULT_ADDR)
{
I2cConnectionSettings settings=new(busid, address);
_device = I2cDevice.Create(settings);
}
The slave address uses the default address, which is the constant DEFAULT_ADDR defined above.
Next, we implement various methods. First, let’s look at the methods for switching the two register banks; I’ve made these private methods as there’s no need to expose them.
private void SelectBank0()
{
Span buff = stackalloc byte[2]{
SELECTE_BANK,
BANK0
};
_device.Write(buff);
}
Since only two bytes need to be sent, we can use stackalloc to allocate memory directly on the stack for speed, of course, you can also use the traditional array instantiation method.
byte[] buff = new byte[] { };
The first byte is the register address 0xEF for selecting the bank, and the second byte is the bank number. The principle is the same for the other method.
private void SelectBank1()
{
Span buff = stackalloc byte[]
{
SELECTE_BANK, BANK1
};
_device.Write(buff);
}
Alright, next is the implementation of the SetEnable method, which can enable or disable the module.
public void SetEnable(bool isenable)
{
SelectBank1(); //First switch to Bank 1
byte[] data =
{
ISENABLE, //0x72
(byte)(isenable? 0x01 : 0x00)
};
_device.Write(data);
}
The isenable parameter is a boolean value; if true, write 1 to register 0x72; otherwise, write 0.
Next is the Suspend method to suspend the module.
public void Suspend()
{
// First disable it
SetEnable(false);
// Then suspend
SelectBank0(); //Remember to switch banks
byte[] data = {SUSPEND, 0x01};
_device.Write(data);
}
Before suspending, you must disable the module to enter the suspended state.
Next is the method to wake up the module.
public void WakeUp()
{
int count = 0;
// Try to wake up
while(0==0)
{
_device.WriteByte(0x00);
// Wait for 700 microseconds
// 1 millisecond is usually enough
Sleep(1);
count++;
byte back = _device.ReadByte();
if(back == 0x20)
{
break;
}
if(count > 4)
{
// If it can't wake up after several attempts
throw new Exception("Module cannot wake up");
}
Sleep(5);
}
// Enable
SetEnable(true);
}
The WakeUp method is actually divided into two stages: first, read register 0x00, and when reading the register, it sends information to the module, which is equivalent to sending a wake-up signal (any IIC communication will include Start timing), then attempts five times; if it can’t wake up after five attempts, it’s likely dead and throws an exception.
The second stage is to enable the module by calling the SetEnable method.
Finally, the core method is to read the detected gestures.
public int GetGesture()
{
SelectBank0();
// First eight
_device.WriteByte(GES_DETECT);
byte p1 = _device.ReadByte();
// Ninth
_device.WriteByte(GES_DETECT2);
byte p2 = _device.ReadByte();
// Combine
return (p2 << 8) | p1;
}
As mentioned earlier, there are nine gestures, distributed over two bytes; the first byte is read from register 0x43, and the second from 0x44. To make it convenient, I combine the two bytes and convert it into an int type value. From the low bit, bits 1 to 9 indicate the detected nine gestures.
Here’s the complete code, feel free to use it.
using System;
using System.Device.I2c;
using static System.Threading.Thread;
namespace Device
{
public class Paj7620 : IDisposable
{
#region Register List
const byte SELECTE_BANK = 0xEF; //Switch bank
const byte BANK0 = 0x00; //Bank 0
const byte BANK1 = 0x01; //Bank 1
const byte ISENABLE = 0x72; //Enable/Disable module
const byte GES_DETECT = 0x43; //Read gesture
const byte GES_DETECT2 = 0x44; //Read gesture (ninth)
const byte SUSPEND = 0x03; //Suspend module (sleep)
#endregion
/// <summary>
/// Default address
/// </summary>
public const int DEFAULT_ADDR = 0x73;
private I2cDevice _device=default;
public Paj7620(int busid = 1, int address = DEFAULT_ADDR)
{
I2cConnectionSettings settings=new(busid, address);
_device = I2cDevice.Create(settings);
}
public void Dispose()
{
Suspend();
_device?.Dispose();
}
#region Public Methods
/// <summary>
/// Wake up the module
/// </summary>
public void WakeUp()
{
int count = 0;
// Try to wake up
while(0==0)
{
_device.WriteByte(0x00);
// Wait for 700 microseconds
// 1 millisecond is usually enough
Sleep(1);
count++;
byte back = _device.ReadByte();
if(back == 0x20)
{
break;
}
if(count > 4)
{
// If it can't wake up after several attempts
throw new Exception("Module cannot wake up");
}
Sleep(5);
}
// Enable
SetEnable(true);
}
/// <summary>
/// Suspend, put the module into sleep state
/// </summary>
public void Suspend()
{
// First disable it
SetEnable(false);
// Then suspend
SelectBank0(); //Remember to switch banks
byte[] data = {SUSPEND, 0x01};
_device.Write(data);
}
/// <summary>
/// Enable or disable the module
/// </summary>
/// <param name="isenble">true: enable; false: disable</param>
public void SetEnable(bool isenable)
{
SelectBank1(); //First switch to Bank 1
byte[] data =
{
ISENABLE, //0x72
(byte)(isenable? 0x01 : 0x00)
};
_device.Write(data);
}
/// <summary>
/// Get the recognized gesture
/// </summary>
/// <returns>Contains nine flags</returns>
public int GetGesture()
{
SelectBank0();
// First eight
_device.WriteByte(GES_DETECT);
byte p1 = _device.ReadByte();
// Ninth
_device.WriteByte(GES_DETECT2);
byte p2 = _device.ReadByte();
// Combine
return (p2 << 8) | p1;
}
#endregion
#region Private Methods
/// <summary>
/// Switch to Bank0
/// </summary>
private void SelectBank0()
{
Span<byte> buff = stackalloc byte[2]{
SELECTE_BANK,
BANK0
};
_device.Write(buff);
}
/// <summary>
/// Switch to Bank1
/// </summary>
private void SelectBank1()
{
Span<byte> buff = stackalloc byte[]
{
SELECTE_BANK, BANK1
};
_device.Write(buff);
}
#endregion
}
}
Alright, the basic type encapsulation is done, and now we can start using it. Here, I didn’t prepare any advanced applications, just a simple test program.
using System;
using static System.Threading.Thread;
using static System.Console;
using Device;
namespace myapp
{
class Program
{
static bool isRunning = false;
static void Main(string[] args)
{
using Paj7620 paj = new();
// Wake up
paj.WakeUp();
WriteLine("Device has been woken up");
CancelKeyPress += (_, _) => isRunning = false;
Sleep(500);
isRunning = true;
while (isRunning)
{
int res = paj.GetGesture();
// Display in binary
string str = Convert.ToString(res, 2);
str = str.PadLeft(9, '0');
str = string.Join(" | ", str.ToCharArray());
WriteLine(str);
WriteLine("Press any key to continue");
ReadKey(true);
}
}
}
}
Hardware wiring: Only connect the VCC, GND, SCL, and SDA pins; the others can be ignored.
VCC connects to the Raspberry Pi’s 3.3V; 5V is also fine, as the module is compatible with wide voltage;
GND connects to the Raspberry Pi’s GND;
SCL connects to Raspberry Pi’s GPIO 3;
SDA connects to Raspberry Pi’s GPIO 2.
After running this program, you can perform various gestures in front of it, then press any key to continue the loop, and the screen will print out the values of each binary bit.
As mentioned earlier, the definitions of the nine gestures are relative and depend on the installation direction and angle of the module. However, the ninth bit (waving) remains unchanged, as no matter how you place it, the action of waving back and forth will yield the same recognition result; Furthermore, the forward and backward gestures are also the same; if you place the module horizontally with the emitter facing up, and your hand approaches the module from above, it’s a forward gesture; Conversely, if your hand rises from a position closer to the module, it’s a backward gesture. Different installation orientations generally only affect the gestures in the up, down, left, and right directions.
This module doesn’t have a very high recognition accuracy and is easily interfered with; for instance, if you turn on a lamp next to it, or wave a flashlight diagonally in front of it, or eat roast duck nearby, it may lead to incorrect recognition or even no recognition at all.
Summary
As for what can be done with this module? Currently, with the rise of artificial intelligence… Zhang… Oh no, Z can do it, so you can use it to create a gesture-controlled light, control a smart car’s turns (though it might crash), gesture-controlled doors (not sure if it will pinch someone), gesture-operated wheelchairs (risky). Going further, after using the restroom, you could wave your hand over the toilet to flush automatically, leaving no trace.
Source: Dong Xie Du Gu
Link: cnblogs.com/tcjiaan/p/14687788.html
– EOF –
Did you gain anything from this article? Please share it with more people
Recommended to follow “DotNet” to improve .Net skills
Likes and views are the biggest support ❤️
Leave a Comment
Your email address will not be published. Required fields are marked *