12 more apps gone, 44 to go

This commit is contained in:
Willy-JL
2023-10-31 03:51:54 +00:00
parent d888fcbc02
commit 9056ab4fe8
162 changed files with 0 additions and 27310 deletions

View File

@@ -1,152 +0,0 @@
#include <furi.h>
#include <gui/gui.h>
#include <input/input.h>
#include <stdlib.h>
#include "GPIO_reader_item.h"
typedef enum {
EventTypeTick,
EventTypeKey,
} EventType;
typedef struct {
EventType type;
InputEvent input;
} PluginEvent;
typedef struct {
int pin;
int pullMode;
FuriMutex* mutex;
} PluginState;
static void render_callback(Canvas* const canvas, void* ctx) {
furi_assert(ctx);
const PluginState* plugin_state = ctx;
furi_mutex_acquire(plugin_state->mutex, FuriWaitForever);
canvas_set_font(canvas, FontPrimary);
canvas_draw_str_aligned(
canvas,
canvas_width(canvas) / 2,
canvas_height(canvas) / 10,
AlignCenter,
AlignCenter,
"GPIO reader");
canvas_set_font(canvas, FontSecondary);
canvas_draw_str_aligned(
canvas,
canvas_width(canvas) / 2,
canvas_height(canvas) / 10 * 3,
AlignCenter,
AlignCenter,
gpio_item_get_pin_name(plugin_state->pin));
canvas_draw_str_aligned(
canvas,
canvas_width(canvas) / 2,
canvas_height(canvas) / 10 * 5,
AlignCenter,
AlignCenter,
gpio_item_get_pull_mode(plugin_state->pullMode));
canvas_set_font(canvas, FontPrimary);
canvas_draw_str_aligned(
canvas,
canvas_width(canvas) / 2,
canvas_height(canvas) / 10 * 8,
AlignCenter,
AlignCenter,
gpio_item_get_pin_level(plugin_state->pin));
furi_mutex_release(plugin_state->mutex);
}
static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
furi_assert(event_queue);
PluginEvent event = {.type = EventTypeKey, .input = *input_event};
furi_message_queue_put(event_queue, &event, FuriWaitForever);
}
static void GPIO_reader_state_init(PluginState* const plugin_state) {
plugin_state->pin = 0;
plugin_state->pullMode = 0;
gpio_item_configure_pin(plugin_state->pin, plugin_state->pullMode);
}
int32_t GPIO_reader_app(void* p) {
UNUSED(p);
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent));
PluginState* plugin_state = malloc(sizeof(PluginState));
GPIO_reader_state_init(plugin_state);
plugin_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
if(!plugin_state->mutex) {
FURI_LOG_E("GPIO_reader", "cannot create mutex\r\n");
free(plugin_state);
return 255;
}
// Set system callbacks
ViewPort* view_port = view_port_alloc();
view_port_draw_callback_set(view_port, render_callback, plugin_state);
view_port_input_callback_set(view_port, input_callback, event_queue);
// Open GUI and register view_port
Gui* gui = furi_record_open("gui");
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
PluginEvent event;
for(bool processing = true; processing;) {
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
furi_mutex_acquire(plugin_state->mutex, FuriWaitForever);
if(event_status == FuriStatusOk) {
// press events
if(event.type == EventTypeKey) {
if(event.input.type == InputTypePress || event.input.type == InputTypeRepeat) {
switch(event.input.key) {
case InputKeyRight:
plugin_state->pin = (plugin_state->pin + 1) % GPIO_ITEM_COUNT;
gpio_item_configure_pin(plugin_state->pin, plugin_state->pullMode);
break;
case InputKeyLeft:
plugin_state->pin =
(plugin_state->pin - 1 + GPIO_ITEM_COUNT) % GPIO_ITEM_COUNT;
gpio_item_configure_pin(plugin_state->pin, plugin_state->pullMode);
break;
case InputKeyUp:
plugin_state->pullMode = (plugin_state->pullMode + 1) % GPIO_PULL_COUNT;
gpio_item_configure_pin(plugin_state->pin, plugin_state->pullMode);
break;
case InputKeyDown:
plugin_state->pullMode =
(plugin_state->pullMode - 1 + GPIO_PULL_COUNT) % GPIO_PULL_COUNT;
gpio_item_configure_pin(plugin_state->pin, plugin_state->pullMode);
break;
case InputKeyBack:
processing = false;
break;
default:
break;
}
}
}
}
view_port_update(view_port);
furi_mutex_release(plugin_state->mutex);
}
view_port_enabled_set(view_port, false);
gui_remove_view_port(gui, view_port);
furi_record_close("gui");
view_port_free(view_port);
furi_mutex_free(plugin_state->mutex);
furi_message_queue_free(event_queue);
free(plugin_state);
return 0;
}

View File

@@ -1,27 +0,0 @@
#include "GPIO_reader_item.h"
const char* gpio_item_get_pin_name(uint8_t index) {
furi_assert(index < GPIO_ITEM_COUNT);
return gpio_item[index].name;
}
const char* gpio_item_get_pull_mode(uint8_t pull_mode) {
furi_assert(pull_mode < GPIO_PULL_COUNT);
return gpio_pull_mode[pull_mode].name;
}
const char* gpio_item_get_pin_level(uint8_t index) {
furi_assert(index < GPIO_ITEM_COUNT);
//furi_hal_gpio_write(gpio_item[index].pin, level);
if(furi_hal_gpio_read(gpio_item[index].pin)) {
return "High";
} else {
return "Low";
}
}
void gpio_item_configure_pin(uint8_t index, uint8_t pull_mode) {
furi_assert(index < GPIO_ITEM_COUNT);
furi_hal_gpio_init(
gpio_item[index].pin, GpioModeInput, gpio_pull_mode[pull_mode].pull, GpioSpeedVeryHigh);
}

View File

@@ -1,42 +0,0 @@
#ifndef GPIO_READER_ITEM
#define GPIO_READER_ITEM
#include <furi.h>
#include <furi_hal_resources.h>
#define GPIO_ITEM_COUNT 8
#define GPIO_PULL_COUNT 3
typedef struct {
const char* name;
const GpioPin* pin;
} GpioItem;
static const GpioItem gpio_item[GPIO_ITEM_COUNT] = {
{"2: PA7", &gpio_ext_pa7},
{"3: PA6", &gpio_ext_pa6},
{"4: PA4", &gpio_ext_pa4},
{"5: PB3", &gpio_ext_pb3},
{"6: PB2", &gpio_ext_pb2},
{"7: PC3", &gpio_ext_pc3},
{"15: PC1", &gpio_ext_pc1},
{"16: PC0", &gpio_ext_pc0},
};
typedef struct {
const char* name;
const GpioPull pull;
} GpioPullMode;
static const GpioPullMode gpio_pull_mode[3] = {
{"high impedence", GpioPullNo},
{"pull up", GpioPullUp},
{"pull down", GpioPullDown},
};
const char* gpio_item_get_pin_name(uint8_t index);
const char* gpio_item_get_pin_level(uint8_t index);
void gpio_item_configure_pin(uint8_t index, uint8_t pullMode);
const char* gpio_item_get_pull_mode(uint8_t pull_mode);
#endif

View File

@@ -1,14 +0,0 @@
App(
appid="gpio_reader",
name="[GPIO] Reader (aureli1c)",
apptype=FlipperAppType.EXTERNAL,
entry_point="GPIO_reader_app",
requires=["gui"],
stack_size=1 * 1024,
fap_category="GPIO",
fap_icon="icon.png",
fap_author="@aureli1c",
fap_weburl="https://github.com/aureli1c/flipperzero_GPIO_read",
fap_version="1.0",
fap_description="Read GPIO pins states, and display them on the screen",
)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 135 B

View File

@@ -1,674 +0,0 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.

View File

@@ -1,11 +0,0 @@
App(
appid="gpioreader2",
name="[GPIO] Reader (biotinker)",
apptype=FlipperAppType.EXTERNAL,
entry_point="gpio_app",
requires=["gui"],
stack_size=1 * 1024,
fap_category="GPIO",
fap_icon="icon.png",
fap_icon_assets="icons",
)

View File

@@ -1,111 +0,0 @@
#include "gpio_app_i.h"
#include <furi.h>
#include <furi_hal.h>
static bool gpio_app_custom_event_callback(void* context, uint32_t event) {
furi_assert(context);
GpioApp* app = context;
return scene_manager_handle_custom_event(app->scene_manager, event);
}
static bool gpio_app_back_event_callback(void* context) {
furi_assert(context);
GpioApp* app = context;
return scene_manager_handle_back_event(app->scene_manager);
}
static void gpio_app_tick_event_callback(void* context) {
furi_assert(context);
GpioApp* app = context;
scene_manager_handle_tick_event(app->scene_manager);
}
GpioApp* gpio_app_alloc() {
GpioApp* app = malloc(sizeof(GpioApp));
app->gui = furi_record_open(RECORD_GUI);
app->view_dispatcher = view_dispatcher_alloc();
app->scene_manager = scene_manager_alloc(&gpio_scene_handlers, app);
view_dispatcher_enable_queue(app->view_dispatcher);
view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
view_dispatcher_set_custom_event_callback(
app->view_dispatcher, gpio_app_custom_event_callback);
view_dispatcher_set_navigation_event_callback(
app->view_dispatcher, gpio_app_back_event_callback);
view_dispatcher_set_tick_event_callback(
app->view_dispatcher, gpio_app_tick_event_callback, 100);
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
app->notifications = furi_record_open(RECORD_NOTIFICATION);
app->var_item_list = variable_item_list_alloc();
view_dispatcher_add_view(
app->view_dispatcher,
GpioAppViewVarItemList,
variable_item_list_get_view(app->var_item_list));
app->gpio_test = gpio_test_alloc();
view_dispatcher_add_view(
app->view_dispatcher, GpioAppViewGpioTest, gpio_test_get_view(app->gpio_test));
app->gpio_reader = gpio_reader_alloc();
view_dispatcher_add_view(
app->view_dispatcher, GpioAppViewGpioReader, gpio_reader_get_view(app->gpio_reader));
app->widget = widget_alloc();
view_dispatcher_add_view(
app->view_dispatcher, GpioAppViewUsbUartCloseRpc, widget_get_view(app->widget));
app->gpio_usb_uart = gpio_usb_uart_alloc();
view_dispatcher_add_view(
app->view_dispatcher, GpioAppViewUsbUart, gpio_usb_uart_get_view(app->gpio_usb_uart));
view_dispatcher_add_view(
app->view_dispatcher,
GpioAppViewUsbUartCfg,
variable_item_list_get_view(app->var_item_list));
scene_manager_next_scene(app->scene_manager, GpioSceneStart);
return app;
}
void gpio_app_free(GpioApp* app) {
furi_assert(app);
// Views
view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewVarItemList);
view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewGpioTest);
view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewGpioReader);
view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewUsbUart);
view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewUsbUartCfg);
view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewUsbUartCloseRpc);
variable_item_list_free(app->var_item_list);
widget_free(app->widget);
gpio_test_free(app->gpio_test);
gpio_reader_free(app->gpio_reader);
gpio_usb_uart_free(app->gpio_usb_uart);
// View dispatcher
view_dispatcher_free(app->view_dispatcher);
scene_manager_free(app->scene_manager);
// Close records
furi_record_close(RECORD_GUI);
furi_record_close(RECORD_NOTIFICATION);
free(app);
}
int32_t gpio_app(void* p) {
UNUSED(p);
GpioApp* gpio_app = gpio_app_alloc();
view_dispatcher_run(gpio_app->view_dispatcher);
gpio_app_free(gpio_app);
return 0;
}

View File

@@ -1,11 +0,0 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
typedef struct GpioApp GpioApp;
#ifdef __cplusplus
}
#endif

View File

@@ -1,45 +0,0 @@
#pragma once
#include "gpio_app.h"
#include "gpio_item.h"
#include "scenes/gpio_scene.h"
#include "gpio_custom_event.h"
#include "usb_uart_bridge.h"
#include <gui/gui.h>
#include <gui/view_dispatcher.h>
#include <gui/scene_manager.h>
#include <gui/modules/submenu.h>
#include <notification/notification_messages.h>
#include <gui/modules/variable_item_list.h>
#include <gui/modules/widget.h>
#include "views/gpio_test.h"
#include "views/gpio_reader.h"
#include "views/gpio_usb_uart.h"
#include "gpioreader2_icons.h"
#include <assets_icons.h>
struct GpioApp {
Gui* gui;
NotificationApp* notifications;
ViewDispatcher* view_dispatcher;
SceneManager* scene_manager;
Widget* widget;
VariableItemList* var_item_list;
VariableItem* var_item_flow;
GpioTest* gpio_test;
GpioReader* gpio_reader;
GpioUsbUart* gpio_usb_uart;
UsbUartBridge* usb_uart_bridge;
UsbUartConfig* usb_uart_cfg;
};
typedef enum {
GpioAppViewVarItemList,
GpioAppViewGpioTest,
GpioAppViewGpioReader,
GpioAppViewUsbUart,
GpioAppViewUsbUartCfg,
GpioAppViewUsbUartCloseRpc,
} GpioAppView;

View File

@@ -1,14 +0,0 @@
#pragma once
typedef enum {
GpioStartEventOtgOff = 0,
GpioStartEventOtgOn,
GpioStartEventManualControl,
GpioStartEventReader,
GpioStartEventUsbUart,
GpioCustomEventErrorBack,
GpioUsbUartEventConfig,
GpioUsbUartEventConfigSet,
} GpioCustomEvent;

View File

@@ -1,60 +0,0 @@
#include "gpio_item.h"
#include <furi_hal_resources.h>
typedef struct {
const char* name;
const GpioPin* pin;
} GpioItem;
static const GpioItem gpio_item[GPIO_ITEM_COUNT] = {
{"1.2: PA7", &gpio_ext_pa7},
{"1.3: PA6", &gpio_ext_pa6},
{"1.4: PA4", &gpio_ext_pa4},
{"1.5: PB3", &gpio_ext_pb3},
{"1.6: PB2", &gpio_ext_pb2},
{"1.7: PC3", &gpio_ext_pc3},
{"2.7: PC1", &gpio_ext_pc1},
{"2.8: PC0", &gpio_ext_pc0},
};
void gpio_item_configure_pin(uint8_t index, GpioMode mode, GpioPull pull) {
furi_assert(index < GPIO_ITEM_COUNT);
furi_hal_gpio_write(gpio_item[index].pin, false);
furi_hal_gpio_init(gpio_item[index].pin, mode, pull, GpioSpeedVeryHigh);
}
void gpio_item_configure_all_pins(GpioMode mode) {
GpioPull pull = GpioPullNo;
if(mode == GpioModeInput) {
pull = GpioPullDown;
}
for(uint8_t i = 0; i < GPIO_ITEM_COUNT; i++) {
gpio_item_configure_pin(i, mode, pull);
}
}
void gpio_item_set_pin(uint8_t index, bool level) {
furi_assert(index < GPIO_ITEM_COUNT);
furi_hal_gpio_write(gpio_item[index].pin, level);
}
bool gpio_item_get_pin(uint8_t index) {
furi_assert(index < GPIO_ITEM_COUNT);
return furi_hal_gpio_read(gpio_item[index].pin);
}
void gpio_item_set_all_pins(bool level) {
for(uint8_t i = 0; i < GPIO_ITEM_COUNT; i++) {
gpio_item_set_pin(i, level);
}
}
const char* gpio_item_get_pin_name(uint8_t index) {
furi_assert(index < GPIO_ITEM_COUNT + 1);
if(index == GPIO_ITEM_COUNT) {
return "ALL";
} else {
return gpio_item[index].name;
}
}

View File

@@ -1,17 +0,0 @@
#pragma once
#include <furi_hal_gpio.h>
#define GPIO_ITEM_COUNT 8
void gpio_item_configure_pin(uint8_t index, GpioMode mode, GpioPull pull);
void gpio_item_configure_all_pins(GpioMode mode);
void gpio_item_set_pin(uint8_t index, bool level);
void gpio_item_set_all_pins(bool level);
const char* gpio_item_get_pin_name(uint8_t index);
bool gpio_item_get_pin(uint8_t index);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 135 B

View File

@@ -1,30 +0,0 @@
#include "gpio_scene.h"
// Generate scene on_enter handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
void (*const gpio_scene_on_enter_handlers[])(void*) = {
#include "gpio_scene_config.h"
};
#undef ADD_SCENE
// Generate scene on_event handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,
bool (*const gpio_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = {
#include "gpio_scene_config.h"
};
#undef ADD_SCENE
// Generate scene on_exit handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,
void (*const gpio_scene_on_exit_handlers[])(void* context) = {
#include "gpio_scene_config.h"
};
#undef ADD_SCENE
// Initialize scene handlers configuration structure
const SceneManagerHandlers gpio_scene_handlers = {
.on_enter_handlers = gpio_scene_on_enter_handlers,
.on_event_handlers = gpio_scene_on_event_handlers,
.on_exit_handlers = gpio_scene_on_exit_handlers,
.scene_num = GpioSceneNum,
};

View File

@@ -1,29 +0,0 @@
#pragma once
#include <gui/scene_manager.h>
// Generate scene id and total number
#define ADD_SCENE(prefix, name, id) GpioScene##id,
typedef enum {
#include "gpio_scene_config.h"
GpioSceneNum,
} GpioScene;
#undef ADD_SCENE
extern const SceneManagerHandlers gpio_scene_handlers;
// Generate scene on_enter handlers declaration
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);
#include "gpio_scene_config.h"
#undef ADD_SCENE
// Generate scene on_event handlers declaration
#define ADD_SCENE(prefix, name, id) \
bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);
#include "gpio_scene_config.h"
#undef ADD_SCENE
// Generate scene on_exit handlers declaration
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);
#include "gpio_scene_config.h"
#undef ADD_SCENE

View File

@@ -1,6 +0,0 @@
ADD_SCENE(gpio, start, Start)
ADD_SCENE(gpio, test, Test)
ADD_SCENE(gpio, reader, Reader)
ADD_SCENE(gpio, usb_uart, UsbUart)
ADD_SCENE(gpio, usb_uart_cfg, UsbUartCfg)
ADD_SCENE(gpio, usb_uart_close_rpc, UsbUartCloseRpc)

View File

@@ -1,30 +0,0 @@
#include "../gpio_app_i.h"
void gpio_scene_reader_ok_callback(InputType type, void* context) {
furi_assert(context);
GpioApp* app = context;
if(type == InputTypePress) {
notification_message(app->notifications, &sequence_set_green_255);
} else if(type == InputTypeRelease) {
notification_message(app->notifications, &sequence_reset_green);
}
}
void gpio_scene_reader_on_enter(void* context) {
GpioApp* app = context;
gpio_item_configure_all_pins(GpioModeInput);
gpio_reader_set_ok_callback(app->gpio_reader, gpio_scene_reader_ok_callback, app);
view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewGpioReader);
}
bool gpio_scene_reader_on_event(void* context, SceneManagerEvent event) {
UNUSED(context);
UNUSED(event);
return false;
}
void gpio_scene_reader_on_exit(void* context) {
UNUSED(context);
gpio_item_configure_all_pins(GpioModeAnalog);
}

View File

@@ -1,114 +0,0 @@
#include "../gpio_app_i.h"
#include "furi_hal_power.h"
#include "furi_hal_usb.h"
#include <dolphin/dolphin.h>
enum GpioItem {
GpioItemUsbUart,
GpioItemTest,
GpioItemReader,
GpioItemOtg,
};
enum GpioOtg {
GpioOtgOff,
GpioOtgOn,
GpioOtgSettingsNum,
};
const char* const gpio_otg_text[GpioOtgSettingsNum] = {
"OFF",
"ON",
};
static void gpio_scene_start_var_list_enter_callback(void* context, uint32_t index) {
furi_assert(context);
GpioApp* app = context;
if(index == GpioItemTest) {
view_dispatcher_send_custom_event(app->view_dispatcher, GpioStartEventManualControl);
} else if(index == GpioItemUsbUart) {
view_dispatcher_send_custom_event(app->view_dispatcher, GpioStartEventUsbUart);
} else if(index == GpioItemReader) {
view_dispatcher_send_custom_event(app->view_dispatcher, GpioStartEventReader);
}
}
static void gpio_scene_start_var_list_change_callback(VariableItem* item) {
GpioApp* app = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, gpio_otg_text[index]);
if(index == GpioOtgOff) {
view_dispatcher_send_custom_event(app->view_dispatcher, GpioStartEventOtgOff);
} else if(index == GpioOtgOn) {
view_dispatcher_send_custom_event(app->view_dispatcher, GpioStartEventOtgOn);
}
}
void gpio_scene_start_on_enter(void* context) {
GpioApp* app = context;
VariableItemList* var_item_list = app->var_item_list;
VariableItem* item;
variable_item_list_set_enter_callback(
var_item_list, gpio_scene_start_var_list_enter_callback, app);
variable_item_list_add(var_item_list, "USB-UART Bridge", 0, NULL, NULL);
variable_item_list_add(var_item_list, "GPIO Manual Control", 0, NULL, NULL);
variable_item_list_add(var_item_list, "GPIO Manual Read", 0, NULL, NULL);
item = variable_item_list_add(
var_item_list,
"5V on GPIO",
GpioOtgSettingsNum,
gpio_scene_start_var_list_change_callback,
app);
if(furi_hal_power_is_otg_enabled()) {
variable_item_set_current_value_index(item, GpioOtgOn);
variable_item_set_current_value_text(item, gpio_otg_text[GpioOtgOn]);
} else {
variable_item_set_current_value_index(item, GpioOtgOff);
variable_item_set_current_value_text(item, gpio_otg_text[GpioOtgOff]);
}
variable_item_list_set_selected_item(
var_item_list, scene_manager_get_scene_state(app->scene_manager, GpioSceneStart));
view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewVarItemList);
}
bool gpio_scene_start_on_event(void* context, SceneManagerEvent event) {
GpioApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == GpioStartEventOtgOn) {
furi_hal_power_enable_otg();
} else if(event.event == GpioStartEventOtgOff) {
furi_hal_power_disable_otg();
} else if(event.event == GpioStartEventManualControl) {
scene_manager_set_scene_state(app->scene_manager, GpioSceneStart, GpioItemTest);
scene_manager_next_scene(app->scene_manager, GpioSceneTest);
} else if(event.event == GpioStartEventReader) {
scene_manager_set_scene_state(app->scene_manager, GpioSceneStart, GpioItemReader);
scene_manager_next_scene(app->scene_manager, GpioSceneReader);
} else if(event.event == GpioStartEventUsbUart) {
scene_manager_set_scene_state(app->scene_manager, GpioSceneStart, GpioItemUsbUart);
if(!furi_hal_usb_is_locked()) {
dolphin_deed(DolphinDeedGpioUartBridge);
scene_manager_next_scene(app->scene_manager, GpioSceneUsbUart);
} else {
scene_manager_next_scene(app->scene_manager, GpioSceneUsbUartCloseRpc);
}
}
consumed = true;
}
return consumed;
}
void gpio_scene_start_on_exit(void* context) {
GpioApp* app = context;
variable_item_list_reset(app->var_item_list);
}

View File

@@ -1,30 +0,0 @@
#include "../gpio_app_i.h"
void gpio_scene_test_ok_callback(InputType type, void* context) {
furi_assert(context);
GpioApp* app = context;
if(type == InputTypePress) {
notification_message(app->notifications, &sequence_set_green_255);
} else if(type == InputTypeRelease) {
notification_message(app->notifications, &sequence_reset_green);
}
}
void gpio_scene_test_on_enter(void* context) {
GpioApp* app = context;
gpio_item_configure_all_pins(GpioModeOutputPushPull);
gpio_test_set_ok_callback(app->gpio_test, gpio_scene_test_ok_callback, app);
view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewGpioTest);
}
bool gpio_scene_test_on_event(void* context, SceneManagerEvent event) {
UNUSED(context);
UNUSED(event);
return false;
}
void gpio_scene_test_on_exit(void* context) {
UNUSED(context);
gpio_item_configure_all_pins(GpioModeAnalog);
}

View File

@@ -1,67 +0,0 @@
#include "../gpio_app_i.h"
#include "../usb_uart_bridge.h"
typedef struct {
UsbUartConfig cfg;
UsbUartState state;
} SceneUsbUartBridge;
static SceneUsbUartBridge* scene_usb_uart;
void gpio_scene_usb_uart_callback(GpioCustomEvent event, void* context) {
furi_assert(context);
GpioApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, event);
}
void gpio_scene_usb_uart_on_enter(void* context) {
GpioApp* app = context;
uint32_t prev_state = scene_manager_get_scene_state(app->scene_manager, GpioAppViewUsbUart);
if(prev_state == 0) {
scene_usb_uart = malloc(sizeof(SceneUsbUartBridge));
scene_usb_uart->cfg.vcp_ch = 0; // TODO: settings load
scene_usb_uart->cfg.uart_ch = 0;
scene_usb_uart->cfg.flow_pins = 0;
scene_usb_uart->cfg.baudrate_mode = 0;
scene_usb_uart->cfg.baudrate = 0;
app->usb_uart_bridge = usb_uart_enable(&scene_usb_uart->cfg);
}
usb_uart_get_config(app->usb_uart_bridge, &scene_usb_uart->cfg);
usb_uart_get_state(app->usb_uart_bridge, &scene_usb_uart->state);
gpio_usb_uart_set_callback(app->gpio_usb_uart, gpio_scene_usb_uart_callback, app);
scene_manager_set_scene_state(app->scene_manager, GpioSceneUsbUart, 0);
view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewUsbUart);
notification_message(app->notifications, &sequence_display_backlight_enforce_on);
}
bool gpio_scene_usb_uart_on_event(void* context, SceneManagerEvent event) {
GpioApp* app = context;
if(event.type == SceneManagerEventTypeCustom) {
scene_manager_set_scene_state(app->scene_manager, GpioSceneUsbUart, 1);
scene_manager_next_scene(app->scene_manager, GpioSceneUsbUartCfg);
return true;
} else if(event.type == SceneManagerEventTypeTick) {
uint32_t tx_cnt_last = scene_usb_uart->state.tx_cnt;
uint32_t rx_cnt_last = scene_usb_uart->state.rx_cnt;
usb_uart_get_state(app->usb_uart_bridge, &scene_usb_uart->state);
gpio_usb_uart_update_state(
app->gpio_usb_uart, &scene_usb_uart->cfg, &scene_usb_uart->state);
if(tx_cnt_last != scene_usb_uart->state.tx_cnt)
notification_message(app->notifications, &sequence_blink_blue_10);
if(rx_cnt_last != scene_usb_uart->state.rx_cnt)
notification_message(app->notifications, &sequence_blink_green_10);
}
return false;
}
void gpio_scene_usb_uart_on_exit(void* context) {
GpioApp* app = context;
uint32_t prev_state = scene_manager_get_scene_state(app->scene_manager, GpioSceneUsbUart);
if(prev_state == 0) {
usb_uart_disable(app->usb_uart_bridge);
free(scene_usb_uart);
}
notification_message(app->notifications, &sequence_display_backlight_enforce_auto);
}

View File

@@ -1,41 +0,0 @@
#include "../gpio_app_i.h"
#include "../gpio_custom_event.h"
void gpio_scene_usb_uart_close_rpc_on_enter(void* context) {
GpioApp* app = context;
widget_add_icon_element(app->widget, 78, 0, &I_ActiveConnection_50x64);
widget_add_string_multiline_element(
app->widget, 3, 2, AlignLeft, AlignTop, FontPrimary, "Connection\nis active!");
widget_add_string_multiline_element(
app->widget,
3,
30,
AlignLeft,
AlignTop,
FontSecondary,
"Disconnect from\nPC or phone to\nuse this function.");
view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewUsbUartCloseRpc);
}
bool gpio_scene_usb_uart_close_rpc_on_event(void* context, SceneManagerEvent event) {
GpioApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == GpioCustomEventErrorBack) {
if(!scene_manager_previous_scene(app->scene_manager)) {
scene_manager_stop(app->scene_manager);
view_dispatcher_stop(app->view_dispatcher);
}
consumed = true;
}
}
return consumed;
}
void gpio_scene_usb_uart_close_rpc_on_exit(void* context) {
GpioApp* app = context;
widget_reset(app->widget);
}

View File

@@ -1,169 +0,0 @@
#include "../usb_uart_bridge.h"
#include "../gpio_app_i.h"
#include "furi_hal.h"
typedef enum {
UsbUartLineIndexVcp,
UsbUartLineIndexBaudrate,
UsbUartLineIndexUart,
UsbUartLineIndexFlow,
} LineIndex;
static const char* vcp_ch[] = {"0 (CLI)", "1"};
static const char* uart_ch[] = {"13,14", "15,16"};
static const char* flow_pins[] = {"None", "2,3", "6,7", "16,15"};
static const char* baudrate_mode[] = {"Host"};
static const uint32_t baudrate_list[] = {
2400,
9600,
19200,
38400,
57600,
115200,
230400,
460800,
921600,
};
bool gpio_scene_usb_uart_cfg_on_event(void* context, SceneManagerEvent event) {
GpioApp* app = context;
furi_assert(app);
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == GpioUsbUartEventConfigSet) {
usb_uart_set_config(app->usb_uart_bridge, app->usb_uart_cfg);
return true;
}
}
return false;
}
void line_ensure_flow_invariant(GpioApp* app) {
// GPIO pins PC0, PC1 (16,15) are unavailable for RTS/DTR when LPUART is
// selected. This function enforces that invariant by resetting flow_pins
// to None if it is configured to 16,15 when LPUART is selected.
uint8_t available_flow_pins = app->usb_uart_cfg->uart_ch == FuriHalUartIdLPUART1 ? 3 : 4;
VariableItem* item = app->var_item_flow;
variable_item_set_values_count(item, available_flow_pins);
if(app->usb_uart_cfg->flow_pins >= available_flow_pins) {
app->usb_uart_cfg->flow_pins = 0;
variable_item_set_current_value_index(item, app->usb_uart_cfg->flow_pins);
variable_item_set_current_value_text(item, flow_pins[app->usb_uart_cfg->flow_pins]);
}
}
static void line_vcp_cb(VariableItem* item) {
GpioApp* app = variable_item_get_context(item);
furi_assert(app);
uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, vcp_ch[index]);
app->usb_uart_cfg->vcp_ch = index;
view_dispatcher_send_custom_event(app->view_dispatcher, GpioUsbUartEventConfigSet);
}
static void line_port_cb(VariableItem* item) {
GpioApp* app = variable_item_get_context(item);
furi_assert(app);
uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, uart_ch[index]);
if(index == 0)
app->usb_uart_cfg->uart_ch = FuriHalUartIdUSART1;
else if(index == 1)
app->usb_uart_cfg->uart_ch = FuriHalUartIdLPUART1;
line_ensure_flow_invariant(app);
view_dispatcher_send_custom_event(app->view_dispatcher, GpioUsbUartEventConfigSet);
}
static void line_flow_cb(VariableItem* item) {
GpioApp* app = variable_item_get_context(item);
furi_assert(app);
uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, flow_pins[index]);
app->usb_uart_cfg->flow_pins = index;
view_dispatcher_send_custom_event(app->view_dispatcher, GpioUsbUartEventConfigSet);
}
static void line_baudrate_cb(VariableItem* item) {
GpioApp* app = variable_item_get_context(item);
furi_assert(app);
uint8_t index = variable_item_get_current_value_index(item);
char br_text[8];
if(index > 0) {
snprintf(br_text, 7, "%lu", baudrate_list[index - 1]);
variable_item_set_current_value_text(item, br_text);
app->usb_uart_cfg->baudrate = baudrate_list[index - 1];
} else {
variable_item_set_current_value_text(item, baudrate_mode[index]);
app->usb_uart_cfg->baudrate = 0;
}
app->usb_uart_cfg->baudrate_mode = index;
view_dispatcher_send_custom_event(app->view_dispatcher, GpioUsbUartEventConfigSet);
}
void gpio_scene_usb_uart_cfg_on_enter(void* context) {
GpioApp* app = context;
furi_assert(app);
VariableItemList* var_item_list = app->var_item_list;
app->usb_uart_cfg = malloc(sizeof(UsbUartConfig));
usb_uart_get_config(app->usb_uart_bridge, app->usb_uart_cfg);
VariableItem* item;
char br_text[8];
item = variable_item_list_add(var_item_list, "USB Channel", 2, line_vcp_cb, app);
variable_item_set_current_value_index(item, app->usb_uart_cfg->vcp_ch);
variable_item_set_current_value_text(item, vcp_ch[app->usb_uart_cfg->vcp_ch]);
item = variable_item_list_add(
var_item_list,
"Baudrate",
sizeof(baudrate_list) / sizeof(baudrate_list[0]) + 1,
line_baudrate_cb,
app);
variable_item_set_current_value_index(item, app->usb_uart_cfg->baudrate_mode);
if(app->usb_uart_cfg->baudrate_mode > 0) {
snprintf(br_text, 7, "%lu", baudrate_list[app->usb_uart_cfg->baudrate_mode - 1]);
variable_item_set_current_value_text(item, br_text);
} else {
variable_item_set_current_value_text(
item, baudrate_mode[app->usb_uart_cfg->baudrate_mode]);
}
item = variable_item_list_add(var_item_list, "UART Pins", 2, line_port_cb, app);
variable_item_set_current_value_index(item, app->usb_uart_cfg->uart_ch);
variable_item_set_current_value_text(item, uart_ch[app->usb_uart_cfg->uart_ch]);
item = variable_item_list_add(
var_item_list, "RTS/DTR Pins", COUNT_OF(flow_pins), line_flow_cb, app);
variable_item_set_current_value_index(item, app->usb_uart_cfg->flow_pins);
variable_item_set_current_value_text(item, flow_pins[app->usb_uart_cfg->flow_pins]);
app->var_item_flow = item;
line_ensure_flow_invariant(app);
variable_item_list_set_selected_item(
var_item_list, scene_manager_get_scene_state(app->scene_manager, GpioAppViewUsbUartCfg));
view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewUsbUartCfg);
}
void gpio_scene_usb_uart_cfg_on_exit(void* context) {
GpioApp* app = context;
scene_manager_set_scene_state(
app->scene_manager,
GpioAppViewUsbUartCfg,
variable_item_list_get_selected_item_index(app->var_item_list));
variable_item_list_reset(app->var_item_list);
free(app->usb_uart_cfg);
}

View File

@@ -1,375 +0,0 @@
#include "usb_uart_bridge.h"
#include "furi_hal.h"
#include <furi_hal_usb_cdc.h>
#include "usb_cdc.h"
#include "cli/cli_vcp.h"
#include <toolbox/api_lock.h>
#include "cli/cli.h"
#define USB_CDC_PKT_LEN CDC_DATA_SZ
#define USB_UART_RX_BUF_SIZE (USB_CDC_PKT_LEN * 5)
#define USB_CDC_BIT_DTR (1 << 0)
#define USB_CDC_BIT_RTS (1 << 1)
static const GpioPin* flow_pins[][2] = {
{&gpio_ext_pa7, &gpio_ext_pa6}, // 2, 3
{&gpio_ext_pb2, &gpio_ext_pc3}, // 6, 7
{&gpio_ext_pc0, &gpio_ext_pc1}, // 16, 15
};
typedef enum {
WorkerEvtStop = (1 << 0),
WorkerEvtRxDone = (1 << 1),
WorkerEvtTxStop = (1 << 2),
WorkerEvtCdcRx = (1 << 3),
WorkerEvtCfgChange = (1 << 4),
WorkerEvtLineCfgSet = (1 << 5),
WorkerEvtCtrlLineSet = (1 << 6),
} WorkerEvtFlags;
#define WORKER_ALL_RX_EVENTS \
(WorkerEvtStop | WorkerEvtRxDone | WorkerEvtCfgChange | WorkerEvtLineCfgSet | \
WorkerEvtCtrlLineSet)
#define WORKER_ALL_TX_EVENTS (WorkerEvtTxStop | WorkerEvtCdcRx)
struct UsbUartBridge {
UsbUartConfig cfg;
UsbUartConfig cfg_new;
FuriThread* thread;
FuriThread* tx_thread;
FuriStreamBuffer* rx_stream;
FuriMutex* usb_mutex;
FuriSemaphore* tx_sem;
UsbUartState st;
FuriApiLock cfg_lock;
uint8_t rx_buf[USB_CDC_PKT_LEN];
};
static void vcp_on_cdc_tx_complete(void* context);
static void vcp_on_cdc_rx(void* context);
static void vcp_state_callback(void* context, uint8_t state);
static void vcp_on_cdc_control_line(void* context, uint8_t state);
static void vcp_on_line_config(void* context, struct usb_cdc_line_coding* config);
static const CdcCallbacks cdc_cb = {
vcp_on_cdc_tx_complete,
vcp_on_cdc_rx,
vcp_state_callback,
vcp_on_cdc_control_line,
vcp_on_line_config,
};
/* USB UART worker */
static int32_t usb_uart_tx_thread(void* context);
static void usb_uart_on_irq_cb(UartIrqEvent ev, uint8_t data, void* context) {
UsbUartBridge* usb_uart = (UsbUartBridge*)context;
if(ev == UartIrqEventRXNE) {
furi_stream_buffer_send(usb_uart->rx_stream, &data, 1, 0);
furi_thread_flags_set(furi_thread_get_id(usb_uart->thread), WorkerEvtRxDone);
}
}
static void usb_uart_vcp_init(UsbUartBridge* usb_uart, uint8_t vcp_ch) {
furi_hal_usb_unlock();
if(vcp_ch == 0) {
Cli* cli = furi_record_open(RECORD_CLI);
cli_session_close(cli);
furi_record_close(RECORD_CLI);
furi_check(furi_hal_usb_set_config(&usb_cdc_single, NULL) == true);
} else {
furi_check(furi_hal_usb_set_config(&usb_cdc_dual, NULL) == true);
Cli* cli = furi_record_open(RECORD_CLI);
cli_session_open(cli, &cli_vcp);
furi_record_close(RECORD_CLI);
}
furi_hal_cdc_set_callbacks(vcp_ch, (CdcCallbacks*)&cdc_cb, usb_uart);
}
static void usb_uart_vcp_deinit(UsbUartBridge* usb_uart, uint8_t vcp_ch) {
UNUSED(usb_uart);
furi_hal_cdc_set_callbacks(vcp_ch, NULL, NULL);
if(vcp_ch != 0) {
Cli* cli = furi_record_open(RECORD_CLI);
cli_session_close(cli);
furi_record_close(RECORD_CLI);
}
}
static void usb_uart_serial_init(UsbUartBridge* usb_uart, uint8_t uart_ch) {
if(uart_ch == FuriHalUartIdUSART1) {
furi_hal_console_disable();
} else if(uart_ch == FuriHalUartIdLPUART1) {
furi_hal_uart_init(uart_ch, 115200);
}
furi_hal_uart_set_irq_cb(uart_ch, usb_uart_on_irq_cb, usb_uart);
}
static void usb_uart_serial_deinit(UsbUartBridge* usb_uart, uint8_t uart_ch) {
UNUSED(usb_uart);
furi_hal_uart_set_irq_cb(uart_ch, NULL, NULL);
if(uart_ch == FuriHalUartIdUSART1)
furi_hal_console_enable();
else if(uart_ch == FuriHalUartIdLPUART1)
furi_hal_uart_deinit(uart_ch);
}
static void usb_uart_set_baudrate(UsbUartBridge* usb_uart, uint32_t baudrate) {
if(baudrate != 0) {
furi_hal_uart_set_br(usb_uart->cfg.uart_ch, baudrate);
usb_uart->st.baudrate_cur = baudrate;
} else {
struct usb_cdc_line_coding* line_cfg =
furi_hal_cdc_get_port_settings(usb_uart->cfg.vcp_ch);
if(line_cfg->dwDTERate > 0) {
furi_hal_uart_set_br(usb_uart->cfg.uart_ch, line_cfg->dwDTERate);
usb_uart->st.baudrate_cur = line_cfg->dwDTERate;
}
}
}
static void usb_uart_update_ctrl_lines(UsbUartBridge* usb_uart) {
if(usb_uart->cfg.flow_pins != 0) {
furi_assert((size_t)(usb_uart->cfg.flow_pins - 1) < COUNT_OF(flow_pins));
uint8_t state = furi_hal_cdc_get_ctrl_line_state(usb_uart->cfg.vcp_ch);
furi_hal_gpio_write(flow_pins[usb_uart->cfg.flow_pins - 1][0], !(state & USB_CDC_BIT_RTS));
furi_hal_gpio_write(flow_pins[usb_uart->cfg.flow_pins - 1][1], !(state & USB_CDC_BIT_DTR));
}
}
static int32_t usb_uart_worker(void* context) {
UsbUartBridge* usb_uart = (UsbUartBridge*)context;
memcpy(&usb_uart->cfg, &usb_uart->cfg_new, sizeof(UsbUartConfig));
usb_uart->rx_stream = furi_stream_buffer_alloc(USB_UART_RX_BUF_SIZE, 1);
usb_uart->tx_sem = furi_semaphore_alloc(1, 1);
usb_uart->usb_mutex = furi_mutex_alloc(FuriMutexTypeNormal);
usb_uart->tx_thread =
furi_thread_alloc_ex("UsbUartTxWorker", 512, usb_uart_tx_thread, usb_uart);
usb_uart_vcp_init(usb_uart, usb_uart->cfg.vcp_ch);
usb_uart_serial_init(usb_uart, usb_uart->cfg.uart_ch);
usb_uart_set_baudrate(usb_uart, usb_uart->cfg.baudrate);
if(usb_uart->cfg.flow_pins != 0) {
furi_assert((size_t)(usb_uart->cfg.flow_pins - 1) < COUNT_OF(flow_pins));
furi_hal_gpio_init_simple(
flow_pins[usb_uart->cfg.flow_pins - 1][0], GpioModeOutputPushPull);
furi_hal_gpio_init_simple(
flow_pins[usb_uart->cfg.flow_pins - 1][1], GpioModeOutputPushPull);
usb_uart_update_ctrl_lines(usb_uart);
}
furi_thread_flags_set(furi_thread_get_id(usb_uart->tx_thread), WorkerEvtCdcRx);
furi_thread_start(usb_uart->tx_thread);
while(1) {
uint32_t events =
furi_thread_flags_wait(WORKER_ALL_RX_EVENTS, FuriFlagWaitAny, FuriWaitForever);
furi_check(!(events & FuriFlagError));
if(events & WorkerEvtStop) break;
if(events & WorkerEvtRxDone) {
size_t len = furi_stream_buffer_receive(
usb_uart->rx_stream, usb_uart->rx_buf, USB_CDC_PKT_LEN, 0);
if(len > 0) {
if(furi_semaphore_acquire(usb_uart->tx_sem, 100) == FuriStatusOk) {
usb_uart->st.rx_cnt += len;
furi_check(
furi_mutex_acquire(usb_uart->usb_mutex, FuriWaitForever) == FuriStatusOk);
furi_hal_cdc_send(usb_uart->cfg.vcp_ch, usb_uart->rx_buf, len);
furi_check(furi_mutex_release(usb_uart->usb_mutex) == FuriStatusOk);
} else {
furi_stream_buffer_reset(usb_uart->rx_stream);
}
}
}
if(events & WorkerEvtCfgChange) {
if(usb_uart->cfg.vcp_ch != usb_uart->cfg_new.vcp_ch) {
furi_thread_flags_set(furi_thread_get_id(usb_uart->tx_thread), WorkerEvtTxStop);
furi_thread_join(usb_uart->tx_thread);
usb_uart_vcp_deinit(usb_uart, usb_uart->cfg.vcp_ch);
usb_uart_vcp_init(usb_uart, usb_uart->cfg_new.vcp_ch);
usb_uart->cfg.vcp_ch = usb_uart->cfg_new.vcp_ch;
furi_thread_start(usb_uart->tx_thread);
events |= WorkerEvtCtrlLineSet;
events |= WorkerEvtLineCfgSet;
}
if(usb_uart->cfg.uart_ch != usb_uart->cfg_new.uart_ch) {
furi_thread_flags_set(furi_thread_get_id(usb_uart->tx_thread), WorkerEvtTxStop);
furi_thread_join(usb_uart->tx_thread);
usb_uart_serial_deinit(usb_uart, usb_uart->cfg.uart_ch);
usb_uart_serial_init(usb_uart, usb_uart->cfg_new.uart_ch);
usb_uart->cfg.uart_ch = usb_uart->cfg_new.uart_ch;
usb_uart_set_baudrate(usb_uart, usb_uart->cfg.baudrate);
furi_thread_start(usb_uart->tx_thread);
}
if(usb_uart->cfg.baudrate != usb_uart->cfg_new.baudrate) {
usb_uart_set_baudrate(usb_uart, usb_uart->cfg_new.baudrate);
usb_uart->cfg.baudrate = usb_uart->cfg_new.baudrate;
}
if(usb_uart->cfg.flow_pins != usb_uart->cfg_new.flow_pins) {
if(usb_uart->cfg.flow_pins != 0) {
furi_hal_gpio_init_simple(
flow_pins[usb_uart->cfg.flow_pins - 1][0], GpioModeAnalog);
furi_hal_gpio_init_simple(
flow_pins[usb_uart->cfg.flow_pins - 1][1], GpioModeAnalog);
}
if(usb_uart->cfg_new.flow_pins != 0) {
furi_assert((size_t)(usb_uart->cfg_new.flow_pins - 1) < COUNT_OF(flow_pins));
furi_hal_gpio_init_simple(
flow_pins[usb_uart->cfg_new.flow_pins - 1][0], GpioModeOutputPushPull);
furi_hal_gpio_init_simple(
flow_pins[usb_uart->cfg_new.flow_pins - 1][1], GpioModeOutputPushPull);
}
usb_uart->cfg.flow_pins = usb_uart->cfg_new.flow_pins;
events |= WorkerEvtCtrlLineSet;
}
api_lock_unlock(usb_uart->cfg_lock);
}
if(events & WorkerEvtLineCfgSet) {
if(usb_uart->cfg.baudrate == 0)
usb_uart_set_baudrate(usb_uart, usb_uart->cfg.baudrate);
}
if(events & WorkerEvtCtrlLineSet) {
usb_uart_update_ctrl_lines(usb_uart);
}
}
usb_uart_vcp_deinit(usb_uart, usb_uart->cfg.vcp_ch);
usb_uart_serial_deinit(usb_uart, usb_uart->cfg.uart_ch);
if(usb_uart->cfg.flow_pins != 0) {
furi_hal_gpio_init_simple(flow_pins[usb_uart->cfg.flow_pins - 1][0], GpioModeAnalog);
furi_hal_gpio_init_simple(flow_pins[usb_uart->cfg.flow_pins - 1][1], GpioModeAnalog);
}
furi_thread_flags_set(furi_thread_get_id(usb_uart->tx_thread), WorkerEvtTxStop);
furi_thread_join(usb_uart->tx_thread);
furi_thread_free(usb_uart->tx_thread);
furi_stream_buffer_free(usb_uart->rx_stream);
furi_mutex_free(usb_uart->usb_mutex);
furi_semaphore_free(usb_uart->tx_sem);
furi_hal_usb_unlock();
furi_check(furi_hal_usb_set_config(&usb_cdc_single, NULL) == true);
Cli* cli = furi_record_open(RECORD_CLI);
cli_session_open(cli, &cli_vcp);
furi_record_close(RECORD_CLI);
return 0;
}
static int32_t usb_uart_tx_thread(void* context) {
UsbUartBridge* usb_uart = (UsbUartBridge*)context;
uint8_t data[USB_CDC_PKT_LEN];
while(1) {
uint32_t events =
furi_thread_flags_wait(WORKER_ALL_TX_EVENTS, FuriFlagWaitAny, FuriWaitForever);
furi_check(!(events & FuriFlagError));
if(events & WorkerEvtTxStop) break;
if(events & WorkerEvtCdcRx) {
furi_check(furi_mutex_acquire(usb_uart->usb_mutex, FuriWaitForever) == FuriStatusOk);
size_t len = furi_hal_cdc_receive(usb_uart->cfg.vcp_ch, data, USB_CDC_PKT_LEN);
furi_check(furi_mutex_release(usb_uart->usb_mutex) == FuriStatusOk);
if(len > 0) {
usb_uart->st.tx_cnt += len;
furi_hal_uart_tx(usb_uart->cfg.uart_ch, data, len);
}
}
}
return 0;
}
/* VCP callbacks */
static void vcp_on_cdc_tx_complete(void* context) {
UsbUartBridge* usb_uart = (UsbUartBridge*)context;
furi_semaphore_release(usb_uart->tx_sem);
}
static void vcp_on_cdc_rx(void* context) {
UsbUartBridge* usb_uart = (UsbUartBridge*)context;
furi_thread_flags_set(furi_thread_get_id(usb_uart->tx_thread), WorkerEvtCdcRx);
}
static void vcp_state_callback(void* context, uint8_t state) {
UNUSED(context);
UNUSED(state);
}
static void vcp_on_cdc_control_line(void* context, uint8_t state) {
UNUSED(state);
UsbUartBridge* usb_uart = (UsbUartBridge*)context;
furi_thread_flags_set(furi_thread_get_id(usb_uart->thread), WorkerEvtCtrlLineSet);
}
static void vcp_on_line_config(void* context, struct usb_cdc_line_coding* config) {
UNUSED(config);
UsbUartBridge* usb_uart = (UsbUartBridge*)context;
furi_thread_flags_set(furi_thread_get_id(usb_uart->thread), WorkerEvtLineCfgSet);
}
UsbUartBridge* usb_uart_enable(UsbUartConfig* cfg) {
UsbUartBridge* usb_uart = malloc(sizeof(UsbUartBridge));
memcpy(&(usb_uart->cfg_new), cfg, sizeof(UsbUartConfig));
usb_uart->thread = furi_thread_alloc_ex("UsbUartWorker", 1024, usb_uart_worker, usb_uart);
furi_thread_start(usb_uart->thread);
return usb_uart;
}
void usb_uart_disable(UsbUartBridge* usb_uart) {
furi_assert(usb_uart);
furi_thread_flags_set(furi_thread_get_id(usb_uart->thread), WorkerEvtStop);
furi_thread_join(usb_uart->thread);
furi_thread_free(usb_uart->thread);
free(usb_uart);
}
void usb_uart_set_config(UsbUartBridge* usb_uart, UsbUartConfig* cfg) {
furi_assert(usb_uart);
furi_assert(cfg);
usb_uart->cfg_lock = api_lock_alloc_locked();
memcpy(&(usb_uart->cfg_new), cfg, sizeof(UsbUartConfig));
furi_thread_flags_set(furi_thread_get_id(usb_uart->thread), WorkerEvtCfgChange);
api_lock_wait_unlock_and_free(usb_uart->cfg_lock);
}
void usb_uart_get_config(UsbUartBridge* usb_uart, UsbUartConfig* cfg) {
furi_assert(usb_uart);
furi_assert(cfg);
memcpy(cfg, &(usb_uart->cfg_new), sizeof(UsbUartConfig));
}
void usb_uart_get_state(UsbUartBridge* usb_uart, UsbUartState* st) {
furi_assert(usb_uart);
furi_assert(st);
memcpy(st, &(usb_uart->st), sizeof(UsbUartState));
}

View File

@@ -1,30 +0,0 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
typedef struct UsbUartBridge UsbUartBridge;
typedef struct {
uint8_t vcp_ch;
uint8_t uart_ch;
uint8_t flow_pins;
uint8_t baudrate_mode;
uint32_t baudrate;
} UsbUartConfig;
typedef struct {
uint32_t rx_cnt;
uint32_t tx_cnt;
uint32_t baudrate_cur;
} UsbUartState;
UsbUartBridge* usb_uart_enable(UsbUartConfig* cfg);
void usb_uart_disable(UsbUartBridge* usb_uart);
void usb_uart_set_config(UsbUartBridge* usb_uart, UsbUartConfig* cfg);
void usb_uart_get_config(UsbUartBridge* usb_uart, UsbUartConfig* cfg);
void usb_uart_get_state(UsbUartBridge* usb_uart, UsbUartState* st);

View File

@@ -1,160 +0,0 @@
#include "gpio_reader.h"
#include "../gpio_item.h"
#include <gui/elements.h>
#include <furi_hal_resources.h>
struct GpioReader {
View* view;
GpioReaderOkCallback callback;
void* context;
};
typedef struct {
uint8_t pin_idx;
bool pullUp[GPIO_ITEM_COUNT];
} GpioReaderModel;
static bool gpio_reader_process_ok(GpioReader* gpio_reader, InputEvent* event);
static bool gpio_reader_process_left(GpioReader* gpio_reader);
static bool gpio_reader_process_right(GpioReader* gpio_reader);
static void gpio_reader_draw_callback(Canvas* canvas, void* _model) {
GpioReaderModel* model = _model;
canvas_set_font(canvas, FontPrimary);
elements_multiline_text_aligned(canvas, 64, 2, AlignCenter, AlignTop, "GPIO Reader");
canvas_set_font(canvas, FontSecondary);
elements_multiline_text_aligned(
canvas, 64, 16, AlignCenter, AlignTop, "A7 A6 A4 B3 B2 C3 C1 C0");
elements_multiline_text_aligned(canvas, 64, 40, AlignCenter, AlignTop, "Pull Up");
int charOffset = 10;
for(uint8_t i = 0; i < GPIO_ITEM_COUNT; i++) {
bool high = gpio_item_get_pin(i);
if(high) {
elements_multiline_text_aligned(canvas, charOffset, 25, AlignCenter, AlignTop, "1");
} else {
elements_multiline_text_aligned(canvas, charOffset, 25, AlignCenter, AlignTop, "0");
}
if(model->pullUp[i]) {
elements_multiline_text_aligned(canvas, charOffset, 50, AlignCenter, AlignTop, "1");
} else {
elements_multiline_text_aligned(canvas, charOffset, 50, AlignCenter, AlignTop, "0");
}
if(i == model->pin_idx) {
elements_multiline_text_aligned(canvas, charOffset, 53, AlignCenter, AlignTop, "_");
}
charOffset += 16;
}
//~ free(charOffset);
}
static bool gpio_reader_input_callback(InputEvent* event, void* context) {
furi_assert(context);
GpioReader* gpio_reader = context;
bool consumed = false;
if(event->type == InputTypeShort) {
if(event->key == InputKeyRight) {
consumed = gpio_reader_process_right(gpio_reader);
} else if(event->key == InputKeyLeft) {
consumed = gpio_reader_process_left(gpio_reader);
}
} else if(event->key == InputKeyOk) {
consumed = gpio_reader_process_ok(gpio_reader, event);
}
return consumed;
}
static bool gpio_reader_process_left(GpioReader* gpio_reader) {
with_view_model(
gpio_reader->view,
GpioReaderModel * model,
{
if(model->pin_idx) {
model->pin_idx--;
}
},
true);
return true;
}
static bool gpio_reader_process_right(GpioReader* gpio_reader) {
with_view_model(
gpio_reader->view,
GpioReaderModel * model,
{
if(model->pin_idx < GPIO_ITEM_COUNT - 1) {
model->pin_idx++;
}
},
true);
return true;
}
static bool gpio_reader_process_ok(GpioReader* gpio_reader, InputEvent* event) {
bool consumed = false;
with_view_model(
gpio_reader->view,
GpioReaderModel * model,
{
if(event->type == InputTypePress) {
if(model->pullUp[model->pin_idx]) {
gpio_item_configure_pin(model->pin_idx, GpioModeInput, GpioPullDown);
model->pullUp[model->pin_idx] = 0;
consumed = true;
} else {
gpio_item_configure_pin(model->pin_idx, GpioModeInput, GpioPullUp);
model->pullUp[model->pin_idx] = 1;
consumed = true;
}
}
gpio_reader->callback(event->type, gpio_reader->context);
},
true);
return consumed;
}
GpioReader* gpio_reader_alloc() {
GpioReader* gpio_reader = malloc(sizeof(GpioReader));
gpio_reader->view = view_alloc();
view_allocate_model(gpio_reader->view, ViewModelTypeLocking, sizeof(GpioReaderModel));
view_set_context(gpio_reader->view, gpio_reader);
view_set_draw_callback(gpio_reader->view, gpio_reader_draw_callback);
view_set_input_callback(gpio_reader->view, gpio_reader_input_callback);
return gpio_reader;
}
void gpio_reader_free(GpioReader* gpio_reader) {
furi_assert(gpio_reader);
view_free(gpio_reader->view);
free(gpio_reader);
}
View* gpio_reader_get_view(GpioReader* gpio_reader) {
furi_assert(gpio_reader);
return gpio_reader->view;
}
void gpio_reader_set_ok_callback(
GpioReader* gpio_reader,
GpioReaderOkCallback callback,
void* context) {
furi_assert(gpio_reader);
furi_assert(callback);
with_view_model(
gpio_reader->view,
GpioReaderModel * model,
{
UNUSED(model);
gpio_reader->callback = callback;
gpio_reader->context = context;
},
false);
}

View File

@@ -1,17 +0,0 @@
#pragma once
#include <gui/view.h>
typedef struct GpioReader GpioReader;
typedef void (*GpioReaderOkCallback)(InputType type, void* context);
GpioReader* gpio_reader_alloc();
void gpio_reader_free(GpioReader* gpio_reader);
View* gpio_reader_get_view(GpioReader* gpio_reader);
void gpio_reader_set_ok_callback(
GpioReader* gpio_reader,
GpioReaderOkCallback callback,
void* context);

View File

@@ -1,139 +0,0 @@
#include "gpio_test.h"
#include "../gpio_item.h"
#include <gui/elements.h>
struct GpioTest {
View* view;
GpioTestOkCallback callback;
void* context;
};
typedef struct {
uint8_t pin_idx;
} GpioTestModel;
static bool gpio_test_process_left(GpioTest* gpio_test);
static bool gpio_test_process_right(GpioTest* gpio_test);
static bool gpio_test_process_ok(GpioTest* gpio_test, InputEvent* event);
static void gpio_test_draw_callback(Canvas* canvas, void* _model) {
GpioTestModel* model = _model;
canvas_set_font(canvas, FontPrimary);
elements_multiline_text_aligned(canvas, 64, 2, AlignCenter, AlignTop, "GPIO Output Mode Test");
canvas_set_font(canvas, FontSecondary);
elements_multiline_text_aligned(
canvas, 64, 16, AlignCenter, AlignTop, "Press < or > to change pin");
elements_multiline_text_aligned(
canvas, 64, 32, AlignCenter, AlignTop, gpio_item_get_pin_name(model->pin_idx));
}
static bool gpio_test_input_callback(InputEvent* event, void* context) {
furi_assert(context);
GpioTest* gpio_test = context;
bool consumed = false;
if(event->type == InputTypeShort) {
if(event->key == InputKeyRight) {
consumed = gpio_test_process_right(gpio_test);
} else if(event->key == InputKeyLeft) {
consumed = gpio_test_process_left(gpio_test);
}
} else if(event->key == InputKeyOk) {
consumed = gpio_test_process_ok(gpio_test, event);
}
return consumed;
}
static bool gpio_test_process_left(GpioTest* gpio_test) {
with_view_model(
gpio_test->view,
GpioTestModel * model,
{
if(model->pin_idx) {
model->pin_idx--;
}
},
true);
return true;
}
static bool gpio_test_process_right(GpioTest* gpio_test) {
with_view_model(
gpio_test->view,
GpioTestModel * model,
{
if(model->pin_idx < GPIO_ITEM_COUNT) {
model->pin_idx++;
}
},
true);
return true;
}
static bool gpio_test_process_ok(GpioTest* gpio_test, InputEvent* event) {
bool consumed = false;
with_view_model(
gpio_test->view,
GpioTestModel * model,
{
if(event->type == InputTypePress) {
if(model->pin_idx < GPIO_ITEM_COUNT) {
gpio_item_set_pin(model->pin_idx, true);
} else {
gpio_item_set_all_pins(true);
}
consumed = true;
} else if(event->type == InputTypeRelease) {
if(model->pin_idx < GPIO_ITEM_COUNT) {
gpio_item_set_pin(model->pin_idx, false);
} else {
gpio_item_set_all_pins(false);
}
consumed = true;
}
gpio_test->callback(event->type, gpio_test->context);
},
true);
return consumed;
}
GpioTest* gpio_test_alloc() {
GpioTest* gpio_test = malloc(sizeof(GpioTest));
gpio_test->view = view_alloc();
view_allocate_model(gpio_test->view, ViewModelTypeLocking, sizeof(GpioTestModel));
view_set_context(gpio_test->view, gpio_test);
view_set_draw_callback(gpio_test->view, gpio_test_draw_callback);
view_set_input_callback(gpio_test->view, gpio_test_input_callback);
return gpio_test;
}
void gpio_test_free(GpioTest* gpio_test) {
furi_assert(gpio_test);
view_free(gpio_test->view);
free(gpio_test);
}
View* gpio_test_get_view(GpioTest* gpio_test) {
furi_assert(gpio_test);
return gpio_test->view;
}
void gpio_test_set_ok_callback(GpioTest* gpio_test, GpioTestOkCallback callback, void* context) {
furi_assert(gpio_test);
furi_assert(callback);
with_view_model(
gpio_test->view,
GpioTestModel * model,
{
UNUSED(model);
gpio_test->callback = callback;
gpio_test->context = context;
},
false);
}

View File

@@ -1,14 +0,0 @@
#pragma once
#include <gui/view.h>
typedef struct GpioTest GpioTest;
typedef void (*GpioTestOkCallback)(InputType type, void* context);
GpioTest* gpio_test_alloc();
void gpio_test_free(GpioTest* gpio_test);
View* gpio_test_get_view(GpioTest* gpio_test);
void gpio_test_set_ok_callback(GpioTest* gpio_test, GpioTestOkCallback callback, void* context);

View File

@@ -1,161 +0,0 @@
#include "../usb_uart_bridge.h"
#include "../gpio_app_i.h"
#include "furi_hal.h"
#include <gui/elements.h>
struct GpioUsbUart {
View* view;
GpioUsbUartCallback callback;
void* context;
};
typedef struct {
uint32_t baudrate;
uint32_t tx_cnt;
uint32_t rx_cnt;
uint8_t vcp_port;
uint8_t tx_pin;
uint8_t rx_pin;
bool tx_active;
bool rx_active;
} GpioUsbUartModel;
static void gpio_usb_uart_draw_callback(Canvas* canvas, void* _model) {
GpioUsbUartModel* model = _model;
char temp_str[18];
elements_button_left(canvas, "Config");
canvas_draw_line(canvas, 2, 10, 125, 10);
canvas_draw_line(canvas, 44, 52, 123, 52);
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, 2, 9, "USB Serial");
canvas_draw_str(canvas, 3, 25, "TX:");
canvas_draw_str(canvas, 3, 42, "RX:");
canvas_set_font(canvas, FontSecondary);
snprintf(temp_str, 18, "COM PORT:%u", model->vcp_port);
canvas_draw_str_aligned(canvas, 126, 8, AlignRight, AlignBottom, temp_str);
snprintf(temp_str, 18, "Pin %u", model->tx_pin);
canvas_draw_str(canvas, 22, 25, temp_str);
snprintf(temp_str, 18, "Pin %u", model->rx_pin);
canvas_draw_str(canvas, 22, 42, temp_str);
if(model->baudrate == 0)
snprintf(temp_str, 18, "Baud: ????");
else
snprintf(temp_str, 18, "Baud: %lu", model->baudrate);
canvas_draw_str(canvas, 45, 62, temp_str);
if(model->tx_cnt < 100000000) {
canvas_set_font(canvas, FontSecondary);
canvas_draw_str_aligned(canvas, 127, 24, AlignRight, AlignBottom, "B.");
canvas_set_font(canvas, FontKeyboard);
snprintf(temp_str, 18, "%lu", model->tx_cnt);
canvas_draw_str_aligned(canvas, 116, 24, AlignRight, AlignBottom, temp_str);
} else {
canvas_set_font(canvas, FontSecondary);
canvas_draw_str_aligned(canvas, 127, 24, AlignRight, AlignBottom, "KiB.");
canvas_set_font(canvas, FontKeyboard);
snprintf(temp_str, 18, "%lu", model->tx_cnt / 1024);
canvas_draw_str_aligned(canvas, 111, 24, AlignRight, AlignBottom, temp_str);
}
if(model->rx_cnt < 100000000) {
canvas_set_font(canvas, FontSecondary);
canvas_draw_str_aligned(canvas, 127, 41, AlignRight, AlignBottom, "B.");
canvas_set_font(canvas, FontKeyboard);
snprintf(temp_str, 18, "%lu", model->rx_cnt);
canvas_draw_str_aligned(canvas, 116, 41, AlignRight, AlignBottom, temp_str);
} else {
canvas_set_font(canvas, FontSecondary);
canvas_draw_str_aligned(canvas, 127, 41, AlignRight, AlignBottom, "KiB.");
canvas_set_font(canvas, FontKeyboard);
snprintf(temp_str, 18, "%lu", model->rx_cnt / 1024);
canvas_draw_str_aligned(canvas, 111, 41, AlignRight, AlignBottom, temp_str);
}
if(model->tx_active)
canvas_draw_icon(canvas, 48, 14, &I_ArrowUpFilled_14x15);
else
canvas_draw_icon(canvas, 48, 14, &I_ArrowUpEmpty_14x15);
if(model->rx_active)
canvas_draw_icon_ex(canvas, 48, 34, &I_ArrowUpFilled_14x15, IconRotation180);
else
canvas_draw_icon_ex(canvas, 48, 34, &I_ArrowUpEmpty_14x15, IconRotation180);
}
static bool gpio_usb_uart_input_callback(InputEvent* event, void* context) {
furi_assert(context);
GpioUsbUart* usb_uart = context;
bool consumed = false;
if(event->type == InputTypeShort) {
if(event->key == InputKeyLeft) {
consumed = true;
furi_assert(usb_uart->callback);
usb_uart->callback(GpioUsbUartEventConfig, usb_uart->context);
}
}
return consumed;
}
GpioUsbUart* gpio_usb_uart_alloc() {
GpioUsbUart* usb_uart = malloc(sizeof(GpioUsbUart));
usb_uart->view = view_alloc();
view_allocate_model(usb_uart->view, ViewModelTypeLocking, sizeof(GpioUsbUartModel));
view_set_context(usb_uart->view, usb_uart);
view_set_draw_callback(usb_uart->view, gpio_usb_uart_draw_callback);
view_set_input_callback(usb_uart->view, gpio_usb_uart_input_callback);
return usb_uart;
}
void gpio_usb_uart_free(GpioUsbUart* usb_uart) {
furi_assert(usb_uart);
view_free(usb_uart->view);
free(usb_uart);
}
View* gpio_usb_uart_get_view(GpioUsbUart* usb_uart) {
furi_assert(usb_uart);
return usb_uart->view;
}
void gpio_usb_uart_set_callback(GpioUsbUart* usb_uart, GpioUsbUartCallback callback, void* context) {
furi_assert(usb_uart);
furi_assert(callback);
with_view_model(
usb_uart->view,
GpioUsbUartModel * model,
{
UNUSED(model);
usb_uart->callback = callback;
usb_uart->context = context;
},
false);
}
void gpio_usb_uart_update_state(GpioUsbUart* instance, UsbUartConfig* cfg, UsbUartState* st) {
furi_assert(instance);
furi_assert(cfg);
furi_assert(st);
with_view_model(
instance->view,
GpioUsbUartModel * model,
{
model->baudrate = st->baudrate_cur;
model->vcp_port = cfg->vcp_ch;
model->tx_pin = (cfg->uart_ch == 0) ? (13) : (15);
model->rx_pin = (cfg->uart_ch == 0) ? (14) : (16);
model->tx_active = (model->tx_cnt != st->tx_cnt);
model->rx_active = (model->rx_cnt != st->rx_cnt);
model->tx_cnt = st->tx_cnt;
model->rx_cnt = st->rx_cnt;
},
true);
}

View File

@@ -1,18 +0,0 @@
#pragma once
#include <gui/view.h>
#include "../gpio_custom_event.h"
#include "../usb_uart_bridge.h"
typedef struct GpioUsbUart GpioUsbUart;
typedef void (*GpioUsbUartCallback)(GpioCustomEvent event, void* context);
GpioUsbUart* gpio_usb_uart_alloc();
void gpio_usb_uart_free(GpioUsbUart* usb_uart);
View* gpio_usb_uart_get_view(GpioUsbUart* usb_uart);
void gpio_usb_uart_set_callback(GpioUsbUart* usb_uart, GpioUsbUartCallback callback, void* context);
void gpio_usb_uart_update_state(GpioUsbUart* instance, UsbUartConfig* cfg, UsbUartState* st);

View File

@@ -1,674 +0,0 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.

View File

@@ -1,13 +0,0 @@
App(
appid="gps_nmea",
name="[NMEA] GPS",
apptype=FlipperAppType.EXTERNAL,
entry_point="gps_app",
requires=["gui"],
stack_size=1 * 1024,
fap_icon="gps_10px.png",
fap_category="GPIO",
fap_author="@ezod & @xMasterX",
fap_version="1.0",
fap_description="Works with GPS modules via UART, using NMEA protocol.",
)

View File

@@ -1,199 +0,0 @@
#include "gps_uart.h"
#include <furi.h>
#include <gui/gui.h>
#include <string.h>
typedef enum {
EventTypeTick,
EventTypeKey,
} EventType;
typedef struct {
EventType type;
InputEvent input;
} PluginEvent;
static void render_callback(Canvas* const canvas, void* context) {
furi_assert(context);
GpsUart* gps_uart = context;
furi_mutex_acquire(gps_uart->mutex, FuriWaitForever);
if(!gps_uart->changing_baudrate) {
canvas_set_font(canvas, FontPrimary);
canvas_draw_str_aligned(canvas, 32, 8, AlignCenter, AlignBottom, "Latitude");
canvas_draw_str_aligned(canvas, 96, 8, AlignCenter, AlignBottom, "Longitude");
canvas_draw_str_aligned(canvas, 21, 30, AlignCenter, AlignBottom, "Course");
canvas_draw_str_aligned(canvas, 64, 30, AlignCenter, AlignBottom, "Speed");
canvas_draw_str_aligned(canvas, 107, 30, AlignCenter, AlignBottom, "Altitude");
canvas_draw_str_aligned(canvas, 32, 52, AlignCenter, AlignBottom, "Satellites");
canvas_draw_str_aligned(canvas, 96, 52, AlignCenter, AlignBottom, "Last Fix");
canvas_set_font(canvas, FontSecondary);
char buffer[64];
snprintf(buffer, 64, "%f", (double)gps_uart->status.latitude);
canvas_draw_str_aligned(canvas, 32, 18, AlignCenter, AlignBottom, buffer);
snprintf(buffer, 64, "%f", (double)gps_uart->status.longitude);
canvas_draw_str_aligned(canvas, 96, 18, AlignCenter, AlignBottom, buffer);
snprintf(buffer, 64, "%.1f", (double)gps_uart->status.course);
canvas_draw_str_aligned(canvas, 21, 40, AlignCenter, AlignBottom, buffer);
if(!gps_uart->speed_in_kms) {
snprintf(buffer, 64, "%.2f kn", (double)gps_uart->status.speed);
} else {
snprintf(buffer, 64, "%.2f km", (double)(gps_uart->status.speed * 1.852));
}
canvas_draw_str_aligned(canvas, 64, 40, AlignCenter, AlignBottom, buffer);
snprintf(
buffer,
64,
"%.1f %c",
(double)gps_uart->status.altitude,
tolower(gps_uart->status.altitude_units));
canvas_draw_str_aligned(canvas, 107, 40, AlignCenter, AlignBottom, buffer);
snprintf(buffer, 64, "%d", gps_uart->status.satellites_tracked);
canvas_draw_str_aligned(canvas, 32, 62, AlignCenter, AlignBottom, buffer);
snprintf(
buffer,
64,
"%02d:%02d:%02d UTC",
gps_uart->status.time_hours,
gps_uart->status.time_minutes,
gps_uart->status.time_seconds);
canvas_draw_str_aligned(canvas, 96, 62, AlignCenter, AlignBottom, buffer);
} else {
char buffer[64];
canvas_set_font(canvas, FontPrimary);
canvas_draw_str_aligned(canvas, 64, 32, AlignCenter, AlignBottom, "Baudrate set to:");
snprintf(buffer, 64, "%ld baud", gps_uart->baudrate);
canvas_draw_str_aligned(canvas, 64, 47, AlignCenter, AlignBottom, buffer);
}
furi_mutex_release(gps_uart->mutex);
}
static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
furi_assert(event_queue);
PluginEvent event = {.type = EventTypeKey, .input = *input_event};
furi_message_queue_put(event_queue, &event, FuriWaitForever);
}
int32_t gps_app(void* p) {
UNUSED(p);
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent));
GpsUart* gps_uart = gps_uart_enable();
gps_uart->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
if(!gps_uart->mutex) {
FURI_LOG_E("GPS", "cannot create mutex\r\n");
free(gps_uart);
return 255;
}
uint8_t attempts = 0;
bool otg_was_enabled = furi_hal_power_is_otg_enabled();
while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) {
furi_hal_power_enable_otg();
furi_delay_ms(10);
}
furi_delay_ms(200);
// set system callbacks
ViewPort* view_port = view_port_alloc();
view_port_draw_callback_set(view_port, render_callback, gps_uart);
view_port_input_callback_set(view_port, input_callback, event_queue);
// open GUI and register view_port
Gui* gui = furi_record_open(RECORD_GUI);
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
PluginEvent event;
for(bool processing = true; processing;) {
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
furi_mutex_acquire(gps_uart->mutex, FuriWaitForever);
if(event_status == FuriStatusOk) {
// press events
if(event.type == EventTypeKey) {
if(event.input.type == InputTypeShort) {
switch(event.input.key) {
case InputKeyBack:
processing = false;
break;
case InputKeyOk:
if(!gps_uart->backlight_on) {
notification_message_block(
gps_uart->notifications, &sequence_display_backlight_enforce_on);
gps_uart->backlight_on = true;
} else {
notification_message_block(
gps_uart->notifications, &sequence_display_backlight_enforce_auto);
notification_message(
gps_uart->notifications, &sequence_display_backlight_off);
gps_uart->backlight_on = false;
}
break;
default:
break;
}
} else if(event.input.type == InputTypeLong) {
switch(event.input.key) {
case InputKeyUp:
gps_uart_deinit_thread(gps_uart);
const int baudrate_length =
sizeof(gps_baudrates) / sizeof(gps_baudrates[0]);
current_gps_baudrate++;
if(current_gps_baudrate >= baudrate_length) {
current_gps_baudrate = 0;
}
gps_uart->baudrate = gps_baudrates[current_gps_baudrate];
gps_uart_init_thread(gps_uart);
gps_uart->changing_baudrate = true;
view_port_update(view_port);
furi_mutex_release(gps_uart->mutex);
break;
case InputKeyRight:
if(gps_uart->speed_in_kms) {
gps_uart->speed_in_kms = false;
} else {
gps_uart->speed_in_kms = true;
}
break;
case InputKeyBack:
processing = false;
break;
default:
break;
}
}
}
}
if(!gps_uart->changing_baudrate) {
view_port_update(view_port);
furi_mutex_release(gps_uart->mutex);
} else {
furi_delay_ms(1000);
gps_uart->changing_baudrate = false;
}
}
notification_message_block(gps_uart->notifications, &sequence_display_backlight_enforce_auto);
view_port_enabled_set(view_port, false);
gui_remove_view_port(gui, view_port);
furi_record_close(RECORD_GUI);
view_port_free(view_port);
furi_message_queue_free(event_queue);
furi_mutex_free(gps_uart->mutex);
gps_uart_disable(gps_uart);
if(furi_hal_power_is_otg_enabled() && !otg_was_enabled) {
furi_hal_power_disable_otg();
}
return 0;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

View File

@@ -1,226 +0,0 @@
#include <string.h>
#include "minmea.h"
#include "gps_uart.h"
typedef enum {
WorkerEvtStop = (1 << 0),
WorkerEvtRxDone = (1 << 1),
} WorkerEvtFlags;
#define WORKER_ALL_RX_EVENTS (WorkerEvtStop | WorkerEvtRxDone)
static void gps_uart_on_irq_cb(UartIrqEvent ev, uint8_t data, void* context) {
GpsUart* gps_uart = (GpsUart*)context;
if(ev == UartIrqEventRXNE) {
furi_stream_buffer_send(gps_uart->rx_stream, &data, 1, 0);
furi_thread_flags_set(furi_thread_get_id(gps_uart->thread), WorkerEvtRxDone);
}
}
static void gps_uart_serial_init(GpsUart* gps_uart) {
if(UART_CH == FuriHalUartIdUSART1) {
furi_hal_console_disable();
} else if(UART_CH == FuriHalUartIdLPUART1) {
furi_hal_uart_init(UART_CH, gps_uart->baudrate);
}
furi_hal_uart_set_irq_cb(UART_CH, gps_uart_on_irq_cb, gps_uart);
furi_hal_uart_set_br(UART_CH, gps_uart->baudrate);
}
static void gps_uart_serial_deinit(GpsUart* gps_uart) {
UNUSED(gps_uart);
furi_hal_uart_set_irq_cb(UART_CH, NULL, NULL);
if(UART_CH == FuriHalUartIdLPUART1) {
furi_hal_uart_deinit(UART_CH);
} else {
furi_hal_console_enable();
}
}
static void gps_uart_parse_nmea(GpsUart* gps_uart, char* line) {
switch(minmea_sentence_id(line, false)) {
case MINMEA_SENTENCE_RMC: {
struct minmea_sentence_rmc frame;
if(minmea_parse_rmc(&frame, line)) {
gps_uart->status.valid = frame.valid;
gps_uart->status.latitude = minmea_tocoord(&frame.latitude);
gps_uart->status.longitude = minmea_tocoord(&frame.longitude);
gps_uart->status.speed = minmea_tofloat(&frame.speed);
gps_uart->status.course = minmea_tofloat(&frame.course);
gps_uart->status.time_hours = frame.time.hours;
gps_uart->status.time_minutes = frame.time.minutes;
gps_uart->status.time_seconds = frame.time.seconds;
notification_message_block(gps_uart->notifications, &sequence_blink_green_10);
}
} break;
case MINMEA_SENTENCE_GGA: {
struct minmea_sentence_gga frame;
if(minmea_parse_gga(&frame, line)) {
gps_uart->status.latitude = minmea_tocoord(&frame.latitude);
gps_uart->status.longitude = minmea_tocoord(&frame.longitude);
gps_uart->status.altitude = minmea_tofloat(&frame.altitude);
gps_uart->status.altitude_units = frame.altitude_units;
gps_uart->status.fix_quality = frame.fix_quality;
gps_uart->status.satellites_tracked = frame.satellites_tracked;
gps_uart->status.time_hours = frame.time.hours;
gps_uart->status.time_minutes = frame.time.minutes;
gps_uart->status.time_seconds = frame.time.seconds;
notification_message_block(gps_uart->notifications, &sequence_blink_magenta_10);
}
} break;
case MINMEA_SENTENCE_GLL: {
struct minmea_sentence_gll frame;
if(minmea_parse_gll(&frame, line)) {
gps_uart->status.latitude = minmea_tocoord(&frame.latitude);
gps_uart->status.longitude = minmea_tocoord(&frame.longitude);
gps_uart->status.time_hours = frame.time.hours;
gps_uart->status.time_minutes = frame.time.minutes;
gps_uart->status.time_seconds = frame.time.seconds;
notification_message_block(gps_uart->notifications, &sequence_blink_red_10);
}
} break;
default:
break;
}
}
static int32_t gps_uart_worker(void* context) {
GpsUart* gps_uart = (GpsUart*)context;
size_t rx_offset = 0;
while(1) {
uint32_t events =
furi_thread_flags_wait(WORKER_ALL_RX_EVENTS, FuriFlagWaitAny, FuriWaitForever);
furi_check((events & FuriFlagError) == 0);
if(events & WorkerEvtStop) {
break;
}
if(events & WorkerEvtRxDone) {
size_t len = 0;
do {
// receive serial bytes into rx_buf, starting at rx_offset from the start of the buffer
// the maximum we can receive is RX_BUF_SIZE - 1 - rx_offset
len = furi_stream_buffer_receive(
gps_uart->rx_stream,
gps_uart->rx_buf + rx_offset,
RX_BUF_SIZE - 1 - rx_offset,
0);
if(len > 0) {
// increase rx_offset by the number of bytes received, and null-terminate rx_buf
rx_offset += len;
gps_uart->rx_buf[rx_offset] = '\0';
// look for strings ending in newlines, starting at the start of rx_buf
char* line_current = (char*)gps_uart->rx_buf;
while(1) {
// skip null characters
while(*line_current == '\0' &&
line_current < (char*)gps_uart->rx_buf + rx_offset - 1) {
line_current++;
}
// find the next newline
char* newline = strchr(line_current, '\n');
if(newline) // newline found
{
// put a null terminator in place of the newline, to delimit the line string
*newline = '\0';
// attempt to parse the line as a NMEA sentence
gps_uart_parse_nmea(gps_uart, line_current);
// move the cursor to the character after the newline
line_current = newline + 1;
} else // no more newlines found
{
if(line_current >
(char*)gps_uart->rx_buf) // at least one line was found
{
// clear parsed lines, and move any leftover bytes to the start of rx_buf
rx_offset = 0;
while(
*line_current) // stop when the original rx_offset terminator is reached
{
gps_uart->rx_buf[rx_offset++] = *(line_current++);
}
}
break; // go back to receiving bytes from the serial stream
}
}
}
} while(len > 0);
}
}
gps_uart_serial_deinit(gps_uart);
furi_stream_buffer_free(gps_uart->rx_stream);
return 0;
}
void gps_uart_init_thread(GpsUart* gps_uart) {
furi_assert(gps_uart);
gps_uart->status.valid = false;
gps_uart->status.latitude = 0.0;
gps_uart->status.longitude = 0.0;
gps_uart->status.speed = 0.0;
gps_uart->status.course = 0.0;
gps_uart->status.altitude = 0.0;
gps_uart->status.altitude_units = ' ';
gps_uart->status.fix_quality = 0;
gps_uart->status.satellites_tracked = 0;
gps_uart->status.time_hours = 0;
gps_uart->status.time_minutes = 0;
gps_uart->status.time_seconds = 0;
gps_uart->rx_stream = furi_stream_buffer_alloc(RX_BUF_SIZE * 5, 1);
gps_uart->thread = furi_thread_alloc();
furi_thread_set_name(gps_uart->thread, "GpsUartWorker");
furi_thread_set_stack_size(gps_uart->thread, 1024);
furi_thread_set_context(gps_uart->thread, gps_uart);
furi_thread_set_callback(gps_uart->thread, gps_uart_worker);
furi_thread_start(gps_uart->thread);
gps_uart_serial_init(gps_uart);
}
void gps_uart_deinit_thread(GpsUart* gps_uart) {
furi_assert(gps_uart);
furi_thread_flags_set(furi_thread_get_id(gps_uart->thread), WorkerEvtStop);
furi_thread_join(gps_uart->thread);
furi_thread_free(gps_uart->thread);
}
GpsUart* gps_uart_enable() {
GpsUart* gps_uart = malloc(sizeof(GpsUart));
gps_uart->notifications = furi_record_open(RECORD_NOTIFICATION);
gps_uart->baudrate = gps_baudrates[current_gps_baudrate];
gps_uart_init_thread(gps_uart);
return gps_uart;
}
void gps_uart_disable(GpsUart* gps_uart) {
furi_assert(gps_uart);
gps_uart_deinit_thread(gps_uart);
furi_record_close(RECORD_NOTIFICATION);
free(gps_uart);
}

View File

@@ -1,50 +0,0 @@
#pragma once
#include <furi_hal.h>
#include <notification/notification_messages.h>
#include <xtreme.h>
#define UART_CH \
(xtreme_settings.uart_nmea_channel == UARTDefault ? FuriHalUartIdUSART1 : FuriHalUartIdLPUART1)
#define RX_BUF_SIZE 1024
static const int gps_baudrates[5] = {9600, 19200, 38400, 57600, 115200};
static int current_gps_baudrate = 0;
typedef struct {
bool valid;
float latitude;
float longitude;
float speed;
float course;
float altitude;
char altitude_units;
int fix_quality;
int satellites_tracked;
int time_hours;
int time_minutes;
int time_seconds;
} GpsStatus;
typedef struct {
FuriMutex* mutex;
FuriThread* thread;
FuriStreamBuffer* rx_stream;
uint8_t rx_buf[RX_BUF_SIZE];
NotificationApp* notifications;
uint32_t baudrate;
bool changing_baudrate;
bool backlight_on;
bool speed_in_kms;
GpsStatus status;
} GpsUart;
void gps_uart_init_thread(GpsUart* gps_uart);
void gps_uart_deinit_thread(GpsUart* gps_uart);
GpsUart* gps_uart_enable();
void gps_uart_disable(GpsUart* gps_uart);

View File

@@ -1,640 +0,0 @@
/*
* Copyright © 2014 Kosma Moczek <kosma@cloudyourcar.com>
* This program is free software. It comes without any warranty, to the extent
* permitted by applicable law. You can redistribute it and/or modify it under
* the terms of the Do What The Fuck You Want To Public License, Version 2, as
* published by Sam Hocevar. See the COPYING file for more details.
*/
#include "minmea.h"
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#define boolstr(s) ((s) ? "true" : "false")
static int hex2int(char c) {
if(c >= '0' && c <= '9') return c - '0';
if(c >= 'A' && c <= 'F') return c - 'A' + 10;
if(c >= 'a' && c <= 'f') return c - 'a' + 10;
return -1;
}
uint8_t minmea_checksum(const char* sentence) {
// Support senteces with or without the starting dollar sign.
if(*sentence == '$') sentence++;
uint8_t checksum = 0x00;
// The optional checksum is an XOR of all bytes between "$" and "*".
while(*sentence && *sentence != '*') checksum ^= *sentence++;
return checksum;
}
bool minmea_check(const char* sentence, bool strict) {
uint8_t checksum = 0x00;
// A valid sentence starts with "$".
if(*sentence++ != '$') return false;
// The optional checksum is an XOR of all bytes between "$" and "*".
while(*sentence && *sentence != '*' && isprint((unsigned char)*sentence))
checksum ^= *sentence++;
// If checksum is present...
if(*sentence == '*') {
// Extract checksum.
sentence++;
int upper = hex2int(*sentence++);
if(upper == -1) return false;
int lower = hex2int(*sentence++);
if(lower == -1) return false;
int expected = upper << 4 | lower;
// Check for checksum mismatch.
if(checksum != expected) return false;
} else if(strict) {
// Discard non-checksummed frames in strict mode.
return false;
}
// The only stuff allowed at this point is a newline.
while(*sentence == '\r' || *sentence == '\n') {
sentence++;
}
if(*sentence) {
return false;
}
return true;
}
bool minmea_scan(const char* sentence, const char* format, ...) {
bool result = false;
bool optional = false;
if(sentence == NULL) return false;
va_list ap;
va_start(ap, format);
const char* field = sentence;
#define next_field() \
do { \
/* Progress to the next field. */ \
while(minmea_isfield(*sentence)) sentence++; \
/* Make sure there is a field there. */ \
if(*sentence == ',') { \
sentence++; \
field = sentence; \
} else { \
field = NULL; \
} \
} while(0)
while(*format) {
char type = *format++;
if(type == ';') {
// All further fields are optional.
optional = true;
continue;
}
if(!field && !optional) {
// Field requested but we ran out if input. Bail out.
goto parse_error;
}
switch(type) {
case 'c': { // Single character field (char).
char value = '\0';
if(field && minmea_isfield(*field)) value = *field;
*va_arg(ap, char*) = value;
} break;
case 'd': { // Single character direction field (int).
int value = 0;
if(field && minmea_isfield(*field)) {
switch(*field) {
case 'N':
case 'E':
value = 1;
break;
case 'S':
case 'W':
value = -1;
break;
default:
goto parse_error;
}
}
*va_arg(ap, int*) = value;
} break;
case 'f': { // Fractional value with scale (struct minmea_float).
int sign = 0;
int_least32_t value = -1;
int_least32_t scale = 0;
if(field) {
while(minmea_isfield(*field)) {
if(*field == '+' && !sign && value == -1) {
sign = 1;
} else if(*field == '-' && !sign && value == -1) {
sign = -1;
} else if(isdigit((unsigned char)*field)) {
int digit = *field - '0';
if(value == -1) value = 0;
if(value > (INT_LEAST32_MAX - digit) / 10) {
/* we ran out of bits, what do we do? */
if(scale) {
/* truncate extra precision */
break;
} else {
/* integer overflow. bail out. */
goto parse_error;
}
}
value = (10 * value) + digit;
if(scale) scale *= 10;
} else if(*field == '.' && scale == 0) {
scale = 1;
} else if(*field == ' ') {
/* Allow spaces at the start of the field. Not NMEA
* conformant, but some modules do this. */
if(sign != 0 || value != -1 || scale != 0) goto parse_error;
} else {
goto parse_error;
}
field++;
}
}
if((sign || scale) && value == -1) goto parse_error;
if(value == -1) {
/* No digits were scanned. */
value = 0;
scale = 0;
} else if(scale == 0) {
/* No decimal point. */
scale = 1;
}
if(sign) value *= sign;
*va_arg(ap, struct minmea_float*) = (struct minmea_float){value, scale};
} break;
case 'i': { // Integer value, default 0 (int).
int value = 0;
if(field) {
char* endptr;
value = strtol(field, &endptr, 10);
if(minmea_isfield(*endptr)) goto parse_error;
}
*va_arg(ap, int*) = value;
} break;
case 's': { // String value (char *).
char* buf = va_arg(ap, char*);
if(field) {
while(minmea_isfield(*field)) *buf++ = *field++;
}
*buf = '\0';
} break;
case 't': { // NMEA talker+sentence identifier (char *).
// This field is always mandatory.
if(!field) goto parse_error;
if(field[0] != '$') goto parse_error;
for(int f = 0; f < 5; f++)
if(!minmea_isfield(field[1 + f])) goto parse_error;
char* buf = va_arg(ap, char*);
memcpy(buf, field + 1, 5);
buf[5] = '\0';
} break;
case 'D': { // Date (int, int, int), -1 if empty.
struct minmea_date* date = va_arg(ap, struct minmea_date*);
int d = -1, m = -1, y = -1;
if(field && minmea_isfield(*field)) {
// Always six digits.
for(int f = 0; f < 6; f++)
if(!isdigit((unsigned char)field[f])) goto parse_error;
char dArr[] = {field[0], field[1], '\0'};
char mArr[] = {field[2], field[3], '\0'};
char yArr[] = {field[4], field[5], '\0'};
d = strtol(dArr, NULL, 10);
m = strtol(mArr, NULL, 10);
y = strtol(yArr, NULL, 10);
}
date->day = d;
date->month = m;
date->year = y;
} break;
case 'T': { // Time (int, int, int, int), -1 if empty.
struct minmea_time* time_ = va_arg(ap, struct minmea_time*);
int h = -1, i = -1, s = -1, u = -1;
if(field && minmea_isfield(*field)) {
// Minimum required: integer time.
for(int f = 0; f < 6; f++)
if(!isdigit((unsigned char)field[f])) goto parse_error;
char hArr[] = {field[0], field[1], '\0'};
char iArr[] = {field[2], field[3], '\0'};
char sArr[] = {field[4], field[5], '\0'};
h = strtol(hArr, NULL, 10);
i = strtol(iArr, NULL, 10);
s = strtol(sArr, NULL, 10);
field += 6;
// Extra: fractional time. Saved as microseconds.
if(*field++ == '.') {
uint32_t value = 0;
uint32_t scale = 1000000LU;
while(isdigit((unsigned char)*field) && scale > 1) {
value = (value * 10) + (*field++ - '0');
scale /= 10;
}
u = value * scale;
} else {
u = 0;
}
}
time_->hours = h;
time_->minutes = i;
time_->seconds = s;
time_->microseconds = u;
} break;
case '_': { // Ignore the field.
} break;
default: { // Unknown.
goto parse_error;
}
}
next_field();
}
result = true;
parse_error:
va_end(ap);
return result;
}
bool minmea_talker_id(char talker[3], const char* sentence) {
char type[6];
if(!minmea_scan(sentence, "t", type)) return false;
talker[0] = type[0];
talker[1] = type[1];
talker[2] = '\0';
return true;
}
enum minmea_sentence_id minmea_sentence_id(const char* sentence, bool strict) {
if(!minmea_check(sentence, strict)) return MINMEA_INVALID;
char type[6];
if(!minmea_scan(sentence, "t", type)) return MINMEA_INVALID;
if(!strcmp(type + 2, "GBS")) return MINMEA_SENTENCE_GBS;
if(!strcmp(type + 2, "GGA")) return MINMEA_SENTENCE_GGA;
if(!strcmp(type + 2, "GLL")) return MINMEA_SENTENCE_GLL;
if(!strcmp(type + 2, "GSA")) return MINMEA_SENTENCE_GSA;
if(!strcmp(type + 2, "GST")) return MINMEA_SENTENCE_GST;
if(!strcmp(type + 2, "GSV")) return MINMEA_SENTENCE_GSV;
if(!strcmp(type + 2, "RMC")) return MINMEA_SENTENCE_RMC;
if(!strcmp(type + 2, "VTG")) return MINMEA_SENTENCE_VTG;
if(!strcmp(type + 2, "ZDA")) return MINMEA_SENTENCE_ZDA;
return MINMEA_UNKNOWN;
}
bool minmea_parse_gbs(struct minmea_sentence_gbs* frame, const char* sentence) {
// $GNGBS,170556.00,3.0,2.9,8.3,,,,*5C
char type[6];
if(!minmea_scan(
sentence,
"tTfffifff",
type,
&frame->time,
&frame->err_latitude,
&frame->err_longitude,
&frame->err_altitude,
&frame->svid,
&frame->prob,
&frame->bias,
&frame->stddev))
return false;
if(strcmp(type + 2, "GBS")) return false;
return true;
}
bool minmea_parse_rmc(struct minmea_sentence_rmc* frame, const char* sentence) {
// $GPRMC,081836,A,3751.65,S,14507.36,E,000.0,360.0,130998,011.3,E*62
char type[6];
char validity;
int latitude_direction;
int longitude_direction;
int variation_direction;
if(!minmea_scan(
sentence,
"tTcfdfdffDfd",
type,
&frame->time,
&validity,
&frame->latitude,
&latitude_direction,
&frame->longitude,
&longitude_direction,
&frame->speed,
&frame->course,
&frame->date,
&frame->variation,
&variation_direction))
return false;
if(strcmp(type + 2, "RMC")) return false;
frame->valid = (validity == 'A');
frame->latitude.value *= latitude_direction;
frame->longitude.value *= longitude_direction;
frame->variation.value *= variation_direction;
return true;
}
bool minmea_parse_gga(struct minmea_sentence_gga* frame, const char* sentence) {
// $GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47
char type[6];
int latitude_direction;
int longitude_direction;
if(!minmea_scan(
sentence,
"tTfdfdiiffcfcf_",
type,
&frame->time,
&frame->latitude,
&latitude_direction,
&frame->longitude,
&longitude_direction,
&frame->fix_quality,
&frame->satellites_tracked,
&frame->hdop,
&frame->altitude,
&frame->altitude_units,
&frame->height,
&frame->height_units,
&frame->dgps_age))
return false;
if(strcmp(type + 2, "GGA")) return false;
frame->latitude.value *= latitude_direction;
frame->longitude.value *= longitude_direction;
return true;
}
bool minmea_parse_gsa(struct minmea_sentence_gsa* frame, const char* sentence) {
// $GPGSA,A,3,04,05,,09,12,,,24,,,,,2.5,1.3,2.1*39
char type[6];
if(!minmea_scan(
sentence,
"tciiiiiiiiiiiiifff",
type,
&frame->mode,
&frame->fix_type,
&frame->sats[0],
&frame->sats[1],
&frame->sats[2],
&frame->sats[3],
&frame->sats[4],
&frame->sats[5],
&frame->sats[6],
&frame->sats[7],
&frame->sats[8],
&frame->sats[9],
&frame->sats[10],
&frame->sats[11],
&frame->pdop,
&frame->hdop,
&frame->vdop))
return false;
if(strcmp(type + 2, "GSA")) return false;
return true;
}
bool minmea_parse_gll(struct minmea_sentence_gll* frame, const char* sentence) {
// $GPGLL,3723.2475,N,12158.3416,W,161229.487,A,A*41$;
char type[6];
int latitude_direction;
int longitude_direction;
if(!minmea_scan(
sentence,
"tfdfdTc;c",
type,
&frame->latitude,
&latitude_direction,
&frame->longitude,
&longitude_direction,
&frame->time,
&frame->status,
&frame->mode))
return false;
if(strcmp(type + 2, "GLL")) return false;
frame->latitude.value *= latitude_direction;
frame->longitude.value *= longitude_direction;
return true;
}
bool minmea_parse_gst(struct minmea_sentence_gst* frame, const char* sentence) {
// $GPGST,024603.00,3.2,6.6,4.7,47.3,5.8,5.6,22.0*58
char type[6];
if(!minmea_scan(
sentence,
"tTfffffff",
type,
&frame->time,
&frame->rms_deviation,
&frame->semi_major_deviation,
&frame->semi_minor_deviation,
&frame->semi_major_orientation,
&frame->latitude_error_deviation,
&frame->longitude_error_deviation,
&frame->altitude_error_deviation))
return false;
if(strcmp(type + 2, "GST")) return false;
return true;
}
bool minmea_parse_gsv(struct minmea_sentence_gsv* frame, const char* sentence) {
// $GPGSV,3,1,11,03,03,111,00,04,15,270,00,06,01,010,00,13,06,292,00*74
// $GPGSV,3,3,11,22,42,067,42,24,14,311,43,27,05,244,00,,,,*4D
// $GPGSV,4,2,11,08,51,203,30,09,45,215,28*75
// $GPGSV,4,4,13,39,31,170,27*40
// $GPGSV,4,4,13*7B
char type[6];
if(!minmea_scan(
sentence,
"tiii;iiiiiiiiiiiiiiii",
type,
&frame->total_msgs,
&frame->msg_nr,
&frame->total_sats,
&frame->sats[0].nr,
&frame->sats[0].elevation,
&frame->sats[0].azimuth,
&frame->sats[0].snr,
&frame->sats[1].nr,
&frame->sats[1].elevation,
&frame->sats[1].azimuth,
&frame->sats[1].snr,
&frame->sats[2].nr,
&frame->sats[2].elevation,
&frame->sats[2].azimuth,
&frame->sats[2].snr,
&frame->sats[3].nr,
&frame->sats[3].elevation,
&frame->sats[3].azimuth,
&frame->sats[3].snr)) {
return false;
}
if(strcmp(type + 2, "GSV")) return false;
return true;
}
bool minmea_parse_vtg(struct minmea_sentence_vtg* frame, const char* sentence) {
// $GPVTG,054.7,T,034.4,M,005.5,N,010.2,K*48
// $GPVTG,156.1,T,140.9,M,0.0,N,0.0,K*41
// $GPVTG,096.5,T,083.5,M,0.0,N,0.0,K,D*22
// $GPVTG,188.36,T,,M,0.820,N,1.519,K,A*3F
char type[6];
char c_true, c_magnetic, c_knots, c_kph, c_faa_mode;
if(!minmea_scan(
sentence,
"t;fcfcfcfcc",
type,
&frame->true_track_degrees,
&c_true,
&frame->magnetic_track_degrees,
&c_magnetic,
&frame->speed_knots,
&c_knots,
&frame->speed_kph,
&c_kph,
&c_faa_mode))
return false;
if(strcmp(type + 2, "VTG")) return false;
// values are only valid with the accompanying characters
if(c_true != 'T') frame->true_track_degrees.scale = 0;
if(c_magnetic != 'M') frame->magnetic_track_degrees.scale = 0;
if(c_knots != 'N') frame->speed_knots.scale = 0;
if(c_kph != 'K') frame->speed_kph.scale = 0;
frame->faa_mode = (enum minmea_faa_mode)c_faa_mode;
return true;
}
bool minmea_parse_zda(struct minmea_sentence_zda* frame, const char* sentence) {
// $GPZDA,201530.00,04,07,2002,00,00*60
char type[6];
if(!minmea_scan(
sentence,
"tTiiiii",
type,
&frame->time,
&frame->date.day,
&frame->date.month,
&frame->date.year,
&frame->hour_offset,
&frame->minute_offset))
return false;
if(strcmp(type + 2, "ZDA")) return false;
// check offsets
if(abs(frame->hour_offset) > 13 || frame->minute_offset > 59 || frame->minute_offset < 0)
return false;
return true;
}
int minmea_getdatetime(
struct tm* tm,
const struct minmea_date* date,
const struct minmea_time* time_) {
if(date->year == -1 || time_->hours == -1) return -1;
memset(tm, 0, sizeof(*tm));
if(date->year < 80) {
tm->tm_year = 2000 + date->year - 1900; // 2000-2079
} else if(date->year >= 1900) {
tm->tm_year = date->year - 1900; // 4 digit year, use directly
} else {
tm->tm_year = date->year; // 1980-1999
}
tm->tm_mon = date->month - 1;
tm->tm_mday = date->day;
tm->tm_hour = time_->hours;
tm->tm_min = time_->minutes;
tm->tm_sec = time_->seconds;
return 0;
}
int minmea_gettime(
struct timespec* ts,
const struct minmea_date* date,
const struct minmea_time* time_) {
struct tm tm;
if(minmea_getdatetime(&tm, date, time_)) return -1;
time_t timestamp = mktime(&tm); /* See README.md if your system lacks timegm(). */
if(timestamp != (time_t)-1) {
ts->tv_sec = timestamp;
ts->tv_nsec = time_->microseconds * 1000;
return 0;
} else {
return -1;
}
}
/* vim: set ts=4 sw=4 et: */

View File

@@ -1,295 +0,0 @@
/*
* Copyright © 2014 Kosma Moczek <kosma@cloudyourcar.com>
* This program is free software. It comes without any warranty, to the extent
* permitted by applicable law. You can redistribute it and/or modify it under
* the terms of the Do What The Fuck You Want To Public License, Version 2, as
* published by Sam Hocevar. See the COPYING file for more details.
*/
#ifndef MINMEA_H
#define MINMEA_H
#ifdef __cplusplus
extern "C" {
#endif
#include <ctype.h>
#include <stdint.h>
#include <stdbool.h>
#include <time.h>
#include <math.h>
#ifdef MINMEA_INCLUDE_COMPAT
#include <minmea_compat.h>
#endif
#ifndef MINMEA_MAX_SENTENCE_LENGTH
#define MINMEA_MAX_SENTENCE_LENGTH 80
#endif
enum minmea_sentence_id {
MINMEA_INVALID = -1,
MINMEA_UNKNOWN = 0,
MINMEA_SENTENCE_GBS,
MINMEA_SENTENCE_GGA,
MINMEA_SENTENCE_GLL,
MINMEA_SENTENCE_GSA,
MINMEA_SENTENCE_GST,
MINMEA_SENTENCE_GSV,
MINMEA_SENTENCE_RMC,
MINMEA_SENTENCE_VTG,
MINMEA_SENTENCE_ZDA,
};
struct minmea_float {
int_least32_t value;
int_least32_t scale;
};
struct minmea_date {
int day;
int month;
int year;
};
struct minmea_time {
int hours;
int minutes;
int seconds;
int microseconds;
};
struct minmea_sentence_gbs {
struct minmea_time time;
struct minmea_float err_latitude;
struct minmea_float err_longitude;
struct minmea_float err_altitude;
int svid;
struct minmea_float prob;
struct minmea_float bias;
struct minmea_float stddev;
};
struct minmea_sentence_rmc {
struct minmea_time time;
bool valid;
struct minmea_float latitude;
struct minmea_float longitude;
struct minmea_float speed;
struct minmea_float course;
struct minmea_date date;
struct minmea_float variation;
};
struct minmea_sentence_gga {
struct minmea_time time;
struct minmea_float latitude;
struct minmea_float longitude;
int fix_quality;
int satellites_tracked;
struct minmea_float hdop;
struct minmea_float altitude;
char altitude_units;
struct minmea_float height;
char height_units;
struct minmea_float dgps_age;
};
enum minmea_gll_status {
MINMEA_GLL_STATUS_DATA_VALID = 'A',
MINMEA_GLL_STATUS_DATA_NOT_VALID = 'V',
};
// FAA mode added to some fields in NMEA 2.3.
enum minmea_faa_mode {
MINMEA_FAA_MODE_AUTONOMOUS = 'A',
MINMEA_FAA_MODE_DIFFERENTIAL = 'D',
MINMEA_FAA_MODE_ESTIMATED = 'E',
MINMEA_FAA_MODE_MANUAL = 'M',
MINMEA_FAA_MODE_SIMULATED = 'S',
MINMEA_FAA_MODE_NOT_VALID = 'N',
MINMEA_FAA_MODE_PRECISE = 'P',
};
struct minmea_sentence_gll {
struct minmea_float latitude;
struct minmea_float longitude;
struct minmea_time time;
char status;
char mode;
};
struct minmea_sentence_gst {
struct minmea_time time;
struct minmea_float rms_deviation;
struct minmea_float semi_major_deviation;
struct minmea_float semi_minor_deviation;
struct minmea_float semi_major_orientation;
struct minmea_float latitude_error_deviation;
struct minmea_float longitude_error_deviation;
struct minmea_float altitude_error_deviation;
};
enum minmea_gsa_mode {
MINMEA_GPGSA_MODE_AUTO = 'A',
MINMEA_GPGSA_MODE_FORCED = 'M',
};
enum minmea_gsa_fix_type {
MINMEA_GPGSA_FIX_NONE = 1,
MINMEA_GPGSA_FIX_2D = 2,
MINMEA_GPGSA_FIX_3D = 3,
};
struct minmea_sentence_gsa {
char mode;
int fix_type;
int sats[12];
struct minmea_float pdop;
struct minmea_float hdop;
struct minmea_float vdop;
};
struct minmea_sat_info {
int nr;
int elevation;
int azimuth;
int snr;
};
struct minmea_sentence_gsv {
int total_msgs;
int msg_nr;
int total_sats;
struct minmea_sat_info sats[4];
};
struct minmea_sentence_vtg {
struct minmea_float true_track_degrees;
struct minmea_float magnetic_track_degrees;
struct minmea_float speed_knots;
struct minmea_float speed_kph;
enum minmea_faa_mode faa_mode;
};
struct minmea_sentence_zda {
struct minmea_time time;
struct minmea_date date;
int hour_offset;
int minute_offset;
};
/**
* Calculate raw sentence checksum. Does not check sentence integrity.
*/
uint8_t minmea_checksum(const char* sentence);
/**
* Check sentence validity and checksum. Returns true for valid sentences.
*/
bool minmea_check(const char* sentence, bool strict);
/**
* Determine talker identifier.
*/
bool minmea_talker_id(char talker[3], const char* sentence);
/**
* Determine sentence identifier.
*/
enum minmea_sentence_id minmea_sentence_id(const char* sentence, bool strict);
/**
* Scanf-like processor for NMEA sentences. Supports the following formats:
* c - single character (char *)
* d - direction, returned as 1/-1, default 0 (int *)
* f - fractional, returned as value + scale (struct minmea_float *)
* i - decimal, default zero (int *)
* s - string (char *)
* t - talker identifier and type (char *)
* D - date (struct minmea_date *)
* T - time stamp (struct minmea_time *)
* _ - ignore this field
* ; - following fields are optional
* Returns true on success. See library source code for details.
*/
bool minmea_scan(const char* sentence, const char* format, ...);
/*
* Parse a specific type of sentence. Return true on success.
*/
bool minmea_parse_gbs(struct minmea_sentence_gbs* frame, const char* sentence);
bool minmea_parse_rmc(struct minmea_sentence_rmc* frame, const char* sentence);
bool minmea_parse_gga(struct minmea_sentence_gga* frame, const char* sentence);
bool minmea_parse_gsa(struct minmea_sentence_gsa* frame, const char* sentence);
bool minmea_parse_gll(struct minmea_sentence_gll* frame, const char* sentence);
bool minmea_parse_gst(struct minmea_sentence_gst* frame, const char* sentence);
bool minmea_parse_gsv(struct minmea_sentence_gsv* frame, const char* sentence);
bool minmea_parse_vtg(struct minmea_sentence_vtg* frame, const char* sentence);
bool minmea_parse_zda(struct minmea_sentence_zda* frame, const char* sentence);
/**
* Convert GPS UTC date/time representation to a UNIX calendar time.
*/
int minmea_getdatetime(
struct tm* tm,
const struct minmea_date* date,
const struct minmea_time* time_);
/**
* Convert GPS UTC date/time representation to a UNIX timestamp.
*/
int minmea_gettime(
struct timespec* ts,
const struct minmea_date* date,
const struct minmea_time* time_);
/**
* Rescale a fixed-point value to a different scale. Rounds towards zero.
*/
static inline int_least32_t minmea_rescale(const struct minmea_float* f, int_least32_t new_scale) {
if(f->scale == 0) return 0;
if(f->scale == new_scale) return f->value;
if(f->scale > new_scale)
return (f->value + ((f->value > 0) - (f->value < 0)) * f->scale / new_scale / 2) /
(f->scale / new_scale);
else
return f->value * (new_scale / f->scale);
}
/**
* Convert a fixed-point value to a floating-point value.
* Returns NaN for "unknown" values.
*/
static inline float minmea_tofloat(const struct minmea_float* f) {
if(f->scale == 0) return NAN;
return (float)f->value / (float)f->scale;
}
/**
* Convert a raw coordinate to a floating point DD.DDD... value.
* Returns NaN for "unknown" values.
*/
static inline float minmea_tocoord(const struct minmea_float* f) {
if(f->scale == 0) return NAN;
if(f->scale > (INT_LEAST32_MAX / 100)) return NAN;
if(f->scale < (INT_LEAST32_MIN / 100)) return NAN;
int_least32_t degrees = f->value / (f->scale * 100);
int_least32_t minutes = f->value % (f->scale * 100);
return (float)degrees + (float)minutes / (60 * f->scale);
}
/**
* Check whether a character belongs to the set of characters allowed in a
* sentence data field.
*/
static inline bool minmea_isfield(char c) {
return isprint((unsigned char)c) && c != ',' && c != '*';
}
#ifdef __cplusplus
}
#endif
#endif /* MINMEA_H */
/* vim: set ts=4 sw=4 et: */

View File

@@ -1,16 +0,0 @@
App(
appid="metronome",
name="Metronome",
apptype=FlipperAppType.EXTERNAL,
entry_point="metronome_app",
requires=[
"gui",
],
fap_icon="metronome_icon.png",
fap_category="Media",
fap_icon_assets="images",
stack_size=2 * 1024,
fap_author="@panki27 & @xMasterX",
fap_version="1.0",
fap_description="Metronome app",
)

View File

@@ -1,57 +0,0 @@
#include <gui/canvas.h>
#include <gui/icon_i.h>
#include "metronome_icons.h"
#include <assets_icons.h>
//lib can only do bottom left/right
void elements_button_top_left(Canvas* canvas, const char* str) {
const uint8_t button_height = 12;
const uint8_t vertical_offset = 3;
const uint8_t horizontal_offset = 3;
const uint8_t string_width = canvas_string_width(canvas, str);
const Icon* icon = &I_ButtonUp_7x4;
const uint8_t icon_h_offset = 3;
const uint8_t icon_width_with_offset = icon->width + icon_h_offset;
const uint8_t icon_v_offset = icon->height + vertical_offset;
const uint8_t button_width = string_width + horizontal_offset * 2 + icon_width_with_offset;
const uint8_t x = 0;
const uint8_t y = 0 + button_height;
canvas_draw_box(canvas, x, y - button_height, button_width, button_height);
canvas_draw_line(canvas, x + button_width + 0, y - button_height, x + button_width + 0, y - 1);
canvas_draw_line(canvas, x + button_width + 1, y - button_height, x + button_width + 1, y - 2);
canvas_draw_line(canvas, x + button_width + 2, y - button_height, x + button_width + 2, y - 3);
canvas_invert_color(canvas);
canvas_draw_icon(canvas, x + horizontal_offset, y - icon_v_offset, &I_ButtonUp_7x4);
canvas_draw_str(
canvas, x + horizontal_offset + icon_width_with_offset, y - vertical_offset, str);
canvas_invert_color(canvas);
}
void elements_button_top_right(Canvas* canvas, const char* str) {
const uint8_t button_height = 12;
const uint8_t vertical_offset = 3;
const uint8_t horizontal_offset = 3;
const uint8_t string_width = canvas_string_width(canvas, str);
const Icon* icon = &I_ButtonUp_7x4;
const uint8_t icon_h_offset = 3;
const uint8_t icon_width_with_offset = icon->width + icon_h_offset;
const uint8_t icon_v_offset = icon->height + vertical_offset;
const uint8_t button_width = string_width + horizontal_offset * 2 + icon_width_with_offset;
const uint8_t x = canvas_width(canvas);
const uint8_t y = 0 + button_height;
canvas_draw_box(canvas, x - button_width, y - button_height, button_width, button_height);
canvas_draw_line(canvas, x - button_width - 1, y - button_height, x - button_width - 1, y - 1);
canvas_draw_line(canvas, x - button_width - 2, y - button_height, x - button_width - 2, y - 2);
canvas_draw_line(canvas, x - button_width - 3, y - button_height, x - button_width - 3, y - 3);
canvas_invert_color(canvas);
canvas_draw_str(canvas, x - button_width + horizontal_offset, y - vertical_offset, str);
canvas_draw_icon(
canvas, x - horizontal_offset - icon->width, y - icon_v_offset, &I_ButtonUp_7x4);
canvas_invert_color(canvas);
}

View File

@@ -1,3 +0,0 @@
void elements_button_top_right(Canvas* canvas, const char* str);
void elements_button_top_left(Canvas* canvas, const char* str);

View File

@@ -1,398 +0,0 @@
#include <furi.h>
#include <furi_hal.h>
#include <input/input.h>
#include <stdlib.h>
#include <gui/gui.h>
#include <gui/elements.h>
#include <gui/canvas.h>
#include <notification/notification.h>
#include <notification/notification_messages.h>
#include "gui_extensions.h"
#define BPM_STEP_SIZE_FINE 0.5d
#define BPM_STEP_SIZE_COARSE 10.0d
#define BPM_BOUNDARY_LOW 10.0d
#define BPM_BOUNDARY_HIGH 300.0d
#define BEEP_DELAY_MS 50
#define wave_bitmap_left_width 4
#define wave_bitmap_left_height 14
static uint8_t wave_bitmap_left_bits[] =
{0x08, 0x0C, 0x06, 0x06, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x06, 0x06, 0x0C, 0x08};
#define wave_bitmap_right_width 4
#define wave_bitmap_right_height 14
static uint8_t wave_bitmap_right_bits[] =
{0x01, 0x03, 0x06, 0x06, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x06, 0x06, 0x03, 0x01};
typedef enum {
EventTypeTick,
EventTypeKey,
} EventType;
typedef struct {
EventType type;
InputEvent input;
} PluginEvent;
enum OutputMode { Loud, Vibro, Silent };
typedef struct {
double bpm;
bool playing;
int beats_per_bar;
int note_length;
int current_beat;
enum OutputMode output_mode;
FuriTimer* timer;
NotificationApp* notifications;
FuriMutex* mutex;
} MetronomeState;
static void render_callback(Canvas* const canvas, void* ctx) {
furi_assert(ctx);
const MetronomeState* metronome_state = ctx;
furi_mutex_acquire(metronome_state->mutex, FuriWaitForever);
FuriString* tempStr = furi_string_alloc();
canvas_draw_frame(canvas, 0, 0, 128, 64);
canvas_set_font(canvas, FontPrimary);
// draw bars/beat
furi_string_printf(
tempStr, "%d/%d", metronome_state->beats_per_bar, metronome_state->note_length);
canvas_draw_str_aligned(
canvas, 64, 8, AlignCenter, AlignCenter, furi_string_get_cstr(tempStr));
furi_string_reset(tempStr);
// draw BPM value
furi_string_printf(tempStr, "%.2f", metronome_state->bpm);
canvas_set_font(canvas, FontBigNumbers);
canvas_draw_str_aligned(
canvas, 64, 24, AlignCenter, AlignCenter, furi_string_get_cstr(tempStr));
furi_string_reset(tempStr);
// draw volume indicator
// always draw first waves
canvas_draw_xbm(
canvas, 20, 17, wave_bitmap_left_width, wave_bitmap_left_height, wave_bitmap_left_bits);
canvas_draw_xbm(
canvas,
canvas_width(canvas) - 20 - wave_bitmap_right_width,
17,
wave_bitmap_right_width,
wave_bitmap_right_height,
wave_bitmap_right_bits);
if(metronome_state->output_mode < Silent) {
canvas_draw_xbm(
canvas, 16, 17, wave_bitmap_left_width, wave_bitmap_left_height, wave_bitmap_left_bits);
canvas_draw_xbm(
canvas,
canvas_width(canvas) - 16 - wave_bitmap_right_width,
17,
wave_bitmap_right_width,
wave_bitmap_right_height,
wave_bitmap_right_bits);
}
if(metronome_state->output_mode < Vibro) {
canvas_draw_xbm(
canvas, 12, 17, wave_bitmap_left_width, wave_bitmap_left_height, wave_bitmap_left_bits);
canvas_draw_xbm(
canvas,
canvas_width(canvas) - 12 - wave_bitmap_right_width,
17,
wave_bitmap_right_width,
wave_bitmap_right_height,
wave_bitmap_right_bits);
}
// draw button prompts
canvas_set_font(canvas, FontSecondary);
elements_button_left(canvas, "Slow");
elements_button_right(canvas, "Fast");
if(metronome_state->playing) {
elements_button_center(canvas, "Stop ");
} else {
elements_button_center(canvas, "Start");
}
elements_button_top_left(canvas, "Push");
elements_button_top_right(canvas, "Hold");
// draw progress bar
elements_progress_bar(
canvas, 8, 36, 112, (float)metronome_state->current_beat / metronome_state->beats_per_bar);
// cleanup
furi_string_free(tempStr);
furi_mutex_release(metronome_state->mutex);
}
static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
furi_assert(event_queue);
PluginEvent event = {.type = EventTypeKey, .input = *input_event};
furi_message_queue_put(event_queue, &event, FuriWaitForever);
}
static void timer_callback(void* ctx) {
// this is where we go BEEP!
furi_assert(ctx);
MetronomeState* metronome_state = ctx;
furi_mutex_acquire(metronome_state->mutex, FuriWaitForever);
metronome_state->current_beat++;
if(metronome_state->current_beat > metronome_state->beats_per_bar) {
metronome_state->current_beat = 1;
}
if(metronome_state->current_beat == 1) {
// pronounced beat
notification_message(metronome_state->notifications, &sequence_set_only_red_255);
switch(metronome_state->output_mode) {
case Loud:
if(furi_hal_speaker_acquire(1000)) {
furi_hal_speaker_start(440.0f, 1.0f);
}
break;
case Vibro:
notification_message(metronome_state->notifications, &sequence_set_vibro_on);
break;
case Silent:
break;
}
} else {
// unpronounced beat
notification_message(metronome_state->notifications, &sequence_set_only_green_255);
switch(metronome_state->output_mode) {
case Loud:
if(furi_hal_speaker_acquire(1000)) {
furi_hal_speaker_start(220.0f, 1.0f);
}
break;
case Vibro:
notification_message(metronome_state->notifications, &sequence_set_vibro_on);
break;
case Silent:
break;
}
};
// this is a bit of a kludge... if we are on vibro and unpronounced, stop vibro after half the usual duration
switch(metronome_state->output_mode) {
case Loud:
furi_delay_ms(BEEP_DELAY_MS);
if(furi_hal_speaker_is_mine()) {
furi_hal_speaker_stop();
furi_hal_speaker_release();
}
break;
case Vibro:
if(metronome_state->current_beat == 1) {
furi_delay_ms(BEEP_DELAY_MS);
notification_message(metronome_state->notifications, &sequence_reset_vibro);
} else {
furi_delay_ms((int)BEEP_DELAY_MS / 2);
notification_message(metronome_state->notifications, &sequence_reset_vibro);
furi_delay_ms((int)BEEP_DELAY_MS / 2);
}
break;
case Silent:
break;
}
notification_message(metronome_state->notifications, &sequence_reset_rgb);
furi_mutex_release(metronome_state->mutex);
}
static uint32_t state_to_sleep_ticks(MetronomeState* metronome_state) {
// calculate time between beeps
uint32_t tps = furi_kernel_get_tick_frequency();
double multiplier = 4.0d / metronome_state->note_length;
double bps = (double)metronome_state->bpm / 60;
return (uint32_t)(round(tps / bps) - ((BEEP_DELAY_MS / 1000) * tps)) * multiplier;
}
static void update_timer(MetronomeState* metronome_state) {
if(furi_timer_is_running(metronome_state->timer)) {
furi_timer_stop(metronome_state->timer);
furi_timer_start(metronome_state->timer, state_to_sleep_ticks(metronome_state));
}
}
static void increase_bpm(MetronomeState* metronome_state, double amount) {
metronome_state->bpm += amount;
if(metronome_state->bpm > (double)BPM_BOUNDARY_HIGH) {
metronome_state->bpm = BPM_BOUNDARY_HIGH;
}
update_timer(metronome_state);
}
static void decrease_bpm(MetronomeState* metronome_state, double amount) {
metronome_state->bpm -= amount;
if(metronome_state->bpm < (double)BPM_BOUNDARY_LOW) {
metronome_state->bpm = BPM_BOUNDARY_LOW;
}
update_timer(metronome_state);
}
static void cycle_beats_per_bar(MetronomeState* metronome_state) {
metronome_state->beats_per_bar++;
if(metronome_state->beats_per_bar > metronome_state->note_length) {
metronome_state->beats_per_bar = 1;
}
}
static void cycle_note_length(MetronomeState* metronome_state) {
metronome_state->note_length *= 2;
if(metronome_state->note_length > 16) {
metronome_state->note_length = 2;
metronome_state->beats_per_bar = 1;
}
update_timer(metronome_state);
}
static void cycle_output_mode(MetronomeState* metronome_state) {
metronome_state->output_mode++;
if(metronome_state->output_mode > Silent) {
metronome_state->output_mode = Loud;
}
}
static void metronome_state_init(MetronomeState* const metronome_state) {
metronome_state->bpm = 120.0;
metronome_state->playing = false;
metronome_state->beats_per_bar = 4;
metronome_state->note_length = 4;
metronome_state->current_beat = 0;
metronome_state->output_mode = Loud;
metronome_state->notifications = furi_record_open(RECORD_NOTIFICATION);
}
int32_t metronome_app() {
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent));
MetronomeState* metronome_state = malloc(sizeof(MetronomeState));
metronome_state_init(metronome_state);
metronome_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
if(!metronome_state->mutex) {
FURI_LOG_E("Metronome", "cannot create mutex\r\n");
free(metronome_state);
return 255;
}
// Set system callbacks
ViewPort* view_port = view_port_alloc();
view_port_draw_callback_set(view_port, render_callback, metronome_state);
view_port_input_callback_set(view_port, input_callback, event_queue);
metronome_state->timer =
furi_timer_alloc(timer_callback, FuriTimerTypePeriodic, metronome_state);
// Open GUI and register view_port
Gui* gui = furi_record_open(RECORD_GUI);
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
PluginEvent event;
for(bool processing = true; processing;) {
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
furi_mutex_acquire(metronome_state->mutex, FuriWaitForever);
if(event_status == FuriStatusOk) {
if(event.type == EventTypeKey) {
if(event.input.type == InputTypeShort) {
// push events
switch(event.input.key) {
case InputKeyUp:
cycle_beats_per_bar(metronome_state);
break;
case InputKeyDown:
cycle_output_mode(metronome_state);
break;
case InputKeyRight:
increase_bpm(metronome_state, BPM_STEP_SIZE_FINE);
break;
case InputKeyLeft:
decrease_bpm(metronome_state, BPM_STEP_SIZE_FINE);
break;
case InputKeyOk:
metronome_state->playing = !metronome_state->playing;
if(metronome_state->playing) {
furi_timer_start(
metronome_state->timer, state_to_sleep_ticks(metronome_state));
} else {
furi_timer_stop(metronome_state->timer);
}
break;
case InputKeyBack:
processing = false;
break;
default:
break;
}
} else if(event.input.type == InputTypeLong) {
// hold events
switch(event.input.key) {
case InputKeyUp:
cycle_note_length(metronome_state);
break;
case InputKeyDown:
break;
case InputKeyRight:
increase_bpm(metronome_state, BPM_STEP_SIZE_COARSE);
break;
case InputKeyLeft:
decrease_bpm(metronome_state, BPM_STEP_SIZE_COARSE);
break;
case InputKeyOk:
break;
case InputKeyBack:
processing = false;
break;
default:
break;
}
} else if(event.input.type == InputTypeRepeat) {
// repeat events
switch(event.input.key) {
case InputKeyUp:
break;
case InputKeyDown:
break;
case InputKeyRight:
increase_bpm(metronome_state, BPM_STEP_SIZE_COARSE);
break;
case InputKeyLeft:
decrease_bpm(metronome_state, BPM_STEP_SIZE_COARSE);
break;
case InputKeyOk:
break;
case InputKeyBack:
processing = false;
break;
default:
break;
}
}
}
}
view_port_update(view_port);
furi_mutex_release(metronome_state->mutex);
}
view_port_enabled_set(view_port, false);
gui_remove_view_port(gui, view_port);
furi_record_close(RECORD_GUI);
view_port_free(view_port);
furi_message_queue_free(event_queue);
furi_mutex_free(metronome_state->mutex);
furi_timer_free(metronome_state->timer);
furi_record_close(RECORD_NOTIFICATION);
free(metronome_state);
return 0;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 170 B

View File

@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2022 Skurydin Alexey
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -1,15 +0,0 @@
App(
appid="passgen",
name="Password Generator",
apptype=FlipperAppType.EXTERNAL,
entry_point="passgenapp",
requires=[
"gui",
],
fap_category="Tools",
fap_icon="icons/passgen_icon.png",
fap_icon_assets="icons",
fap_author="@anakod & @henrygab",
fap_version="1.1",
fap_description="Simple password generator",
)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 173 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 980 B

View File

@@ -1,264 +0,0 @@
#include <furi.h>
#include <furi_hal_random.h>
#include <gui/gui.h>
#include <gui/elements.h>
#include <input/input.h>
#include <notification/notification_messages.h>
#include <stdlib.h>
#include "passgen_icons.h"
#include <assets_icons.h>
#define PASSGEN_MAX_LENGTH 16
#define PASSGEN_CHARACTERS_LENGTH (26 * 4)
#define PASSGEN_DIGITS "0123456789"
#define PASSGEN_LETTERS_LOW "abcdefghijklmnopqrstuvwxyz"
#define PASSGEN_LETTERS_UP "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
#define PASSGEN_SPECIAL "!#$%^&*.-_"
typedef enum PassGen_Alphabet {
Digits = 1,
Lowercase = 2,
Uppercase = 4,
Special = 8,
DigitsLower = Digits | Lowercase,
DigitsAllLetters = Digits | Lowercase | Uppercase,
Mixed = DigitsAllLetters | Special
} PassGen_Alphabet;
const char* const PassGen_AlphabetChars[16] = {
"0", // invalid value
/* PASSGEN_SPECIAL PASSGEN_LETTERS_UP PASSGEN_LETTERS_LOW */ PASSGEN_DIGITS,
/* PASSGEN_SPECIAL PASSGEN_LETTERS_UP */ PASSGEN_LETTERS_LOW /* PASSGEN_DIGITS */,
/* PASSGEN_SPECIAL PASSGEN_LETTERS_UP */ PASSGEN_LETTERS_LOW PASSGEN_DIGITS,
/* PASSGEN_SPECIAL */ PASSGEN_LETTERS_UP /* PASSGEN_LETTERS_LOW PASSGEN_DIGITS */,
/* PASSGEN_SPECIAL */ PASSGEN_LETTERS_UP /* PASSGEN_LETTERS_LOW */ PASSGEN_DIGITS,
/* PASSGEN_SPECIAL */ PASSGEN_LETTERS_UP PASSGEN_LETTERS_LOW /* PASSGEN_DIGITS */,
/* PASSGEN_SPECIAL */ PASSGEN_LETTERS_UP PASSGEN_LETTERS_LOW PASSGEN_DIGITS,
PASSGEN_SPECIAL /* PASSGEN_LETTERS_UP PASSGEN_LETTERS_LOW PASSGEN_DIGITS */,
PASSGEN_SPECIAL /* PASSGEN_LETTERS_UP PASSGEN_LETTERS_LOW */ PASSGEN_DIGITS,
PASSGEN_SPECIAL /* PASSGEN_LETTERS_UP */ PASSGEN_LETTERS_LOW /* PASSGEN_DIGITS */,
PASSGEN_SPECIAL /* PASSGEN_LETTERS_UP */ PASSGEN_LETTERS_LOW PASSGEN_DIGITS,
PASSGEN_SPECIAL PASSGEN_LETTERS_UP /* PASSGEN_LETTERS_LOW PASSGEN_DIGITS */,
PASSGEN_SPECIAL PASSGEN_LETTERS_UP /* PASSGEN_LETTERS_LOW */ PASSGEN_DIGITS,
PASSGEN_SPECIAL PASSGEN_LETTERS_UP PASSGEN_LETTERS_LOW /* PASSGEN_DIGITS */,
PASSGEN_SPECIAL PASSGEN_LETTERS_UP PASSGEN_LETTERS_LOW PASSGEN_DIGITS,
};
const int AlphabetLevels[] = {Digits, Lowercase, DigitsLower, DigitsAllLetters, Mixed};
const char* AlphabetLevelNames[] = {"1234", "abcd", "ab12", "Ab12", "Ab1#"};
const int AlphabetLevelsCount = sizeof(AlphabetLevels) / sizeof(int);
const NotificationSequence PassGen_Alert_vibro = {
&message_vibro_on,
&message_blue_255,
&message_delay_50,
&message_vibro_off,
NULL,
};
typedef struct {
FuriMessageQueue* input_queue;
ViewPort* view_port;
Gui* gui;
FuriMutex** mutex;
NotificationApp* notify;
const char* alphabet;
char password[PASSGEN_MAX_LENGTH + 1];
int length; // must be <= PASSGEN_MAX_LENGTH
int level;
} PassGen;
void state_free(PassGen* app) {
// NOTE: would have preferred if a "safe" memset() was available...
// but, since cannot prevent optimization from removing
// memset(), fill with random data instead.
furi_hal_random_fill_buf((void*)(app->password), PASSGEN_MAX_LENGTH);
gui_remove_view_port(app->gui, app->view_port);
furi_record_close(RECORD_GUI);
view_port_free(app->view_port);
furi_message_queue_free(app->input_queue);
furi_mutex_free(app->mutex);
furi_record_close(RECORD_NOTIFICATION);
free(app);
}
static void input_callback(InputEvent* input_event, void* ctx) {
PassGen* app = ctx;
if(input_event->type == InputTypeShort) {
furi_message_queue_put(app->input_queue, input_event, 0);
}
}
static void render_callback(Canvas* canvas, void* ctx) {
char str_length[8];
PassGen* app = ctx;
furi_check(furi_mutex_acquire(app->mutex, FuriWaitForever) == FuriStatusOk);
canvas_clear(canvas);
canvas_draw_box(canvas, 0, 0, 128, 14);
canvas_set_color(canvas, ColorWhite);
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, 2, 11, "Password Generator");
canvas_set_color(canvas, ColorBlack);
canvas_draw_str_aligned(canvas, 64, 35, AlignCenter, AlignCenter, app->password);
// Navigation menu:
canvas_set_font(canvas, FontSecondary);
canvas_draw_icon(canvas, 96, 52, &I_Pin_back_arrow_10x8);
canvas_draw_str(canvas, 108, 60, "Exit");
canvas_draw_icon(canvas, 54, 52, &I_Vertical_arrow_7x9);
canvas_draw_str(canvas, 64, 60, AlphabetLevelNames[app->level]);
snprintf(str_length, sizeof(str_length), "Len: %d", app->length);
canvas_draw_icon(canvas, 4, 53, &I_Horizontal_arrow_9x7);
canvas_draw_str(canvas, 15, 60, str_length);
furi_mutex_release(app->mutex);
}
void build_alphabet(PassGen* app) {
PassGen_Alphabet mode = AlphabetLevels[app->level];
if(mode > 0 && mode < 16) {
app->alphabet = PassGen_AlphabetChars[mode];
} else {
app->alphabet =
PassGen_AlphabetChars[0]; // Invalid mode ... password will be all zero digits
}
}
PassGen* state_init() {
PassGen* app = malloc(sizeof(PassGen));
_Static_assert(8 <= PASSGEN_MAX_LENGTH, "app->length must be set <= PASSGEN_MAX_LENGTH");
app->length = 8;
app->level = 2;
build_alphabet(app);
app->input_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
app->view_port = view_port_alloc();
app->gui = furi_record_open(RECORD_GUI);
app->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
view_port_input_callback_set(app->view_port, input_callback, app);
view_port_draw_callback_set(app->view_port, render_callback, app);
gui_add_view_port(app->gui, app->view_port, GuiLayerFullscreen);
app->notify = furi_record_open(RECORD_NOTIFICATION);
return app;
}
void generate(PassGen* app) {
memset(app->password, 0, PASSGEN_MAX_LENGTH + 1);
int char_option_count = strlen(app->alphabet);
if(char_option_count < 0) {
return;
}
// determine largest character value that avoids bias
char ceil = CHAR_MAX - (CHAR_MAX % char_option_count) - 1;
// iteratively fill the password buffer with random values
// then keep only values that are in-range (no bias)
void* remaining_buffer = app->password;
size_t remaining_length = (app->length * sizeof(char));
while(remaining_length != 0) {
// fewer calls to hardware TRNG is more efficient
furi_hal_random_fill_buf(remaining_buffer, remaining_length);
// keep only values that are in-range (no bias)
char* target = remaining_buffer;
char* source = remaining_buffer;
size_t valid_count = 0;
for(size_t i = 0; i < remaining_length; i++) {
int v = *source;
// if the generated random value is in range, keep it
if(v < ceil) {
v %= char_option_count;
*target = app->alphabet[v];
// increment target pointer and count of valid items found
target++;
valid_count++;
}
// always increment the source pointer
source++;
}
remaining_length -= valid_count;
remaining_buffer = target;
}
}
void update_password(PassGen* app, bool vibro) {
generate(app);
if(vibro)
notification_message(app->notify, &PassGen_Alert_vibro);
else
notification_message(app->notify, &sequence_blink_blue_100);
view_port_update(app->view_port);
}
int32_t passgenapp(void) {
PassGen* app = state_init();
generate(app);
while(1) {
InputEvent input;
while(furi_message_queue_get(app->input_queue, &input, FuriWaitForever) == FuriStatusOk) {
furi_check(furi_mutex_acquire(app->mutex, FuriWaitForever) == FuriStatusOk);
if(input.type == InputTypeShort) {
switch(input.key) {
case InputKeyBack:
furi_mutex_release(app->mutex);
state_free(app);
return 0;
case InputKeyDown:
if(app->level > 0) {
app->level--;
build_alphabet(app);
update_password(app, false);
} else
notification_message(app->notify, &sequence_blink_red_100);
break;
case InputKeyUp:
if(app->level < AlphabetLevelsCount - 1) {
app->level++;
build_alphabet(app);
update_password(app, false);
} else
notification_message(app->notify, &sequence_blink_red_100);
break;
case InputKeyLeft:
if(app->length > 1) {
app->length--;
update_password(app, false);
} else
notification_message(app->notify, &sequence_blink_red_100);
break;
case InputKeyRight:
if(app->length < PASSGEN_MAX_LENGTH) {
app->length++;
update_password(app, false);
} else
notification_message(app->notify, &sequence_blink_red_100);
break;
case InputKeyOk:
update_password(app, true);
break;
default:
break;
}
}
furi_mutex_release(app->mutex);
}
}
state_free(app);
return 0;
}

View File

@@ -1,15 +0,0 @@
App(
appid="flipp_pomodoro",
name="Pomodoro Timer",
apptype=FlipperAppType.EXTERNAL,
entry_point="flipp_pomodoro_app",
requires=["gui", "notification", "dolphin"],
stack_size=1 * 1024,
fap_category="Tools",
fap_icon_assets="images",
fap_icon="flipp_pomodoro_10.png",
fap_author="@Th3Un1q3",
fap_weburl="https://github.com/Th3Un1q3/flipp_pomodoro",
fap_version="1.2",
fap_description="Boost Your Productivity with the Pomodoro Timer",
)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 157 B

View File

@@ -1,121 +0,0 @@
#include "flipp_pomodoro_app_i.h"
#define TAG "FlippPomodoro"
enum {
CustomEventConsumed = true,
CustomEventNotConsumed = false,
};
static bool flipp_pomodoro_app_back_event_callback(void* ctx) {
furi_assert(ctx);
FlippPomodoroApp* app = ctx;
return scene_manager_handle_back_event(app->scene_manager);
};
static void flipp_pomodoro_app_tick_event_callback(void* ctx) {
furi_assert(ctx);
FlippPomodoroApp* app = ctx;
scene_manager_handle_custom_event(app->scene_manager, FlippPomodoroAppCustomEventTimerTick);
};
static bool flipp_pomodoro_app_custom_event_callback(void* ctx, uint32_t event) {
furi_assert(ctx);
FlippPomodoroApp* app = ctx;
switch(event) {
case FlippPomodoroAppCustomEventStageSkip:
flipp_pomodoro__toggle_stage(app->state);
view_dispatcher_send_custom_event(
app->view_dispatcher, FlippPomodoroAppCustomEventStateUpdated);
return CustomEventConsumed;
case FlippPomodoroAppCustomEventStageComplete:
if(flipp_pomodoro__get_stage(app->state) == FlippPomodoroStageFocus) {
// REGISTER a deed on work stage complete to get an acheivement
dolphin_deed(DolphinDeedPluginGameWin);
FURI_LOG_I(TAG, "Focus stage reward added");
flipp_pomodoro_statistics__increase_focus_stages_completed(app->statistics);
};
flipp_pomodoro__toggle_stage(app->state);
notification_message(
app->notification_app,
stage_start_notification_sequence_map[flipp_pomodoro__get_stage(app->state)]);
view_dispatcher_send_custom_event(
app->view_dispatcher, FlippPomodoroAppCustomEventStateUpdated);
return CustomEventConsumed;
default:
break;
}
return scene_manager_handle_custom_event(app->scene_manager, event);
};
FlippPomodoroApp* flipp_pomodoro_app_alloc() {
FlippPomodoroApp* app = malloc(sizeof(FlippPomodoroApp));
app->state = flipp_pomodoro__new();
app->scene_manager = scene_manager_alloc(&flipp_pomodoro_scene_handlers, app);
app->gui = furi_record_open(RECORD_GUI);
app->notification_app = furi_record_open(RECORD_NOTIFICATION);
app->view_dispatcher = view_dispatcher_alloc();
app->statistics = flipp_pomodoro_statistics__new();
view_dispatcher_enable_queue(app->view_dispatcher);
view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
view_dispatcher_set_custom_event_callback(
app->view_dispatcher, flipp_pomodoro_app_custom_event_callback);
view_dispatcher_set_tick_event_callback(
app->view_dispatcher, flipp_pomodoro_app_tick_event_callback, 1000);
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
view_dispatcher_set_navigation_event_callback(
app->view_dispatcher, flipp_pomodoro_app_back_event_callback);
app->timer_view = flipp_pomodoro_view_timer_alloc();
app->info_view = flipp_pomodoro_info_view_alloc();
view_dispatcher_add_view(
app->view_dispatcher,
FlippPomodoroAppViewTimer,
flipp_pomodoro_view_timer_get_view(app->timer_view));
view_dispatcher_add_view(
app->view_dispatcher,
FlippPomodoroAppViewInfo,
flipp_pomodoro_info_view_get_view(app->info_view));
scene_manager_next_scene(app->scene_manager, FlippPomodoroSceneTimer);
FURI_LOG_I(TAG, "Alloc complete");
return app;
};
void flipp_pomodoro_app_free(FlippPomodoroApp* app) {
view_dispatcher_remove_view(app->view_dispatcher, FlippPomodoroAppViewTimer);
view_dispatcher_remove_view(app->view_dispatcher, FlippPomodoroAppViewInfo);
view_dispatcher_free(app->view_dispatcher);
scene_manager_free(app->scene_manager);
flipp_pomodoro_view_timer_free(app->timer_view);
flipp_pomodoro_info_view_free(app->info_view);
flipp_pomodoro_statistics__destroy(app->statistics);
flipp_pomodoro__destroy(app->state);
free(app);
furi_record_close(RECORD_GUI);
furi_record_close(RECORD_NOTIFICATION);
};
int32_t flipp_pomodoro_app(void* p) {
UNUSED(p);
FURI_LOG_I(TAG, "Initial");
FlippPomodoroApp* app = flipp_pomodoro_app_alloc();
FURI_LOG_I(TAG, "Run deed added");
dolphin_deed(DolphinDeedPluginGameStart);
view_dispatcher_run(app->view_dispatcher);
flipp_pomodoro_app_free(app);
return 0;
};

View File

@@ -1,39 +0,0 @@
#pragma once
#include <furi.h>
#include <furi_hal.h>
#include <gui/gui.h>
#include <gui/view_dispatcher.h>
#include <gui/scene_manager.h>
#include <notification/notification_messages.h>
#include "views/flipp_pomodoro_timer_view.h"
#include "views/flipp_pomodoro_info_view.h"
#include "modules/flipp_pomodoro.h"
#include "modules/flipp_pomodoro_statistics.h"
typedef enum {
// Reserve first 100 events for button types and indexes, starting from 0
FlippPomodoroAppCustomEventStageSkip = 100,
FlippPomodoroAppCustomEventStageComplete, // By Expiration
FlippPomodoroAppCustomEventTimerTick,
FlippPomodoroAppCustomEventTimerAskHint,
FlippPomodoroAppCustomEventStateUpdated,
FlippPomodoroAppCustomEventResumeTimer,
} FlippPomodoroAppCustomEvent;
typedef struct {
SceneManager* scene_manager;
ViewDispatcher* view_dispatcher;
Gui* gui;
NotificationApp* notification_app;
FlippPomodoroTimerView* timer_view;
FlippPomodoroInfoView* info_view;
FlippPomodoroState* state;
FlippPomodoroStatistics* statistics;
} FlippPomodoroApp;
typedef enum {
FlippPomodoroAppViewTimer,
FlippPomodoroAppViewInfo,
} FlippPomodoroAppView;

View File

@@ -1,32 +0,0 @@
#pragma once
// #define FURI_DEBUG 1
/**
* Index of dependencies for the main app
*/
// Platform Imports
#include <furi.h>
#include <furi_hal.h>
#include <gui/gui.h>
#include <gui/view_stack.h>
#include <gui/view_dispatcher.h>
#include <gui/scene_manager.h>
#include <gui/elements.h>
#include <dolphin/dolphin.h>
#include <input/input.h>
// App resource imports
#include "helpers/time.h"
#include "helpers/notifications.h"
#include "modules/flipp_pomodoro.h"
#include "flipp_pomodoro_app.h"
#include "scenes/flipp_pomodoro_scene.h"
#include "views/flipp_pomodoro_timer_view.h"
// Auto-compiled icons
#include "flipp_pomodoro_icons.h"
#include <assets_icons.h>

View File

@@ -1,5 +0,0 @@
#pragma once
#include <furi.h>
#define TAG "FlippPomodoro"

View File

@@ -1,49 +0,0 @@
#include <notification/notification_messages.h>
const NotificationSequence work_start_notification = {
&message_display_backlight_on,
&message_vibro_on,
&message_note_b5,
&message_delay_250,
&message_note_d5,
&message_delay_250,
&message_sound_off,
&message_vibro_off,
&message_green_255,
&message_delay_1000,
&message_green_0,
&message_delay_250,
&message_green_255,
&message_delay_1000,
NULL,
};
const NotificationSequence rest_start_notification = {
&message_display_backlight_on,
&message_vibro_on,
&message_note_d5,
&message_delay_250,
&message_note_b5,
&message_delay_250,
&message_sound_off,
&message_vibro_off,
&message_red_255,
&message_delay_1000,
&message_red_0,
&message_delay_250,
&message_red_255,
&message_delay_1000,
NULL,
};

View File

@@ -1,14 +0,0 @@
#pragma once
#include "../modules/flipp_pomodoro.h"
#include <notification/notification_messages.h>
extern const NotificationSequence work_start_notification;
extern const NotificationSequence rest_start_notification;
/// @brief Defines a notification sequence that should indicate start of specific pomodoro stage.
const NotificationSequence* stage_start_notification_sequence_map[] = {
[FlippPomodoroStageFocus] = &work_start_notification,
[FlippPomodoroStageRest] = &rest_start_notification,
[FlippPomodoroStageLongBreak] = &rest_start_notification,
};

View File

@@ -1,20 +0,0 @@
#include <furi.h>
#include <furi_hal.h>
#include "time.h"
const int TIME_SECONDS_IN_MINUTE = 60;
const int TIME_MINUTES_IN_HOUR = 60;
uint32_t time_now() {
return furi_hal_rtc_get_timestamp();
};
TimeDifference time_difference_seconds(uint32_t begin, uint32_t end) {
const uint32_t duration_seconds = end - begin;
uint32_t minutes = (duration_seconds / TIME_MINUTES_IN_HOUR) % TIME_MINUTES_IN_HOUR;
uint32_t seconds = duration_seconds % TIME_SECONDS_IN_MINUTE;
return (
TimeDifference){.total_seconds = duration_seconds, .minutes = minutes, .seconds = seconds};
};

View File

@@ -1,24 +0,0 @@
#pragma once
#include <furi.h>
#include <furi_hal.h>
extern const int TIME_SECONDS_IN_MINUTE;
extern const int TIME_MINUTES_IN_HOUR;
/// @brief Container for a time period
typedef struct {
uint8_t seconds;
uint8_t minutes;
uint32_t total_seconds;
} TimeDifference;
/// @brief Time by the moment of calling
/// @return A timestamp(seconds percision)
uint32_t time_now();
/// @brief Calculates difference between two provided timestamps
/// @param begin - start timestamp of the period
/// @param end - end timestamp of the period to measure
/// @return TimeDifference struct
TimeDifference time_difference_seconds(uint32_t begin, uint32_t end);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -1,94 +0,0 @@
#include <furi.h>
#include <furi_hal.h>
#include "../helpers/time.h"
#include "flipp_pomodoro.h"
PomodoroStage stages_sequence[] = {
FlippPomodoroStageFocus,
FlippPomodoroStageRest,
FlippPomodoroStageFocus,
FlippPomodoroStageRest,
FlippPomodoroStageFocus,
FlippPomodoroStageRest,
FlippPomodoroStageFocus,
FlippPomodoroStageLongBreak,
};
char* current_stage_label[] = {
[FlippPomodoroStageFocus] = "Focusing...",
[FlippPomodoroStageRest] = "Short Break...",
[FlippPomodoroStageLongBreak] = "Long Break...",
};
char* next_stage_label[] = {
[FlippPomodoroStageFocus] = "Focus",
[FlippPomodoroStageRest] = "Short Break",
[FlippPomodoroStageLongBreak] = "Long Break",
};
PomodoroStage flipp_pomodoro__stage_by_index(int index) {
const int one_loop_size = sizeof(stages_sequence);
return stages_sequence[index % one_loop_size];
}
void flipp_pomodoro__toggle_stage(FlippPomodoroState* state) {
furi_assert(state);
state->current_stage_index = state->current_stage_index + 1;
state->started_at_timestamp = time_now();
};
PomodoroStage flipp_pomodoro__get_stage(FlippPomodoroState* state) {
furi_assert(state);
return flipp_pomodoro__stage_by_index(state->current_stage_index);
};
char* flipp_pomodoro__current_stage_label(FlippPomodoroState* state) {
furi_assert(state);
return current_stage_label[flipp_pomodoro__get_stage(state)];
};
char* flipp_pomodoro__next_stage_label(FlippPomodoroState* state) {
furi_assert(state);
return next_stage_label[flipp_pomodoro__stage_by_index(state->current_stage_index + 1)];
};
void flipp_pomodoro__destroy(FlippPomodoroState* state) {
furi_assert(state);
free(state);
};
uint32_t flipp_pomodoro__current_stage_total_duration(FlippPomodoroState* state) {
const int32_t stage_duration_seconds_map[] = {
[FlippPomodoroStageFocus] = 25 * TIME_SECONDS_IN_MINUTE,
[FlippPomodoroStageRest] = 5 * TIME_SECONDS_IN_MINUTE,
[FlippPomodoroStageLongBreak] = 30 * TIME_SECONDS_IN_MINUTE,
};
return stage_duration_seconds_map[flipp_pomodoro__get_stage(state)];
};
uint32_t flipp_pomodoro__stage_expires_timestamp(FlippPomodoroState* state) {
return state->started_at_timestamp + flipp_pomodoro__current_stage_total_duration(state);
};
TimeDifference flipp_pomodoro__stage_remaining_duration(FlippPomodoroState* state) {
const uint32_t stage_ends_at = flipp_pomodoro__stage_expires_timestamp(state);
return time_difference_seconds(time_now(), stage_ends_at);
};
bool flipp_pomodoro__is_stage_expired(FlippPomodoroState* state) {
const uint32_t expired_by = flipp_pomodoro__stage_expires_timestamp(state);
const uint8_t seamless_change_span_seconds = 1;
return (time_now() - seamless_change_span_seconds) >= expired_by;
};
FlippPomodoroState* flipp_pomodoro__new() {
FlippPomodoroState* state = malloc(sizeof(FlippPomodoroState));
const uint32_t now = time_now();
state->started_at_timestamp = now;
state->current_stage_index = 0;
return state;
};

View File

@@ -1,53 +0,0 @@
#pragma once
#include <furi_hal.h>
#include "../helpers/time.h"
/// @brief Options of pomodoro stages
typedef enum {
FlippPomodoroStageFocus,
FlippPomodoroStageRest,
FlippPomodoroStageLongBreak,
} PomodoroStage;
/// @brief State of the pomodoro timer
typedef struct {
uint8_t current_stage_index;
uint32_t started_at_timestamp;
} FlippPomodoroState;
/// @brief Generates initial state
/// @returns A new pre-populated state for pomodoro timer
FlippPomodoroState* flipp_pomodoro__new();
/// @brief Extract current stage of pomodoro
/// @param state - pointer to the state of pomorodo
/// @returns Current stage value
PomodoroStage flipp_pomodoro__get_stage(FlippPomodoroState* state);
/// @brief Destroys state of timer and it's dependencies
void flipp_pomodoro__destroy(FlippPomodoroState* state);
/// @brief Get remaining stage time.
/// @param state - pointer to the state of pomorodo
/// @returns Time difference to the end of current stage
TimeDifference flipp_pomodoro__stage_remaining_duration(FlippPomodoroState* state);
/// @brief Label of currently active stage
/// @param state - pointer to the state of pomorodo
/// @returns A string that explains current stage
char* flipp_pomodoro__current_stage_label(FlippPomodoroState* state);
/// @brief Label of transition to the next stage
/// @param state - pointer to the state of pomorodo.
/// @returns string with the label of the "skipp" button
char* flipp_pomodoro__next_stage_label(FlippPomodoroState* state);
/// @brief Check if current stage is expired
/// @param state - pointer to the state of pomorodo.
/// @returns expriations status - true means stage is expired
bool flipp_pomodoro__is_stage_expired(FlippPomodoroState* state);
/// @brief Rotate stage of the timer
/// @param state - pointer to the state of pomorodo.
void flipp_pomodoro__toggle_stage(FlippPomodoroState* state);

View File

@@ -1,26 +0,0 @@
#include "flipp_pomodoro_statistics.h"
FlippPomodoroStatistics* flipp_pomodoro_statistics__new() {
FlippPomodoroStatistics* statistics = malloc(sizeof(FlippPomodoroStatistics));
statistics->focus_stages_completed = 0;
return statistics;
}
// Return the number of completed focus stages
uint8_t
flipp_pomodoro_statistics__get_focus_stages_completed(FlippPomodoroStatistics* statistics) {
return statistics->focus_stages_completed;
}
// Increase the number of completed focus stages by one
void flipp_pomodoro_statistics__increase_focus_stages_completed(
FlippPomodoroStatistics* statistics) {
statistics->focus_stages_completed++;
}
void flipp_pomodoro_statistics__destroy(FlippPomodoroStatistics* statistics) {
furi_assert(statistics);
free(statistics);
};

View File

@@ -1,45 +0,0 @@
#pragma once
#include <furi_hal.h>
/** @brief FlippPomodoroStatistics structure
*
* This structure is used to keep track of completed focus stages.
*/
typedef struct {
uint8_t focus_stages_completed;
} FlippPomodoroStatistics;
/** @brief Allocate and initialize a new FlippPomodoroStatistics
*
* This function allocates a new FlippPomodoroStatistics structure, initializes its members
* and returns a pointer to it.
*
* @return A pointer to a new FlippPomodoroStatistics structure
*/
FlippPomodoroStatistics* flipp_pomodoro_statistics__new();
/** @brief Get the number of completed focus stages
*
* This function retrieves the number of completed focus stages in a FlippPomodoroStatistics structure.
*
* @param statistics A pointer to a FlippPomodoroStatistics structure
* @return The number of completed focus stages
*/
uint8_t flipp_pomodoro_statistics__get_focus_stages_completed(FlippPomodoroStatistics* statistics);
/** @brief Increase the number of completed focus stages
*
* This function increases the count of the completed focus stages by one in a FlippPomodoroStatistics structure.
*
* @param statistics A pointer to a FlippPomodoroStatistics structure
*/
void flipp_pomodoro_statistics__increase_focus_stages_completed(
FlippPomodoroStatistics* statistics);
/** @brief Free a FlippPomodoroStatistics structure
*
* This function frees the memory used by a FlippPomodoroStatistics structure.
*
* @param statistics A pointer to a FlippPomodoroStatistics structure
*/
void flipp_pomodoro_statistics__destroy(FlippPomodoroStatistics* state);

View File

@@ -1,2 +0,0 @@
ADD_SCENE(flipp_pomodoro, info, Info)
ADD_SCENE(flipp_pomodoro, timer, Timer)

View File

@@ -1,30 +0,0 @@
#include "flipp_pomodoro_scene.h"
// Generate scene on_enter handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
void (*const flipp_pomodoro_scene_on_enter_handlers[])(void*) = {
#include "config/flipp_pomodoro_scene_config.h"
};
#undef ADD_SCENE
// Generate scene on_event handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,
bool (*const flipp_pomodoro_scene_on_event_handlers[])(void* ctx, SceneManagerEvent event) = {
#include "config/flipp_pomodoro_scene_config.h"
};
#undef ADD_SCENE
// Generate scene on_exit handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,
void (*const flipp_pomodoro_scene_on_exit_handlers[])(void* ctx) = {
#include "config/flipp_pomodoro_scene_config.h"
};
#undef ADD_SCENE
// Initialize scene handlers configuration structure
const SceneManagerHandlers flipp_pomodoro_scene_handlers = {
.on_enter_handlers = flipp_pomodoro_scene_on_enter_handlers,
.on_event_handlers = flipp_pomodoro_scene_on_event_handlers,
.on_exit_handlers = flipp_pomodoro_scene_on_exit_handlers,
.scene_num = FlippPomodoroSceneNum,
};

View File

@@ -1,28 +0,0 @@
#pragma once
#include <gui/scene_manager.h>
// Generate scene id and total number
#define ADD_SCENE(prefix, name, id) FlippPomodoroScene##id,
typedef enum {
#include "config/flipp_pomodoro_scene_config.h"
FlippPomodoroSceneNum,
} FlippPomodoroScene;
#undef ADD_SCENE
extern const SceneManagerHandlers flipp_pomodoro_scene_handlers;
// Generate scene on_enter handlers declaration
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);
#include "config/flipp_pomodoro_scene_config.h"
#undef ADD_SCENE
// Generate scene on_event handlers declaration
#define ADD_SCENE(prefix, name, id) \
bool prefix##_scene_##name##_on_event(void* ctx, SceneManagerEvent event);
#include "config/flipp_pomodoro_scene_config.h"
#undef ADD_SCENE
// Generate scene on_exit handlers declaration
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* ctx);
#include "config/flipp_pomodoro_scene_config.h"
#undef ADD_SCENE

View File

@@ -1,59 +0,0 @@
#include <furi.h>
#include <gui/view_dispatcher.h>
#include <gui/scene_manager.h>
#include "flipp_pomodoro_scene.h"
#include "../flipp_pomodoro_app.h"
#include "../views/flipp_pomodoro_info_view.h"
enum { SceneEventConusmed = true, SceneEventNotConusmed = false };
void flipp_pomodoro_scene_info_on_back_to_timer(void* ctx) {
furi_assert(ctx);
FlippPomodoroApp* app = ctx;
view_dispatcher_send_custom_event(
app->view_dispatcher, FlippPomodoroAppCustomEventResumeTimer);
};
void flipp_pomodoro_scene_info_on_enter(void* ctx) {
furi_assert(ctx);
FlippPomodoroApp* app = ctx;
view_dispatcher_switch_to_view(app->view_dispatcher, FlippPomodoroAppViewInfo);
flipp_pomodoro_info_view_set_pomodoros_completed(
flipp_pomodoro_info_view_get_view(app->info_view),
flipp_pomodoro_statistics__get_focus_stages_completed(app->statistics));
flipp_pomodoro_info_view_set_mode(
flipp_pomodoro_info_view_get_view(app->info_view), FlippPomodoroInfoViewModeStats);
flipp_pomodoro_info_view_set_resume_timer_cb(
app->info_view, flipp_pomodoro_scene_info_on_back_to_timer, app);
};
void flipp_pomodoro_scene_info_handle_custom_event(
FlippPomodoroApp* app,
FlippPomodoroAppCustomEvent custom_event) {
if(custom_event == FlippPomodoroAppCustomEventResumeTimer) {
scene_manager_next_scene(app->scene_manager, FlippPomodoroSceneTimer);
}
};
bool flipp_pomodoro_scene_info_on_event(void* ctx, SceneManagerEvent event) {
furi_assert(ctx);
FlippPomodoroApp* app = ctx;
switch(event.type) {
case SceneManagerEventTypeBack:
view_dispatcher_stop(app->view_dispatcher);
return SceneEventConusmed;
case SceneManagerEventTypeCustom:
flipp_pomodoro_scene_info_handle_custom_event(app, event.event);
return SceneEventConusmed;
default:
break;
};
return SceneEventNotConusmed;
};
void flipp_pomodoro_scene_info_on_exit(void* ctx) {
UNUSED(ctx);
};

View File

@@ -1,154 +0,0 @@
#include <furi.h>
#include <gui/scene_manager.h>
#include <gui/view_dispatcher.h>
#include <gui/scene_manager.h>
#include "flipp_pomodoro_scene.h"
#include "../flipp_pomodoro_app.h"
#include "../views/flipp_pomodoro_timer_view.h"
enum { SceneEventConusmed = true, SceneEventNotConusmed = false };
static char* work_hints[] = {
"Can you explain the problem as if I'm five?",
"Expected output vs. reality: what's the difference?",
"Ever thought of slicing the problem into bite-sized pieces?",
"What's the story when you walk through the code?",
"Any error messages gossiping about the issue?",
"What tricks have you tried to fix this?",
"Did you test the code, or just hoping for the best?",
"How's this code mingling with the rest of the app?",
"Any sneaky side effects causing mischief?",
"What are you assuming, and is it safe to do so?",
"Did you remember to invite all the edge cases to the party?",
"What happens in the isolation chamber (running code separately)?",
"Can you make the issue appear on command?",
"What's the scene at the crime spot when the error occurs?",
"Did you seek wisdom from the grand oracle (Google)?",
"What if you take a different path to solve this?",
"Did you take a coffee break to reboot your brain?"};
static char* break_hints[] = {
"Time to stretch! Remember, your body isn't made of code.",
"Hydrate or diedrate! Grab a glass of water.",
"Blink! Your eyes need a break too.",
"How about a quick dance-off with your shadow?",
"Ever tried chair yoga? Now's the time!",
"Time for a quick peek out the window. The outside world still exists!",
"Quick, think about kittens! Or puppies! Or baby turtles!",
"Time for a laugh. Look up a joke or two!",
"Sing a song. Bonus points for making up your own lyrics.",
"Do a quick tidy-up. A clean space is a happy space!",
"Time to play 'air' musical instrument for a minute.",
"How about a quick doodle? Unleash your inner Picasso!",
"Practice your superhero pose. Feel the power surge!",
"Quick, tell yourself a joke. Don't worry, I won't judge.",
"Time to practice your mime skills. Stuck in a box, anyone?",
"Ever tried juggling? Now's your chance!",
"Do a quick self high-five, you're doing great!"};
static char* random_string_of_list(char** hints, size_t num_hints) {
int random_index = rand() % num_hints;
return hints[random_index];
}
void flipp_pomodoro_scene_timer_sync_view_state(void* ctx) {
furi_assert(ctx);
FlippPomodoroApp* app = ctx;
flipp_pomodoro_view_timer_set_state(
flipp_pomodoro_view_timer_get_view(app->timer_view), app->state);
};
void flipp_pomodoro_scene_timer_on_next_stage(void* ctx) {
furi_assert(ctx);
FlippPomodoroApp* app = ctx;
view_dispatcher_send_custom_event(app->view_dispatcher, FlippPomodoroAppCustomEventStageSkip);
};
void flipp_pomodoro_scene_timer_on_ask_hint(void* ctx) {
FlippPomodoroApp* app = ctx;
view_dispatcher_send_custom_event(
app->view_dispatcher, FlippPomodoroAppCustomEventTimerAskHint);
}
void flipp_pomodoro_scene_timer_on_enter(void* ctx) {
furi_assert(ctx);
FlippPomodoroApp* app = ctx;
if(flipp_pomodoro__is_stage_expired(app->state)) {
flipp_pomodoro__destroy(app->state);
app->state = flipp_pomodoro__new();
}
view_dispatcher_switch_to_view(app->view_dispatcher, FlippPomodoroAppViewTimer);
flipp_pomodoro_scene_timer_sync_view_state(app);
flipp_pomodoro_view_timer_set_callback_context(app->timer_view, app);
flipp_pomodoro_view_timer_set_on_ok_cb(
app->timer_view, flipp_pomodoro_scene_timer_on_ask_hint);
flipp_pomodoro_view_timer_set_on_right_cb(
app->timer_view, flipp_pomodoro_scene_timer_on_next_stage);
};
char* flipp_pomodoro_scene_timer_get_contextual_hint(FlippPomodoroApp* app) {
switch(flipp_pomodoro__get_stage(app->state)) {
case FlippPomodoroStageFocus:
return random_string_of_list(work_hints, sizeof(work_hints) / sizeof(work_hints[0]));
case FlippPomodoroStageRest:
case FlippPomodoroStageLongBreak:
return random_string_of_list(break_hints, sizeof(break_hints) / sizeof(break_hints[0]));
default:
return "What's up?";
}
}
void flipp_pomodoro_scene_timer_handle_custom_event(
FlippPomodoroApp* app,
FlippPomodoroAppCustomEvent custom_event) {
switch(custom_event) {
case FlippPomodoroAppCustomEventTimerTick:
if(flipp_pomodoro__is_stage_expired(app->state)) {
view_dispatcher_send_custom_event(
app->view_dispatcher, FlippPomodoroAppCustomEventStageComplete);
}
break;
case FlippPomodoroAppCustomEventStateUpdated:
flipp_pomodoro_scene_timer_sync_view_state(app);
break;
case FlippPomodoroAppCustomEventTimerAskHint:
flipp_pomodoro_view_timer_display_hint(
flipp_pomodoro_view_timer_get_view(app->timer_view),
flipp_pomodoro_scene_timer_get_contextual_hint(app));
break;
default:
// optional: code to be executed if custom_event doesn't match any cases
break;
}
};
bool flipp_pomodoro_scene_timer_on_event(void* ctx, SceneManagerEvent event) {
furi_assert(ctx);
FlippPomodoroApp* app = ctx;
switch(event.type) {
case SceneManagerEventTypeCustom:
flipp_pomodoro_scene_timer_handle_custom_event(app, event.event);
return SceneEventConusmed;
case SceneManagerEventTypeBack:
scene_manager_next_scene(app->scene_manager, FlippPomodoroSceneInfo);
return SceneEventConusmed;
default:
break;
};
return SceneEventNotConusmed;
};
void flipp_pomodoro_scene_timer_on_exit(void* ctx) {
UNUSED(ctx);
};

View File

@@ -1,153 +0,0 @@
#include <furi.h>
#include <gui/gui.h>
#include <gui/elements.h>
#include <gui/view.h>
#include "flipp_pomodoro_info_view.h"
// Auto-compiled icons
#include "flipp_pomodoro_icons.h"
#include <assets_icons.h>
enum {
ViewInputConsumed = true,
ViewInputNotConusmed = false,
};
struct FlippPomodoroInfoView {
View* view;
FlippPomodoroInfoViewUserActionCb resume_timer_cb;
void* user_action_cb_ctx;
};
typedef struct {
uint8_t pomodoros_completed;
FlippPomodoroInfoViewMode mode;
} FlippPomodoroInfoViewModel;
static void
flipp_pomodoro_info_view_draw_statistics(Canvas* canvas, FlippPomodoroInfoViewModel* model) {
FuriString* stats_string = furi_string_alloc();
furi_string_printf(
stats_string,
"So Long,\nand Thanks for All the Focus...\nand for completing\n\e#%i\e# pomodoro(s)",
model->pomodoros_completed);
const char* stats_string_formatted = furi_string_get_cstr(stats_string);
elements_text_box(
canvas,
0,
0,
canvas_width(canvas),
canvas_height(canvas) - 10,
AlignCenter,
AlignCenter,
stats_string_formatted,
true);
furi_string_free(stats_string);
elements_button_left(canvas, "Guide");
}
static void
flipp_pomodoro_info_view_draw_about(Canvas* canvas, FlippPomodoroInfoViewModel* model) {
UNUSED(model);
canvas_draw_icon(canvas, 0, 0, &I_flipp_pomodoro_learn_50x128);
elements_button_left(canvas, "Stats");
}
static void flipp_pomodoro_info_view_draw_callback(Canvas* canvas, void* _model) {
if(!_model) {
return;
};
FlippPomodoroInfoViewModel* model = _model;
canvas_clear(canvas);
if(model->mode == FlippPomodoroInfoViewModeStats) {
flipp_pomodoro_info_view_draw_statistics(canvas, model);
} else {
flipp_pomodoro_info_view_draw_about(canvas, model);
}
elements_button_right(canvas, "Resume");
}
void flipp_pomodoro_info_view_set_mode(View* view, FlippPomodoroInfoViewMode desired_mode) {
with_view_model(
view, FlippPomodoroInfoViewModel * model, { model->mode = desired_mode; }, false);
}
void flipp_pomodoro_info_view_toggle_mode(FlippPomodoroInfoView* info_view) {
with_view_model(
flipp_pomodoro_info_view_get_view(info_view),
FlippPomodoroInfoViewModel * model,
{
flipp_pomodoro_info_view_set_mode(
flipp_pomodoro_info_view_get_view(info_view),
(model->mode == FlippPomodoroInfoViewModeStats) ? FlippPomodoroInfoViewModeAbout :
FlippPomodoroInfoViewModeStats);
},
true);
}
bool flipp_pomodoro_info_view_input_callback(InputEvent* event, void* ctx) {
FlippPomodoroInfoView* info_view = ctx;
if(event->type == InputTypePress) {
if(event->key == InputKeyRight && info_view->resume_timer_cb != NULL) {
info_view->resume_timer_cb(info_view->user_action_cb_ctx);
return ViewInputConsumed;
} else if(event->key == InputKeyLeft) {
flipp_pomodoro_info_view_toggle_mode(info_view);
return ViewInputConsumed;
}
}
return ViewInputNotConusmed;
}
FlippPomodoroInfoView* flipp_pomodoro_info_view_alloc() {
FlippPomodoroInfoView* info_view = malloc(sizeof(FlippPomodoroInfoView));
info_view->view = view_alloc();
view_allocate_model(
flipp_pomodoro_info_view_get_view(info_view),
ViewModelTypeLockFree,
sizeof(FlippPomodoroInfoViewModel));
view_set_context(flipp_pomodoro_info_view_get_view(info_view), info_view);
view_set_draw_callback(
flipp_pomodoro_info_view_get_view(info_view), flipp_pomodoro_info_view_draw_callback);
view_set_input_callback(
flipp_pomodoro_info_view_get_view(info_view), flipp_pomodoro_info_view_input_callback);
return info_view;
}
View* flipp_pomodoro_info_view_get_view(FlippPomodoroInfoView* info_view) {
return info_view->view;
}
void flipp_pomodoro_info_view_free(FlippPomodoroInfoView* info_view) {
furi_assert(info_view);
view_free(info_view->view);
free(info_view);
}
void flipp_pomodoro_info_view_set_pomodoros_completed(View* view, uint8_t pomodoros_completed) {
with_view_model(
view,
FlippPomodoroInfoViewModel * model,
{ model->pomodoros_completed = pomodoros_completed; },
false);
}
void flipp_pomodoro_info_view_set_resume_timer_cb(
FlippPomodoroInfoView* info_view,
FlippPomodoroInfoViewUserActionCb user_action_cb,
void* user_action_cb_ctx) {
info_view->resume_timer_cb = user_action_cb;
info_view->user_action_cb_ctx = user_action_cb_ctx;
}

View File

@@ -1,71 +0,0 @@
#pragma once
#include <gui/view.h>
/** @brief Mode types for FlippPomodoroInfoView
*
* These are the modes that can be used in the FlippPomodoroInfoView
*/
typedef enum {
FlippPomodoroInfoViewModeStats,
FlippPomodoroInfoViewModeAbout,
} FlippPomodoroInfoViewMode;
/** @brief Forward declaration of the FlippPomodoroInfoView struct */
typedef struct FlippPomodoroInfoView FlippPomodoroInfoView;
/** @brief User action callback function type
*
* Callback functions of this type are called when a user action is performed.
*/
typedef void (*FlippPomodoroInfoViewUserActionCb)(void* ctx);
/** @brief Allocate a new FlippPomodoroInfoView
*
* Allocates a new FlippPomodoroInfoView and returns a pointer to it.
* @return A pointer to a new FlippPomodoroInfoView
*/
FlippPomodoroInfoView* flipp_pomodoro_info_view_alloc();
/** @brief Get the view from a FlippPomodoroInfoView
*
* Returns a pointer to the view associated with a FlippPomodoroInfoView.
* @param info_view A pointer to a FlippPomodoroInfoView
* @return A pointer to the view of the FlippPomodoroInfoView
*/
View* flipp_pomodoro_info_view_get_view(FlippPomodoroInfoView* info_view);
/** @brief Free a FlippPomodoroInfoView
*
* Frees the memory used by a FlippPomodoroInfoView.
* @param info_view A pointer to a FlippPomodoroInfoView
*/
void flipp_pomodoro_info_view_free(FlippPomodoroInfoView* info_view);
/** @brief Set the number of completed pomodoros in the view
*
* Sets the number of completed pomodoros that should be displayed in the view.
* @param info_view A pointer to the view
* @param pomodoros_completed The number of completed pomodoros
*/
void flipp_pomodoro_info_view_set_pomodoros_completed(View* info_view, uint8_t pomodoros_completed);
/** @brief Set the callback function to be called when the timer should be resumed
*
* Sets the callback function that will be called when the timer should be resumed.
* @param info_view A pointer to the FlippPomodoroInfoView
* @param user_action_cb The callback function
* @param user_action_cb_ctx The context to be passed to the callback function
*/
void flipp_pomodoro_info_view_set_resume_timer_cb(
FlippPomodoroInfoView* info_view,
FlippPomodoroInfoViewUserActionCb user_action_cb,
void* user_action_cb_ctx);
/** @brief Set the mode of the view
*
* Sets the mode that should be used in the view.
* @param view A pointer to the view
* @param desired_mode The desired mode
*/
void flipp_pomodoro_info_view_set_mode(View* view, FlippPomodoroInfoViewMode desired_mode);

View File

@@ -1,294 +0,0 @@
#include "flipp_pomodoro_timer_view.h"
#include <furi.h>
#include <gui/gui.h>
#include <gui/elements.h>
#include <gui/view.h>
#include "../helpers/debug.h"
#include "../flipp_pomodoro_app.h"
#include "../modules/flipp_pomodoro.h"
// Auto-compiled icons
#include "flipp_pomodoro_icons.h"
#include <assets_icons.h>
enum {
ViewInputConsumed = true,
ViewInputNotConusmed = false,
};
struct FlippPomodoroTimerView {
View* view;
FlippPomodoroTimerViewInputCb right_cb;
FlippPomodoroTimerViewInputCb ok_cb;
void* callback_context;
};
typedef struct {
IconAnimation* icon;
FlippPomodoroState* state;
size_t scroll_counter;
char* current_hint;
} FlippPomodoroTimerViewModel;
static const Icon* stage_background_image[] = {
[FlippPomodoroStageFocus] = &A_flipp_pomodoro_focus_64,
[FlippPomodoroStageRest] = &A_flipp_pomodoro_rest_64,
[FlippPomodoroStageLongBreak] = &A_flipp_pomodoro_rest_64,
};
static void
flipp_pomodoro_view_timer_draw_countdown(Canvas* canvas, TimeDifference remaining_time) {
canvas_set_font(canvas, FontBigNumbers);
const uint8_t right_border_margin = 1;
const uint8_t countdown_box_height = canvas_height(canvas) * 0.4;
const uint8_t countdown_box_width = canvas_width(canvas) * 0.5;
const uint8_t countdown_box_x =
canvas_width(canvas) - countdown_box_width - right_border_margin;
const uint8_t countdown_box_y = 15;
elements_bold_rounded_frame(
canvas, countdown_box_x, countdown_box_y, countdown_box_width, countdown_box_height);
FuriString* timer_string = furi_string_alloc();
furi_string_printf(timer_string, "%02u:%02u", remaining_time.minutes, remaining_time.seconds);
const char* remaining_stage_time_string = furi_string_get_cstr(timer_string);
canvas_draw_str_aligned(
canvas,
countdown_box_x + (countdown_box_width / 2),
countdown_box_y + (countdown_box_height / 2),
AlignCenter,
AlignCenter,
remaining_stage_time_string);
furi_string_free(timer_string);
};
static void draw_str_with_drop_shadow(
Canvas* canvas,
uint8_t x,
uint8_t y,
Align horizontal,
Align vertical,
const char* str) {
canvas_set_color(canvas, ColorWhite);
for(int x_off = -2; x_off <= 2; x_off++) {
for(int y_off = -2; y_off <= 2; y_off++) {
canvas_draw_str_aligned(canvas, x + x_off, y + y_off, horizontal, vertical, str);
}
}
canvas_set_color(canvas, ColorBlack);
canvas_draw_str_aligned(canvas, x, y, horizontal, vertical, str);
}
static void
flipp_pomodoro_view_timer_draw_current_stage_label(Canvas* canvas, FlippPomodoroState* state) {
canvas_set_font(canvas, FontPrimary);
draw_str_with_drop_shadow(
canvas,
canvas_width(canvas),
0,
AlignRight,
AlignTop,
flipp_pomodoro__current_stage_label(state));
}
static void
flipp_pomodoro_view_timer_draw_hint(Canvas* canvas, FlippPomodoroTimerViewModel* model) {
size_t MAX_SCROLL_COUNTER = 300;
uint8_t SCROLL_DELAY_FRAMES = 3;
if(model->scroll_counter >= MAX_SCROLL_COUNTER || model->current_hint == NULL) {
return;
}
uint8_t hint_width = 90;
uint8_t hint_height = 18;
uint8_t hint_x = canvas_width(canvas) - hint_width - 6;
uint8_t hint_y = 35;
FuriString* displayed_hint_string = furi_string_alloc();
furi_string_printf(displayed_hint_string, "%s", model->current_hint);
size_t perfect_duration = furi_string_size(displayed_hint_string) * 1.5;
if(model->scroll_counter > perfect_duration) {
model->scroll_counter = MAX_SCROLL_COUNTER;
furi_string_free(displayed_hint_string);
return;
}
size_t scroll_offset = (model->scroll_counter < SCROLL_DELAY_FRAMES) ?
0 :
model->scroll_counter - SCROLL_DELAY_FRAMES;
canvas_set_color(canvas, ColorWhite);
canvas_draw_box(canvas, hint_x, hint_y, hint_width + 3, hint_height);
canvas_set_color(canvas, ColorBlack);
elements_bubble(canvas, hint_x, hint_y, hint_width, hint_height);
elements_scrollable_text_line(
canvas,
hint_x + 6,
hint_y + 12,
hint_width - 4,
displayed_hint_string,
scroll_offset,
true);
furi_string_free(displayed_hint_string);
model->scroll_counter++;
}
static void flipp_pomodoro_view_timer_draw_callback(Canvas* canvas, void* _model) {
if(!_model) {
return;
};
FlippPomodoroTimerViewModel* model = _model;
canvas_clear(canvas);
if(model->icon) {
canvas_draw_icon_animation(canvas, 0, 0, model->icon);
}
flipp_pomodoro_view_timer_draw_countdown(
canvas, flipp_pomodoro__stage_remaining_duration(model->state));
flipp_pomodoro_view_timer_draw_current_stage_label(canvas, model->state);
canvas_set_color(canvas, ColorBlack);
canvas_set_font(canvas, FontSecondary);
elements_button_right(canvas, flipp_pomodoro__next_stage_label(model->state));
flipp_pomodoro_view_timer_draw_hint(canvas, model);
};
bool flipp_pomodoro_view_timer_input_callback(InputEvent* event, void* ctx) {
furi_assert(ctx);
furi_assert(event);
FlippPomodoroTimerView* timer = ctx;
const bool is_press_event = event->type == InputTypePress;
if(!is_press_event) {
return ViewInputNotConusmed;
}
switch(event->key) {
case InputKeyRight:
timer->right_cb(timer->callback_context);
return ViewInputConsumed;
case InputKeyOk:
timer->ok_cb(timer->callback_context);
return ViewInputConsumed;
default:
return ViewInputNotConusmed;
}
};
View* flipp_pomodoro_view_timer_get_view(FlippPomodoroTimerView* timer) {
furi_assert(timer);
return timer->view;
};
void flipp_pomodoro_view_timer_display_hint(View* view, char* hint) {
with_view_model(
view,
FlippPomodoroTimerViewModel * model,
{
model->scroll_counter = 0;
model->current_hint = hint;
},
true);
}
void flipp_pomodoro_view_timer_assign_animation(View* view) {
with_view_model(
view,
FlippPomodoroTimerViewModel * model,
{
if(model->icon) {
icon_animation_free(model->icon);
}
model->icon = icon_animation_alloc(
stage_background_image[flipp_pomodoro__get_stage(model->state)]);
view_tie_icon_animation(view, model->icon);
icon_animation_start(model->icon);
},
true);
}
FlippPomodoroTimerView* flipp_pomodoro_view_timer_alloc() {
FlippPomodoroTimerView* timer = malloc(sizeof(FlippPomodoroTimerView));
timer->view = view_alloc();
view_allocate_model(
flipp_pomodoro_view_timer_get_view(timer),
ViewModelTypeLockFree,
sizeof(FlippPomodoroTimerViewModel));
view_set_context(flipp_pomodoro_view_timer_get_view(timer), timer);
view_set_draw_callback(timer->view, flipp_pomodoro_view_timer_draw_callback);
view_set_input_callback(timer->view, flipp_pomodoro_view_timer_input_callback);
with_view_model(
flipp_pomodoro_view_timer_get_view(timer),
FlippPomodoroTimerViewModel * model,
{ model->scroll_counter = 0; },
false);
return timer;
};
void flipp_pomodoro_view_timer_set_callback_context(
FlippPomodoroTimerView* timer,
void* callback_ctx) {
furi_assert(timer);
furi_assert(callback_ctx);
timer->callback_context = callback_ctx;
}
void flipp_pomodoro_view_timer_set_on_right_cb(
FlippPomodoroTimerView* timer,
FlippPomodoroTimerViewInputCb right_cb) {
furi_assert(timer);
furi_assert(right_cb);
timer->right_cb = right_cb;
};
void flipp_pomodoro_view_timer_set_on_ok_cb(
FlippPomodoroTimerView* timer,
FlippPomodoroTimerViewInputCb ok_kb) {
furi_assert(ok_kb);
furi_assert(timer);
timer->ok_cb = ok_kb;
}
void flipp_pomodoro_view_timer_set_state(View* view, FlippPomodoroState* state) {
furi_assert(view);
furi_assert(state);
with_view_model(
view,
FlippPomodoroTimerViewModel * model,
{
model->state = state;
model->current_hint = NULL;
},
false);
flipp_pomodoro_view_timer_assign_animation(view);
};
void flipp_pomodoro_view_timer_free(FlippPomodoroTimerView* timer) {
furi_assert(timer);
with_view_model(
timer->view,
FlippPomodoroTimerViewModel * model,
{ icon_animation_free(model->icon); },
false);
view_free(timer->view);
free(timer);
};

View File

@@ -1,30 +0,0 @@
#pragma once
#include <gui/view.h>
#include "../modules/flipp_pomodoro.h"
typedef struct FlippPomodoroTimerView FlippPomodoroTimerView;
typedef void (*FlippPomodoroTimerViewInputCb)(void* ctx);
FlippPomodoroTimerView* flipp_pomodoro_view_timer_alloc();
View* flipp_pomodoro_view_timer_get_view(FlippPomodoroTimerView* timer);
void flipp_pomodoro_view_timer_free(FlippPomodoroTimerView* timer);
void flipp_pomodoro_view_timer_set_state(View* view, FlippPomodoroState* state);
void flipp_pomodoro_view_timer_set_callback_context(
FlippPomodoroTimerView* timer,
void* callback_ctx);
void flipp_pomodoro_view_timer_set_on_right_cb(
FlippPomodoroTimerView* timer,
FlippPomodoroTimerViewInputCb right_cb);
void flipp_pomodoro_view_timer_set_on_ok_cb(
FlippPomodoroTimerView* timer,
FlippPomodoroTimerViewInputCb ok_cb);
void flipp_pomodoro_view_timer_display_hint(View* view, char* hint);

View File

@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2022 Bob Matcuk
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -1,19 +0,0 @@
App(
appid="qrcode",
name="QR Code",
fap_version=(1, 1),
fap_description="Display qrcodes",
fap_author="Bob Matcuk",
fap_weburl="https://github.com/bmatcuk/flipperzero-qrcode",
apptype=FlipperAppType.EXTERNAL,
entry_point="qrcode_app",
stack_size=2 * 1024,
cdefines=["APP_QRCODE"],
requires=[
"gui",
"dialogs",
],
fap_category="Tools",
fap_icon="icons/qrcode_10px.png",
fap_icon_assets="icons",
)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -1,967 +0,0 @@
/**
* The MIT License (MIT)
*
* This library is written and maintained by Richard Moore.
* Major parts were derived from Project Nayuki's library.
*
* Copyright (c) 2017 Richard Moore (https://github.com/ricmoo/QRCode)
* Copyright (c) 2017 Project Nayuki (https://www.nayuki.io/page/qr-code-generator-library)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/**
* Special thanks to Nayuki (https://www.nayuki.io/) from which this library was
* heavily inspired and compared against.
*
* See: https://github.com/nayuki/QR-Code-generator/tree/master/cpp
*/
#include "qrcode.h"
#include <stdlib.h>
#include <string.h>
#if LOCK_VERSION == 0
static const uint16_t NUM_ERROR_CORRECTION_CODEWORDS[4][40] = {
// 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level
{10, 16, 26, 36, 48, 64, 72, 88, 110, 130, 150, 176, 198, 216,
240, 280, 308, 338, 364, 416, 442, 476, 504, 560, 588, 644, 700, 728,
784, 812, 868, 924, 980, 1036, 1064, 1120, 1204, 1260, 1316, 1372}, // Medium
{7, 10, 15, 20, 26, 36, 40, 48, 60, 72, 80, 96, 104, 120,
132, 144, 168, 180, 196, 224, 224, 252, 270, 300, 312, 336, 360, 390,
420, 450, 480, 510, 540, 570, 570, 600, 630, 660, 720, 750}, // Low
{17, 28, 44, 64, 88, 112, 130, 156, 192, 224, 264, 308, 352, 384,
432, 480, 532, 588, 650, 700, 750, 816, 900, 960, 1050, 1110, 1200, 1260,
1350, 1440, 1530, 1620, 1710, 1800, 1890, 1980, 2100, 2220, 2310, 2430}, // High
{13, 22, 36, 52, 72, 96, 108, 132, 160, 192, 224, 260, 288, 320,
360, 408, 448, 504, 546, 600, 644, 690, 750, 810, 870, 952, 1020, 1050,
1140, 1200, 1290, 1350, 1440, 1530, 1590, 1680, 1770, 1860, 1950, 2040}, // Quartile
};
static const uint8_t NUM_ERROR_CORRECTION_BLOCKS[4][40] = {
// Version: (note that index 0 is for padding, and is set to an illegal value)
// 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level
{1, 1, 1, 2, 2, 4, 4, 4, 5, 5, 5, 8, 9, 9, 10, 10, 11, 13, 14, 16,
17, 17, 18, 20, 21, 23, 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49}, // Medium
{1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8,
8, 9, 9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25}, // Low
{1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25,
25, 34, 30, 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81}, // High
{1, 1, 2, 2, 4, 4, 6, 6, 8, 8, 8, 10, 12, 16, 12, 17, 16, 18, 21, 20,
23, 23, 25, 27, 29, 34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68}, // Quartile
};
static const uint16_t NUM_RAW_DATA_MODULES[40] = {
// 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
208,
359,
567,
807,
1079,
1383,
1568,
1936,
2336,
2768,
3232,
3728,
4256,
4651,
5243,
5867,
6523,
// 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
7211,
7931,
8683,
9252,
10068,
10916,
11796,
12708,
13652,
14628,
15371,
16411,
17483,
18587,
// 32, 33, 34, 35, 36, 37, 38, 39, 40
19723,
20891,
22091,
23008,
24272,
25568,
26896,
28256,
29648};
// @TODO: Put other LOCK_VERSIONS here
#elif LOCK_VERSION == 3
static const int16_t NUM_ERROR_CORRECTION_CODEWORDS[4] = {26, 15, 44, 36};
static const int8_t NUM_ERROR_CORRECTION_BLOCKS[4] = {1, 1, 2, 2};
static const uint16_t NUM_RAW_DATA_MODULES = 567;
#else
#error Unsupported LOCK_VERSION (add it...)
#endif
static int max(int a, int b) {
if(a > b) {
return a;
}
return b;
}
/*
static int abs(int value) {
if (value < 0) { return -value; }
return value;
}
*/
static int8_t getAlphanumeric(char c) {
if(c >= '0' && c <= '9') {
return (c - '0');
}
if(c >= 'A' && c <= 'Z') {
return (c - 'A' + 10);
}
switch(c) {
case ' ':
return 36;
case '$':
return 37;
case '%':
return 38;
case '*':
return 39;
case '+':
return 40;
case '-':
return 41;
case '.':
return 42;
case '/':
return 43;
case ':':
return 44;
}
return -1;
}
static bool isAlphanumeric(const char* text, uint16_t length) {
while(length != 0) {
if(getAlphanumeric(text[--length]) == -1) {
return false;
}
}
return true;
}
static bool isNumeric(const char* text, uint16_t length) {
while(length != 0) {
char c = text[--length];
if(c < '0' || c > '9') {
return false;
}
}
return true;
}
// We store the following tightly packed (less 8) in modeInfo
// <=9 <=26 <= 40
// NUMERIC ( 10, 12, 14);
// ALPHANUMERIC ( 9, 11, 13);
// BYTE ( 8, 16, 16);
static char getModeBits(uint8_t version, uint8_t mode) {
// Note: We use 15 instead of 16; since 15 doesn't exist and we cannot store 16 (8 + 8) in 3 bits
// hex(int("".join(reversed([('00' + bin(x - 8)[2:])[-3:] for x in [10, 9, 8, 12, 11, 15, 14, 13, 15]])), 2))
unsigned int modeInfo = 0x7bbb80a;
#if LOCK_VERSION == 0 || LOCK_VERSION > 9
if(version > 9) {
modeInfo >>= 9;
}
#endif
#if LOCK_VERSION == 0 || LOCK_VERSION > 26
if(version > 26) {
modeInfo >>= 9;
}
#endif
char result = 8 + ((modeInfo >> (3 * mode)) & 0x07);
if(result == 15) {
result = 16;
}
return result;
}
typedef struct BitBucket {
uint32_t bitOffsetOrWidth;
uint16_t capacityBytes;
uint8_t* data;
} BitBucket;
/*
void bb_dump(BitBucket *bitBuffer) {
printf("Buffer: ");
for (uint32_t i = 0; i < bitBuffer->capacityBytes; i++) {
printf("%02x", bitBuffer->data[i]);
if ((i % 4) == 3) { printf(" "); }
}
printf("\n");
}
*/
static uint16_t bb_getGridSizeBytes(uint8_t size) {
return (((size * size) + 7) / 8);
}
static uint16_t bb_getBufferSizeBytes(uint32_t bits) {
return ((bits + 7) / 8);
}
static void bb_initBuffer(BitBucket* bitBuffer, uint8_t* data, int32_t capacityBytes) {
bitBuffer->bitOffsetOrWidth = 0;
bitBuffer->capacityBytes = capacityBytes;
bitBuffer->data = data;
memset(data, 0, bitBuffer->capacityBytes);
}
static void bb_initGrid(BitBucket* bitGrid, uint8_t* data, uint8_t size) {
bitGrid->bitOffsetOrWidth = size;
bitGrid->capacityBytes = bb_getGridSizeBytes(size);
bitGrid->data = data;
memset(data, 0, bitGrid->capacityBytes);
}
static void bb_appendBits(BitBucket* bitBuffer, uint32_t val, uint8_t length) {
uint32_t offset = bitBuffer->bitOffsetOrWidth;
for(int8_t i = length - 1; i >= 0; i--, offset++) {
bitBuffer->data[offset >> 3] |= ((val >> i) & 1) << (7 - (offset & 7));
}
bitBuffer->bitOffsetOrWidth = offset;
}
/*
void bb_setBits(BitBucket *bitBuffer, uint32_t val, int offset, uint8_t length) {
for (int8_t i = length - 1; i >= 0; i--, offset++) {
bitBuffer->data[offset >> 3] |= ((val >> i) & 1) << (7 - (offset & 7));
}
}
*/
static void bb_setBit(BitBucket* bitGrid, uint8_t x, uint8_t y, bool on) {
uint32_t offset = y * bitGrid->bitOffsetOrWidth + x;
uint8_t mask = 1 << (7 - (offset & 0x07));
if(on) {
bitGrid->data[offset >> 3] |= mask;
} else {
bitGrid->data[offset >> 3] &= ~mask;
}
}
static void bb_invertBit(BitBucket* bitGrid, uint8_t x, uint8_t y, bool invert) {
uint32_t offset = y * bitGrid->bitOffsetOrWidth + x;
uint8_t mask = 1 << (7 - (offset & 0x07));
bool on = ((bitGrid->data[offset >> 3] & (1 << (7 - (offset & 0x07)))) != 0);
if(on ^ invert) {
bitGrid->data[offset >> 3] |= mask;
} else {
bitGrid->data[offset >> 3] &= ~mask;
}
}
static bool bb_getBit(BitBucket* bitGrid, uint8_t x, uint8_t y) {
uint32_t offset = y * bitGrid->bitOffsetOrWidth + x;
return (bitGrid->data[offset >> 3] & (1 << (7 - (offset & 0x07)))) != 0;
}
// XORs the data modules in this QR Code with the given mask pattern. Due to XOR's mathematical
// properties, calling applyMask(m) twice with the same value is equivalent to no change at all.
// This means it is possible to apply a mask, undo it, and try another mask. Note that a final
// well-formed QR Code symbol needs exactly one mask applied (not zero, not two, etc.).
static void applyMask(BitBucket* modules, BitBucket* isFunction, uint8_t mask) {
uint8_t size = modules->bitOffsetOrWidth;
for(uint8_t y = 0; y < size; y++) {
for(uint8_t x = 0; x < size; x++) {
if(bb_getBit(isFunction, x, y)) {
continue;
}
bool invert = 0;
switch(mask) {
case 0:
invert = (x + y) % 2 == 0;
break;
case 1:
invert = y % 2 == 0;
break;
case 2:
invert = x % 3 == 0;
break;
case 3:
invert = (x + y) % 3 == 0;
break;
case 4:
invert = (x / 3 + y / 2) % 2 == 0;
break;
case 5:
invert = x * y % 2 + x * y % 3 == 0;
break;
case 6:
invert = (x * y % 2 + x * y % 3) % 2 == 0;
break;
case 7:
invert = ((x + y) % 2 + x * y % 3) % 2 == 0;
break;
}
bb_invertBit(modules, x, y, invert);
}
}
}
static void
setFunctionModule(BitBucket* modules, BitBucket* isFunction, uint8_t x, uint8_t y, bool on) {
bb_setBit(modules, x, y, on);
bb_setBit(isFunction, x, y, true);
}
// Draws a 9*9 finder pattern including the border separator, with the center module at (x, y).
static void drawFinderPattern(BitBucket* modules, BitBucket* isFunction, uint8_t x, uint8_t y) {
uint8_t size = modules->bitOffsetOrWidth;
for(int8_t i = -4; i <= 4; i++) {
for(int8_t j = -4; j <= 4; j++) {
uint8_t dist = max(abs(i), abs(j)); // Chebyshev/infinity norm
int16_t xx = x + j, yy = y + i;
if(0 <= xx && xx < size && 0 <= yy && yy < size) {
setFunctionModule(modules, isFunction, xx, yy, dist != 2 && dist != 4);
}
}
}
}
// Draws a 5*5 alignment pattern, with the center module at (x, y).
static void drawAlignmentPattern(BitBucket* modules, BitBucket* isFunction, uint8_t x, uint8_t y) {
for(int8_t i = -2; i <= 2; i++) {
for(int8_t j = -2; j <= 2; j++) {
setFunctionModule(modules, isFunction, x + j, y + i, max(abs(i), abs(j)) != 1);
}
}
}
// Draws two copies of the format bits (with its own error correction code)
// based on the given mask and this object's error correction level field.
static void drawFormatBits(BitBucket* modules, BitBucket* isFunction, uint8_t ecc, uint8_t mask) {
uint8_t size = modules->bitOffsetOrWidth;
// Calculate error correction code and pack bits
uint32_t data = ecc << 3 | mask; // errCorrLvl is uint2, mask is uint3
uint32_t rem = data;
for(int i = 0; i < 10; i++) {
rem = (rem << 1) ^ ((rem >> 9) * 0x537);
}
data = data << 10 | rem;
data ^= 0x5412; // uint15
// Draw first copy
for(uint8_t i = 0; i <= 5; i++) {
setFunctionModule(modules, isFunction, 8, i, ((data >> i) & 1) != 0);
}
setFunctionModule(modules, isFunction, 8, 7, ((data >> 6) & 1) != 0);
setFunctionModule(modules, isFunction, 8, 8, ((data >> 7) & 1) != 0);
setFunctionModule(modules, isFunction, 7, 8, ((data >> 8) & 1) != 0);
for(int8_t i = 9; i < 15; i++) {
setFunctionModule(modules, isFunction, 14 - i, 8, ((data >> i) & 1) != 0);
}
// Draw second copy
for(int8_t i = 0; i <= 7; i++) {
setFunctionModule(modules, isFunction, size - 1 - i, 8, ((data >> i) & 1) != 0);
}
for(int8_t i = 8; i < 15; i++) {
setFunctionModule(modules, isFunction, 8, size - 15 + i, ((data >> i) & 1) != 0);
}
setFunctionModule(modules, isFunction, 8, size - 8, true);
}
// Draws two copies of the version bits (with its own error correction code),
// based on this object's version field (which only has an effect for 7 <= version <= 40).
static void drawVersion(BitBucket* modules, BitBucket* isFunction, uint8_t version) {
int8_t size = modules->bitOffsetOrWidth;
#if LOCK_VERSION != 0 && LOCK_VERSION < 7
return;
#else
if(version < 7) {
return;
}
// Calculate error correction code and pack bits
uint32_t rem = version; // version is uint6, in the range [7, 40]
for(uint8_t i = 0; i < 12; i++) {
rem = (rem << 1) ^ ((rem >> 11) * 0x1F25);
}
uint32_t data = version << 12 | rem; // uint18
// Draw two copies
for(uint8_t i = 0; i < 18; i++) {
bool bit = ((data >> i) & 1) != 0;
uint8_t a = size - 11 + i % 3, b = i / 3;
setFunctionModule(modules, isFunction, a, b, bit);
setFunctionModule(modules, isFunction, b, a, bit);
}
#endif
}
static void
drawFunctionPatterns(BitBucket* modules, BitBucket* isFunction, uint8_t version, uint8_t ecc) {
uint8_t size = modules->bitOffsetOrWidth;
// Draw the horizontal and vertical timing patterns
for(uint8_t i = 0; i < size; i++) {
setFunctionModule(modules, isFunction, 6, i, i % 2 == 0);
setFunctionModule(modules, isFunction, i, 6, i % 2 == 0);
}
// Draw 3 finder patterns (all corners except bottom right; overwrites some timing modules)
drawFinderPattern(modules, isFunction, 3, 3);
drawFinderPattern(modules, isFunction, size - 4, 3);
drawFinderPattern(modules, isFunction, 3, size - 4);
#if LOCK_VERSION == 0 || LOCK_VERSION > 1
if(version > 1) {
// Draw the numerous alignment patterns
uint8_t alignCount = version / 7 + 2;
uint8_t step;
if(version != 32) {
step = (version * 4 + alignCount * 2 + 1) / (2 * alignCount - 2) *
2; // ceil((size - 13) / (2*numAlign - 2)) * 2
} else { // C-C-C-Combo breaker!
step = 26;
}
uint8_t alignPositionIndex = alignCount - 1;
uint8_t alignPosition[alignCount];
alignPosition[0] = 6;
uint8_t size = version * 4 + 17;
for(uint8_t i = 0, pos = size - 7; i < alignCount - 1; i++, pos -= step) {
alignPosition[alignPositionIndex--] = pos;
}
for(uint8_t i = 0; i < alignCount; i++) {
for(uint8_t j = 0; j < alignCount; j++) {
if((i == 0 && j == 0) || (i == 0 && j == alignCount - 1) ||
(i == alignCount - 1 && j == 0)) {
continue; // Skip the three finder corners
} else {
drawAlignmentPattern(modules, isFunction, alignPosition[i], alignPosition[j]);
}
}
}
}
#endif
// Draw configuration data
drawFormatBits(
modules, isFunction, ecc, 0); // Dummy mask value; overwritten later in the constructor
drawVersion(modules, isFunction, version);
}
// Draws the given sequence of 8-bit codewords (data and error correction) onto the entire
// data area of this QR Code symbol. Function modules need to be marked off before this is called.
static void drawCodewords(BitBucket* modules, BitBucket* isFunction, BitBucket* codewords) {
uint32_t bitLength = codewords->bitOffsetOrWidth;
uint8_t* data = codewords->data;
uint8_t size = modules->bitOffsetOrWidth;
// Bit index into the data
uint32_t i = 0;
// Do the funny zigzag scan
for(int16_t right = size - 1; right >= 1;
right -= 2) { // Index of right column in each column pair
if(right == 6) {
right = 5;
}
for(uint8_t vert = 0; vert < size; vert++) { // Vertical counter
for(int j = 0; j < 2; j++) {
uint8_t x = right - j; // Actual x coordinate
bool upwards = ((right & 2) == 0) ^ (x < 6);
uint8_t y = upwards ? size - 1 - vert : vert; // Actual y coordinate
if(!bb_getBit(isFunction, x, y) && i < bitLength) {
bb_setBit(modules, x, y, ((data[i >> 3] >> (7 - (i & 7))) & 1) != 0);
i++;
}
// If there are any remainder bits (0 to 7), they are already
// set to 0/false/white when the grid of modules was initialized
}
}
}
}
#define PENALTY_N1 3
#define PENALTY_N2 3
#define PENALTY_N3 40
#define PENALTY_N4 10
// Calculates and returns the penalty score based on state of this QR Code's current modules.
// This is used by the automatic mask choice algorithm to find the mask pattern that yields the lowest score.
// @TODO: This can be optimized by working with the bytes instead of bits.
static uint32_t getPenaltyScore(BitBucket* modules) {
uint32_t result = 0;
uint8_t size = modules->bitOffsetOrWidth;
// Adjacent modules in row having same color
for(uint8_t y = 0; y < size; y++) {
bool colorX = bb_getBit(modules, 0, y);
for(uint8_t x = 1, runX = 1; x < size; x++) {
bool cx = bb_getBit(modules, x, y);
if(cx != colorX) {
colorX = cx;
runX = 1;
} else {
runX++;
if(runX == 5) {
result += PENALTY_N1;
} else if(runX > 5) {
result++;
}
}
}
}
// Adjacent modules in column having same color
for(uint8_t x = 0; x < size; x++) {
bool colorY = bb_getBit(modules, x, 0);
for(uint8_t y = 1, runY = 1; y < size; y++) {
bool cy = bb_getBit(modules, x, y);
if(cy != colorY) {
colorY = cy;
runY = 1;
} else {
runY++;
if(runY == 5) {
result += PENALTY_N1;
} else if(runY > 5) {
result++;
}
}
}
}
uint16_t black = 0;
for(uint8_t y = 0; y < size; y++) {
uint16_t bitsRow = 0, bitsCol = 0;
for(uint8_t x = 0; x < size; x++) {
bool color = bb_getBit(modules, x, y);
// 2*2 blocks of modules having same color
if(x > 0 && y > 0) {
bool colorUL = bb_getBit(modules, x - 1, y - 1);
bool colorUR = bb_getBit(modules, x, y - 1);
bool colorL = bb_getBit(modules, x - 1, y);
if(color == colorUL && color == colorUR && color == colorL) {
result += PENALTY_N2;
}
}
// Finder-like pattern in rows and columns
bitsRow = ((bitsRow << 1) & 0x7FF) | color;
bitsCol = ((bitsCol << 1) & 0x7FF) | bb_getBit(modules, y, x);
// Needs 11 bits accumulated
if(x >= 10) {
if(bitsRow == 0x05D || bitsRow == 0x5D0) {
result += PENALTY_N3;
}
if(bitsCol == 0x05D || bitsCol == 0x5D0) {
result += PENALTY_N3;
}
}
// Balance of black and white modules
if(color) {
black++;
}
}
}
// Find smallest k such that (45-5k)% <= dark/total <= (55+5k)%
uint16_t total = size * size;
for(uint16_t k = 0; black * 20 < (9 - k) * total || black * 20 > (11 + k) * total; k++) {
result += PENALTY_N4;
}
return result;
}
static uint8_t rs_multiply(uint8_t x, uint8_t y) {
// Russian peasant multiplication
// See: https://en.wikipedia.org/wiki/Ancient_Egyptian_multiplication
uint16_t z = 0;
for(int8_t i = 7; i >= 0; i--) {
z = (z << 1) ^ ((z >> 7) * 0x11D);
z ^= ((y >> i) & 1) * x;
}
return z;
}
static void rs_init(uint8_t degree, uint8_t* coeff) {
memset(coeff, 0, degree);
coeff[degree - 1] = 1;
// Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}),
// drop the highest term, and store the rest of the coefficients in order of descending powers.
// Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D).
uint16_t root = 1;
for(uint8_t i = 0; i < degree; i++) {
// Multiply the current product by (x - r^i)
for(uint8_t j = 0; j < degree; j++) {
coeff[j] = rs_multiply(coeff[j], root);
if(j + 1 < degree) {
coeff[j] ^= coeff[j + 1];
}
}
root = (root << 1) ^ ((root >> 7) * 0x11D); // Multiply by 0x02 mod GF(2^8/0x11D)
}
}
static void rs_getRemainder(
uint8_t degree,
uint8_t* coeff,
uint8_t* data,
uint8_t length,
uint8_t* result,
uint8_t stride) {
// Compute the remainder by performing polynomial division
//for (uint8_t i = 0; i < degree; i++) { result[] = 0; }
//memset(result, 0, degree);
for(uint8_t i = 0; i < length; i++) {
uint8_t factor = data[i] ^ result[0];
for(uint8_t j = 1; j < degree; j++) {
result[(j - 1) * stride] = result[j * stride];
}
result[(degree - 1) * stride] = 0;
for(uint8_t j = 0; j < degree; j++) {
result[j * stride] ^= rs_multiply(coeff[j], factor);
}
}
}
static int8_t encodeDataCodewords(
BitBucket* dataCodewords,
const uint8_t* text,
uint16_t length,
uint8_t version) {
int8_t mode = MODE_BYTE;
if(isNumeric((char*)text, length)) {
mode = MODE_NUMERIC;
bb_appendBits(dataCodewords, 1 << MODE_NUMERIC, 4);
bb_appendBits(dataCodewords, length, getModeBits(version, MODE_NUMERIC));
uint16_t accumData = 0;
uint8_t accumCount = 0;
for(uint16_t i = 0; i < length; i++) {
accumData = accumData * 10 + ((char)(text[i]) - '0');
accumCount++;
if(accumCount == 3) {
bb_appendBits(dataCodewords, accumData, 10);
accumData = 0;
accumCount = 0;
}
}
// 1 or 2 digits remaining
if(accumCount > 0) {
bb_appendBits(dataCodewords, accumData, accumCount * 3 + 1);
}
} else if(isAlphanumeric((char*)text, length)) {
mode = MODE_ALPHANUMERIC;
bb_appendBits(dataCodewords, 1 << MODE_ALPHANUMERIC, 4);
bb_appendBits(dataCodewords, length, getModeBits(version, MODE_ALPHANUMERIC));
uint16_t accumData = 0;
uint8_t accumCount = 0;
for(uint16_t i = 0; i < length; i++) {
accumData = accumData * 45 + getAlphanumeric((char)(text[i]));
accumCount++;
if(accumCount == 2) {
bb_appendBits(dataCodewords, accumData, 11);
accumData = 0;
accumCount = 0;
}
}
// 1 character remaining
if(accumCount > 0) {
bb_appendBits(dataCodewords, accumData, 6);
}
} else {
bb_appendBits(dataCodewords, 1 << MODE_BYTE, 4);
bb_appendBits(dataCodewords, length, getModeBits(version, MODE_BYTE));
for(uint16_t i = 0; i < length; i++) {
bb_appendBits(dataCodewords, (char)(text[i]), 8);
}
}
//bb_setBits(dataCodewords, length, 4, getModeBits(version, mode));
return mode;
}
static void performErrorCorrection(uint8_t version, uint8_t ecc, BitBucket* data) {
// See: http://www.thonky.com/qr-code-tutorial/structure-final-message
#if LOCK_VERSION == 0
uint8_t numBlocks = NUM_ERROR_CORRECTION_BLOCKS[ecc][version - 1];
uint16_t totalEcc = NUM_ERROR_CORRECTION_CODEWORDS[ecc][version - 1];
uint16_t moduleCount = NUM_RAW_DATA_MODULES[version - 1];
#else
uint8_t numBlocks = NUM_ERROR_CORRECTION_BLOCKS[ecc];
uint16_t totalEcc = NUM_ERROR_CORRECTION_CODEWORDS[ecc];
uint16_t moduleCount = NUM_RAW_DATA_MODULES;
#endif
uint8_t blockEccLen = totalEcc / numBlocks;
uint8_t numShortBlocks = numBlocks - moduleCount / 8 % numBlocks;
uint8_t shortBlockLen = moduleCount / 8 / numBlocks;
uint8_t shortDataBlockLen = shortBlockLen - blockEccLen;
uint8_t result[data->capacityBytes];
memset(result, 0, sizeof(result));
uint8_t coeff[blockEccLen];
rs_init(blockEccLen, coeff);
uint16_t offset = 0;
uint8_t* dataBytes = data->data;
// Interleave all short blocks
for(uint8_t i = 0; i < shortDataBlockLen; i++) {
uint16_t index = i;
uint8_t stride = shortDataBlockLen;
for(uint8_t blockNum = 0; blockNum < numBlocks; blockNum++) {
result[offset++] = dataBytes[index];
#if LOCK_VERSION == 0 || LOCK_VERSION >= 5
if(blockNum == numShortBlocks) {
stride++;
}
#endif
index += stride;
}
}
// Version less than 5 only have short blocks
#if LOCK_VERSION == 0 || LOCK_VERSION >= 5
{
// Interleave long blocks
uint16_t index = shortDataBlockLen * (numShortBlocks + 1);
uint8_t stride = shortDataBlockLen;
for(uint8_t blockNum = 0; blockNum < numBlocks - numShortBlocks; blockNum++) {
result[offset++] = dataBytes[index];
if(blockNum == 0) {
stride++;
}
index += stride;
}
}
#endif
// Add all ecc blocks, interleaved
uint8_t blockSize = shortDataBlockLen;
for(uint8_t blockNum = 0; blockNum < numBlocks; blockNum++) {
#if LOCK_VERSION == 0 || LOCK_VERSION >= 5
if(blockNum == numShortBlocks) {
blockSize++;
}
#endif
rs_getRemainder(
blockEccLen, coeff, dataBytes, blockSize, &result[offset + blockNum], numBlocks);
dataBytes += blockSize;
}
memcpy(data->data, result, data->capacityBytes);
data->bitOffsetOrWidth = moduleCount;
}
// We store the Format bits tightly packed into a single byte (each of the 4 modes is 2 bits)
// The format bits can be determined by ECC_FORMAT_BITS >> (2 * ecc)
static const uint8_t ECC_FORMAT_BITS = (0x02 << 6) | (0x03 << 4) | (0x00 << 2) | (0x01 << 0);
uint16_t qrcode_getBufferSize(uint8_t version) {
return bb_getGridSizeBytes(4 * version + 17);
}
// @TODO: Return error if data is too big.
int8_t qrcode_initBytes(
QRCode* qrcode,
uint8_t* modules,
uint8_t version,
uint8_t ecc,
uint8_t* data,
uint16_t length) {
uint8_t size = version * 4 + 17;
qrcode->version = version;
qrcode->size = size;
qrcode->ecc = ecc;
qrcode->modules = modules;
uint8_t eccFormatBits = (ECC_FORMAT_BITS >> (2 * ecc)) & 0x03;
#if LOCK_VERSION == 0
uint16_t moduleCount = NUM_RAW_DATA_MODULES[version - 1];
uint16_t dataCapacity =
moduleCount / 8 - NUM_ERROR_CORRECTION_CODEWORDS[eccFormatBits][version - 1];
#else
version = LOCK_VERSION;
uint16_t moduleCount = NUM_RAW_DATA_MODULES;
uint16_t dataCapacity = moduleCount / 8 - NUM_ERROR_CORRECTION_CODEWORDS[eccFormatBits];
#endif
struct BitBucket codewords;
uint8_t codewordBytes[bb_getBufferSizeBytes(moduleCount)];
bb_initBuffer(&codewords, codewordBytes, (int32_t)sizeof(codewordBytes));
// Place the data code words into the buffer
int8_t mode = encodeDataCodewords(&codewords, data, length, version);
if(mode < 0) {
return -1;
}
qrcode->mode = mode;
// Add terminator and pad up to a byte if applicable
uint32_t padding = (dataCapacity * 8) - codewords.bitOffsetOrWidth;
if(padding > 4) {
padding = 4;
}
bb_appendBits(&codewords, 0, padding);
bb_appendBits(&codewords, 0, (8 - codewords.bitOffsetOrWidth % 8) % 8);
// Pad with alternate bytes until data capacity is reached
for(uint8_t padByte = 0xEC; codewords.bitOffsetOrWidth < (dataCapacity * 8);
padByte ^= 0xEC ^ 0x11) {
bb_appendBits(&codewords, padByte, 8);
}
BitBucket modulesGrid;
bb_initGrid(&modulesGrid, modules, size);
BitBucket isFunctionGrid;
uint8_t isFunctionGridBytes[bb_getGridSizeBytes(size)];
bb_initGrid(&isFunctionGrid, isFunctionGridBytes, size);
// Draw function patterns, draw all codewords, do masking
drawFunctionPatterns(&modulesGrid, &isFunctionGrid, version, eccFormatBits);
performErrorCorrection(version, eccFormatBits, &codewords);
drawCodewords(&modulesGrid, &isFunctionGrid, &codewords);
// Find the best (lowest penalty) mask
uint8_t mask = 0;
int32_t minPenalty = INT32_MAX;
for(uint8_t i = 0; i < 8; i++) {
drawFormatBits(&modulesGrid, &isFunctionGrid, eccFormatBits, i);
applyMask(&modulesGrid, &isFunctionGrid, i);
int penalty = getPenaltyScore(&modulesGrid);
if(penalty < minPenalty) {
mask = i;
minPenalty = penalty;
}
applyMask(&modulesGrid, &isFunctionGrid, i); // Undoes the mask due to XOR
}
qrcode->mask = mask;
// Overwrite old format bits
drawFormatBits(&modulesGrid, &isFunctionGrid, eccFormatBits, mask);
// Apply the final choice of mask
applyMask(&modulesGrid, &isFunctionGrid, mask);
return 0;
}
int8_t qrcode_initText(
QRCode* qrcode,
uint8_t* modules,
uint8_t version,
uint8_t ecc,
const char* data) {
return qrcode_initBytes(qrcode, modules, version, ecc, (uint8_t*)data, strlen(data));
}
bool qrcode_getModule(QRCode* qrcode, uint8_t x, uint8_t y) {
if(x >= qrcode->size || y >= qrcode->size) {
return false;
}
uint32_t offset = y * qrcode->size + x;
return (qrcode->modules[offset >> 3] & (1 << (7 - (offset & 0x07)))) != 0;
}
/*
uint8_t qrcode_getHexLength(QRCode *qrcode) {
return ((qrcode->size * qrcode->size) + 7) / 4;
}
void qrcode_getHex(QRCode *qrcode, char *result) {
}
*/

View File

@@ -1,100 +0,0 @@
/**
* The MIT License (MIT)
*
* This library is written and maintained by Richard Moore.
* Major parts were derived from Project Nayuki's library.
*
* Copyright (c) 2017 Richard Moore (https://github.com/ricmoo/QRCode)
* Copyright (c) 2017 Project Nayuki (https://www.nayuki.io/page/qr-code-generator-library)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/**
* Special thanks to Nayuki (https://www.nayuki.io/) from which this library was
* heavily inspired and compared against.
*
* See: https://github.com/nayuki/QR-Code-generator/tree/master/cpp
*/
#ifndef __QRCODE_H_
#define __QRCODE_H_
// #ifndef __cplusplus
// typedef unsigned char bool;
// static const bool false = 0;
// static const bool true = 1;
// #endif
#include <stdbool.h>
#include <stdint.h>
// QR Code Format Encoding
#define MODE_NUMERIC 0
#define MODE_ALPHANUMERIC 1
#define MODE_BYTE 2
// Error Correction Code Levels
#define ECC_LOW 0
#define ECC_MEDIUM 1
#define ECC_QUARTILE 2
#define ECC_HIGH 3
// If set to non-zero, this library can ONLY produce QR codes at that version
// This saves a lot of dynamic memory, as the codeword tables are skipped
#ifndef LOCK_VERSION
#define LOCK_VERSION 0
#endif
typedef struct QRCode {
uint8_t version;
uint8_t size;
uint8_t ecc;
uint8_t mode;
uint8_t mask;
uint8_t* modules;
} QRCode;
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
uint16_t qrcode_getBufferSize(uint8_t version);
int8_t qrcode_initText(
QRCode* qrcode,
uint8_t* modules,
uint8_t version,
uint8_t ecc,
const char* data);
int8_t qrcode_initBytes(
QRCode* qrcode,
uint8_t* modules,
uint8_t version,
uint8_t ecc,
uint8_t* data,
uint16_t length);
bool qrcode_getModule(QRCode* qrcode, uint8_t x, uint8_t y);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* __QRCODE_H_ */

View File

@@ -1,643 +0,0 @@
#include <furi.h>
#include <dialogs/dialogs.h>
#include <gui/gui.h>
#include <storage/storage.h>
#include <lib/flipper_format/flipper_format.h>
// this file is generated by the build script
#include "qrcode_icons.h"
#include <assets_icons.h>
#include "qrcode.h"
#define TAG "qrcode"
#define QRCODE_FOLDER STORAGE_APP_DATA_PATH_PREFIX
#define QRCODE_EXTENSION ".qrcode"
#define QRCODE_FILETYPE "QRCode"
#define QRCODE_FILE_VERSION 0
/**
* Maximum version is 11 because the f0 screen is only 64 pixels high and
* version 12 is 65x65. Version 11 is 61x61.
*/
#define MAX_QRCODE_VERSION 11
/** Maximum length by mode, ecc, and version */
static const uint16_t MAX_LENGTH[3][4][MAX_QRCODE_VERSION] = {
{
// Numeric
{41, 77, 127, 187, 255, 322, 370, 461, 552, 652, 772}, // Low
{34, 63, 101, 149, 202, 255, 293, 365, 432, 513, 604}, // Medium
{27, 48, 77, 111, 144, 178, 207, 259, 312, 364, 427}, // Quartile
{17, 34, 58, 82, 106, 139, 154, 202, 235, 288, 331}, // High
},
{
// Alphanumeric
{25, 47, 77, 114, 154, 195, 224, 279, 335, 395, 468}, // Low
{20, 38, 61, 90, 122, 154, 178, 221, 262, 311, 366}, // Medium
{16, 29, 47, 67, 87, 108, 125, 157, 189, 221, 259}, // Quartile
{10, 20, 35, 50, 64, 84, 93, 122, 143, 174, 200}, // High
},
{
// Binary
{17, 32, 53, 78, 106, 134, 154, 192, 230, 271, 321}, // Low
{14, 26, 42, 62, 84, 106, 122, 152, 180, 213, 251}, // Medium
{11, 20, 32, 46, 60, 74, 86, 108, 130, 151, 177}, // Quartile
{7, 14, 24, 34, 44, 58, 64, 84, 98, 119, 137}, // High
},
};
/** Main app instance */
typedef struct {
FuriMessageQueue* input_queue;
Gui* gui;
ViewPort* view_port;
FuriMutex** mutex;
FuriString* message;
QRCode* qrcode;
uint8_t min_version;
uint8_t max_ecc_at_min_version;
bool loading;
bool too_long;
bool show_stats;
uint8_t selected_idx;
bool edit;
uint8_t set_version;
uint8_t set_ecc;
} 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->loading) {
canvas_draw_str_aligned(
canvas, width / 2, height / 2, AlignCenter, AlignCenter, "Loading...");
} else 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 = ((instance->show_stats ? 65 : 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)) {
if(pixel_size == 1) {
canvas_draw_dot(canvas, left + x * pixel_size, top + y * pixel_size);
} else {
canvas_draw_box(
canvas,
left + x * pixel_size,
top + y * pixel_size,
pixel_size,
pixel_size);
}
}
}
}
if(instance->show_stats) {
top = 10;
left = 66;
FuriString* str = furi_string_alloc();
if(!instance->edit || instance->selected_idx == 0) {
furi_string_printf(str, "Ver: %i", instance->set_version);
canvas_draw_str(canvas, left + 5, top + font_height, furi_string_get_cstr(str));
if(instance->selected_idx == 0) {
canvas_draw_triangle(
canvas,
left,
top + font_height / 2,
font_height - 4,
4,
CanvasDirectionLeftToRight);
}
if(instance->edit) {
uint8_t arrow_left = left + 5 + canvas_string_width(canvas, "Ver: 8") / 2;
canvas_draw_triangle(
canvas, arrow_left, top, font_height - 4, 4, CanvasDirectionBottomToTop);
canvas_draw_triangle(
canvas,
arrow_left,
top + font_height + 1,
font_height - 4,
4,
CanvasDirectionTopToBottom);
}
}
if(!instance->edit || instance->selected_idx == 1) {
furi_string_printf(str, "ECC: %c", get_ecc_char(instance->set_ecc));
canvas_draw_str(
canvas, left + 5, 2 * font_height + top + 2, furi_string_get_cstr(str));
if(instance->selected_idx == 1) {
canvas_draw_triangle(
canvas,
left,
3 * font_height / 2 + top + 2,
font_height - 4,
4,
CanvasDirectionLeftToRight);
}
if(instance->edit) {
uint8_t arrow_left = left + 5 + canvas_string_width(canvas, "ECC: H") / 2;
canvas_draw_triangle(
canvas,
arrow_left,
font_height + top + 2,
font_height - 4,
4,
CanvasDirectionBottomToTop);
canvas_draw_triangle(
canvas,
arrow_left,
2 * font_height + top + 3,
font_height - 4,
4,
CanvasDirectionTopToBottom);
}
}
if(!instance->edit) {
furi_string_printf(str, "Mod: %c", get_mode_char(instance->qrcode->mode));
canvas_draw_str(
canvas, left + 5, 3 * font_height + top + 4, furi_string_get_cstr(str));
}
furi_string_free(str);
}
} 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) {
canvas_set_font(canvas, FontSecondary);
canvas_draw_str(canvas, width / 2, margin * 2 + font_height, "Message is too long.");
}
}
furi_mutex_release(instance->mutex);
}
/**
* Handle input
* @param input_event The received input event
* @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);
}
}
/**
* Determine if the given string is all numeric
* @param str The string to test
* @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;
}
return true;
}
/**
* Determine if the given string is alphanumeric
* @param str The string to test
* @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;
if(c >= 'A' && c <= 'Z') continue;
if(c == ' ' || c == '$' || c == '%' || c == '*' || c == '+' || c == '-' || c == '.' ||
c == '/' || c == ':')
continue;
return false;
}
return true;
}
/**
* Allocate a qrcode
* @param version qrcode version
* @returns an allocated QRCode
*/
static QRCode* qrcode_alloc(uint8_t version) {
QRCode* qrcode = malloc(sizeof(QRCode));
qrcode->modules = malloc(qrcode_getBufferSize(version));
return qrcode;
}
/**
* Free a QRCode
* @param qrcode The QRCode to free
*/
static void qrcode_free(QRCode* qrcode) {
furi_assert(qrcode);
free(qrcode->modules);
free(qrcode);
}
/**
* Rebuild the qrcode. Assumes that instance->message is the message to encode,
* that the mutex has been acquired, and the specified version/ecc will be
* sufficiently large enough to encode the full message. It is also assumed
* that the old qrcode will be free'd by the caller.
* @param instance The qrcode app instance
* @param version The qrcode version to use
* @param ecc The qrcode ECC level to use
* @returns true if the qrcode was successfully created
*/
static bool rebuild_qrcode(QRCodeApp* instance, uint8_t version, uint8_t ecc) {
furi_assert(instance);
furi_assert(instance->message);
const char* cstr = furi_string_get_cstr(instance->message);
uint16_t len = strlen(cstr);
instance->qrcode = qrcode_alloc(version);
int8_t res = qrcode_initBytes(
instance->qrcode, instance->qrcode->modules, version, ecc, (uint8_t*)cstr, len);
if(res != 0) {
FURI_LOG_E(TAG, "Could not create qrcode");
qrcode_free(instance->qrcode);
instance->qrcode = NULL;
return false;
}
return true;
}
/**
* Load a qrcode from a string
* @param instance The qrcode app instance
* @param str The message to encode as a 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->message) {
furi_string_free(instance->message);
instance->message = NULL;
}
if(instance->qrcode) {
qrcode_free(instance->qrcode);
instance->qrcode = NULL;
}
instance->too_long = false;
instance->show_stats = false;
instance->selected_idx = 0;
instance->edit = false;
bool result = false;
do {
const char* cstr = furi_string_get_cstr(str);
uint16_t len = strlen(cstr);
instance->message = furi_string_alloc_set(str);
if(!instance->message) {
FURI_LOG_E(TAG, "Could not allocate message");
break;
}
// figure out the qrcode "mode"
uint8_t mode = MODE_BYTE;
if(is_numeric(cstr, len))
mode = MODE_NUMERIC;
else if(is_alphanumeric(cstr, len))
mode = MODE_ALPHANUMERIC;
// Figure out the smallest qrcode version that'll fit all of the data -
// we prefer the smallest version to maximize the pixel size of each
// module to improve reader performance. Here, version is the 0-based
// index. The qrcode_initBytes function will want a 1-based version
// number, so we'll add one later.
uint8_t ecc = ECC_LOW;
uint8_t version = 0;
while(version < MAX_QRCODE_VERSION && MAX_LENGTH[mode][ecc][version] < len) {
version++;
}
if(version == MAX_QRCODE_VERSION) {
instance->too_long = true;
break;
}
// Figure out the maximum ECC we can use. I shouldn't need to
// bounds-check ecc in this loop because I already know from the loop
// above that ECC_LOW (0) works... don't forget to add one to that
// version number...
ecc = ECC_HIGH;
while(MAX_LENGTH[mode][ecc][version] < len) {
ecc--;
}
version++;
// Build the qrcode
if(!rebuild_qrcode(instance, version, ecc)) {
furi_string_free(instance->message);
instance->message = NULL;
break;
}
instance->min_version = instance->set_version = version;
instance->max_ecc_at_min_version = instance->set_ecc = ecc;
result = true;
} while(false);
instance->loading = false;
furi_mutex_release(instance->mutex);
return result;
}
/**
* Load a qrcode from a file
* @param instance The qrcode app instance
* @param file_path Path to the file to read
* @returns true if the file was successfully loaded
*/
static bool qrcode_load_file(QRCodeApp* instance, const char* file_path) {
furi_assert(instance);
furi_assert(file_path);
FuriString* temp_str = furi_string_alloc();
bool result = false;
Storage* storage = furi_record_open(RECORD_STORAGE);
FlipperFormat* file = flipper_format_file_alloc(storage);
do {
if(!flipper_format_file_open_existing(file, file_path)) break;
uint32_t version = 0;
if(!flipper_format_read_header(file, temp_str, &version)) break;
if(furi_string_cmp_str(temp_str, QRCODE_FILETYPE) || version != QRCODE_FILE_VERSION) {
FURI_LOG_E(TAG, "Incorrect file format or version");
break;
}
if(!flipper_format_read_string(file, "Message", temp_str)) {
FURI_LOG_E(TAG, "Message is missing");
break;
}
if(!qrcode_load_string(instance, temp_str)) {
break;
}
result = true;
} while(false);
furi_record_close(RECORD_STORAGE);
flipper_format_free(file);
furi_string_free(temp_str);
return result;
}
/**
* Allocate the qrcode app
* @returns a qrcode app instance
*/
static QRCodeApp* qrcode_app_alloc() {
QRCodeApp* instance = malloc(sizeof(QRCodeApp));
instance->input_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
instance->view_port = view_port_alloc();
view_port_draw_callback_set(instance->view_port, render_callback, instance);
view_port_input_callback_set(instance->view_port, input_callback, instance);
instance->gui = furi_record_open(RECORD_GUI);
gui_add_view_port(instance->gui, instance->view_port, GuiLayerFullscreen);
instance->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
instance->message = NULL;
instance->qrcode = NULL;
instance->loading = true;
instance->too_long = false;
instance->show_stats = false;
instance->selected_idx = 0;
instance->edit = false;
return instance;
}
/**
* Free the qrcode app
* @param qrcode_app The app to free
*/
static void qrcode_app_free(QRCodeApp* instance) {
if(instance->message) furi_string_free(instance->message);
if(instance->qrcode) qrcode_free(instance->qrcode);
gui_remove_view_port(instance->gui, instance->view_port);
furi_record_close(RECORD_GUI);
view_port_free(instance->view_port);
furi_message_queue_free(instance->input_queue);
furi_mutex_free(instance->mutex);
free(instance);
}
/** App entrypoint */
int32_t qrcode_app(void* p) {
QRCodeApp* instance = qrcode_app_alloc();
FuriString* file_path = furi_string_alloc();
Storage* storage = furi_record_open(RECORD_STORAGE);
storage_common_migrate(storage, EXT_PATH("qrcodes"), QRCODE_FOLDER);
furi_record_close(RECORD_STORAGE);
do {
if(p && strlen(p)) {
furi_string_set(file_path, (const char*)p);
} else {
furi_string_set(file_path, QRCODE_FOLDER);
DialogsFileBrowserOptions browser_options;
dialog_file_browser_set_basic_options(
&browser_options, QRCODE_EXTENSION, &I_qrcode_10px);
browser_options.hide_ext = true;
browser_options.base_path = QRCODE_FOLDER;
DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);
bool res = dialog_file_browser_show(dialogs, file_path, file_path, &browser_options);
furi_record_close(RECORD_DIALOGS);
if(!res) {
FURI_LOG_E(TAG, "No file selected");
break;
}
}
if(!qrcode_load_file(instance, furi_string_get_cstr(file_path))) {
FURI_LOG_E(TAG, "Unable to load file");
}
InputEvent input;
while(1) {
if(furi_message_queue_get(instance->input_queue, &input, 100) == FuriStatusOk) {
furi_check(furi_mutex_acquire(instance->mutex, FuriWaitForever) == FuriStatusOk);
if(input.key == InputKeyBack) {
if(instance->message) {
furi_string_free(instance->message);
instance->message = NULL;
}
if(instance->qrcode) {
qrcode_free(instance->qrcode);
instance->qrcode = NULL;
}
instance->loading = true;
instance->edit = false;
furi_mutex_release(instance->mutex);
break;
} else if(input.key == InputKeyRight) {
instance->show_stats = true;
} else if(input.key == InputKeyLeft) {
instance->show_stats = false;
} else if(instance->show_stats && !instance->loading && instance->qrcode) {
if(input.key == InputKeyUp) {
if(!instance->edit) {
instance->selected_idx = MAX(0, instance->selected_idx - 1);
} else {
if(instance->selected_idx == 0 &&
instance->set_version < MAX_QRCODE_VERSION) {
instance->set_version++;
} else if(instance->selected_idx == 1) {
uint8_t max_ecc = instance->set_version == instance->min_version ?
instance->max_ecc_at_min_version :
ECC_HIGH;
if(instance->set_ecc < max_ecc) {
instance->set_ecc++;
}
}
}
} else if(input.key == InputKeyDown) {
if(!instance->edit) {
instance->selected_idx = MIN(1, instance->selected_idx + 1);
} else {
if(instance->selected_idx == 0 &&
instance->set_version > instance->min_version) {
instance->set_version--;
if(instance->set_version == instance->min_version) {
instance->set_ecc =
MAX(instance->set_ecc, instance->max_ecc_at_min_version);
}
} else if(instance->selected_idx == 1 && instance->set_ecc > 0) {
instance->set_ecc--;
}
}
} else if(input.key == InputKeyOk) {
if(instance->edit && (instance->set_version != instance->qrcode->version ||
instance->set_ecc != instance->qrcode->ecc)) {
QRCode* qrcode = instance->qrcode;
instance->loading = true;
if(rebuild_qrcode(instance, instance->set_version, instance->set_ecc)) {
qrcode_free(qrcode);
} else {
FURI_LOG_E(TAG, "Could not rebuild qrcode");
instance->qrcode = qrcode;
instance->set_version = qrcode->version;
instance->set_ecc = qrcode->ecc;
}
instance->loading = false;
}
instance->edit = !instance->edit;
}
}
furi_mutex_release(instance->mutex);
}
view_port_update(instance->view_port);
}
if(p && strlen(p)) {
// if started with an arg, exit instead
// of looping back to the browser
break;
}
} while(true);
furi_string_free(file_path);
qrcode_app_free(instance);
return 0;
}

View File

@@ -1,674 +0,0 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.

View File

@@ -1,14 +0,0 @@
App(
appid="radar_scanner",
name="[RCWL0516] Radar Scan",
apptype=FlipperAppType.EXTERNAL,
entry_point="app_radar_scanner",
requires=["gui"],
stack_size=1 * 1024,
fap_icon="icon.png",
fap_category="GPIO",
fap_author="@MatthewKuKanich",
fap_weburl="https://github.com/MatthewKuKanich/flipper-radar",
fap_version="2.0",
fap_description="Detects the movement of living things using radar",
)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 137 B

View File

@@ -1,203 +0,0 @@
// Created by @MatthewKuKanich for use with the RCWL-0516
// Design inspired from @unixispower Wire Tester
#include <furi_hal.h>
#include <gui/gui.h>
#include <notification/notification_messages.h>
#include <gui/elements.h>
static const uint32_t EVENT_PERIOD_MS = 10;
static const float BEEP_FREQ = 1000.0f;
static const float BEEP_VOL = 0.9f;
static const GpioPin* const radarPin = &gpio_ext_pc3; // Pin 7
static const GpioPin* const altRadarPin = &gpio_ext_pa7; // Pin 2
static const GpioPin* const altGroundPin = &gpio_ext_pa6; // Pin 3
bool presenceDetected = false;
bool muted = false;
bool active = false;
bool continuous = false; // Start with no signal from OUT
bool altPinout; // Sets which GPIO pinout config to use
static void start_feedback(NotificationApp* notifications) {
// Set LED to red for detection
notification_message_block(notifications, &sequence_set_only_red_255);
// Set vibration
notification_message_block(notifications, &sequence_double_vibro);
if(!muted) {
// Start beep if not muted
if(furi_hal_speaker_acquire(1000)) {
furi_hal_speaker_start(BEEP_FREQ, BEEP_VOL);
}
}
}
static void stop_feedback(NotificationApp* notifications) {
// Clear LED
notification_message_block(notifications, &sequence_reset_rgb);
// Reset vibration
notification_message_block(notifications, &sequence_reset_vibro);
// Stop beeping
if(furi_hal_speaker_is_mine()) {
furi_hal_speaker_stop();
furi_hal_speaker_release();
}
}
static void draw_callback(Canvas* canvas, void* ctx) {
furi_assert(ctx);
canvas_clear(canvas);
canvas_set_font(canvas, FontPrimary);
elements_multiline_text_aligned(canvas, 64, 2, AlignCenter, AlignTop, "Microwave Radar");
canvas_set_font(canvas, FontSecondary);
if(active) {
elements_multiline_text_aligned(canvas, 64, 12, AlignCenter, AlignTop, "Active");
} else {
elements_multiline_text_aligned(canvas, 64, 12, AlignCenter, AlignTop, "On Standby");
}
// Display presence status
canvas_set_font(canvas, FontPrimary);
if(presenceDetected) {
elements_multiline_text_aligned(
canvas, 64, 20, AlignCenter, AlignTop, "Presence Detected");
} else {
elements_multiline_text_aligned(canvas, 64, 20, AlignCenter, AlignTop, "No Presence");
}
canvas_set_font(canvas, FontSecondary);
if(muted) {
elements_multiline_text_aligned(canvas, 64, 32, AlignCenter, AlignTop, "Muted");
}
canvas_set_font(canvas, FontBatteryPercent);
if(altPinout) {
elements_multiline_text_aligned(
canvas, 64, 42, AlignCenter, AlignTop, "Alt-Pinout Enabled");
elements_multiline_text_aligned(
canvas, 64, 49, AlignCenter, AlignTop, "VIN -> 5v :: GND -> Pin 3");
elements_multiline_text_aligned(
canvas, 64, 56, AlignCenter, AlignTop, "OUT -> Pin 2 (A7)");
} else if(!altPinout) {
elements_multiline_text_aligned(
canvas, 64, 42, AlignCenter, AlignTop, "Alt-Pinout Disabled");
elements_multiline_text_aligned(
canvas, 64, 49, AlignCenter, AlignTop, "VIN -> 5v :: GND -> GND");
elements_multiline_text_aligned(
canvas, 64, 56, AlignCenter, AlignTop, "OUT -> Pin 7 (C3)");
}
}
static void input_callback(InputEvent* input_event, void* ctx) {
furi_assert(ctx);
FuriMessageQueue* event_queue = ctx;
furi_message_queue_put(event_queue, input_event, FuriWaitForever);
}
static void get_reading() {
if(altPinout) {
continuous = furi_hal_gpio_read(altRadarPin);
} else {
continuous = furi_hal_gpio_read(radarPin);
}
}
int32_t app_radar_scanner(void* p) {
UNUSED(p);
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
// I'm keeping the forced backlight as you will likely be away from Flipper
NotificationApp* notifications = furi_record_open(RECORD_NOTIFICATION);
notification_message_block(notifications, &sequence_display_backlight_enforce_on);
ViewPort* view_port = view_port_alloc();
view_port_draw_callback_set(view_port, draw_callback, view_port);
view_port_input_callback_set(view_port, input_callback, event_queue);
Gui* gui = furi_record_open(RECORD_GUI);
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
view_port_update(view_port);
stop_feedback(notifications);
// set input to be low; RCWL-0516 outputs High (3v) on detection
furi_hal_gpio_init(radarPin, GpioModeInput, GpioPullDown, GpioSpeedVeryHigh);
furi_hal_gpio_init(altRadarPin, GpioModeInput, GpioPullDown, GpioSpeedVeryHigh);
furi_hal_gpio_init(altGroundPin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
furi_hal_gpio_write(altGroundPin, false);
// Auto 5v power
uint8_t attempts = 0;
bool otg_was_enabled = furi_hal_power_is_otg_enabled();
while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) {
furi_hal_power_enable_otg();
furi_delay_ms(10);
}
bool alarming = false; // Sensor begins in-active until user starts
bool running = true; // to prevent unwanted false positives
while(running) {
if(active) {
// start and stop feedback if sensor state is active
get_reading();
if(continuous && !alarming) {
presenceDetected = true;
start_feedback(notifications);
} else if(!continuous && alarming) {
presenceDetected = false;
stop_feedback(notifications); // Green LED if clear/no presence
notification_message_block(notifications, &sequence_set_only_green_255);
}
alarming = continuous;
}
// Exit on back key
InputEvent event;
if(furi_message_queue_get(event_queue, &event, EVENT_PERIOD_MS) == FuriStatusOk) {
if(event.type == InputTypePress) {
if(event.key == InputKeyBack) {
break;
}
if(event.key == InputKeyOk) {
active = !active; // Toggle the value of 'active'
stop_feedback(notifications);
}
if(event.key == InputKeyDown) {
muted = !muted; // Toggle the value of 'muted'
stop_feedback(notifications);
}
if(event.key == InputKeyRight) {
altPinout = !altPinout; // Toggle alternate pinout
}
}
}
}
// return control of the LED, beeper, backlight, and stop vibration
stop_feedback(notifications);
notification_message_block(notifications, &sequence_display_backlight_enforce_auto);
// Disable 5v power
if(furi_hal_power_is_otg_enabled() && !otg_was_enabled) {
furi_hal_power_disable_otg();
}
view_port_enabled_set(view_port, false);
gui_remove_view_port(gui, view_port);
view_port_free(view_port);
furi_message_queue_free(event_queue);
furi_record_close(RECORD_GUI);
furi_record_close(RECORD_NOTIFICATION);
return 0;
}

View File

@@ -1,674 +0,0 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.

View File

@@ -1,12 +0,0 @@
App(
appid="coleco",
name="[RC2014] ColecoVision",
apptype=FlipperAppType.EXTERNAL,
entry_point="coleco_app",
cdefines=["APP_COLECO"],
requires=["gui"],
stack_size=1 * 1024,
fap_icon="coleco_10px.png",
fap_icon_assets="icons",
fap_category="GPIO",
)

View File

@@ -1,365 +0,0 @@
#include <furi.h>
#include <furi_hal_gpio.h>
#include <furi_hal_power.h>
#include <gui/gui.h>
#include "coleco_icons.h"
#include <assets_icons.h>
#define CODE_0 0x0A
#define CODE_1 0x0D
#define CODE_2 0x07
#define CODE_3 0x0C
#define CODE_4 0x02
#define CODE_5 0x03
#define CODE_6 0x0E
#define CODE_7 0x05
#define CODE_8 0x01
#define CODE_9 0x0B
#define CODE_H 0x06
#define CODE_S 0x09
#define CODE_N 0x0F
const GpioPin* const pin_up = &gpio_ext_pa6;
const GpioPin* const pin_down = &gpio_ext_pc0;
const GpioPin* const pin_right = &gpio_ext_pb2;
const GpioPin* const pin_left = &gpio_ext_pc3;
const GpioPin* const pin_code0 = &gpio_ext_pa7;
const GpioPin* const pin_code1 = &gpio_ext_pa4;
const GpioPin* const pin_code2 = &gpio_ibutton;
const GpioPin* const pin_code3 = &gpio_ext_pc1;
const GpioPin* const pin_fire = &gpio_ext_pb3;
const GpioPin* const pin_alt = &gpio_usart_tx;
typedef enum {
EventTypeTick,
EventTypeKey,
} EventType;
typedef struct {
EventType type;
InputEvent input;
} PluginEvent;
typedef struct {
FuriMutex* mutex;
bool dpad;
int row;
int column;
} Coleco;
static void render_callback(Canvas* const canvas, void* context) {
furi_assert(context);
Coleco* coleco = context;
furi_mutex_acquire(coleco->mutex, FuriWaitForever);
if(coleco->dpad) {
canvas_draw_icon(canvas, 4, 16, &I_ColecoJoystick_sel_33x33);
canvas_draw_icon(canvas, 27, 52, &I_ColecoFire_sel_18x9);
} else {
const bool hvr = coleco->row == 0 && coleco->column < 2;
canvas_draw_icon(
canvas, 4, 16, hvr ? &I_ColecoJoystick_hvr_33x33 : &I_ColecoJoystick_33x33);
canvas_draw_icon(canvas, 27, 52, hvr ? &I_ColecoFire_hvr_18x9 : &I_ColecoFire_18x9);
}
canvas_draw_icon(
canvas,
27,
4,
(coleco->row == 0 && coleco->column == 2) ? &I_ColecoAlt_hvr_18x9 : &I_ColecoAlt_18x9);
canvas_draw_icon(
canvas,
49,
44,
(coleco->row == 1 && coleco->column == 0) ? &I_Coleco1_hvr_17x17 : &I_Coleco1_17x17);
canvas_draw_icon(
canvas,
49,
24,
(coleco->row == 1 && coleco->column == 1) ? &I_Coleco2_hvr_17x17 : &I_Coleco2_17x17);
canvas_draw_icon(
canvas,
49,
4,
(coleco->row == 1 && coleco->column == 2) ? &I_Coleco3_hvr_17x17 : &I_Coleco3_17x17);
canvas_draw_icon(
canvas,
69,
44,
(coleco->row == 2 && coleco->column == 0) ? &I_Coleco4_hvr_17x17 : &I_Coleco4_17x17);
canvas_draw_icon(
canvas,
69,
24,
(coleco->row == 2 && coleco->column == 1) ? &I_Coleco5_hvr_17x17 : &I_Coleco5_17x17);
canvas_draw_icon(
canvas,
69,
4,
(coleco->row == 2 && coleco->column == 2) ? &I_Coleco6_hvr_17x17 : &I_Coleco6_17x17);
canvas_draw_icon(
canvas,
89,
44,
(coleco->row == 3 && coleco->column == 0) ? &I_Coleco7_hvr_17x17 : &I_Coleco7_17x17);
canvas_draw_icon(
canvas,
89,
24,
(coleco->row == 3 && coleco->column == 1) ? &I_Coleco8_hvr_17x17 : &I_Coleco8_17x17);
canvas_draw_icon(
canvas,
89,
4,
(coleco->row == 3 && coleco->column == 2) ? &I_Coleco9_hvr_17x17 : &I_Coleco9_17x17);
canvas_draw_icon(
canvas,
109,
44,
(coleco->row == 4 && coleco->column == 0) ? &I_ColecoStar_hvr_17x17 : &I_ColecoStar_17x17);
canvas_draw_icon(
canvas,
109,
24,
(coleco->row == 4 && coleco->column == 1) ? &I_Coleco0_hvr_17x17 : &I_Coleco0_17x17);
canvas_draw_icon(
canvas,
109,
4,
(coleco->row == 4 && coleco->column == 2) ? &I_ColecoPound_hvr_17x17 :
&I_ColecoPound_17x17);
furi_mutex_release(coleco->mutex);
}
static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
furi_assert(event_queue);
PluginEvent event = {.type = EventTypeKey, .input = *input_event};
furi_message_queue_put(event_queue, &event, FuriWaitForever);
}
static void coleco_write_code(uint8_t code) {
furi_hal_gpio_write(pin_code0, (code & 1));
furi_hal_gpio_write(pin_code1, (code & 2));
furi_hal_gpio_write(pin_code2, (code & 4));
furi_hal_gpio_write(pin_code3, (code & 8));
}
static void coleco_gpio_init() {
// configure output pins
furi_hal_gpio_init(pin_up, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
furi_hal_gpio_init(pin_down, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
furi_hal_gpio_init(pin_right, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
furi_hal_gpio_init(pin_left, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
furi_hal_gpio_init(pin_code0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
furi_hal_gpio_init(pin_code1, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
furi_hal_gpio_init(pin_code2, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
furi_hal_gpio_init(pin_code3, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
furi_hal_gpio_init(pin_fire, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
furi_hal_gpio_init(pin_alt, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
furi_hal_gpio_write(pin_up, true);
furi_hal_gpio_write(pin_down, true);
furi_hal_gpio_write(pin_right, true);
furi_hal_gpio_write(pin_left, true);
furi_hal_gpio_write(pin_fire, true);
furi_hal_gpio_write(pin_alt, true);
coleco_write_code(CODE_N);
}
static Coleco* coleco_alloc() {
Coleco* coleco = malloc(sizeof(Coleco));
coleco->dpad = false;
coleco->row = 0;
coleco->column = 1;
return coleco;
}
static void coleco_free(Coleco* coleco) {
furi_assert(coleco);
free(coleco);
}
int32_t coleco_app(void* p) {
UNUSED(p);
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent));
Coleco* coleco = coleco_alloc();
coleco->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
if(!coleco->mutex) {
FURI_LOG_E("Coleco", "cannot create mutex\r\n");
coleco_free(coleco);
return 255;
}
// set system callbacks
ViewPort* view_port = view_port_alloc();
view_port_draw_callback_set(view_port, render_callback, coleco);
view_port_input_callback_set(view_port, input_callback, event_queue);
// open GUI and register view_port
Gui* gui = furi_record_open("gui");
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
coleco_gpio_init();
furi_hal_power_enable_otg();
PluginEvent event;
for(bool processing = true; processing;) {
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
furi_mutex_acquire(coleco->mutex, FuriWaitForever);
if(event_status == FuriStatusOk) {
// press events
if(event.type == EventTypeKey) {
switch(event.input.key) {
case InputKeyUp:
if(coleco->dpad) {
if(event.input.type == InputTypePress) {
furi_hal_gpio_write(pin_up, false);
} else if(event.input.type == InputTypeRelease) {
furi_hal_gpio_write(pin_up, true);
}
} else {
if(event.input.type == InputTypePress && coleco->column < 2) {
coleco->column++;
coleco_write_code(CODE_N);
}
}
break;
case InputKeyDown:
if(coleco->dpad) {
if(event.input.type == InputTypePress) {
furi_hal_gpio_write(pin_down, false);
} else if(event.input.type == InputTypeRelease) {
furi_hal_gpio_write(pin_down, true);
}
} else {
if(event.input.type == InputTypePress && coleco->column > 0) {
coleco->column--;
coleco_write_code(CODE_N);
}
}
break;
case InputKeyRight:
if(coleco->dpad) {
if(event.input.type == InputTypePress) {
furi_hal_gpio_write(pin_right, false);
} else if(event.input.type == InputTypeRelease) {
furi_hal_gpio_write(pin_right, true);
}
} else {
if(event.input.type == InputTypePress && coleco->row < 4) {
coleco->row++;
coleco_write_code(CODE_N);
}
}
break;
case InputKeyLeft:
if(coleco->dpad) {
if(event.input.type == InputTypePress) {
furi_hal_gpio_write(pin_left, false);
} else if(event.input.type == InputTypeRelease) {
furi_hal_gpio_write(pin_left, true);
}
} else {
if(event.input.type == InputTypePress && coleco->row > 0) {
coleco->row--;
coleco_write_code(CODE_N);
}
}
break;
case InputKeyOk:
if(coleco->dpad) {
if(event.input.type == InputTypePress) {
furi_hal_gpio_write(pin_fire, false);
} else if(event.input.type == InputTypeRelease) {
furi_hal_gpio_write(pin_fire, true);
}
} else {
if(event.input.type == InputTypePress) {
if(coleco->row == 0) {
if(coleco->column == 2) {
furi_hal_gpio_write(pin_alt, false);
} else {
coleco->dpad = true;
}
} else if(coleco->row == 1) {
if(coleco->column == 0) {
coleco_write_code(CODE_1);
} else if(coleco->column == 1) {
coleco_write_code(CODE_2);
} else {
coleco_write_code(CODE_3);
}
} else if(coleco->row == 2) {
if(coleco->column == 0) {
coleco_write_code(CODE_4);
} else if(coleco->column == 1) {
coleco_write_code(CODE_5);
} else {
coleco_write_code(CODE_6);
}
} else if(coleco->row == 3) {
if(coleco->column == 0) {
coleco_write_code(CODE_7);
} else if(coleco->column == 1) {
coleco_write_code(CODE_8);
} else {
coleco_write_code(CODE_9);
}
} else if(coleco->row == 4) {
if(coleco->column == 0) {
coleco_write_code(CODE_S);
} else if(coleco->column == 1) {
coleco_write_code(CODE_0);
} else {
coleco_write_code(CODE_H);
}
}
}
if(event.input.type == InputTypeRelease) {
furi_hal_gpio_write(pin_alt, true);
coleco_write_code(CODE_N);
}
}
break;
case InputKeyBack:
if(event.input.type == InputTypePress) {
if(coleco->dpad) {
coleco->dpad = false;
} else {
processing = false;
}
}
break;
default:
break;
}
view_port_update(view_port);
}
}
furi_mutex_release(coleco->mutex);
}
furi_hal_power_disable_otg();
view_port_enabled_set(view_port, false);
gui_remove_view_port(gui, view_port);
furi_record_close("gui");
view_port_free(view_port);
furi_message_queue_free(event_queue);
furi_mutex_free(coleco->mutex);
coleco_free(coleco);
return 0;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 628 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 627 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 623 B

Some files were not shown because too many files have changed in this diff Show More