Skip to content
This repository has been archived by the owner on Jul 28, 2024. It is now read-only.

Commit

Permalink
added "stats", increased stack size, github workflow
Browse files Browse the repository at this point in the history
  • Loading branch information
bmatcuk committed Dec 29, 2022
1 parent 4aac32b commit 56ae06e
Show file tree
Hide file tree
Showing 4 changed files with 192 additions and 34 deletions.
30 changes: 30 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: Release

on:
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+'

jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- name: Checkout Flipper Zero Firmware
uses: actions/checkout@v3
with:
repository: 'flipperdevices/flipperzero-firmware'
ref: '0.74.2'
submodules: true
- name: Checkout
uses: actions/checkout@v3
with:
path: 'applications_user/qrcode_app'
- name: Build
run: ./fbt fap_qrcode
- name: Publish
uses: softprops/action-gh-release@v1
with:
files: build/f7-firmware-D/.extapps/qrcode.fap
generate_release_notes: true
fail_on_unmatched_files: true
104 changes: 80 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,35 +1,19 @@
# flipperzero-qrcode
Display qrcodes on the [Flipper Zero]

## Building
First, clone the [flipperzero-firmware] repo and then clone this repo in the
`applications_user` directory:

```bash
git clone [email protected]:flipperdevices/flipperzero-firmware.git
cd flipperzero-firmware/applications_user
git clone [email protected]:bmatcuk/flipperzero-qrcode.git
```

Next, in the base of the [flipperzero-firmware] directory, run fbt:

```bash
cd ..
./fbt fap_qrcode
```

This will automatically install dependencies and build the application. When it
has finished building, the .fap will be in
`build/f7-firmware-D/.extapps/qrcode.fap` (fbt output will tell you where to
find the .fap, should it change in the future).
## Download
Grab the latest `qrcode.fap` from [Releases].

## Installation
Copy the `qrcode.fap` file onto your [Flipper Zero] sd card in the `apps/Tools`
directory. Then create a top level directory called `qrcodes` to store your
qrcode files.
qrcode files. This can be done using [qFlipper], for example, by
draging-and-dropping `qrcode.fap` into `apps/Tools` and then navigating back to
the top level (where the directories like `infrared` and `nfc` live), right
click, and create a new folder called `qrcodes`.

## Creating QR Codes
qrcode files are simple text files with the extension .qrcode. This app will
qrcode files are simple text files with the extension `.qrcode`. This app will
expect them to live in a top-level directory on your sd card called `qrcodes`.
They should have the following content:

Expand Down Expand Up @@ -76,7 +60,77 @@ A qrcode in binary mode can encode ~60% less data than numeric mode, and ~30%
less than alpha-numeric.

#### Kanji Mode
This mode is unsupported, so I won't go into detail.
This mode is unsupported, so I won't go into detail. A limitation of the
underlying qrcode library that I'm using, unfortunately. If there's interest,
perhaps I'll hack in support sometime.

## Using the App
The app is fairly straightforward. When it first starts, the file browser will
automatically open to the `qrcodes` directory and display any `.qrcode` files.
Select one using the arrow keys and the center button. The qrcode will display.
If you push the right arrow, some stats will display: the qrcode "Version" -
which corresponds to how big it is; the ECC level - which determines the
qrcode's resilience to damage, such as a dirty screen (Low, Medium, Quartile,
and High); and the qrcode Mode (Numeric, Alpha-Numeric, Binary, or Kanji). You
can hide the stats by pressing the left arrow.

When you're done viewing the qrcode, press the back button to return to the
file browser. If you push the back button in the file browser, the app will
exit.

I will ask that you temper your expectations: the Flipper Zero screen is small
and many readers may have difficulty reading the qrcodes, especially if they
are encoding a lot of data. I have successfully got my iPhone to read qrcodes
encoding phone numbers and wifi info, both of which are relatively short.

## Wifi QRCodes
Most phones can automatically connect to wifi networks from a qrcode. If you
should like to encode your wifi's connection info into a qrcode, here's how
you'd do it:

```
Filetype: QRCode
Version: 0
Message: WIFI:S:<ssid>;P:<password>;T:<encryption>;
```

Replace `<ssid>` with the name of your wifi, `<password>` with the password.
`<encryption>` would be "WPA" or "WEP". If your wifi is open (no password),
this can be "None" and you can remove `P:<password>;` from the message. If your
wifi is hidden (ie, does not broadcast the ssid), you can add `H:true;` to the
end.

Note that if your ssid or password contain any of these characters: `\";,:`,
you'll need to "escape" it by placing a backslash (`\`) before it.

For example, if my ssid was "wifiball" and not broadcast, and the password was
"pa$$:word" with WPA encryption, the message would be:

```
Message: WIFI:S:wifiball;P:pa$$\:word;T:WPA;H:true;
```

## Building
First, clone the [flipperzero-firmware] repo and then clone this repo in the
`applications_user` directory:

```bash
git clone [email protected]:flipperdevices/flipperzero-firmware.git
cd flipperzero-firmware/applications_user
git clone [email protected]:bmatcuk/flipperzero-qrcode.git
```

Next, in the base of the [flipperzero-firmware] directory, run fbt:

```bash
cd ..
./fbt fap_qrcode
```

This will automatically install dependencies and build the application. When it
has finished building, the .fap will be in
`build/f7-firmware-D/.extapps/qrcode.fap` (fbt output will tell you where to
find the .fap, should it change in the future).

## qrcode library
This application uses the [QRCode] library by ricmoo. This is the same library
Expand All @@ -88,3 +142,5 @@ compiler errors.
[flipperzero-firmware]: https://github.com/flipperdevices/flipperzero-firmware
[Flipper Zero]: https://flipperzero.one/
[QRCode]: https://github.com/ricmoo/QRCode
[qFlipper]: https://docs.flipperzero.one/qflipper
[Releases]: https://github.com/bmatcuk/flipperzero-qrcode/releases/latest
2 changes: 1 addition & 1 deletion application.fam
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ App(
fap_weburl="https://github.com/bmatcuk/flipperzero-qrcode",
apptype=FlipperAppType.EXTERNAL,
entry_point="qrcode_app",
stack_size=1024,
stack_size=2 * 1024,
cdefines=["APP_QRCODE"],
requires=[
"gui",
Expand Down
90 changes: 81 additions & 9 deletions qrcode_app.c
Original file line number Diff line number Diff line change
Expand Up @@ -55,28 +55,63 @@ typedef struct {

FuriMutex** mutex;
QRCode* qrcode;
bool loading;
bool too_long;
bool show_stats;
} QRCodeApp;

/**
* @param ecc ECC number
* @returns a character corresponding to the ecc level
*/
static char get_ecc_char(uint8_t ecc) {
switch (ecc) {
case 0: return 'L';
case 1: return 'M';
case 2: return 'Q';
case 3: return 'H';
default: return '?';
}
}

/**
* @param mode qrcode mode
* @returns a character corresponding to the mode
*/
static char get_mode_char(uint8_t mode) {
switch (mode) {
case 0: return 'N';
case 1: return 'A';
case 2: return 'B';
case 3: return 'K';
default: return '?';
}
}

/**
* Render
* @param canvas The canvas to render to
* @param ctx Context provided to the callback by view_port_draw_callback_set
*/
static void render_callback(Canvas* canvas, void* ctx) {
furi_assert(canvas);
furi_assert(ctx);

QRCodeApp* instance = ctx;
furi_check(furi_mutex_acquire(instance->mutex, FuriWaitForever) == FuriStatusOk);

canvas_clear(canvas);
canvas_set_color(canvas, ColorBlack);
canvas_set_font(canvas, FontPrimary);

uint8_t font_height = canvas_current_font_height(canvas);
uint8_t width = canvas_width(canvas);
uint8_t height = canvas_height(canvas);
if (instance->qrcode) {
uint8_t size = instance->qrcode->size;
uint8_t pixel_size = height / size;
uint8_t top = (height - pixel_size * size) / 2;
uint8_t left = (width - pixel_size * size) / 2;
uint8_t left = instance->show_stats ? top : (width - pixel_size * size) / 2;
for (uint8_t y = 0; y < size; y++) {
for (uint8_t x = 0; x < size; x++) {
if (qrcode_getModule(instance->qrcode, x, y)) {
Expand All @@ -88,10 +123,27 @@ static void render_callback(Canvas* canvas, void* ctx) {
}
}
}
} else {
canvas_set_font(canvas, FontPrimary);

uint8_t font_height = canvas_current_font_height(canvas);
if (instance->show_stats) {
if (top < 2) top = 2;
left += size * pixel_size + top;

FuriString* str = furi_string_alloc();

furi_string_printf(str, "Ver: %i", instance->qrcode->version);
canvas_draw_str(canvas, left, top + font_height, furi_string_get_cstr(str));

furi_string_printf(str, "ECC: %c", get_ecc_char(instance->qrcode->ecc));
canvas_draw_str(canvas, left, 2 * (top + font_height), furi_string_get_cstr(str));

furi_string_printf(str, "Mod: %c", get_mode_char(instance->qrcode->mode));
canvas_draw_str(canvas, left, 3 * (top + font_height), furi_string_get_cstr(str));

furi_string_free(str);
}
} else if (instance->loading) {
canvas_draw_str_aligned(canvas, width / 2, height / 2, AlignCenter, AlignCenter, "Loading...");
} else {
uint8_t margin = (height - font_height * 2) / 3;
canvas_draw_str_aligned(canvas, width / 2, margin, AlignCenter, AlignTop, "Could not load qrcode.");
if (instance->too_long) {
Expand All @@ -109,6 +161,8 @@ static void render_callback(Canvas* canvas, void* ctx) {
* @param ctx Context provided to the callback by view_port_input_callback_set
*/
static void input_callback(InputEvent* input_event, void* ctx) {
furi_assert(input_event);
furi_assert(ctx);
if (input_event->type == InputTypeShort) {
QRCodeApp* instance = ctx;
furi_message_queue_put(instance->input_queue, input_event, 0);
Expand All @@ -121,6 +175,7 @@ static void input_callback(InputEvent* input_event, void* ctx) {
* @returns true if the string is all numeric
*/
static bool is_numeric(const char* str, uint16_t len) {
furi_assert(str);
while (len > 0) {
char c = str[--len];
if (c < '0' || c > '9') return false;
Expand All @@ -134,6 +189,7 @@ static bool is_numeric(const char* str, uint16_t len) {
* @returns true if the string is alphanumeric
*/
static bool is_alphanumeric(const char* str, uint16_t len) {
furi_assert(str);
while (len > 0) {
char c = str[--len];
if (c >= '0' && c <= '9') continue;
Expand Down Expand Up @@ -169,6 +225,7 @@ static QRCode* qrcode_alloc(uint8_t version) {
* @param qrcode The QRCode to free
*/
static void qrcode_free(QRCode* qrcode) {
furi_assert(qrcode);
free(qrcode->modules);
free(qrcode);
}
Expand All @@ -180,12 +237,16 @@ static void qrcode_free(QRCode* qrcode) {
* @returns true if the string was successfully loaded
*/
static bool qrcode_load_string(QRCodeApp* instance, FuriString* str) {
furi_assert(instance);
furi_assert(str);

furi_check(furi_mutex_acquire(instance->mutex, FuriWaitForever) == FuriStatusOk);
if (instance->qrcode) {
qrcode_free(instance->qrcode);
instance->qrcode = NULL;
}
instance->too_long = false;
instance->show_stats = false;

bool result = false;
do {
Expand Down Expand Up @@ -238,6 +299,8 @@ static bool qrcode_load_string(QRCodeApp* instance, FuriString* str) {
result = true;
} while (false);

instance->loading = false;

furi_mutex_release(instance->mutex);

return result;
Expand Down Expand Up @@ -307,6 +370,11 @@ static QRCodeApp* qrcode_app_alloc() {

instance->mutex = furi_mutex_alloc(FuriMutexTypeNormal);

instance->qrcode = NULL;
instance->loading = true;
instance->too_long = false;
instance->show_stats = false;

return instance;
}

Expand All @@ -332,9 +400,7 @@ static void qrcode_app_free(QRCodeApp* instance) {
/** App entrypoint */
int32_t qrcode_app(void* p) {
QRCodeApp* instance = qrcode_app_alloc();

FuriString* file_path;
file_path = furi_string_alloc();
FuriString* file_path = furi_string_alloc();

do {
if (p && strlen(p)) {
Expand Down Expand Up @@ -371,19 +437,25 @@ int32_t qrcode_app(void* p) {
qrcode_free(instance->qrcode);
instance->qrcode = NULL;
}
instance->loading = true;
furi_mutex_release(instance->mutex);
break;
} else if (input.key == InputKeyRight) {
instance->show_stats = true;
} else if (input.key == InputKeyLeft) {
instance->show_stats = false;
}

furi_mutex_release(instance->mutex);
view_port_update(instance->view_port);
}

if (p && strlen(p)) {
// if started with an arg, exit instead of going to the browser
// if started with an arg, exit instead
// of looping back to the browser
break;
}
} while (1);
} while (true);

furi_string_free(file_path);
qrcode_app_free(instance);
Expand Down

0 comments on commit 56ae06e

Please sign in to comment.