Files

289 lines
8.7 KiB
C++

/*
* SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
/*
Simple example using M5UnitUnified for UnitFinger
*/
#include <M5Unified.h>
#include <M5UnitUnified.h>
#include <M5UnitUnifiedFINGER.h>
#include <M5Utility.hpp>
// *************************************************************
// Choose one define symbol to match the unit you are using
// *************************************************************
#if !defined(USING_UNIT_FINGER) && !defined(USING_HAT_FINGER) && !defined(USING_FACES_FINGER)
// For UnitFinger (SKU:U008)
// #define USING_UNIT_FINGER
// For HatFinger (SKU:U074)
// #define USING_HAT_FINGER
// For FacesFinger (SKU:A066)
// #define USING_FACES_FINGER
#endif
// *************************************************************
using namespace m5::unit::fpc1xxx;
namespace {
auto& lcd = M5.Display;
m5::unit::UnitUnified Units;
#if defined(USING_UNIT_FINGER)
m5::unit::UnitFinger unit;
#elif defined(USING_HAT_FINGER)
m5::unit::HatFinger unit;
#elif defined(USING_FACES_FINGER)
m5::unit::UnitFacesFinger unit;
#else
#error Please choose unit!
#endif
uint16_t target_user_id{};
#if defined(USING_HAT_FINGER)
struct UartPins {
int rx; // SCL pin of Hat connector
int tx; // SDA pin of Hat connector
};
UartPins get_hat_uart_pins(const m5::board_t board)
{
switch (board) {
case m5::board_t::board_M5StickC:
case m5::board_t::board_M5StickCPlus:
case m5::board_t::board_M5StickCPlus2:
return {26, 0};
case m5::board_t::board_M5StickS3:
return {0, 8};
case m5::board_t::board_M5StackCoreInk:
return {26, 25};
default:
return {-1, -1};
}
}
#endif
#if defined(USING_FACES_FINGER)
// M-Bus pins for Faces Finger (GPIO varies by board)
struct FacesPins {
int rx; // UART RX (mbus_pin15)
int tx; // UART TX (mbus_pin16)
int panel_power; // Panel power (mbus_pin10)
int touch_power; // Touch IC power (mbus_pin20)
};
FacesPins get_faces_pins()
{
switch (M5.getBoard()) {
case m5::board_t::board_M5Stack: // Core/Gray/Fire
return {16, 17, 26, 5};
default:
return {-1, -1, -1, -1};
}
}
#endif
void print_all_users()
{
std::vector<User> v{};
if (unit.readAllUser(v)) {
M5.Log.printf("All user data (%u):\n", v.size());
uint16_t idx{};
for (auto&& u : v) {
M5.Log.printf(" [%3d] ID:%5u, PERMISSION:%u\n", idx++, u.id, u.permission);
}
}
}
} // namespace
void setup()
{
auto m5cfg = M5.config();
#if defined(USING_HAT_FINGER)
m5cfg.pmic_button = false; // Disable BtnPWR
m5cfg.internal_imu = false; // Disable internal IMU
m5cfg.internal_rtc = false; // Disable internal RTC
#endif
M5.begin(m5cfg);
M5.setTouchButtonHeightByRatio(100);
// The screen shall be in landscape mode
if (lcd.height() > lcd.width()) {
lcd.setRotation(1);
}
#if defined(USING_HAT_FINGER)
const auto pins = get_hat_uart_pins(M5.getBoard());
M5_LOGI("getHatPin: RX:%d TX:%d", pins.rx, pins.tx);
if (pins.rx < 0 || pins.tx < 0) {
M5_LOGE("No Hat port on this board");
lcd.fillScreen(TFT_RED);
while (true) {
m5::utility::delay(10000);
}
}
auto pin_num_in = pins.rx;
auto pin_num_out = pins.tx;
#elif defined(USING_FACES_FINGER)
const auto fp = get_faces_pins();
M5_LOGI("getFacesPin: RX:%d TX:%d PWR:%d TCH:%d", fp.rx, fp.tx, fp.panel_power, fp.touch_power);
if (fp.rx < 0 || fp.tx < 0) {
M5_LOGE("No M-Bus on this board");
lcd.fillScreen(TFT_RED);
while (true) {
m5::utility::delay(10000);
}
}
auto pin_num_in = fp.rx;
auto pin_num_out = fp.tx;
{
auto cfg = unit.config();
cfg.panel_power_pin = fp.panel_power;
cfg.touch_power_pin = fp.touch_power;
unit.config(cfg);
}
#else
auto pin_num_in = M5.getPin(m5::pin_name_t::port_c_rxd);
auto pin_num_out = M5.getPin(m5::pin_name_t::port_c_txd);
if (pin_num_in < 0 || pin_num_out < 0) {
M5_LOGW("PortC is not available");
// NanoC6: Ex_I2C.setPort() registers m5gfx::i2c on GROVE pins;
// Wire.end() alone won't release it, causing dual-driver conflict on uart_driver_install
if (M5.getBoard() == m5::board_t::board_M5NanoC6) {
M5.Ex_I2C.release();
}
Wire.end();
pin_num_in = M5.getPin(m5::pin_name_t::port_a_pin1);
pin_num_out = M5.getPin(m5::pin_name_t::port_a_pin2);
}
#endif
M5_LOGI("getPin: %d,%d", pin_num_in, pin_num_out);
// clang-format off
#if defined(CONFIG_IDF_TARGET_ESP32C6)
auto& s = Serial1;
#elif SOC_UART_NUM > 2
auto& s = Serial2;
#elif SOC_UART_NUM > 1
auto& s = Serial1;
#else
#error "Not enough Serial"
#endif
// clang-format on
s.end();
// When changing the chip's baud rate, make sure to synchronize it
// s.begin(9600, SERIAL_8N1, pin_num_in, pin_num_out);
s.begin(19200, SERIAL_8N1, pin_num_in, pin_num_out); // as default
// s.begin(38400, SERIAL_8N1, pin_num_in, pin_num_out);
// s.begin(57600, SERIAL_8N1, pin_num_in, pin_num_out);
// s.begin(115200, SERIAL_8N1, pin_num_in, pin_num_out);
if (!Units.add(unit, s) || !Units.begin()) {
M5_LOGE("Failed to begin");
lcd.fillScreen(TFT_RED);
while (true) {
m5::utility::delay(10000);
}
}
M5_LOGI("M5UnitUnified initialized");
M5_LOGI("%s", Units.debugInfo().c_str());
{
uint8_t clv{}, timeout{};
Mode mode{};
uint16_t user_count{};
unit.readRegistrationMode(mode);
unit.readComparisonLevel(clv);
unit.readTimeout(timeout);
unit.readRegisteredUserCount(user_count);
M5.Log.printf("=== %s information ===\n", unit.deviceName());
M5.Log.printf(" Mode: %s\n",
mode == Mode::ProhibitDuplicate ? "Prohibit duplicate" : "Allow duplicate");
M5.Log.printf(" Comparison Lv: %u\n", clv);
M5.Log.printf(" Timeout: %u\n", timeout);
M5.Log.printf("Registered user: %u\n", user_count);
print_all_users();
if (unit.findAvailableUserID(target_user_id)) {
M5.Log.printf("Lowest unregistered UserID: %u\n", target_user_id);
}
uint8_t characteristic[193]{};
if (target_user_id > 1 && unit.readUserCharacteristic(characteristic, target_user_id - 1)) {
M5.Log.printf("--- UserID:%u characteristic\n", target_user_id - 1);
m5::utility::log::dump(characteristic, m5::stl::size(characteristic), false);
}
}
lcd.fillScreen(TFT_DARKGREEN);
}
void loop()
{
M5.update();
Units.update();
// Register finger
if (M5.BtnA.wasHold()) {
lcd.fillScreen(TFT_DARKGREEN);
M5.Speaker.tone(2500, 20);
lcd.drawString("Try register", 0, 0);
M5.Log.printf("Try register\n");
if (unit.registerFinger(target_user_id, 1)) {
M5.Speaker.tone(2000, 20);
lcd.setCursor(0, 0);
lcd.printf("Registered %u", target_user_id);
M5.Log.printf("Registered user %u\n", target_user_id);
++target_user_id;
} else {
M5.Log.printf("Failed to register %u", target_user_id);
lcd.drawString("Try again ", 0, 0);
}
return;
}
// Identify and verify finger
if (M5.BtnA.wasClicked()) {
uint16_t id{};
uint8_t per{};
M5.Speaker.tone(1500, 20);
lcd.drawString("Try identify", 0, 0);
M5.Log.printf("Try identify\n");
if (unit.identifyFinger(id, per)) {
if (id) {
M5.Speaker.tone(2000, 20);
M5.Log.printf("Identified user:%u, permission:%u\n", id, per);
// If it was identified, it should pass verification
lcd.drawString("Try verify ", 0, 0);
M5.Log.printf("Try verify\n");
bool match{};
if (unit.verifyFinger(match, id) && match) {
M5.Speaker.tone(2500, 20);
M5.Log.printf("Verified: %u\n", id);
lcd.drawString("Verified ", 0, 0);
} else {
M5.Log.printf("Failed or not match %u\n", id);
lcd.drawString("Failed to Verify ", 0, 0);
}
} else {
M5.Speaker.tone(800, 40);
M5.Log.printf("No user\n");
lcd.drawString("No user ", 0, 0);
}
} else {
M5.Speaker.tone(800, 40);
M5.Log.printf("No user\n");
lcd.drawString("No user ", 0, 0);
}
}
}