Skip to content

Commit 56ae06e

Browse files
committed
added "stats", increased stack size, github workflow
1 parent 4aac32b commit 56ae06e

File tree

4 files changed

+192
-34
lines changed

4 files changed

+192
-34
lines changed

.github/workflows/release.yml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
tags:
6+
- 'v[0-9]+.[0-9]+.[0-9]+'
7+
8+
jobs:
9+
build:
10+
name: Build
11+
runs-on: ubuntu-latest
12+
steps:
13+
- name: Checkout Flipper Zero Firmware
14+
uses: actions/checkout@v3
15+
with:
16+
repository: 'flipperdevices/flipperzero-firmware'
17+
ref: '0.74.2'
18+
submodules: true
19+
- name: Checkout
20+
uses: actions/checkout@v3
21+
with:
22+
path: 'applications_user/qrcode_app'
23+
- name: Build
24+
run: ./fbt fap_qrcode
25+
- name: Publish
26+
uses: softprops/action-gh-release@v1
27+
with:
28+
files: build/f7-firmware-D/.extapps/qrcode.fap
29+
generate_release_notes: true
30+
fail_on_unmatched_files: true

README.md

Lines changed: 80 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,19 @@
11
# flipperzero-qrcode
22
Display qrcodes on the [Flipper Zero]
33

4-
## Building
5-
First, clone the [flipperzero-firmware] repo and then clone this repo in the
6-
`applications_user` directory:
7-
8-
```bash
9-
git clone [email protected]:flipperdevices/flipperzero-firmware.git
10-
cd flipperzero-firmware/applications_user
11-
git clone [email protected]:bmatcuk/flipperzero-qrcode.git
12-
```
13-
14-
Next, in the base of the [flipperzero-firmware] directory, run fbt:
15-
16-
```bash
17-
cd ..
18-
./fbt fap_qrcode
19-
```
20-
21-
This will automatically install dependencies and build the application. When it
22-
has finished building, the .fap will be in
23-
`build/f7-firmware-D/.extapps/qrcode.fap` (fbt output will tell you where to
24-
find the .fap, should it change in the future).
4+
## Download
5+
Grab the latest `qrcode.fap` from [Releases].
256

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

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

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

7862
#### Kanji Mode
79-
This mode is unsupported, so I won't go into detail.
63+
This mode is unsupported, so I won't go into detail. A limitation of the
64+
underlying qrcode library that I'm using, unfortunately. If there's interest,
65+
perhaps I'll hack in support sometime.
66+
67+
## Using the App
68+
The app is fairly straightforward. When it first starts, the file browser will
69+
automatically open to the `qrcodes` directory and display any `.qrcode` files.
70+
Select one using the arrow keys and the center button. The qrcode will display.
71+
If you push the right arrow, some stats will display: the qrcode "Version" -
72+
which corresponds to how big it is; the ECC level - which determines the
73+
qrcode's resilience to damage, such as a dirty screen (Low, Medium, Quartile,
74+
and High); and the qrcode Mode (Numeric, Alpha-Numeric, Binary, or Kanji). You
75+
can hide the stats by pressing the left arrow.
76+
77+
When you're done viewing the qrcode, press the back button to return to the
78+
file browser. If you push the back button in the file browser, the app will
79+
exit.
80+
81+
I will ask that you temper your expectations: the Flipper Zero screen is small
82+
and many readers may have difficulty reading the qrcodes, especially if they
83+
are encoding a lot of data. I have successfully got my iPhone to read qrcodes
84+
encoding phone numbers and wifi info, both of which are relatively short.
85+
86+
## Wifi QRCodes
87+
Most phones can automatically connect to wifi networks from a qrcode. If you
88+
should like to encode your wifi's connection info into a qrcode, here's how
89+
you'd do it:
90+
91+
```
92+
Filetype: QRCode
93+
Version: 0
94+
Message: WIFI:S:<ssid>;P:<password>;T:<encryption>;
95+
```
96+
97+
Replace `<ssid>` with the name of your wifi, `<password>` with the password.
98+
`<encryption>` would be "WPA" or "WEP". If your wifi is open (no password),
99+
this can be "None" and you can remove `P:<password>;` from the message. If your
100+
wifi is hidden (ie, does not broadcast the ssid), you can add `H:true;` to the
101+
end.
102+
103+
Note that if your ssid or password contain any of these characters: `\";,:`,
104+
you'll need to "escape" it by placing a backslash (`\`) before it.
105+
106+
For example, if my ssid was "wifiball" and not broadcast, and the password was
107+
"pa$$:word" with WPA encryption, the message would be:
108+
109+
```
110+
Message: WIFI:S:wifiball;P:pa$$\:word;T:WPA;H:true;
111+
```
112+
113+
## Building
114+
First, clone the [flipperzero-firmware] repo and then clone this repo in the
115+
`applications_user` directory:
116+
117+
```bash
118+
git clone [email protected]:flipperdevices/flipperzero-firmware.git
119+
cd flipperzero-firmware/applications_user
120+
git clone [email protected]:bmatcuk/flipperzero-qrcode.git
121+
```
122+
123+
Next, in the base of the [flipperzero-firmware] directory, run fbt:
124+
125+
```bash
126+
cd ..
127+
./fbt fap_qrcode
128+
```
129+
130+
This will automatically install dependencies and build the application. When it
131+
has finished building, the .fap will be in
132+
`build/f7-firmware-D/.extapps/qrcode.fap` (fbt output will tell you where to
133+
find the .fap, should it change in the future).
80134

81135
## qrcode library
82136
This application uses the [QRCode] library by ricmoo. This is the same library
@@ -88,3 +142,5 @@ compiler errors.
88142
[flipperzero-firmware]: https://github.com/flipperdevices/flipperzero-firmware
89143
[Flipper Zero]: https://flipperzero.one/
90144
[QRCode]: https://github.com/ricmoo/QRCode
145+
[qFlipper]: https://docs.flipperzero.one/qflipper
146+
[Releases]: https://github.com/bmatcuk/flipperzero-qrcode/releases/latest

application.fam

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ App(
66
fap_weburl="https://github.com/bmatcuk/flipperzero-qrcode",
77
apptype=FlipperAppType.EXTERNAL,
88
entry_point="qrcode_app",
9-
stack_size=1024,
9+
stack_size=2 * 1024,
1010
cdefines=["APP_QRCODE"],
1111
requires=[
1212
"gui",

qrcode_app.c

Lines changed: 81 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -55,28 +55,63 @@ typedef struct {
5555

5656
FuriMutex** mutex;
5757
QRCode* qrcode;
58+
bool loading;
5859
bool too_long;
60+
bool show_stats;
5961
} QRCodeApp;
6062

63+
/**
64+
* @param ecc ECC number
65+
* @returns a character corresponding to the ecc level
66+
*/
67+
static char get_ecc_char(uint8_t ecc) {
68+
switch (ecc) {
69+
case 0: return 'L';
70+
case 1: return 'M';
71+
case 2: return 'Q';
72+
case 3: return 'H';
73+
default: return '?';
74+
}
75+
}
76+
77+
/**
78+
* @param mode qrcode mode
79+
* @returns a character corresponding to the mode
80+
*/
81+
static char get_mode_char(uint8_t mode) {
82+
switch (mode) {
83+
case 0: return 'N';
84+
case 1: return 'A';
85+
case 2: return 'B';
86+
case 3: return 'K';
87+
default: return '?';
88+
}
89+
}
90+
6191
/**
6292
* Render
6393
* @param canvas The canvas to render to
6494
* @param ctx Context provided to the callback by view_port_draw_callback_set
6595
*/
6696
static void render_callback(Canvas* canvas, void* ctx) {
97+
furi_assert(canvas);
98+
furi_assert(ctx);
99+
67100
QRCodeApp* instance = ctx;
68101
furi_check(furi_mutex_acquire(instance->mutex, FuriWaitForever) == FuriStatusOk);
69102

70103
canvas_clear(canvas);
71104
canvas_set_color(canvas, ColorBlack);
105+
canvas_set_font(canvas, FontPrimary);
72106

107+
uint8_t font_height = canvas_current_font_height(canvas);
73108
uint8_t width = canvas_width(canvas);
74109
uint8_t height = canvas_height(canvas);
75110
if (instance->qrcode) {
76111
uint8_t size = instance->qrcode->size;
77112
uint8_t pixel_size = height / size;
78113
uint8_t top = (height - pixel_size * size) / 2;
79-
uint8_t left = (width - pixel_size * size) / 2;
114+
uint8_t left = instance->show_stats ? top : (width - pixel_size * size) / 2;
80115
for (uint8_t y = 0; y < size; y++) {
81116
for (uint8_t x = 0; x < size; x++) {
82117
if (qrcode_getModule(instance->qrcode, x, y)) {
@@ -88,10 +123,27 @@ static void render_callback(Canvas* canvas, void* ctx) {
88123
}
89124
}
90125
}
91-
} else {
92-
canvas_set_font(canvas, FontPrimary);
93126

94-
uint8_t font_height = canvas_current_font_height(canvas);
127+
if (instance->show_stats) {
128+
if (top < 2) top = 2;
129+
left += size * pixel_size + top;
130+
131+
FuriString* str = furi_string_alloc();
132+
133+
furi_string_printf(str, "Ver: %i", instance->qrcode->version);
134+
canvas_draw_str(canvas, left, top + font_height, furi_string_get_cstr(str));
135+
136+
furi_string_printf(str, "ECC: %c", get_ecc_char(instance->qrcode->ecc));
137+
canvas_draw_str(canvas, left, 2 * (top + font_height), furi_string_get_cstr(str));
138+
139+
furi_string_printf(str, "Mod: %c", get_mode_char(instance->qrcode->mode));
140+
canvas_draw_str(canvas, left, 3 * (top + font_height), furi_string_get_cstr(str));
141+
142+
furi_string_free(str);
143+
}
144+
} else if (instance->loading) {
145+
canvas_draw_str_aligned(canvas, width / 2, height / 2, AlignCenter, AlignCenter, "Loading...");
146+
} else {
95147
uint8_t margin = (height - font_height * 2) / 3;
96148
canvas_draw_str_aligned(canvas, width / 2, margin, AlignCenter, AlignTop, "Could not load qrcode.");
97149
if (instance->too_long) {
@@ -109,6 +161,8 @@ static void render_callback(Canvas* canvas, void* ctx) {
109161
* @param ctx Context provided to the callback by view_port_input_callback_set
110162
*/
111163
static void input_callback(InputEvent* input_event, void* ctx) {
164+
furi_assert(input_event);
165+
furi_assert(ctx);
112166
if (input_event->type == InputTypeShort) {
113167
QRCodeApp* instance = ctx;
114168
furi_message_queue_put(instance->input_queue, input_event, 0);
@@ -121,6 +175,7 @@ static void input_callback(InputEvent* input_event, void* ctx) {
121175
* @returns true if the string is all numeric
122176
*/
123177
static bool is_numeric(const char* str, uint16_t len) {
178+
furi_assert(str);
124179
while (len > 0) {
125180
char c = str[--len];
126181
if (c < '0' || c > '9') return false;
@@ -134,6 +189,7 @@ static bool is_numeric(const char* str, uint16_t len) {
134189
* @returns true if the string is alphanumeric
135190
*/
136191
static bool is_alphanumeric(const char* str, uint16_t len) {
192+
furi_assert(str);
137193
while (len > 0) {
138194
char c = str[--len];
139195
if (c >= '0' && c <= '9') continue;
@@ -169,6 +225,7 @@ static QRCode* qrcode_alloc(uint8_t version) {
169225
* @param qrcode The QRCode to free
170226
*/
171227
static void qrcode_free(QRCode* qrcode) {
228+
furi_assert(qrcode);
172229
free(qrcode->modules);
173230
free(qrcode);
174231
}
@@ -180,12 +237,16 @@ static void qrcode_free(QRCode* qrcode) {
180237
* @returns true if the string was successfully loaded
181238
*/
182239
static bool qrcode_load_string(QRCodeApp* instance, FuriString* str) {
240+
furi_assert(instance);
241+
furi_assert(str);
242+
183243
furi_check(furi_mutex_acquire(instance->mutex, FuriWaitForever) == FuriStatusOk);
184244
if (instance->qrcode) {
185245
qrcode_free(instance->qrcode);
186246
instance->qrcode = NULL;
187247
}
188248
instance->too_long = false;
249+
instance->show_stats = false;
189250

190251
bool result = false;
191252
do {
@@ -238,6 +299,8 @@ static bool qrcode_load_string(QRCodeApp* instance, FuriString* str) {
238299
result = true;
239300
} while (false);
240301

302+
instance->loading = false;
303+
241304
furi_mutex_release(instance->mutex);
242305

243306
return result;
@@ -307,6 +370,11 @@ static QRCodeApp* qrcode_app_alloc() {
307370

308371
instance->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
309372

373+
instance->qrcode = NULL;
374+
instance->loading = true;
375+
instance->too_long = false;
376+
instance->show_stats = false;
377+
310378
return instance;
311379
}
312380

@@ -332,9 +400,7 @@ static void qrcode_app_free(QRCodeApp* instance) {
332400
/** App entrypoint */
333401
int32_t qrcode_app(void* p) {
334402
QRCodeApp* instance = qrcode_app_alloc();
335-
336-
FuriString* file_path;
337-
file_path = furi_string_alloc();
403+
FuriString* file_path = furi_string_alloc();
338404

339405
do {
340406
if (p && strlen(p)) {
@@ -371,19 +437,25 @@ int32_t qrcode_app(void* p) {
371437
qrcode_free(instance->qrcode);
372438
instance->qrcode = NULL;
373439
}
440+
instance->loading = true;
374441
furi_mutex_release(instance->mutex);
375442
break;
443+
} else if (input.key == InputKeyRight) {
444+
instance->show_stats = true;
445+
} else if (input.key == InputKeyLeft) {
446+
instance->show_stats = false;
376447
}
377448

378449
furi_mutex_release(instance->mutex);
379450
view_port_update(instance->view_port);
380451
}
381452

382453
if (p && strlen(p)) {
383-
// if started with an arg, exit instead of going to the browser
454+
// if started with an arg, exit instead
455+
// of looping back to the browser
384456
break;
385457
}
386-
} while (1);
458+
} while (true);
387459

388460
furi_string_free(file_path);
389461
qrcode_app_free(instance);

0 commit comments

Comments
 (0)