Merge pull request #18 from kitsunehunter/main

Add wiegand formats, decoding, enhance log file
This commit is contained in:
Derek Jamison 2023-12-03 13:30:15 -06:00 committed by GitHub
commit 49204805cf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 365 additions and 156 deletions

View File

@ -4,22 +4,25 @@ This application supports W4, W8, W24, W26, W32, W34, W37, W40 and W48 formats.
This application can be used to test Wiegand readers and keypads. It can save the data to a file, and can load and replay the data. Timings are measured and displayed; which can be used to help debug Wiegand readers. This application can be used to test Wiegand readers and keypads. It can save the data to a file, and can load and replay the data. Timings are measured and displayed; which can be used to help debug Wiegand readers.
## Wiring ## Wiring
The D0 and D1 wires of the Wiegand must be about 1K or higher for the Flipper to be able to effectively pull them down to ground. If the line has a lower resistance, like 100 ohms, then you need to add MOSFETs to help pull the lines low. The following [YouTube video](https://youtu.be/OVyd3ffnZ0M) is a demonstration of how to correctly wire the device. The D0 and D1 wires of the Wiegand must be about 1K or higher for the Flipper to be able to effectively pull them down to ground. If the line has a lower resistance, like 100 ohms, then you need to add MOSFETs to help pull the lines low. The following [YouTube video](https://youtu.be/OVyd3ffnZ0M) is a demonstration of how to correctly wire the device.
In all configurations: In all configurations:
- Pin A7 goes to your Wiegand D1 (white) wire. - Pin A7 goes to your Wiegand D1 (white) wire.
- Pin A4 goes to your Wiegand D0 (green) wire. - Pin A4 goes to your Wiegand D0 (green) wire.
- Pin GND goes to your Wiegand GND (black) wire. - Pin GND goes to your Wiegand GND (black) wire.
This is sufficient for reading signals with both this application and the Debug Accessor application. This is sufficient for reading signals with both this application and the Debug Accessor application.
If the pull-up resistors on your card reader are less than 1K, you also need the following: If the pull-up resistors on your card reader are less than 1K, you also need the following:
- Two additional MOSFETs are required. I used IRF540 (but perhaps IRL540 would be better?) - Two additional MOSFETs are required. I used IRF540 (but perhaps IRL540 would be better?)
- Two additional Pull-down resitors are required. I used 5K, but 4.7K would be fine as well. - Two additional Pull-down resitors are required. I used 5K, but 4.7K would be fine as well.
- I'm still learning, but perhaps a MOSFET driver / optocoupler would provide even more protection to the Flipper GPIO pins? Use this circuit at your own risk. :) - I'm still learning, but perhaps a MOSFET driver / optocoupler would provide even more protection to the Flipper GPIO pins? Use this circuit at your own risk. :)
Here is how you wire up the MOSFET and the pull-down resistors: Here is how you wire up the MOSFET and the pull-down resistors:
- The source pin of each MOSFET connects to ground. - The source pin of each MOSFET connects to ground.
- The gate pin connects to one side of a resistor. The other side of the resistor goes to ground. - The gate pin connects to one side of a resistor. The other side of the resistor goes to ground.
- For the MOSFET used for D1: - For the MOSFET used for D1:
@ -47,15 +50,25 @@ This is a 26-bit format used by many readers. The first bit is an even parity bi
This is similar to W26, but without the leading and trailing parity bits. The first 8 bits are the facility code. The next 16 bits are the card number. The application will display the facility code and card number. This is similar to W26, but without the leading and trailing parity bits. The first 8 bits are the facility code. The next 16 bits are the card number. The application will display the facility code and card number.
## W35: 35-bit Wiegand
This is HID 35 bit Corporate 1000 - C1k35s format. The first bit is odd parity 2 (based on bits 2-35). The next bit is even parity (based on 4-5,7-8,10-11,...,33-34). Then 12 bit company code. Then 20 bit card id. Then odd parity 1.
## W36: 36-bit Wiegand
This is decode HID 35 bit Keyscan - C15001 format. The first bit is an even parity bit. The next 10 bits are the OEM number. The next 8 bits are the facility code. The next 16 bits are the card number. The last bit is an odd parity bit.
Other 36 bit credentials may be decoded incorrectly.
## W48: 48-bit Wiegand ## W48: 48-bit Wiegand
This is HID 48 bit Corporate 1000 - H2004064 format. The first bit is odd parity 2 (based on bits 2-48). The next bit is even parity (based on 4-5,7-8,10-11,...,46-47). Then 22 bit company code. Then 23 bit card id. Then odd parity 1 (based on 3-4,6-7,9-10,...,45-46). This is HID 48 bit Corporate 1000 - C1k48s format. The first bit is odd parity 2 (based on bits 2-48). The next bit is even parity (based on 4-5,7-8,10-11,...,46-47). Then 22 bit company code. Then 23 bit card id. Then odd parity 1 (based on 3-4,6-7,9-10,...,45-46).
## W32/W34/W37/W40: 32/34/37/40-bit Wiegand ## W32/W34/W37/W40: 32/34/37/40-bit Wiegand
These formats are not very standardized, so the application will not try to interpret the data. You can modify the wiegand_data.c file to add your own interpretation. These formats are not very standardized, so the application will not try to interpret the data. You can modify the wiegand_data.c file to add your own interpretation.
## Installation ## Installation
### Step 1. Install Git and VS Code. ### Step 1. Install Git and VS Code.
If you have not already installed Git and VS Code, you will need to do so. The following links will take you to the download pages for Git and VS Code. If you have not already installed Git and VS Code, you will need to do so. The following links will take you to the download pages for Git and VS Code.

View File

@ -1,98 +1,131 @@
#include "../wiegand.h" #include "../wiegand.h"
void wiegand_add_info_4bit_8bit(FuriString* buffer) { void wiegand_add_info_4bit_8bit(FuriString *buffer)
if(bit_count == 8) { {
for(int i = 0; i < 4; i++) { if (bit_count == 8)
{
for (int i = 0; i < 4; i++)
{
furi_string_cat_printf( furi_string_cat_printf(
buffer, "\nbit %d: %d %d (bit %d)", i, data[i], data[i + 4], i + 4); buffer, "\nbit %d: %d %d (bit %d)", i, data[i], data[i + 4], i + 4);
if(data[i] == data[i + 4]) { if (data[i] == data[i + 4])
{
furi_string_cat_printf(buffer, " - ERROR"); furi_string_cat_printf(buffer, " - ERROR");
} else { }
else
{
furi_string_cat_printf(buffer, " - OK"); furi_string_cat_printf(buffer, " - OK");
} }
} }
} }
if(bit_count == 4 || bit_count == 8) { if (bit_count == 4 || bit_count == 8)
{
int code = 0; int code = 0;
int offset = bit_count == 4 ? 0 : 4; int offset = bit_count == 4 ? 0 : 4;
for(int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++)
{
code = code << 1; code = code << 1;
code += data[i + offset] ? 1 : 0; code += data[i + offset] ? 1 : 0;
} }
if(code <= 9) { if (code <= 9)
{
furi_string_cat_printf(buffer, "\nButton: %d", code); furi_string_cat_printf(buffer, "\nButton: %d", code);
} else if(code == 10) { }
else if (code == 10)
{
furi_string_cat_printf(buffer, "\nButton: Escape"); furi_string_cat_printf(buffer, "\nButton: Escape");
} else if(code == 11) { }
else if (code == 11)
{
furi_string_cat_printf(buffer, "\nButton: Enter"); furi_string_cat_printf(buffer, "\nButton: Enter");
} }
} }
} }
void wiegand_add_info_26bit(FuriString* buffer) { void wiegand_add_info_26bit(FuriString *buffer)
{
// 26 bit wiegand, the first bit is the even parity bit, which is // 26 bit wiegand, the first bit is the even parity bit, which is
// based on the next 12 bits. The number of bits that are a 1 should // based on the next 12 bits. The number of bits that are a 1 should
// be even. // be even.
int parity = 0;
if(data[0]) {
parity = 1;
}
for(int i = 1; i < 13; i++) {
if(data[i]) {
parity++;
}
}
if(parity % 2 == 0) {
furi_string_cat_printf(buffer, "\nEven Parity (%d): OK", parity);
} else {
furi_string_cat_printf(buffer, "\nEven Parity (%d): ERROR", parity);
}
// After the parity bit, the next 8 bits are the facility code. // After the parity bit, the next 8 bits are the facility code.
// Then the next 16 bits are the card id . // Then the next 16 bits are the card id .
furi_string_cat_printf(buffer, "\nFacility: 0x"); furi_string_cat_printf(buffer, "\nFacility: ");
int code = 0; int code = 0;
int count = 0; int count = 0;
uint32_t dec = 0; uint32_t dec = 0;
for(int i = 1; i < 25; i++) { for (int i = 1; i < 25; i++)
{
code = code << 1; code = code << 1;
dec = dec << 1; dec = dec << 1;
code |= data[i] ? 1 : 0; code |= data[i] ? 1 : 0;
dec |= data[i] ? 1 : 0; dec |= data[i] ? 1 : 0;
if(++count % 4 == 0) { if (++count % 4 == 0)
{
furi_string_cat_printf(buffer, "%X", code); furi_string_cat_printf(buffer, "%X", code);
code = 0; code = 0;
} }
if(i == 8) { if (i == 8)
{
furi_string_cat_printf(buffer, " (%ld)", dec); furi_string_cat_printf(buffer, " (%ld)", dec);
dec = 0; dec = 0;
} }
// Parity, then 8 bit facility code, then id. // Parity, then 8 bit facility code, then id.
if(i == 9) { if (i == 9)
furi_string_cat_printf(buffer, "\nId: 0x"); {
furi_string_cat_printf(buffer, "\nCard: ");
} }
} }
furi_string_cat_printf(buffer, " (%ld)", dec); furi_string_cat_printf(buffer, " (%ld)", dec);
if(data[13]) { int parity = 0;
if (data[0])
{
parity = 1; parity = 1;
} else {
parity = 0;
} }
for(int i = 14; i < 26; i++) { for (int i = 1; i < 13; i++)
if(data[i]) { {
if (data[i])
{
parity++; parity++;
} }
} }
if(parity % 2 == 0) { if (parity % 2 == 0)
{
furi_string_cat_printf(buffer, "\nEven Parity (%d): OK", parity);
}
else
{
furi_string_cat_printf(buffer, "\nEven Parity (%d): ERROR", parity);
}
if (data[13])
{
parity = 1;
}
else
{
parity = 0;
}
for (int i = 14; i < 26; i++)
{
if (data[i])
{
parity++;
}
}
if (parity % 2 == 0)
{
furi_string_cat_printf(buffer, "\nOdd Parity (%d): ERROR", parity); furi_string_cat_printf(buffer, "\nOdd Parity (%d): ERROR", parity);
} else { }
else
{
furi_string_cat_printf(buffer, "\nOdd Parity (%d): OK", parity); furi_string_cat_printf(buffer, "\nOdd Parity (%d): OK", parity);
} }
} }
void wiegand_add_info_24bit(FuriString* buffer) { void wiegand_add_info_24bit(FuriString *buffer)
{
// 24 bit wiegand (no parity info). // 24 bit wiegand (no parity info).
// The First 8 bits are the facility code. // The First 8 bits are the facility code.
@ -101,26 +134,30 @@ void wiegand_add_info_24bit(FuriString* buffer) {
int code = 0; int code = 0;
int count = 0; int count = 0;
uint32_t dec = 0; uint32_t dec = 0;
for(int i = 0; i < 24; i++) { for (int i = 0; i < 24; i++)
{
code = code << 1; code = code << 1;
dec = dec << 1; dec = dec << 1;
code |= data[i] ? 1 : 0; code |= data[i] ? 1 : 0;
dec |= data[i] ? 1 : 0; dec |= data[i] ? 1 : 0;
if(++count % 4 == 0) { if (++count % 4 == 0)
{
furi_string_cat_printf(buffer, "%X", code); furi_string_cat_printf(buffer, "%X", code);
code = 0; code = 0;
} }
// The first 8 bits are facility code, then comes id. // The first 8 bits are facility code, then comes id.
if(i == 8) { if (i == 8)
{
furi_string_cat_printf(buffer, " (%ld)", dec); furi_string_cat_printf(buffer, " (%ld)", dec);
dec = 0; dec = 0;
furi_string_cat_printf(buffer, "\nId: 0x"); furi_string_cat_printf(buffer, "\nCard: 0x");
} }
} }
furi_string_cat_printf(buffer, " (%ld)", dec); furi_string_cat_printf(buffer, " (%ld)", dec);
} }
void wiegand_add_info_48bit(FuriString* buffer) { void wiegand_add_info_48bit(FuriString *buffer)
{
// We assume this is HID 48 bit Corporate 1000 - H2004064 format. // We assume this is HID 48 bit Corporate 1000 - H2004064 format.
// The first bit is odd parity 2 (based on bits 2-48). // The first bit is odd parity 2 (based on bits 2-48).
// The next bit is even parity (based on 4-5,7-8,10-11,...,46-47). // The next bit is even parity (based on 4-5,7-8,10-11,...,46-47).
@ -130,15 +167,17 @@ void wiegand_add_info_48bit(FuriString* buffer) {
// 22 bits company code (bits 3-24; data[2..23]) // 22 bits company code (bits 3-24; data[2..23])
uint32_t code = 0; uint32_t code = 0;
for(int i = 2; i <= 23; i++) { for (int i = 2; i <= 23; i++)
{
code = code << 1; code = code << 1;
code |= data[i] ? 1 : 0; code |= data[i] ? 1 : 0;
} }
furi_string_cat_printf(buffer, "\nCompany: %lX (%ld)", code, code); furi_string_cat_printf(buffer, "\nFacility: %lX (%ld)", code, code);
// 23 bit card id (bits 25-47; data[24..46]). // 23 bit card id (bits 25-47; data[24..46]).
code = 0; code = 0;
for(int i = 24; i <= 46; i++) { for (int i = 24; i <= 46; i++)
{
code = code << 1; code = code << 1;
code |= data[i] ? 1 : 0; code |= data[i] ? 1 : 0;
} }
@ -147,45 +186,121 @@ void wiegand_add_info_48bit(FuriString* buffer) {
// TODO: Add the 3 parity checks. // TODO: Add the 3 parity checks.
} }
void wiegand_add_info(FuriString* buffer) { void wiegand_add_info_35bit(FuriString *buffer)
{
// We assume this is HID 35 bit Corporate 1000 - C1k35s format.
// 12 bits company code
uint32_t code = 0;
for (int i = 2; i <= 13; i++)
{
code = code << 1;
code |= data[i] ? 1 : 0;
}
furi_string_cat_printf(buffer, "\nFacility: %lX (%ld)", code, code);
// 20 bit card id
code = 0;
for (int i = 14; i <= 33; i++)
{
code = code << 1;
code |= data[i] ? 1 : 0;
}
furi_string_cat_printf(buffer, "\nCard: %lX (%ld)", code, code);
}
void wiegand_add_info_36bit(FuriString *buffer)
{
// We assume this is HID 36 bit Keyscan - C15001 format.
// 10 bits OEM
uint32_t oem = 0;
for (int i = 1; i <= 10; i++)
{
oem = (oem << 1) | (data[i] ? 1 : 0);
}
furi_string_cat_printf(buffer, "\nOEM: %lX (%ld)", oem, oem);
// 8 bits facility code
uint32_t facilityCode = 0;
for (int i = 11; i <= 18; i++)
{
facilityCode = (facilityCode << 1) | (data[i] ? 1 : 0);
}
furi_string_cat_printf(buffer, "\nFacility: %lX (%ld)", facilityCode, facilityCode);
// 16 bits card ID
uint32_t cardID = 0;
for (int i = 19; i <= 34; i++)
{
cardID = (cardID << 1) | (data[i] ? 1 : 0);
}
furi_string_cat_printf(buffer, "\nCard: %lX (%ld)", cardID, cardID);
}
void wiegand_add_info(FuriString *buffer)
{
furi_string_push_back(buffer, '\n'); furi_string_push_back(buffer, '\n');
if(bit_count == 4 || bit_count == 8) { if (bit_count == 4 || bit_count == 8)
{
wiegand_add_info_4bit_8bit(buffer); wiegand_add_info_4bit_8bit(buffer);
} else if(bit_count == 26) { }
else if (bit_count == 26)
{
wiegand_add_info_26bit(buffer); wiegand_add_info_26bit(buffer);
} else if(bit_count == 24) { }
else if (bit_count == 24)
{
wiegand_add_info_24bit(buffer); wiegand_add_info_24bit(buffer);
} else if(bit_count == 48) { }
else if (bit_count == 35)
{
wiegand_add_info_35bit(buffer);
}
else if (bit_count == 36)
{
wiegand_add_info_36bit(buffer);
}
else if (bit_count == 48)
{
wiegand_add_info_48bit(buffer); wiegand_add_info_48bit(buffer);
} }
furi_string_push_back(buffer, '\n'); furi_string_push_back(buffer, '\n');
} }
void wiegand_button_callback(GuiButtonType result, InputType type, void* context) { void wiegand_button_callback(GuiButtonType result, InputType type, void *context)
{
App *app = context; App *app = context;
if(type == InputTypeShort && result == GuiButtonTypeLeft) { if (type == InputTypeShort && result == GuiButtonTypeLeft)
{
view_dispatcher_send_custom_event(app->view_dispatcher, WiegandDataSceneSaveButtonEvent); view_dispatcher_send_custom_event(app->view_dispatcher, WiegandDataSceneSaveButtonEvent);
} else if(type == InputTypeShort && result == GuiButtonTypeCenter) { }
else if (type == InputTypeShort && result == GuiButtonTypeCenter)
{
view_dispatcher_send_custom_event(app->view_dispatcher, WiegandDataScenePlayButtonEvent); view_dispatcher_send_custom_event(app->view_dispatcher, WiegandDataScenePlayButtonEvent);
} }
} }
void wiegand_data_scene_on_enter(void* context) { void wiegand_data_scene_on_enter(void *context)
{
App *app = context; App *app = context;
widget_reset(app->widget); widget_reset(app->widget);
widget_add_string_element(app->widget, 0, 0, AlignLeft, AlignTop, FontPrimary, "Wiegand Data"); widget_add_string_element(app->widget, 0, 0, AlignLeft, AlignTop, FontPrimary, "Wiegand Data");
FuriString *buffer = furi_string_alloc(1024); FuriString *buffer = furi_string_alloc(1024);
furi_string_printf(buffer, "Bits: %d\n", bit_count); furi_string_printf(buffer, "Bits: %d\n", bit_count);
for(int i = 0; i < bit_count; i++) { for (int i = 0; i < bit_count; i++)
{
furi_string_push_back(buffer, data[i] ? '1' : '0'); furi_string_push_back(buffer, data[i] ? '1' : '0');
if((bit_count - i - 1) % 22 == 21) { if ((bit_count - i - 1) % 22 == 21)
{
furi_string_push_back(buffer, '\n'); furi_string_push_back(buffer, '\n');
} }
} }
furi_string_cat_printf(buffer, "\nPulse: %ld us", (data_rise[0] - data_fall[0]) / 64); // furi_string_cat_printf(buffer, "\nPulse: %ld us", (data_rise[0] - data_fall[0]) / 64);
furi_string_cat_printf(buffer, "\nPeriod: %ld us", (data_fall[1] - data_fall[0]) / 64); // furi_string_cat_printf(buffer, "\nPeriod: %ld us", (data_fall[1] - data_fall[0]) / 64);
wiegand_add_info(buffer); wiegand_add_info(buffer);
for(int i = 0; i < bit_count;) { for (int i = 0; i < bit_count;)
{
uint32_t pulse = (data_rise[i] - data_fall[i]) / 64; uint32_t pulse = (data_rise[i] - data_fall[i]) / 64;
i++; i++;
uint32_t period = (i < bit_count) ? (data_fall[i] - data_fall[i - 1]) / 64 : 0; uint32_t period = (i < bit_count) ? (data_fall[i] - data_fall[i - 1]) / 64 : 0;
@ -193,7 +308,8 @@ void wiegand_data_scene_on_enter(void* context) {
buffer, "\n%c : %ld us, %ld us", data[i] ? '1' : '0', pulse, period); buffer, "\n%c : %ld us, %ld us", data[i] ? '1' : '0', pulse, period);
} }
widget_add_text_scroll_element(app->widget, 0, 12, 128, 34, furi_string_get_cstr(buffer)); widget_add_text_scroll_element(app->widget, 0, 12, 128, 34, furi_string_get_cstr(buffer));
if(!data_saved) { if (!data_saved)
{
widget_add_button_element( widget_add_button_element(
app->widget, GuiButtonTypeLeft, "Save", wiegand_button_callback, app); app->widget, GuiButtonTypeLeft, "Save", wiegand_button_callback, app);
} }
@ -202,12 +318,15 @@ void wiegand_data_scene_on_enter(void* context) {
view_dispatcher_switch_to_view(app->view_dispatcher, WiegandWidgetView); view_dispatcher_switch_to_view(app->view_dispatcher, WiegandWidgetView);
} }
bool wiegand_data_scene_on_event(void* context, SceneManagerEvent event) { bool wiegand_data_scene_on_event(void *context, SceneManagerEvent event)
{
App *app = context; App *app = context;
bool consumed = false; bool consumed = false;
switch(event.type) { switch (event.type)
{
case SceneManagerEventTypeCustom: case SceneManagerEventTypeCustom:
switch(event.event) { switch (event.event)
{
case WiegandDataScenePlayButtonEvent: case WiegandDataScenePlayButtonEvent:
wiegand_play(); wiegand_play();
consumed = true; consumed = true;

View File

@ -1,40 +1,51 @@
#include "../wiegand.h" #include "../wiegand.h"
void wiegand_isr_d0(void* context) { void wiegand_isr_d0(void *context)
{
UNUSED(context); UNUSED(context);
uint32_t time = DWT->CYCCNT; uint32_t time = DWT->CYCCNT;
bool rise = furi_hal_gpio_read(pinD0); bool rise = furi_hal_gpio_read(pinD0);
data[bit_count] = 0; data[bit_count] = 0;
if(rise) { if (rise)
{
data_rise[bit_count] = time; data_rise[bit_count] = time;
if(bit_count < MAX_BITS) { if (bit_count < MAX_BITS)
{
bit_count++; bit_count++;
} }
} else { }
else
{
data_fall[bit_count] = time; data_fall[bit_count] = time;
} }
} }
void wiegand_isr_d1(void* context) { void wiegand_isr_d1(void *context)
{
UNUSED(context); UNUSED(context);
uint32_t time = DWT->CYCCNT; uint32_t time = DWT->CYCCNT;
bool rise = furi_hal_gpio_read(pinD1); bool rise = furi_hal_gpio_read(pinD1);
data[bit_count] = 1; data[bit_count] = 1;
if(rise) { if (rise)
{
data_rise[bit_count] = time; data_rise[bit_count] = time;
if(bit_count < MAX_BITS) { if (bit_count < MAX_BITS)
{
bit_count++; bit_count++;
} }
} else { }
else
{
data_fall[bit_count] = time; data_fall[bit_count] = time;
} }
} }
void wiegand_start_read(void* context) { void wiegand_start_read(void *context)
{
App *app = context; App *app = context;
data_saved = false; data_saved = false;
bit_count = 0; bit_count = 0;
@ -45,7 +56,8 @@ void wiegand_start_read(void* context) {
furi_timer_start(app->timer, 100); furi_timer_start(app->timer, 100);
} }
void wiegand_stop_read(void* context) { void wiegand_stop_read(void *context)
{
App *app = context; App *app = context;
furi_hal_gpio_remove_int_callback(pinD0); furi_hal_gpio_remove_int_callback(pinD0);
furi_hal_gpio_remove_int_callback(pinD1); furi_hal_gpio_remove_int_callback(pinD1);
@ -54,25 +66,31 @@ void wiegand_stop_read(void* context) {
furi_timer_stop(app->timer); furi_timer_stop(app->timer);
} }
void wiegand_timer_callback(void* context) { void wiegand_timer_callback(void *context)
{
App *app = context; App *app = context;
uint32_t duration = DWT->CYCCNT; uint32_t duration = DWT->CYCCNT;
const uint32_t one_millisecond = 64000; const uint32_t one_millisecond = 64000;
if(bit_count == 0) { if (bit_count == 0)
{
return; return;
} }
duration -= data_fall[bit_count - 1]; duration -= data_fall[bit_count - 1];
FURI_CRITICAL_ENTER(); FURI_CRITICAL_ENTER();
if(duration > 25 * one_millisecond) { if (duration > 25 * one_millisecond)
{
if (bit_count == 4 || bit_count == 8 || bit_count == 24 || bit_count == 26 || if (bit_count == 4 || bit_count == 8 || bit_count == 24 || bit_count == 26 ||
bit_count == 32 || bit_count == 34 || bit_count == 37 || bit_count == 40 || bit_count == 32 || bit_count == 34 || bit_count == 35 || bit_count == 36 ||
bit_count == 48) { bit_count == 37 || bit_count == 40 || bit_count == 48)
{
wiegand_stop_read(app); wiegand_stop_read(app);
scene_manager_next_scene(app->scene_manager, WiegandDataScene); scene_manager_next_scene(app->scene_manager, WiegandDataScene);
} else { }
else
{
// No data, clear // No data, clear
bit_count = 0; bit_count = 0;
} }
@ -80,7 +98,8 @@ void wiegand_timer_callback(void* context) {
FURI_CRITICAL_EXIT(); FURI_CRITICAL_EXIT();
} }
void wiegand_read_scene_on_enter(void* context) { void wiegand_read_scene_on_enter(void *context)
{
App *app = context; App *app = context;
widget_reset(app->widget); widget_reset(app->widget);
widget_add_string_element(app->widget, 0, 0, AlignLeft, AlignTop, FontPrimary, "Read Wiegand"); widget_add_string_element(app->widget, 0, 0, AlignLeft, AlignTop, FontPrimary, "Read Wiegand");
@ -94,7 +113,8 @@ void wiegand_read_scene_on_enter(void* context) {
view_dispatcher_switch_to_view(app->view_dispatcher, WiegandWidgetView); view_dispatcher_switch_to_view(app->view_dispatcher, WiegandWidgetView);
} }
void wiegand_read_scene_on_exit(void* context) { void wiegand_read_scene_on_exit(void *context)
{
App *app = context; App *app = context;
wiegand_stop_read(app); wiegand_stop_read(app);
} }

View File

@ -1,29 +1,38 @@
#include "../wiegand.h" #include "../wiegand.h"
void wiegand_data_scene_save_name_text_input_callback(void* context) { void wiegand_data_scene_save_name_text_input_callback(void *context)
{
App *app = context; App *app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, WiegandDataSceneSaveFileEvent); view_dispatcher_send_custom_event(app->view_dispatcher, WiegandDataSceneSaveFileEvent);
} }
void ensure_dir_exists(Storage* storage) { void ensure_dir_exists(Storage *storage)
{
// If apps_data directory doesn't exist, create it. // If apps_data directory doesn't exist, create it.
if(!storage_dir_exists(storage, WIEGAND_APPS_DATA_FOLDER)) { if (!storage_dir_exists(storage, WIEGAND_APPS_DATA_FOLDER))
{
FURI_LOG_I(TAG, "Creating directory: %s", WIEGAND_APPS_DATA_FOLDER); FURI_LOG_I(TAG, "Creating directory: %s", WIEGAND_APPS_DATA_FOLDER);
storage_simply_mkdir(storage, WIEGAND_APPS_DATA_FOLDER); storage_simply_mkdir(storage, WIEGAND_APPS_DATA_FOLDER);
} else { }
else
{
FURI_LOG_I(TAG, "Directory exists: %s", WIEGAND_APPS_DATA_FOLDER); FURI_LOG_I(TAG, "Directory exists: %s", WIEGAND_APPS_DATA_FOLDER);
} }
// If wiegand directory doesn't exist, create it. // If wiegand directory doesn't exist, create it.
if(!storage_dir_exists(storage, WIEGAND_SAVE_FOLDER)) { if (!storage_dir_exists(storage, WIEGAND_SAVE_FOLDER))
{
FURI_LOG_I(TAG, "Creating directory: %s", WIEGAND_SAVE_FOLDER); FURI_LOG_I(TAG, "Creating directory: %s", WIEGAND_SAVE_FOLDER);
storage_simply_mkdir(storage, WIEGAND_SAVE_FOLDER); storage_simply_mkdir(storage, WIEGAND_SAVE_FOLDER);
} else { }
else
{
FURI_LOG_I(TAG, "Directory exists: %s", WIEGAND_SAVE_FOLDER); FURI_LOG_I(TAG, "Directory exists: %s", WIEGAND_SAVE_FOLDER);
} }
} }
void wiegand_save(void* context) { void wiegand_save(void *context)
{
App *app = context; App *app = context;
FuriString *buffer = furi_string_alloc(1024); FuriString *buffer = furi_string_alloc(1024);
FuriString *file_path = furi_string_alloc(); FuriString *file_path = furi_string_alloc();
@ -34,7 +43,8 @@ void wiegand_save(void* context) {
ensure_dir_exists(storage); ensure_dir_exists(storage);
File *data_file = storage_file_alloc(storage); File *data_file = storage_file_alloc(storage);
if (storage_file_open( if (storage_file_open(
data_file, furi_string_get_cstr(file_path), FSAM_WRITE, FSOM_OPEN_ALWAYS)) { data_file, furi_string_get_cstr(file_path), FSAM_WRITE, FSOM_OPEN_ALWAYS))
{
furi_string_printf(buffer, "Filetype: Flipper Wiegand Key File\n"); furi_string_printf(buffer, "Filetype: Flipper Wiegand Key File\n");
storage_file_write(data_file, furi_string_get_cstr(buffer), furi_string_size(buffer)); storage_file_write(data_file, furi_string_get_cstr(buffer), furi_string_size(buffer));
furi_string_printf(buffer, "Version: 1\n"); furi_string_printf(buffer, "Version: 1\n");
@ -44,7 +54,8 @@ void wiegand_save(void* context) {
furi_string_printf(buffer, "Bits: %d\n", bit_count); furi_string_printf(buffer, "Bits: %d\n", bit_count);
storage_file_write(data_file, furi_string_get_cstr(buffer), furi_string_size(buffer)); storage_file_write(data_file, furi_string_get_cstr(buffer), furi_string_size(buffer));
furi_string_printf(buffer, "RAW_Data: "); furi_string_printf(buffer, "RAW_Data: ");
for(int i = 0; i < bit_count; i++) { for (int i = 0; i < bit_count; i++)
{
furi_string_cat_printf( furi_string_cat_printf(
buffer, buffer,
"D%d %ld %ld ", "D%d %ld %ld ",
@ -52,8 +63,28 @@ void wiegand_save(void* context) {
data_fall[i] - data_fall[0], data_fall[i] - data_fall[0],
data_rise[i] - data_fall[0]); data_rise[i] - data_fall[0]);
} }
furi_string_push_back(buffer, '\n'); furi_string_push_back(buffer, '\n');
storage_file_write(data_file, furi_string_get_cstr(buffer), furi_string_size(buffer)); storage_file_write(data_file, furi_string_get_cstr(buffer), furi_string_size(buffer));
furi_string_printf(buffer, "PACS_Binary: ");
for (int i = 0; i < bit_count; i++)
{
furi_string_cat_printf(buffer, "%d", data[i] ? 1 : 0);
}
furi_string_push_back(buffer, '\n');
storage_file_write(data_file, furi_string_get_cstr(buffer), furi_string_size(buffer));
furi_string_printf(buffer, "PM3_Command: hf ic encode --bin ");
for (int i = 0; i < bit_count; i++)
{
furi_string_cat_printf(buffer, "%d", data[i] ? 1 : 0);
}
furi_string_cat_printf(buffer, " --ki 0\n");
storage_file_write(data_file, furi_string_get_cstr(buffer), furi_string_size(buffer));
storage_file_close(data_file); storage_file_close(data_file);
} }
@ -63,7 +94,8 @@ void wiegand_save(void* context) {
furi_string_free(buffer); furi_string_free(buffer);
} }
void wiegand_save_scene_on_enter(void* context) { void wiegand_save_scene_on_enter(void *context)
{
App *app = context; App *app = context;
text_input_reset(app->text_input); text_input_reset(app->text_input);
@ -71,8 +103,8 @@ void wiegand_save_scene_on_enter(void* context) {
furi_hal_rtc_get_datetime(&datetime); furi_hal_rtc_get_datetime(&datetime);
snprintf( snprintf(
app->file_name, app->file_name,
25, 50,
"%02d%02d%02d_%02d%02d%02d", "%02d_%02d_%02d_%02d_%02d_%02d",
datetime.year, datetime.year,
datetime.month, datetime.month,
datetime.day, datetime.day,
@ -93,12 +125,15 @@ void wiegand_save_scene_on_enter(void* context) {
view_dispatcher_switch_to_view(app->view_dispatcher, WiegandTextInputView); view_dispatcher_switch_to_view(app->view_dispatcher, WiegandTextInputView);
} }
bool wiegand_save_scene_on_event(void* context, SceneManagerEvent event) { bool wiegand_save_scene_on_event(void *context, SceneManagerEvent event)
{
App *app = context; App *app = context;
bool consumed = false; bool consumed = false;
switch(event.type) { switch (event.type)
{
case SceneManagerEventTypeCustom: case SceneManagerEventTypeCustom:
switch(event.event) { switch (event.event)
{
case WiegandDataSceneSaveFileEvent: case WiegandDataSceneSaveFileEvent:
wiegand_save(app); wiegand_save(app);
data_saved = true; data_saved = true;

View File

@ -2,41 +2,52 @@
FuriTimer *timer = NULL; FuriTimer *timer = NULL;
static void wiegand_scan_isr_d0(void* context) { static void wiegand_scan_isr_d0(void *context)
{
UNUSED(context); UNUSED(context);
uint32_t time = DWT->CYCCNT; uint32_t time = DWT->CYCCNT;
bool rise = furi_hal_gpio_read(pinD0); bool rise = furi_hal_gpio_read(pinD0);
data[bit_count] = 0; data[bit_count] = 0;
if(rise) { if (rise)
{
data_rise[bit_count] = time; data_rise[bit_count] = time;
if(bit_count < MAX_BITS) { if (bit_count < MAX_BITS)
{
bit_count++; bit_count++;
} }
} else { }
else
{
data_fall[bit_count] = time; data_fall[bit_count] = time;
} }
} }
static void wiegand_scan_isr_d1(void* context) { static void wiegand_scan_isr_d1(void *context)
{
UNUSED(context); UNUSED(context);
uint32_t time = DWT->CYCCNT; uint32_t time = DWT->CYCCNT;
bool rise = furi_hal_gpio_read(pinD1); bool rise = furi_hal_gpio_read(pinD1);
data[bit_count] = 1; data[bit_count] = 1;
if(rise) { if (rise)
{
data_rise[bit_count] = time; data_rise[bit_count] = time;
if(bit_count < MAX_BITS) { if (bit_count < MAX_BITS)
{
bit_count++; bit_count++;
} }
} else { }
else
{
data_fall[bit_count] = time; data_fall[bit_count] = time;
} }
} }
static void wiegand_start_scan(void* context) { static void wiegand_start_scan(void *context)
{
UNUSED(context); UNUSED(context);
data_saved = false; data_saved = false;
bit_count = 0; bit_count = 0;
@ -47,7 +58,8 @@ static void wiegand_start_scan(void* context) {
furi_timer_start(timer, 100); furi_timer_start(timer, 100);
} }
static void wiegand_stop_scan(void* context) { static void wiegand_stop_scan(void *context)
{
UNUSED(context); UNUSED(context);
furi_hal_gpio_remove_int_callback(pinD0); furi_hal_gpio_remove_int_callback(pinD0);
furi_hal_gpio_remove_int_callback(pinD1); furi_hal_gpio_remove_int_callback(pinD1);
@ -56,15 +68,16 @@ static void wiegand_stop_scan(void* context) {
furi_timer_stop(timer); furi_timer_stop(timer);
} }
static void wiegand_scan_found(void* context) { static void wiegand_scan_found(void *context)
{
App *app = context; App *app = context;
FuriHalRtcDateTime datetime; FuriHalRtcDateTime datetime;
furi_hal_rtc_get_datetime(&datetime); furi_hal_rtc_get_datetime(&datetime);
snprintf( snprintf(
app->file_name, app->file_name,
25, 50,
"%02d%02d%02d_%02d%02d%02d", "%02d_%02d_%02d_%02d_%02d_%02d",
datetime.year, datetime.year,
datetime.month, datetime.month,
datetime.day, datetime.day,
@ -83,12 +96,14 @@ static void wiegand_scan_found(void* context) {
wiegand_start_scan(app); wiegand_start_scan(app);
} }
static void wiegand_scan_timer_callback(void* context) { static void wiegand_scan_timer_callback(void *context)
{
App *app = context; App *app = context;
uint32_t duration = DWT->CYCCNT; uint32_t duration = DWT->CYCCNT;
const uint32_t one_millisecond = 64000; const uint32_t one_millisecond = 64000;
if(bit_count == 0) { if (bit_count == 0)
{
return; return;
} }
@ -96,25 +111,31 @@ static void wiegand_scan_timer_callback(void* context) {
bool found = false; bool found = false;
FURI_CRITICAL_ENTER(); FURI_CRITICAL_ENTER();
if(duration > 25 * one_millisecond) { if (duration > 25 * one_millisecond)
{
if (bit_count == 4 || bit_count == 8 || bit_count == 24 || bit_count == 26 || if (bit_count == 4 || bit_count == 8 || bit_count == 24 || bit_count == 26 ||
bit_count == 32 || bit_count == 34 || bit_count == 37 || bit_count == 40 || bit_count == 32 || bit_count == 34 || bit_count == 35 || bit_count == 36 ||
bit_count == 48) { bit_count == 37 || bit_count == 40 || bit_count == 48)
{
wiegand_stop_scan(app); wiegand_stop_scan(app);
found = true; found = true;
} else { }
else
{
// No data, clear // No data, clear
bit_count = 0; bit_count = 0;
} }
} }
FURI_CRITICAL_EXIT(); FURI_CRITICAL_EXIT();
if(found) { if (found)
{
wiegand_scan_found(app); wiegand_scan_found(app);
} }
} }
void wiegand_scan_scene_on_enter(void* context) { void wiegand_scan_scene_on_enter(void *context)
{
App *app = context; App *app = context;
timer = furi_timer_alloc(wiegand_scan_timer_callback, FuriTimerTypePeriodic, app); timer = furi_timer_alloc(wiegand_scan_timer_callback, FuriTimerTypePeriodic, app);
widget_reset(app->widget); widget_reset(app->widget);
@ -131,7 +152,8 @@ void wiegand_scan_scene_on_enter(void* context) {
view_dispatcher_switch_to_view(app->view_dispatcher, WiegandWidgetView); view_dispatcher_switch_to_view(app->view_dispatcher, WiegandWidgetView);
} }
void wiegand_scan_scene_on_exit(void* context) { void wiegand_scan_scene_on_exit(void *context)
{
App *app = context; App *app = context;
wiegand_stop_scan(app); wiegand_stop_scan(app);
furi_timer_free(timer); furi_timer_free(timer);