Offline Voice Recognition Sensor DFRobot

https://dfimg.dfrobot.com/nobody/wiki/4f868c5f5b2b9ae909271b3865939655.png

Zasilanie 3,3V – 5V

Obsługa modułu

Uczenie dodatkowej frazy wybudzającej

Aby nauczyć moduł nowej frazy wybudzającej (zawsze aktywne pozostaje „Hello Robot!„) należy po wywołaniu standardowym wygłosić frazę: Learning wake word„. Następnie podać nowe sformułowanie wybudzające. Ja podałem Hello Jane. Trzeba to zrobić trzykrotnie słuchając poleceń.

Kod dla Arduino z biblioteką DFRobot_DF2301QG v1.0

Według AI lepszym wyborem w przypadku DF2301QG jest język Arduino. Mamy stabilną bibliotekę dla połączenia I2C. Należy pamiętać, by ustawić Communication Mode Select w module w tej właśnie pozycji.

Biblioteka Arduino:  https://github.com/DFRobot/DFRobot_DF2301Q

Poniżej kod na mikrokontroler ESP32 połączony przez I2C z modułem DF2301QG v1.0. Łączy się z serwerem MQTT i wysyła ID usłyszanych komendy na mqtt_topic.

// Hello Robot!

#include <WiFi.h>
#include <PubSubClient.h>
#include <DFRobot_DF2301Q.h>
#include <Wire.h>
#include "config.h"

// ================= USTAWIENIA WI-FI =================
const char* ssid = WIFI_SSID;
const char* password = WIFI_PASSWORD;

// ================= USTAWIENIA MQTT =================
const char* mqtt_server = MQTT_SERVER;      
const int mqtt_port = MQTT_PORT;            
const char* mqtt_topic = MQTT_TOPIC_VOICE;  
const char* mqtt_client_id = MQTT_CLIENT;

// Piny I2C dla ESP32
#define I2C_SDA 21
#define I2C_SCL 22

// Inicjalizacja obiektów
WiFiClient espClient;
PubSubClient client(espClient);
DFRobot_DF2301Q_I2C asr;

// Zmienne do opóźnień
unsigned long lastReconnectAttempt = 0;
unsigned long lastVoiceCheck = 0;

void printProgramInfo() {
  while (!Serial) { ; }
  Serial.println();
  Serial.println("-----------------------------");
  Serial.println("--- Informacje o programie ---");
  Serial.print("Nazwa programu: ");
  Serial.println(__FILE__);
  Serial.print("Data kompilacji: ");
  Serial.print(__DATE__); 
  Serial.print(" "); 
  Serial.println(__TIME__); 
  Serial.println("-----------------------------");
}

// Funkcja łącząca z Wi-Fi
void setup_wifi() {
  delay(10);
  Serial.println();
  Serial.print("Łączenie z siecią Wi-Fi: ");
  Serial.println(ssid);

  WiFi.mode(WIFI_STA);
  WiFi.setAutoReconnect(true); 
  WiFi.persistent(true);
  
  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("\nPołączono z Wi-Fi!");
  Serial.print("Adres IP: ");
  Serial.println(WiFi.localIP());
}

// Funkcja MQTT
boolean reconnectMQTT() {
  Serial.print("Próba połączenia z MQTT...");
  if (client.connect(mqtt_client_id)) {
    Serial.println(" Połączono z Mosquitto!");
    return true;
  } else {
    Serial.print(" Błąd, rc=");
    Serial.print(client.state());
    Serial.println(" - kolejna próba za 5 sekund.");
    return false;
  }
}

void setup() {
  Serial.begin(115200);
  printProgramInfo();
  
  Wire.begin(I2C_SDA, I2C_SCL);

  setup_wifi();
  client.setServer(mqtt_server, mqtt_port);

  Serial.println("Inicjalizacja modułu DF2301Q...");
  if (!(asr.begin())) {
    Serial.println("Błąd komunikacji z modułem DF2301Q. Sprawdź połączenia I2C!");
    while (1) { yield(); }  
  }
  Serial.println("Moduł mowy zainicjowany pomyślnie.");

  asr.setVolume(7);     
  asr.setMuteMode(0);   
  asr.setWakeTime(20);  
}

void loop() {
  // 1. Sprawdzanie Wi-Fi
  if (WiFi.status() == WL_CONNECTED) {
    // 2. Obsługa MQTT (tylko jeśli jest Wi-Fi)
    if (!client.connected()) {
      unsigned long now = millis();
      if (now - lastReconnectAttempt > 5000) {
        lastReconnectAttempt = now;
        reconnectMQTT();
      }
    } else {
      client.loop(); // Podtrzymanie komunikacji z Mosquitto
    }
  }

  // 3. Odczyt komendy z czujnika mowy (ZAWSZE, NAWET BEZ MQTT!)
  // Zwiększony interwał z 50ms na 250ms dla stabilności szyny I2C
  unsigned long currentMillis = millis();
  if (currentMillis - lastVoiceCheck >= 250) {
    lastVoiceCheck = currentMillis;
    
    uint8_t CMDID = asr.getCMDID();

    // Dodano zabezpieczenie przed kodem błędu I2C (255)
    if (CMDID != 0 && CMDID != 98 && CMDID != 255) {
      Serial.print("&#x2705; Rozpoznano komendę o ID: ");
      Serial.println(CMDID);

      // 4. Publikacja MQTT - wysyłamy tylko wtedy, gdy serwer faktycznie nas słucha
      if (client.connected()) {
        char payload[10];
        itoa(CMDID, payload, 10);
        
        if (client.publish(mqtt_topic, payload, false)) {
          Serial.println("  -> Wysłano do MQTT pomyślnie.");
        } else {
          Serial.println("  -> Błąd wysyłania do MQTT.");
        }
      } else {
        Serial.println("  -> &#x26a0;&#xfe0f; Brak połączenia z MQTT! Komenda usłyszana, ale nie wysłana.");
      }
    }
  }
}

Tabela komend z ID (PDF)

Powered By EmbedPress

Flow dla Node-RED włączający i wyłączający światło (komenda 103, 104) podpięte do odbiornika na „topic”: „d1mini/urzadzenie1/set” ON, OFF

[
    {
        "id": "750214f6e6c63297",
        "type": "tab",
        "label": "Flow 3",
        "disabled": false,
        "info": "",
        "env": []
    },
    {
        "id": "s1",
        "type": "switch",
        "z": "750214f6e6c63297",
        "name": "Wybór ID",
        "property": "payload",
        "propertyType": "msg",
        "rules": [
            {
                "t": "eq",
                "v": "103",
                "vt": "num"
            },
            {
                "t": "eq",
                "v": "104",
                "vt": "num"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 2,
        "x": 360,
        "y": 140,
        "wires": [
            [
                "c1"
            ],
            [
                "c2"
            ]
        ]
    },
    {
        "id": "c1",
        "type": "change",
        "z": "750214f6e6c63297",
        "name": "Zamień na ON",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "OFF",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 560,
        "y": 120,
        "wires": [
            [
                "m1"
            ]
        ]
    },
    {
        "id": "c2",
        "type": "change",
        "z": "750214f6e6c63297",
        "name": "Zamień na OFF",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "ON",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 560,
        "y": 160,
        "wires": [
            [
                "m1"
            ]
        ]
    },
    {
        "id": "m1",
        "type": "mqtt out",
        "z": "750214f6e6c63297",
        "name": "",
        "topic": "d1mini/urzadzenie1/set",
        "qos": "1",
        "retain": "",
        "respTopic": "",
        "contentType": "",
        "userProps": "",
        "correl": "",
        "expiry": "",
        "broker": "be57f5ae9b4c6eb4",
        "x": 810,
        "y": 140,
        "wires": []
    },
    {
        "id": "31c29f1ad59174b1",
        "type": "mqtt in",
        "z": "750214f6e6c63297",
        "name": "Głos ESP32",
        "topic": "home/voice1/commands",
        "qos": "1",
        "datatype": "auto-detect",
        "broker": "be57f5ae9b4c6eb4",
        "nl": false,
        "rap": false,
        "inputs": 0,
        "x": 190,
        "y": 140,
        "wires": [
            [
                "s1",
                "ea3cb018ea7ed788"
            ]
        ]
    },
    {
        "id": "ea3cb018ea7ed788",
        "type": "debug",
        "z": "750214f6e6c63297",
        "name": "debug 2",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "statusVal": "",
        "statusType": "auto",
        "x": 320,
        "y": 240,
        "wires": []
    },
    {
        "id": "be57f5ae9b4c6eb4",
        "type": "mqtt-broker",
        "name": "",
        "broker": "localhost",
        "port": 1883,
        "clientid": "",
        "autoConnect": true,
        "usetls": false,
        "protocolVersion": 4,
        "keepalive": 60,
        "cleansession": true,
        "autoUnsubscribe": true,
        "birthTopic": "",
        "birthQos": "0",
        "birthRetain": "false",
        "birthPayload": "",
        "birthMsg": {},
        "closeTopic": "",
        "closeQos": "0",
        "closeRetain": "false",
        "closePayload": "",
        "closeMsg": {},
        "willTopic": "",
        "willQos": "0",
        "willRetain": "false",
        "willPayload": "",
        "willMsg": {},
        "userProps": "",
        "sessionExpiry": ""
    }
]

MICROPYTHON

Biblioteka MicroPython

Biblioteka lekko zmodyfikowana. W oryginalnej był błąd w metodzie get_cmdid(). Poprawiony przez Chat GPT.
Uwaga! Kody nie działają dla ESP8266. Prawdopodobnie problem tkwi w bibliotece I2C. Może trzeba użyć softI2C? Sprawdzę.
Wszystko dobrze działa na ESP32.

from micropython import const
from machine import I2C, Pin
from utime import sleep


DF2301Q_I2C_ADDR = const(0x64)


class DFRobot_DF2301Q_I2C:
    """
    MicroPython class for communication with the DF2301Q from DFRobot via I2C
    """

    DF2301Q_I2C_REG_CMDID = const(0x02)
    DF2301Q_I2C_REG_PLAY_CMDID = const(0x03)
    DF2301Q_I2C_REG_SET_MUTE = const(0x04)
    DF2301Q_I2C_REG_SET_VOLUME = const(0x05)
    DF2301Q_I2C_REG_WAKE_TIME = const(0x06)
    DF2301Q_I2C_8BIT_RANGE = const(0xFF)
    DF2301Q_I2C_PLAY_CMDID_DURATION = const(1)

    def __init__(self, sda, scl, i2c_addr=DF2301Q_I2C_ADDR, i2c_bus=0):
        """
        Initialize the DF2301Q I2C communication
        :param sda: I2C SDA pin
        :param scl: I2C SCL pin
        :param i2c_addr: I2C address
        :param i2c_bus: I2C bus number
        """
        self._addr = i2c_addr

        try:
            self._i2c = I2C(i2c_bus, sda=Pin(sda), scl=Pin(scl))
        except Exception as err:
            print(f'Could not initialize i2c! bus: {i2c_bus}, sda: {sda}, scl: {scl}, error: {err}')

    def _write_reg(self, reg, data) -> None:
        """
        Writes data to the I2C register
        :param reg: register address
        :param data: data to write
        :return: None
        """
        if isinstance(data, int):
            data = [data]

        try:
            self._i2c.writeto_mem(self._addr, reg, bytearray(data))
        except Exception as err:
            print(f'Write issue: {err}')

    def _read_reg(self, reg, length) -> bytes:
        """
        Reads data from the I2C register
        :param reg: register address
        :param length: number of bytes to read
        :return: bytes
        """
        try:
            result = self._i2c.readfrom_mem(self._addr, reg, length)
        except Exception as err:
            print(f'Read issue: {err}')
            result = bytearray(length)

        return result

    def get_cmdid(self) -> int:
        """
        Returns the current command id
        :return: int
        """
        data = self._read_reg(self.DF2301Q_I2C_REG_CMDID, 1)
        return int(data[0])

    def get_wake_time(self) -> int:
        """
        Returns the current wake-up duration
        :return: int
        """
        data = self._read_reg(self.DF2301Q_I2C_REG_WAKE_TIME, 1)
        return int(data[0])

    def play_by_cmdid(self, cmdid: int) -> None:
        """
        Play the current command words by command id
        :param cmdid: command words as integer
        :return: None
        """
        self._write_reg(self.DF2301Q_I2C_REG_PLAY_CMDID, int(cmdid))
        sleep(self.DF2301Q_I2C_PLAY_CMDID_DURATION)

    def set_wake_time(self, wake_time: int) -> None:
        """
        Set the wake-up duration of the device
        :param wake_time: integer between 0 and 255
        :return: None
        """
        wake_up_time = int(wake_time) & self.DF2301Q_I2C_8BIT_RANGE
        self._write_reg(self.DF2301Q_I2C_REG_WAKE_TIME, wake_up_time)

    def set_volume(self, vol: int) -> None:
        """
        Set the volume of the device
        :param vol: integer between 1 and 7
        :return: None
        """
        self._write_reg(self.DF2301Q_I2C_REG_SET_VOLUME, int(vol))

    def set_mute_mode(self, mode) -> None:
        """
        Set the mute mode of the device
        :param mode: integer 0 for off, 1 for on
        :return: None
        """
        self._write_reg(self.DF2301Q_I2C_REG_SET_MUTE, int(bool(mode)))



Kod programu wyświetlającego na konsoli numery komend głosowych

from DFRobot_DF2301Q_I2C import DFRobot_DF2301Q_I2C
from micropython import const
from utime import sleep


SDA_PIN = const(21)
SCL_PIN = const(22)
SLEEP_SECONDS = const(3)


def setup(sensor) -> None:
    """
    Set up the DFRobot DF2301Q sensor
    :param sensor: instance of DFRobot_DF2301Q_I2C
    :return: None
    """
    sensor.set_volume(5)
    sensor.set_mute_mode(0)
    sensor.set_wake_time(20)


def get_cmd_id(sensor) -> int:
    """
    Get the command id from the DF2301Q sensor
    :param sensor: instance of DFRobot_DF2301Q_I2C
    :return: int
    """
    command_id = sensor.get_cmdid()
    return command_id if command_id != 0 else None


if __name__ == "__main__":
    try:
        voice_sensor = DFRobot_DF2301Q_I2C(sda=SDA_PIN, scl=SCL_PIN)
        setup(sensor=voice_sensor)

        print('Speak your commands:')

        while True:
            cmd_id = get_cmd_id(sensor=voice_sensor)

            if cmd_id is not None:
                print(f'COMMAND ID: {cmd_id}')

            sleep(SLEEP_SECONDS)
    except Exception as e:
        print(f'Initialization error: {e}')


LINKI

Źródła:

Konfiguracja i działanie

Trening

Inne

Dodaj komentarz