new release
This commit is contained in:
@@ -174,23 +174,6 @@ auth_mode_t auth_get_mode(void) {
|
||||
return s_mode;
|
||||
}
|
||||
|
||||
const char *auth_mode_to_str(auth_mode_t mode) {
|
||||
switch (mode) {
|
||||
case AUTH_MODE_OPEN: return "open";
|
||||
case AUTH_MODE_LOCAL_RFID: return "local";
|
||||
case AUTH_MODE_OCPP_RFID: return "ocpp";
|
||||
default: return "open";
|
||||
}
|
||||
}
|
||||
|
||||
bool auth_mode_from_str(const char *s, auth_mode_t *out) {
|
||||
if (!s || !out) return false;
|
||||
if (!strcasecmp(s, "open")) { *out = AUTH_MODE_OPEN; return true; }
|
||||
if (!strcasecmp(s, "local")) { *out = AUTH_MODE_LOCAL_RFID; return true; }
|
||||
if (!strcasecmp(s, "ocpp")) { *out = AUTH_MODE_OCPP_RFID; return true; }
|
||||
return false;
|
||||
}
|
||||
|
||||
bool auth_add_tag(const char *tag) {
|
||||
if (tag_count >= MAX_TAGS) return false;
|
||||
if (!tag || strlen(tag) >= AUTH_TAG_MAX_LEN) return false;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <freertos/queue.h>
|
||||
@@ -7,61 +8,272 @@
|
||||
#include <wiegand.h>
|
||||
#include "auth.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define CONFIG_EXAMPLE_BUF_SIZE 50
|
||||
#define IDTAG_MAX_LEN 20
|
||||
|
||||
static const char *TAG = "WiegandReader";
|
||||
|
||||
// ---- Parâmetro global/locale para controlar a convenção de paridade ----
|
||||
static const bool PARITY_INVERTED = false; // mude para true se o seu leitor vier "invertido"
|
||||
|
||||
static wiegand_reader_t reader;
|
||||
static QueueHandle_t queue = NULL;
|
||||
|
||||
typedef struct {
|
||||
typedef struct
|
||||
{
|
||||
uint8_t data[CONFIG_EXAMPLE_BUF_SIZE];
|
||||
size_t bits;
|
||||
size_t bytes;
|
||||
} data_packet_t;
|
||||
|
||||
static void reader_callback(wiegand_reader_t *r) {
|
||||
data_packet_t p;
|
||||
p.bits = r->bits;
|
||||
memcpy(p.data, r->buf, CONFIG_EXAMPLE_BUF_SIZE);
|
||||
xQueueSendToBack(queue, &p, 0);
|
||||
static inline uint8_t get_bit_msb_first(const uint8_t *buf, size_t bit_index)
|
||||
{
|
||||
return (buf[bit_index / 8] >> (7 - (bit_index % 8))) & 0x01;
|
||||
}
|
||||
|
||||
static void wiegand_task(void *arg) {
|
||||
queue = xQueueCreate(5, sizeof(data_packet_t));
|
||||
if (!queue) {
|
||||
// Versão parametrizável
|
||||
static bool check_parity_param(const uint8_t *buf, size_t bits, bool invert)
|
||||
{
|
||||
if (bits != 26 && bits != 34)
|
||||
return false;
|
||||
|
||||
const int first_parity = get_bit_msb_first(buf, 0);
|
||||
const int last_parity = get_bit_msb_first(buf, bits - 1);
|
||||
|
||||
uint32_t left = 0, right = 0;
|
||||
|
||||
if (bits == 26)
|
||||
{
|
||||
for (int i = 1; i <= 12; i++)
|
||||
if (get_bit_msb_first(buf, i))
|
||||
left++;
|
||||
for (int i = 13; i <= 24; i++)
|
||||
if (get_bit_msb_first(buf, i))
|
||||
right++;
|
||||
}
|
||||
else
|
||||
{ // 34
|
||||
for (int i = 1; i <= 16; i++)
|
||||
if (get_bit_msb_first(buf, i))
|
||||
left++;
|
||||
for (int i = 17; i <= 32; i++)
|
||||
if (get_bit_msb_first(buf, i))
|
||||
right++;
|
||||
}
|
||||
|
||||
// Regra padrão (W26/W34 “normais”): primeiro = PAR (left), último = ÍMPAR (right)
|
||||
bool exp_first = (left % 2 == 0);
|
||||
bool exp_last = (right % 2 == 1);
|
||||
|
||||
// Se invertido, troca as expectativas (par <-> ímpar)
|
||||
if (invert)
|
||||
{
|
||||
exp_first = !exp_first;
|
||||
exp_last = !exp_last;
|
||||
}
|
||||
|
||||
return (first_parity == exp_first) && (last_parity == exp_last);
|
||||
}
|
||||
|
||||
// CRC-8/ATM (polinómio 0x07, init 0x00, sem ref, xor 0x00)
|
||||
static uint8_t crc8_atm(const uint8_t *data, size_t len)
|
||||
{
|
||||
uint8_t crc = 0x00;
|
||||
for (size_t i = 0; i < len; i++)
|
||||
{
|
||||
crc ^= data[i];
|
||||
for (int b = 0; b < 8; b++)
|
||||
{
|
||||
crc = (crc & 0x80) ? (uint8_t)((crc << 1) ^ 0x07) : (uint8_t)(crc << 1);
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
// CRC-16/IBM (CRC-16-ANSI) poly 0xA001 (refin/refout), init 0xFFFF
|
||||
static uint16_t crc16_ibm(const uint8_t *data, size_t len)
|
||||
{
|
||||
uint16_t crc = 0xFFFF;
|
||||
for (size_t i = 0; i < len; i++)
|
||||
{
|
||||
crc ^= data[i];
|
||||
for (int b = 0; b < 8; b++)
|
||||
{
|
||||
if (crc & 1)
|
||||
crc = (crc >> 1) ^ 0xA001;
|
||||
else
|
||||
crc >>= 1;
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
// Extrai payload (sem paridades) e devolve FC/CN para W26/34
|
||||
static bool wiegand_extract_fc_cn(const uint8_t *buf, size_t bits, uint32_t *fc, uint32_t *cn)
|
||||
{
|
||||
if (bits != 26 && bits != 34)
|
||||
return false;
|
||||
uint32_t payload = 0;
|
||||
size_t payload_bits = bits - 2;
|
||||
for (size_t i = 0; i < payload_bits; i++)
|
||||
{
|
||||
size_t bit = 1 + i; // ignora bit de paridade inicial
|
||||
uint8_t bitval = (buf[bit / 8] >> (7 - (bit % 8))) & 0x01;
|
||||
payload = (payload << 1) | bitval;
|
||||
}
|
||||
if (bits == 26)
|
||||
{
|
||||
*fc = (payload >> 16) & 0xFF; // 8b
|
||||
*cn = payload & 0xFFFF; // 16b
|
||||
}
|
||||
else
|
||||
{
|
||||
*fc = (payload >> 16) & 0xFFFF; // 16b
|
||||
*cn = payload & 0xFFFF; // 16b
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool build_idtag_w26_4B(uint32_t fc, uint32_t cn, char *out, size_t outlen)
|
||||
{
|
||||
uint8_t raw[4] = {
|
||||
(uint8_t)(fc & 0xFF),
|
||||
(uint8_t)((cn >> 8) & 0xFF),
|
||||
(uint8_t)(cn & 0xFF),
|
||||
0};
|
||||
raw[3] = crc8_atm(raw, 3);
|
||||
if (outlen < 9)
|
||||
return false;
|
||||
int n = snprintf(out, outlen, "%02X%02X%02X%02X", raw[0], raw[1], raw[2], raw[3]);
|
||||
return (n > 0 && (size_t)n < outlen);
|
||||
}
|
||||
|
||||
static bool build_idtag_w34_7B(uint32_t fc, uint32_t cn, char *out, size_t outlen)
|
||||
{
|
||||
uint8_t raw[7];
|
||||
raw[0] = 0x34;
|
||||
raw[1] = (uint8_t)((fc >> 8) & 0xFF);
|
||||
raw[2] = (uint8_t)(fc & 0xFF);
|
||||
raw[3] = (uint8_t)((cn >> 8) & 0xFF);
|
||||
raw[4] = (uint8_t)(cn & 0xFF);
|
||||
uint16_t crc = crc16_ibm(raw, 5);
|
||||
raw[5] = (uint8_t)((crc >> 8) & 0xFF);
|
||||
raw[6] = (uint8_t)(crc & 0xFF);
|
||||
if (outlen < 15)
|
||||
return false;
|
||||
int n = snprintf(out, outlen, "%02X%02X%02X%02X%02X%02X%02X",
|
||||
raw[0], raw[1], raw[2], raw[3], raw[4], raw[5], raw[6]);
|
||||
return (n > 0 && (size_t)n < outlen);
|
||||
}
|
||||
|
||||
// Se o callback for de ISR, troque para xQueueSendToBackFromISR.
|
||||
static void reader_callback(wiegand_reader_t *r)
|
||||
{
|
||||
data_packet_t p = {0};
|
||||
p.bits = r->bits;
|
||||
p.bytes = (r->bits + 7) / 8;
|
||||
if (p.bytes > sizeof(p.data))
|
||||
p.bytes = sizeof(p.data);
|
||||
memcpy(p.data, r->buf, p.bytes);
|
||||
|
||||
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||
// Se NÃO for ISR, use xQueueSendToBack(queue, &p, 0);
|
||||
xQueueSendToBackFromISR(queue, &p, &xHigherPriorityTaskWoken);
|
||||
if (xHigherPriorityTaskWoken)
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
|
||||
static void wiegand_task(void *arg)
|
||||
{
|
||||
queue = xQueueCreate(8, sizeof(data_packet_t));
|
||||
if (!queue)
|
||||
{
|
||||
ESP_LOGE(TAG, "Failed to create queue");
|
||||
vTaskDelete(NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_ERROR_CHECK(wiegand_reader_init(&reader, 21, 22,
|
||||
true, CONFIG_EXAMPLE_BUF_SIZE, reader_callback, WIEGAND_MSB_FIRST, WIEGAND_LSB_FIRST));
|
||||
true, CONFIG_EXAMPLE_BUF_SIZE, reader_callback, WIEGAND_MSB_FIRST, WIEGAND_LSB_FIRST));
|
||||
|
||||
data_packet_t p;
|
||||
while (1) {
|
||||
for (;;)
|
||||
{
|
||||
ESP_LOGI(TAG, "Waiting for Wiegand data...");
|
||||
if (xQueueReceive(queue, &p, portMAX_DELAY) == pdPASS) {
|
||||
ESP_LOGI(TAG, "Bits received: %d", p.bits);
|
||||
if (xQueueReceive(queue, &p, portMAX_DELAY) != pdPASS)
|
||||
continue;
|
||||
|
||||
char tag[20] = {0};
|
||||
ESP_LOGI(TAG, "Bits received: %d", (int)p.bits);
|
||||
|
||||
if (p.bits == 26) {
|
||||
snprintf(tag, sizeof(tag), "%03d%03d%03d", p.data[0], p.data[1], p.data[2]);
|
||||
} else if (p.bits == 34) {
|
||||
snprintf(tag, sizeof(tag), "%03d%03d%03d%03d", p.data[0], p.data[1], p.data[2], p.data[3]);
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Unsupported bit length: %d", (int)p.bits);
|
||||
ESP_LOG_BUFFER_HEX(TAG, p.data, sizeof(p.data)); // loga o buffer bruto
|
||||
continue;
|
||||
}
|
||||
if (!check_parity_param(p.data, p.bits, PARITY_INVERTED))
|
||||
{
|
||||
ESP_LOGW(TAG, "Parity check failed (%d bits). Dump:", (int)p.bits);
|
||||
ESP_LOG_BUFFER_HEX(TAG, p.data, p.bytes);
|
||||
continue;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Tag read: %s", tag);
|
||||
auth_process_tag(tag); // agora delega toda a lógica à auth.c
|
||||
char tag[21] = {0}; // OCPP 1.6: máx 20 chars (+NUL)
|
||||
uint32_t fc = 0, cn = 0;
|
||||
|
||||
if (!wiegand_extract_fc_cn(p.data, p.bits, &fc, &cn))
|
||||
{
|
||||
ESP_LOGW(TAG, "Unsupported bit length: %d", (int)p.bits);
|
||||
continue;
|
||||
}
|
||||
|
||||
bool ok = false;
|
||||
if (p.bits == 26)
|
||||
{
|
||||
ok = build_idtag_w26_4B(fc, cn, tag, sizeof(tag)); // 8 hex
|
||||
}
|
||||
else
|
||||
{ // 34
|
||||
ok = build_idtag_w34_7B(fc, cn, tag, sizeof(tag)); // 14 hex
|
||||
}
|
||||
|
||||
if (!ok)
|
||||
{
|
||||
ESP_LOGW(TAG, "Failed to build idTag");
|
||||
continue;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "idTag: %s", tag); // apenas [0-9A-F], alfanumérico
|
||||
auth_process_tag(tag);
|
||||
}
|
||||
}
|
||||
|
||||
static void wiegand_sim_task(void *arg)
|
||||
{
|
||||
// lista fixa de idTags simuladas
|
||||
static const char *idtaglist[] = {
|
||||
"0000004134962107",
|
||||
"W2602312345",
|
||||
"W34ABCDE12345",
|
||||
};
|
||||
const size_t list_size = sizeof(idtaglist) / sizeof(idtaglist[0]);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
for (size_t i = 0; i < list_size; i++)
|
||||
{
|
||||
ESP_LOGI(TAG, "Simulação -> idTag: %s", idtaglist[i]);
|
||||
auth_process_tag(idtaglist[i]);
|
||||
|
||||
// espera 20 segundos entre cada tag
|
||||
vTaskDelay(pdMS_TO_TICKS(30000));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void initWiegand(void) {
|
||||
void initWiegand(void)
|
||||
{
|
||||
ESP_LOGI(TAG, "Initializing Wiegand reader");
|
||||
xTaskCreate(wiegand_task, TAG, configMINIMAL_STACK_SIZE * 4, NULL, 4, NULL);
|
||||
|
||||
// ESP_LOGI(TAG, "Inicializando Wiegand simulado");
|
||||
// xTaskCreate(wiegand_sim_task, "WiegandSim", configMINIMAL_STACK_SIZE * 3, NULL, 3, NULL);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user