Get back apps & update NFC
@@ -45,6 +45,7 @@
|
||||
ARRAY_DEF(MfClassicUserKeys, char*, M_PTR_OPLIST);
|
||||
|
||||
#define NFC_TEXT_STORE_SIZE 128
|
||||
#define NFC_APP_FOLDER ANY_PATH("nfc")
|
||||
|
||||
typedef enum {
|
||||
NfcRpcStateIdle,
|
||||
|
||||
@@ -52,6 +52,7 @@ void nfc_scene_delete_on_enter(void* context) {
|
||||
|
||||
bool nfc_scene_delete_on_event(void* context, SceneManagerEvent event) {
|
||||
Nfc* nfc = context;
|
||||
furi_string_printf(nfc->dev->folder, "%s", NFC_APP_FOLDER);
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
void nfc_scene_file_select_on_enter(void* context) {
|
||||
Nfc* nfc = context;
|
||||
furi_string_printf(nfc->dev->folder, "%s", NFC_APP_FOLDER);
|
||||
// Process file_select return
|
||||
nfc_device_set_loading_callback(nfc->dev, nfc_show_loading_popup, nfc);
|
||||
if(!furi_string_size(nfc->dev->load_path)) {
|
||||
|
||||
@@ -24,6 +24,7 @@ void nfc_scene_restore_original_confirm_on_enter(void* context) {
|
||||
|
||||
bool nfc_scene_restore_original_confirm_on_event(void* context, SceneManagerEvent event) {
|
||||
Nfc* nfc = context;
|
||||
furi_string_printf(nfc->dev->folder, "%s", NFC_APP_FOLDER);
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
|
||||
@@ -51,6 +51,7 @@ void nfc_scene_save_name_on_enter(void* context) {
|
||||
|
||||
bool nfc_scene_save_name_on_event(void* context, SceneManagerEvent event) {
|
||||
Nfc* nfc = context;
|
||||
furi_string_printf(nfc->dev->folder, "%s", NFC_APP_FOLDER);
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
App(
|
||||
appid="AM2320_temp_sensor",
|
||||
name="[AM2320] Temp. Sensor",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="am_temperature_sensor_app",
|
||||
cdefines=["APP_AM_TEMPERATURE_SENSOR"],
|
||||
requires=[
|
||||
"gui",
|
||||
],
|
||||
stack_size=2 * 1024,
|
||||
order=90,
|
||||
fap_icon="temperature_sensor.png",
|
||||
fap_category="GPIO",
|
||||
)
|
||||
@@ -0,0 +1,336 @@
|
||||
/* Flipper Plugin to read the values from a AM2320/AM2321 Sensor */
|
||||
/* Created by @xMasterX, original app (was used as template) by Mywk - https://github.com/Mywk */
|
||||
/* Lib used as reference: https://github.com/Gozem/am2320/blob/master/am2321.c*/
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <furi_hal_i2c.h>
|
||||
#include <math.h>
|
||||
|
||||
#include <gui/gui.h>
|
||||
#include <input/input.h>
|
||||
|
||||
#include <notification/notification_messages.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#define TS_DEFAULT_VALUE 0xFFFF
|
||||
|
||||
#define AM2320_ADDRESS (0x5C << 1)
|
||||
|
||||
#define DATA_BUFFER_SIZE 8
|
||||
|
||||
// External I2C BUS
|
||||
#define I2C_BUS &furi_hal_i2c_handle_external
|
||||
|
||||
typedef enum {
|
||||
TSSInitializing,
|
||||
TSSNoSensor,
|
||||
TSSPendingUpdate,
|
||||
} TSStatus;
|
||||
|
||||
typedef enum {
|
||||
TSEventTypeTick,
|
||||
TSEventTypeInput,
|
||||
} TSEventType;
|
||||
|
||||
typedef struct {
|
||||
TSEventType type;
|
||||
InputEvent input;
|
||||
} TSEvent;
|
||||
|
||||
extern const NotificationSequence sequence_blink_red_100;
|
||||
extern const NotificationSequence sequence_blink_blue_100;
|
||||
|
||||
static TSStatus temperature_sensor_current_status = TSSInitializing;
|
||||
|
||||
// Temperature and Humidity data buffers, ready to print
|
||||
char ts_data_buffer_temperature_c[DATA_BUFFER_SIZE];
|
||||
char ts_data_buffer_temperature_f[DATA_BUFFER_SIZE];
|
||||
char ts_data_buffer_relative_humidity[DATA_BUFFER_SIZE];
|
||||
char ts_data_buffer_absolute_humidity[DATA_BUFFER_SIZE];
|
||||
|
||||
// CRC16 calculation
|
||||
static uint16_t get_crc16(const uint8_t* buf, size_t len) {
|
||||
uint16_t crc = 0xFFFF;
|
||||
|
||||
while(len--) {
|
||||
crc ^= (uint16_t)*buf++;
|
||||
for(unsigned i = 0; i < 8; i++) {
|
||||
if(crc & 0x0001) {
|
||||
crc >>= 1;
|
||||
crc ^= 0xA001;
|
||||
} else {
|
||||
crc >>= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
// Combine bytes
|
||||
static uint16_t combine_bytes(uint8_t msb, uint8_t lsb) {
|
||||
return ((uint16_t)msb << 8) | (uint16_t)lsb;
|
||||
}
|
||||
|
||||
// Executes an I2C wake up, sends command and reads result
|
||||
// true if fetch was successful, false otherwise
|
||||
static bool temperature_sensor_get_data(uint8_t* buffer, uint8_t size) {
|
||||
uint32_t timeout = furi_ms_to_ticks(100);
|
||||
uint8_t cmdbuffer[3] = {0, 0, 0};
|
||||
bool ret = false;
|
||||
|
||||
// Aquire I2C bus
|
||||
furi_hal_i2c_acquire(I2C_BUS);
|
||||
|
||||
// Wake UP AM2320 (sensor goes to sleep to not warm up and affect the humidity sensor)
|
||||
furi_hal_i2c_is_device_ready(I2C_BUS, (uint8_t)AM2320_ADDRESS, timeout);
|
||||
// Check if device woken up then we do next stuff
|
||||
if(furi_hal_i2c_is_device_ready(I2C_BUS, (uint8_t)AM2320_ADDRESS, timeout)) {
|
||||
// Wait a bit
|
||||
furi_delay_us(1000);
|
||||
|
||||
// Prepare command: Addr 0x03, start register = 0x00, number of registers to read = 0x04
|
||||
cmdbuffer[0] = 0x03;
|
||||
cmdbuffer[1] = 0x00;
|
||||
cmdbuffer[2] = 0x04;
|
||||
|
||||
// Transmit command to read registers
|
||||
ret = furi_hal_i2c_tx(I2C_BUS, (uint8_t)AM2320_ADDRESS, cmdbuffer, 3, timeout);
|
||||
|
||||
// Wait a bit
|
||||
furi_delay_us(1600);
|
||||
if(ret) {
|
||||
/*
|
||||
* Read out 8 bytes of data
|
||||
* Byte 0: Should be Modbus function code 0x03
|
||||
* Byte 1: Should be number of registers to read (0x04)
|
||||
* Byte 2: Humidity msb
|
||||
* Byte 3: Humidity lsb
|
||||
* Byte 4: Temperature msb
|
||||
* Byte 5: Temperature lsb
|
||||
* Byte 6: CRC lsb byte
|
||||
* Byte 7: CRC msb byte
|
||||
*/
|
||||
ret = furi_hal_i2c_rx(I2C_BUS, (uint8_t)AM2320_ADDRESS, buffer, size, timeout);
|
||||
}
|
||||
}
|
||||
// Release i2c bus
|
||||
furi_hal_i2c_release(I2C_BUS);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Fetches temperature and humidity from sensor
|
||||
// Temperature and humidity must be preallocated
|
||||
// true if fetch was successful, false otherwise
|
||||
static bool temperature_sensor_fetch_info(double* temperature, double* humidity) {
|
||||
*humidity = (float)0;
|
||||
bool ret = false;
|
||||
|
||||
uint8_t buffer[8] = {0, 0, 0, 0, 0, 0, 0, 0};
|
||||
|
||||
// Fetch data from sensor
|
||||
ret = temperature_sensor_get_data(buffer, 8);
|
||||
|
||||
// If we got no result
|
||||
if(!ret) return false;
|
||||
|
||||
if(buffer[0] != 0x03) return false; // must be 0x03 modbus reply
|
||||
if(buffer[1] != 0x04) return false; // must be 0x04 number of registers reply
|
||||
|
||||
// Check CRC16 sum, if not correct - return false
|
||||
uint16_t crcdata = get_crc16(buffer, 6);
|
||||
uint16_t crcread = combine_bytes(buffer[7], buffer[6]);
|
||||
if(crcdata != crcread) return false;
|
||||
|
||||
// Combine bytes for temp and humidity
|
||||
uint16_t temp16 = combine_bytes(buffer[4], buffer[5]);
|
||||
uint16_t humi16 = combine_bytes(buffer[2], buffer[3]);
|
||||
|
||||
/* Temperature resolution is 16Bit,
|
||||
* temperature highest bit (Bit15) is equal to 1 indicates a
|
||||
* negative temperature, the temperature highest bit (Bit15)
|
||||
* is equal to 0 indicates a positive temperature;
|
||||
* temperature in addition to the most significant bit (Bit14 ~ Bit0)
|
||||
* indicates the temperature sensor string value.
|
||||
* Temperature sensor value is a string of 10 times the
|
||||
* actual temperature value.
|
||||
*/
|
||||
if(temp16 & 0x8000) {
|
||||
temp16 = -(temp16 & 0x7FFF);
|
||||
}
|
||||
|
||||
// Prepare output data
|
||||
*temperature = (float)temp16 / 10.0;
|
||||
*humidity = (float)humi16 / 10.0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Draw callback
|
||||
|
||||
static void temperature_sensor_draw_callback(Canvas* canvas, void* ctx) {
|
||||
UNUSED(ctx);
|
||||
|
||||
canvas_clear(canvas);
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str(canvas, 2, 10, "AM2320/AM2321 Sensor");
|
||||
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_draw_str(canvas, 2, 62, "Press back to exit.");
|
||||
|
||||
switch(temperature_sensor_current_status) {
|
||||
case TSSInitializing:
|
||||
canvas_draw_str(canvas, 2, 30, "Initializing..");
|
||||
break;
|
||||
case TSSNoSensor:
|
||||
canvas_draw_str(canvas, 2, 30, "No sensor found!");
|
||||
break;
|
||||
case TSSPendingUpdate: {
|
||||
canvas_draw_str(canvas, 3, 24, "Temperature");
|
||||
canvas_draw_str(canvas, 68, 24, "Humidity");
|
||||
|
||||
// Draw vertical lines
|
||||
canvas_draw_line(canvas, 61, 16, 61, 50);
|
||||
canvas_draw_line(canvas, 62, 16, 62, 50);
|
||||
|
||||
// Draw horizontal line
|
||||
canvas_draw_line(canvas, 2, 27, 122, 27);
|
||||
|
||||
// Draw temperature and humidity values
|
||||
canvas_draw_str(canvas, 8, 38, ts_data_buffer_temperature_c);
|
||||
canvas_draw_str(canvas, 42, 38, "C");
|
||||
canvas_draw_str(canvas, 8, 48, ts_data_buffer_temperature_f);
|
||||
canvas_draw_str(canvas, 42, 48, "F");
|
||||
canvas_draw_str(canvas, 68, 38, ts_data_buffer_relative_humidity);
|
||||
canvas_draw_str(canvas, 100, 38, "%");
|
||||
canvas_draw_str(canvas, 68, 48, ts_data_buffer_absolute_humidity);
|
||||
canvas_draw_str(canvas, 100, 48, "g/m3");
|
||||
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Input callback
|
||||
|
||||
static void temperature_sensor_input_callback(InputEvent* input_event, void* ctx) {
|
||||
furi_assert(ctx);
|
||||
FuriMessageQueue* event_queue = ctx;
|
||||
|
||||
TSEvent event = {.type = TSEventTypeInput, .input = *input_event};
|
||||
furi_message_queue_put(event_queue, &event, FuriWaitForever);
|
||||
}
|
||||
|
||||
// Timer callback
|
||||
|
||||
static void temperature_sensor_timer_callback(FuriMessageQueue* event_queue) {
|
||||
furi_assert(event_queue);
|
||||
|
||||
TSEvent event = {.type = TSEventTypeTick};
|
||||
furi_message_queue_put(event_queue, &event, 0);
|
||||
}
|
||||
|
||||
// App entry point
|
||||
|
||||
int32_t am_temperature_sensor_app(void* p) {
|
||||
UNUSED(p);
|
||||
|
||||
furi_hal_power_suppress_charge_enter();
|
||||
// Declare our variables and assign variables a default value
|
||||
TSEvent tsEvent;
|
||||
bool sensorFound = false;
|
||||
double celsius, fahrenheit, rel_humidity, abs_humidity = TS_DEFAULT_VALUE;
|
||||
|
||||
// Used for absolute humidity calculation
|
||||
double vapour_pressure = 0;
|
||||
|
||||
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(TSEvent));
|
||||
|
||||
// Register callbacks
|
||||
ViewPort* view_port = view_port_alloc();
|
||||
view_port_draw_callback_set(view_port, temperature_sensor_draw_callback, NULL);
|
||||
view_port_input_callback_set(view_port, temperature_sensor_input_callback, event_queue);
|
||||
|
||||
// Create timer and register its callback
|
||||
FuriTimer* timer =
|
||||
furi_timer_alloc(temperature_sensor_timer_callback, FuriTimerTypePeriodic, event_queue);
|
||||
furi_timer_start(timer, furi_kernel_get_tick_frequency());
|
||||
|
||||
// Register viewport
|
||||
Gui* gui = furi_record_open(RECORD_GUI);
|
||||
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
|
||||
|
||||
// Used to notify the user by blinking red (error) or blue (fetch successful)
|
||||
NotificationApp* notifications = furi_record_open(RECORD_NOTIFICATION);
|
||||
|
||||
while(1) {
|
||||
furi_check(furi_message_queue_get(event_queue, &tsEvent, FuriWaitForever) == FuriStatusOk);
|
||||
|
||||
// Handle events
|
||||
if(tsEvent.type == TSEventTypeInput) {
|
||||
// Exit on back key
|
||||
if(tsEvent.input.key ==
|
||||
InputKeyBack) // We dont check for type here, we can check the type of keypress like: (event.input.type == InputTypeShort)
|
||||
break;
|
||||
|
||||
} else if(tsEvent.type == TSEventTypeTick) {
|
||||
// Update sensor data
|
||||
// Fetch data and set the sensor current status accordingly
|
||||
sensorFound = temperature_sensor_fetch_info(&celsius, &rel_humidity);
|
||||
temperature_sensor_current_status = (sensorFound ? TSSPendingUpdate : TSSNoSensor);
|
||||
|
||||
if(sensorFound) {
|
||||
// Blink blue
|
||||
notification_message(notifications, &sequence_blink_blue_100);
|
||||
|
||||
if(celsius != TS_DEFAULT_VALUE && rel_humidity != TS_DEFAULT_VALUE) {
|
||||
// Convert celsius to fahrenheit
|
||||
fahrenheit = (celsius * 9 / 5) + 32;
|
||||
|
||||
// Calculate absolute humidity - For more info refer to https://github.com/Mywk/FlipperTemperatureSensor/issues/1
|
||||
// Calculate saturation vapour pressure first
|
||||
vapour_pressure =
|
||||
(double)6.11 *
|
||||
pow(10, (double)(((double)7.5 * celsius) / ((double)237.3 + celsius)));
|
||||
// Then the vapour pressure in Pa
|
||||
vapour_pressure = vapour_pressure * rel_humidity;
|
||||
// Calculate absolute humidity
|
||||
abs_humidity =
|
||||
(double)2.16679 * (double)(vapour_pressure / ((double)273.15 + celsius));
|
||||
|
||||
// Fill our buffers here, not on the canvas draw callback
|
||||
snprintf(ts_data_buffer_temperature_c, DATA_BUFFER_SIZE, "%.2f", celsius);
|
||||
snprintf(ts_data_buffer_temperature_f, DATA_BUFFER_SIZE, "%.2f", fahrenheit);
|
||||
snprintf(
|
||||
ts_data_buffer_relative_humidity, DATA_BUFFER_SIZE, "%.2f", rel_humidity);
|
||||
snprintf(
|
||||
ts_data_buffer_absolute_humidity, DATA_BUFFER_SIZE, "%.2f", abs_humidity);
|
||||
}
|
||||
|
||||
} else {
|
||||
// Reset our variables to their default values
|
||||
celsius = fahrenheit = rel_humidity = abs_humidity = TS_DEFAULT_VALUE;
|
||||
|
||||
// Blink red
|
||||
notification_message(notifications, &sequence_blink_red_100);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t wait_ticks = furi_ms_to_ticks(!sensorFound ? 100 : 500);
|
||||
furi_delay_tick(wait_ticks);
|
||||
}
|
||||
|
||||
furi_hal_power_suppress_charge_exit();
|
||||
// Dobby is freee (free our variables, Flipper will crash if we don't do this!)
|
||||
furi_timer_free(timer);
|
||||
gui_remove_view_port(gui, view_port);
|
||||
view_port_free(view_port);
|
||||
furi_message_queue_free(event_queue);
|
||||
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
furi_record_close(RECORD_GUI);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
After Width: | Height: | Size: 181 B |
@@ -0,0 +1,674 @@
|
||||
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>.
|
||||
@@ -0,0 +1,14 @@
|
||||
# BPM Tapper
|
||||
|
||||
A BPM Tapper for the Flipper Zero.
|
||||
|
||||

|
||||
|
||||
Hit any button other than back repeatedly. Calculates based on the average of the last 8 inputs.
|
||||
|
||||
## Compiling
|
||||
|
||||
```
|
||||
./fbt firmware_bpm_tapper
|
||||
```
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
App(
|
||||
appid="BPM_Tapper",
|
||||
name="BPM Tapper",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="bpm_tapper_app",
|
||||
cdefines=["APP_BPM_TAPPER"],
|
||||
requires=["gui"],
|
||||
stack_size=2 * 1024,
|
||||
fap_icon="bpm_10px.png",
|
||||
fap_category="Music",
|
||||
fap_icon_assets="icons",
|
||||
order=15,
|
||||
)
|
||||
@@ -0,0 +1,262 @@
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <dialogs/dialogs.h>
|
||||
#include <gui/gui.h>
|
||||
#include <input/input.h>
|
||||
#include <stdlib.h>
|
||||
#include "BPM_Tapper_icons.h"
|
||||
|
||||
typedef enum {
|
||||
EventTypeTick,
|
||||
EventTypeKey,
|
||||
} EventType;
|
||||
|
||||
typedef struct {
|
||||
EventType type;
|
||||
InputEvent input;
|
||||
} PluginEvent;
|
||||
|
||||
//QUEUE
|
||||
|
||||
struct node {
|
||||
int interval;
|
||||
struct node* next;
|
||||
};
|
||||
typedef struct node node;
|
||||
|
||||
typedef struct {
|
||||
int size;
|
||||
int max_size;
|
||||
node* front;
|
||||
node* rear;
|
||||
} queue;
|
||||
|
||||
static void init_queue(queue* q) {
|
||||
q->size = 0;
|
||||
q->max_size = 8;
|
||||
q->front = NULL;
|
||||
q->rear = NULL;
|
||||
}
|
||||
|
||||
static void queue_remove(queue* q) {
|
||||
node* tmp;
|
||||
tmp = q->front;
|
||||
q->front = q->front->next;
|
||||
q->size--;
|
||||
free(tmp);
|
||||
}
|
||||
|
||||
static void queue_add(queue* q, int value) {
|
||||
node* tmp = malloc(sizeof(node));
|
||||
tmp->interval = value;
|
||||
tmp->next = NULL;
|
||||
if(q->size == q->max_size) {
|
||||
queue_remove(q);
|
||||
}
|
||||
// check if empty
|
||||
if(q->rear == NULL) {
|
||||
q->front = tmp;
|
||||
q->rear = tmp;
|
||||
} else {
|
||||
q->rear->next = tmp;
|
||||
q->rear = tmp;
|
||||
}
|
||||
q->size++;
|
||||
}
|
||||
|
||||
static float queue_avg(queue* q) {
|
||||
float avg = 0.0;
|
||||
if(q->size == 0) {
|
||||
return avg;
|
||||
} else {
|
||||
node* tmp;
|
||||
float sum = 0.0;
|
||||
tmp = q->front;
|
||||
while(tmp != NULL) {
|
||||
sum = sum + tmp->interval;
|
||||
tmp = tmp->next;
|
||||
}
|
||||
avg = sum / q->size;
|
||||
FURI_LOG_D("BPM-Tapper", "Sum: %.2f Avg: %.2f", (double)sum, (double)avg);
|
||||
return avg;
|
||||
}
|
||||
}
|
||||
|
||||
// TOO SLOW!
|
||||
//uint64_t dolphin_state_timestamp() {
|
||||
// FuriHalRtcDateTime datetime;
|
||||
// furi_hal_rtc_get_datetime(&datetime);
|
||||
// return furi_hal_rtc_datetime_to_timestamp(&datetime);
|
||||
//}
|
||||
//
|
||||
typedef struct {
|
||||
int taps;
|
||||
double bpm;
|
||||
uint32_t last_stamp;
|
||||
uint32_t interval;
|
||||
queue* tap_queue;
|
||||
} BPMTapper;
|
||||
|
||||
static void show_hello() {
|
||||
// BEGIN HELLO DIALOG
|
||||
DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);
|
||||
DialogMessage* message = dialog_message_alloc();
|
||||
|
||||
const char* header_text = "BPM Tapper";
|
||||
const char* message_text = "Tap center to start";
|
||||
|
||||
dialog_message_set_header(message, header_text, 63, 3, AlignCenter, AlignTop);
|
||||
dialog_message_set_text(message, message_text, 0, 17, AlignLeft, AlignTop);
|
||||
dialog_message_set_buttons(message, NULL, "Tap", NULL);
|
||||
|
||||
dialog_message_set_icon(message, &I_DolphinCommon_56x48, 72, 17);
|
||||
|
||||
dialog_message_show(dialogs, message);
|
||||
|
||||
dialog_message_free(message);
|
||||
furi_record_close(RECORD_DIALOGS);
|
||||
// END HELLO DIALOG
|
||||
}
|
||||
|
||||
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 render_callback(Canvas* const canvas, void* ctx) {
|
||||
FuriString* tempStr;
|
||||
|
||||
const BPMTapper* bpm_state = acquire_mutex((ValueMutex*)ctx, 25);
|
||||
if(bpm_state == NULL) {
|
||||
return;
|
||||
}
|
||||
// border
|
||||
//canvas_draw_frame(canvas, 0, 0, 128, 64);
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
|
||||
tempStr = furi_string_alloc();
|
||||
|
||||
furi_string_printf(tempStr, "Taps: %d", bpm_state->taps);
|
||||
canvas_draw_str_aligned(canvas, 5, 10, AlignLeft, AlignBottom, furi_string_get_cstr(tempStr));
|
||||
furi_string_reset(tempStr);
|
||||
|
||||
furi_string_printf(tempStr, "Queue: %d", bpm_state->tap_queue->size);
|
||||
canvas_draw_str_aligned(canvas, 70, 10, AlignLeft, AlignBottom, furi_string_get_cstr(tempStr));
|
||||
furi_string_reset(tempStr);
|
||||
|
||||
furi_string_printf(tempStr, "Interval: %ldms", bpm_state->interval);
|
||||
canvas_draw_str_aligned(canvas, 5, 20, AlignLeft, AlignBottom, furi_string_get_cstr(tempStr));
|
||||
furi_string_reset(tempStr);
|
||||
|
||||
furi_string_printf(tempStr, "x2 %.2f /2 %.2f", bpm_state->bpm * 2, bpm_state->bpm / 2);
|
||||
canvas_draw_str_aligned(
|
||||
canvas, 64, 60, AlignCenter, AlignCenter, furi_string_get_cstr(tempStr));
|
||||
furi_string_reset(tempStr);
|
||||
|
||||
furi_string_printf(tempStr, "%.2f", bpm_state->bpm);
|
||||
canvas_set_font(canvas, FontBigNumbers);
|
||||
canvas_draw_str_aligned(
|
||||
canvas, 64, 40, AlignCenter, AlignCenter, furi_string_get_cstr(tempStr));
|
||||
furi_string_reset(tempStr);
|
||||
|
||||
furi_string_free(tempStr);
|
||||
|
||||
release_mutex((ValueMutex*)ctx, bpm_state);
|
||||
}
|
||||
|
||||
static void bpm_state_init(BPMTapper* const plugin_state) {
|
||||
plugin_state->taps = 0;
|
||||
plugin_state->bpm = 120.0;
|
||||
plugin_state->last_stamp = 0; // furi_get_tick();
|
||||
plugin_state->interval = 0;
|
||||
queue* q;
|
||||
q = malloc(sizeof(queue));
|
||||
init_queue(q);
|
||||
plugin_state->tap_queue = q;
|
||||
}
|
||||
|
||||
int32_t bpm_tapper_app(void* p) {
|
||||
UNUSED(p);
|
||||
|
||||
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent));
|
||||
|
||||
BPMTapper* bpm_state = malloc(sizeof(BPMTapper));
|
||||
// setup
|
||||
bpm_state_init(bpm_state);
|
||||
|
||||
ValueMutex state_mutex;
|
||||
if(!init_mutex(&state_mutex, bpm_state, sizeof(bpm_state))) {
|
||||
FURI_LOG_E("BPM-Tapper", "cannot create mutex\r\n");
|
||||
free(bpm_state);
|
||||
return 255;
|
||||
}
|
||||
show_hello();
|
||||
|
||||
// BEGIN IMPLEMENTATION
|
||||
|
||||
// Set system callbacks
|
||||
ViewPort* view_port = view_port_alloc();
|
||||
view_port_draw_callback_set(view_port, render_callback, &state_mutex);
|
||||
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);
|
||||
BPMTapper* bpm_state = (BPMTapper*)acquire_mutex_block(&state_mutex);
|
||||
if(event_status == FuriStatusOk) {
|
||||
// press events
|
||||
if(event.type == EventTypeKey) {
|
||||
if(event.input.type == InputTypePress) {
|
||||
switch(event.input.key) {
|
||||
case InputKeyUp:
|
||||
case InputKeyDown:
|
||||
case InputKeyRight:
|
||||
case InputKeyLeft:
|
||||
case InputKeyOk:
|
||||
bpm_state->taps++;
|
||||
uint32_t new_stamp = furi_get_tick();
|
||||
if(bpm_state->last_stamp == 0) {
|
||||
bpm_state->last_stamp = new_stamp;
|
||||
break;
|
||||
}
|
||||
bpm_state->interval = new_stamp - bpm_state->last_stamp;
|
||||
bpm_state->last_stamp = new_stamp;
|
||||
queue_add(bpm_state->tap_queue, bpm_state->interval);
|
||||
float avg = queue_avg(bpm_state->tap_queue);
|
||||
float bps = 1.0 / (avg / 1000.0);
|
||||
bpm_state->bpm = bps * 60.0;
|
||||
break;
|
||||
case InputKeyBack:
|
||||
// Exit the plugin
|
||||
processing = false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
FURI_LOG_D("BPM-Tapper", "FuriMessageQueue: event timeout");
|
||||
// event timeout
|
||||
}
|
||||
view_port_update(view_port);
|
||||
release_mutex(&state_mutex, bpm_state);
|
||||
}
|
||||
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);
|
||||
delete_mutex(&state_mutex);
|
||||
queue* q = bpm_state->tap_queue;
|
||||
free(q);
|
||||
free(bpm_state);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
After Width: | Height: | Size: 181 B |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
@@ -0,0 +1,169 @@
|
||||
#include "DHT.h"
|
||||
|
||||
#define lineDown() furi_hal_gpio_write(sensor->GPIO, false)
|
||||
#define lineUp() furi_hal_gpio_write(sensor->GPIO, true)
|
||||
#define getLine() furi_hal_gpio_read(sensor->GPIO)
|
||||
#define Delay(d) furi_delay_ms(d)
|
||||
|
||||
DHT_data DHT_getData(DHT_sensor* sensor) {
|
||||
DHT_data data = {-128.0f, -128.0f};
|
||||
|
||||
#if DHT_POLLING_CONTROL == 1
|
||||
/* Ограничение по частоте опроса датчика */
|
||||
//Определение интервала опроса в зависимости от датчика
|
||||
uint16_t pollingInterval;
|
||||
if(sensor->type == DHT11) {
|
||||
pollingInterval = DHT_POLLING_INTERVAL_DHT11;
|
||||
} else {
|
||||
pollingInterval = DHT_POLLING_INTERVAL_DHT22;
|
||||
}
|
||||
|
||||
//Если интервал маленький, то возврат последнего удачного значения
|
||||
if((furi_get_tick() - sensor->lastPollingTime < pollingInterval) &&
|
||||
sensor->lastPollingTime != 0) {
|
||||
data.hum = sensor->lastHum;
|
||||
data.temp = sensor->lastTemp;
|
||||
return data;
|
||||
}
|
||||
sensor->lastPollingTime = furi_get_tick() + 1;
|
||||
#endif
|
||||
|
||||
//Опускание линии данных на 18 мс
|
||||
lineDown();
|
||||
#ifdef DHT_IRQ_CONTROL
|
||||
//Выключение прерываний, чтобы ничто не мешало обработке данных
|
||||
__disable_irq();
|
||||
#endif
|
||||
Delay(18);
|
||||
|
||||
//Подъём линии
|
||||
lineUp();
|
||||
|
||||
/* Ожидание ответа от датчика */
|
||||
uint16_t timeout = 0;
|
||||
while(!getLine()) {
|
||||
timeout++;
|
||||
if(timeout > DHT_TIMEOUT) {
|
||||
#ifdef DHT_IRQ_CONTROL
|
||||
__enable_irq();
|
||||
#endif
|
||||
//Если датчик не отозвался, значит его точно нет
|
||||
//Обнуление последнего удачного значения, чтобы
|
||||
//не получать фантомные значения
|
||||
sensor->lastHum = -128.0f;
|
||||
sensor->lastTemp = -128.0f;
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
//Ожидание спада
|
||||
while(getLine()) {
|
||||
timeout++;
|
||||
if(timeout > DHT_TIMEOUT) {
|
||||
#ifdef DHT_IRQ_CONTROL
|
||||
__enable_irq();
|
||||
#endif
|
||||
//Если датчик не отозвался, значит его точно нет
|
||||
//Обнуление последнего удачного значения, чтобы
|
||||
//не получать фантомные значения
|
||||
sensor->lastHum = -128.0f;
|
||||
sensor->lastTemp = -128.0f;
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
timeout = 0;
|
||||
//Ожидание подъёма
|
||||
while(!getLine()) {
|
||||
timeout++;
|
||||
if(timeout > DHT_TIMEOUT) {
|
||||
if(timeout > DHT_TIMEOUT) {
|
||||
#ifdef DHT_IRQ_CONTROL
|
||||
__enable_irq();
|
||||
#endif
|
||||
//Если датчик не отозвался, значит его точно нет
|
||||
//Обнуление последнего удачного значения, чтобы
|
||||
//не получать фантомные значения
|
||||
sensor->lastHum = -128.0f;
|
||||
sensor->lastTemp = -128.0f;
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
}
|
||||
timeout = 0;
|
||||
//Ожидание спада
|
||||
while(getLine()) {
|
||||
timeout++;
|
||||
if(timeout > DHT_TIMEOUT) {
|
||||
#ifdef DHT_IRQ_CONTROL
|
||||
__enable_irq();
|
||||
#endif
|
||||
//Если датчик не отозвался, значит его точно нет
|
||||
//Обнуление последнего удачного значения, чтобы
|
||||
//не получать фантомные значения
|
||||
sensor->lastHum = -128.0f;
|
||||
sensor->lastTemp = -128.0f;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
/* Чтение ответа от датчика */
|
||||
uint8_t rawData[5] = {0, 0, 0, 0, 0};
|
||||
for(uint8_t a = 0; a < 5; a++) {
|
||||
for(uint8_t b = 7; b != 255; b--) {
|
||||
uint16_t hT = 0, lT = 0;
|
||||
//Пока линия в низком уровне, инкремент переменной lT
|
||||
while(!getLine() && lT != 65535) lT++;
|
||||
//Пока линия в высоком уровне, инкремент переменной hT
|
||||
timeout = 0;
|
||||
while(getLine() && hT != 65535) hT++;
|
||||
//Если hT больше lT, то пришла единица
|
||||
if(hT > lT) rawData[a] |= (1 << b);
|
||||
}
|
||||
}
|
||||
#ifdef DHT_IRQ_CONTROL
|
||||
//Включение прерываний после приёма данных
|
||||
__enable_irq();
|
||||
#endif
|
||||
/* Проверка целостности данных */
|
||||
if((uint8_t)(rawData[0] + rawData[1] + rawData[2] + rawData[3]) == rawData[4]) {
|
||||
//Если контрольная сумма совпадает, то конвертация и возврат полученных значений
|
||||
if(sensor->type == DHT22) {
|
||||
data.hum = (float)(((uint16_t)rawData[0] << 8) | rawData[1]) * 0.1f;
|
||||
//Проверка на отрицательность температуры
|
||||
if(!(rawData[2] & (1 << 7))) {
|
||||
data.temp = (float)(((uint16_t)rawData[2] << 8) | rawData[3]) * 0.1f;
|
||||
} else {
|
||||
rawData[2] &= ~(1 << 7);
|
||||
data.temp = (float)(((uint16_t)rawData[2] << 8) | rawData[3]) * -0.1f;
|
||||
}
|
||||
}
|
||||
if(sensor->type == DHT11) {
|
||||
data.hum = (float)rawData[0];
|
||||
data.temp = (float)rawData[2];
|
||||
//DHT11 производства ASAIR имеют дробную часть в температуре
|
||||
//А ещё температуру измеряет от -20 до +60 *С
|
||||
//Вот прикол, да?
|
||||
if(rawData[3] != 0) {
|
||||
//Проверка знака
|
||||
if(!(rawData[3] & (1 << 7))) {
|
||||
//Добавление положительной дробной части
|
||||
data.temp += rawData[3] * 0.1f;
|
||||
} else {
|
||||
//А тут делаем отрицательное значение
|
||||
rawData[3] &= ~(1 << 7);
|
||||
data.temp += rawData[3] * 0.1f;
|
||||
data.temp *= -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if DHT_POLLING_CONTROL == 1
|
||||
sensor->lastHum = data.hum;
|
||||
sensor->lastTemp = data.temp;
|
||||
#endif
|
||||
|
||||
return data;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
#ifndef DHT_H_
|
||||
#define DHT_H_
|
||||
|
||||
#include <furi_hal_resources.h>
|
||||
|
||||
/* Настройки */
|
||||
#define DHT_TIMEOUT 65534 //Количество итераций, после которых функция вернёт пустые значения
|
||||
#define DHT_POLLING_CONTROL 1 //Включение проверки частоты опроса датчика
|
||||
#define DHT_POLLING_INTERVAL_DHT11 \
|
||||
2000 //Интервал опроса DHT11 (0.5 Гц по даташиту). Можно поставить 1500, будет работать
|
||||
//Костыль, временно 2 секунды для датчика AM2302
|
||||
#define DHT_POLLING_INTERVAL_DHT22 2000 //Интервал опроса DHT22 (1 Гц по даташиту)
|
||||
#define DHT_IRQ_CONTROL //Выключать прерывания во время обмена данных с датчиком
|
||||
/* Структура возвращаемых датчиком данных */
|
||||
typedef struct {
|
||||
float hum;
|
||||
float temp;
|
||||
} DHT_data;
|
||||
|
||||
/* Тип используемого датчика */
|
||||
typedef enum { DHT11, DHT22 } DHT_type;
|
||||
|
||||
/* Структура объекта датчика */
|
||||
typedef struct {
|
||||
char name[11];
|
||||
const GpioPin* GPIO; //Пин датчика
|
||||
DHT_type type; //Тип датчика (DHT11 или DHT22)
|
||||
|
||||
//Контроль частоты опроса датчика. Значения не заполнять!
|
||||
#if DHT_POLLING_CONTROL == 1
|
||||
uint32_t lastPollingTime; //Время последнего опроса датчика
|
||||
float lastTemp; //Последнее значение температуры
|
||||
float lastHum; //Последнее значение влажности
|
||||
#endif
|
||||
} DHT_sensor;
|
||||
|
||||
/* Прототипы функций */
|
||||
DHT_data DHT_getData(DHT_sensor* sensor); //Получить данные с датчика
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,13 @@
|
||||
App(
|
||||
appid="DHT_Monitor",
|
||||
name="[DHT] Temp. Monitor",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="quenon_dht_mon_app",
|
||||
cdefines=["QUENON_DHT_MON"],
|
||||
requires=[
|
||||
"gui",
|
||||
],
|
||||
fap_category="GPIO",
|
||||
fap_icon="icon.png",
|
||||
stack_size=2 * 1024,
|
||||
)
|
||||
|
After Width: | Height: | Size: 136 B |
@@ -0,0 +1,469 @@
|
||||
#include "quenon_dht_mon.h"
|
||||
#include <m-string.h>
|
||||
|
||||
//Порты ввода/вывода, которые не были обозначены в общем списке
|
||||
const GpioPin SWC_10 = {.pin = LL_GPIO_PIN_14, .port = GPIOA};
|
||||
const GpioPin SIO_12 = {.pin = LL_GPIO_PIN_13, .port = GPIOA};
|
||||
const GpioPin TX_13 = {.pin = LL_GPIO_PIN_6, .port = GPIOB};
|
||||
const GpioPin RX_14 = {.pin = LL_GPIO_PIN_7, .port = GPIOB};
|
||||
|
||||
//Количество доступных портов ввода/вывода
|
||||
#define GPIO_ITEMS (sizeof(gpio_item) / sizeof(GpioItem))
|
||||
|
||||
//Перечень достуных портов ввода/вывода
|
||||
static const GpioItem gpio_item[] = {
|
||||
{2, "2 (A7)", &gpio_ext_pa7},
|
||||
{3, "3 (A6)", &gpio_ext_pa6},
|
||||
{4, "4 (A4)", &gpio_ext_pa4},
|
||||
{5, "5 (B3)", &gpio_ext_pb3},
|
||||
{6, "6 (B2)", &gpio_ext_pb2},
|
||||
{7, "7 (C3)", &gpio_ext_pc3},
|
||||
{10, " 10(SWC) ", &SWC_10},
|
||||
{12, "12 (SIO)", &SIO_12},
|
||||
{13, "13 (TX)", &TX_13},
|
||||
{14, "14 (RX)", &RX_14},
|
||||
{15, "15 (C1)", &gpio_ext_pc1},
|
||||
{16, "16 (C0)", &gpio_ext_pc0},
|
||||
{17, "17 (1W)", &ibutton_gpio}};
|
||||
|
||||
//Данные плагина
|
||||
static PluginData* app;
|
||||
|
||||
uint8_t DHTMon_GPIO_to_int(const GpioPin* gpio) {
|
||||
if(gpio == NULL) return 255;
|
||||
for(uint8_t i = 0; i < GPIO_ITEMS; i++) {
|
||||
if(gpio_item[i].pin->pin == gpio->pin && gpio_item[i].pin->port == gpio->port) {
|
||||
return gpio_item[i].num;
|
||||
}
|
||||
}
|
||||
return 255;
|
||||
}
|
||||
|
||||
const GpioPin* DHTMon_GPIO_form_int(uint8_t name) {
|
||||
for(uint8_t i = 0; i < GPIO_ITEMS; i++) {
|
||||
if(gpio_item[i].num == name) {
|
||||
return gpio_item[i].pin;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const GpioPin* DHTMon_GPIO_from_index(uint8_t index) {
|
||||
if(index > GPIO_ITEMS) return NULL;
|
||||
return gpio_item[index].pin;
|
||||
}
|
||||
|
||||
uint8_t DHTMon_GPIO_to_index(const GpioPin* gpio) {
|
||||
if(gpio == NULL) return 255;
|
||||
for(uint8_t i = 0; i < GPIO_ITEMS; i++) {
|
||||
if(gpio_item[i].pin->pin == gpio->pin && gpio_item[i].pin->port == gpio->port) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return 255;
|
||||
}
|
||||
|
||||
const char* DHTMon_GPIO_getName(const GpioPin* gpio) {
|
||||
if(gpio == NULL) return NULL;
|
||||
for(uint8_t i = 0; i < GPIO_ITEMS; i++) {
|
||||
if(gpio_item[i].pin->pin == gpio->pin && gpio_item[i].pin->port == gpio->port) {
|
||||
return gpio_item[i].name;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void DHTMon_sensors_init(void) {
|
||||
//Включение 5V если на порту 1 FZ его нет
|
||||
if(furi_hal_power_is_otg_enabled() != true) {
|
||||
furi_hal_power_enable_otg();
|
||||
}
|
||||
|
||||
//Настройка GPIO загруженных датчиков
|
||||
for(uint8_t i = 0; i < app->sensors_count; i++) {
|
||||
//Высокий уровень по умолчанию
|
||||
furi_hal_gpio_write(app->sensors[i].GPIO, true);
|
||||
//Режим работы - OpenDrain, подтяжка включается на всякий случай
|
||||
furi_hal_gpio_init(
|
||||
app->sensors[i].GPIO, //Порт FZ
|
||||
GpioModeOutputOpenDrain, //Режим работы - открытый сток
|
||||
GpioPullUp, //Принудительная подтяжка линии данных к питанию
|
||||
GpioSpeedVeryHigh); //Скорость работы - максимальная
|
||||
}
|
||||
}
|
||||
|
||||
void DHTMon_sensors_deinit(void) {
|
||||
//Возврат исходного состояния 5V
|
||||
if(app->last_OTG_State != true) {
|
||||
furi_hal_power_disable_otg();
|
||||
}
|
||||
|
||||
//Перевод портов GPIO в состояние по умолчанию
|
||||
for(uint8_t i = 0; i < app->sensors_count; i++) {
|
||||
furi_hal_gpio_init(
|
||||
app->sensors[i].GPIO, //Порт FZ
|
||||
GpioModeAnalog, //Режим работы - аналог
|
||||
GpioPullNo, //Отключение подтяжки
|
||||
GpioSpeedLow); //Скорость работы - низкая
|
||||
//Установка низкого уровня
|
||||
furi_hal_gpio_write(app->sensors[i].GPIO, false);
|
||||
}
|
||||
}
|
||||
|
||||
bool DHTMon_sensor_check(DHT_sensor* sensor) {
|
||||
/* Проверка имени */
|
||||
//1) Строка должна быть длиной от 1 до 10 символов
|
||||
//2) Первый символ строки должен быть только 0-9, A-Z, a-z и _
|
||||
if(strlen(sensor->name) == 0 || strlen(sensor->name) > 10 ||
|
||||
(!(sensor->name[0] >= '0' && sensor->name[0] <= '9') &&
|
||||
!(sensor->name[0] >= 'A' && sensor->name[0] <= 'Z') &&
|
||||
!(sensor->name[0] >= 'a' && sensor->name[0] <= 'z') && !(sensor->name[0] == '_'))) {
|
||||
FURI_LOG_D(APP_NAME, "Sensor [%s] name check failed\r\n", sensor->name);
|
||||
return false;
|
||||
}
|
||||
//Проверка GPIO
|
||||
if(DHTMon_GPIO_to_int(sensor->GPIO) == 255) {
|
||||
FURI_LOG_D(
|
||||
APP_NAME,
|
||||
"Sensor [%s] GPIO check failed: %d\r\n",
|
||||
sensor->name,
|
||||
DHTMon_GPIO_to_int(sensor->GPIO));
|
||||
return false;
|
||||
}
|
||||
//Проверка типа датчика
|
||||
if(sensor->type != DHT11 && sensor->type != DHT22) {
|
||||
FURI_LOG_D(APP_NAME, "Sensor [%s] type check failed: %d\r\n", sensor->name, sensor->type);
|
||||
return false;
|
||||
}
|
||||
|
||||
//Возврат истины если всё ок
|
||||
FURI_LOG_D(APP_NAME, "Sensor [%s] all checks passed\r\n", sensor->name);
|
||||
return true;
|
||||
}
|
||||
|
||||
void DHTMon_sensor_delete(DHT_sensor* sensor) {
|
||||
if(sensor == NULL) return;
|
||||
//Делаем параметры датчика неверными
|
||||
sensor->name[0] = '\0';
|
||||
sensor->type = 255;
|
||||
//Теперь сохраняем текущие датчики. Сохранятор не сохранит неисправный датчик
|
||||
DHTMon_sensors_save();
|
||||
//Перезагружаемся с SD-карты
|
||||
DHTMon_sensors_reload();
|
||||
}
|
||||
|
||||
uint8_t DHTMon_sensors_save(void) {
|
||||
//Выделение памяти для потока
|
||||
app->file_stream = file_stream_alloc(app->storage);
|
||||
uint8_t savedSensorsCount = 0;
|
||||
//Переменная пути к файлу
|
||||
FuriString* filepath = furi_string_alloc();
|
||||
//Составление пути к файлу
|
||||
furi_string_printf(filepath, "%s/%s", APP_PATH_FOLDER, APP_FILENAME);
|
||||
|
||||
//Открытие потока. Если поток открылся, то выполнение сохранения датчиков
|
||||
if(file_stream_open(
|
||||
app->file_stream, furi_string_get_cstr(filepath), FSAM_READ_WRITE, FSOM_CREATE_ALWAYS)) {
|
||||
const char template[] =
|
||||
"#DHT monitor sensors file\n#Name - name of sensor. Up to 10 sumbols\n#Type - type of sensor. DHT11 - 0, DHT22 - 1\n#GPIO - connection port. May being 2-7, 10, 12-17\n#Name Type GPIO\n";
|
||||
stream_write(app->file_stream, (uint8_t*)template, strlen(template));
|
||||
//Сохранение датчиков
|
||||
for(uint8_t i = 0; i < app->sensors_count; i++) {
|
||||
//Если параметры датчика верны, то сохраняемся
|
||||
if(DHTMon_sensor_check(&app->sensors[i])) {
|
||||
stream_write_format(
|
||||
app->file_stream,
|
||||
"%s %d %d\n",
|
||||
app->sensors[i].name,
|
||||
app->sensors[i].type,
|
||||
DHTMon_GPIO_to_int(app->sensors[i].GPIO));
|
||||
savedSensorsCount++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//TODO: печать ошибки на экран
|
||||
FURI_LOG_E(APP_NAME, "cannot create sensors file\r\n");
|
||||
}
|
||||
stream_free(app->file_stream);
|
||||
|
||||
return savedSensorsCount;
|
||||
}
|
||||
|
||||
bool DHTMon_sensors_load(void) {
|
||||
//Обнуление количества датчиков
|
||||
app->sensors_count = -1;
|
||||
//Очистка предыдущих датчиков
|
||||
memset(app->sensors, 0, sizeof(app->sensors));
|
||||
|
||||
//Открытие файла на SD-карте
|
||||
//Выделение памяти для потока
|
||||
app->file_stream = file_stream_alloc(app->storage);
|
||||
//Переменная пути к файлу
|
||||
FuriString* filepath = furi_string_alloc();
|
||||
//Составление пути к файлу
|
||||
furi_string_printf(filepath, "%s/%s", APP_PATH_FOLDER, APP_FILENAME);
|
||||
//Открытие потока к файлу
|
||||
if(!file_stream_open(
|
||||
app->file_stream, furi_string_get_cstr(filepath), FSAM_READ_WRITE, FSOM_OPEN_EXISTING)) {
|
||||
//Если файл отсутствует, то создание болванки
|
||||
FURI_LOG_W(APP_NAME, "Missing sensors file. Creating new file\r\n");
|
||||
app->sensors_count = 0;
|
||||
stream_free(app->file_stream);
|
||||
DHTMon_sensors_save();
|
||||
return false;
|
||||
}
|
||||
//Вычисление размера файла
|
||||
size_t file_size = stream_size(app->file_stream);
|
||||
if(file_size == (size_t)0) {
|
||||
//Выход если файл пустой
|
||||
FURI_LOG_W(APP_NAME, "Sensors file is empty\r\n");
|
||||
app->sensors_count = 0;
|
||||
stream_free(app->file_stream);
|
||||
return false;
|
||||
}
|
||||
|
||||
//Выделение памяти под загрузку файла
|
||||
uint8_t* file_buf = malloc(file_size);
|
||||
//Опустошение буфера файла
|
||||
memset(file_buf, 0, file_size);
|
||||
//Загрузка файла
|
||||
if(stream_read(app->file_stream, file_buf, file_size) != file_size) {
|
||||
//Выход при ошибке чтения
|
||||
FURI_LOG_E(APP_NAME, "Error reading sensor file\r\n");
|
||||
app->sensors_count = 0;
|
||||
stream_free(app->file_stream);
|
||||
return false;
|
||||
}
|
||||
//Построчное чтение файла
|
||||
//Указатель на начало строки
|
||||
FuriString* file = furi_string_alloc_set_str((char*)file_buf);
|
||||
//Сколько байт до конца строки
|
||||
size_t line_end = 0;
|
||||
while(line_end != STRING_FAILURE && app->sensors_count < MAX_SENSORS) {
|
||||
if(((char*)(file_buf + line_end))[1] != '#') {
|
||||
DHT_sensor s = {0};
|
||||
int type, port;
|
||||
char name[11] = {0};
|
||||
sscanf(((char*)(file_buf + line_end)), "%s %d %d", name, &type, &port);
|
||||
s.type = type;
|
||||
s.GPIO = DHTMon_GPIO_form_int(port);
|
||||
|
||||
name[10] = '\0';
|
||||
strcpy(s.name, name);
|
||||
//Если данные корректны, то
|
||||
if(DHTMon_sensor_check(&s) == true) {
|
||||
//Установка нуля при первом датчике
|
||||
if(app->sensors_count == -1) app->sensors_count = 0;
|
||||
//Добавление датчика в общий список
|
||||
app->sensors[app->sensors_count] = s;
|
||||
//Увеличение количества загруженных датчиков
|
||||
app->sensors_count++;
|
||||
}
|
||||
}
|
||||
line_end = furi_string_search_char(file, '\n', line_end + 1);
|
||||
}
|
||||
stream_free(app->file_stream);
|
||||
free(file_buf);
|
||||
|
||||
//Обнуление количества датчиков если ни один из них не был загружен
|
||||
if(app->sensors_count == -1) app->sensors_count = 0;
|
||||
|
||||
//Инициализация портов датчиков если таковые есть
|
||||
if(app->sensors_count > 0) {
|
||||
DHTMon_sensors_init();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DHTMon_sensors_reload(void) {
|
||||
DHTMon_sensors_deinit();
|
||||
return DHTMon_sensors_load();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Обработчик отрисовки экрана
|
||||
*
|
||||
* @param canvas Указатель на холст
|
||||
* @param ctx Данные плагина
|
||||
*/
|
||||
static void render_callback(Canvas* const canvas, void* ctx) {
|
||||
PluginData* app = acquire_mutex((ValueMutex*)ctx, 25);
|
||||
if(app == NULL) {
|
||||
return;
|
||||
}
|
||||
//Вызов отрисовки главного экрана
|
||||
scene_main(canvas, app);
|
||||
|
||||
release_mutex((ValueMutex*)ctx, app);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Обработчик нажатия кнопок главного экрана
|
||||
*
|
||||
* @param input_event Указатель на событие
|
||||
* @param event_queue Указатель на очередь событий
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Выделение места под переменные плагина
|
||||
*
|
||||
* @return true Если всё прошло успешно
|
||||
* @return false Если в процессе загрузки произошла ошибка
|
||||
*/
|
||||
static bool DHTMon_alloc(void) {
|
||||
//Выделение места под данные плагина
|
||||
app = malloc(sizeof(PluginData));
|
||||
//Выделение места под очередь событий
|
||||
app->event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent));
|
||||
|
||||
//Обнуление количества датчиков
|
||||
app->sensors_count = -1;
|
||||
|
||||
//Инициализация мутекса
|
||||
if(!init_mutex(&app->state_mutex, app, sizeof(PluginData))) {
|
||||
FURI_LOG_E(APP_NAME, "cannot create mutex\r\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set system callbacks
|
||||
app->view_port = view_port_alloc();
|
||||
view_port_draw_callback_set(app->view_port, render_callback, &app->state_mutex);
|
||||
view_port_input_callback_set(app->view_port, input_callback, app->event_queue);
|
||||
|
||||
// Open GUI and register view_port
|
||||
app->gui = furi_record_open(RECORD_GUI);
|
||||
gui_add_view_port(app->gui, app->view_port, GuiLayerFullscreen);
|
||||
|
||||
app->view_dispatcher = view_dispatcher_alloc();
|
||||
|
||||
sensorActions_sceneCreate(app);
|
||||
sensorEdit_sceneCreate(app);
|
||||
|
||||
app->widget = widget_alloc();
|
||||
view_dispatcher_add_view(app->view_dispatcher, WIDGET_VIEW, widget_get_view(app->widget));
|
||||
|
||||
app->text_input = text_input_alloc();
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, TEXTINPUT_VIEW, text_input_get_view(app->text_input));
|
||||
|
||||
view_dispatcher_enable_queue(app->view_dispatcher);
|
||||
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
|
||||
|
||||
//Уведомления
|
||||
app->notifications = furi_record_open(RECORD_NOTIFICATION);
|
||||
|
||||
//Подготовка хранилища
|
||||
app->storage = furi_record_open(RECORD_STORAGE);
|
||||
storage_common_mkdir(app->storage, APP_PATH_FOLDER);
|
||||
app->file_stream = file_stream_alloc(app->storage);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Освыбождение памяти после работы приложения
|
||||
*/
|
||||
static void DHTMon_free(void) {
|
||||
//Автоматическое управление подсветкой
|
||||
notification_message(app->notifications, &sequence_display_backlight_enforce_auto);
|
||||
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
|
||||
text_input_free(app->text_input);
|
||||
widget_free(app->widget);
|
||||
sensorEdit_sceneRemove();
|
||||
sensorActions_screneRemove();
|
||||
view_dispatcher_free(app->view_dispatcher);
|
||||
|
||||
furi_record_close(RECORD_GUI);
|
||||
|
||||
view_port_enabled_set(app->view_port, false);
|
||||
gui_remove_view_port(app->gui, app->view_port);
|
||||
|
||||
view_port_free(app->view_port);
|
||||
furi_message_queue_free(app->event_queue);
|
||||
delete_mutex(&app->state_mutex);
|
||||
|
||||
free(app);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Точка входа в приложение
|
||||
*
|
||||
* @return Код ошибки
|
||||
*/
|
||||
int32_t quenon_dht_mon_app() {
|
||||
if(!DHTMon_alloc()) {
|
||||
DHTMon_free();
|
||||
return 255;
|
||||
}
|
||||
//Постоянное свечение подсветки
|
||||
notification_message(app->notifications, &sequence_display_backlight_enforce_on);
|
||||
//Сохранение состояния наличия 5V на порту 1 FZ
|
||||
app->last_OTG_State = furi_hal_power_is_otg_enabled();
|
||||
|
||||
//Загрузка датчиков с SD-карты
|
||||
DHTMon_sensors_load();
|
||||
|
||||
app->currentSensorEdit = &app->sensors[0];
|
||||
|
||||
PluginEvent event;
|
||||
for(bool processing = true; processing;) {
|
||||
FuriStatus event_status = furi_message_queue_get(app->event_queue, &event, 100);
|
||||
|
||||
acquire_mutex_block(&app->state_mutex);
|
||||
|
||||
if(event_status == FuriStatusOk) {
|
||||
// press events
|
||||
if(event.type == EventTypeKey) {
|
||||
if(event.input.type == InputTypePress) {
|
||||
switch(event.input.key) {
|
||||
case InputKeyUp:
|
||||
break;
|
||||
case InputKeyDown:
|
||||
break;
|
||||
case InputKeyRight:
|
||||
break;
|
||||
case InputKeyLeft:
|
||||
break;
|
||||
case InputKeyMAX:
|
||||
break;
|
||||
case InputKeyOk:
|
||||
view_port_update(app->view_port);
|
||||
release_mutex(&app->state_mutex, app);
|
||||
mainMenu_scene(app);
|
||||
break;
|
||||
case InputKeyBack:
|
||||
processing = false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
FURI_LOG_D(APP_NAME, "FuriMessageQueue: event timeout");
|
||||
// event timeout
|
||||
}
|
||||
|
||||
view_port_update(app->view_port);
|
||||
release_mutex(&app->state_mutex, app);
|
||||
}
|
||||
//Освобождение памяти и деинициализация
|
||||
DHTMon_sensors_deinit();
|
||||
DHTMon_free();
|
||||
|
||||
return 0;
|
||||
}
|
||||
//TODO: Обработка ошибок
|
||||
//TODO: Пропуск использованных портов в меню добавления датчиков
|
||||
@@ -0,0 +1,176 @@
|
||||
#ifndef QUENON_DHT_MON
|
||||
#define QUENON_DHT_MON
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <furi.h>
|
||||
#include <furi_hal_power.h>
|
||||
|
||||
#include <gui/gui.h>
|
||||
#include <gui/modules/submenu.h>
|
||||
#include <gui/modules/variable_item_list.h>
|
||||
#include <gui/modules/text_input.h>
|
||||
#include <gui/modules/widget.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
#include <gui/scene_manager.h>
|
||||
|
||||
#include <notification/notification.h>
|
||||
#include <notification/notification_messages.h>
|
||||
|
||||
#include <toolbox/stream/file_stream.h>
|
||||
#include <input/input.h>
|
||||
|
||||
#include "DHT.h"
|
||||
|
||||
#define APP_NAME "DHT_monitor"
|
||||
#define APP_PATH_FOLDER "/ext/dht_monitor"
|
||||
#define APP_FILENAME "sensors.txt"
|
||||
#define MAX_SENSORS 5
|
||||
|
||||
// //Виды менюшек
|
||||
typedef enum {
|
||||
MAIN_MENU_VIEW,
|
||||
ADDSENSOR_MENU_VIEW,
|
||||
TEXTINPUT_VIEW,
|
||||
SENSOR_ACTIONS_VIEW,
|
||||
WIDGET_VIEW,
|
||||
} MENU_VIEWS;
|
||||
|
||||
typedef enum {
|
||||
EventTypeTick,
|
||||
EventTypeKey,
|
||||
} EventType;
|
||||
|
||||
typedef struct {
|
||||
EventType type;
|
||||
InputEvent input;
|
||||
} PluginEvent;
|
||||
|
||||
typedef struct {
|
||||
const uint8_t num;
|
||||
const char* name;
|
||||
const GpioPin* pin;
|
||||
} GpioItem;
|
||||
|
||||
//Структура с данными плагина
|
||||
typedef struct {
|
||||
//Очередь сообщений
|
||||
FuriMessageQueue* event_queue;
|
||||
//Мутекс
|
||||
ValueMutex state_mutex;
|
||||
//Вьюпорт
|
||||
ViewPort* view_port;
|
||||
//GUI
|
||||
Gui* gui;
|
||||
NotificationApp* notifications;
|
||||
ViewDispatcher* view_dispatcher;
|
||||
View* view;
|
||||
TextInput* text_input;
|
||||
VariableItem* item;
|
||||
Widget* widget;
|
||||
|
||||
char txtbuff[30]; //Буффер для печати строк на экране
|
||||
bool last_OTG_State; //Состояние OTG до запуска приложения
|
||||
Storage* storage; //Хранилище датчиков
|
||||
Stream* file_stream; //Поток файла с датчиками
|
||||
int8_t sensors_count; // Количество загруженных датчиков
|
||||
DHT_sensor sensors[MAX_SENSORS]; //Сохранённые датчики
|
||||
DHT_data data; //Инфа из датчика
|
||||
DHT_sensor* currentSensorEdit; //Указатель на редактируемый датчик
|
||||
|
||||
} PluginData;
|
||||
|
||||
/* ================== Работа с GPIO ================== */
|
||||
/**
|
||||
* @brief Конвертация GPIO в его номер на корпусе FZ
|
||||
*
|
||||
* @param gpio Указатель на преобразовываемый GPIO
|
||||
* @return Номер порта на корпусе FZ
|
||||
*/
|
||||
uint8_t DHTMon_GPIO_to_int(const GpioPin* gpio);
|
||||
/**
|
||||
* @brief Конвертация номера порта на корпусе FZ в GPIO
|
||||
*
|
||||
* @param name Номер порта на корпусе FZ
|
||||
* @return Указатель на GPIO при успехе, NULL при ошибке
|
||||
*/
|
||||
const GpioPin* DHTMon_GPIO_form_int(uint8_t name);
|
||||
/**
|
||||
* @brief Преобразование порядкового номера порта в GPIO
|
||||
*
|
||||
* @param index Индекс порта от 0 до GPIO_ITEMS-1
|
||||
* @return Указатель на GPIO при успехе, NULL при ошибке
|
||||
*/
|
||||
const GpioPin* DHTMon_GPIO_from_index(uint8_t index);
|
||||
/**
|
||||
* @brief Преобразование GPIO в порядковый номер порта
|
||||
*
|
||||
* @param gpio Указатель на GPIO
|
||||
* @return index при успехе, 255 при ошибке
|
||||
*/
|
||||
uint8_t DHTMon_GPIO_to_index(const GpioPin* gpio);
|
||||
|
||||
/**
|
||||
* @brief Получить имя GPIO в виде строки
|
||||
*
|
||||
* @param gpio Искомый порт
|
||||
* @return char* Указатель на строку с именем порта
|
||||
*/
|
||||
const char* DHTMon_GPIO_getName(const GpioPin* gpio);
|
||||
|
||||
/* ================== Работа с датчиками ================== */
|
||||
/**
|
||||
* @brief Инициализация портов ввода/вывода датчиков
|
||||
*/
|
||||
void DHTMon_sensors_init(void);
|
||||
/**
|
||||
* @brief Функция деинициализации портов ввода/вывода датчиков
|
||||
*/
|
||||
void DHTMon_sensors_deinit(void);
|
||||
/**
|
||||
* @brief Проверка корректности параметров датчика
|
||||
*
|
||||
* @param sensor Указатель на проверяемый датчик
|
||||
* @return true Параметры датчика корректные
|
||||
* @return false Параметры датчика некорректные
|
||||
*/
|
||||
bool DHTMon_sensor_check(DHT_sensor* sensor);
|
||||
/**
|
||||
* @brief Удаление датчика из списка и перезагрузка
|
||||
*
|
||||
* @param sensor Указатель на удаляемый датчик
|
||||
*/
|
||||
void DHTMon_sensor_delete(DHT_sensor* sensor);
|
||||
/**
|
||||
* @brief Сохранение датчиков на SD-карту
|
||||
*
|
||||
* @return Количество сохранённых датчиков
|
||||
*/
|
||||
uint8_t DHTMon_sensors_save(void);
|
||||
/**
|
||||
* @brief Загрузка датчиков с SD-карты
|
||||
*
|
||||
* @return true Был загружен хотя бы 1 датчик
|
||||
* @return false Датчики отсутствуют
|
||||
*/
|
||||
bool DHTMon_sensors_load(void);
|
||||
/**
|
||||
* @brief Перезагрузка датчиков с SD-карты
|
||||
*
|
||||
* @return true Когда был загружен хотя бы 1 датчик
|
||||
* @return false Ни один из датчиков не был загружен
|
||||
*/
|
||||
bool DHTMon_sensors_reload(void);
|
||||
|
||||
void scene_main(Canvas* const canvas, PluginData* app);
|
||||
void mainMenu_scene(PluginData* app);
|
||||
|
||||
void sensorEdit_sceneCreate(PluginData* app);
|
||||
void sensorEdit_scene(PluginData* app);
|
||||
void sensorEdit_sceneRemove(void);
|
||||
|
||||
void sensorActions_sceneCreate(PluginData* app);
|
||||
void sensorActions_scene(PluginData* app);
|
||||
void sensorActions_screneRemove(void);
|
||||
#endif
|
||||
@@ -0,0 +1,157 @@
|
||||
#include "../quenon_dht_mon.h"
|
||||
//Текущий вид
|
||||
static View* view;
|
||||
//Список
|
||||
static VariableItemList* variable_item_list;
|
||||
|
||||
/**
|
||||
* @brief Функция обработки нажатия кнопки "Назад"
|
||||
*
|
||||
* @param context Указатель на данные приложения
|
||||
* @return ID вида в который нужно переключиться
|
||||
*/
|
||||
static uint32_t actions_exitCallback(void* context) {
|
||||
PluginData* app = context;
|
||||
UNUSED(app);
|
||||
//Возвращаем ID вида, в который нужно вернуться
|
||||
return VIEW_NONE;
|
||||
}
|
||||
/**
|
||||
* @brief Функция обработки нажатия средней кнопки
|
||||
*
|
||||
* @param context Указатель на данные приложения
|
||||
* @param index На каком элементе списка была нажата кнопка
|
||||
*/
|
||||
static void enterCallback(void* context, uint32_t index) {
|
||||
PluginData* app = context;
|
||||
if((uint8_t)index < (uint8_t)app->sensors_count) {
|
||||
app->currentSensorEdit = &app->sensors[index];
|
||||
sensorActions_scene(app);
|
||||
}
|
||||
if((uint8_t)index == (uint8_t)app->sensors_count) {
|
||||
app->currentSensorEdit = &app->sensors[app->sensors_count++];
|
||||
strcpy(app->currentSensorEdit->name, "NewSensor");
|
||||
app->currentSensorEdit->GPIO = DHTMon_GPIO_from_index(0);
|
||||
app->currentSensorEdit->type = DHT11;
|
||||
sensorEdit_scene(app);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Создание списка действий с указанным датчиком
|
||||
*
|
||||
* @param app Указатель на данные плагина
|
||||
*/
|
||||
void mainMenu_scene(PluginData* app) {
|
||||
variable_item_list = variable_item_list_alloc();
|
||||
//Сброс всех элементов меню
|
||||
variable_item_list_reset(variable_item_list);
|
||||
//Добавление названий датчиков в качестве элементов списка
|
||||
for(uint8_t i = 0; i < app->sensors_count; i++) {
|
||||
variable_item_list_add(variable_item_list, app->sensors[i].name, 1, NULL, NULL);
|
||||
}
|
||||
if(app->sensors_count < (uint8_t)MAX_SENSORS) {
|
||||
variable_item_list_add(variable_item_list, " + Add new sensor +", 1, NULL, NULL);
|
||||
}
|
||||
|
||||
//Добавление колбека на нажатие средней кнопки
|
||||
variable_item_list_set_enter_callback(variable_item_list, enterCallback, app);
|
||||
|
||||
//Создание вида из списка
|
||||
view = variable_item_list_get_view(variable_item_list);
|
||||
//Добавление колбека на нажатие кнопки "Назад"
|
||||
view_set_previous_callback(view, actions_exitCallback);
|
||||
//Добавление вида в диспетчер
|
||||
view_dispatcher_add_view(app->view_dispatcher, MAIN_MENU_VIEW, view);
|
||||
|
||||
view_dispatcher_enable_queue(app->view_dispatcher);
|
||||
|
||||
//Переключение на наш вид
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, MAIN_MENU_VIEW);
|
||||
|
||||
//Запуск диспетчера
|
||||
view_dispatcher_run(app->view_dispatcher);
|
||||
|
||||
//Очистка списка элементов
|
||||
variable_item_list_free(variable_item_list);
|
||||
//Удаление вида после обработки
|
||||
view_dispatcher_remove_view(app->view_dispatcher, MAIN_MENU_VIEW);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
*/
|
||||
|
||||
// static VariableItemList* variable_item_list;
|
||||
// /* ============== Главное меню ============== */
|
||||
// static uint32_t mainMenu_exitCallback(void* context) {
|
||||
// UNUSED(context);
|
||||
// variable_item_list_free(variable_item_list);
|
||||
// DHT_sensors_reload();
|
||||
// return VIEW_NONE;
|
||||
// }
|
||||
// static void mainMenu_enterCallback(void* context, uint32_t index) {
|
||||
// PluginData* app = context;
|
||||
// if((uint8_t)index == (uint8_t)app->sensors_count) {
|
||||
// addSensor_scene(app);
|
||||
// view_dispatcher_run(app->view_dispatcher);
|
||||
// }
|
||||
// }
|
||||
// void mainMenu_scene(PluginData* app) {
|
||||
// variable_item_list = variable_item_list_alloc();
|
||||
// variable_item_list_reset(variable_item_list);
|
||||
// for(uint8_t i = 0; i < app->sensors_count; i++) {
|
||||
// variable_item_list_add(variable_item_list, app->sensors[i].name, 1, NULL, NULL);
|
||||
// }
|
||||
// variable_item_list_add(variable_item_list, "+ Add new sensor +", 1, NULL, NULL);
|
||||
|
||||
// app->view = variable_item_list_get_view(variable_item_list);
|
||||
// app->view_dispatcher = view_dispatcher_alloc();
|
||||
|
||||
// view_dispatcher_enable_queue(app->view_dispatcher);
|
||||
// view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
|
||||
// view_dispatcher_add_view(app->view_dispatcher, MAIN_MENU_VIEW, app->view);
|
||||
// view_dispatcher_switch_to_view(app->view_dispatcher, MAIN_MENU_VIEW);
|
||||
|
||||
// variable_item_list_set_enter_callback(variable_item_list, mainMenu_enterCallback, app);
|
||||
// view_set_previous_callback(app->view, mainMenu_exitCallback);
|
||||
// }
|
||||
@@ -0,0 +1,40 @@
|
||||
#include "../quenon_dht_mon.h"
|
||||
|
||||
/* ============== Главный экран ============== */
|
||||
void scene_main(Canvas* const canvas, PluginData* app) {
|
||||
//Рисование бара
|
||||
canvas_draw_box(canvas, 0, 0, 128, 14);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str(canvas, 32, 11, "DHT Monitor");
|
||||
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
if(app->sensors_count > 0) {
|
||||
if(!furi_hal_power_is_otg_enabled()) {
|
||||
furi_hal_power_enable_otg();
|
||||
}
|
||||
for(uint8_t i = 0; i < app->sensors_count; i++) {
|
||||
app->data = DHT_getData(&app->sensors[i]);
|
||||
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str(canvas, 0, 24 + 10 * i, app->sensors[i].name);
|
||||
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
if(app->data.hum == -128.0f && app->data.temp == -128.0f) {
|
||||
canvas_draw_str(canvas, 96, 24 + 10 * i, "timeout");
|
||||
} else {
|
||||
snprintf(
|
||||
app->txtbuff,
|
||||
sizeof(app->txtbuff),
|
||||
"%2.1f*C/%d%%",
|
||||
(double)app->data.temp,
|
||||
(int8_t)app->data.hum);
|
||||
canvas_draw_str(canvas, 64, 24 + 10 * i, app->txtbuff);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
if(app->sensors_count == 0) canvas_draw_str(canvas, 0, 24, "Sensors not found");
|
||||
if(app->sensors_count == -1) canvas_draw_str(canvas, 0, 24, "Loading...");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
#include "../quenon_dht_mon.h"
|
||||
|
||||
//Текущий вид
|
||||
static View* view;
|
||||
//Список
|
||||
static VariableItemList* variable_item_list;
|
||||
|
||||
/* ================== Информация о датчике ================== */
|
||||
/**
|
||||
* @brief Функция обработки нажатия кнопки "Назад"
|
||||
*
|
||||
* @param context Указатель на данные приложения
|
||||
* @return ID вида в который нужно переключиться
|
||||
*/
|
||||
static uint32_t infoWidget_exitCallback(void* context) {
|
||||
PluginData* app = context;
|
||||
UNUSED(app);
|
||||
//Возвращаем ID вида, в который нужно вернуться
|
||||
return SENSOR_ACTIONS_VIEW;
|
||||
}
|
||||
/**
|
||||
* @brief Обработчик нажатий на кнопку в виджете
|
||||
*
|
||||
* @param result Какая из кнопок была нажата
|
||||
* @param type Тип нажатия
|
||||
* @param context Указатель на данные плагина
|
||||
*/
|
||||
static void infoWidget_callback(GuiButtonType result, InputType type, void* context) {
|
||||
PluginData* app = context;
|
||||
//Коротко нажата левая кнопка (Back)
|
||||
if(result == GuiButtonTypeLeft && type == InputTypeShort) {
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, SENSOR_ACTIONS_VIEW);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @brief Создание виджета информации о датчике
|
||||
*
|
||||
* @param app Указатель на данные плагина
|
||||
*/
|
||||
static void sensorInfo_widget(PluginData* app) {
|
||||
//Очистка виджета
|
||||
widget_reset(app->widget);
|
||||
//Добавление кнопок
|
||||
widget_add_button_element(app->widget, GuiButtonTypeLeft, "Back", infoWidget_callback, app);
|
||||
|
||||
char str[32];
|
||||
snprintf(str, sizeof(str), "\e#%s\e#", app->currentSensorEdit->name);
|
||||
widget_add_text_box_element(app->widget, 0, 0, 128, 23, AlignCenter, AlignCenter, str, false);
|
||||
snprintf(str, sizeof(str), "\e#Type:\e# %s", app->currentSensorEdit->type ? "DHT22" : "DHT11");
|
||||
widget_add_text_box_element(app->widget, 0, 0, 128, 47, AlignLeft, AlignCenter, str, false);
|
||||
snprintf(
|
||||
str, sizeof(str), "\e#GPIO:\e# %s", DHTMon_GPIO_getName(app->currentSensorEdit->GPIO));
|
||||
widget_add_text_box_element(app->widget, 0, 0, 128, 72, AlignLeft, AlignCenter, str, false);
|
||||
view_set_previous_callback(widget_get_view(app->widget), infoWidget_exitCallback);
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, WIDGET_VIEW);
|
||||
}
|
||||
|
||||
/* ================== Подтверждение удаления ================== */
|
||||
/**
|
||||
* @brief Функция обработки нажатия кнопки "Назад"
|
||||
*
|
||||
* @param context Указатель на данные приложения
|
||||
* @return ID вида в который нужно переключиться
|
||||
*/
|
||||
static uint32_t deleteWidget_exitCallback(void* context) {
|
||||
PluginData* app = context;
|
||||
UNUSED(app);
|
||||
//Возвращаем ID вида, в который нужно вернуться
|
||||
return SENSOR_ACTIONS_VIEW;
|
||||
}
|
||||
/**
|
||||
* @brief Обработчик нажатий на кнопку в виджете
|
||||
*
|
||||
* @param result Какая из кнопок была нажата
|
||||
* @param type Тип нажатия
|
||||
* @param context Указатель на данные плагина
|
||||
*/
|
||||
static void deleteWidget_callback(GuiButtonType result, InputType type, void* context) {
|
||||
PluginData* app = context;
|
||||
//Коротко нажата левая кнопка (Cancel)
|
||||
if(result == GuiButtonTypeLeft && type == InputTypeShort) {
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, SENSOR_ACTIONS_VIEW);
|
||||
}
|
||||
//Коротко нажата правая кнопка (Delete)
|
||||
if(result == GuiButtonTypeRight && type == InputTypeShort) {
|
||||
//Удаление датчика
|
||||
DHTMon_sensor_delete(app->currentSensorEdit);
|
||||
//Выход из меню
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, VIEW_NONE);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @brief Создание виджета удаления датчика
|
||||
*
|
||||
* @param app Указатель на данные плагина
|
||||
*/
|
||||
static void sensorDelete_widget(PluginData* app) {
|
||||
//Очистка виджета
|
||||
widget_reset(app->widget);
|
||||
//Добавление кнопок
|
||||
widget_add_button_element(
|
||||
app->widget, GuiButtonTypeLeft, "Cancel", deleteWidget_callback, app);
|
||||
widget_add_button_element(
|
||||
app->widget, GuiButtonTypeRight, "Delete", deleteWidget_callback, app);
|
||||
|
||||
char delete_str[32];
|
||||
snprintf(delete_str, sizeof(delete_str), "\e#Delete %s?\e#", app->currentSensorEdit->name);
|
||||
widget_add_text_box_element(
|
||||
app->widget, 0, 0, 128, 23, AlignCenter, AlignCenter, delete_str, false);
|
||||
snprintf(
|
||||
delete_str,
|
||||
sizeof(delete_str),
|
||||
"\e#Type:\e# %s",
|
||||
app->currentSensorEdit->type ? "DHT22" : "DHT11");
|
||||
widget_add_text_box_element(
|
||||
app->widget, 0, 0, 128, 47, AlignLeft, AlignCenter, delete_str, false);
|
||||
snprintf(
|
||||
delete_str,
|
||||
sizeof(delete_str),
|
||||
"\e#GPIO:\e# %s",
|
||||
DHTMon_GPIO_getName(app->currentSensorEdit->GPIO));
|
||||
widget_add_text_box_element(
|
||||
app->widget, 0, 0, 128, 72, AlignLeft, AlignCenter, delete_str, false);
|
||||
view_set_previous_callback(widget_get_view(app->widget), deleteWidget_exitCallback);
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, WIDGET_VIEW);
|
||||
}
|
||||
|
||||
/* ================== Меню действий ================== */
|
||||
/**
|
||||
* @brief Функция обработки нажатия средней кнопки
|
||||
*
|
||||
* @param context Указатель на данные приложения
|
||||
* @param index На каком элементе списка была нажата кнопка
|
||||
*/
|
||||
static void enterCallback(void* context, uint32_t index) {
|
||||
PluginData* app = context;
|
||||
if(index == 0) {
|
||||
sensorInfo_widget(app);
|
||||
}
|
||||
if(index == 1) {
|
||||
sensorEdit_scene(app);
|
||||
}
|
||||
if(index == 2) {
|
||||
sensorDelete_widget(app);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Функция обработки нажатия кнопки "Назад"
|
||||
*
|
||||
* @param context Указатель на данные приложения
|
||||
* @return ID вида в который нужно переключиться
|
||||
*/
|
||||
static uint32_t actions_exitCallback(void* context) {
|
||||
PluginData* app = context;
|
||||
UNUSED(app);
|
||||
//Возвращаем ID вида, в который нужно вернуться
|
||||
return MAIN_MENU_VIEW;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Создание списка действий с указанным датчиком
|
||||
*
|
||||
* @param app Указатель на данные плагина
|
||||
*/
|
||||
void sensorActions_sceneCreate(PluginData* app) {
|
||||
variable_item_list = variable_item_list_alloc();
|
||||
//Сброс всех элементов меню
|
||||
variable_item_list_reset(variable_item_list);
|
||||
//Добавление элементов в список
|
||||
variable_item_list_add(variable_item_list, "Info", 0, NULL, NULL);
|
||||
variable_item_list_add(variable_item_list, "Edit", 0, NULL, NULL);
|
||||
variable_item_list_add(variable_item_list, "Delete", 0, NULL, NULL);
|
||||
|
||||
//Добавление колбека на нажатие средней кнопки
|
||||
variable_item_list_set_enter_callback(variable_item_list, enterCallback, app);
|
||||
|
||||
//Создание вида из списка
|
||||
view = variable_item_list_get_view(variable_item_list);
|
||||
//Добавление колбека на нажатие кнопки "Назад"
|
||||
view_set_previous_callback(view, actions_exitCallback);
|
||||
//Добавление вида в диспетчер
|
||||
view_dispatcher_add_view(app->view_dispatcher, SENSOR_ACTIONS_VIEW, view);
|
||||
}
|
||||
void sensorActions_scene(PluginData* app) {
|
||||
//Сброс выбранного пункта в ноль
|
||||
variable_item_list_set_selected_item(variable_item_list, 0);
|
||||
//Переключение на наш вид
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, SENSOR_ACTIONS_VIEW);
|
||||
}
|
||||
|
||||
void sensorActions_screneRemove(void) {
|
||||
variable_item_list_free(variable_item_list);
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
#include "../quenon_dht_mon.h"
|
||||
|
||||
static VariableItem* nameItem;
|
||||
static VariableItemList* variable_item_list;
|
||||
|
||||
static const char* const sensorsTypes[2] = {
|
||||
"DHT11",
|
||||
"DHT22",
|
||||
};
|
||||
|
||||
// /* ============== Добавление датчика ============== */
|
||||
static uint32_t addSensor_exitCallback(void* context) {
|
||||
UNUSED(context);
|
||||
DHTMon_sensors_reload();
|
||||
return VIEW_NONE;
|
||||
}
|
||||
|
||||
static void addSensor_sensorTypeChanged(VariableItem* item) {
|
||||
uint8_t index = variable_item_get_current_value_index(item);
|
||||
PluginData* app = variable_item_get_context(item);
|
||||
variable_item_set_current_value_text(item, sensorsTypes[index]);
|
||||
app->currentSensorEdit->type = index;
|
||||
}
|
||||
|
||||
static void addSensor_GPIOChanged(VariableItem* item) {
|
||||
uint8_t index = variable_item_get_current_value_index(item);
|
||||
variable_item_set_current_value_text(item, DHTMon_GPIO_getName(DHTMon_GPIO_from_index(index)));
|
||||
PluginData* app = variable_item_get_context(item);
|
||||
app->currentSensorEdit->GPIO = DHTMon_GPIO_from_index(index);
|
||||
}
|
||||
|
||||
static void addSensor_sensorNameChanged(void* context) {
|
||||
PluginData* app = context;
|
||||
variable_item_set_current_value_text(nameItem, app->currentSensorEdit->name);
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, ADDSENSOR_MENU_VIEW);
|
||||
}
|
||||
static void addSensor_sensorNameChange(PluginData* app) {
|
||||
text_input_set_header_text(app->text_input, "Sensor name");
|
||||
//По неясной мне причине в длину строки входит терминатор. Поэтому при длине 10 приходится указывать 11
|
||||
text_input_set_result_callback(
|
||||
app->text_input, addSensor_sensorNameChanged, app, app->currentSensorEdit->name, 11, true);
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, TEXTINPUT_VIEW);
|
||||
}
|
||||
|
||||
static void addSensor_enterCallback(void* context, uint32_t index) {
|
||||
PluginData* app = context;
|
||||
if(index == 0) {
|
||||
addSensor_sensorNameChange(app);
|
||||
}
|
||||
if(index == 3) {
|
||||
//Сохранение датчика
|
||||
DHTMon_sensors_save();
|
||||
DHTMon_sensors_reload();
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, VIEW_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
void sensorEdit_sceneCreate(PluginData* app) {
|
||||
variable_item_list = variable_item_list_alloc();
|
||||
|
||||
variable_item_list_reset(variable_item_list);
|
||||
|
||||
variable_item_list_set_enter_callback(variable_item_list, addSensor_enterCallback, app);
|
||||
|
||||
app->view = variable_item_list_get_view(variable_item_list);
|
||||
|
||||
view_set_previous_callback(app->view, addSensor_exitCallback);
|
||||
|
||||
view_dispatcher_add_view(app->view_dispatcher, ADDSENSOR_MENU_VIEW, app->view);
|
||||
}
|
||||
void sensorEdit_scene(PluginData* app) {
|
||||
//Очистка списка
|
||||
variable_item_list_reset(variable_item_list);
|
||||
|
||||
//Имя редактируемого датчика
|
||||
nameItem = variable_item_list_add(variable_item_list, "Name: ", 1, NULL, NULL);
|
||||
variable_item_set_current_value_index(nameItem, 0);
|
||||
variable_item_set_current_value_text(nameItem, app->currentSensorEdit->name);
|
||||
|
||||
//Тип датчика
|
||||
app->item =
|
||||
variable_item_list_add(variable_item_list, "Type:", 2, addSensor_sensorTypeChanged, app);
|
||||
|
||||
variable_item_set_current_value_index(app->item, app->currentSensorEdit->type);
|
||||
variable_item_set_current_value_text(app->item, sensorsTypes[app->currentSensorEdit->type]);
|
||||
|
||||
//GPIO
|
||||
app->item =
|
||||
variable_item_list_add(variable_item_list, "GPIO:", 13, addSensor_GPIOChanged, app);
|
||||
variable_item_set_current_value_index(
|
||||
app->item, DHTMon_GPIO_to_index(app->currentSensorEdit->GPIO));
|
||||
variable_item_set_current_value_text(
|
||||
app->item, DHTMon_GPIO_getName(app->currentSensorEdit->GPIO));
|
||||
variable_item_list_add(variable_item_list, "Save", 1, NULL, app);
|
||||
|
||||
//Сброс выбранного пункта в ноль
|
||||
variable_item_list_set_selected_item(variable_item_list, 0);
|
||||
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, ADDSENSOR_MENU_VIEW);
|
||||
}
|
||||
void sensorEdit_sceneRemove(void) {
|
||||
variable_item_list_free(variable_item_list);
|
||||
}
|
||||
@@ -0,0 +1,674 @@
|
||||
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>.
|
||||
@@ -0,0 +1,21 @@
|
||||
# Flipper Zero DnD Dice
|
||||
|
||||
<div style="text-align:center"><img src="sources/flipper-screen.png"/></div>
|
||||
<br />
|
||||
|
||||
**DnD Dice** is a dice rolling application for your **Flipper Zero**.
|
||||
|
||||
Dice types: Coin, d4, d6, d8, d10, d12, d20, d100
|
||||
|
||||
## Screenshots
|
||||
|
||||
<div style="text-align:center"><img src="sources/main-screen.png"/></div>
|
||||
<br />
|
||||
<div style="text-align:center"><img src="sources/roll-screen.png"/></div>
|
||||
|
||||
## Compiling
|
||||
|
||||
1. Clone the [flipperzero-firmware](https://github.com/flipperdevices/flipperzero-firmware) repository or another firmware that you use (for example [unleashed-firmware](https://github.com/DarkFlippers/unleashed-firmware)).
|
||||
2. Create a symbolic link in `applications_user` named **dice**, pointing to this repository.
|
||||
3. Compile by command `./fbt fap_dice_dnd_app`
|
||||
4. Copy `build/f7-firmware-D/.extapps/dice_dnd_app.fap` to **apps/Games** on the SD card or by [qFlipper](https://flipperzero.one/update) app.
|
||||
@@ -0,0 +1,13 @@
|
||||
App(
|
||||
appid="DND_Dice_app",
|
||||
name="DnD Dice [Ka3u6y6a]",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="dice_dnd_app",
|
||||
cdefines=["APP_DICE"],
|
||||
requires=["gui"],
|
||||
stack_size=1 * 1024,
|
||||
order=90,
|
||||
fap_icon="icon.png",
|
||||
fap_category="Games",
|
||||
fap_icon_assets="assets",
|
||||
)
|
||||
|
After Width: | Height: | Size: 539 B |
|
After Width: | Height: | Size: 535 B |
|
After Width: | Height: | Size: 204 B |
|
After Width: | Height: | Size: 546 B |
|
After Width: | Height: | Size: 530 B |
|
After Width: | Height: | Size: 546 B |
|
After Width: | Height: | Size: 535 B |
|
After Width: | Height: | Size: 628 B |
|
After Width: | Height: | Size: 618 B |
|
After Width: | Height: | Size: 630 B |
|
After Width: | Height: | Size: 635 B |
|
After Width: | Height: | Size: 544 B |
|
After Width: | Height: | Size: 531 B |
|
After Width: | Height: | Size: 516 B |
|
After Width: | Height: | Size: 517 B |
|
After Width: | Height: | Size: 514 B |
|
After Width: | Height: | Size: 517 B |
|
After Width: | Height: | Size: 523 B |
|
After Width: | Height: | Size: 493 B |
|
After Width: | Height: | Size: 523 B |
|
After Width: | Height: | Size: 508 B |
|
After Width: | Height: | Size: 503 B |
|
After Width: | Height: | Size: 504 B |
|
After Width: | Height: | Size: 376 B |
|
After Width: | Height: | Size: 490 B |
|
After Width: | Height: | Size: 369 B |
|
After Width: | Height: | Size: 471 B |
|
After Width: | Height: | Size: 483 B |
|
After Width: | Height: | Size: 478 B |
|
After Width: | Height: | Size: 467 B |
|
After Width: | Height: | Size: 457 B |
|
After Width: | Height: | Size: 482 B |
|
After Width: | Height: | Size: 444 B |
|
After Width: | Height: | Size: 471 B |
|
After Width: | Height: | Size: 220 B |
|
After Width: | Height: | Size: 102 B |
|
After Width: | Height: | Size: 211 B |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 189 B |
|
After Width: | Height: | Size: 102 B |
|
After Width: | Height: | Size: 182 B |
|
After Width: | Height: | Size: 180 B |
|
After Width: | Height: | Size: 279 B |
@@ -0,0 +1,159 @@
|
||||
#include <gui/icon.h>
|
||||
#include "DND_Dice_app_icons.h"
|
||||
|
||||
#define TAG "DiceApp"
|
||||
|
||||
#define DICE_TYPES 8
|
||||
|
||||
#define MAX_DICE_COUNT 10
|
||||
#define MAX_COIN_FRAMES 9
|
||||
#define MAX_DICE_FRAMES 4
|
||||
|
||||
#define DICE_X 45
|
||||
#define DICE_Y 6
|
||||
#define DICE_Y_T 0
|
||||
|
||||
#define DICE_GAP 44
|
||||
|
||||
#define RESULT_BORDER_X 44
|
||||
#define RESULT_OFFSET 20
|
||||
|
||||
#define SWIPE_DIST 11
|
||||
|
||||
const Icon* coin_heads_start[] = {&I_coin_1, &I_coin_2};
|
||||
const Icon* coin_heads_end[] = {&I_coin_7, &I_coin_1};
|
||||
const Icon* coin_tails_start[] = {&I_coin_5, &I_coin_6};
|
||||
const Icon* coin_tails_end[] = {&I_coin_4, &I_coin_5};
|
||||
const Icon* coin_frames[] = {
|
||||
&I_coin_1,
|
||||
&I_coin_2,
|
||||
&I_coin_3,
|
||||
&I_coin_4,
|
||||
&I_coin_5,
|
||||
&I_coin_6,
|
||||
&I_coin_3,
|
||||
&I_coin_7,
|
||||
&I_coin_1,
|
||||
};
|
||||
|
||||
const int8_t result_frame_pos_y[] = {-30, -20, -10, 0};
|
||||
const Icon* dice_frames[] = {
|
||||
&I_d4_1, &I_d4_2, &I_d4_3, &I_d4_1, // d4
|
||||
&I_d6_1, &I_d6_2, &I_d6_3, &I_d6_4, // d6
|
||||
&I_d8_1, &I_d8_2, &I_d8_3, &I_d8_4, // d8
|
||||
&I_d10_1, &I_d10_2, &I_d10_3, &I_d10_4, // d10
|
||||
&I_d12_1, &I_d12_2, &I_d12_3, &I_d12_4, // d12
|
||||
&I_d20_1, &I_d20_2, &I_d20_3, &I_d20_4, // d20
|
||||
&I_d100_1, &I_d100_2, &I_d100_3, &I_d100_4, // d100
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint8_t type;
|
||||
int x;
|
||||
int y;
|
||||
char* name;
|
||||
} Dice;
|
||||
|
||||
const uint8_t screen_pos[] = {};
|
||||
|
||||
static const Dice dice_types[] = {
|
||||
{2, 0, 0, "Coin"},
|
||||
{4, 0, 0, "d4"},
|
||||
{6, 0, 0, "d6"},
|
||||
{8, 0, 0, "d8"},
|
||||
{10, 0, 0, "d10"},
|
||||
{12, 0, 0, "d12"},
|
||||
{20, 0, 0, "d20"},
|
||||
{100, 0, 0, "d100"},
|
||||
};
|
||||
|
||||
typedef enum { EventTypeTick, EventTypeKey } EventType;
|
||||
typedef enum {
|
||||
SelectState,
|
||||
SwipeLeftState,
|
||||
SwipeRightState,
|
||||
AnimState,
|
||||
AnimResultState,
|
||||
ResultState
|
||||
} AppState;
|
||||
|
||||
typedef struct {
|
||||
EventType type;
|
||||
InputEvent input;
|
||||
} AppEvent;
|
||||
|
||||
typedef struct {
|
||||
AppState app_state;
|
||||
uint16_t roll_result;
|
||||
uint8_t rolled_dices[MAX_DICE_COUNT];
|
||||
uint8_t anim_frame;
|
||||
uint8_t dice_index;
|
||||
uint8_t dice_count;
|
||||
int8_t result_pos;
|
||||
Dice dices[DICE_TYPES];
|
||||
} State;
|
||||
|
||||
void init(State* const state) {
|
||||
state->app_state = SelectState;
|
||||
state->roll_result = 0;
|
||||
state->dice_index = 0;
|
||||
state->anim_frame = 0;
|
||||
state->dice_count = 1;
|
||||
|
||||
for(uint8_t i = 0; i < DICE_TYPES; i++) {
|
||||
state->dices[i] = dice_types[i];
|
||||
state->dices[i].x = DICE_X + (i * DICE_GAP);
|
||||
state->dices[i].y = i == 0 ? DICE_Y_T : DICE_Y;
|
||||
}
|
||||
}
|
||||
|
||||
void coin_set_start(uint16_t type) {
|
||||
if(type == 1) {
|
||||
coin_frames[0] = coin_heads_start[0];
|
||||
coin_frames[1] = coin_heads_start[1];
|
||||
} else {
|
||||
coin_frames[0] = coin_tails_start[0];
|
||||
coin_frames[1] = coin_tails_start[1];
|
||||
}
|
||||
}
|
||||
|
||||
void coin_set_end(uint16_t type) {
|
||||
if(type == 1) {
|
||||
coin_frames[MAX_COIN_FRAMES - 2] = coin_heads_end[0];
|
||||
coin_frames[MAX_COIN_FRAMES - 1] = coin_heads_end[1];
|
||||
} else {
|
||||
coin_frames[MAX_COIN_FRAMES - 2] = coin_tails_end[0];
|
||||
coin_frames[MAX_COIN_FRAMES - 1] = coin_tails_end[1];
|
||||
}
|
||||
}
|
||||
|
||||
bool isResultVisible(AppState state, uint8_t dice_index) {
|
||||
return (state == ResultState || state == AnimResultState) && dice_index != 0;
|
||||
}
|
||||
|
||||
bool isDiceNameVisible(AppState state) {
|
||||
return state != SwipeLeftState && state != SwipeRightState;
|
||||
}
|
||||
|
||||
bool isDiceButtonsVisible(AppState state) {
|
||||
return isDiceNameVisible(state) && state != AnimResultState && state != ResultState &&
|
||||
state != AnimState;
|
||||
}
|
||||
|
||||
bool isOneDice(uint8_t dice_index) {
|
||||
return dice_index == 0 || dice_index == 7;
|
||||
}
|
||||
|
||||
bool isDiceSettingsDisabled(AppState state, uint8_t dice_index) {
|
||||
return isOneDice(dice_index) || state == ResultState || state == AnimResultState ||
|
||||
state == AnimState;
|
||||
}
|
||||
|
||||
bool isAnimState(AppState state) {
|
||||
return state == SwipeLeftState || state == SwipeRightState || state == AnimResultState ||
|
||||
state == AnimState;
|
||||
}
|
||||
|
||||
bool isMenuState(AppState state) {
|
||||
return state == SwipeLeftState || state == SwipeRightState || state == SelectState;
|
||||
}
|
||||
@@ -0,0 +1,333 @@
|
||||
#include <furi.h>
|
||||
#include <input/input.h>
|
||||
#include <gui/gui.h>
|
||||
#include "constants.h"
|
||||
|
||||
const Icon* draw_dice_frame;
|
||||
|
||||
static void update(State* const state) {
|
||||
if(state->app_state == SwipeLeftState) {
|
||||
for(uint8_t i = 0; i < DICE_TYPES; i++) {
|
||||
state->dices[i].x -= SWIPE_DIST;
|
||||
state->dices[i].y = DICE_Y;
|
||||
}
|
||||
|
||||
if(state->dices[state->dice_index].x == DICE_X) {
|
||||
state->app_state = SelectState;
|
||||
state->dices[state->dice_index].y = DICE_Y_T;
|
||||
}
|
||||
|
||||
} else if(state->app_state == SwipeRightState) {
|
||||
for(uint8_t i = 0; i < DICE_TYPES; i++) {
|
||||
state->dices[i].x += SWIPE_DIST;
|
||||
state->dices[i].y = DICE_Y;
|
||||
}
|
||||
|
||||
if(state->dices[state->dice_index].x == DICE_X) {
|
||||
state->app_state = SelectState;
|
||||
state->dices[state->dice_index].y = DICE_Y_T;
|
||||
}
|
||||
} else if(state->app_state == AnimState) {
|
||||
state->anim_frame += 1;
|
||||
|
||||
if(state->dice_index == 0) {
|
||||
if(state->anim_frame == 3) coin_set_start(state->roll_result); // change coin anim
|
||||
|
||||
if(state->anim_frame >= MAX_COIN_FRAMES) {
|
||||
state->anim_frame = 0;
|
||||
state->app_state = AnimResultState;
|
||||
}
|
||||
} else {
|
||||
if(state->anim_frame >= MAX_DICE_FRAMES) {
|
||||
state->anim_frame = 0;
|
||||
state->app_state = AnimResultState;
|
||||
}
|
||||
}
|
||||
} else if(state->app_state == AnimResultState) {
|
||||
if(state->dice_index == 0) { // no extra animations for coin
|
||||
state->anim_frame = 0;
|
||||
state->app_state = ResultState;
|
||||
return;
|
||||
}
|
||||
|
||||
state->result_pos = result_frame_pos_y[state->anim_frame];
|
||||
state->anim_frame += 1;
|
||||
|
||||
// end animation
|
||||
if(state->result_pos == 0) {
|
||||
state->anim_frame = 0;
|
||||
state->app_state = ResultState;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void roll(State* const state) {
|
||||
state->roll_result = 0;
|
||||
state->result_pos = result_frame_pos_y[0];
|
||||
|
||||
for(uint8_t i = 0; i < MAX_DICE_COUNT; i++) {
|
||||
if(i < state->dice_count) {
|
||||
state->rolled_dices[i] = (rand() % dice_types[state->dice_index].type) + 1;
|
||||
state->roll_result += state->rolled_dices[i];
|
||||
} else {
|
||||
state->rolled_dices[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if(state->dice_index == 0) coin_set_end(state->roll_result); // change coin anim
|
||||
|
||||
state->app_state = AnimState;
|
||||
}
|
||||
|
||||
static void draw_ui(const State* state, Canvas* canvas) {
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
|
||||
FuriString* count = furi_string_alloc();
|
||||
furi_string_printf(count, "%01d", state->dice_count);
|
||||
|
||||
// dice name
|
||||
if(isDiceNameVisible(state->app_state)) {
|
||||
canvas_draw_str_aligned(
|
||||
canvas, 63, 50, AlignCenter, AlignBottom, dice_types[state->dice_index].name);
|
||||
}
|
||||
// dice arrow buttons
|
||||
if(isDiceButtonsVisible(state->app_state)) {
|
||||
if(state->dice_index > 0) canvas_draw_icon(canvas, 45, 44, &I_ui_button_left);
|
||||
if(state->dice_index < DICE_TYPES - 1)
|
||||
canvas_draw_icon(canvas, 78, 44, &I_ui_button_right);
|
||||
}
|
||||
|
||||
// dice count settings
|
||||
if(isDiceSettingsDisabled(state->app_state, state->dice_index))
|
||||
canvas_draw_icon(canvas, 48, 51, &I_ui_count_1);
|
||||
else
|
||||
canvas_draw_icon(canvas, 48, 51, &I_ui_count);
|
||||
canvas_draw_str_aligned(canvas, 58, 61, AlignCenter, AlignBottom, furi_string_get_cstr(count));
|
||||
|
||||
// buttons
|
||||
if(isAnimState(state->app_state) == false) canvas_draw_icon(canvas, 92, 54, &I_ui_button_roll);
|
||||
|
||||
if(state->app_state != AnimResultState && state->app_state != ResultState) {
|
||||
canvas_draw_icon(canvas, 0, 54, &I_ui_button_exit);
|
||||
} else {
|
||||
canvas_draw_icon(canvas, 0, 54, &I_ui_button_back);
|
||||
}
|
||||
|
||||
furi_string_free(count);
|
||||
}
|
||||
|
||||
static void draw_dice(const State* state, Canvas* canvas) {
|
||||
if(isMenuState(state->app_state) == false) { // draw only selected dice
|
||||
if(state->dice_index == 0) { // coin
|
||||
draw_dice_frame = coin_frames[state->anim_frame];
|
||||
} else { // dices
|
||||
draw_dice_frame =
|
||||
dice_frames[(state->dice_index - 1) * MAX_DICE_FRAMES + state->anim_frame];
|
||||
}
|
||||
|
||||
canvas_draw_icon(
|
||||
canvas,
|
||||
state->dices[state->dice_index].x,
|
||||
state->dices[state->dice_index].y,
|
||||
draw_dice_frame);
|
||||
return;
|
||||
}
|
||||
|
||||
for(uint8_t i = 0; i < DICE_TYPES; i++) {
|
||||
if(state->app_state == ResultState && state->dice_index == i && state->dice_index != 0)
|
||||
continue; // draw results except coin
|
||||
if(state->dices[i].x > 128 || state->dices[i].x < -35) continue; // outside the screen
|
||||
|
||||
if(i == 0) { // coin
|
||||
draw_dice_frame = coin_frames[0];
|
||||
} else { // dices
|
||||
draw_dice_frame = dice_frames[(i - 1) * MAX_DICE_FRAMES];
|
||||
}
|
||||
|
||||
canvas_draw_icon(canvas, state->dices[i].x, state->dices[i].y, draw_dice_frame);
|
||||
}
|
||||
}
|
||||
|
||||
static void draw_results(const State* state, Canvas* canvas) {
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
|
||||
FuriString* sum = furi_string_alloc();
|
||||
furi_string_printf(sum, "%01d", state->roll_result);
|
||||
|
||||
// ui frame
|
||||
if(state->app_state == AnimResultState)
|
||||
canvas_draw_icon(canvas, RESULT_BORDER_X, state->result_pos, &I_ui_result_border);
|
||||
else
|
||||
canvas_draw_icon(
|
||||
canvas, RESULT_BORDER_X, result_frame_pos_y[MAX_DICE_FRAMES - 1], &I_ui_result_border);
|
||||
|
||||
// result text
|
||||
canvas_draw_str_aligned(
|
||||
canvas,
|
||||
64,
|
||||
state->result_pos + RESULT_OFFSET,
|
||||
AlignCenter,
|
||||
AlignCenter,
|
||||
furi_string_get_cstr(sum));
|
||||
|
||||
if(state->app_state == ResultState && isOneDice(state->dice_index) == false) {
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
|
||||
FuriString* dices = furi_string_alloc();
|
||||
for(uint8_t i = 0; i < state->dice_count; i++) {
|
||||
furi_string_cat_printf(dices, "%01d", state->rolled_dices[i]);
|
||||
|
||||
if(i != state->dice_count - 1) furi_string_cat_printf(dices, "%s", ", ");
|
||||
}
|
||||
|
||||
canvas_draw_str_aligned(
|
||||
canvas, 63, 37, AlignCenter, AlignCenter, furi_string_get_cstr(dices));
|
||||
furi_string_free(dices);
|
||||
}
|
||||
|
||||
furi_string_free(sum);
|
||||
}
|
||||
|
||||
static void draw_callback(Canvas* canvas, void* ctx) {
|
||||
const State* state = acquire_mutex((ValueMutex*)ctx, 25);
|
||||
if(state == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
canvas_clear(canvas);
|
||||
|
||||
draw_ui(state, canvas);
|
||||
|
||||
if(isResultVisible(state->app_state, state->dice_index)) {
|
||||
draw_results(state, canvas);
|
||||
} else {
|
||||
draw_dice(state, canvas);
|
||||
}
|
||||
|
||||
release_mutex((ValueMutex*)ctx, state);
|
||||
}
|
||||
|
||||
static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
|
||||
furi_assert(event_queue);
|
||||
|
||||
AppEvent event = {.type = EventTypeKey, .input = *input_event};
|
||||
furi_message_queue_put(event_queue, &event, FuriWaitForever);
|
||||
}
|
||||
|
||||
static void timer_callback(FuriMessageQueue* event_queue) {
|
||||
furi_assert(event_queue);
|
||||
|
||||
AppEvent event = {.type = EventTypeTick};
|
||||
furi_message_queue_put(event_queue, &event, 0);
|
||||
}
|
||||
|
||||
int32_t dice_dnd_app(void* p) {
|
||||
UNUSED(p);
|
||||
|
||||
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(AppEvent));
|
||||
|
||||
FURI_LOG_E(TAG, ">>> Started...\r\n");
|
||||
State* state = malloc(sizeof(State));
|
||||
init(state);
|
||||
|
||||
ValueMutex state_mutex;
|
||||
if(!init_mutex(&state_mutex, state, sizeof(State))) {
|
||||
FURI_LOG_E(TAG, "cannot create mutex\r\n");
|
||||
free(state);
|
||||
return 255;
|
||||
}
|
||||
|
||||
// Set callbacks
|
||||
ViewPort* view_port = view_port_alloc();
|
||||
view_port_draw_callback_set(view_port, draw_callback, &state_mutex);
|
||||
view_port_input_callback_set(view_port, input_callback, event_queue);
|
||||
|
||||
FuriTimer* timer = furi_timer_alloc(timer_callback, FuriTimerTypePeriodic, event_queue);
|
||||
furi_timer_start(timer, furi_kernel_get_tick_frequency() * 0.2);
|
||||
|
||||
// Create GUI, register view port
|
||||
Gui* gui = furi_record_open(RECORD_GUI);
|
||||
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
|
||||
|
||||
AppEvent event;
|
||||
for(bool processing = true; processing;) {
|
||||
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
|
||||
State* state = (State*)acquire_mutex_block(&state_mutex);
|
||||
|
||||
if(event_status == FuriStatusOk) {
|
||||
// timer evetn
|
||||
if(event.type == EventTypeTick) {
|
||||
update(state);
|
||||
}
|
||||
// button events
|
||||
if(event.type == EventTypeKey) {
|
||||
if(event.input.type == InputTypePress) {
|
||||
// dice type
|
||||
if(isDiceButtonsVisible(state->app_state)) {
|
||||
if(event.input.key == InputKeyRight) {
|
||||
if(state->dice_index < DICE_TYPES - 1) {
|
||||
state->dice_index += 1;
|
||||
state->app_state = SwipeLeftState;
|
||||
}
|
||||
} else if(event.input.key == InputKeyLeft) {
|
||||
if(state->dice_index > 0) {
|
||||
state->dice_index -= 1;
|
||||
state->app_state = SwipeRightState;
|
||||
}
|
||||
}
|
||||
|
||||
if(isOneDice(state->dice_index)) state->dice_count = 1;
|
||||
}
|
||||
// dice count
|
||||
if(isDiceSettingsDisabled(state->app_state, state->dice_index) == false &&
|
||||
isAnimState(state->app_state) == false) {
|
||||
if(event.input.key == InputKeyUp) {
|
||||
if(state->dice_index != 0) {
|
||||
state->dice_count += 1;
|
||||
if(state->dice_count > MAX_DICE_COUNT) {
|
||||
state->dice_count = MAX_DICE_COUNT;
|
||||
}
|
||||
}
|
||||
} else if(event.input.key == InputKeyDown) {
|
||||
state->dice_count -= 1;
|
||||
if(state->dice_count < 1) {
|
||||
state->dice_count = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
// roll
|
||||
if(event.input.key == InputKeyOk && isAnimState(state->app_state) == false) {
|
||||
roll(state);
|
||||
}
|
||||
// back to dice select state or quit from app
|
||||
if(event.input.key == InputKeyBack) {
|
||||
if(state->app_state == ResultState ||
|
||||
state->app_state == AnimResultState) {
|
||||
state->anim_frame = 0;
|
||||
state->app_state = SelectState;
|
||||
} else {
|
||||
processing = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
FURI_LOG_D(TAG, "osMessageQueue: event timeout");
|
||||
}
|
||||
|
||||
view_port_update(view_port);
|
||||
release_mutex(&state_mutex, state);
|
||||
}
|
||||
|
||||
// Clear
|
||||
free(state);
|
||||
furi_timer_free(timer);
|
||||
furi_message_queue_free(event_queue);
|
||||
view_port_enabled_set(view_port, false);
|
||||
gui_remove_view_port(gui, view_port);
|
||||
furi_record_close(RECORD_GUI);
|
||||
view_port_free(view_port);
|
||||
delete_mutex(&state_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
After Width: | Height: | Size: 128 B |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
@@ -0,0 +1,674 @@
|
||||
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>.
|
||||
@@ -0,0 +1,16 @@
|
||||

|
||||
|
||||
## DTMF Dolphin
|
||||
|
||||
DTMF (Dual-Tone Multi-Frequency) dialer, Bluebox, and Redbox.
|
||||
|
||||
Now in a release-ready state for both Dialer, Bluebox, and Redbox (US/UK) functionality!
|
||||
|
||||
Please note that using the current tone output method, the 2600 tone is scaled about 33 Hz higher than it should be. This is a limitation of the current sample rate.
|
||||
|
||||
### Educational Links:
|
||||
|
||||
* http://www.phrack.org/issues/25/7.html#article
|
||||
* https://en.wikipedia.org/wiki/Dual-tone_multi-frequency_signaling
|
||||
* https://en.wikipedia.org/wiki/Blue_box
|
||||
* https://en.wikipedia.org/wiki/Red_box_(phreaking)
|
||||
@@ -0,0 +1,16 @@
|
||||
App(
|
||||
appid="DTMF_Dolphin",
|
||||
name="DTMF Dolphin",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="dtmf_dolphin_app",
|
||||
cdefines=["DTMF_DOLPHIN"],
|
||||
requires=[
|
||||
"storage",
|
||||
"gui",
|
||||
"dialogs",
|
||||
],
|
||||
fap_icon="phone.png",
|
||||
stack_size=8 * 1024,
|
||||
order=20,
|
||||
fap_category="Tools",
|
||||
)
|
||||
|
After Width: | Height: | Size: 1.9 MiB |
@@ -0,0 +1,89 @@
|
||||
#include "dtmf_dolphin_i.h"
|
||||
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
static bool dtmf_dolphin_app_custom_event_callback(void* context, uint32_t event) {
|
||||
furi_assert(context);
|
||||
DTMFDolphinApp* app = context;
|
||||
return scene_manager_handle_custom_event(app->scene_manager, event);
|
||||
}
|
||||
|
||||
static bool dtmf_dolphin_app_back_event_callback(void* context) {
|
||||
furi_assert(context);
|
||||
DTMFDolphinApp* app = context;
|
||||
return scene_manager_handle_back_event(app->scene_manager);
|
||||
}
|
||||
|
||||
static void dtmf_dolphin_app_tick_event_callback(void* context) {
|
||||
furi_assert(context);
|
||||
DTMFDolphinApp* app = context;
|
||||
|
||||
scene_manager_handle_tick_event(app->scene_manager);
|
||||
}
|
||||
|
||||
static DTMFDolphinApp* app_alloc() {
|
||||
DTMFDolphinApp* app = malloc(sizeof(DTMFDolphinApp));
|
||||
|
||||
app->gui = furi_record_open(RECORD_GUI);
|
||||
app->view_dispatcher = view_dispatcher_alloc();
|
||||
app->scene_manager = scene_manager_alloc(&dtmf_dolphin_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, dtmf_dolphin_app_custom_event_callback);
|
||||
view_dispatcher_set_navigation_event_callback(
|
||||
app->view_dispatcher, dtmf_dolphin_app_back_event_callback);
|
||||
view_dispatcher_set_tick_event_callback(
|
||||
app->view_dispatcher, dtmf_dolphin_app_tick_event_callback, 100);
|
||||
|
||||
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
|
||||
|
||||
app->main_menu_list = variable_item_list_alloc();
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher,
|
||||
DTMFDolphinViewMainMenu,
|
||||
variable_item_list_get_view(app->main_menu_list));
|
||||
|
||||
app->dtmf_dolphin_dialer = dtmf_dolphin_dialer_alloc();
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher,
|
||||
DTMFDolphinViewDialer,
|
||||
dtmf_dolphin_dialer_get_view(app->dtmf_dolphin_dialer));
|
||||
|
||||
app->notification = furi_record_open(RECORD_NOTIFICATION);
|
||||
notification_message(app->notification, &sequence_display_backlight_enforce_on);
|
||||
|
||||
scene_manager_next_scene(app->scene_manager, DTMFDolphinSceneStart);
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
static void app_free(DTMFDolphinApp* app) {
|
||||
furi_assert(app);
|
||||
view_dispatcher_remove_view(app->view_dispatcher, DTMFDolphinViewMainMenu);
|
||||
view_dispatcher_remove_view(app->view_dispatcher, DTMFDolphinViewDialer);
|
||||
variable_item_list_free(app->main_menu_list);
|
||||
|
||||
dtmf_dolphin_dialer_free(app->dtmf_dolphin_dialer);
|
||||
|
||||
view_dispatcher_free(app->view_dispatcher);
|
||||
scene_manager_free(app->scene_manager);
|
||||
|
||||
notification_message(app->notification, &sequence_display_backlight_enforce_auto);
|
||||
|
||||
furi_record_close(RECORD_GUI);
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
free(app);
|
||||
}
|
||||
|
||||
int32_t dtmf_dolphin_app(void* p) {
|
||||
UNUSED(p);
|
||||
DTMFDolphinApp* app = app_alloc();
|
||||
|
||||
view_dispatcher_run(app->view_dispatcher);
|
||||
|
||||
app_free(app);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,265 @@
|
||||
#include "dtmf_dolphin_audio.h"
|
||||
|
||||
DTMFDolphinAudio* current_player;
|
||||
|
||||
static void dtmf_dolphin_audio_dma_isr(void* ctx) {
|
||||
FuriMessageQueue* event_queue = ctx;
|
||||
|
||||
if(LL_DMA_IsActiveFlag_HT1(DMA1)) {
|
||||
LL_DMA_ClearFlag_HT1(DMA1);
|
||||
|
||||
DTMFDolphinCustomEvent event = {.type = DTMFDolphinEventDMAHalfTransfer};
|
||||
furi_message_queue_put(event_queue, &event, 0);
|
||||
}
|
||||
|
||||
if(LL_DMA_IsActiveFlag_TC1(DMA1)) {
|
||||
LL_DMA_ClearFlag_TC1(DMA1);
|
||||
|
||||
DTMFDolphinCustomEvent event = {.type = DTMFDolphinEventDMAFullTransfer};
|
||||
furi_message_queue_put(event_queue, &event, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void dtmf_dolphin_audio_clear_samples(DTMFDolphinAudio* player) {
|
||||
for(size_t i = 0; i < player->buffer_length; i++) {
|
||||
player->sample_buffer[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
DTMFDolphinOsc* dtmf_dolphin_osc_alloc() {
|
||||
DTMFDolphinOsc* osc = malloc(sizeof(DTMFDolphinOsc));
|
||||
osc->cached_freq = 0;
|
||||
osc->offset = 0;
|
||||
osc->period = 0;
|
||||
osc->lookup_table = NULL;
|
||||
return osc;
|
||||
}
|
||||
|
||||
DTMFDolphinPulseFilter* dtmf_dolphin_pulse_filter_alloc() {
|
||||
DTMFDolphinPulseFilter* pf = malloc(sizeof(DTMFDolphinPulseFilter));
|
||||
pf->duration = 0;
|
||||
pf->period = 0;
|
||||
pf->offset = 0;
|
||||
pf->lookup_table = NULL;
|
||||
return pf;
|
||||
}
|
||||
|
||||
DTMFDolphinAudio* dtmf_dolphin_audio_alloc() {
|
||||
DTMFDolphinAudio* player = malloc(sizeof(DTMFDolphinAudio));
|
||||
player->buffer_length = SAMPLE_BUFFER_LENGTH;
|
||||
player->half_buffer_length = SAMPLE_BUFFER_LENGTH / 2;
|
||||
player->sample_buffer = malloc(sizeof(uint16_t) * player->buffer_length);
|
||||
player->osc1 = dtmf_dolphin_osc_alloc();
|
||||
player->osc2 = dtmf_dolphin_osc_alloc();
|
||||
player->volume = 1.0f;
|
||||
player->queue = furi_message_queue_alloc(10, sizeof(DTMFDolphinCustomEvent));
|
||||
player->filter = dtmf_dolphin_pulse_filter_alloc();
|
||||
player->playing = false;
|
||||
dtmf_dolphin_audio_clear_samples(player);
|
||||
|
||||
return player;
|
||||
}
|
||||
|
||||
size_t calc_waveform_period(float freq) {
|
||||
if(!freq) {
|
||||
return 0;
|
||||
}
|
||||
// DMA Rate calculation, thanks to Dr_Zlo
|
||||
float dma_rate = CPU_CLOCK_FREQ / 2 / DTMF_DOLPHIN_HAL_DMA_PRESCALER /
|
||||
(DTMF_DOLPHIN_HAL_DMA_AUTORELOAD + 1);
|
||||
|
||||
// Using a constant scaling modifier, which likely represents
|
||||
// the combined system overhead and isr latency.
|
||||
return (uint16_t)dma_rate * 2 / freq * 0.801923;
|
||||
}
|
||||
|
||||
void osc_generate_lookup_table(DTMFDolphinOsc* osc, float freq) {
|
||||
if(osc->lookup_table != NULL) {
|
||||
free(osc->lookup_table);
|
||||
}
|
||||
osc->offset = 0;
|
||||
osc->cached_freq = freq;
|
||||
osc->period = calc_waveform_period(freq);
|
||||
if(!osc->period) {
|
||||
osc->lookup_table = NULL;
|
||||
return;
|
||||
}
|
||||
osc->lookup_table = malloc(sizeof(float) * osc->period);
|
||||
|
||||
for(size_t i = 0; i < osc->period; i++) {
|
||||
osc->lookup_table[i] = sin(i * PERIOD_2_PI / osc->period) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
void filter_generate_lookup_table(
|
||||
DTMFDolphinPulseFilter* pf,
|
||||
uint16_t pulses,
|
||||
uint16_t pulse_ms,
|
||||
uint16_t gap_ms) {
|
||||
if(pf->lookup_table != NULL) {
|
||||
free(pf->lookup_table);
|
||||
}
|
||||
pf->offset = 0;
|
||||
|
||||
uint16_t gap_period = calc_waveform_period(1000 / (float)gap_ms);
|
||||
uint16_t pulse_period = calc_waveform_period(1000 / (float)pulse_ms);
|
||||
pf->period = pulse_period + gap_period;
|
||||
|
||||
if(!pf->period) {
|
||||
pf->lookup_table = NULL;
|
||||
return;
|
||||
}
|
||||
pf->duration = pf->period * pulses;
|
||||
pf->lookup_table = malloc(sizeof(bool) * pf->duration);
|
||||
|
||||
for(size_t i = 0; i < pf->duration; i++) {
|
||||
pf->lookup_table[i] = i % pf->period < pulse_period;
|
||||
}
|
||||
}
|
||||
|
||||
float sample_frame(DTMFDolphinOsc* osc) {
|
||||
float frame = 0.0;
|
||||
|
||||
if(osc->period) {
|
||||
frame = osc->lookup_table[osc->offset];
|
||||
osc->offset = (osc->offset + 1) % osc->period;
|
||||
}
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
bool sample_filter(DTMFDolphinPulseFilter* pf) {
|
||||
bool frame = true;
|
||||
|
||||
if(pf->duration) {
|
||||
if(pf->offset < pf->duration) {
|
||||
frame = pf->lookup_table[pf->offset];
|
||||
pf->offset = pf->offset + 1;
|
||||
} else {
|
||||
frame = false;
|
||||
}
|
||||
}
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
void dtmf_dolphin_osc_free(DTMFDolphinOsc* osc) {
|
||||
if(osc->lookup_table != NULL) {
|
||||
free(osc->lookup_table);
|
||||
}
|
||||
free(osc);
|
||||
}
|
||||
|
||||
void dtmf_dolphin_filter_free(DTMFDolphinPulseFilter* pf) {
|
||||
if(pf->lookup_table != NULL) {
|
||||
free(pf->lookup_table);
|
||||
}
|
||||
free(pf);
|
||||
}
|
||||
|
||||
void dtmf_dolphin_audio_free(DTMFDolphinAudio* player) {
|
||||
furi_message_queue_free(player->queue);
|
||||
dtmf_dolphin_osc_free(player->osc1);
|
||||
dtmf_dolphin_osc_free(player->osc2);
|
||||
dtmf_dolphin_filter_free(player->filter);
|
||||
free(player->sample_buffer);
|
||||
free(player);
|
||||
current_player = NULL;
|
||||
}
|
||||
|
||||
bool generate_waveform(DTMFDolphinAudio* player, uint16_t buffer_index) {
|
||||
uint16_t* sample_buffer_start = &player->sample_buffer[buffer_index];
|
||||
|
||||
for(size_t i = 0; i < player->half_buffer_length; i++) {
|
||||
float data = 0;
|
||||
if(player->osc2->period) {
|
||||
data = (sample_frame(player->osc1) / 2) + (sample_frame(player->osc2) / 2);
|
||||
} else {
|
||||
data = (sample_frame(player->osc1));
|
||||
}
|
||||
data *= sample_filter(player->filter) ? player->volume : 0.0;
|
||||
data *= UINT8_MAX / 2; // scale -128..127
|
||||
data += UINT8_MAX / 2; // to unsigned
|
||||
|
||||
if(data < 0) {
|
||||
data = 0;
|
||||
}
|
||||
|
||||
if(data > 255) {
|
||||
data = 255;
|
||||
}
|
||||
|
||||
sample_buffer_start[i] = data;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dtmf_dolphin_audio_play_tones(
|
||||
float freq1,
|
||||
float freq2,
|
||||
uint16_t pulses,
|
||||
uint16_t pulse_ms,
|
||||
uint16_t gap_ms) {
|
||||
if(current_player != NULL && current_player->playing) {
|
||||
// Cannot start playing while still playing something else
|
||||
return false;
|
||||
}
|
||||
current_player = dtmf_dolphin_audio_alloc();
|
||||
|
||||
osc_generate_lookup_table(current_player->osc1, freq1);
|
||||
osc_generate_lookup_table(current_player->osc2, freq2);
|
||||
filter_generate_lookup_table(current_player->filter, pulses, pulse_ms, gap_ms);
|
||||
|
||||
generate_waveform(current_player, 0);
|
||||
generate_waveform(current_player, current_player->half_buffer_length);
|
||||
|
||||
dtmf_dolphin_speaker_init();
|
||||
dtmf_dolphin_dma_init((uint32_t)current_player->sample_buffer, current_player->buffer_length);
|
||||
|
||||
furi_hal_interrupt_set_isr(
|
||||
FuriHalInterruptIdDma1Ch1, dtmf_dolphin_audio_dma_isr, current_player->queue);
|
||||
|
||||
dtmf_dolphin_dma_start();
|
||||
dtmf_dolphin_speaker_start();
|
||||
current_player->playing = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dtmf_dolphin_audio_stop_tones() {
|
||||
if(current_player != NULL && !current_player->playing) {
|
||||
// Can't stop a player that isn't playing.
|
||||
return false;
|
||||
}
|
||||
while(current_player->filter->offset > 0 &&
|
||||
current_player->filter->offset < current_player->filter->duration) {
|
||||
// run remaining ticks if needed to complete filter sequence
|
||||
dtmf_dolphin_audio_handle_tick();
|
||||
}
|
||||
dtmf_dolphin_speaker_stop();
|
||||
dtmf_dolphin_dma_stop();
|
||||
|
||||
furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, NULL, NULL);
|
||||
|
||||
dtmf_dolphin_audio_free(current_player);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dtmf_dolphin_audio_handle_tick() {
|
||||
bool handled = false;
|
||||
|
||||
if(current_player) {
|
||||
DTMFDolphinCustomEvent event;
|
||||
if(furi_message_queue_get(current_player->queue, &event, 250) == FuriStatusOk) {
|
||||
if(event.type == DTMFDolphinEventDMAHalfTransfer) {
|
||||
generate_waveform(current_player, 0);
|
||||
handled = true;
|
||||
} else if(event.type == DTMFDolphinEventDMAFullTransfer) {
|
||||
generate_waveform(current_player, current_player->half_buffer_length);
|
||||
handled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return handled;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
// #include "dtmf_dolphin_i.h"
|
||||
#include "dtmf_dolphin_event.h"
|
||||
#include "dtmf_dolphin_hal.h"
|
||||
|
||||
#define SAMPLE_BUFFER_LENGTH 8192
|
||||
#define PERIOD_2_PI 6.2832
|
||||
#define CPU_CLOCK_FREQ 64000000
|
||||
|
||||
typedef struct {
|
||||
float cached_freq;
|
||||
size_t period;
|
||||
float* lookup_table;
|
||||
uint16_t offset;
|
||||
} DTMFDolphinOsc;
|
||||
|
||||
typedef struct {
|
||||
float duration;
|
||||
size_t period;
|
||||
bool* lookup_table;
|
||||
uint16_t offset;
|
||||
} DTMFDolphinPulseFilter;
|
||||
|
||||
typedef struct {
|
||||
size_t buffer_length;
|
||||
size_t half_buffer_length;
|
||||
uint8_t* buffer_buffer;
|
||||
uint16_t* sample_buffer;
|
||||
float volume;
|
||||
FuriMessageQueue* queue;
|
||||
DTMFDolphinOsc* osc1;
|
||||
DTMFDolphinOsc* osc2;
|
||||
DTMFDolphinPulseFilter* filter;
|
||||
bool playing;
|
||||
} DTMFDolphinAudio;
|
||||
|
||||
DTMFDolphinOsc* dtmf_dolphin_osc_alloc();
|
||||
|
||||
DTMFDolphinAudio* dtmf_dolphin_audio_alloc();
|
||||
|
||||
void dtmf_dolphin_audio_free(DTMFDolphinAudio* player);
|
||||
|
||||
void dtmf_dolphin_osc_free(DTMFDolphinOsc* osc);
|
||||
|
||||
bool dtmf_dolphin_audio_play_tones(
|
||||
float freq1,
|
||||
float freq2,
|
||||
uint16_t pulses,
|
||||
uint16_t pulse_ms,
|
||||
uint16_t gap_ms);
|
||||
|
||||
bool dtmf_dolphin_audio_stop_tones();
|
||||
|
||||
bool dtmf_dolphin_audio_handle_tick();
|
||||
@@ -0,0 +1,220 @@
|
||||
#include "dtmf_dolphin_data.h"
|
||||
|
||||
typedef struct {
|
||||
const uint8_t row;
|
||||
const uint8_t col;
|
||||
const uint8_t span;
|
||||
} DTMFDolphinTonePos;
|
||||
|
||||
typedef struct {
|
||||
const char* name;
|
||||
const float frequency_1;
|
||||
const float frequency_2;
|
||||
const DTMFDolphinTonePos pos;
|
||||
const uint16_t pulses; // for Redbox
|
||||
const uint16_t pulse_ms; // for Redbox
|
||||
const uint16_t gap_duration; // for Redbox
|
||||
} DTMFDolphinTones;
|
||||
|
||||
typedef struct {
|
||||
const char* name;
|
||||
DTMFDolphinToneSection block;
|
||||
uint8_t tone_count;
|
||||
DTMFDolphinTones tones[DTMF_DOLPHIN_MAX_TONE_COUNT];
|
||||
} DTMFDolphinSceneData;
|
||||
|
||||
DTMFDolphinSceneData DTMFDolphinSceneDataDialer = {
|
||||
.name = "Dialer",
|
||||
.block = DTMF_DOLPHIN_TONE_BLOCK_DIALER,
|
||||
.tone_count = 16,
|
||||
.tones = {
|
||||
{"1", 697.0, 1209.0, {0, 0, 1}, 0, 0, 0},
|
||||
{"2", 697.0, 1336.0, {0, 1, 1}, 0, 0, 0},
|
||||
{"3", 697.0, 1477.0, {0, 2, 1}, 0, 0, 0},
|
||||
{"A", 697.0, 1633.0, {0, 3, 1}, 0, 0, 0},
|
||||
{"4", 770.0, 1209.0, {1, 0, 1}, 0, 0, 0},
|
||||
{"5", 770.0, 1336.0, {1, 1, 1}, 0, 0, 0},
|
||||
{"6", 770.0, 1477.0, {1, 2, 1}, 0, 0, 0},
|
||||
{"B", 770.0, 1633.0, {1, 3, 1}, 0, 0, 0},
|
||||
{"7", 852.0, 1209.0, {2, 0, 1}, 0, 0, 0},
|
||||
{"8", 852.0, 1336.0, {2, 1, 1}, 0, 0, 0},
|
||||
{"9", 852.0, 1477.0, {2, 2, 1}, 0, 0, 0},
|
||||
{"C", 852.0, 1633.0, {2, 3, 1}, 0, 0, 0},
|
||||
{"*", 941.0, 1209.0, {3, 0, 1}, 0, 0, 0},
|
||||
{"0", 941.0, 1336.0, {3, 1, 1}, 0, 0, 0},
|
||||
{"#", 941.0, 1477.0, {3, 2, 1}, 0, 0, 0},
|
||||
{"D", 941.0, 1633.0, {3, 3, 1}, 0, 0, 0},
|
||||
}};
|
||||
|
||||
DTMFDolphinSceneData DTMFDolphinSceneDataBluebox = {
|
||||
.name = "Bluebox",
|
||||
.block = DTMF_DOLPHIN_TONE_BLOCK_BLUEBOX,
|
||||
.tone_count = 13,
|
||||
.tones = {
|
||||
{"1", 700.0, 900.0, {0, 0, 1}, 0, 0, 0},
|
||||
{"2", 700.0, 1100.0, {0, 1, 1}, 0, 0, 0},
|
||||
{"3", 900.0, 1100.0, {0, 2, 1}, 0, 0, 0},
|
||||
{"4", 700.0, 1300.0, {1, 0, 1}, 0, 0, 0},
|
||||
{"5", 900.0, 1300.0, {1, 1, 1}, 0, 0, 0},
|
||||
{"6", 1100.0, 1300.0, {1, 2, 1}, 0, 0, 0},
|
||||
{"7", 700.0, 1500.0, {2, 0, 1}, 0, 0, 0},
|
||||
{"8", 900.0, 1500.0, {2, 1, 1}, 0, 0, 0},
|
||||
{"9", 1100.0, 1500.0, {2, 2, 1}, 0, 0, 0},
|
||||
{"0", 1300.0, 1500.0, {3, 1, 1}, 0, 0, 0},
|
||||
{"KP", 1100.0, 1700.0, {0, 3, 2}, 0, 0, 0},
|
||||
{"ST", 1500.0, 1700.0, {1, 3, 2}, 0, 0, 0},
|
||||
{"2600", 2600.0, 0.0, {3, 2, 3}, 0, 0, 0},
|
||||
}};
|
||||
|
||||
DTMFDolphinSceneData DTMFDolphinSceneDataRedboxUS = {
|
||||
.name = "Redbox (US)",
|
||||
.block = DTMF_DOLPHIN_TONE_BLOCK_REDBOX_US,
|
||||
.tone_count = 4,
|
||||
.tones = {
|
||||
{"Nickel", 1700.0, 2200.0, {0, 0, 5}, 1, 66, 0},
|
||||
{"Dime", 1700.0, 2200.0, {1, 0, 5}, 2, 66, 66},
|
||||
{"Quarter", 1700.0, 2200.0, {2, 0, 5}, 5, 33, 33},
|
||||
{"Dollar", 1700.0, 2200.0, {3, 0, 5}, 1, 650, 0},
|
||||
}};
|
||||
|
||||
DTMFDolphinSceneData DTMFDolphinSceneDataRedboxCA = {
|
||||
.name = "Redbox (CA)",
|
||||
.block = DTMF_DOLPHIN_TONE_BLOCK_REDBOX_CA,
|
||||
.tone_count = 3,
|
||||
.tones = {
|
||||
{"Nickel", 2200.0, 0.0, {0, 0, 5}, 1, 66, 0},
|
||||
{"Dime", 2200.0, 0.0, {1, 0, 5}, 2, 66, 66},
|
||||
{"Quarter", 2200.0, 0.0, {2, 0, 5}, 5, 33, 33},
|
||||
}};
|
||||
|
||||
DTMFDolphinSceneData DTMFDolphinSceneDataRedboxUK = {
|
||||
.name = "Redbox (UK)",
|
||||
.block = DTMF_DOLPHIN_TONE_BLOCK_REDBOX_UK,
|
||||
.tone_count = 2,
|
||||
.tones = {
|
||||
{"10p", 1000.0, 0.0, {0, 0, 5}, 1, 200, 0},
|
||||
{"50p", 1000.0, 0.0, {1, 0, 5}, 1, 350, 0},
|
||||
}};
|
||||
|
||||
DTMFDolphinSceneData DTMFDolphinSceneDataMisc = {
|
||||
.name = "Misc",
|
||||
.block = DTMF_DOLPHIN_TONE_BLOCK_MISC,
|
||||
.tone_count = 3,
|
||||
.tones = {
|
||||
{"CCITT 11", 700.0, 1700.0, {0, 0, 5}, 0, 0, 0},
|
||||
{"CCITT 12", 900.0, 1700.0, {1, 0, 5}, 0, 0, 0},
|
||||
{"CCITT KP2", 1300.0, 1700.0, {2, 0, 5}, 0, 0, 0},
|
||||
}};
|
||||
|
||||
DTMFDolphinToneSection current_section;
|
||||
DTMFDolphinSceneData* current_scene_data;
|
||||
|
||||
void dtmf_dolphin_data_set_current_section(DTMFDolphinToneSection section) {
|
||||
current_section = section;
|
||||
|
||||
switch(current_section) {
|
||||
case DTMF_DOLPHIN_TONE_BLOCK_BLUEBOX:
|
||||
current_scene_data = &DTMFDolphinSceneDataBluebox;
|
||||
break;
|
||||
case DTMF_DOLPHIN_TONE_BLOCK_REDBOX_US:
|
||||
current_scene_data = &DTMFDolphinSceneDataRedboxUS;
|
||||
break;
|
||||
case DTMF_DOLPHIN_TONE_BLOCK_REDBOX_CA:
|
||||
current_scene_data = &DTMFDolphinSceneDataRedboxCA;
|
||||
break;
|
||||
case DTMF_DOLPHIN_TONE_BLOCK_REDBOX_UK:
|
||||
current_scene_data = &DTMFDolphinSceneDataRedboxUK;
|
||||
break;
|
||||
case DTMF_DOLPHIN_TONE_BLOCK_MISC:
|
||||
current_scene_data = &DTMFDolphinSceneDataMisc;
|
||||
break;
|
||||
default: // DTMF_DOLPHIN_TONE_BLOCK_DIALER:
|
||||
current_scene_data = &DTMFDolphinSceneDataDialer;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
DTMFDolphinToneSection dtmf_dolphin_data_get_current_section() {
|
||||
return current_section;
|
||||
}
|
||||
|
||||
DTMFDolphinSceneData* dtmf_dolphin_data_get_current_scene_data() {
|
||||
return current_scene_data;
|
||||
}
|
||||
|
||||
bool dtmf_dolphin_data_get_tone_frequencies(float* freq1, float* freq2, uint8_t row, uint8_t col) {
|
||||
for(size_t i = 0; i < current_scene_data->tone_count; i++) {
|
||||
DTMFDolphinTones tones = current_scene_data->tones[i];
|
||||
if(tones.pos.row == row && tones.pos.col == col) {
|
||||
freq1[0] = tones.frequency_1;
|
||||
freq2[0] = tones.frequency_2;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool dtmf_dolphin_data_get_filter_data(
|
||||
uint16_t* pulses,
|
||||
uint16_t* pulse_ms,
|
||||
uint16_t* gap_ms,
|
||||
uint8_t row,
|
||||
uint8_t col) {
|
||||
for(size_t i = 0; i < current_scene_data->tone_count; i++) {
|
||||
DTMFDolphinTones tones = current_scene_data->tones[i];
|
||||
if(tones.pos.row == row && tones.pos.col == col) {
|
||||
pulses[0] = tones.pulses;
|
||||
pulse_ms[0] = tones.pulse_ms;
|
||||
gap_ms[0] = tones.gap_duration;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* dtmf_dolphin_data_get_tone_name(uint8_t row, uint8_t col) {
|
||||
for(size_t i = 0; i < current_scene_data->tone_count; i++) {
|
||||
DTMFDolphinTones tones = current_scene_data->tones[i];
|
||||
if(tones.pos.row == row && tones.pos.col == col) {
|
||||
return tones.name;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char* dtmf_dolphin_data_get_current_section_name() {
|
||||
if(current_scene_data) {
|
||||
return current_scene_data->name;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void dtmf_dolphin_tone_get_max_pos(uint8_t* max_rows, uint8_t* max_cols, uint8_t* max_span) {
|
||||
max_rows[0] = 0;
|
||||
max_cols[0] = 0;
|
||||
max_span[0] = 0;
|
||||
uint8_t tmp_rowspan[5] = {0, 0, 0, 0, 0};
|
||||
for(size_t i = 0; i < current_scene_data->tone_count; i++) {
|
||||
DTMFDolphinTones tones = current_scene_data->tones[i];
|
||||
if(tones.pos.row > max_rows[0]) {
|
||||
max_rows[0] = tones.pos.row;
|
||||
}
|
||||
if(tones.pos.col > max_cols[0]) {
|
||||
max_cols[0] = tones.pos.col;
|
||||
}
|
||||
tmp_rowspan[tones.pos.row] += tones.pos.span;
|
||||
if(tmp_rowspan[tones.pos.row] > max_span[0]) max_span[0] = tmp_rowspan[tones.pos.row];
|
||||
}
|
||||
max_rows[0]++;
|
||||
max_cols[0]++;
|
||||
}
|
||||
|
||||
uint8_t dtmf_dolphin_get_tone_span(uint8_t row, uint8_t col) {
|
||||
for(size_t i = 0; i < current_scene_data->tone_count; i++) {
|
||||
DTMFDolphinTones tones = current_scene_data->tones[i];
|
||||
if(tones.pos.row == row && tones.pos.col == col) {
|
||||
return tones.pos.span;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define DTMF_DOLPHIN_MAX_TONE_COUNT 16
|
||||
|
||||
typedef enum {
|
||||
DTMF_DOLPHIN_TONE_BLOCK_DIALER,
|
||||
DTMF_DOLPHIN_TONE_BLOCK_BLUEBOX,
|
||||
DTMF_DOLPHIN_TONE_BLOCK_REDBOX_US,
|
||||
DTMF_DOLPHIN_TONE_BLOCK_REDBOX_UK,
|
||||
DTMF_DOLPHIN_TONE_BLOCK_REDBOX_CA,
|
||||
DTMF_DOLPHIN_TONE_BLOCK_MISC,
|
||||
} DTMFDolphinToneSection;
|
||||
|
||||
void dtmf_dolphin_data_set_current_section(DTMFDolphinToneSection section);
|
||||
|
||||
DTMFDolphinToneSection dtmf_dolphin_data_get_current_section();
|
||||
|
||||
bool dtmf_dolphin_data_get_tone_frequencies(float* freq1, float* freq2, uint8_t row, uint8_t col);
|
||||
|
||||
bool dtmf_dolphin_data_get_filter_data(
|
||||
uint16_t* pulses,
|
||||
uint16_t* pulse_ms,
|
||||
uint16_t* gap_ms,
|
||||
uint8_t row,
|
||||
uint8_t col);
|
||||
|
||||
const char* dtmf_dolphin_data_get_tone_name(uint8_t row, uint8_t col);
|
||||
|
||||
const char* dtmf_dolphin_data_get_current_section_name();
|
||||
|
||||
void dtmf_dolphin_tone_get_max_pos(uint8_t* max_rows, uint8_t* max_cols, uint8_t* max_span);
|
||||
|
||||
uint8_t dtmf_dolphin_get_tone_span(uint8_t row, uint8_t col);
|
||||
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
typedef enum {
|
||||
DTMFDolphinEventVolumeUp = 0,
|
||||
DTMFDolphinEventVolumeDown,
|
||||
DTMFDolphinDialerOkCB,
|
||||
DTMFDolphinEventStartDialer,
|
||||
DTMFDolphinEventStartBluebox,
|
||||
DTMFDolphinEventStartRedboxUS,
|
||||
DTMFDolphinEventStartRedboxUK,
|
||||
DTMFDolphinEventStartRedboxCA,
|
||||
DTMFDolphinEventStartMisc,
|
||||
DTMFDolphinEventPlayTones,
|
||||
DTMFDolphinEventStopTones,
|
||||
DTMFDolphinEventDMAHalfTransfer,
|
||||
DTMFDolphinEventDMAFullTransfer,
|
||||
} DTMFDolphinEvent;
|
||||
|
||||
typedef struct {
|
||||
DTMFDolphinEvent type;
|
||||
} DTMFDolphinCustomEvent;
|
||||
@@ -0,0 +1,52 @@
|
||||
#include "dtmf_dolphin_hal.h"
|
||||
|
||||
void dtmf_dolphin_speaker_init() {
|
||||
LL_TIM_InitTypeDef TIM_InitStruct = {0};
|
||||
TIM_InitStruct.Prescaler = DTMF_DOLPHIN_HAL_DMA_PRESCALER;
|
||||
TIM_InitStruct.Autoreload = DTMF_DOLPHIN_HAL_DMA_AUTORELOAD;
|
||||
LL_TIM_Init(FURI_HAL_SPEAKER_TIMER, &TIM_InitStruct);
|
||||
|
||||
LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {0};
|
||||
TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_PWM1;
|
||||
TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_ENABLE;
|
||||
TIM_OC_InitStruct.CompareValue = 127;
|
||||
LL_TIM_OC_Init(FURI_HAL_SPEAKER_TIMER, FURI_HAL_SPEAKER_CHANNEL, &TIM_OC_InitStruct);
|
||||
}
|
||||
|
||||
void dtmf_dolphin_speaker_start() {
|
||||
LL_TIM_EnableAllOutputs(FURI_HAL_SPEAKER_TIMER);
|
||||
LL_TIM_EnableCounter(FURI_HAL_SPEAKER_TIMER);
|
||||
}
|
||||
|
||||
void dtmf_dolphin_speaker_stop() {
|
||||
LL_TIM_DisableAllOutputs(FURI_HAL_SPEAKER_TIMER);
|
||||
LL_TIM_DisableCounter(FURI_HAL_SPEAKER_TIMER);
|
||||
}
|
||||
|
||||
void dtmf_dolphin_dma_init(uint32_t address, size_t size) {
|
||||
uint32_t dma_dst = (uint32_t) & (FURI_HAL_SPEAKER_TIMER->CCR1);
|
||||
|
||||
LL_DMA_ConfigAddresses(DMA_INSTANCE, address, dma_dst, LL_DMA_DIRECTION_MEMORY_TO_PERIPH);
|
||||
LL_DMA_SetDataLength(DMA_INSTANCE, size);
|
||||
|
||||
LL_DMA_SetPeriphRequest(DMA_INSTANCE, LL_DMAMUX_REQ_TIM16_UP);
|
||||
LL_DMA_SetDataTransferDirection(DMA_INSTANCE, LL_DMA_DIRECTION_MEMORY_TO_PERIPH);
|
||||
LL_DMA_SetChannelPriorityLevel(DMA_INSTANCE, LL_DMA_PRIORITY_VERYHIGH);
|
||||
LL_DMA_SetMode(DMA_INSTANCE, LL_DMA_MODE_CIRCULAR);
|
||||
LL_DMA_SetPeriphIncMode(DMA_INSTANCE, LL_DMA_PERIPH_NOINCREMENT);
|
||||
LL_DMA_SetMemoryIncMode(DMA_INSTANCE, LL_DMA_MEMORY_INCREMENT);
|
||||
LL_DMA_SetPeriphSize(DMA_INSTANCE, LL_DMA_PDATAALIGN_HALFWORD);
|
||||
LL_DMA_SetMemorySize(DMA_INSTANCE, LL_DMA_MDATAALIGN_HALFWORD);
|
||||
|
||||
LL_DMA_EnableIT_TC(DMA_INSTANCE);
|
||||
LL_DMA_EnableIT_HT(DMA_INSTANCE);
|
||||
}
|
||||
|
||||
void dtmf_dolphin_dma_start() {
|
||||
LL_DMA_EnableChannel(DMA_INSTANCE);
|
||||
LL_TIM_EnableDMAReq_UPDATE(FURI_HAL_SPEAKER_TIMER);
|
||||
}
|
||||
|
||||
void dtmf_dolphin_dma_stop() {
|
||||
LL_DMA_DisableChannel(DMA_INSTANCE);
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <stm32wb55xx.h>
|
||||
#include <stm32wbxx_ll_tim.h>
|
||||
#include <stm32wbxx_ll_dma.h>
|
||||
|
||||
#define FURI_HAL_SPEAKER_TIMER TIM16
|
||||
#define FURI_HAL_SPEAKER_CHANNEL LL_TIM_CHANNEL_CH1
|
||||
#define DMA_INSTANCE DMA1, LL_DMA_CHANNEL_1
|
||||
|
||||
#define DTMF_DOLPHIN_HAL_DMA_PRESCALER 4
|
||||
#define DTMF_DOLPHIN_HAL_DMA_AUTORELOAD 255
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void dtmf_dolphin_speaker_init();
|
||||
|
||||
void dtmf_dolphin_speaker_start();
|
||||
|
||||
void dtmf_dolphin_speaker_stop();
|
||||
|
||||
void dtmf_dolphin_dma_init(uint32_t address, size_t size);
|
||||
|
||||
void dtmf_dolphin_dma_start();
|
||||
|
||||
void dtmf_dolphin_dma_stop();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#include "scenes/dtmf_dolphin_scene.h"
|
||||
|
||||
#include <gui/gui.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
#include <gui/scene_manager.h>
|
||||
// #include <gui/modules/submenu.h>
|
||||
// #include <gui/modules/widget.h>
|
||||
#include <gui/modules/variable_item_list.h>
|
||||
#include <notification/notification_messages.h>
|
||||
#include <input/input.h>
|
||||
|
||||
#include "dtmf_dolphin_event.h"
|
||||
|
||||
#include "views/dtmf_dolphin_dialer.h"
|
||||
|
||||
#define TAG "DTMFDolphin"
|
||||
|
||||
enum DTMFDolphinSceneState {
|
||||
DTMFDolphinSceneStateDialer,
|
||||
DTMFDolphinSceneStateBluebox,
|
||||
DTMFDolphinSceneStateRedboxUS,
|
||||
DTMFDolphinSceneStateRedboxUK,
|
||||
DTMFDolphinSceneStateRedboxCA,
|
||||
DTMFDolphinSceneStateMisc,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
ViewDispatcher* view_dispatcher;
|
||||
SceneManager* scene_manager;
|
||||
VariableItemList* main_menu_list;
|
||||
DTMFDolphinDialer* dtmf_dolphin_dialer;
|
||||
|
||||
Gui* gui;
|
||||
// ButtonPanel* dialer_button_panel;
|
||||
// ButtonPanel* bluebox_button_panel;
|
||||
// ButtonPanel* redbox_button_panel;
|
||||
NotificationApp* notification;
|
||||
} DTMFDolphinApp;
|
||||
|
||||
typedef enum { DTMFDolphinViewMainMenu, DTMFDolphinViewDialer } DTMFDolphinView;
|
||||
|
After Width: | Height: | Size: 306 B |