changeset 781:6c42c1f64f00

new driver for esp32 and R503 fingerprint sensor
author drewp@bigasterisk.com
date Mon, 24 Aug 2020 01:27:33 -0700
parents 729ab70c6212
children 01005d533e0a
files espNode/desk/platformio.ini espNode/desk/src/display.cpp espNode/desk/src/display.h espNode/desk/src/fingerprint.cpp espNode/desk/src/fingerprint.h espNode/desk/src/main.cpp espNode/desk/src/mqtt.cpp espNode/desk/src/mqtt.h espNode/desk/src/wifi.cpp espNode/desk/src/wifi.h
diffstat 10 files changed, 638 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/espNode/desk/platformio.ini	Mon Aug 24 01:27:33 2020 -0700
@@ -0,0 +1,52 @@
+; PlatformIO Project Configuration File
+;
+;   Build options: build flags, source filter
+;   Upload options: custom upload port, speed and extra flags
+;   Library options: dependencies, extra library storages
+;   Advanced options: extra scripting
+;
+; Please visit documentation for the other options and examples
+; https://docs.platformio.org/page/projectconf.html
+
+[env:ttgo-t1]
+platform = espressif32
+board = ttgo-t1
+framework = arduino
+upload_port = /dev/ttyUSB0
+upload_protocol = esptool
+upload_speed = 921600
+monitor_port = /dev/ttyUSB0
+monitor_speed = 115200
+
+build_flags =
+  -Os
+  -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_DEBUG
+  -DUSER_SETUP_LOADED=1
+  -DST7789_DRIVER=1
+  -DTFT_WIDTH=135
+  -DTFT_HEIGHT=240
+  -DCGRAM_OFFSET=1
+  -DTFT_MISO=-1
+  -DTFT_MOSI=19
+  -DTFT_SCLK=18
+  -DTFT_CS=5
+  -DTFT_DC=16
+  -DTFT_RST=23
+  -DTFT_BL=4
+  -DTFT_BACKLIGHT_ON=1
+  -DLOAD_GLCD=1
+  -DLOAD_FONT2=1
+  -DLOAD_FONT4=1
+  -DLOAD_FONT6=1
+  -DLOAD_FONT7=1
+  -DLOAD_FONT8=1
+  -DLOAD_GFXFF=1
+  -DSMOOTH_FONT=1
+  -DSPI_FREQUENCY=40000000
+  -DSPI_READ_FREQUENCY=6000000
+
+lib_deps =
+    TFT_eSPI@1.4.21    
+    Button2@1.0.0
+    https://github.com/brianrho/FPM
+    AsyncMqttClient@0.8.2
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/espNode/desk/src/display.cpp	Mon Aug 24 01:27:33 2020 -0700
@@ -0,0 +1,24 @@
+#include "display.h"
+
+#include <TFT_eSPI.h>
+#include <Wire.h>
+
+// see https://github.com/JakubAndrysek/TTGO_T_Display
+
+namespace display {
+
+TFT_eSPI tft(135, 240);
+
+void Setup() {
+  tft.init();
+  tft.fontHeight(2);
+  tft.setRotation(1);
+  tft.fillScreen(TFT_BLACK);
+}
+
+void Message(std::string msg) {
+  tft.drawString(msg.c_str(), tft.width() / 4, tft.height() / 2,
+                 4);  // string,start x,start y, font weight {1;2;4;6;7;8}
+}
+
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/espNode/desk/src/display.h	Mon Aug 24 01:27:33 2020 -0700
@@ -0,0 +1,17 @@
+#ifndef INCLUDED_DISPLAY
+#define INCLUDED_DISPLAY
+#include <string>
+
+#ifndef TFT_DISPOFF
+#define TFT_DISPOFF 0x28
+#endif
+
+#ifndef TFT_SLPIN
+#define TFT_SLPIN 0x10
+#endif
+
+namespace display {
+void Setup();
+void Message(std::string msg);
+}  // namespace display
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/espNode/desk/src/fingerprint.cpp	Mon Aug 24 01:27:33 2020 -0700
@@ -0,0 +1,314 @@
+#include "fingerprint.h"
+
+#include <string>
+
+#include "mqtt.h"
+
+namespace fingerprint {
+
+HardwareSerial fserial(1);
+FPM finger(&fserial);
+
+constexpr uint8_t led_red = 0x01, led_blue = 0x02, led_purple = 0x03;
+constexpr uint8_t led_breathe = 0x01, led_flash = 0x02, led_on = 0x03,
+                  led_off = 0x04, led_gradual_on = 0x05, led_gradual_off = 0x06;
+constexpr uint8_t led_fast = 0x30, led_medium = 0x60, led_slow = 0x80;
+constexpr uint8_t led_forever = 0;
+
+FPM_System_Params params;
+
+void BlinkNotConnected() {
+  finger.led_control(led_flash, led_fast, led_red, led_forever);
+}
+void BlinkConnected() {
+  finger.led_control(led_flash, led_fast, led_red, /*times=*/1);
+}
+void BlinkProgress() {
+  finger.led_control(led_flash, led_fast, led_blue, /*times=*/1);
+}
+void BlinkSuccess() {
+  finger.led_control(led_breathe, led_medium, led_purple, led_forever);
+}
+void BlinkClearSuccess() {
+  finger.led_control(led_breathe, led_medium, led_purple, 1);
+}
+void BlinkError() {
+  finger.led_control(led_flash, led_medium, led_red, /*times=*/3);
+  delay(500);
+}
+void BlinkDoorUnlocked() {}
+void BlinkStartEnroll() {
+  finger.led_control(led_flash, led_slow, led_blue, led_forever);
+}
+void BlinkStartEnrollRepeat() {
+  finger.led_control(led_flash, led_medium, led_blue, led_forever);
+}
+void BlinkClearEnroll() {
+  finger.led_control(led_flash, led_slow, led_blue, 1);
+}
+
+void (*queued)() = nullptr;
+void QueueBlinkConnected() { queued = BlinkConnected; }
+void ExecuteAnyQueued() {
+  if (queued) {
+    Serial.println("executing queued function");
+    queued();
+    queued = nullptr;
+  }
+}
+
+void PublishError(std::string caller, int16_t p) {
+  std::string errStr;
+  switch (p) {
+    case FPM_FEATUREFAIL:
+      errStr = "Could not find fingerprint features";
+      break;
+    case FPM_IMAGEFAIL:
+      errStr = "Imaging error";
+      break;
+    case FPM_IMAGEMESS:
+      errStr = "Image too messy";
+      break;
+    case FPM_INVALIDIMAGE:
+      errStr = "Could not find fingerprint features";
+      break;
+    case FPM_NOTFOUND:
+      errStr = "Did not find a match";
+      break;
+    case FPM_PACKETRECIEVEERR:
+      errStr = "Communication error";
+      break;
+    case FPM_READ_ERROR:
+      errStr = "Got wrong PID or length";
+      break;
+    case FPM_BADLOCATION:
+      errStr = "Could not store/delete in that location";
+      break;
+    case FPM_FLASHERR:
+      errStr = "Error writing to flash";
+      break;
+    case FPM_TIMEOUT:
+      errStr = "Timeout";
+      break;
+    case FPM_ENROLLMISMATCH:
+      errStr = "Fingerprints did not match";
+      break;
+    default:
+      char buf[100];
+      snprintf(buf, sizeof(buf), "Unknown error (%d)", p);
+      errStr = buf;
+      break;
+  }
+  mqtt::Publish("messages", caller + ": " + errStr);
+}
+
+bool GetImage() {
+  int16_t p = -1;
+  mqtt::Publish("messages", "Waiting for valid finger");
+
+  while (p != FPM_OK) {
+    p = finger.getImage();
+
+    if (p == FPM_OK) {
+      mqtt::Publish("messages", "getImage: Image taken");
+    } else if (p == FPM_NOFINGER) {
+      if (mqtt::HasPendingCommand() || queued) {
+        return false;
+      }
+    } else {
+      PublishError("getImage", p);
+      return false;
+    }
+    yield();
+  }
+  mqtt::Publish("messages", "getImage: got image");
+
+  BlinkProgress();
+  return true;
+}
+
+bool ConvertImage(uint8_t slot = 1) {
+  int16_t p = -1;
+  p = finger.image2Tz();
+  if (p == FPM_OK) {
+    mqtt::Publish("messages", "image2Tz: Image converted");
+  } else {
+    PublishError("image2Tz", p);
+    return false;
+  }
+  return true;
+}
+
+bool SearchDatabase(uint16_t* fid, uint16_t* score) {
+  int16_t p = -1;
+  p = finger.searchDatabase(fid, score);
+
+  /* now wait to remove the finger, though not necessary;
+     this was moved here after the search because of the R503 sensor,
+     which seems to wipe its buffers after each scan */
+  mqtt::Publish("messages", "Waiting for finger removal");
+  while (finger.getImage() != FPM_NOFINGER) {
+    delay(500);
+  }
+
+  if (p != FPM_OK) {
+    PublishError("searchDatabase", p);
+
+    if (p == FPM_NOTFOUND) {
+      BlinkError();
+    }
+    return false;
+  }
+  return true;
+}
+
+void ReportFoundMatch(uint16_t fid, uint16_t score) {
+  char msg[100];
+  snprintf(msg, sizeof(msg), "found id %d confidence %d", fid, score);
+  mqtt::Publish("match", msg);
+}
+
+void ScanLoop() {
+  if (!GetImage()) {
+    return;
+  }
+
+  if (!ConvertImage()) {
+    return;
+  }
+
+  uint16_t fid, score;
+  if (!SearchDatabase(&fid, &score)) {
+    return;
+  }
+
+  ReportFoundMatch(fid, score);
+}
+
+bool get_free_id(int16_t* fid) {
+  int16_t p = -1;
+  for (int page = 0; page < (params.capacity / FPM_TEMPLATES_PER_PAGE) + 1;
+       page++) {
+    p = finger.getFreeIndex(page, fid);
+    if (p != FPM_OK) {
+      PublishError("getFreeIndex", p);
+      return false;
+    }
+    if (*fid != FPM_NOFREEINDEX) {
+      char buf[100];
+      snprintf(buf, sizeof(buf), "getFreeIndex: Free slot at id %d", *fid);
+      mqtt::Publish("messages", buf);
+      return true;
+    }
+    yield();
+  }
+  mqtt::Publish("messages", "getFreeIndex: No free slots");
+  return false;
+}
+
+void WaitForRemove() {
+  int16_t p = -1;
+  mqtt::Publish("messages", "Remove finger");
+  delay(2000);
+  p = 0;
+  while (p != FPM_NOFINGER) {
+    p = finger.getImage();
+    yield();
+  }
+}
+
+void EnrollFailed() {
+  mqtt::Publish("messages", "exiting enroll");
+  BlinkError();
+  WaitForRemove();
+}
+
+void enroll_finger(int16_t fid) {
+  int16_t p = -1;
+  mqtt::Publish("messages", "Waiting for valid finger to enroll");
+  BlinkStartEnroll();
+  if (!GetImage()) {
+    return EnrollFailed();
+  }
+
+  if (!ConvertImage(1)) {
+    return EnrollFailed();
+  }
+
+  WaitForRemove();
+
+  BlinkStartEnrollRepeat();
+  mqtt::Publish("messages", "Place same finger again");
+  if (!GetImage()) {
+    return EnrollFailed();
+  }
+  if (!ConvertImage(2)) {
+    return EnrollFailed();
+  }
+
+  p = finger.createModel();
+  if (p == FPM_OK) {
+    mqtt::Publish("messages", "createModel: Prints matched");
+  } else {
+    PublishError("createModel", p);
+    return EnrollFailed();
+  }
+
+  p = finger.storeModel(fid);
+  if (p == FPM_OK) {
+    mqtt::Publish("messages", "Stored!");
+    BlinkSuccess();
+    WaitForRemove();
+    BlinkClearSuccess();
+    return;
+  } else {
+    PublishError("storeModel", p);
+    return EnrollFailed();
+  }
+}
+
+void DeleteFingerprint(uint16_t fid) {
+  int p = -1;
+
+  p = finger.deleteModel(fid);
+
+  if (p == FPM_OK) {
+    Serial.println("Deleted!");
+  } else {
+    PublishError("deleteModel", p);
+  }
+}
+
+void Enroll() {
+  BlinkStartEnroll();
+  mqtt::Publish("messages",
+                "Searching for a free slot to store the template...");
+  int16_t fid;
+  if (get_free_id(&fid)) {
+    enroll_finger(fid);
+  } else {
+    mqtt::Publish("messages", "No free slot in flash library!");
+    BlinkError();
+  }
+}
+
+void DownloadPrintImage(uint16_t fid) {}
+void DeleteAll() {}
+
+void Setup() {
+  fserial.begin(57600, SERIAL_8N1, 26 /*rx*/, 27 /*tx*/);
+
+  if (finger.begin()) {
+    finger.readParams(&params);
+    Serial.println("Found fingerprint sensor!");
+    Serial.print("Capacity: ");
+    Serial.println(params.capacity);
+    Serial.print("Packet length: ");
+    Serial.println(FPM::packet_lengths[params.packet_len]);
+  } else {
+    Serial.println("Did not find fingerprint sensor :(");
+    while (1) yield();
+  }
+  BlinkNotConnected();
+}
+}  // namespace fingerprint
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/espNode/desk/src/fingerprint.h	Mon Aug 24 01:27:33 2020 -0700
@@ -0,0 +1,20 @@
+#ifndef INCLUDED_FINGERPRINT
+#define INCLUDED_FINGERPRINT
+#include <FPM.h>
+#include <HardwareSerial.h>
+
+namespace fingerprint {
+void Setup();
+void ExecuteAnyQueued();
+void BlinkProgress();
+void BlinkNotConnected();
+void BlinkConnected();
+void QueueBlinkConnected(); // for inside an ISR
+void BlinkSuccess();
+void BlinkClearSuccess();
+void ScanLoop();
+void Enroll();
+void DownloadPrintImage(uint16_t fid);
+void DeleteAll();
+}
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/espNode/desk/src/main.cpp	Mon Aug 24 01:27:33 2020 -0700
@@ -0,0 +1,42 @@
+#include <Arduino.h>
+
+#include "display.h"
+#include "fingerprint.h"
+#include "mqtt.h"
+#include "wifi.h"
+
+#define ADC_EN 14
+#define ADC_PIN 34
+
+// #include <Button2.h>
+// #define BUTTON_1 35
+// #define BUTTON_2 0
+
+void setup() {
+  Serial.begin(115200);
+  Serial.println("Serial.begin");
+
+  fingerprint::Setup();  // go early since the others display status on our LED
+  display::Setup();
+  display::Message("Hello world");
+  wifi::Setup();
+  mqtt::Setup();
+}
+
+void loop() {
+  Serial.println("--loop--");
+  fingerprint::ExecuteAnyQueued();
+  fingerprint::ScanLoop();
+  if (mqtt::HasPendingCommand()) {
+    std::string cmd = mqtt::PopPendingCommand();
+    if (cmd == "enroll") {
+      fingerprint::Enroll();
+    } else if (cmd == "show_success") {
+      fingerprint::BlinkSuccess();
+      while (!mqtt::HasPendingCommand()) yield();
+      cmd = mqtt::PopPendingCommand();
+      // hope it's "clear_success", but who cares
+      fingerprint::BlinkClearSuccess();
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/espNode/desk/src/mqtt.cpp	Mon Aug 24 01:27:33 2020 -0700
@@ -0,0 +1,86 @@
+#include "mqtt.h"
+
+#include "config.h"
+#include "fingerprint.h"
+#include "wifi.h"
+
+namespace mqtt {
+AsyncMqttClient mqttClient;
+TimerHandle_t mqttReconnectTimer;
+std::string pendingCmd = "";
+
+void StopTimer() {
+  xTimerStop(mqttReconnectTimer,
+             0);  // ensure we don't reconnect to MQTT while reconnecting
+                  // to Wi-Fi
+}
+
+void Publish(std::string subtopic, std::string msg) {
+  std::string topic = "fingerprint/" + subtopic;
+  mqttClient.publish(topic.c_str(), 1, /*retain=*/false, msg.c_str());
+}
+
+void ConnectToMqtt() {
+  Serial.println("Connecting to MQTT...");
+  mqttClient.connect();
+}
+
+void SendTemperature() {
+  float temp_c = temperatureRead();
+  char buf[20];
+  snprintf(buf, sizeof(buf), "%.3fC", temp_c);
+  mqttClient.publish("fingerprint/temperature", 1, /*retain=*/true, buf);
+}
+void onMqttConnect(bool sessionPresent) {
+  Serial.println("Connected to MQTT.");
+  Serial.print("Session present: ");
+  Serial.println(sessionPresent);
+
+  mqttClient.subscribe("fingerprint/command", 1);
+
+  SendTemperature();
+
+  mqttClient.setWill("fingerprint/status", 1, /*retain=*/true, "offline");
+  mqttClient.publish("fingerprint/status", 1, /*retain=*/true, "online");
+    
+  Serial.println("queuing a blink change");
+  fingerprint::QueueBlinkConnected();
+}
+
+void onMqttDisconnect(AsyncMqttClientDisconnectReason reason) {
+  Serial.println("Disconnected from MQTT.");
+  fingerprint::BlinkNotConnected();
+
+  if (wifi::IsConnected()) {
+    xTimerStart(mqttReconnectTimer, 0);
+  }
+}
+
+void onMqttMessage(char* topic, char* payload,
+                   AsyncMqttClientMessageProperties properties, size_t len,
+                   size_t index, size_t total) {
+  std::string cmd(payload, len);
+  pendingCmd = cmd;
+}
+
+bool HasPendingCommand() {
+    return pendingCmd != "";
+}
+std::string PopPendingCommand() {
+    std::string cmd = pendingCmd;
+    pendingCmd = "";
+    return cmd;
+}
+
+void Setup() {
+  mqttReconnectTimer =
+      xTimerCreate("mqttTimer", pdMS_TO_TICKS(2000), pdFALSE, (void*)0,
+                   reinterpret_cast<TimerCallbackFunction_t>(ConnectToMqtt));
+
+  mqttClient.onConnect(onMqttConnect);
+  mqttClient.onDisconnect(onMqttDisconnect);
+  mqttClient.onMessage(onMqttMessage);
+  mqttClient.setServer(MQTT_HOST, MQTT_PORT);
+}
+
+}  // namespace mqtt
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/espNode/desk/src/mqtt.h	Mon Aug 24 01:27:33 2020 -0700
@@ -0,0 +1,24 @@
+#ifndef INCLUDED_MQTT
+#define INCLUDED_MQTT
+#include <AsyncMqttClient.h>
+
+#include <string>
+
+// #include "esp_adc_cal.h"
+
+extern "C" {
+#include "freertos/FreeRTOS.h"
+#include "freertos/timers.h"
+}
+
+namespace mqtt {
+
+void Setup();
+void Publish(std::string subtopic, std::string msg);
+void StopTimer();
+void ConnectToMqtt();
+bool HasPendingCommand();
+std::string PopPendingCommand();
+
+}  // namespace mqtt
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/espNode/desk/src/wifi.cpp	Mon Aug 24 01:27:33 2020 -0700
@@ -0,0 +1,45 @@
+#include "wifi.h"
+
+#include "config.h"
+#include "mqtt.h"
+#include "fingerprint.h"
+
+namespace wifi {
+
+TimerHandle_t wifiReconnectTimer;
+namespace {
+void connectToWifi() {
+  Serial.println("Connecting to Wi-Fi...");
+  fingerprint::BlinkNotConnected();
+  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
+}
+void WiFiEvent(WiFiEvent_t event) {
+  Serial.printf("[WiFi-event] event: %d\n", event);
+  switch (event) {
+    case SYSTEM_EVENT_STA_GOT_IP:
+      Serial.println("WiFi connected");
+      Serial.println("IP address: ");
+      Serial.println(WiFi.localIP());
+      mqtt::ConnectToMqtt();
+      break;
+    case SYSTEM_EVENT_STA_DISCONNECTED:
+      Serial.println("WiFi lost connection");
+      mqtt::StopTimer();
+      xTimerStart(wifiReconnectTimer, 0);
+      break;
+    default:
+      // ??
+      break;
+  }
+}
+}  // namespace
+void Setup() {
+  wifiReconnectTimer =
+      xTimerCreate("wifiTimer", pdMS_TO_TICKS(2000), pdFALSE, (void*)0,
+                   reinterpret_cast<TimerCallbackFunction_t>(connectToWifi));
+
+  WiFi.onEvent(WiFiEvent);
+  connectToWifi();
+}
+bool IsConnected() { return WiFi.isConnected(); }
+}  // namespace wifi
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/espNode/desk/src/wifi.h	Mon Aug 24 01:27:33 2020 -0700
@@ -0,0 +1,14 @@
+#ifndef INCLUDED_WIFI
+#define INCLUDED_WIFI
+
+#include "WiFi.h"
+extern "C" {
+#include "freertos/FreeRTOS.h"
+#include "freertos/timers.h"
+}
+
+namespace wifi {
+void Setup();
+bool IsConnected();
+}  // namespace wifi
+#endif
\ No newline at end of file