Can't get GT-NUCL1633K1 fingerprint reader to work

I’m trying to interface an ATmega328P with a GT-NUCL1633K1 sensor over UART. When I send commands to the sensor, it responds just by echoing the command, instead of actually doing anything.

For example, I’m trying to execute the 0xA0 command with Flag set to 1 (page 8 in the sensor’s programming guide) using the following code.

cmd: F5 A0 00 00 01 00 A1 F5   (open and send dev info)
res: F5 A0 00 00 01 00 A1 F5

According to the programming guide, the 01 in the fifth position means ACK_FAIL (execute failed). So, I tried the following command sequence:

cmd: F5 A0 00 00 00 00 A1 F5   (open)
res: F5 A0 00 00 00 00 A1 F5

cmd: F5 B4 00 00 00 00 B4 F5   (LED on)
res: F5 B4 00 00 00 00 B4 F5

cmd: F5 0D 00 00 00 00 0D F5   (Get an unused ID between 1 and N)
res: F5 0D 00 00 00 00 0D F5

As you can see, the response from the sensor just echoes my command. My driver code for the sensor:

#include <stdio.h>

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <util/setbaud.h>

#include "nrfm.h"
#include "finprint.h"

#define FINPRINT_PIN    PC5
#define FINPRINT_DDR    DDRC
#define FINPRINT_PORT   PORTC

#define LEN(a)  (sizeof(a) / sizeof(a[0]))

static uint8_t txaddr[] = { 194, 178, 83 };

static inline void send(uint8_t c)
{
	while (!(UCSR0A & (1 << UDRE0)))
		;
	UDR0 = c;
}

static inline void enable(void)
{
	FINPRINT_PORT |= (1 << FINPRINT_PIN);
	_delay_ms(100);
}

static inline void disable(void)
{
	FINPRINT_PORT &= ~(1 << FINPRINT_PIN);
}

static inline void sendcmd(uint8_t cm, uint8_t p1, 
	uint8_t p2, uint8_t p3, uint8_t p4)
{
	uint8_t cs;

	cs = cm ^ p1 ^ p2 ^ p3 ^ p4;

	send(0xF5);
	send(cm);
	send(p1);
	send(p2);
	send(p3);
	send(p4);
	send(cs);
	send(0xF5);
}

void finprint_init(void)
{
	UBRR0H = UBRRH_VALUE;
	UBRR0L = UBRRL_VALUE;
#if USE_2X
	UCSR0A |= (1 << U2X0);
#else
	UCSR0A &= ~(1 << U2X0);
#endif
	UCSR0B = (1 << TXEN0) | (1 << RXEN0) | (1 << RXCIE0);
	UCSR0C = (1 << UCSZ01) | (1 << UCSZ00);

	FINPRINT_DDR |= (1 << FINPRINT_PIN);
	FINPRINT_PORT &= ~(1 << FINPRINT_PIN);
}

void finprint_info(uint8_t buf[8]) 
{
	PORTC &= ~(1 << PC4);
	enable();

	sendcmd(0xA0, 0, 0, 0, 0);
	_delay_ms(500);
	sendcmd(0xB4, 0, 0, 0, 0);
	_delay_ms(500);
	sendcmd(0x0D, 0, 0, 0, 0);
	
	//disable();
}

ISR(USART_RX_vect) 
{
	char s[5];

	PORTC |= (1 << PC4);
	snprintf(s, 5, "0x%02X", UDR0);
	radio_sendto(txaddr, s, 4);
	PORTC &= ~(1 << PC4);
}

Calling from main.c:

#include <avr/interrupt.h>
#include <util/delay.h>

#include "nrfm.h"
#include "finprint.h"

int main(void)
{
	char buf[8];
	uint8_t rxaddr[] = { 194, 178, 82 };
	uint8_t txaddr[] = { 194, 178, 83 };

	finprint_init();
	radio_init(rxaddr);
	
	sei();

	finprint_info((uint8_t *) buf);

	for (;;) {
		_delay_ms(2000);
	}

	return 0;
}

According to the programming guide, the expected response for the last command is

cmd: F5 0D 00 00 00 00 0D F5   (Get unused ID)
res: F5 0D 00 01 00 09 0F F5

The sensor operates at 3.3V. So, I’m connecting the VCC, GND, TXD and RXD of the sensor to PC5, GND, RXD, and TXD of the ATmega328P respectively via a bi-directional logic level converter. I’m using the default baudrate, which is supposed to be 115200, with 1 start and stop bit.

I verified the TXD and RXD signals using an oscilloscope. The signals on the oscilloscope match the ones I get from the RX interrupt.

Has anyone gotten one of these things to work successfully? What am I doing wrong :frowning:

I pitched this to an LLM and it had some suggestions, I’d run through those to start

Chatgpt mostly has the same ideas, here’s its link