Merge branch 'release-candidate' into release
This commit is contained in:
commit
fd29e190b7
19
.github/workflows/build.yml
vendored
19
.github/workflows/build.yml
vendored
@ -10,7 +10,8 @@ on:
|
||||
pull_request:
|
||||
|
||||
env:
|
||||
TARGETS: f6
|
||||
TARGETS: f6 f7
|
||||
DEFAULT_TARGET: f6
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@ -69,7 +70,7 @@ jobs:
|
||||
echo "::set-output name=artifacts-path::${BRANCH_OR_TAG}"
|
||||
echo "::set-output name=suffix::${SUFFIX}"
|
||||
echo "::set-output name=short-hash::${SHA}"
|
||||
echo "::set-output name=latest-target::${TARGETS[${#TARGETS[@]}-1]}"
|
||||
echo "::set-output name=default-target::${DEFAULT_TARGET}"
|
||||
|
||||
- name: 'Build bootloader in docker'
|
||||
uses: ./.github/actions/docker
|
||||
@ -169,12 +170,7 @@ jobs:
|
||||
run: |
|
||||
test -d core2_firmware && rm -rf core2_firmware || true
|
||||
mkdir core2_firmware
|
||||
cp \
|
||||
lib/STM32CubeWB/package.xml \
|
||||
lib/STM32CubeWB/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x/stm32wb5x_FUS_fw.bin \
|
||||
lib/STM32CubeWB/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x/stm32wb5x_FUS_fw_for_fus_0_5_3.bin \
|
||||
lib/STM32CubeWB/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x/stm32wb5x_BLE_Stack_full_fw.bin \
|
||||
core2_firmware
|
||||
./scripts/assets.py copro lib/STM32CubeWB core2_firmware STM32WB5x
|
||||
tar czpf artifacts/flipper-z-any-core2_firmware-${{steps.names.outputs.suffix}}.tgz core2_firmware
|
||||
|
||||
- name: 'Bundle scripts'
|
||||
@ -185,6 +181,7 @@ jobs:
|
||||
- name: 'Bundle resources'
|
||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||
run: |
|
||||
./scripts/assets.py manifest assets/resources
|
||||
tar czpf artifacts/flipper-z-any-resources-${{steps.names.outputs.suffix}}.tgz -C assets resources
|
||||
|
||||
- name: 'Upload artifacts to update server'
|
||||
@ -205,7 +202,7 @@ jobs:
|
||||
with:
|
||||
args: -X POST -F 'key=${{ secrets.REINDEX_KEY }}' ${{ secrets.REINDEX_URL }}
|
||||
|
||||
- name: Find Previous Comment
|
||||
- name: 'Find Previous Comment'
|
||||
if: ${{ !github.event.pull_request.head.repo.fork && github.event.pull_request }}
|
||||
uses: peter-evans/find-comment@v1
|
||||
id: fc
|
||||
@ -214,12 +211,12 @@ jobs:
|
||||
comment-author: 'github-actions[bot]'
|
||||
body-includes: 'to flash the'
|
||||
|
||||
- name: Create or update comment
|
||||
- name: 'Create or update comment'
|
||||
if: ${{ !github.event.pull_request.head.repo.fork && github.event.pull_request}}
|
||||
uses: peter-evans/create-or-update-comment@v1
|
||||
with:
|
||||
comment-id: ${{ steps.fc.outputs.comment-id }}
|
||||
issue-number: ${{ github.event.pull_request.number }}
|
||||
body: |
|
||||
[Click here](https://update.flipperzero.one/?url=https://update.flipperzero.one/builds/firmware/${{steps.names.outputs.artifacts-path}}/flipper-z-${{steps.names.outputs.latest-target}}-full-${{steps.names.outputs.suffix}}.dfu&channel=${{steps.names.outputs.artifacts-path}}&version=${{steps.names.outputs.short-hash}}) to flash the `${{steps.names.outputs.short-hash}}` version of this branch via WebUSB.
|
||||
[Click here](https://update.flipperzero.one/?url=https://update.flipperzero.one/builds/firmware/${{steps.names.outputs.artifacts-path}}/flipper-z-${{steps.names.outputs.default-target}}-full-${{steps.names.outputs.suffix}}.dfu&channel=${{steps.names.outputs.artifacts-path}}&version=${{steps.names.outputs.short-hash}}) to flash the `${{steps.names.outputs.short-hash}}` version of this branch via WebUSB.
|
||||
edit-mode: replace
|
||||
|
||||
2
.github/workflows/lint_c.yml
vendored
2
.github/workflows/lint_c.yml
vendored
@ -10,7 +10,7 @@ on:
|
||||
pull_request:
|
||||
|
||||
env:
|
||||
TARGETS: f6
|
||||
TARGETS: f6 f7
|
||||
|
||||
jobs:
|
||||
lint_c_cpp:
|
||||
|
||||
12
ReadMe.md
12
ReadMe.md
@ -14,7 +14,7 @@ Our goal is to create nice and clean code with good documentation, to make it a
|
||||
|
||||
Flipper Zero's firmware consists of three components:
|
||||
|
||||
- Core2 firmware set - proprietary componenets by ST: FUS + radio stack.
|
||||
- Core2 firmware set - proprietary components by ST: FUS + radio stack.
|
||||
- Core1 Bootloader - controls basic hardware initialization and loads firmware
|
||||
- Core1 Firmware - HAL + OS + Drivers + Applications
|
||||
|
||||
@ -67,13 +67,13 @@ One liner: `./flash_core1_main.sh`
|
||||
```
|
||||
3. Prepare the container:
|
||||
```sh
|
||||
docker compose up -d
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
## Compile bootloader
|
||||
|
||||
```sh
|
||||
docker compose exec dev make -j$(nproc) -C bootloader TARGET=f6
|
||||
docker-compose exec dev make -j$(nproc) -C bootloader TARGET=f6
|
||||
```
|
||||
|
||||
Bootloader compilation results:
|
||||
@ -85,7 +85,7 @@ Bootloader compilation results:
|
||||
## Compile firmware
|
||||
|
||||
```sh
|
||||
docker compose exec dev make -j$(nproc) -C firmware TARGET=f6
|
||||
docker-compose exec dev make -j$(nproc) -C firmware TARGET=f6
|
||||
```
|
||||
|
||||
Firmware compilation results:
|
||||
@ -102,14 +102,14 @@ That's exactly how we generate our `full` builds.
|
||||
|
||||
1. Concatenate HEX files:
|
||||
```sh
|
||||
docker compose exec dev srec_cat \
|
||||
docker-compose exec dev srec_cat \
|
||||
bootloader/.obj/f6/bootloader.hex -Intel \
|
||||
firmware/.obj/f6/firmware.hex -Intel \
|
||||
-o firmware/.obj/f6/full.hex -Intel
|
||||
```
|
||||
2. Convert HEX to DFU:
|
||||
```sh
|
||||
docker compose exec dev hex2dfu \
|
||||
docker-compose exec dev hex2dfu \
|
||||
-i firmware/.obj/f6/full.hex \
|
||||
-o firmware/.obj/f6/full.dfu \
|
||||
-l "Flipper Zero F6"
|
||||
|
||||
@ -1,831 +1,69 @@
|
||||
#include "archive_i.h"
|
||||
|
||||
static bool archive_get_filenames(ArchiveApp* archive);
|
||||
|
||||
static void update_offset(ArchiveApp* archive) {
|
||||
furi_assert(archive);
|
||||
|
||||
with_view_model(
|
||||
archive->view_archive_main, (ArchiveViewModel * model) {
|
||||
size_t array_size = files_array_size(model->files);
|
||||
uint16_t bounds = array_size > 3 ? 2 : array_size;
|
||||
|
||||
if(array_size > 3 && model->idx >= array_size - 1) {
|
||||
model->list_offset = model->idx - 3;
|
||||
} else if(model->list_offset < model->idx - bounds) {
|
||||
model->list_offset = CLAMP(model->list_offset + 1, array_size - bounds, 0);
|
||||
} else if(model->list_offset > model->idx - bounds) {
|
||||
model->list_offset = CLAMP(model->idx - 1, array_size - bounds, 0);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
static void archive_update_last_idx(ArchiveApp* archive) {
|
||||
furi_assert(archive);
|
||||
|
||||
with_view_model(
|
||||
archive->view_archive_main, (ArchiveViewModel * model) {
|
||||
archive->browser.last_idx[archive->browser.depth] =
|
||||
CLAMP(model->idx, files_array_size(model->files) - 1, 0);
|
||||
model->idx = 0;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
static void archive_switch_dir(ArchiveApp* archive, const char* path) {
|
||||
furi_assert(archive);
|
||||
furi_assert(path);
|
||||
string_set(archive->browser.path, path);
|
||||
archive_get_filenames(archive);
|
||||
update_offset(archive);
|
||||
}
|
||||
|
||||
static void archive_switch_tab(ArchiveApp* archive) {
|
||||
furi_assert(archive);
|
||||
|
||||
with_view_model(
|
||||
archive->view_archive_main, (ArchiveViewModel * model) {
|
||||
model->tab_idx = archive->browser.tab_id;
|
||||
model->idx = 0;
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
archive->browser.depth = 0;
|
||||
archive_switch_dir(archive, tab_default_paths[archive->browser.tab_id]);
|
||||
}
|
||||
|
||||
static void archive_leave_dir(ArchiveApp* archive) {
|
||||
furi_assert(archive);
|
||||
|
||||
char* last_char_ptr = strrchr(string_get_cstr(archive->browser.path), '/');
|
||||
|
||||
if(last_char_ptr) {
|
||||
size_t pos = last_char_ptr - string_get_cstr(archive->browser.path);
|
||||
string_left(archive->browser.path, pos);
|
||||
}
|
||||
|
||||
archive->browser.depth = CLAMP(archive->browser.depth - 1, MAX_DEPTH, 0);
|
||||
|
||||
with_view_model(
|
||||
archive->view_archive_main, (ArchiveViewModel * model) {
|
||||
model->idx = archive->browser.last_idx[archive->browser.depth];
|
||||
model->list_offset =
|
||||
model->idx -
|
||||
(files_array_size(model->files) > 3 ? 3 : files_array_size(model->files));
|
||||
return true;
|
||||
});
|
||||
|
||||
archive_switch_dir(archive, string_get_cstr(archive->browser.path));
|
||||
update_offset(archive);
|
||||
}
|
||||
|
||||
static void archive_enter_dir(ArchiveApp* archive, string_t name) {
|
||||
furi_assert(archive);
|
||||
furi_assert(name);
|
||||
|
||||
archive_update_last_idx(archive);
|
||||
archive->browser.depth = CLAMP(archive->browser.depth + 1, MAX_DEPTH, 0);
|
||||
|
||||
string_cat(archive->browser.path, "/");
|
||||
string_cat(archive->browser.path, archive->browser.name);
|
||||
|
||||
archive_switch_dir(archive, string_get_cstr(archive->browser.path));
|
||||
}
|
||||
|
||||
static bool filter_by_extension(ArchiveApp* archive, FileInfo* file_info, const char* name) {
|
||||
furi_assert(archive);
|
||||
furi_assert(file_info);
|
||||
furi_assert(name);
|
||||
|
||||
bool result = false;
|
||||
const char* filter_ext_ptr = get_tab_ext(archive->browser.tab_id);
|
||||
|
||||
if(strcmp(filter_ext_ptr, "*") == 0) {
|
||||
result = true;
|
||||
} else if(strstr(name, filter_ext_ptr) != NULL) {
|
||||
result = true;
|
||||
} else if(file_info->flags & FSF_DIRECTORY) {
|
||||
result = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void set_file_type(ArchiveFile_t* file, FileInfo* file_info) {
|
||||
furi_assert(file);
|
||||
furi_assert(file_info);
|
||||
|
||||
for(size_t i = 0; i < SIZEOF_ARRAY(known_ext); i++) {
|
||||
if(string_search_str(file->name, known_ext[i], 0) != STRING_FAILURE) {
|
||||
file->type = i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(file_info->flags & FSF_DIRECTORY) {
|
||||
file->type = ArchiveFileTypeFolder;
|
||||
} else {
|
||||
file->type = ArchiveFileTypeUnknown;
|
||||
}
|
||||
}
|
||||
|
||||
static void archive_file_append(ArchiveApp* archive, const char* path, string_t string) {
|
||||
furi_assert(archive);
|
||||
furi_assert(path);
|
||||
furi_assert(string);
|
||||
|
||||
FileWorker* file_worker = file_worker_alloc(false);
|
||||
|
||||
if(!file_worker_open(file_worker, path, FSAM_WRITE, FSOM_OPEN_APPEND)) {
|
||||
FURI_LOG_E("Archive", "Append open error");
|
||||
}
|
||||
|
||||
if(!file_worker_write(file_worker, string_get_cstr(string), string_size(string))) {
|
||||
FURI_LOG_E("Archive", "Append write error");
|
||||
}
|
||||
|
||||
file_worker_close(file_worker);
|
||||
file_worker_free(file_worker);
|
||||
}
|
||||
|
||||
static void archive_view_add_item(ArchiveApp* archive, FileInfo* file_info, const char* name) {
|
||||
furi_assert(archive);
|
||||
furi_assert(file_info);
|
||||
furi_assert(name);
|
||||
|
||||
ArchiveFile_t item;
|
||||
|
||||
if(filter_by_extension(archive, file_info, name)) {
|
||||
ArchiveFile_t_init(&item);
|
||||
string_init_set_str(item.name, name);
|
||||
set_file_type(&item, file_info);
|
||||
|
||||
with_view_model(
|
||||
archive->view_archive_main, (ArchiveViewModel * model) {
|
||||
files_array_push_back(model->files, item);
|
||||
return true;
|
||||
});
|
||||
|
||||
ArchiveFile_t_clear(&item);
|
||||
}
|
||||
}
|
||||
|
||||
static bool archive_is_favorite(ArchiveApp* archive, ArchiveFile_t* selected) {
|
||||
furi_assert(selected);
|
||||
string_t path;
|
||||
string_t buffer;
|
||||
string_init(buffer);
|
||||
bool found = false;
|
||||
|
||||
string_init_printf(
|
||||
path, "%s/%s", string_get_cstr(archive->browser.path), string_get_cstr(selected->name));
|
||||
|
||||
bool load_result =
|
||||
file_worker_open(archive->file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_ALWAYS);
|
||||
|
||||
if(load_result) {
|
||||
while(1) {
|
||||
if(!file_worker_read_until(archive->file_worker, buffer, '\n')) {
|
||||
break;
|
||||
}
|
||||
if(!string_size(buffer)) {
|
||||
break;
|
||||
}
|
||||
if(!string_search(buffer, path)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string_clear(buffer);
|
||||
string_clear(path);
|
||||
file_worker_close(archive->file_worker);
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
static bool archive_favorites_read(ArchiveApp* archive) {
|
||||
string_t buffer;
|
||||
FileInfo file_info;
|
||||
string_init(buffer);
|
||||
|
||||
bool load_result =
|
||||
file_worker_open(archive->file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING);
|
||||
|
||||
if(load_result) {
|
||||
while(1) {
|
||||
if(!file_worker_read_until(archive->file_worker, buffer, '\n')) {
|
||||
break;
|
||||
}
|
||||
if(!string_size(buffer)) {
|
||||
break;
|
||||
}
|
||||
|
||||
archive_view_add_item(archive, &file_info, string_get_cstr(buffer));
|
||||
string_clean(buffer);
|
||||
}
|
||||
}
|
||||
string_clear(buffer);
|
||||
file_worker_close(archive->file_worker);
|
||||
|
||||
return load_result;
|
||||
}
|
||||
|
||||
static bool
|
||||
archive_favorites_rename(ArchiveApp* archive, ArchiveFile_t* selected, const char* dst) {
|
||||
furi_assert(selected);
|
||||
string_t path;
|
||||
string_t buffer;
|
||||
string_t temp;
|
||||
|
||||
string_init(buffer);
|
||||
string_init(temp);
|
||||
|
||||
string_init_printf(
|
||||
path, "%s/%s", string_get_cstr(archive->browser.path), string_get_cstr(selected->name));
|
||||
bool load_result =
|
||||
file_worker_open(archive->file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING);
|
||||
|
||||
if(load_result) {
|
||||
while(1) {
|
||||
if(!file_worker_read_until(archive->file_worker, buffer, '\n')) {
|
||||
break;
|
||||
}
|
||||
if(!string_size(buffer)) {
|
||||
break;
|
||||
}
|
||||
|
||||
string_printf(
|
||||
temp, "%s\r\n", string_search(buffer, path) ? string_get_cstr(buffer) : dst);
|
||||
archive_file_append(archive, ARCHIVE_FAV_TEMP_PATH, temp);
|
||||
string_clean(temp);
|
||||
}
|
||||
}
|
||||
|
||||
string_clear(temp);
|
||||
string_clear(buffer);
|
||||
string_clear(path);
|
||||
|
||||
file_worker_close(archive->file_worker);
|
||||
file_worker_remove(archive->file_worker, ARCHIVE_FAV_PATH);
|
||||
file_worker_rename(archive->file_worker, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH);
|
||||
|
||||
return load_result;
|
||||
}
|
||||
|
||||
static bool archive_favorites_delete(ArchiveApp* archive, ArchiveFile_t* selected) {
|
||||
furi_assert(selected);
|
||||
string_t path;
|
||||
string_t buffer;
|
||||
string_init(buffer);
|
||||
|
||||
string_init_printf(
|
||||
path, "%s/%s", string_get_cstr(archive->browser.path), string_get_cstr(selected->name));
|
||||
|
||||
bool load_result =
|
||||
file_worker_open(archive->file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING);
|
||||
if(load_result) {
|
||||
while(1) {
|
||||
if(!file_worker_read_until(archive->file_worker, buffer, '\n')) {
|
||||
break;
|
||||
}
|
||||
if(!string_size(buffer)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if(string_search(buffer, path)) {
|
||||
string_t temp;
|
||||
string_init_printf(temp, "%s\r\n", string_get_cstr(buffer));
|
||||
archive_file_append(archive, ARCHIVE_FAV_TEMP_PATH, temp);
|
||||
string_clear(temp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string_clear(buffer);
|
||||
string_clear(path);
|
||||
|
||||
file_worker_close(archive->file_worker);
|
||||
file_worker_remove(archive->file_worker, ARCHIVE_FAV_PATH);
|
||||
file_worker_rename(archive->file_worker, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH);
|
||||
|
||||
return load_result;
|
||||
}
|
||||
|
||||
static bool archive_read_dir(ArchiveApp* archive) {
|
||||
FileInfo file_info;
|
||||
File* directory = storage_file_alloc(archive->api);
|
||||
char name[MAX_NAME_LEN];
|
||||
|
||||
if(!storage_dir_open(directory, string_get_cstr(archive->browser.path))) {
|
||||
storage_dir_close(directory);
|
||||
storage_file_free(directory);
|
||||
return false;
|
||||
}
|
||||
while(1) {
|
||||
if(!storage_dir_read(directory, &file_info, name, MAX_NAME_LEN)) {
|
||||
break;
|
||||
}
|
||||
|
||||
uint16_t files_cnt;
|
||||
with_view_model(
|
||||
archive->view_archive_main, (ArchiveViewModel * model) {
|
||||
files_cnt = files_array_size(model->files);
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
if(files_cnt > MAX_FILES) {
|
||||
break;
|
||||
} else if(storage_file_get_error(directory) == FSE_OK) {
|
||||
archive_view_add_item(archive, &file_info, name);
|
||||
} else {
|
||||
storage_dir_close(directory);
|
||||
storage_file_free(directory);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
storage_dir_close(directory);
|
||||
storage_file_free(directory);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool archive_get_filenames(ArchiveApp* archive) {
|
||||
furi_assert(archive);
|
||||
with_view_model(
|
||||
archive->view_archive_main, (ArchiveViewModel * model) {
|
||||
files_array_clean(model->files);
|
||||
return true;
|
||||
});
|
||||
|
||||
if(archive->browser.tab_id != ArchiveTabFavorites) {
|
||||
archive_read_dir(archive);
|
||||
} else {
|
||||
archive_favorites_read(archive);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
static void archive_exit_callback(ArchiveApp* archive) {
|
||||
furi_assert(archive);
|
||||
|
||||
AppEvent event;
|
||||
event.type = EventTypeExit;
|
||||
furi_check(osMessageQueuePut(archive->event_queue, &event, 0, osWaitForever) == osOK);
|
||||
}
|
||||
|
||||
static uint32_t archive_previous_callback(void* context) {
|
||||
return ArchiveViewMain;
|
||||
}
|
||||
|
||||
/* file menu */
|
||||
static void archive_add_to_favorites(ArchiveApp* archive) {
|
||||
furi_assert(archive);
|
||||
string_t buffer_src;
|
||||
|
||||
string_init_printf(
|
||||
buffer_src,
|
||||
"%s/%s\r\n",
|
||||
string_get_cstr(archive->browser.path),
|
||||
string_get_cstr(archive->browser.name));
|
||||
|
||||
archive_file_append(archive, ARCHIVE_FAV_PATH, buffer_src);
|
||||
|
||||
string_clear(buffer_src);
|
||||
}
|
||||
|
||||
static void archive_text_input_callback(void* context) {
|
||||
bool archive_custom_event_callback(void* context, uint32_t event) {
|
||||
furi_assert(context);
|
||||
|
||||
ArchiveApp* archive = (ArchiveApp*)context;
|
||||
|
||||
string_t buffer_src;
|
||||
string_t buffer_dst;
|
||||
|
||||
string_init_printf(
|
||||
buffer_src,
|
||||
"%s/%s",
|
||||
string_get_cstr(archive->browser.path),
|
||||
string_get_cstr(archive->browser.name));
|
||||
string_init_printf(
|
||||
buffer_dst,
|
||||
"%s/%s",
|
||||
string_get_cstr(archive->browser.path),
|
||||
archive->browser.text_input_buffer);
|
||||
|
||||
string_set(archive->browser.name, archive->browser.text_input_buffer);
|
||||
// append extension
|
||||
|
||||
ArchiveFile_t* file;
|
||||
|
||||
with_view_model(
|
||||
archive->view_archive_main, (ArchiveViewModel * model) {
|
||||
file = files_array_get(
|
||||
model->files, CLAMP(model->idx, files_array_size(model->files) - 1, 0));
|
||||
file->fav = archive_is_favorite(archive, file);
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
string_cat(buffer_dst, known_ext[file->type]);
|
||||
storage_common_rename(archive->api, string_get_cstr(buffer_src), string_get_cstr(buffer_dst));
|
||||
|
||||
if(file->fav) {
|
||||
archive_favorites_rename(archive, file, string_get_cstr(buffer_dst));
|
||||
}
|
||||
|
||||
view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveViewMain);
|
||||
archive_get_filenames(archive);
|
||||
|
||||
with_view_model(
|
||||
archive->view_archive_main, (ArchiveViewModel * model) {
|
||||
model->idx = 0;
|
||||
while(model->idx < files_array_size(model->files)) {
|
||||
ArchiveFile_t* current = files_array_get(model->files, model->idx);
|
||||
if(!string_search(current->name, archive->browser.text_input_buffer)) {
|
||||
break;
|
||||
}
|
||||
++model->idx;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
update_offset(archive);
|
||||
|
||||
string_clear(buffer_src);
|
||||
string_clear(buffer_dst);
|
||||
return scene_manager_handle_custom_event(archive->scene_manager, event);
|
||||
}
|
||||
|
||||
static void archive_enter_text_input(ArchiveApp* archive) {
|
||||
furi_assert(archive);
|
||||
*archive->browser.text_input_buffer = '\0';
|
||||
|
||||
strlcpy(
|
||||
archive->browser.text_input_buffer, string_get_cstr(archive->browser.name), MAX_NAME_LEN);
|
||||
|
||||
archive_trim_file_ext(archive->browser.text_input_buffer);
|
||||
|
||||
text_input_set_header_text(archive->text_input, "Rename:");
|
||||
|
||||
text_input_set_result_callback(
|
||||
archive->text_input,
|
||||
archive_text_input_callback,
|
||||
archive,
|
||||
archive->browser.text_input_buffer,
|
||||
MAX_NAME_LEN,
|
||||
false);
|
||||
|
||||
view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveViewTextInput);
|
||||
}
|
||||
|
||||
static void archive_show_file_menu(ArchiveApp* archive) {
|
||||
furi_assert(archive);
|
||||
|
||||
archive->browser.menu = true;
|
||||
|
||||
with_view_model(
|
||||
archive->view_archive_main, (ArchiveViewModel * model) {
|
||||
ArchiveFile_t* selected;
|
||||
selected = files_array_get(model->files, model->idx);
|
||||
model->menu = true;
|
||||
model->menu_idx = 0;
|
||||
selected->fav = is_known_app(selected->type) ? archive_is_favorite(archive, selected) :
|
||||
false;
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
static void archive_close_file_menu(ArchiveApp* archive) {
|
||||
furi_assert(archive);
|
||||
|
||||
archive->browser.menu = false;
|
||||
|
||||
with_view_model(
|
||||
archive->view_archive_main, (ArchiveViewModel * model) {
|
||||
model->menu = false;
|
||||
model->menu_idx = 0;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
static void archive_open_app(ArchiveApp* archive, const char* app_name, const char* args) {
|
||||
furi_assert(archive);
|
||||
furi_assert(app_name);
|
||||
|
||||
loader_start(archive->loader, app_name, args);
|
||||
}
|
||||
|
||||
static void archive_delete_file(ArchiveApp* archive, ArchiveFile_t* file) {
|
||||
furi_assert(archive);
|
||||
furi_assert(file);
|
||||
|
||||
string_t path;
|
||||
string_init(path);
|
||||
|
||||
string_printf(
|
||||
path, "%s/%s", string_get_cstr(archive->browser.path), string_get_cstr(file->name));
|
||||
|
||||
if(archive_is_favorite(archive, file)) { // remove from favorites
|
||||
archive_favorites_delete(archive, file);
|
||||
}
|
||||
|
||||
file_worker_remove(archive->file_worker, string_get_cstr(path));
|
||||
|
||||
string_clear(path);
|
||||
archive_get_filenames(archive);
|
||||
|
||||
with_view_model(
|
||||
archive->view_archive_main, (ArchiveViewModel * model) {
|
||||
model->idx = CLAMP(model->idx, files_array_size(model->files) - 1, 0);
|
||||
return true;
|
||||
});
|
||||
|
||||
update_offset(archive);
|
||||
}
|
||||
|
||||
static void
|
||||
archive_run_in_app(ArchiveApp* archive, ArchiveFile_t* selected, bool full_path_provided) {
|
||||
string_t full_path;
|
||||
|
||||
if(!full_path_provided) {
|
||||
string_init_printf(
|
||||
full_path,
|
||||
"%s/%s",
|
||||
string_get_cstr(archive->browser.path),
|
||||
string_get_cstr(selected->name));
|
||||
} else {
|
||||
string_init_set(full_path, selected->name);
|
||||
}
|
||||
|
||||
archive_open_app(archive, flipper_app_name[selected->type], string_get_cstr(full_path));
|
||||
string_clear(full_path);
|
||||
}
|
||||
|
||||
static void archive_file_menu_callback(ArchiveApp* archive) {
|
||||
furi_assert(archive);
|
||||
|
||||
ArchiveFile_t* selected;
|
||||
uint8_t idx = 0;
|
||||
|
||||
with_view_model(
|
||||
archive->view_archive_main, (ArchiveViewModel * model) {
|
||||
selected = files_array_get(model->files, model->idx);
|
||||
idx = model->menu_idx;
|
||||
return true;
|
||||
});
|
||||
|
||||
switch(idx) {
|
||||
case 0:
|
||||
if(is_known_app(selected->type)) {
|
||||
archive_run_in_app(archive, selected, false);
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
if(is_known_app(selected->type)) {
|
||||
if(!archive_is_favorite(archive, selected)) {
|
||||
string_set(archive->browser.name, selected->name);
|
||||
archive_add_to_favorites(archive);
|
||||
} else {
|
||||
// delete from favorites
|
||||
archive_favorites_delete(archive, selected);
|
||||
}
|
||||
archive_close_file_menu(archive);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
// open rename view
|
||||
if(is_known_app(selected->type)) {
|
||||
archive_enter_text_input(archive);
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
// confirmation?
|
||||
archive_delete_file(archive, selected);
|
||||
archive_close_file_menu(archive);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
archive_close_file_menu(archive);
|
||||
break;
|
||||
}
|
||||
selected = NULL;
|
||||
}
|
||||
|
||||
static void menu_input_handler(ArchiveApp* archive, InputEvent* event) {
|
||||
furi_assert(archive);
|
||||
furi_assert(archive);
|
||||
|
||||
if(event->type == InputTypeShort) {
|
||||
if(event->key == InputKeyUp || event->key == InputKeyDown) {
|
||||
with_view_model(
|
||||
archive->view_archive_main, (ArchiveViewModel * model) {
|
||||
if(event->key == InputKeyUp) {
|
||||
model->menu_idx = ((model->menu_idx - 1) + MENU_ITEMS) % MENU_ITEMS;
|
||||
} else if(event->key == InputKeyDown) {
|
||||
model->menu_idx = (model->menu_idx + 1) % MENU_ITEMS;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
if(event->key == InputKeyOk) {
|
||||
archive_file_menu_callback(archive);
|
||||
} else if(event->key == InputKeyBack) {
|
||||
archive_close_file_menu(archive);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* main controls */
|
||||
|
||||
static bool archive_view_input(InputEvent* event, void* context) {
|
||||
furi_assert(event);
|
||||
bool archive_back_event_callback(void* context) {
|
||||
furi_assert(context);
|
||||
|
||||
ArchiveApp* archive = context;
|
||||
bool in_menu = archive->browser.menu;
|
||||
|
||||
if(in_menu) {
|
||||
menu_input_handler(archive, event);
|
||||
return true;
|
||||
}
|
||||
|
||||
if(event->type == InputTypeShort) {
|
||||
if(event->key == InputKeyLeft) {
|
||||
if(archive->browser.tab_id > 0) {
|
||||
archive->browser.tab_id = CLAMP(archive->browser.tab_id - 1, ArchiveTabTotal, 0);
|
||||
archive_switch_tab(archive);
|
||||
return true;
|
||||
}
|
||||
} else if(event->key == InputKeyRight) {
|
||||
if(archive->browser.tab_id < ArchiveTabTotal - 1) {
|
||||
archive->browser.tab_id =
|
||||
CLAMP(archive->browser.tab_id + 1, ArchiveTabTotal - 1, 0);
|
||||
archive_switch_tab(archive);
|
||||
return true;
|
||||
}
|
||||
|
||||
} else if(event->key == InputKeyBack) {
|
||||
if(archive->browser.depth == 0) {
|
||||
archive_exit_callback(archive);
|
||||
} else {
|
||||
archive_leave_dir(archive);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if(event->key == InputKeyUp || event->key == InputKeyDown) {
|
||||
with_view_model(
|
||||
archive->view_archive_main, (ArchiveViewModel * model) {
|
||||
uint16_t num_elements = (uint16_t)files_array_size(model->files);
|
||||
if((event->type == InputTypeShort || event->type == InputTypeRepeat)) {
|
||||
if(event->key == InputKeyUp) {
|
||||
model->idx = ((model->idx - 1) + num_elements) % num_elements;
|
||||
} else if(event->key == InputKeyDown) {
|
||||
model->idx = (model->idx + 1) % num_elements;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
update_offset(archive);
|
||||
}
|
||||
|
||||
if(event->key == InputKeyOk) {
|
||||
ArchiveFile_t* selected;
|
||||
|
||||
with_view_model(
|
||||
archive->view_archive_main, (ArchiveViewModel * model) {
|
||||
selected = files_array_size(model->files) > 0 ?
|
||||
files_array_get(model->files, model->idx) :
|
||||
NULL;
|
||||
return true;
|
||||
});
|
||||
|
||||
if(selected) {
|
||||
string_set(archive->browser.name, selected->name);
|
||||
|
||||
if(selected->type == ArchiveFileTypeFolder) {
|
||||
if(event->type == InputTypeShort) {
|
||||
archive_enter_dir(archive, archive->browser.name);
|
||||
} else if(event->type == InputTypeLong) {
|
||||
archive_show_file_menu(archive);
|
||||
}
|
||||
} else {
|
||||
if(event->type == InputTypeShort) {
|
||||
if(archive->browser.tab_id == ArchiveTabFavorites) {
|
||||
if(is_known_app(selected->type)) {
|
||||
archive_run_in_app(archive, selected, true);
|
||||
}
|
||||
} else {
|
||||
archive_show_file_menu(archive);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
update_offset(archive);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void archive_free(ArchiveApp* archive) {
|
||||
furi_assert(archive);
|
||||
|
||||
file_worker_free(archive->file_worker);
|
||||
|
||||
view_dispatcher_remove_view(archive->view_dispatcher, ArchiveViewMain);
|
||||
view_dispatcher_remove_view(archive->view_dispatcher, ArchiveViewTextInput);
|
||||
view_dispatcher_free(archive->view_dispatcher);
|
||||
|
||||
with_view_model(
|
||||
archive->view_archive_main, (ArchiveViewModel * model) {
|
||||
files_array_clear(model->files);
|
||||
return false;
|
||||
});
|
||||
|
||||
view_free(archive->view_archive_main);
|
||||
|
||||
string_clear(archive->browser.name);
|
||||
string_clear(archive->browser.path);
|
||||
|
||||
text_input_free(archive->text_input);
|
||||
|
||||
furi_record_close("storage");
|
||||
archive->api = NULL;
|
||||
furi_record_close("gui");
|
||||
archive->gui = NULL;
|
||||
furi_record_close("loader");
|
||||
archive->loader = NULL;
|
||||
furi_thread_free(archive->app_thread);
|
||||
furi_check(osMessageQueueDelete(archive->event_queue) == osOK);
|
||||
|
||||
free(archive);
|
||||
ArchiveApp* archive = (ArchiveApp*)context;
|
||||
return scene_manager_handle_back_event(archive->scene_manager);
|
||||
}
|
||||
|
||||
ArchiveApp* archive_alloc() {
|
||||
ArchiveApp* archive = furi_alloc(sizeof(ArchiveApp));
|
||||
|
||||
archive->event_queue = osMessageQueueNew(8, sizeof(AppEvent), NULL);
|
||||
archive->app_thread = furi_thread_alloc();
|
||||
archive->gui = furi_record_open("gui");
|
||||
archive->loader = furi_record_open("loader");
|
||||
archive->api = furi_record_open("storage");
|
||||
archive->text_input = text_input_alloc();
|
||||
archive->view_archive_main = view_alloc();
|
||||
archive->file_worker = file_worker_alloc(true);
|
||||
|
||||
furi_check(archive->event_queue);
|
||||
|
||||
view_allocate_model(
|
||||
archive->view_archive_main, ViewModelTypeLocking, sizeof(ArchiveViewModel));
|
||||
with_view_model(
|
||||
archive->view_archive_main, (ArchiveViewModel * model) {
|
||||
files_array_init(model->files);
|
||||
return false;
|
||||
});
|
||||
|
||||
view_set_context(archive->view_archive_main, archive);
|
||||
view_set_draw_callback(archive->view_archive_main, archive_view_render);
|
||||
view_set_input_callback(archive->view_archive_main, archive_view_input);
|
||||
view_set_previous_callback(
|
||||
text_input_get_view(archive->text_input), archive_previous_callback);
|
||||
|
||||
// View Dispatcher
|
||||
archive->view_dispatcher = view_dispatcher_alloc();
|
||||
view_dispatcher_add_view(
|
||||
archive->view_dispatcher, ArchiveViewMain, archive->view_archive_main);
|
||||
view_dispatcher_add_view(
|
||||
archive->view_dispatcher, ArchiveViewTextInput, text_input_get_view(archive->text_input));
|
||||
archive->scene_manager = scene_manager_alloc(&archive_scene_handlers, archive);
|
||||
|
||||
view_dispatcher_enable_queue(archive->view_dispatcher);
|
||||
view_dispatcher_attach_to_gui(
|
||||
archive->view_dispatcher, archive->gui, ViewDispatcherTypeFullscreen);
|
||||
|
||||
view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveTabFavorites);
|
||||
view_dispatcher_set_event_callback_context(archive->view_dispatcher, archive);
|
||||
view_dispatcher_set_custom_event_callback(
|
||||
archive->view_dispatcher, archive_custom_event_callback);
|
||||
view_dispatcher_set_navigation_event_callback(
|
||||
archive->view_dispatcher, archive_back_event_callback);
|
||||
|
||||
archive->main_view = main_view_alloc();
|
||||
|
||||
view_dispatcher_add_view(
|
||||
archive->view_dispatcher, ArchiveViewBrowser, archive_main_get_view(archive->main_view));
|
||||
|
||||
view_dispatcher_add_view(
|
||||
archive->view_dispatcher, ArchiveViewTextInput, text_input_get_view(archive->text_input));
|
||||
|
||||
return archive;
|
||||
}
|
||||
|
||||
void archive_free(ArchiveApp* archive) {
|
||||
furi_assert(archive);
|
||||
|
||||
view_dispatcher_remove_view(archive->view_dispatcher, ArchiveViewBrowser);
|
||||
view_dispatcher_remove_view(archive->view_dispatcher, ArchiveViewTextInput);
|
||||
view_dispatcher_free(archive->view_dispatcher);
|
||||
scene_manager_free(archive->scene_manager);
|
||||
main_view_free(archive->main_view);
|
||||
|
||||
text_input_free(archive->text_input);
|
||||
|
||||
furi_record_close("gui");
|
||||
archive->gui = NULL;
|
||||
|
||||
free(archive);
|
||||
}
|
||||
|
||||
int32_t archive_app(void* p) {
|
||||
ArchiveApp* archive = archive_alloc();
|
||||
|
||||
// default tab
|
||||
archive_switch_tab(archive);
|
||||
|
||||
AppEvent event;
|
||||
while(1) {
|
||||
furi_check(osMessageQueueGet(archive->event_queue, &event, NULL, osWaitForever) == osOK);
|
||||
if(event.type == EventTypeExit) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneBrowser);
|
||||
view_dispatcher_run(archive->view_dispatcher);
|
||||
archive_free(archive);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -5,44 +5,27 @@
|
||||
#include <furi.h>
|
||||
#include <gui/gui_i.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
#include <gui/scene_manager.h>
|
||||
#include <gui/modules/text_input.h>
|
||||
#include <loader/loader.h>
|
||||
|
||||
#include <m-string.h>
|
||||
#include <m-array.h>
|
||||
#include <storage/storage.h>
|
||||
#include "archive_views.h"
|
||||
#include "applications.h"
|
||||
#include "file-worker.h"
|
||||
|
||||
#define MAX_DEPTH 32
|
||||
#define MAX_FILES 100 //temp
|
||||
#include "views/archive_main_view.h"
|
||||
#include "scenes/archive_scene.h"
|
||||
|
||||
#define MAX_FILE_SIZE 128
|
||||
#define ARCHIVE_FAV_PATH "/any/favorites.txt"
|
||||
#define ARCHIVE_FAV_TEMP_PATH "/any/favorites.tmp"
|
||||
|
||||
typedef enum {
|
||||
ArchiveViewMain,
|
||||
ArchiveViewBrowser,
|
||||
ArchiveViewTextInput,
|
||||
ArchiveViewTotal,
|
||||
} ArchiveViewEnum;
|
||||
|
||||
static const char* flipper_app_name[] = {
|
||||
[ArchiveFileTypeIButton] = "iButton",
|
||||
[ArchiveFileTypeNFC] = "NFC",
|
||||
[ArchiveFileTypeSubGhz] = "Sub-GHz",
|
||||
[ArchiveFileTypeLFRFID] = "125 kHz RFID",
|
||||
[ArchiveFileTypeIrda] = "Infrared",
|
||||
};
|
||||
|
||||
static const char* known_ext[] = {
|
||||
[ArchiveFileTypeIButton] = ".ibtn",
|
||||
[ArchiveFileTypeNFC] = ".nfc",
|
||||
[ArchiveFileTypeSubGhz] = ".sub",
|
||||
[ArchiveFileTypeLFRFID] = ".rfid",
|
||||
[ArchiveFileTypeIrda] = ".ir",
|
||||
};
|
||||
|
||||
static const char* tab_default_paths[] = {
|
||||
[ArchiveTabFavorites] = "/any/favorites",
|
||||
[ArchiveTabIButton] = "/any/ibutton",
|
||||
@ -53,23 +36,6 @@ static const char* tab_default_paths[] = {
|
||||
[ArchiveTabBrowser] = "/any",
|
||||
};
|
||||
|
||||
static inline const char* get_tab_ext(ArchiveTabEnum tab) {
|
||||
switch(tab) {
|
||||
case ArchiveTabIButton:
|
||||
return known_ext[ArchiveFileTypeIButton];
|
||||
case ArchiveTabNFC:
|
||||
return known_ext[ArchiveFileTypeNFC];
|
||||
case ArchiveTabSubGhz:
|
||||
return known_ext[ArchiveFileTypeSubGhz];
|
||||
case ArchiveTabLFRFID:
|
||||
return known_ext[ArchiveFileTypeLFRFID];
|
||||
case ArchiveTabIrda:
|
||||
return known_ext[ArchiveFileTypeIrda];
|
||||
default:
|
||||
return "*";
|
||||
}
|
||||
}
|
||||
|
||||
static inline const char* get_default_path(ArchiveFileTypeEnum type) {
|
||||
switch(type) {
|
||||
case ArchiveFileTypeIButton:
|
||||
@ -104,35 +70,11 @@ typedef struct {
|
||||
EventType type;
|
||||
} AppEvent;
|
||||
|
||||
typedef enum {
|
||||
FavoritesCheck,
|
||||
FavoritesRead,
|
||||
FavoritesDelete,
|
||||
FavoritesRename,
|
||||
} FavActionsEnum;
|
||||
|
||||
typedef struct {
|
||||
ArchiveTabEnum tab_id;
|
||||
string_t name;
|
||||
string_t path;
|
||||
char text_input_buffer[MAX_NAME_LEN];
|
||||
|
||||
uint8_t depth;
|
||||
uint16_t last_idx[MAX_DEPTH];
|
||||
|
||||
bool menu;
|
||||
} ArchiveBrowser;
|
||||
|
||||
struct ArchiveApp {
|
||||
osMessageQueueId_t event_queue;
|
||||
FuriThread* app_thread;
|
||||
Loader* loader;
|
||||
Gui* gui;
|
||||
ViewDispatcher* view_dispatcher;
|
||||
View* view_archive_main;
|
||||
SceneManager* scene_manager;
|
||||
ArchiveMainView* main_view;
|
||||
TextInput* text_input;
|
||||
|
||||
Storage* api;
|
||||
FileWorker* file_worker;
|
||||
ArchiveBrowser browser;
|
||||
char text_store[MAX_NAME_LEN];
|
||||
};
|
||||
|
||||
@ -1,168 +0,0 @@
|
||||
#include "archive_views.h"
|
||||
|
||||
static const char* ArchiveTabNames[] = {
|
||||
[ArchiveTabFavorites] = "Favorites",
|
||||
[ArchiveTabIButton] = "iButton",
|
||||
[ArchiveTabNFC] = "NFC",
|
||||
[ArchiveTabSubGhz] = "Sub-GHz",
|
||||
[ArchiveTabLFRFID] = "RFID LF",
|
||||
[ArchiveTabIrda] = "Infrared",
|
||||
[ArchiveTabBrowser] = "Browser"};
|
||||
|
||||
static const Icon* ArchiveItemIcons[] = {
|
||||
[ArchiveFileTypeIButton] = &I_ibutt_10px,
|
||||
[ArchiveFileTypeNFC] = &I_Nfc_10px,
|
||||
[ArchiveFileTypeSubGhz] = &I_sub1_10px,
|
||||
[ArchiveFileTypeLFRFID] = &I_125_10px,
|
||||
[ArchiveFileTypeIrda] = &I_ir_10px,
|
||||
[ArchiveFileTypeFolder] = &I_dir_10px,
|
||||
[ArchiveFileTypeUnknown] = &I_unknown_10px,
|
||||
};
|
||||
|
||||
static void render_item_menu(Canvas* canvas, ArchiveViewModel* model) {
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
canvas_draw_box(canvas, 71, 17, 57, 46);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
elements_slightly_rounded_frame(canvas, 70, 16, 58, 48);
|
||||
|
||||
string_t menu[MENU_ITEMS];
|
||||
|
||||
string_init_set_str(menu[0], "Run in app");
|
||||
string_init_set_str(menu[1], "Pin");
|
||||
string_init_set_str(menu[2], "Rename");
|
||||
string_init_set_str(menu[3], "Delete");
|
||||
|
||||
ArchiveFile_t* selected = files_array_get(model->files, model->idx);
|
||||
|
||||
if(!is_known_app(selected->type)) {
|
||||
string_set_str(menu[0], "---");
|
||||
string_set_str(menu[1], "---");
|
||||
string_set_str(menu[2], "---");
|
||||
} else if(model->tab_idx == 0 || selected->fav) {
|
||||
string_set_str(menu[1], "Unpin");
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < MENU_ITEMS; i++) {
|
||||
canvas_draw_str(canvas, 82, 27 + i * 11, string_get_cstr(menu[i]));
|
||||
string_clear(menu[i]);
|
||||
}
|
||||
|
||||
canvas_draw_icon(canvas, 74, 20 + model->menu_idx * 11, &I_ButtonRight_4x7);
|
||||
}
|
||||
|
||||
void archive_trim_file_ext(char* name) {
|
||||
size_t str_len = strlen(name);
|
||||
char* end = name + str_len;
|
||||
while(end > name && *end != '.' && *end != '\\' && *end != '/') {
|
||||
--end;
|
||||
}
|
||||
if((end > name && *end == '.') && (*(end - 1) != '\\' && *(end - 1) != '/')) {
|
||||
*end = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
static void archive_draw_frame(Canvas* canvas, uint16_t idx, bool scrollbar) {
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_draw_box(canvas, 0, 15 + idx * FRAME_HEIGHT, scrollbar ? 122 : 127, FRAME_HEIGHT);
|
||||
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
canvas_draw_dot(canvas, 0, 15 + idx * FRAME_HEIGHT);
|
||||
canvas_draw_dot(canvas, 1, 15 + idx * FRAME_HEIGHT);
|
||||
canvas_draw_dot(canvas, 0, (15 + idx * FRAME_HEIGHT) + 1);
|
||||
|
||||
canvas_draw_dot(canvas, 0, (15 + idx * FRAME_HEIGHT) + 11);
|
||||
canvas_draw_dot(canvas, scrollbar ? 121 : 126, 15 + idx * FRAME_HEIGHT);
|
||||
canvas_draw_dot(canvas, scrollbar ? 121 : 126, (15 + idx * FRAME_HEIGHT) + 11);
|
||||
}
|
||||
|
||||
static void draw_list(Canvas* canvas, ArchiveViewModel* model) {
|
||||
furi_assert(model);
|
||||
|
||||
size_t array_size = files_array_size(model->files);
|
||||
bool scrollbar = array_size > 4;
|
||||
|
||||
for(size_t i = 0; i < MIN(array_size, MENU_ITEMS); ++i) {
|
||||
string_t str_buff;
|
||||
char cstr_buff[MAX_NAME_LEN];
|
||||
|
||||
size_t idx = CLAMP(i + model->list_offset, array_size, 0);
|
||||
ArchiveFile_t* file = files_array_get(model->files, CLAMP(idx, array_size - 1, 0));
|
||||
|
||||
string_init_set(str_buff, file->name);
|
||||
string_right(str_buff, string_search_rchar(str_buff, '/') + 1);
|
||||
strlcpy(cstr_buff, string_get_cstr(str_buff), string_size(str_buff) + 1);
|
||||
|
||||
if(is_known_app(file->type)) archive_trim_file_ext(cstr_buff);
|
||||
|
||||
string_clean(str_buff);
|
||||
string_set_str(str_buff, cstr_buff);
|
||||
|
||||
elements_string_fit_width(canvas, str_buff, scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX);
|
||||
|
||||
if(model->idx == idx) {
|
||||
archive_draw_frame(canvas, i, scrollbar);
|
||||
} else {
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
}
|
||||
|
||||
canvas_draw_icon(canvas, 2, 16 + i * FRAME_HEIGHT, ArchiveItemIcons[file->type]);
|
||||
canvas_draw_str(canvas, 15, 24 + i * FRAME_HEIGHT, string_get_cstr(str_buff));
|
||||
string_clear(str_buff);
|
||||
}
|
||||
|
||||
if(scrollbar) {
|
||||
elements_scrollbar_pos(canvas, 126, 15, 49, model->idx, array_size);
|
||||
}
|
||||
|
||||
if(model->menu) {
|
||||
render_item_menu(canvas, model);
|
||||
}
|
||||
}
|
||||
|
||||
static void archive_render_status_bar(Canvas* canvas, ArchiveViewModel* model) {
|
||||
furi_assert(model);
|
||||
|
||||
const char* tab_name = ArchiveTabNames[model->tab_idx];
|
||||
|
||||
canvas_draw_icon(canvas, 0, 0, &I_Background_128x11);
|
||||
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
canvas_draw_box(canvas, 0, 0, 50, 13);
|
||||
canvas_draw_box(canvas, 107, 0, 20, 13);
|
||||
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_draw_frame(canvas, 1, 0, 50, 12);
|
||||
canvas_draw_line(canvas, 0, 1, 0, 11);
|
||||
canvas_draw_line(canvas, 1, 12, 49, 12);
|
||||
canvas_draw_str_aligned(canvas, 26, 9, AlignCenter, AlignBottom, tab_name);
|
||||
|
||||
canvas_draw_frame(canvas, 108, 0, 20, 12);
|
||||
canvas_draw_line(canvas, 107, 1, 107, 11);
|
||||
canvas_draw_line(canvas, 108, 12, 126, 12);
|
||||
|
||||
if(model->tab_idx > 0) {
|
||||
canvas_draw_icon(canvas, 112, 2, &I_ButtonLeft_4x7);
|
||||
}
|
||||
if(model->tab_idx < SIZEOF_ARRAY(ArchiveTabNames) - 1) {
|
||||
canvas_draw_icon(canvas, 120, 2, &I_ButtonRight_4x7);
|
||||
}
|
||||
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
canvas_draw_dot(canvas, 50, 0);
|
||||
canvas_draw_dot(canvas, 127, 0);
|
||||
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
}
|
||||
|
||||
void archive_view_render(Canvas* canvas, void* model) {
|
||||
ArchiveViewModel* m = model;
|
||||
|
||||
archive_render_status_bar(canvas, model);
|
||||
|
||||
if(files_array_size(m->files) > 0) {
|
||||
draw_list(canvas, m);
|
||||
} else {
|
||||
canvas_draw_str_aligned(
|
||||
canvas, GUI_DISPLAY_WIDTH / 2, 40, AlignCenter, AlignCenter, "Empty");
|
||||
}
|
||||
}
|
||||
171
applications/archive/helpers/archive_favorites.c
Normal file
171
applications/archive/helpers/archive_favorites.c
Normal file
@ -0,0 +1,171 @@
|
||||
#include "archive_favorites.h"
|
||||
#include "archive_files.h"
|
||||
#include "../views/archive_main_view.h"
|
||||
|
||||
bool archive_favorites_read(void* context) {
|
||||
furi_assert(context);
|
||||
|
||||
ArchiveMainView* archive_view = context;
|
||||
FileWorker* file_worker = file_worker_alloc(true);
|
||||
|
||||
string_t buffer;
|
||||
FileInfo file_info;
|
||||
string_init(buffer);
|
||||
|
||||
bool result = file_worker_open(file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_ALWAYS);
|
||||
|
||||
if(result) {
|
||||
while(1) {
|
||||
if(!file_worker_read_until(file_worker, buffer, '\n')) {
|
||||
break;
|
||||
}
|
||||
if(!string_size(buffer)) {
|
||||
break;
|
||||
}
|
||||
|
||||
archive_view_add_item(archive_view, &file_info, string_get_cstr(buffer));
|
||||
string_clean(buffer);
|
||||
}
|
||||
}
|
||||
string_clear(buffer);
|
||||
file_worker_close(file_worker);
|
||||
file_worker_free(file_worker);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool archive_favorites_delete(const char* file_path, const char* name) {
|
||||
furi_assert(file_path);
|
||||
furi_assert(name);
|
||||
|
||||
FileWorker* file_worker = file_worker_alloc(true);
|
||||
|
||||
string_t path;
|
||||
string_t buffer;
|
||||
string_init(buffer);
|
||||
|
||||
string_init_printf(path, "%s/%s", file_path, name);
|
||||
|
||||
bool result = file_worker_open(file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING);
|
||||
if(result) {
|
||||
while(1) {
|
||||
if(!file_worker_read_until(file_worker, buffer, '\n')) {
|
||||
break;
|
||||
}
|
||||
if(!string_size(buffer)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if(string_search(buffer, path)) {
|
||||
string_t temp;
|
||||
string_init_printf(temp, "%s\r\n", string_get_cstr(buffer));
|
||||
archive_file_append(ARCHIVE_FAV_TEMP_PATH, temp);
|
||||
string_clear(temp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string_clear(buffer);
|
||||
string_clear(path);
|
||||
|
||||
file_worker_close(file_worker);
|
||||
file_worker_remove(file_worker, ARCHIVE_FAV_PATH);
|
||||
file_worker_rename(file_worker, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH);
|
||||
|
||||
file_worker_free(file_worker);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool archive_is_favorite(const char* file_path, const char* name) {
|
||||
furi_assert(file_path);
|
||||
furi_assert(name);
|
||||
|
||||
FileWorker* file_worker = file_worker_alloc(true);
|
||||
|
||||
string_t path;
|
||||
string_t buffer;
|
||||
string_init(buffer);
|
||||
bool found = false;
|
||||
|
||||
string_init_printf(path, "%s/%s", file_path, name);
|
||||
bool result = file_worker_open(file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_ALWAYS);
|
||||
|
||||
if(result) {
|
||||
while(1) {
|
||||
if(!file_worker_read_until(file_worker, buffer, '\n')) {
|
||||
break;
|
||||
}
|
||||
if(!string_size(buffer)) {
|
||||
break;
|
||||
}
|
||||
if(!string_search(buffer, path)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string_clear(buffer);
|
||||
string_clear(path);
|
||||
file_worker_close(file_worker);
|
||||
file_worker_free(file_worker);
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
bool archive_favorites_rename(const char* file_path, const char* src, const char* dst) {
|
||||
furi_assert(file_path);
|
||||
furi_assert(src);
|
||||
furi_assert(dst);
|
||||
|
||||
FileWorker* file_worker = file_worker_alloc(true);
|
||||
|
||||
string_t path;
|
||||
string_t buffer;
|
||||
string_t temp;
|
||||
|
||||
string_init(buffer);
|
||||
string_init(temp);
|
||||
string_init(path);
|
||||
|
||||
string_printf(path, "%s/%s", file_path, src);
|
||||
bool result = file_worker_open(file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING);
|
||||
|
||||
if(result) {
|
||||
while(1) {
|
||||
if(!file_worker_read_until(file_worker, buffer, '\n')) {
|
||||
break;
|
||||
}
|
||||
if(!string_size(buffer)) {
|
||||
break;
|
||||
}
|
||||
string_printf(
|
||||
temp, "%s\r\n", string_search(buffer, path) ? string_get_cstr(buffer) : dst);
|
||||
archive_file_append(ARCHIVE_FAV_TEMP_PATH, temp);
|
||||
string_clean(temp);
|
||||
}
|
||||
}
|
||||
|
||||
string_clear(temp);
|
||||
string_clear(buffer);
|
||||
string_clear(path);
|
||||
|
||||
file_worker_close(file_worker);
|
||||
file_worker_remove(file_worker, ARCHIVE_FAV_PATH);
|
||||
file_worker_rename(file_worker, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH);
|
||||
|
||||
file_worker_free(file_worker);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void archive_add_to_favorites(const char* file_path, const char* name) {
|
||||
furi_assert(file_path);
|
||||
furi_assert(name);
|
||||
|
||||
string_t buffer_src;
|
||||
|
||||
string_init_printf(buffer_src, "%s/%s\r\n", file_path, name);
|
||||
archive_file_append(ARCHIVE_FAV_PATH, buffer_src);
|
||||
string_clear(buffer_src);
|
||||
}
|
||||
11
applications/archive/helpers/archive_favorites.h
Normal file
11
applications/archive/helpers/archive_favorites.h
Normal file
@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
#include "file-worker.h"
|
||||
|
||||
#define ARCHIVE_FAV_PATH "/any/favorites.txt"
|
||||
#define ARCHIVE_FAV_TEMP_PATH "/any/favorites.tmp"
|
||||
|
||||
bool archive_favorites_read(void* context);
|
||||
bool archive_favorites_delete(const char* file_path, const char* name);
|
||||
bool archive_is_favorite(const char* file_path, const char* name);
|
||||
bool archive_favorites_rename(const char* file_path, const char* src, const char* dst);
|
||||
void archive_add_to_favorites(const char* file_path, const char* name);
|
||||
143
applications/archive/helpers/archive_files.c
Normal file
143
applications/archive/helpers/archive_files.c
Normal file
@ -0,0 +1,143 @@
|
||||
#include "archive_files.h"
|
||||
#include "archive_favorites.h"
|
||||
#include "../archive_i.h"
|
||||
|
||||
bool filter_by_extension(FileInfo* file_info, const char* tab_ext, const char* name) {
|
||||
furi_assert(file_info);
|
||||
furi_assert(tab_ext);
|
||||
furi_assert(name);
|
||||
|
||||
bool result = false;
|
||||
|
||||
if(strcmp(tab_ext, "*") == 0) {
|
||||
result = true;
|
||||
} else if(strstr(name, tab_ext) != NULL) {
|
||||
result = true;
|
||||
} else if(file_info->flags & FSF_DIRECTORY) {
|
||||
result = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void archive_trim_file_ext(char* name) {
|
||||
size_t str_len = strlen(name);
|
||||
char* end = name + str_len;
|
||||
while(end > name && *end != '.' && *end != '\\' && *end != '/') {
|
||||
--end;
|
||||
}
|
||||
if((end > name && *end == '.') && (*(end - 1) != '\\' && *(end - 1) != '/')) {
|
||||
*end = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
void set_file_type(ArchiveFile_t* file, FileInfo* file_info) {
|
||||
furi_assert(file);
|
||||
furi_assert(file_info);
|
||||
|
||||
for(size_t i = 0; i < SIZEOF_ARRAY(known_ext); i++) {
|
||||
if(string_search_str(file->name, known_ext[i], 0) != STRING_FAILURE) {
|
||||
file->type = i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(file_info->flags & FSF_DIRECTORY) {
|
||||
file->type = ArchiveFileTypeFolder;
|
||||
} else {
|
||||
file->type = ArchiveFileTypeUnknown;
|
||||
}
|
||||
}
|
||||
|
||||
bool archive_get_filenames(void* context, uint8_t tab_id, const char* path) {
|
||||
furi_assert(context);
|
||||
|
||||
ArchiveMainView* main_view = context;
|
||||
archive_file_array_clean(main_view);
|
||||
|
||||
if(tab_id != ArchiveTabFavorites) {
|
||||
archive_read_dir(main_view, path);
|
||||
} else {
|
||||
archive_favorites_read(main_view);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool archive_read_dir(void* context, const char* path) {
|
||||
furi_assert(context);
|
||||
|
||||
ArchiveMainView* main_view = context;
|
||||
FileInfo file_info;
|
||||
Storage* fs_api = furi_record_open("storage");
|
||||
File* directory = storage_file_alloc(fs_api);
|
||||
char name[MAX_NAME_LEN];
|
||||
|
||||
if(!storage_dir_open(directory, path)) {
|
||||
storage_dir_close(directory);
|
||||
storage_file_free(directory);
|
||||
return false;
|
||||
}
|
||||
|
||||
while(1) {
|
||||
if(!storage_dir_read(directory, &file_info, name, MAX_NAME_LEN)) {
|
||||
break;
|
||||
}
|
||||
|
||||
uint16_t files_cnt = archive_file_array_size(main_view);
|
||||
|
||||
if(files_cnt > MAX_FILES) {
|
||||
break;
|
||||
} else if(storage_file_get_error(directory) == FSE_OK) {
|
||||
archive_view_add_item(main_view, &file_info, name);
|
||||
} else {
|
||||
storage_dir_close(directory);
|
||||
storage_file_free(directory);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
storage_dir_close(directory);
|
||||
storage_file_free(directory);
|
||||
|
||||
furi_record_close("storage");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void archive_file_append(const char* path, string_t string) {
|
||||
furi_assert(path);
|
||||
furi_assert(string);
|
||||
|
||||
FileWorker* file_worker = file_worker_alloc(false);
|
||||
|
||||
if(!file_worker_open(file_worker, path, FSAM_WRITE, FSOM_OPEN_APPEND)) {
|
||||
FURI_LOG_E("Archive", "Append open error");
|
||||
}
|
||||
|
||||
if(!file_worker_write(file_worker, string_get_cstr(string), string_size(string))) {
|
||||
FURI_LOG_E("Archive", "Append write error");
|
||||
}
|
||||
|
||||
file_worker_close(file_worker);
|
||||
file_worker_free(file_worker);
|
||||
}
|
||||
|
||||
void archive_delete_file(void* context, string_t path, string_t name) {
|
||||
furi_assert(context);
|
||||
furi_assert(path);
|
||||
furi_assert(name);
|
||||
ArchiveMainView* main_view = context;
|
||||
FileWorker* file_worker = file_worker_alloc(false);
|
||||
|
||||
string_t full_path;
|
||||
string_init(full_path);
|
||||
string_printf(full_path, "%s/%s", string_get_cstr(path), string_get_cstr(name));
|
||||
file_worker_remove(file_worker, string_get_cstr(full_path));
|
||||
file_worker_free(file_worker);
|
||||
string_clear(full_path);
|
||||
|
||||
if(archive_is_favorite(string_get_cstr(path), string_get_cstr(name))) {
|
||||
archive_favorites_delete(string_get_cstr(path), string_get_cstr(name));
|
||||
}
|
||||
|
||||
archive_file_array_remove_selected(main_view);
|
||||
}
|
||||
@ -1,15 +1,7 @@
|
||||
#pragma once
|
||||
#include "file-worker.h"
|
||||
|
||||
#include <gui/gui_i.h>
|
||||
#include <gui/canvas.h>
|
||||
#include <gui/elements.h>
|
||||
#include <furi.h>
|
||||
#include <storage/storage.h>
|
||||
|
||||
#define MAX_LEN_PX 100
|
||||
#define MAX_NAME_LEN 255
|
||||
#define FRAME_HEIGHT 12
|
||||
#define MENU_ITEMS 4
|
||||
#define MAX_FILES 100 //temp
|
||||
|
||||
typedef enum {
|
||||
ArchiveFileTypeIButton,
|
||||
@ -22,17 +14,6 @@ typedef enum {
|
||||
AppIdTotal,
|
||||
} ArchiveFileTypeEnum;
|
||||
|
||||
typedef enum {
|
||||
ArchiveTabFavorites,
|
||||
ArchiveTabLFRFID,
|
||||
ArchiveTabSubGhz,
|
||||
ArchiveTabNFC,
|
||||
ArchiveTabIButton,
|
||||
ArchiveTabIrda,
|
||||
ArchiveTabBrowser,
|
||||
ArchiveTabTotal,
|
||||
} ArchiveTabEnum;
|
||||
|
||||
typedef struct {
|
||||
string_t name;
|
||||
ArchiveFileTypeEnum type;
|
||||
@ -66,18 +47,10 @@ ARRAY_DEF(
|
||||
INIT_SET(API_6(ArchiveFile_t_init_set)),
|
||||
CLEAR(API_2(ArchiveFile_t_clear))))
|
||||
|
||||
typedef struct {
|
||||
uint8_t tab_idx;
|
||||
uint8_t menu_idx;
|
||||
uint16_t idx;
|
||||
uint16_t list_offset;
|
||||
files_array_t files;
|
||||
bool menu;
|
||||
} ArchiveViewModel;
|
||||
|
||||
void archive_view_render(Canvas* canvas, void* model);
|
||||
bool filter_by_extension(FileInfo* file_info, const char* tab_ext, const char* name);
|
||||
void set_file_type(ArchiveFile_t* file, FileInfo* file_info);
|
||||
void archive_trim_file_ext(char* name);
|
||||
|
||||
static inline bool is_known_app(ArchiveFileTypeEnum type) {
|
||||
return (type != ArchiveFileTypeFolder && type != ArchiveFileTypeUnknown);
|
||||
}
|
||||
bool archive_get_filenames(void* context, uint8_t tab_id, const char* path);
|
||||
bool archive_read_dir(void* context, const char* path);
|
||||
void archive_file_append(const char* path, string_t string);
|
||||
void archive_delete_file(void* context, string_t path, string_t name);
|
||||
30
applications/archive/scenes/archive_scene.c
Normal file
30
applications/archive/scenes/archive_scene.c
Normal file
@ -0,0 +1,30 @@
|
||||
#include "archive_scene.h"
|
||||
|
||||
// Generate scene on_enter handlers array
|
||||
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
|
||||
void (*const archive_on_enter_handlers[])(void*) = {
|
||||
#include "archive_scene_config.h"
|
||||
};
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_event handlers array
|
||||
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,
|
||||
bool (*const archive_on_event_handlers[])(void* context, SceneManagerEvent event) = {
|
||||
#include "archive_scene_config.h"
|
||||
};
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_exit handlers array
|
||||
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,
|
||||
void (*const archive_on_exit_handlers[])(void* context) = {
|
||||
#include "archive_scene_config.h"
|
||||
};
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Initialize scene handlers configuration structure
|
||||
const SceneManagerHandlers archive_scene_handlers = {
|
||||
.on_enter_handlers = archive_on_enter_handlers,
|
||||
.on_event_handlers = archive_on_event_handlers,
|
||||
.on_exit_handlers = archive_on_exit_handlers,
|
||||
.scene_num = ArchiveAppSceneNum,
|
||||
};
|
||||
29
applications/archive/scenes/archive_scene.h
Normal file
29
applications/archive/scenes/archive_scene.h
Normal file
@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <gui/scene_manager.h>
|
||||
|
||||
// Generate scene id and total number
|
||||
#define ADD_SCENE(prefix, name, id) ArchiveAppScene##id,
|
||||
typedef enum {
|
||||
#include "archive_scene_config.h"
|
||||
ArchiveAppSceneNum,
|
||||
} ArchiveAppScene;
|
||||
#undef ADD_SCENE
|
||||
|
||||
extern const SceneManagerHandlers archive_scene_handlers;
|
||||
|
||||
// Generate scene on_enter handlers declaration
|
||||
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);
|
||||
#include "archive_scene_config.h"
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_event handlers declaration
|
||||
#define ADD_SCENE(prefix, name, id) \
|
||||
bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);
|
||||
#include "archive_scene_config.h"
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_exit handlers declaration
|
||||
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);
|
||||
#include "archive_scene_config.h"
|
||||
#undef ADD_SCENE
|
||||
42
applications/archive/scenes/archive_scene_browser.c
Normal file
42
applications/archive/scenes/archive_scene_browser.c
Normal file
@ -0,0 +1,42 @@
|
||||
#include "../archive_i.h"
|
||||
#include "../views/archive_main_view.h"
|
||||
|
||||
void archive_scene_browser_callback(ArchiveBrowserEvent event, void* context) {
|
||||
ArchiveApp* archive = (ArchiveApp*)context;
|
||||
view_dispatcher_send_custom_event(archive->view_dispatcher, event);
|
||||
}
|
||||
|
||||
const void archive_scene_browser_on_enter(void* context) {
|
||||
ArchiveApp* archive = (ArchiveApp*)context;
|
||||
ArchiveMainView* main_view = archive->main_view;
|
||||
|
||||
archive_browser_set_callback(main_view, archive_scene_browser_callback, archive);
|
||||
archive_browser_update(main_view);
|
||||
view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveViewBrowser);
|
||||
}
|
||||
|
||||
const bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) {
|
||||
ArchiveApp* archive = (ArchiveApp*)context;
|
||||
bool consumed;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
switch(event.event) {
|
||||
case ArchiveBrowserEventRename:
|
||||
scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneRename);
|
||||
consumed = true;
|
||||
break;
|
||||
case ArchiveBrowserEventExit:
|
||||
view_dispatcher_stop(archive->view_dispatcher);
|
||||
consumed = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
const void archive_scene_browser_on_exit(void* context) {
|
||||
// ArchiveApp* archive = (ArchiveApp*)context;
|
||||
}
|
||||
2
applications/archive/scenes/archive_scene_config.h
Normal file
2
applications/archive/scenes/archive_scene_config.h
Normal file
@ -0,0 +1,2 @@
|
||||
ADD_SCENE(archive, browser, Browser)
|
||||
ADD_SCENE(archive, rename, Rename)
|
||||
80
applications/archive/scenes/archive_scene_rename.c
Normal file
80
applications/archive/scenes/archive_scene_rename.c
Normal file
@ -0,0 +1,80 @@
|
||||
#include "../archive_i.h"
|
||||
#include "../helpers/archive_favorites.h"
|
||||
#include "../helpers/archive_files.h"
|
||||
|
||||
#define SCENE_RENAME_CUSTOM_EVENT (0UL)
|
||||
|
||||
void archive_scene_rename_text_input_callback(void* context) {
|
||||
ArchiveApp* archive = (ArchiveApp*)context;
|
||||
view_dispatcher_send_custom_event(archive->view_dispatcher, SCENE_RENAME_CUSTOM_EVENT);
|
||||
}
|
||||
|
||||
const void archive_scene_rename_on_enter(void* context) {
|
||||
ArchiveApp* archive = (ArchiveApp*)context;
|
||||
|
||||
TextInput* text_input = archive->text_input;
|
||||
ArchiveFile_t* current = archive_get_current_file(archive->main_view);
|
||||
strlcpy(archive->text_store, string_get_cstr(current->name), MAX_NAME_LEN);
|
||||
|
||||
archive_trim_file_ext(archive->text_store);
|
||||
|
||||
text_input_set_header_text(text_input, "Rename:");
|
||||
|
||||
text_input_set_result_callback(
|
||||
text_input,
|
||||
archive_scene_rename_text_input_callback,
|
||||
archive,
|
||||
archive->text_store,
|
||||
MAX_NAME_LEN,
|
||||
false);
|
||||
|
||||
view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveViewTextInput);
|
||||
}
|
||||
|
||||
const bool archive_scene_rename_on_event(void* context, SceneManagerEvent event) {
|
||||
ArchiveApp* archive = (ArchiveApp*)context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == SCENE_RENAME_CUSTOM_EVENT) {
|
||||
Storage* fs_api = furi_record_open("storage");
|
||||
|
||||
string_t buffer_src;
|
||||
string_t buffer_dst;
|
||||
|
||||
const char* path = archive_get_path(archive->main_view);
|
||||
const char* name = archive_get_name(archive->main_view);
|
||||
|
||||
string_init_printf(buffer_src, "%s/%s", path, name);
|
||||
string_init_printf(buffer_dst, "%s/%s", path, archive->text_store);
|
||||
|
||||
archive_set_name(archive->main_view, archive->text_store);
|
||||
|
||||
// append extension
|
||||
ArchiveFile_t* file = archive_get_current_file(archive->main_view);
|
||||
|
||||
string_cat(buffer_dst, known_ext[file->type]);
|
||||
storage_common_rename(
|
||||
fs_api, string_get_cstr(buffer_src), string_get_cstr(buffer_dst));
|
||||
furi_record_close("storage");
|
||||
|
||||
if(file->fav) {
|
||||
archive_favorites_rename(path, name, string_get_cstr(buffer_dst));
|
||||
}
|
||||
|
||||
string_clear(buffer_src);
|
||||
string_clear(buffer_dst);
|
||||
|
||||
scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneBrowser);
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
const void archive_scene_rename_on_exit(void* context) {
|
||||
ArchiveApp* archive = (ArchiveApp*)context;
|
||||
// Clear view
|
||||
text_input_set_header_text(archive->text_input, NULL);
|
||||
text_input_set_result_callback(archive->text_input, NULL, NULL, NULL, 0, false);
|
||||
}
|
||||
641
applications/archive/views/archive_main_view.c
Normal file
641
applications/archive/views/archive_main_view.c
Normal file
@ -0,0 +1,641 @@
|
||||
#include <furi.h>
|
||||
#include "../archive_i.h"
|
||||
#include "archive_main_view.h"
|
||||
|
||||
static const char* flipper_app_name[] = {
|
||||
[ArchiveFileTypeIButton] = "iButton",
|
||||
[ArchiveFileTypeNFC] = "NFC",
|
||||
[ArchiveFileTypeSubGhz] = "Sub-GHz",
|
||||
[ArchiveFileTypeLFRFID] = "125 kHz RFID",
|
||||
[ArchiveFileTypeIrda] = "Infrared",
|
||||
};
|
||||
|
||||
static const char* ArchiveTabNames[] = {
|
||||
[ArchiveTabFavorites] = "Favorites",
|
||||
[ArchiveTabIButton] = "iButton",
|
||||
[ArchiveTabNFC] = "NFC",
|
||||
[ArchiveTabSubGhz] = "Sub-GHz",
|
||||
[ArchiveTabLFRFID] = "RFID LF",
|
||||
[ArchiveTabIrda] = "Infrared",
|
||||
[ArchiveTabBrowser] = "Browser"};
|
||||
|
||||
static const Icon* ArchiveItemIcons[] = {
|
||||
[ArchiveFileTypeIButton] = &I_ibutt_10px,
|
||||
[ArchiveFileTypeNFC] = &I_Nfc_10px,
|
||||
[ArchiveFileTypeSubGhz] = &I_sub1_10px,
|
||||
[ArchiveFileTypeLFRFID] = &I_125_10px,
|
||||
[ArchiveFileTypeIrda] = &I_ir_10px,
|
||||
[ArchiveFileTypeFolder] = &I_dir_10px,
|
||||
[ArchiveFileTypeUnknown] = &I_unknown_10px,
|
||||
};
|
||||
|
||||
void archive_browser_set_callback(
|
||||
ArchiveMainView* main_view,
|
||||
ArchiveMainViewCallback callback,
|
||||
void* context) {
|
||||
furi_assert(main_view);
|
||||
furi_assert(callback);
|
||||
main_view->callback = callback;
|
||||
main_view->context = context;
|
||||
}
|
||||
|
||||
void update_offset(ArchiveMainView* main_view) {
|
||||
furi_assert(main_view);
|
||||
with_view_model(
|
||||
main_view->view, (ArchiveMainViewModel * model) {
|
||||
size_t array_size = files_array_size(model->files);
|
||||
uint16_t bounds = array_size > 3 ? 2 : array_size;
|
||||
|
||||
if(array_size > 3 && model->idx >= array_size - 1) {
|
||||
model->list_offset = model->idx - 3;
|
||||
} else if(model->list_offset < model->idx - bounds) {
|
||||
model->list_offset = CLAMP(model->idx - 2, array_size - bounds, 0);
|
||||
} else if(model->list_offset > model->idx - bounds) {
|
||||
model->list_offset = CLAMP(model->idx - 1, array_size - bounds, 0);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
size_t archive_file_array_size(ArchiveMainView* main_view) {
|
||||
uint16_t size = 0;
|
||||
with_view_model(
|
||||
main_view->view, (ArchiveMainViewModel * model) {
|
||||
size = files_array_size(model->files);
|
||||
return true;
|
||||
});
|
||||
return size;
|
||||
}
|
||||
|
||||
void archive_file_array_remove_selected(ArchiveMainView* main_view) {
|
||||
with_view_model(
|
||||
main_view->view, (ArchiveMainViewModel * model) {
|
||||
files_array_remove_v(model->files, model->idx, model->idx + 1);
|
||||
model->idx = CLAMP(model->idx, files_array_size(model->files) - 1, 0);
|
||||
return true;
|
||||
});
|
||||
|
||||
update_offset(main_view);
|
||||
}
|
||||
|
||||
void archive_file_array_clean(ArchiveMainView* main_view) {
|
||||
with_view_model(
|
||||
main_view->view, (ArchiveMainViewModel * model) {
|
||||
files_array_clean(model->files);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
ArchiveFile_t* archive_get_current_file(ArchiveMainView* main_view) {
|
||||
ArchiveFile_t* selected;
|
||||
with_view_model(
|
||||
main_view->view, (ArchiveMainViewModel * model) {
|
||||
selected = files_array_size(model->files) > 0 ?
|
||||
files_array_get(model->files, model->idx) :
|
||||
NULL;
|
||||
return true;
|
||||
});
|
||||
return selected;
|
||||
}
|
||||
|
||||
ArchiveTabEnum archive_get_tab(ArchiveMainView* main_view) {
|
||||
ArchiveTabEnum tab_id;
|
||||
with_view_model(
|
||||
main_view->view, (ArchiveMainViewModel * model) {
|
||||
tab_id = model->tab_idx;
|
||||
return true;
|
||||
});
|
||||
return tab_id;
|
||||
}
|
||||
|
||||
void archive_set_tab(ArchiveMainView* main_view, ArchiveTabEnum tab) {
|
||||
with_view_model(
|
||||
main_view->view, (ArchiveMainViewModel * model) {
|
||||
model->tab_idx = tab;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
uint8_t archive_get_depth(ArchiveMainView* main_view) {
|
||||
uint8_t depth;
|
||||
with_view_model(
|
||||
main_view->view, (ArchiveMainViewModel * model) {
|
||||
depth = model->depth;
|
||||
return true;
|
||||
});
|
||||
|
||||
return depth;
|
||||
}
|
||||
|
||||
const char* archive_get_path(ArchiveMainView* main_view) {
|
||||
return string_get_cstr(main_view->path);
|
||||
}
|
||||
const char* archive_get_name(ArchiveMainView* main_view) {
|
||||
ArchiveFile_t* selected = archive_get_current_file(main_view);
|
||||
return string_get_cstr(selected->name);
|
||||
}
|
||||
|
||||
void archive_set_name(ArchiveMainView* main_view, const char* name) {
|
||||
furi_assert(main_view);
|
||||
furi_assert(name);
|
||||
|
||||
string_set(main_view->name, name);
|
||||
}
|
||||
|
||||
void archive_browser_update(ArchiveMainView* main_view) {
|
||||
furi_assert(main_view);
|
||||
|
||||
archive_get_filenames(main_view, archive_get_tab(main_view), string_get_cstr(main_view->path));
|
||||
|
||||
with_view_model(
|
||||
main_view->view, (ArchiveMainViewModel * model) {
|
||||
uint16_t idx = 0;
|
||||
while(idx < files_array_size(model->files)) {
|
||||
ArchiveFile_t* current = files_array_get(model->files, idx);
|
||||
if(!string_search(current->name, string_get_cstr(main_view->name))) {
|
||||
model->idx = idx;
|
||||
break;
|
||||
}
|
||||
++idx;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
update_offset(main_view);
|
||||
}
|
||||
|
||||
void archive_view_add_item(ArchiveMainView* main_view, FileInfo* file_info, const char* name) {
|
||||
furi_assert(main_view);
|
||||
furi_assert(file_info);
|
||||
furi_assert(name);
|
||||
|
||||
ArchiveFile_t item;
|
||||
|
||||
if(filter_by_extension(file_info, get_tab_ext(archive_get_tab(main_view)), name)) {
|
||||
ArchiveFile_t_init(&item);
|
||||
string_init_set_str(item.name, name);
|
||||
set_file_type(&item, file_info);
|
||||
|
||||
with_view_model(
|
||||
main_view->view, (ArchiveMainViewModel * model) {
|
||||
files_array_push_back(model->files, item);
|
||||
return true;
|
||||
});
|
||||
|
||||
ArchiveFile_t_clear(&item);
|
||||
}
|
||||
}
|
||||
|
||||
static void render_item_menu(Canvas* canvas, ArchiveMainViewModel* model) {
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
canvas_draw_box(canvas, 71, 17, 57, 46);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
elements_slightly_rounded_frame(canvas, 70, 16, 58, 48);
|
||||
|
||||
string_t menu[MENU_ITEMS];
|
||||
|
||||
string_init_set_str(menu[0], "Run in app");
|
||||
string_init_set_str(menu[1], "Pin");
|
||||
string_init_set_str(menu[2], "Rename");
|
||||
string_init_set_str(menu[3], "Delete");
|
||||
|
||||
ArchiveFile_t* selected = files_array_get(model->files, model->idx);
|
||||
|
||||
if(!is_known_app(selected->type)) {
|
||||
string_set_str(menu[0], "---");
|
||||
string_set_str(menu[1], "---");
|
||||
string_set_str(menu[2], "---");
|
||||
} else if(selected->fav) {
|
||||
string_set_str(menu[1], "Unpin");
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < MENU_ITEMS; i++) {
|
||||
canvas_draw_str(canvas, 82, 27 + i * 11, string_get_cstr(menu[i]));
|
||||
string_clear(menu[i]);
|
||||
}
|
||||
|
||||
canvas_draw_icon(canvas, 74, 20 + model->menu_idx * 11, &I_ButtonRight_4x7);
|
||||
}
|
||||
|
||||
static void archive_draw_frame(Canvas* canvas, uint16_t idx, bool scrollbar) {
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_draw_box(canvas, 0, 15 + idx * FRAME_HEIGHT, scrollbar ? 122 : 127, FRAME_HEIGHT);
|
||||
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
canvas_draw_dot(canvas, 0, 15 + idx * FRAME_HEIGHT);
|
||||
canvas_draw_dot(canvas, 1, 15 + idx * FRAME_HEIGHT);
|
||||
canvas_draw_dot(canvas, 0, (15 + idx * FRAME_HEIGHT) + 1);
|
||||
|
||||
canvas_draw_dot(canvas, 0, (15 + idx * FRAME_HEIGHT) + 11);
|
||||
canvas_draw_dot(canvas, scrollbar ? 121 : 126, 15 + idx * FRAME_HEIGHT);
|
||||
canvas_draw_dot(canvas, scrollbar ? 121 : 126, (15 + idx * FRAME_HEIGHT) + 11);
|
||||
}
|
||||
|
||||
static void draw_list(Canvas* canvas, ArchiveMainViewModel* model) {
|
||||
furi_assert(model);
|
||||
|
||||
size_t array_size = files_array_size(model->files);
|
||||
bool scrollbar = array_size > 4;
|
||||
|
||||
for(size_t i = 0; i < MIN(array_size, MENU_ITEMS); ++i) {
|
||||
string_t str_buff;
|
||||
char cstr_buff[MAX_NAME_LEN];
|
||||
|
||||
size_t idx = CLAMP(i + model->list_offset, array_size, 0);
|
||||
ArchiveFile_t* file = files_array_get(model->files, CLAMP(idx, array_size - 1, 0));
|
||||
|
||||
string_init_set(str_buff, file->name);
|
||||
string_right(str_buff, string_search_rchar(str_buff, '/') + 1);
|
||||
strlcpy(cstr_buff, string_get_cstr(str_buff), string_size(str_buff) + 1);
|
||||
|
||||
if(is_known_app(file->type)) archive_trim_file_ext(cstr_buff);
|
||||
|
||||
string_clean(str_buff);
|
||||
string_set_str(str_buff, cstr_buff);
|
||||
|
||||
elements_string_fit_width(canvas, str_buff, scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX);
|
||||
|
||||
if(model->idx == idx) {
|
||||
archive_draw_frame(canvas, i, scrollbar);
|
||||
} else {
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
}
|
||||
|
||||
canvas_draw_icon(canvas, 2, 16 + i * FRAME_HEIGHT, ArchiveItemIcons[file->type]);
|
||||
canvas_draw_str(canvas, 15, 24 + i * FRAME_HEIGHT, string_get_cstr(str_buff));
|
||||
string_clear(str_buff);
|
||||
}
|
||||
|
||||
if(scrollbar) {
|
||||
elements_scrollbar_pos(canvas, 126, 15, 49, model->idx, array_size);
|
||||
}
|
||||
|
||||
if(model->action == BrowserActionItemMenu) {
|
||||
render_item_menu(canvas, model);
|
||||
}
|
||||
}
|
||||
|
||||
static void archive_render_status_bar(Canvas* canvas, ArchiveMainViewModel* model) {
|
||||
furi_assert(model);
|
||||
|
||||
const char* tab_name = ArchiveTabNames[model->tab_idx];
|
||||
|
||||
canvas_draw_icon(canvas, 0, 0, &I_Background_128x11);
|
||||
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
canvas_draw_box(canvas, 0, 0, 50, 13);
|
||||
canvas_draw_box(canvas, 107, 0, 20, 13);
|
||||
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_draw_frame(canvas, 1, 0, 50, 12);
|
||||
canvas_draw_line(canvas, 0, 1, 0, 11);
|
||||
canvas_draw_line(canvas, 1, 12, 49, 12);
|
||||
canvas_draw_str_aligned(canvas, 26, 9, AlignCenter, AlignBottom, tab_name);
|
||||
|
||||
canvas_draw_frame(canvas, 108, 0, 20, 12);
|
||||
canvas_draw_line(canvas, 107, 1, 107, 11);
|
||||
canvas_draw_line(canvas, 108, 12, 126, 12);
|
||||
|
||||
if(model->tab_idx > 0) {
|
||||
canvas_draw_icon(canvas, 112, 2, &I_ButtonLeft_4x7);
|
||||
}
|
||||
if(model->tab_idx < SIZEOF_ARRAY(ArchiveTabNames) - 1) {
|
||||
canvas_draw_icon(canvas, 120, 2, &I_ButtonRight_4x7);
|
||||
}
|
||||
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
canvas_draw_dot(canvas, 50, 0);
|
||||
canvas_draw_dot(canvas, 127, 0);
|
||||
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
}
|
||||
|
||||
void archive_view_render(Canvas* canvas, void* model) {
|
||||
ArchiveMainViewModel* m = model;
|
||||
|
||||
archive_render_status_bar(canvas, model);
|
||||
|
||||
if(files_array_size(m->files) > 0) {
|
||||
draw_list(canvas, m);
|
||||
} else {
|
||||
canvas_draw_str_aligned(
|
||||
canvas, GUI_DISPLAY_WIDTH / 2, 40, AlignCenter, AlignCenter, "Empty");
|
||||
}
|
||||
}
|
||||
|
||||
View* archive_main_get_view(ArchiveMainView* main_view) {
|
||||
furi_assert(main_view);
|
||||
return main_view->view;
|
||||
}
|
||||
|
||||
static void archive_show_file_menu(ArchiveMainView* main_view) {
|
||||
furi_assert(main_view);
|
||||
with_view_model(
|
||||
main_view->view, (ArchiveMainViewModel * model) {
|
||||
ArchiveFile_t* selected;
|
||||
selected = files_array_get(model->files, model->idx);
|
||||
model->action = BrowserActionItemMenu;
|
||||
model->menu_idx = 0;
|
||||
selected->fav = is_known_app(selected->type) ? archive_is_favorite(
|
||||
string_get_cstr(main_view->path),
|
||||
string_get_cstr(selected->name)) :
|
||||
false;
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
static void archive_close_file_menu(ArchiveMainView* main_view) {
|
||||
furi_assert(main_view);
|
||||
|
||||
with_view_model(
|
||||
main_view->view, (ArchiveMainViewModel * model) {
|
||||
model->action = BrowserActionBrowse;
|
||||
model->menu_idx = 0;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
static void archive_run_in_app(
|
||||
ArchiveMainView* main_view,
|
||||
ArchiveFile_t* selected,
|
||||
bool full_path_provided) {
|
||||
Loader* loader = furi_record_open("loader");
|
||||
|
||||
string_t full_path;
|
||||
|
||||
if(!full_path_provided) {
|
||||
string_init_printf(
|
||||
full_path, "%s/%s", string_get_cstr(main_view->path), string_get_cstr(selected->name));
|
||||
} else {
|
||||
string_init_set(full_path, selected->name);
|
||||
}
|
||||
loader_start(loader, flipper_app_name[selected->type], string_get_cstr(full_path));
|
||||
|
||||
string_clear(full_path);
|
||||
furi_record_close("loader");
|
||||
}
|
||||
|
||||
static void archive_file_menu_callback(ArchiveMainView* main_view) {
|
||||
furi_assert(main_view);
|
||||
|
||||
ArchiveFile_t* selected = archive_get_current_file(main_view);
|
||||
const char* path = archive_get_path(main_view);
|
||||
const char* name = archive_get_name(main_view);
|
||||
|
||||
uint8_t idx;
|
||||
with_view_model(
|
||||
main_view->view, (ArchiveMainViewModel * model) {
|
||||
idx = model->menu_idx;
|
||||
return true;
|
||||
});
|
||||
|
||||
switch(idx) {
|
||||
case 0:
|
||||
if(is_known_app(selected->type)) {
|
||||
archive_run_in_app(main_view, selected, false);
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
if(is_known_app(selected->type)) {
|
||||
if(!archive_is_favorite(path, name)) {
|
||||
archive_set_name(main_view, string_get_cstr(selected->name));
|
||||
archive_add_to_favorites(path, name);
|
||||
} else {
|
||||
// delete from favorites
|
||||
archive_favorites_delete(path, name);
|
||||
}
|
||||
archive_close_file_menu(main_view);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
// open rename view
|
||||
if(is_known_app(selected->type)) {
|
||||
main_view->callback(ArchiveBrowserEventRename, main_view->context);
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
// confirmation?
|
||||
archive_delete_file(main_view, main_view->path, selected->name);
|
||||
archive_close_file_menu(main_view);
|
||||
break;
|
||||
|
||||
default:
|
||||
archive_close_file_menu(main_view);
|
||||
break;
|
||||
}
|
||||
selected = NULL;
|
||||
}
|
||||
|
||||
static void archive_switch_dir(ArchiveMainView* main_view, const char* path) {
|
||||
furi_assert(main_view);
|
||||
furi_assert(path);
|
||||
|
||||
string_set(main_view->path, path);
|
||||
archive_get_filenames(main_view, archive_get_tab(main_view), string_get_cstr(main_view->path));
|
||||
update_offset(main_view);
|
||||
}
|
||||
|
||||
void archive_switch_tab(ArchiveMainView* main_view) {
|
||||
furi_assert(main_view);
|
||||
|
||||
with_view_model(
|
||||
main_view->view, (ArchiveMainViewModel * model) {
|
||||
model->idx = 0;
|
||||
model->depth = 0;
|
||||
return true;
|
||||
});
|
||||
|
||||
archive_switch_dir(main_view, tab_default_paths[archive_get_tab(main_view)]);
|
||||
}
|
||||
|
||||
static void archive_enter_dir(ArchiveMainView* main_view, string_t name) {
|
||||
furi_assert(main_view);
|
||||
furi_assert(name);
|
||||
|
||||
// update last index
|
||||
with_view_model(
|
||||
main_view->view, (ArchiveMainViewModel * model) {
|
||||
model->last_idx[model->depth] =
|
||||
CLAMP(model->idx, files_array_size(model->files) - 1, 0);
|
||||
model->idx = 0;
|
||||
model->depth = CLAMP(model->depth + 1, MAX_DEPTH, 0);
|
||||
return true;
|
||||
});
|
||||
|
||||
string_cat(main_view->path, "/");
|
||||
string_cat(main_view->path, main_view->name);
|
||||
|
||||
archive_switch_dir(main_view, string_get_cstr(main_view->path));
|
||||
}
|
||||
|
||||
static void archive_leave_dir(ArchiveMainView* main_view) {
|
||||
furi_assert(main_view);
|
||||
|
||||
char* last_char_ptr = strrchr(string_get_cstr(main_view->path), '/');
|
||||
|
||||
if(last_char_ptr) {
|
||||
size_t pos = last_char_ptr - string_get_cstr(main_view->path);
|
||||
string_left(main_view->path, pos);
|
||||
}
|
||||
|
||||
with_view_model(
|
||||
main_view->view, (ArchiveMainViewModel * model) {
|
||||
model->depth = CLAMP(model->depth - 1, MAX_DEPTH, 0);
|
||||
model->idx = model->last_idx[model->depth];
|
||||
return true;
|
||||
});
|
||||
|
||||
archive_switch_dir(main_view, string_get_cstr(main_view->path));
|
||||
}
|
||||
|
||||
bool archive_view_input(InputEvent* event, void* context) {
|
||||
furi_assert(event);
|
||||
furi_assert(context);
|
||||
|
||||
ArchiveMainView* main_view = context;
|
||||
|
||||
BrowserActionEnum action;
|
||||
with_view_model(
|
||||
main_view->view, (ArchiveMainViewModel * model) {
|
||||
action = model->action;
|
||||
return true;
|
||||
});
|
||||
|
||||
switch(action) {
|
||||
case BrowserActionItemMenu:
|
||||
|
||||
if(event->type == InputTypeShort) {
|
||||
if(event->key == InputKeyUp || event->key == InputKeyDown) {
|
||||
with_view_model(
|
||||
main_view->view, (ArchiveMainViewModel * model) {
|
||||
if(event->key == InputKeyUp) {
|
||||
model->menu_idx = ((model->menu_idx - 1) + MENU_ITEMS) % MENU_ITEMS;
|
||||
} else if(event->key == InputKeyDown) {
|
||||
model->menu_idx = (model->menu_idx + 1) % MENU_ITEMS;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
if(event->key == InputKeyOk) {
|
||||
archive_file_menu_callback(main_view);
|
||||
} else if(event->key == InputKeyBack) {
|
||||
archive_close_file_menu(main_view);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case BrowserActionBrowse:
|
||||
|
||||
if(event->type == InputTypeShort) {
|
||||
if(event->key == InputKeyLeft) {
|
||||
ArchiveTabEnum tab = archive_get_tab(main_view);
|
||||
if(tab) {
|
||||
archive_set_tab(main_view, CLAMP(tab - 1, ArchiveTabTotal, 0));
|
||||
archive_switch_tab(main_view);
|
||||
return true;
|
||||
}
|
||||
} else if(event->key == InputKeyRight) {
|
||||
ArchiveTabEnum tab = archive_get_tab(main_view);
|
||||
|
||||
if(tab < ArchiveTabTotal - 1) {
|
||||
archive_set_tab(main_view, CLAMP(tab + 1, ArchiveTabTotal - 1, 0));
|
||||
archive_switch_tab(main_view);
|
||||
return true;
|
||||
}
|
||||
|
||||
} else if(event->key == InputKeyBack) {
|
||||
if(!archive_get_depth(main_view)) {
|
||||
main_view->callback(ArchiveBrowserEventExit, main_view->context);
|
||||
} else {
|
||||
archive_leave_dir(main_view);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if(event->key == InputKeyUp || event->key == InputKeyDown) {
|
||||
with_view_model(
|
||||
main_view->view, (ArchiveMainViewModel * model) {
|
||||
uint16_t num_elements = (uint16_t)files_array_size(model->files);
|
||||
if((event->type == InputTypeShort || event->type == InputTypeRepeat)) {
|
||||
if(event->key == InputKeyUp) {
|
||||
model->idx = ((model->idx - 1) + num_elements) % num_elements;
|
||||
} else if(event->key == InputKeyDown) {
|
||||
model->idx = (model->idx + 1) % num_elements;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
update_offset(main_view);
|
||||
}
|
||||
|
||||
if(event->key == InputKeyOk) {
|
||||
ArchiveFile_t* selected = archive_get_current_file(main_view);
|
||||
|
||||
if(selected) {
|
||||
archive_set_name(main_view, string_get_cstr(selected->name));
|
||||
if(selected->type == ArchiveFileTypeFolder) {
|
||||
if(event->type == InputTypeShort) {
|
||||
archive_enter_dir(main_view, main_view->name);
|
||||
} else if(event->type == InputTypeLong) {
|
||||
archive_show_file_menu(main_view);
|
||||
}
|
||||
} else {
|
||||
if(event->type == InputTypeShort) {
|
||||
if(archive_get_tab(main_view) == ArchiveTabFavorites) {
|
||||
if(is_known_app(selected->type)) {
|
||||
archive_run_in_app(main_view, selected, true);
|
||||
}
|
||||
} else {
|
||||
archive_show_file_menu(main_view);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ArchiveMainView* main_view_alloc() {
|
||||
ArchiveMainView* main_view = furi_alloc(sizeof(ArchiveMainView));
|
||||
main_view->view = view_alloc();
|
||||
view_allocate_model(main_view->view, ViewModelTypeLocking, sizeof(ArchiveMainViewModel));
|
||||
view_set_context(main_view->view, main_view);
|
||||
view_set_draw_callback(main_view->view, (ViewDrawCallback)archive_view_render);
|
||||
view_set_input_callback(main_view->view, archive_view_input);
|
||||
|
||||
string_init(main_view->name);
|
||||
string_init(main_view->path);
|
||||
|
||||
with_view_model(
|
||||
main_view->view, (ArchiveMainViewModel * model) {
|
||||
files_array_init(model->files);
|
||||
return true;
|
||||
});
|
||||
|
||||
return main_view;
|
||||
}
|
||||
|
||||
void main_view_free(ArchiveMainView* main_view) {
|
||||
furi_assert(main_view);
|
||||
|
||||
with_view_model(
|
||||
main_view->view, (ArchiveMainViewModel * model) {
|
||||
files_array_clear(model->files);
|
||||
return false;
|
||||
});
|
||||
|
||||
string_clear(main_view->name);
|
||||
string_clear(main_view->path);
|
||||
|
||||
view_free(main_view->view);
|
||||
free(main_view);
|
||||
}
|
||||
117
applications/archive/views/archive_main_view.h
Normal file
117
applications/archive/views/archive_main_view.h
Normal file
@ -0,0 +1,117 @@
|
||||
#pragma once
|
||||
|
||||
#include <gui/gui_i.h>
|
||||
#include <gui/view.h>
|
||||
#include <gui/canvas.h>
|
||||
#include <gui/elements.h>
|
||||
#include <furi.h>
|
||||
#include <storage/storage.h>
|
||||
#include "../helpers/archive_files.h"
|
||||
#include "../helpers/archive_favorites.h"
|
||||
|
||||
#define MAX_LEN_PX 110
|
||||
#define MAX_NAME_LEN 255
|
||||
#define FRAME_HEIGHT 12
|
||||
#define MENU_ITEMS 4
|
||||
#define MAX_DEPTH 32
|
||||
|
||||
typedef enum {
|
||||
ArchiveTabFavorites,
|
||||
ArchiveTabLFRFID,
|
||||
ArchiveTabSubGhz,
|
||||
ArchiveTabNFC,
|
||||
ArchiveTabIButton,
|
||||
ArchiveTabIrda,
|
||||
ArchiveTabBrowser,
|
||||
ArchiveTabTotal,
|
||||
} ArchiveTabEnum;
|
||||
|
||||
static const char* known_ext[] = {
|
||||
[ArchiveFileTypeIButton] = ".ibtn",
|
||||
[ArchiveFileTypeNFC] = ".nfc",
|
||||
[ArchiveFileTypeSubGhz] = ".sub",
|
||||
[ArchiveFileTypeLFRFID] = ".rfid",
|
||||
[ArchiveFileTypeIrda] = ".ir",
|
||||
};
|
||||
|
||||
static inline const char* get_tab_ext(ArchiveTabEnum tab) {
|
||||
switch(tab) {
|
||||
case ArchiveTabIButton:
|
||||
return known_ext[ArchiveFileTypeIButton];
|
||||
case ArchiveTabNFC:
|
||||
return known_ext[ArchiveFileTypeNFC];
|
||||
case ArchiveTabSubGhz:
|
||||
return known_ext[ArchiveFileTypeSubGhz];
|
||||
case ArchiveTabLFRFID:
|
||||
return known_ext[ArchiveFileTypeLFRFID];
|
||||
case ArchiveTabIrda:
|
||||
return known_ext[ArchiveFileTypeIrda];
|
||||
default:
|
||||
return "*";
|
||||
}
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
ArchiveBrowserEventRename,
|
||||
ArchiveBrowserEventExit,
|
||||
ArchiveBrowserEventLeaveDir,
|
||||
} ArchiveBrowserEvent;
|
||||
|
||||
typedef struct ArchiveMainView ArchiveMainView;
|
||||
|
||||
typedef void (*ArchiveMainViewCallback)(ArchiveBrowserEvent event, void* context);
|
||||
|
||||
typedef enum {
|
||||
BrowserActionBrowse,
|
||||
BrowserActionItemMenu,
|
||||
BrowserActionTotal,
|
||||
} BrowserActionEnum;
|
||||
|
||||
struct ArchiveMainView {
|
||||
View* view;
|
||||
ArchiveMainViewCallback callback;
|
||||
void* context;
|
||||
|
||||
string_t name;
|
||||
string_t path;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
ArchiveTabEnum tab_idx;
|
||||
BrowserActionEnum action;
|
||||
files_array_t files;
|
||||
|
||||
uint8_t depth;
|
||||
uint8_t menu_idx;
|
||||
|
||||
uint16_t idx;
|
||||
uint16_t last_idx[MAX_DEPTH];
|
||||
uint16_t list_offset;
|
||||
|
||||
} ArchiveMainViewModel;
|
||||
|
||||
void archive_browser_set_callback(
|
||||
ArchiveMainView* main_view,
|
||||
ArchiveMainViewCallback callback,
|
||||
void* context);
|
||||
|
||||
View* archive_main_get_view(ArchiveMainView* main_view);
|
||||
|
||||
ArchiveMainView* main_view_alloc();
|
||||
void main_view_free(ArchiveMainView* main_view);
|
||||
|
||||
void archive_file_array_remove_selected(ArchiveMainView* main_view);
|
||||
void archive_file_array_clean(ArchiveMainView* main_view);
|
||||
|
||||
void archive_view_add_item(ArchiveMainView* main_view, FileInfo* file_info, const char* name);
|
||||
void archive_browser_update(ArchiveMainView* main_view);
|
||||
|
||||
size_t archive_file_array_size(ArchiveMainView* main_view);
|
||||
ArchiveFile_t* archive_get_current_file(ArchiveMainView* main_view);
|
||||
const char* archive_get_path(ArchiveMainView* main_view);
|
||||
const char* archive_get_name(ArchiveMainView* main_view);
|
||||
void archive_set_name(ArchiveMainView* main_view, const char* name);
|
||||
|
||||
static inline bool is_known_app(ArchiveFileTypeEnum type) {
|
||||
return (type != ArchiveFileTypeFolder && type != ArchiveFileTypeUnknown);
|
||||
}
|
||||
25
applications/bt/bt_service/bt.c
Normal file → Executable file
25
applications/bt/bt_service/bt.c
Normal file → Executable file
@ -1,4 +1,5 @@
|
||||
#include "bt_i.h"
|
||||
#include "battery_service.h"
|
||||
|
||||
#define BT_SERVICE_TAG "BT"
|
||||
|
||||
@ -21,6 +22,17 @@ static ViewPort* bt_statusbar_view_port_alloc() {
|
||||
return statusbar_view_port;
|
||||
}
|
||||
|
||||
static void bt_pin_code_show_event_handler(Bt* bt, uint32_t pin) {
|
||||
furi_assert(bt);
|
||||
string_t pin_str;
|
||||
string_init_printf(pin_str, "%06d", pin);
|
||||
dialog_message_set_text(
|
||||
bt->dialog_message, string_get_cstr(pin_str), 64, 32, AlignCenter, AlignCenter);
|
||||
dialog_message_set_buttons(bt->dialog_message, "Back", NULL, NULL);
|
||||
dialog_message_show(bt->dialogs, bt->dialog_message);
|
||||
string_clear(pin_str);
|
||||
}
|
||||
|
||||
Bt* bt_alloc() {
|
||||
Bt* bt = furi_alloc(sizeof(Bt));
|
||||
// Load settings
|
||||
@ -40,13 +52,16 @@ Bt* bt_alloc() {
|
||||
bt->gui = furi_record_open("gui");
|
||||
gui_add_view_port(bt->gui, bt->statusbar_view_port, GuiLayerStatusBarLeft);
|
||||
|
||||
// Dialogs
|
||||
bt->dialogs = furi_record_open("dialogs");
|
||||
bt->dialog_message = dialog_message_alloc();
|
||||
|
||||
return bt;
|
||||
}
|
||||
|
||||
int32_t bt_srv() {
|
||||
Bt* bt = bt_alloc();
|
||||
furi_record_create("bt", bt);
|
||||
furi_hal_bt_init();
|
||||
|
||||
if(!furi_hal_bt_wait_startup()) {
|
||||
FURI_LOG_E(BT_SERVICE_TAG, "Core2 startup failed");
|
||||
@ -68,6 +83,14 @@ int32_t bt_srv() {
|
||||
if(message.type == BtMessageTypeUpdateStatusbar) {
|
||||
// Update statusbar
|
||||
view_port_enabled_set(bt->statusbar_view_port, furi_hal_bt_is_alive());
|
||||
} else if(message.type == BtMessageTypeUpdateBatteryLevel) {
|
||||
// Update battery level
|
||||
if(furi_hal_bt_is_alive()) {
|
||||
battery_svc_update_level(message.data.battery_level);
|
||||
}
|
||||
} else if(message.type == BtMessageTypePinCodeShow) {
|
||||
// Display PIN code
|
||||
bt_pin_code_show_event_handler(bt, message.data.pin_code);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
@ -1,3 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct Bt Bt;
|
||||
|
||||
void bt_update_battery_level(Bt* bt, uint8_t battery_level);
|
||||
|
||||
bool bt_pin_code_show(Bt* bt, uint32_t pin_code);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
15
applications/bt/bt_service/bt_api.c
Executable file
15
applications/bt/bt_service/bt_api.c
Executable file
@ -0,0 +1,15 @@
|
||||
#include "bt.h"
|
||||
#include "bt_i.h"
|
||||
|
||||
void bt_update_battery_level(Bt* bt, uint8_t battery_level) {
|
||||
furi_assert(bt);
|
||||
BtMessage message = {
|
||||
.type = BtMessageTypeUpdateBatteryLevel, .data.battery_level = battery_level};
|
||||
furi_check(osMessageQueuePut(bt->message_queue, &message, 0, osWaitForever) == osOK);
|
||||
}
|
||||
|
||||
bool bt_pin_code_show(Bt* bt, uint32_t pin_code) {
|
||||
furi_assert(bt);
|
||||
BtMessage message = {.type = BtMessageTypePinCodeShow, .data.pin_code = pin_code};
|
||||
return osMessageQueuePut(bt->message_queue, &message, 0, 0) == osOK;
|
||||
}
|
||||
@ -9,15 +9,24 @@
|
||||
#include <gui/view_port.h>
|
||||
#include <gui/view.h>
|
||||
|
||||
#include <applications/dialogs/dialogs.h>
|
||||
|
||||
#include "../bt_settings.h"
|
||||
|
||||
typedef enum {
|
||||
BtMessageTypeUpdateStatusbar,
|
||||
BtMessageTypeUpdateBatteryLevel,
|
||||
BtMessageTypePinCodeShow,
|
||||
} BtMessageType;
|
||||
|
||||
typedef union {
|
||||
uint32_t pin_code;
|
||||
uint8_t battery_level;
|
||||
} BtMessageData;
|
||||
|
||||
typedef struct {
|
||||
BtMessageType type;
|
||||
void* param;
|
||||
BtMessageData data;
|
||||
} BtMessage;
|
||||
|
||||
struct Bt {
|
||||
@ -26,4 +35,6 @@ struct Bt {
|
||||
osTimerId_t update_status_timer;
|
||||
Gui* gui;
|
||||
ViewPort* statusbar_view_port;
|
||||
DialogsApp* dialogs;
|
||||
DialogMessage* dialog_message;
|
||||
};
|
||||
|
||||
@ -68,10 +68,10 @@ static void variable_item_list_draw_callback(Canvas* canvas, void* _model) {
|
||||
canvas_draw_str(canvas, 73, item_text_y, "<");
|
||||
}
|
||||
|
||||
canvas_draw_str(canvas, 84, item_text_y, string_get_cstr(item->current_value_text));
|
||||
canvas_draw_str(canvas, 80, item_text_y, string_get_cstr(item->current_value_text));
|
||||
|
||||
if(item->current_value_index < (item->values_count - 1)) {
|
||||
canvas_draw_str(canvas, 113, item_text_y, ">");
|
||||
canvas_draw_str(canvas, 115, item_text_y, ">");
|
||||
}
|
||||
}
|
||||
|
||||
@ -235,6 +235,21 @@ void variable_item_list_free(VariableItemList* variable_item_list) {
|
||||
free(variable_item_list);
|
||||
}
|
||||
|
||||
void variable_item_list_clean(VariableItemList* variable_item_list) {
|
||||
furi_assert(variable_item_list);
|
||||
|
||||
with_view_model(
|
||||
variable_item_list->view, (VariableItemListModel * model) {
|
||||
VariableItemArray_it_t it;
|
||||
for(VariableItemArray_it(it, model->items); !VariableItemArray_end_p(it);
|
||||
VariableItemArray_next(it)) {
|
||||
string_clean(VariableItemArray_ref(it)->current_value_text);
|
||||
}
|
||||
VariableItemArray_clean(model->items);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
View* variable_item_list_get_view(VariableItemList* variable_item_list) {
|
||||
furi_assert(variable_item_list);
|
||||
return variable_item_list->view;
|
||||
|
||||
@ -18,6 +18,9 @@ VariableItemList* variable_item_list_alloc();
|
||||
* @param variable_item_list VariableItemList instance
|
||||
*/
|
||||
void variable_item_list_free(VariableItemList* variable_item_list);
|
||||
|
||||
void variable_item_list_clean(VariableItemList* variable_item_list);
|
||||
|
||||
View* variable_item_list_get_view(VariableItemList* variable_item_list);
|
||||
|
||||
/** Add item to VariableItemList
|
||||
|
||||
@ -118,6 +118,20 @@ static void widget_add_element(Widget* widget, WidgetElement* element) {
|
||||
});
|
||||
}
|
||||
|
||||
void widget_add_string_multi_element(
|
||||
Widget* widget,
|
||||
uint8_t x,
|
||||
uint8_t y,
|
||||
Align horizontal,
|
||||
Align vertical,
|
||||
Font font,
|
||||
const char* text) {
|
||||
furi_assert(widget);
|
||||
WidgetElement* string_multi_element =
|
||||
widget_element_string_multi_create(x, y, horizontal, vertical, font, text);
|
||||
widget_add_element(widget, string_multi_element);
|
||||
}
|
||||
|
||||
void widget_add_string_element(
|
||||
Widget* widget,
|
||||
uint8_t x,
|
||||
|
||||
@ -26,6 +26,23 @@ void widget_clear(Widget* widget);
|
||||
*/
|
||||
View* widget_get_view(Widget* widget);
|
||||
|
||||
/** Add Multi String Element
|
||||
* @param widget Widget instance
|
||||
* @param x - x coordinate
|
||||
* @param y - y coordinate
|
||||
* @param horizontal - Align instance
|
||||
* @param vertical - Align instance
|
||||
* @param font Font instance
|
||||
*/
|
||||
void widget_add_string_multi_element(
|
||||
Widget* widget,
|
||||
uint8_t x,
|
||||
uint8_t y,
|
||||
Align horizontal,
|
||||
Align vertical,
|
||||
Font font,
|
||||
const char* text);
|
||||
|
||||
/** Add String Element
|
||||
* @param widget Widget instance
|
||||
* @param x - x coordinate
|
||||
|
||||
@ -30,7 +30,18 @@ static bool gui_button_input(InputEvent* event, WidgetElement* element) {
|
||||
GuiButtonModel* model = element->model;
|
||||
bool consumed = false;
|
||||
|
||||
if((event->type == InputTypeShort) && model->callback) {
|
||||
if(model->callback == NULL) return consumed;
|
||||
|
||||
if(event->key == InputKeyOk && event->type == InputTypePress &&
|
||||
model->button_type == GuiButtonTypeCenter) {
|
||||
model->callback(GuiButtonTypeCenterPress, model->context);
|
||||
consumed = true;
|
||||
} else if(
|
||||
event->key == InputKeyOk && event->type == InputTypeRelease &&
|
||||
model->button_type == GuiButtonTypeCenter) {
|
||||
model->callback(GuiButtonTypeCenterRelease, model->context);
|
||||
consumed = true;
|
||||
} else if(event->type == InputTypeShort) {
|
||||
if((model->button_type == GuiButtonTypeLeft) && (event->key == InputKeyLeft)) {
|
||||
model->callback(model->button_type, model->context);
|
||||
consumed = true;
|
||||
|
||||
@ -6,6 +6,8 @@ typedef enum {
|
||||
GuiButtonTypeLeft,
|
||||
GuiButtonTypeCenter,
|
||||
GuiButtonTypeRight,
|
||||
GuiButtonTypeCenterPress,
|
||||
GuiButtonTypeCenterRelease,
|
||||
} GuiButtonType;
|
||||
|
||||
typedef void (*ButtonCallback)(GuiButtonType result, void* context);
|
||||
@ -28,6 +30,15 @@ struct WidgetElement {
|
||||
Widget* parent;
|
||||
};
|
||||
|
||||
/* Create multi string element */
|
||||
WidgetElement* widget_element_string_multi_create(
|
||||
uint8_t x,
|
||||
uint8_t y,
|
||||
Align horizontal,
|
||||
Align vertical,
|
||||
Font font,
|
||||
const char* text);
|
||||
|
||||
/* Create string element */
|
||||
WidgetElement* widget_element_string_create(
|
||||
uint8_t x,
|
||||
|
||||
@ -0,0 +1,67 @@
|
||||
#include "widget_element_i.h"
|
||||
#include <m-string.h>
|
||||
#include <gui/elements.h>
|
||||
|
||||
typedef struct {
|
||||
uint8_t x;
|
||||
uint8_t y;
|
||||
Align horizontal;
|
||||
Align vertical;
|
||||
Font font;
|
||||
string_t text;
|
||||
} GuiStringMultiModel;
|
||||
|
||||
static void gui_string_multi_draw(Canvas* canvas, WidgetElement* element) {
|
||||
furi_assert(canvas);
|
||||
furi_assert(element);
|
||||
GuiStringMultiModel* model = element->model;
|
||||
|
||||
if(string_size(model->text)) {
|
||||
canvas_set_font(canvas, model->font);
|
||||
elements_multiline_text_aligned(
|
||||
canvas,
|
||||
model->x,
|
||||
model->y,
|
||||
model->horizontal,
|
||||
model->vertical,
|
||||
string_get_cstr(model->text));
|
||||
}
|
||||
}
|
||||
|
||||
static void gui_string_multi_free(WidgetElement* gui_string) {
|
||||
furi_assert(gui_string);
|
||||
|
||||
GuiStringMultiModel* model = gui_string->model;
|
||||
string_clear(model->text);
|
||||
free(gui_string->model);
|
||||
free(gui_string);
|
||||
}
|
||||
|
||||
WidgetElement* widget_element_string_multi_create(
|
||||
uint8_t x,
|
||||
uint8_t y,
|
||||
Align horizontal,
|
||||
Align vertical,
|
||||
Font font,
|
||||
const char* text) {
|
||||
furi_assert(text);
|
||||
|
||||
// Allocate and init model
|
||||
GuiStringMultiModel* model = furi_alloc(sizeof(GuiStringMultiModel));
|
||||
model->x = x;
|
||||
model->y = y;
|
||||
model->horizontal = horizontal;
|
||||
model->vertical = vertical;
|
||||
model->font = font;
|
||||
string_init_set_str(model->text, text);
|
||||
|
||||
// Allocate and init Element
|
||||
WidgetElement* gui_string = furi_alloc(sizeof(WidgetElement));
|
||||
gui_string->parent = NULL;
|
||||
gui_string->input = NULL;
|
||||
gui_string->draw = gui_string_multi_draw;
|
||||
gui_string->free = gui_string_multi_free;
|
||||
gui_string->model = model;
|
||||
|
||||
return gui_string;
|
||||
}
|
||||
@ -25,9 +25,9 @@ static void signal_received_callback(void* context, IrdaWorkerSignal* received_s
|
||||
sizeof(buf),
|
||||
"%s, A:0x%0*lX, C:0x%0*lX%s\r\n",
|
||||
irda_get_protocol_name(message->protocol),
|
||||
irda_get_protocol_address_length(message->protocol),
|
||||
ROUND_UP_TO(irda_get_protocol_address_length(message->protocol), 4),
|
||||
message->address,
|
||||
irda_get_protocol_command_length(message->protocol),
|
||||
ROUND_UP_TO(irda_get_protocol_command_length(message->protocol), 4),
|
||||
message->command,
|
||||
message->repeat ? " R" : "");
|
||||
cli_write(cli, (uint8_t*)buf, buf_cnt);
|
||||
@ -98,6 +98,20 @@ static bool parse_message(const char* str, IrdaMessage* message) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t address_length = irda_get_protocol_address_length(protocol);
|
||||
uint32_t address_mask = (1LU << address_length) - 1;
|
||||
if(address != (address & address_mask)) {
|
||||
printf("Address out of range (mask 0x%08lX): 0x%lX\r\n", address_mask, address);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t command_length = irda_get_protocol_command_length(protocol);
|
||||
uint32_t command_mask = (1LU << command_length) - 1;
|
||||
if(command != (command & command_mask)) {
|
||||
printf("Command out of range (mask 0x%08lX): 0x%lX\r\n", command_mask, command);
|
||||
return false;
|
||||
}
|
||||
|
||||
message->protocol = protocol;
|
||||
message->address = address;
|
||||
message->command = command;
|
||||
|
||||
@ -45,9 +45,9 @@ size_t IrdaAppFileParser::stringify_message(
|
||||
"%.31s %.31s A:%0*lX C:%0*lX\n",
|
||||
name,
|
||||
irda_get_protocol_name(protocol),
|
||||
irda_get_protocol_address_length(protocol),
|
||||
ROUND_UP_TO(irda_get_protocol_address_length(protocol), 4),
|
||||
message.address,
|
||||
irda_get_protocol_command_length(protocol),
|
||||
ROUND_UP_TO(irda_get_protocol_command_length(protocol), 4),
|
||||
message.command);
|
||||
|
||||
furi_assert(written < buf_size);
|
||||
@ -162,8 +162,8 @@ std::unique_ptr<IrdaAppFileParser::IrdaFileSignal>
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int address_length = irda_get_protocol_address_length(protocol);
|
||||
uint32_t address_mask = (1LU << (4 * address_length)) - 1;
|
||||
uint32_t address_length = irda_get_protocol_address_length(protocol);
|
||||
uint32_t address_mask = (1LU << address_length) - 1;
|
||||
if(address != (address & address_mask)) {
|
||||
size_t end_of_str = MIN(str.find_last_not_of(" \t\r\n") + 1, (size_t)30);
|
||||
FURI_LOG_E(
|
||||
@ -176,8 +176,8 @@ std::unique_ptr<IrdaAppFileParser::IrdaFileSignal>
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int command_length = irda_get_protocol_command_length(protocol);
|
||||
uint32_t command_mask = (1LU << (4 * command_length)) - 1;
|
||||
uint32_t command_length = irda_get_protocol_command_length(protocol);
|
||||
uint32_t command_mask = (1LU << command_length) - 1;
|
||||
if(command != (command & command_mask)) {
|
||||
size_t end_of_str = MIN(str.find_last_not_of(" \t\r\n") + 1, (size_t)30);
|
||||
FURI_LOG_E(
|
||||
|
||||
@ -29,9 +29,9 @@ void IrdaAppSceneEditDelete::on_enter(IrdaApp* app) {
|
||||
"%s\n%s\nA=0x%0*lX C=0x%0*lX",
|
||||
remote_manager->get_button_name(app->get_current_button()).c_str(),
|
||||
irda_get_protocol_name(message->protocol),
|
||||
irda_get_protocol_address_length(message->protocol),
|
||||
ROUND_UP_TO(irda_get_protocol_address_length(message->protocol), 4),
|
||||
message->address,
|
||||
irda_get_protocol_command_length(message->protocol),
|
||||
ROUND_UP_TO(irda_get_protocol_command_length(message->protocol), 4),
|
||||
message->command);
|
||||
} else {
|
||||
app->set_text_store(
|
||||
|
||||
@ -13,7 +13,7 @@ void IrdaAppSceneLearnEnterName::on_enter(IrdaApp* app) {
|
||||
0,
|
||||
"%.4s_%0*lX",
|
||||
irda_get_protocol_name(message->protocol),
|
||||
irda_get_protocol_command_length(message->protocol),
|
||||
ROUND_UP_TO(irda_get_protocol_command_length(message->protocol), 4),
|
||||
message->command);
|
||||
} else {
|
||||
auto raw_signal = signal.get_raw_signal();
|
||||
|
||||
@ -27,9 +27,9 @@ void IrdaAppSceneLearnSuccess::on_enter(IrdaApp* app) {
|
||||
app->set_text_store(
|
||||
1,
|
||||
"A: 0x%0*lX\nC: 0x%0*lX\n",
|
||||
irda_get_protocol_address_length(message->protocol),
|
||||
ROUND_UP_TO(irda_get_protocol_address_length(message->protocol), 4),
|
||||
message->address,
|
||||
irda_get_protocol_command_length(message->protocol),
|
||||
ROUND_UP_TO(irda_get_protocol_command_length(message->protocol), 4),
|
||||
message->command);
|
||||
dialog_ex_set_header(dialog_ex, app->get_text_store(0), 95, 10, AlignCenter, AlignCenter);
|
||||
dialog_ex_set_text(dialog_ex, app->get_text_store(1), 75, 23, AlignLeft, AlignTop);
|
||||
|
||||
@ -64,18 +64,18 @@ static void signal_received_callback(void* context, IrdaWorkerSignal* received_s
|
||||
sizeof(irda_monitor->display_text),
|
||||
"%s\nA:0x%0*lX\nC:0x%0*lX\n%s\n",
|
||||
irda_get_protocol_name(message->protocol),
|
||||
irda_get_protocol_address_length(message->protocol),
|
||||
ROUND_UP_TO(irda_get_protocol_address_length(message->protocol), 4),
|
||||
message->address,
|
||||
irda_get_protocol_command_length(message->protocol),
|
||||
ROUND_UP_TO(irda_get_protocol_command_length(message->protocol), 4),
|
||||
message->command,
|
||||
message->repeat ? " R" : "");
|
||||
view_port_update(irda_monitor->view_port);
|
||||
printf(
|
||||
"== %s, A:0x%0*lX, C:0x%0*lX%s ==\r\n",
|
||||
irda_get_protocol_name(message->protocol),
|
||||
irda_get_protocol_address_length(message->protocol),
|
||||
ROUND_UP_TO(irda_get_protocol_address_length(message->protocol), 4),
|
||||
message->address,
|
||||
irda_get_protocol_command_length(message->protocol),
|
||||
ROUND_UP_TO(irda_get_protocol_command_length(message->protocol), 4),
|
||||
message->command,
|
||||
message->repeat ? " R" : "");
|
||||
} else {
|
||||
|
||||
@ -18,6 +18,7 @@
|
||||
#include <stm32wbxx.h>
|
||||
|
||||
#include <notification/notification-messages.h>
|
||||
#include <applications/bt/bt_service/bt.h>
|
||||
|
||||
#define POWER_OFF_TIMEOUT 30
|
||||
|
||||
@ -39,6 +40,7 @@ struct Power {
|
||||
|
||||
ValueMutex* menu_vm;
|
||||
Cli* cli;
|
||||
Bt* bt;
|
||||
MenuItem* menu;
|
||||
|
||||
PowerState state;
|
||||
@ -108,6 +110,8 @@ Power* power_alloc() {
|
||||
power->cli = furi_record_open("cli");
|
||||
power_cli_init(power->cli, power);
|
||||
|
||||
power->bt = furi_record_open("bt");
|
||||
|
||||
power->menu = menu_item_alloc_menu("Power", icon_animation_alloc(&A_Power_14));
|
||||
menu_item_subitem_add(
|
||||
power->menu, menu_item_alloc_function("Off", NULL, power_menu_off_callback, power));
|
||||
@ -206,13 +210,15 @@ int32_t power_srv(void* p) {
|
||||
power->menu_vm, (Menu * menu) { menu_item_add(menu, power->menu); });
|
||||
|
||||
furi_record_create("power", power);
|
||||
|
||||
uint8_t battery_level = 0;
|
||||
uint8_t battery_level_prev = 0;
|
||||
while(1) {
|
||||
bool battery_low = false;
|
||||
|
||||
with_view_model(
|
||||
power->info_view, (PowerInfoModel * model) {
|
||||
model->charge = furi_hal_power_get_pct();
|
||||
battery_level = model->charge;
|
||||
model->health = furi_hal_power_get_bat_health_pct();
|
||||
model->capacity_remaining = furi_hal_power_get_battery_remaining_capacity();
|
||||
model->capacity_full = furi_hal_power_get_battery_full_capacity();
|
||||
@ -258,6 +264,11 @@ int32_t power_srv(void* p) {
|
||||
|
||||
power_charging_indication_handler(power, notifications);
|
||||
|
||||
if(battery_level_prev != battery_level) {
|
||||
battery_level_prev = battery_level;
|
||||
bt_update_battery_level(power->bt, battery_level);
|
||||
}
|
||||
|
||||
view_port_update(power->battery_view_port);
|
||||
|
||||
osDelay(1024);
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
ADD_SCENE(subghz, start, Start)
|
||||
ADD_SCENE(subghz, receiver, Receiver)
|
||||
ADD_SCENE(subghz, receiver_config, ReceiverConfig)
|
||||
ADD_SCENE(subghz, receiver_info, ReceiverInfo)
|
||||
ADD_SCENE(subghz, save_name, SaveName)
|
||||
ADD_SCENE(subghz, save_success, SaveSuccess)
|
||||
ADD_SCENE(subghz, saved, Saved)
|
||||
ADD_SCENE(subghz, transmitter, Transmitter)
|
||||
ADD_SCENE(subghz, no_man, NoMan)
|
||||
ADD_SCENE(subghz, show_error, ShowError)
|
||||
ADD_SCENE(subghz, test, Test)
|
||||
ADD_SCENE(subghz, test_static, TestStatic)
|
||||
ADD_SCENE(subghz, test_carrier, TestCarrier)
|
||||
|
||||
@ -1,21 +1,102 @@
|
||||
#include "../subghz_i.h"
|
||||
#include "../views/subghz_receiver.h"
|
||||
|
||||
static void subghz_scene_receiver_update_statusbar(void* context) {
|
||||
SubGhz* subghz = context;
|
||||
char frequency_str[20];
|
||||
char preset_str[10];
|
||||
string_t history_stat_str;
|
||||
string_init(history_stat_str);
|
||||
if(!subghz_history_get_text_space_left(subghz->txrx->history, history_stat_str)) {
|
||||
snprintf(
|
||||
frequency_str,
|
||||
sizeof(frequency_str),
|
||||
"%03ld.%02ld",
|
||||
subghz->txrx->frequency / 1000000 % 1000,
|
||||
subghz->txrx->frequency / 10000 % 100);
|
||||
if(subghz->txrx->preset == FuriHalSubGhzPresetOok650Async ||
|
||||
subghz->txrx->preset == FuriHalSubGhzPresetOok270Async) {
|
||||
snprintf(preset_str, sizeof(preset_str), "AM");
|
||||
} else if(subghz->txrx->preset == FuriHalSubGhzPreset2FSKAsync) {
|
||||
snprintf(preset_str, sizeof(preset_str), "FM");
|
||||
} else {
|
||||
furi_check(0);
|
||||
}
|
||||
subghz_receiver_add_data_statusbar(
|
||||
subghz->subghz_receiver, frequency_str, preset_str, string_get_cstr(history_stat_str));
|
||||
} else {
|
||||
subghz_receiver_add_data_statusbar(
|
||||
subghz->subghz_receiver, string_get_cstr(history_stat_str), "", "");
|
||||
subghz->state_notifications = NOTIFICATION_IDLE_STATE;
|
||||
}
|
||||
string_clear(history_stat_str);
|
||||
}
|
||||
|
||||
void subghz_scene_receiver_callback(SubghzReceverEvent event, void* context) {
|
||||
furi_assert(context);
|
||||
SubGhz* subghz = context;
|
||||
view_dispatcher_send_custom_event(subghz->view_dispatcher, event);
|
||||
}
|
||||
|
||||
void subghz_scene_add_to_history_callback(SubGhzProtocolCommon* parser, void* context) {
|
||||
furi_assert(context);
|
||||
SubGhz* subghz = context;
|
||||
string_t str_buff;
|
||||
string_init(str_buff);
|
||||
|
||||
if(subghz_history_add_to_history(
|
||||
subghz->txrx->history, parser, subghz->txrx->frequency, subghz->txrx->preset)) {
|
||||
subghz_protocol_reset(subghz->txrx->protocol);
|
||||
string_clean(str_buff);
|
||||
subghz_history_get_text_item_menu(
|
||||
subghz->txrx->history, str_buff, subghz_history_get_item(subghz->txrx->history) - 1);
|
||||
subghz_receiver_add_item_to_menu(
|
||||
subghz->subghz_receiver,
|
||||
string_get_cstr(str_buff),
|
||||
subghz_history_get_type_protocol(
|
||||
subghz->txrx->history, subghz_history_get_item(subghz->txrx->history) - 1));
|
||||
subghz_scene_receiver_update_statusbar(subghz);
|
||||
}
|
||||
string_clear(str_buff);
|
||||
}
|
||||
|
||||
const void subghz_scene_receiver_on_enter(void* context) {
|
||||
SubGhz* subghz = context;
|
||||
SubghzReceiver* subghz_receiver = subghz->subghz_receiver;
|
||||
|
||||
subghz_receiver_set_callback(subghz_receiver, subghz_scene_receiver_callback, subghz);
|
||||
string_t str_buff;
|
||||
string_init(str_buff);
|
||||
|
||||
//Load history to receiver
|
||||
subghz_receiver_exit(subghz->subghz_receiver);
|
||||
for(uint8_t i = 0; i < subghz_history_get_item(subghz->txrx->history); i++) {
|
||||
string_clean(str_buff);
|
||||
subghz_history_get_text_item_menu(subghz->txrx->history, str_buff, i);
|
||||
subghz_receiver_add_item_to_menu(
|
||||
subghz->subghz_receiver,
|
||||
string_get_cstr(str_buff),
|
||||
subghz_history_get_type_protocol(subghz->txrx->history, i));
|
||||
}
|
||||
string_clear(str_buff);
|
||||
subghz_scene_receiver_update_statusbar(subghz);
|
||||
subghz_receiver_set_callback(subghz->subghz_receiver, subghz_scene_receiver_callback, subghz);
|
||||
subghz_protocol_enable_dump(
|
||||
subghz->txrx->protocol, subghz_scene_add_to_history_callback, subghz);
|
||||
|
||||
subghz_receiver_set_protocol(subghz_receiver, subghz->protocol_result, subghz->protocol);
|
||||
subghz_receiver_set_worker(subghz_receiver, subghz->worker);
|
||||
subghz->state_notifications = NOTIFICATION_RX_STATE;
|
||||
if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) {
|
||||
subghz_rx_end(subghz->txrx->worker);
|
||||
//subghz_sleep();
|
||||
subghz->txrx->txrx_state = SubGhzTxRxStateIdle;
|
||||
};
|
||||
if(subghz->txrx->txrx_state == SubGhzTxRxStateIdle) {
|
||||
subghz_begin(subghz->txrx->preset);
|
||||
subghz_rx(subghz->txrx->worker, subghz->txrx->frequency);
|
||||
subghz->txrx->txrx_state = SubGhzTxRxStateRx;
|
||||
}
|
||||
if(subghz->txrx->idx_menu_chosen != 0) {
|
||||
subghz_receiver_set_idx_menu(subghz->subghz_receiver, subghz->txrx->idx_menu_chosen);
|
||||
}
|
||||
|
||||
view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewReceiver);
|
||||
}
|
||||
|
||||
@ -24,51 +105,43 @@ const bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
switch(event.event) {
|
||||
case SubghzReceverEventSave:
|
||||
subghz->state_notifications = NOTIFICATION_IDLE_STATE;
|
||||
subghz->frequency = subghz_receiver_get_frequency(subghz->subghz_receiver);
|
||||
subghz->preset = subghz_receiver_get_preset(subghz->subghz_receiver);
|
||||
subghz->protocol_result = subghz_receiver_get_protocol(subghz->subghz_receiver);
|
||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName);
|
||||
return true;
|
||||
break;
|
||||
case SubghzReceverEventBack:
|
||||
scene_manager_previous_scene(subghz->scene_manager);
|
||||
// Stop CC1101 Rx
|
||||
if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) {
|
||||
subghz_rx_end(subghz->txrx->worker);
|
||||
subghz_sleep();
|
||||
subghz->txrx->txrx_state = SubGhzTxRxStateIdle;
|
||||
};
|
||||
subghz_history_clean(subghz->txrx->history);
|
||||
subghz->txrx->hopper_state = SubGhzHopperStateOFF;
|
||||
subghz->txrx->frequency = subghz_frequencies[subghz_frequencies_433_92];
|
||||
subghz->txrx->preset = FuriHalSubGhzPresetOok650Async;
|
||||
subghz->txrx->idx_menu_chosen = 0;
|
||||
subghz_protocol_enable_dump(subghz->txrx->protocol, NULL, subghz);
|
||||
scene_manager_search_and_switch_to_previous_scene(
|
||||
subghz->scene_manager, SubGhzSceneStart);
|
||||
return true;
|
||||
break;
|
||||
case SubghzReceverEventSendStart:
|
||||
subghz->state_notifications = NOTIFICATION_TX_STATE;
|
||||
subghz->frequency = subghz_receiver_get_frequency(subghz->subghz_receiver);
|
||||
subghz->preset = subghz_receiver_get_preset(subghz->subghz_receiver);
|
||||
subghz->protocol_result = subghz_receiver_get_protocol(subghz->subghz_receiver);
|
||||
subghz_transmitter_tx_start(subghz);
|
||||
return true;
|
||||
break;
|
||||
case SubghzReceverEventSendStop:
|
||||
subghz->state_notifications = NOTIFICATION_IDLE_STATE;
|
||||
subghz_transmitter_tx_stop(subghz);
|
||||
return true;
|
||||
break;
|
||||
case SubghzReceverEventMain:
|
||||
subghz->state_notifications = NOTIFICATION_RX_STATE;
|
||||
case SubghzReceverEventOK:
|
||||
subghz->txrx->idx_menu_chosen = subghz_receiver_get_idx_menu(subghz->subghz_receiver);
|
||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverInfo);
|
||||
return true;
|
||||
break;
|
||||
case SubghzReceverEventConfig:
|
||||
subghz->state_notifications = NOTIFICATION_IDLE_STATE;
|
||||
return true;
|
||||
break;
|
||||
case SubghzReceverEventSendHistoryFull:
|
||||
subghz->state_notifications = NOTIFICATION_IDLE_STATE;
|
||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverConfig);
|
||||
return true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if(event.type == SceneManagerEventTypeTick) {
|
||||
if(subghz->txrx->hopper_state != SubGhzHopperStateOFF) {
|
||||
subghz_hopper_update(subghz->txrx);
|
||||
subghz_scene_receiver_update_statusbar(subghz);
|
||||
}
|
||||
|
||||
switch(subghz->state_notifications) {
|
||||
case NOTIFICATION_TX_STATE:
|
||||
notification_message(subghz->notifications, &sequence_blink_red_10);
|
||||
break;
|
||||
case NOTIFICATION_RX_STATE:
|
||||
notification_message(subghz->notifications, &sequence_blink_blue_10);
|
||||
break;
|
||||
|
||||
156
applications/subghz/scenes/subghz_scene_receiver_config.c
Normal file
156
applications/subghz/scenes/subghz_scene_receiver_config.c
Normal file
@ -0,0 +1,156 @@
|
||||
#include "../subghz_i.h"
|
||||
|
||||
#define PRESET_COUNT 3
|
||||
const char* const preset_text[PRESET_COUNT] = {
|
||||
"AM270",
|
||||
"AM650",
|
||||
"FM",
|
||||
};
|
||||
const uint32_t preset_value[PRESET_COUNT] = {
|
||||
FuriHalSubGhzPresetOok270Async, /** OOK, bandwidth 270kHz, asynchronous */
|
||||
FuriHalSubGhzPresetOok650Async, /** OOK, bandwidth 650kHz, asynchronous */
|
||||
FuriHalSubGhzPreset2FSKAsync, /** FM, asynchronous */
|
||||
};
|
||||
|
||||
#define HOPPING_COUNT 2
|
||||
const char* const hopping_text[HOPPING_COUNT] = {
|
||||
"OFF",
|
||||
"ON",
|
||||
};
|
||||
const uint32_t hopping_value[HOPPING_COUNT] = {
|
||||
SubGhzHopperStateOFF,
|
||||
SubGhzHopperStateRunnig,
|
||||
};
|
||||
|
||||
uint8_t subghz_scene_receiver_config_uint32_value_index(
|
||||
const uint32_t value,
|
||||
const uint32_t values[],
|
||||
uint8_t values_count) {
|
||||
int64_t last_value = INT64_MIN;
|
||||
uint8_t index = 0;
|
||||
for(uint8_t i = 0; i < values_count; i++) {
|
||||
if((value >= last_value) && (value <= values[i])) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
last_value = values[i];
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
uint8_t subghz_scene_receiver_config_hopper_value_index(
|
||||
const uint32_t value,
|
||||
const uint32_t values[],
|
||||
uint8_t values_count,
|
||||
void* context) {
|
||||
furi_assert(context);
|
||||
SubGhz* subghz = context;
|
||||
|
||||
if(value == values[0]) {
|
||||
return 0;
|
||||
} else {
|
||||
variable_item_set_current_value_text(
|
||||
(VariableItem*)scene_manager_get_scene_state(
|
||||
subghz->scene_manager, SubGhzSceneReceiverConfig),
|
||||
" -----");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void subghz_scene_receiver_config_set_frequency(VariableItem* item) {
|
||||
SubGhz* subghz = variable_item_get_context(item);
|
||||
uint8_t index = variable_item_get_current_value_index(item);
|
||||
|
||||
if(subghz->txrx->hopper_state == SubGhzHopperStateOFF) {
|
||||
variable_item_set_current_value_text(item, subghz_frequencies_text[index]);
|
||||
subghz->txrx->frequency = subghz_frequencies[index];
|
||||
}
|
||||
}
|
||||
|
||||
static void subghz_scene_receiver_config_set_preset(VariableItem* item) {
|
||||
SubGhz* subghz = variable_item_get_context(item);
|
||||
uint8_t index = variable_item_get_current_value_index(item);
|
||||
|
||||
variable_item_set_current_value_text(item, preset_text[index]);
|
||||
subghz->txrx->preset = preset_value[index];
|
||||
}
|
||||
|
||||
static void subghz_scene_receiver_config_set_hopping_runing(VariableItem* item) {
|
||||
SubGhz* subghz = variable_item_get_context(item);
|
||||
uint8_t index = variable_item_get_current_value_index(item);
|
||||
|
||||
variable_item_set_current_value_text(item, hopping_text[index]);
|
||||
if(hopping_value[index] == SubGhzHopperStateOFF) {
|
||||
variable_item_set_current_value_text(
|
||||
(VariableItem*)scene_manager_get_scene_state(
|
||||
subghz->scene_manager, SubGhzSceneReceiverConfig),
|
||||
subghz_frequencies_text[subghz_frequencies_433_92]);
|
||||
subghz->txrx->frequency = subghz_frequencies[subghz_frequencies_433_92];
|
||||
} else {
|
||||
variable_item_set_current_value_text(
|
||||
(VariableItem*)scene_manager_get_scene_state(
|
||||
subghz->scene_manager, SubGhzSceneReceiverConfig),
|
||||
" -----");
|
||||
}
|
||||
|
||||
subghz->txrx->hopper_state = hopping_value[index];
|
||||
}
|
||||
|
||||
void subghz_scene_receiver_config_callback(SubghzReceverEvent event, void* context) {
|
||||
furi_assert(context);
|
||||
SubGhz* subghz = context;
|
||||
view_dispatcher_send_custom_event(subghz->view_dispatcher, event);
|
||||
}
|
||||
|
||||
const void subghz_scene_receiver_config_on_enter(void* context) {
|
||||
SubGhz* subghz = context;
|
||||
VariableItem* item;
|
||||
uint8_t value_index;
|
||||
|
||||
item = variable_item_list_add(
|
||||
subghz->variable_item_list,
|
||||
"Frequency:",
|
||||
subghz_frequencies_count,
|
||||
subghz_scene_receiver_config_set_frequency,
|
||||
subghz);
|
||||
value_index = subghz_scene_receiver_config_uint32_value_index(
|
||||
subghz->txrx->frequency, subghz_frequencies, subghz_frequencies_count);
|
||||
scene_manager_set_scene_state(
|
||||
subghz->scene_manager, SubGhzSceneReceiverConfig, (uint32_t)item);
|
||||
variable_item_set_current_value_index(item, value_index);
|
||||
variable_item_set_current_value_text(item, subghz_frequencies_text[value_index]);
|
||||
|
||||
item = variable_item_list_add(
|
||||
subghz->variable_item_list,
|
||||
"Hopping:",
|
||||
HOPPING_COUNT,
|
||||
subghz_scene_receiver_config_set_hopping_runing,
|
||||
subghz);
|
||||
value_index = subghz_scene_receiver_config_hopper_value_index(
|
||||
subghz->txrx->hopper_state, hopping_value, HOPPING_COUNT, subghz);
|
||||
variable_item_set_current_value_index(item, value_index);
|
||||
variable_item_set_current_value_text(item, hopping_text[value_index]);
|
||||
|
||||
item = variable_item_list_add(
|
||||
subghz->variable_item_list,
|
||||
"Modulation:",
|
||||
PRESET_COUNT,
|
||||
subghz_scene_receiver_config_set_preset,
|
||||
subghz);
|
||||
value_index = subghz_scene_receiver_config_uint32_value_index(
|
||||
subghz->txrx->preset, preset_value, PRESET_COUNT);
|
||||
variable_item_set_current_value_index(item, value_index);
|
||||
variable_item_set_current_value_text(item, preset_text[value_index]);
|
||||
|
||||
view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewVariableItemList);
|
||||
}
|
||||
|
||||
const bool subghz_scene_receiver_config_on_event(void* context, SceneManagerEvent event) {
|
||||
//SubGhz* subghz = context;
|
||||
return false;
|
||||
}
|
||||
|
||||
const void subghz_scene_receiver_config_on_exit(void* context) {
|
||||
SubGhz* subghz = context;
|
||||
variable_item_list_clean(subghz->variable_item_list);
|
||||
}
|
||||
163
applications/subghz/scenes/subghz_scene_receiver_info.c
Normal file
163
applications/subghz/scenes/subghz_scene_receiver_info.c
Normal file
@ -0,0 +1,163 @@
|
||||
#include "../subghz_i.h"
|
||||
|
||||
void subghz_scene_receiver_info_callback(GuiButtonType result, void* context) {
|
||||
furi_assert(context);
|
||||
SubGhz* subghz = context;
|
||||
view_dispatcher_send_custom_event(subghz->view_dispatcher, result);
|
||||
}
|
||||
|
||||
static bool subghz_scene_receiver_info_update_parser(void* context) {
|
||||
SubGhz* subghz = context;
|
||||
subghz->txrx->protocol_result = subghz_protocol_get_by_name(
|
||||
subghz->txrx->protocol,
|
||||
subghz_history_get_name(subghz->txrx->history, subghz->txrx->idx_menu_chosen));
|
||||
|
||||
if(subghz->txrx->protocol_result->to_load_protocol != NULL) {
|
||||
subghz->txrx->protocol_result->to_load_protocol(
|
||||
subghz->txrx->protocol_result,
|
||||
subghz_history_get_raw_data(subghz->txrx->history, subghz->txrx->idx_menu_chosen));
|
||||
subghz->txrx->frequency =
|
||||
subghz_history_get_frequency(subghz->txrx->history, subghz->txrx->idx_menu_chosen);
|
||||
subghz->txrx->preset =
|
||||
subghz_history_get_preset(subghz->txrx->history, subghz->txrx->idx_menu_chosen);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const void subghz_scene_receiver_info_on_enter(void* context) {
|
||||
SubGhz* subghz = context;
|
||||
|
||||
if(subghz_scene_receiver_info_update_parser(subghz)) {
|
||||
char buffer_str[16];
|
||||
snprintf(
|
||||
buffer_str,
|
||||
sizeof(buffer_str),
|
||||
"%03ld.%02ld",
|
||||
subghz->txrx->frequency / 1000000 % 1000,
|
||||
subghz->txrx->frequency / 10000 % 100);
|
||||
widget_add_string_element(
|
||||
subghz->widget, 78, 0, AlignLeft, AlignTop, FontSecondary, buffer_str);
|
||||
if(subghz->txrx->preset == FuriHalSubGhzPresetOok650Async ||
|
||||
subghz->txrx->preset == FuriHalSubGhzPresetOok270Async) {
|
||||
snprintf(buffer_str, sizeof(buffer_str), "AM");
|
||||
} else if(subghz->txrx->preset == FuriHalSubGhzPreset2FSKAsync) {
|
||||
snprintf(buffer_str, sizeof(buffer_str), "FM");
|
||||
} else {
|
||||
furi_check(0);
|
||||
}
|
||||
widget_add_string_element(
|
||||
subghz->widget, 113, 0, AlignLeft, AlignTop, FontSecondary, buffer_str);
|
||||
string_t text;
|
||||
string_init(text);
|
||||
subghz->txrx->protocol_result->to_string(subghz->txrx->protocol_result, text);
|
||||
widget_add_string_multi_element(
|
||||
subghz->widget, 0, 0, AlignLeft, AlignTop, FontSecondary, string_get_cstr(text));
|
||||
string_clear(text);
|
||||
|
||||
if(subghz->txrx->protocol_result && subghz->txrx->protocol_result->to_save_string &&
|
||||
strcmp(subghz->txrx->protocol_result->name, "KeeLoq")) {
|
||||
widget_add_button_element(
|
||||
subghz->widget,
|
||||
GuiButtonTypeRight,
|
||||
"Save",
|
||||
subghz_scene_receiver_info_callback,
|
||||
subghz);
|
||||
widget_add_button_element(
|
||||
subghz->widget,
|
||||
GuiButtonTypeCenter,
|
||||
"Send",
|
||||
subghz_scene_receiver_info_callback,
|
||||
subghz);
|
||||
}
|
||||
|
||||
} else {
|
||||
widget_add_icon_element(subghz->widget, 32, 12, &I_DolphinFirstStart7_61x51);
|
||||
widget_add_string_element(
|
||||
subghz->widget, 13, 8, AlignLeft, AlignBottom, FontSecondary, "Error history parse.");
|
||||
}
|
||||
|
||||
view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewWidget);
|
||||
}
|
||||
|
||||
const bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event) {
|
||||
SubGhz* subghz = context;
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == GuiButtonTypeCenterPress) {
|
||||
//CC1101 Stop RX -> Start TX
|
||||
subghz->state_notifications = NOTIFICATION_TX_STATE;
|
||||
if(subghz->txrx->hopper_state != SubGhzHopperStateOFF) {
|
||||
subghz->txrx->hopper_state = SubGhzHopperStatePause;
|
||||
}
|
||||
if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) {
|
||||
subghz_rx_end(subghz->txrx->worker);
|
||||
//subghz_sleep();
|
||||
subghz->txrx->txrx_state = SubGhzTxRxStateIdle;
|
||||
}
|
||||
if(!subghz_scene_receiver_info_update_parser(subghz)) {
|
||||
return false;
|
||||
}
|
||||
if(subghz->txrx->txrx_state == SubGhzTxRxStateIdle) {
|
||||
subghz_tx_start(subghz);
|
||||
subghz->txrx->txrx_state = SubGhzTxRxStateTx;
|
||||
}
|
||||
return true;
|
||||
} else if(event.event == GuiButtonTypeCenterRelease) {
|
||||
//CC1101 Stop Tx -> Start RX
|
||||
subghz->state_notifications = NOTIFICATION_IDLE_STATE;
|
||||
if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) {
|
||||
subghz_tx_stop(subghz);
|
||||
subghz->txrx->txrx_state = SubGhzTxRxStateIdle;
|
||||
}
|
||||
if(subghz->txrx->txrx_state == SubGhzTxRxStateIdle) {
|
||||
subghz_begin(subghz->txrx->preset);
|
||||
subghz_rx(subghz->txrx->worker, subghz->txrx->frequency);
|
||||
subghz->txrx->txrx_state = SubGhzTxRxStateRx;
|
||||
}
|
||||
if(subghz->txrx->hopper_state == SubGhzHopperStatePause) {
|
||||
subghz->txrx->hopper_state = SubGhzHopperStateRunnig;
|
||||
}
|
||||
subghz->state_notifications = NOTIFICATION_RX_STATE;
|
||||
return true;
|
||||
} else if(event.event == GuiButtonTypeRight) {
|
||||
//CC1101 Stop RX -> Save
|
||||
subghz->state_notifications = NOTIFICATION_IDLE_STATE;
|
||||
if(subghz->txrx->hopper_state != SubGhzHopperStateOFF) {
|
||||
subghz->txrx->hopper_state = SubGhzHopperStateOFF;
|
||||
}
|
||||
if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) {
|
||||
subghz_rx_end(subghz->txrx->worker);
|
||||
subghz_sleep();
|
||||
subghz->txrx->txrx_state = SubGhzTxRxStateIdle;
|
||||
}
|
||||
if(!subghz_scene_receiver_info_update_parser(subghz)) {
|
||||
return false;
|
||||
}
|
||||
if(subghz->txrx->protocol_result && subghz->txrx->protocol_result->to_save_string &&
|
||||
strcmp(subghz->txrx->protocol_result->name, "KeeLoq")) {
|
||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} else if(event.type == SceneManagerEventTypeTick) {
|
||||
if(subghz->txrx->hopper_state != SubGhzHopperStateOFF) {
|
||||
subghz_hopper_update(subghz->txrx);
|
||||
}
|
||||
switch(subghz->state_notifications) {
|
||||
case NOTIFICATION_TX_STATE:
|
||||
notification_message(subghz->notifications, &sequence_blink_red_10);
|
||||
break;
|
||||
case NOTIFICATION_RX_STATE:
|
||||
notification_message(subghz->notifications, &sequence_blink_blue_10);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const void subghz_scene_receiver_info_on_exit(void* context) {
|
||||
SubGhz* subghz = context;
|
||||
widget_clear(subghz->widget);
|
||||
}
|
||||
@ -35,11 +35,13 @@ const bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent even
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == SCENE_SAVE_NAME_CUSTOM_EVENT) {
|
||||
if(subghz_save_protocol_to_file(subghz, subghz->text_store)) {
|
||||
if(strcmp(subghz->text_store, "") &&
|
||||
subghz_save_protocol_to_file(subghz, subghz->text_store)) {
|
||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveSuccess);
|
||||
return true;
|
||||
} else {
|
||||
//Error save
|
||||
string_set(subghz->error_str, "No name file");
|
||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,15 +8,18 @@ enum SubmenuIndex {
|
||||
SubmenuIndexCAME12bit,
|
||||
SubmenuIndexCAME24bit,
|
||||
SubmenuIndexNeroSketch,
|
||||
SubmenuIndexNeroRadio,
|
||||
SubmenuIndexGateTX,
|
||||
SubmenuIndexDoorHan,
|
||||
};
|
||||
|
||||
bool subghz_scene_set_type_submenu_to_find_protocol(void* context, const char* protocol_name) {
|
||||
SubGhz* subghz = context;
|
||||
subghz->protocol_result = subghz_protocol_get_by_name(subghz->protocol, protocol_name);
|
||||
if(subghz->protocol_result == NULL) {
|
||||
//show error
|
||||
subghz->txrx->protocol_result =
|
||||
subghz_protocol_get_by_name(subghz->txrx->protocol, protocol_name);
|
||||
if(subghz->txrx->protocol_result == NULL) {
|
||||
string_set(subghz->error_str, "Protocol not found");
|
||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@ -62,6 +65,8 @@ const void subghz_scene_set_type_on_enter(void* context) {
|
||||
subghz);
|
||||
// submenu_add_item(
|
||||
// subghz->submenu, "Nero Sketch", SubmenuIndexNeroSketch, subghz_scene_set_type_submenu_callback, subghz);
|
||||
// submenu_add_item(
|
||||
// subghz->submenu, "Nero Radio", SubmenuIndexNeroRadio, subghz_scene_set_type_submenu_callback, subghz);
|
||||
submenu_add_item(
|
||||
subghz->submenu,
|
||||
"Gate TX_433",
|
||||
@ -90,81 +95,86 @@ const bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event
|
||||
switch(event.event) {
|
||||
case SubmenuIndexPricenton:
|
||||
if(subghz_scene_set_type_submenu_to_find_protocol(subghz, "Princeton")) {
|
||||
subghz->protocol_result->code_last_count_bit = 24;
|
||||
subghz->txrx->protocol_result->code_last_count_bit = 24;
|
||||
key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8
|
||||
subghz->protocol_result->code_last_found = key;
|
||||
subghz->txrx->protocol_result->code_last_found = key;
|
||||
generated_protocol = true;
|
||||
}
|
||||
break;
|
||||
case SubmenuIndexNiceFlo12bit:
|
||||
if(subghz_scene_set_type_submenu_to_find_protocol(subghz, "Nice FLO")) {
|
||||
subghz->protocol_result->code_last_count_bit = 12;
|
||||
subghz->txrx->protocol_result->code_last_count_bit = 12;
|
||||
key = (key & 0x0000FFF0) | 0x1; //btn 0x1, 0x2, 0x4
|
||||
subghz->protocol_result->code_last_found = key;
|
||||
subghz->txrx->protocol_result->code_last_found = key;
|
||||
generated_protocol = true;
|
||||
}
|
||||
break;
|
||||
case SubmenuIndexNiceFlo24bit:
|
||||
if(subghz_scene_set_type_submenu_to_find_protocol(subghz, "Nice FLO")) {
|
||||
subghz->protocol_result->code_last_count_bit = 24;
|
||||
subghz->txrx->protocol_result->code_last_count_bit = 24;
|
||||
key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8
|
||||
subghz->protocol_result->code_last_found = key;
|
||||
subghz->txrx->protocol_result->code_last_found = key;
|
||||
generated_protocol = true;
|
||||
}
|
||||
break;
|
||||
case SubmenuIndexCAME12bit:
|
||||
if(subghz_scene_set_type_submenu_to_find_protocol(subghz, "CAME")) {
|
||||
subghz->protocol_result->code_last_count_bit = 12;
|
||||
subghz->txrx->protocol_result->code_last_count_bit = 12;
|
||||
key = (key & 0x0000FFF0) | 0x1; //btn 0x1, 0x2, 0x4
|
||||
subghz->protocol_result->code_last_found = key;
|
||||
subghz->txrx->protocol_result->code_last_found = key;
|
||||
generated_protocol = true;
|
||||
}
|
||||
break;
|
||||
case SubmenuIndexCAME24bit:
|
||||
if(subghz_scene_set_type_submenu_to_find_protocol(subghz, "CAME")) {
|
||||
subghz->protocol_result->code_last_count_bit = 24;
|
||||
subghz->txrx->protocol_result->code_last_count_bit = 24;
|
||||
key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8
|
||||
subghz->protocol_result->code_last_found = key;
|
||||
subghz->txrx->protocol_result->code_last_found = key;
|
||||
generated_protocol = true;
|
||||
}
|
||||
break;
|
||||
// case SubmenuIndexNeroSketch:
|
||||
// /* code */
|
||||
// break;
|
||||
// case SubmenuIndexNeroRadio:
|
||||
// /* code */
|
||||
// break;
|
||||
case SubmenuIndexGateTX:
|
||||
if(subghz_scene_set_type_submenu_to_find_protocol(subghz, "GateTX")) {
|
||||
subghz->protocol_result->code_last_count_bit = 24;
|
||||
subghz->txrx->protocol_result->code_last_count_bit = 24;
|
||||
key = (key & 0x00F0FFFF) | 0xF << 16; //btn 0xF, 0xC, 0xA, 0x6
|
||||
subghz->protocol_result->code_last_found = subghz_protocol_common_reverse_key(
|
||||
key, subghz->protocol_result->code_last_count_bit);
|
||||
subghz->txrx->protocol_result->code_last_found =
|
||||
subghz_protocol_common_reverse_key(
|
||||
key, subghz->txrx->protocol_result->code_last_count_bit);
|
||||
generated_protocol = true;
|
||||
}
|
||||
break;
|
||||
case SubmenuIndexDoorHan:
|
||||
if(subghz_scene_set_type_submenu_to_find_protocol(subghz, "KeeLoq")) {
|
||||
subghz->protocol_result->code_last_count_bit = 64;
|
||||
subghz->protocol_result->serial = key & 0x0FFFFFFF;
|
||||
subghz->protocol_result->btn = 0x2; //btn 0x1, 0x2, 0x4, 0x8
|
||||
subghz->protocol_result->cnt = 0x0003;
|
||||
subghz->txrx->protocol_result->code_last_count_bit = 64;
|
||||
subghz->txrx->protocol_result->serial = key & 0x0FFFFFFF;
|
||||
subghz->txrx->protocol_result->btn = 0x2; //btn 0x1, 0x2, 0x4, 0x8
|
||||
subghz->txrx->protocol_result->cnt = 0x0003;
|
||||
if(subghz_protocol_keeloq_set_manufacture_name(
|
||||
subghz->protocol_result, "DoorHan")) {
|
||||
subghz->protocol_result->code_last_found =
|
||||
subghz_protocol_keeloq_gen_key(subghz->protocol_result);
|
||||
subghz->txrx->protocol_result, "DoorHan")) {
|
||||
subghz->txrx->protocol_result->code_last_found =
|
||||
subghz_protocol_keeloq_gen_key(subghz->txrx->protocol_result);
|
||||
generated_protocol = true;
|
||||
} else {
|
||||
generated_protocol = false;
|
||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneNoMan);
|
||||
string_set(subghz->error_str, "No manufactory key");
|
||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
if(generated_protocol) {
|
||||
subghz->frequency = subghz_frequencies[subghz_frequencies_433_92];
|
||||
subghz->preset = FuriHalSubGhzPresetOok650Async;
|
||||
subghz->txrx->frequency = subghz_frequencies[subghz_frequencies_433_92];
|
||||
subghz->txrx->preset = FuriHalSubGhzPresetOok650Async;
|
||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -2,26 +2,26 @@
|
||||
|
||||
#define SCENE_NO_MAN_CUSTOM_EVENT (11UL)
|
||||
|
||||
void subghz_scene_no_man_popup_callback(void* context) {
|
||||
void subghz_scene_show_error_popup_callback(void* context) {
|
||||
SubGhz* subghz = context;
|
||||
view_dispatcher_send_custom_event(subghz->view_dispatcher, SCENE_NO_MAN_CUSTOM_EVENT);
|
||||
}
|
||||
|
||||
const void subghz_scene_no_man_on_enter(void* context) {
|
||||
const void subghz_scene_show_error_on_enter(void* context) {
|
||||
SubGhz* subghz = context;
|
||||
|
||||
// Setup view
|
||||
Popup* popup = subghz->popup;
|
||||
popup_set_icon(popup, 32, 12, &I_DolphinFirstStart7_61x51);
|
||||
popup_set_header(popup, "No manufactory key", 13, 8, AlignLeft, AlignBottom);
|
||||
popup_set_header(popup, string_get_cstr(subghz->error_str), 64, 8, AlignCenter, AlignBottom);
|
||||
popup_set_timeout(popup, 1500);
|
||||
popup_set_context(popup, subghz);
|
||||
popup_set_callback(popup, subghz_scene_no_man_popup_callback);
|
||||
popup_set_callback(popup, subghz_scene_show_error_popup_callback);
|
||||
popup_enable_timeout(popup);
|
||||
view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewPopup);
|
||||
}
|
||||
|
||||
const bool subghz_scene_no_man_on_event(void* context, SceneManagerEvent event) {
|
||||
const bool subghz_scene_show_error_on_event(void* context, SceneManagerEvent event) {
|
||||
SubGhz* subghz = context;
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == SCENE_NO_MAN_CUSTOM_EVENT) {
|
||||
@ -33,7 +33,7 @@ const bool subghz_scene_no_man_on_event(void* context, SceneManagerEvent event)
|
||||
return false;
|
||||
}
|
||||
|
||||
const void subghz_scene_no_man_on_exit(void* context) {
|
||||
const void subghz_scene_show_error_on_exit(void* context) {
|
||||
SubGhz* subghz = context;
|
||||
|
||||
// Clear view
|
||||
@ -45,4 +45,5 @@ const void subghz_scene_no_man_on_exit(void* context) {
|
||||
popup_set_context(popup, NULL);
|
||||
popup_set_timeout(popup, 0);
|
||||
popup_disable_timeout(popup);
|
||||
string_clean(subghz->error_str);
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
#include "../subghz_i.h"
|
||||
#include "../views/subghz_transmitter.h"
|
||||
#include <lib/subghz/protocols/subghz_protocol_keeloq.h>
|
||||
|
||||
void subghz_scene_transmitter_callback(SubghzTransmitterEvent event, void* context) {
|
||||
furi_assert(context);
|
||||
@ -7,42 +8,90 @@ void subghz_scene_transmitter_callback(SubghzTransmitterEvent event, void* conte
|
||||
view_dispatcher_send_custom_event(subghz->view_dispatcher, event);
|
||||
}
|
||||
|
||||
static void subghz_scene_transmitter_update_data_show(void* context) {
|
||||
SubGhz* subghz = context;
|
||||
|
||||
if(subghz->txrx->protocol_result && subghz->txrx->protocol_result->get_upload_protocol) {
|
||||
string_t key_str;
|
||||
string_init(key_str);
|
||||
char frequency_str[10];
|
||||
char preset_str[6];
|
||||
uint8_t show_button = 0;
|
||||
subghz->txrx->protocol_result->to_string(subghz->txrx->protocol_result, key_str);
|
||||
|
||||
if((!strcmp(subghz->txrx->protocol_result->name, "KeeLoq")) &&
|
||||
(!strcmp(
|
||||
subghz_protocol_keeloq_get_manufacture_name(subghz->txrx->protocol_result),
|
||||
"Unknown"))) {
|
||||
show_button = 0;
|
||||
} else {
|
||||
show_button = 1;
|
||||
}
|
||||
snprintf(
|
||||
frequency_str,
|
||||
sizeof(frequency_str),
|
||||
"%03ld.%02ld",
|
||||
subghz->txrx->frequency / 1000000 % 1000,
|
||||
subghz->txrx->frequency / 10000 % 100);
|
||||
if(subghz->txrx->preset == FuriHalSubGhzPresetOok650Async ||
|
||||
subghz->txrx->preset == FuriHalSubGhzPresetOok270Async) {
|
||||
snprintf(preset_str, sizeof(preset_str), "AM");
|
||||
} else if(subghz->txrx->preset == FuriHalSubGhzPreset2FSKAsync) {
|
||||
snprintf(preset_str, sizeof(preset_str), "FM");
|
||||
} else {
|
||||
furi_check(0);
|
||||
}
|
||||
|
||||
subghz_transmitter_add_data_to_show(
|
||||
subghz->subghz_transmitter,
|
||||
string_get_cstr(key_str),
|
||||
frequency_str,
|
||||
preset_str,
|
||||
show_button);
|
||||
string_clear(key_str);
|
||||
} else {
|
||||
string_set(subghz->error_str, "Protocol not found");
|
||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError);
|
||||
}
|
||||
}
|
||||
|
||||
const void subghz_scene_transmitter_on_enter(void* context) {
|
||||
SubGhz* subghz = context;
|
||||
SubghzTransmitter* subghz_transmitter = subghz->subghz_transmitter;
|
||||
|
||||
subghz_transmitter_set_callback(subghz_transmitter, subghz_scene_transmitter_callback, subghz);
|
||||
subghz_transmitter_set_protocol(subghz_transmitter, subghz->protocol_result);
|
||||
subghz_transmitter_set_frequency_preset(subghz_transmitter, subghz->frequency, subghz->preset);
|
||||
|
||||
view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewTransmitter);
|
||||
|
||||
subghz_transmitter_set_callback(
|
||||
subghz->subghz_transmitter, subghz_scene_transmitter_callback, subghz);
|
||||
subghz_scene_transmitter_update_data_show(subghz);
|
||||
subghz->state_notifications = NOTIFICATION_IDLE_STATE;
|
||||
view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewTransmitter);
|
||||
}
|
||||
|
||||
const bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent event) {
|
||||
SubGhz* subghz = context;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == SubghzTransmitterEventSendStart) {
|
||||
subghz->state_notifications = NOTIFICATION_TX_STATE;
|
||||
subghz_transmitter_tx_start(subghz);
|
||||
if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) {
|
||||
subghz_rx_end(subghz->txrx->worker);
|
||||
subghz->txrx->txrx_state = SubGhzTxRxStateIdle;
|
||||
}
|
||||
if(subghz->txrx->txrx_state == SubGhzTxRxStateIdle) {
|
||||
subghz_tx_start(subghz);
|
||||
subghz_scene_transmitter_update_data_show(subghz);
|
||||
subghz->txrx->txrx_state = SubGhzTxRxStateTx;
|
||||
}
|
||||
return true;
|
||||
} else if(event.event == SubghzTransmitterEventSendStop) {
|
||||
subghz->state_notifications = NOTIFICATION_IDLE_STATE;
|
||||
subghz_transmitter_tx_stop(subghz);
|
||||
subghz_sleep();
|
||||
if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) {
|
||||
subghz_tx_stop(subghz);
|
||||
subghz_sleep();
|
||||
subghz->txrx->txrx_state = SubGhzTxRxStateIdle;
|
||||
}
|
||||
return true;
|
||||
} else if(event.event == SubghzTransmitterEventBack) {
|
||||
subghz->state_notifications = NOTIFICATION_IDLE_STATE;
|
||||
scene_manager_search_and_switch_to_previous_scene(
|
||||
subghz->scene_manager, SubGhzSceneStart);
|
||||
return true;
|
||||
} else if(event.event == SubghzTransmitterEventNoMan) {
|
||||
subghz->state_notifications = NOTIFICATION_IDLE_STATE;
|
||||
scene_manager_search_and_switch_to_previous_scene(
|
||||
subghz->scene_manager, SubGhzSceneNoMan);
|
||||
return true;
|
||||
}
|
||||
} else if(event.type == SceneManagerEventTypeTick) {
|
||||
if(subghz->state_notifications == NOTIFICATION_TX_STATE) {
|
||||
@ -55,7 +104,6 @@ const bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent ev
|
||||
|
||||
const void subghz_scene_transmitter_on_exit(void* context) {
|
||||
SubGhz* subghz = context;
|
||||
SubghzTransmitter* subghz_transmitter = subghz->subghz_transmitter;
|
||||
subghz_transmitter_set_callback(subghz_transmitter, NULL, subghz);
|
||||
|
||||
subghz->state_notifications = NOTIFICATION_IDLE_STATE;
|
||||
}
|
||||
|
||||
@ -1,5 +1,22 @@
|
||||
#include "subghz_i.h"
|
||||
|
||||
const char* const subghz_frequencies_text[] = {
|
||||
"300.00",
|
||||
"315.00",
|
||||
"348.00",
|
||||
"387.00",
|
||||
"433.08",
|
||||
"433.92",
|
||||
"434.78",
|
||||
"438.90",
|
||||
"464.00",
|
||||
"779.00",
|
||||
"868.35",
|
||||
"915.00",
|
||||
"925.00",
|
||||
"928.00",
|
||||
};
|
||||
|
||||
const uint32_t subghz_frequencies[] = {
|
||||
/* 300 - 348 */
|
||||
300000000,
|
||||
@ -20,7 +37,15 @@ const uint32_t subghz_frequencies[] = {
|
||||
928000000,
|
||||
};
|
||||
|
||||
const uint32_t subghz_hopper_frequencies[] = {
|
||||
315000000,
|
||||
433920000,
|
||||
868350000,
|
||||
};
|
||||
|
||||
const uint32_t subghz_frequencies_count = sizeof(subghz_frequencies) / sizeof(uint32_t);
|
||||
const uint32_t subghz_hopper_frequencies_count =
|
||||
sizeof(subghz_hopper_frequencies) / sizeof(uint32_t);
|
||||
const uint32_t subghz_frequencies_433_92 = 5;
|
||||
|
||||
bool subghz_custom_event_callback(void* context, uint32_t event) {
|
||||
@ -77,11 +102,6 @@ SubGhz* subghz_alloc() {
|
||||
SubGhzViewReceiver,
|
||||
subghz_receiver_get_view(subghz->subghz_receiver));
|
||||
|
||||
// Dialog
|
||||
subghz->dialog_ex = dialog_ex_alloc();
|
||||
view_dispatcher_add_view(
|
||||
subghz->view_dispatcher, SubGhzViewDialogEx, dialog_ex_get_view(subghz->dialog_ex));
|
||||
|
||||
// Popup
|
||||
subghz->popup = popup_alloc();
|
||||
view_dispatcher_add_view(
|
||||
@ -92,6 +112,11 @@ SubGhz* subghz_alloc() {
|
||||
view_dispatcher_add_view(
|
||||
subghz->view_dispatcher, SubGhzViewTextInput, text_input_get_view(subghz->text_input));
|
||||
|
||||
// Custom Widget
|
||||
subghz->widget = widget_alloc();
|
||||
view_dispatcher_add_view(
|
||||
subghz->view_dispatcher, SubGhzViewWidget, widget_get_view(subghz->widget));
|
||||
|
||||
// Transmitter
|
||||
subghz->subghz_transmitter = subghz_transmitter_alloc();
|
||||
view_dispatcher_add_view(
|
||||
@ -99,6 +124,13 @@ SubGhz* subghz_alloc() {
|
||||
SubGhzViewTransmitter,
|
||||
subghz_transmitter_get_view(subghz->subghz_transmitter));
|
||||
|
||||
// Variable Item List
|
||||
subghz->variable_item_list = variable_item_list_alloc();
|
||||
view_dispatcher_add_view(
|
||||
subghz->view_dispatcher,
|
||||
SubGhzViewVariableItemList,
|
||||
variable_item_list_get_view(subghz->variable_item_list));
|
||||
|
||||
// Carrier Test Module
|
||||
subghz->subghz_test_carrier = subghz_test_carrier_alloc();
|
||||
view_dispatcher_add_view(
|
||||
@ -120,17 +152,26 @@ SubGhz* subghz_alloc() {
|
||||
SubGhzViewStatic,
|
||||
subghz_test_static_get_view(subghz->subghz_test_static));
|
||||
|
||||
//init Worker & Protocol
|
||||
subghz->worker = subghz_worker_alloc();
|
||||
subghz->protocol = subghz_protocol_alloc();
|
||||
//init Worker & Protocol & History
|
||||
subghz->txrx = furi_alloc(sizeof(SubGhzTxRx));
|
||||
subghz->txrx->frequency = subghz_frequencies[subghz_frequencies_433_92];
|
||||
subghz->txrx->preset = FuriHalSubGhzPresetOok650Async;
|
||||
subghz->txrx->txrx_state = SubGhzTxRxStateIdle;
|
||||
subghz->txrx->hopper_state = SubGhzHopperStateOFF;
|
||||
subghz->txrx->history = subghz_history_alloc();
|
||||
subghz->txrx->worker = subghz_worker_alloc();
|
||||
subghz->txrx->protocol = subghz_protocol_alloc();
|
||||
subghz_worker_set_overrun_callback(
|
||||
subghz->worker, (SubGhzWorkerOverrunCallback)subghz_protocol_reset);
|
||||
subghz->txrx->worker, (SubGhzWorkerOverrunCallback)subghz_protocol_reset);
|
||||
subghz_worker_set_pair_callback(
|
||||
subghz->worker, (SubGhzWorkerPairCallback)subghz_protocol_parse);
|
||||
subghz_worker_set_context(subghz->worker, subghz->protocol);
|
||||
subghz->txrx->worker, (SubGhzWorkerPairCallback)subghz_protocol_parse);
|
||||
subghz_worker_set_context(subghz->txrx->worker, subghz->txrx->protocol);
|
||||
|
||||
subghz_protocol_load_keeloq_file(subghz->protocol, "/ext/subghz/keeloq_mfcodes");
|
||||
subghz_protocol_load_nice_flor_s_file(subghz->protocol, "/ext/subghz/nice_floor_s_rx");
|
||||
//Init Error_str
|
||||
string_init(subghz->error_str);
|
||||
|
||||
subghz_protocol_load_keeloq_file(subghz->txrx->protocol, "/ext/subghz/keeloq_mfcodes");
|
||||
subghz_protocol_load_nice_flor_s_file(subghz->txrx->protocol, "/ext/subghz/nice_floor_s_rx");
|
||||
|
||||
//subghz_protocol_enable_dump_text(subghz->protocol, subghz_text_callback, subghz);
|
||||
|
||||
@ -160,18 +201,22 @@ void subghz_free(SubGhz* subghz) {
|
||||
view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewTextInput);
|
||||
text_input_free(subghz->text_input);
|
||||
|
||||
// Receiver
|
||||
// Custom Widget
|
||||
view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewWidget);
|
||||
widget_free(subghz->widget);
|
||||
|
||||
// Transmitter
|
||||
view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewTransmitter);
|
||||
subghz_transmitter_free(subghz->subghz_transmitter);
|
||||
|
||||
// Variable Item List
|
||||
view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewVariableItemList);
|
||||
variable_item_list_free(subghz->variable_item_list);
|
||||
|
||||
// Submenu
|
||||
view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewMenu);
|
||||
submenu_free(subghz->submenu);
|
||||
|
||||
// DialogEx
|
||||
view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewDialogEx);
|
||||
dialog_ex_free(subghz->dialog_ex);
|
||||
|
||||
// Popup
|
||||
view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewPopup);
|
||||
popup_free(subghz->popup);
|
||||
@ -186,9 +231,14 @@ void subghz_free(SubGhz* subghz) {
|
||||
furi_record_close("gui");
|
||||
subghz->gui = NULL;
|
||||
|
||||
//Worker & Protocol
|
||||
subghz_protocol_free(subghz->protocol);
|
||||
subghz_worker_free(subghz->worker);
|
||||
//Worker & Protocol & History
|
||||
subghz_protocol_free(subghz->txrx->protocol);
|
||||
subghz_worker_free(subghz->txrx->worker);
|
||||
subghz_history_free(subghz->txrx->history);
|
||||
free(subghz->txrx);
|
||||
|
||||
//Error string
|
||||
string_clear(subghz->error_str);
|
||||
|
||||
// Notifications
|
||||
furi_record_close("notification");
|
||||
|
||||
@ -128,21 +128,27 @@ void subghz_history_get_text_item_menu(SubGhzHistory* instance, string_t output,
|
||||
}
|
||||
}
|
||||
|
||||
void subghz_history_add_to_history(SubGhzHistory* instance, void* context) {
|
||||
bool subghz_history_add_to_history(
|
||||
SubGhzHistory* instance,
|
||||
void* context,
|
||||
uint32_t frequency,
|
||||
FuriHalSubGhzPreset preset) {
|
||||
furi_assert(instance);
|
||||
furi_assert(context);
|
||||
SubGhzProtocolCommon* protocol = context;
|
||||
|
||||
if(instance->last_index_write >= SUBGHZ_HISTORY_MAX) return;
|
||||
if(instance->last_index_write >= SUBGHZ_HISTORY_MAX) return false;
|
||||
if((instance->code_last_found == (protocol->code_last_found & 0xFFFF0FFFFFFFFFFF)) &&
|
||||
((millis() - instance->last_update_timestamp) < 500)) {
|
||||
instance->last_update_timestamp = millis();
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
instance->code_last_found = protocol->code_last_found & 0xFFFF0FFFFFFFFFFF;
|
||||
instance->last_update_timestamp = millis();
|
||||
|
||||
instance->history[instance->last_index_write].real_frequency = frequency;
|
||||
instance->history[instance->last_index_write].preset = preset;
|
||||
instance->history[instance->last_index_write].te = 0;
|
||||
instance->history[instance->last_index_write].manufacture_name = NULL;
|
||||
instance->history[instance->last_index_write].name = protocol->name;
|
||||
@ -161,4 +167,5 @@ void subghz_history_add_to_history(SubGhzHistory* instance, void* context) {
|
||||
instance->history[instance->last_index_write].type_protocol = protocol->type_protocol;
|
||||
|
||||
instance->last_index_write++;
|
||||
return true;
|
||||
}
|
||||
@ -1,3 +1,4 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <lib/subghz/protocols/subghz_protocol_common.h>
|
||||
@ -94,8 +95,15 @@ bool subghz_history_get_text_space_left(SubGhzHistory* instance, string_t output
|
||||
*
|
||||
* @param instance - SubGhzHistory instance
|
||||
* @param context - SubGhzProtocolCommon context
|
||||
* @param frequency - frequency Hz
|
||||
* @param preset - FuriHalSubGhzPreset preset
|
||||
* @return bool;
|
||||
*/
|
||||
void subghz_history_add_to_history(SubGhzHistory* instance, void* context);
|
||||
bool subghz_history_add_to_history(
|
||||
SubGhzHistory* instance,
|
||||
void* context,
|
||||
uint32_t frequency,
|
||||
FuriHalSubGhzPreset preset);
|
||||
|
||||
/** Get SubGhzProtocolCommonLoad to load into the protocol decoder bin data
|
||||
*
|
||||
@ -104,3 +112,5 @@ void subghz_history_add_to_history(SubGhzHistory* instance, void* context);
|
||||
* @return SubGhzProtocolCommonLoad*
|
||||
*/
|
||||
SubGhzProtocolCommonLoad* subghz_history_get_raw_data(SubGhzHistory* instance, uint16_t idx);
|
||||
|
||||
void subghz_hopper_update(void* context);
|
||||
|
||||
@ -19,6 +19,9 @@ void subghz_begin(FuriHalSubGhzPreset preset) {
|
||||
|
||||
uint32_t subghz_rx(void* context, uint32_t frequency) {
|
||||
furi_assert(context);
|
||||
if(!furi_hal_subghz_is_frequency_valid(frequency)) {
|
||||
furi_check(0);
|
||||
}
|
||||
SubGhzWorker* worker = context;
|
||||
|
||||
furi_hal_subghz_idle();
|
||||
@ -33,6 +36,9 @@ uint32_t subghz_rx(void* context, uint32_t frequency) {
|
||||
}
|
||||
|
||||
uint32_t subghz_tx(uint32_t frequency) {
|
||||
if(!furi_hal_subghz_is_frequency_valid(frequency)) {
|
||||
furi_check(0);
|
||||
}
|
||||
furi_hal_subghz_idle();
|
||||
uint32_t value = furi_hal_subghz_set_frequency_and_path(frequency);
|
||||
hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);
|
||||
@ -53,6 +59,7 @@ void subghz_rx_end(void* context) {
|
||||
subghz_worker_stop(worker);
|
||||
furi_hal_subghz_stop_async_rx();
|
||||
}
|
||||
furi_hal_subghz_idle();
|
||||
}
|
||||
|
||||
void subghz_sleep(void) {
|
||||
@ -66,41 +73,46 @@ void subghz_frequency_preset_to_str(void* context, string_t output) {
|
||||
output,
|
||||
"Frequency: %d\n"
|
||||
"Preset: %d\n",
|
||||
(int)subghz->frequency,
|
||||
(int)subghz->preset);
|
||||
(int)subghz->txrx->frequency,
|
||||
(int)subghz->txrx->preset);
|
||||
}
|
||||
|
||||
void subghz_transmitter_tx_start(void* context) {
|
||||
void subghz_tx_start(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhz* subghz = context;
|
||||
subghz->encoder = subghz_protocol_encoder_common_alloc();
|
||||
subghz->encoder->repeat = 200; //max repeat with the button held down
|
||||
subghz->txrx->encoder = subghz_protocol_encoder_common_alloc();
|
||||
subghz->txrx->encoder->repeat = 200; //max repeat with the button held down
|
||||
//get upload
|
||||
if(subghz->protocol_result->get_upload_protocol) {
|
||||
if(subghz->protocol_result->get_upload_protocol(subghz->protocol_result, subghz->encoder)) {
|
||||
if(subghz->preset) {
|
||||
subghz_begin(subghz->preset);
|
||||
if(subghz->txrx->protocol_result->get_upload_protocol) {
|
||||
if(subghz->txrx->protocol_result->get_upload_protocol(
|
||||
subghz->txrx->protocol_result, subghz->txrx->encoder)) {
|
||||
if(subghz->txrx->preset) {
|
||||
subghz_begin(subghz->txrx->preset);
|
||||
} else {
|
||||
subghz_begin(FuriHalSubGhzPresetOok650Async);
|
||||
subghz_begin(FuriHalSubGhzPresetOok270Async);
|
||||
}
|
||||
if(subghz->frequency) {
|
||||
subghz_tx(subghz->frequency);
|
||||
if(subghz->txrx->frequency) {
|
||||
subghz_tx(subghz->txrx->frequency);
|
||||
} else {
|
||||
subghz_tx(433920000);
|
||||
}
|
||||
|
||||
//Start TX
|
||||
furi_hal_subghz_start_async_tx(subghz_protocol_encoder_common_yield, subghz->encoder);
|
||||
furi_hal_subghz_start_async_tx(
|
||||
subghz_protocol_encoder_common_yield, subghz->txrx->encoder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void subghz_transmitter_tx_stop(void* context) {
|
||||
void subghz_tx_stop(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhz* subghz = context;
|
||||
//Stop TX
|
||||
furi_hal_subghz_stop_async_tx();
|
||||
subghz_protocol_encoder_common_free(subghz->encoder);
|
||||
subghz_protocol_encoder_common_free(subghz->txrx->encoder);
|
||||
furi_hal_subghz_idle();
|
||||
//if protocol dynamic then we save the last upload
|
||||
if(subghz->protocol_result->type_protocol == TYPE_PROTOCOL_DYNAMIC) {
|
||||
if(subghz->txrx->protocol_result->type_protocol == TYPE_PROTOCOL_DYNAMIC) {
|
||||
subghz_save_protocol_to_file(subghz, subghz->text_store);
|
||||
}
|
||||
notification_message(subghz->notifications, &sequence_reset_red);
|
||||
@ -133,7 +145,7 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path) {
|
||||
if(res != 1) {
|
||||
break;
|
||||
}
|
||||
subghz->frequency = (uint32_t)data;
|
||||
subghz->txrx->frequency = (uint32_t)data;
|
||||
|
||||
// Read and parse preset from 2st line
|
||||
if(!file_worker_read_until(file_worker, temp_str, '\n')) {
|
||||
@ -143,7 +155,7 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path) {
|
||||
if(res != 1) {
|
||||
break;
|
||||
}
|
||||
subghz->preset = (FuriHalSubGhzPreset)data;
|
||||
subghz->txrx->preset = (FuriHalSubGhzPreset)data;
|
||||
|
||||
// Read and parse name protocol from 2st line
|
||||
if(!file_worker_read_until(file_worker, temp_str, '\n')) {
|
||||
@ -151,13 +163,13 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path) {
|
||||
}
|
||||
// strlen("Protocol: ") = 10
|
||||
string_right(temp_str, 10);
|
||||
subghz->protocol_result =
|
||||
subghz_protocol_get_by_name(subghz->protocol, string_get_cstr(temp_str));
|
||||
if(subghz->protocol_result == NULL) {
|
||||
subghz->txrx->protocol_result =
|
||||
subghz_protocol_get_by_name(subghz->txrx->protocol, string_get_cstr(temp_str));
|
||||
if(subghz->txrx->protocol_result == NULL) {
|
||||
break;
|
||||
}
|
||||
if(!subghz->protocol_result->to_load_protocol_from_file(
|
||||
file_worker, subghz->protocol_result)) {
|
||||
if(!subghz->txrx->protocol_result->to_load_protocol_from_file(
|
||||
file_worker, subghz->txrx->protocol_result)) {
|
||||
break;
|
||||
}
|
||||
loaded = true;
|
||||
@ -175,8 +187,9 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path) {
|
||||
}
|
||||
|
||||
bool subghz_save_protocol_to_file(void* context, const char* dev_name) {
|
||||
furi_assert(context);
|
||||
SubGhz* subghz = context;
|
||||
furi_assert(subghz->protocol_result);
|
||||
furi_assert(subghz->txrx->protocol_result);
|
||||
FileWorker* file_worker = file_worker_alloc(false);
|
||||
string_t dev_file_name;
|
||||
string_init(dev_file_name);
|
||||
@ -210,7 +223,7 @@ bool subghz_save_protocol_to_file(void* context, const char* dev_name) {
|
||||
break;
|
||||
}
|
||||
//Get string save
|
||||
subghz->protocol_result->to_save_string(subghz->protocol_result, temp_str);
|
||||
subghz->txrx->protocol_result->to_save_string(subghz->txrx->protocol_result, temp_str);
|
||||
// Prepare and write data to file
|
||||
if(!file_worker_write(file_worker, string_get_cstr(temp_str), string_size(temp_str))) {
|
||||
break;
|
||||
@ -276,7 +289,7 @@ bool subghz_load_protocol_from_file(SubGhz* subghz) {
|
||||
if(sscanf_res != 1) {
|
||||
break;
|
||||
}
|
||||
subghz->frequency = (uint32_t)data;
|
||||
subghz->txrx->frequency = (uint32_t)data;
|
||||
|
||||
// Read and parse preset from 2st line
|
||||
if(!file_worker_read_until(file_worker, temp_str, '\n')) {
|
||||
@ -286,7 +299,7 @@ bool subghz_load_protocol_from_file(SubGhz* subghz) {
|
||||
if(sscanf_res != 1) {
|
||||
break;
|
||||
}
|
||||
subghz->preset = (FuriHalSubGhzPreset)data;
|
||||
subghz->txrx->preset = (FuriHalSubGhzPreset)data;
|
||||
|
||||
// Read and parse name protocol from 3st line
|
||||
if(!file_worker_read_until(file_worker, temp_str, '\n')) {
|
||||
@ -294,13 +307,13 @@ bool subghz_load_protocol_from_file(SubGhz* subghz) {
|
||||
}
|
||||
// strlen("Protocol: ") = 10
|
||||
string_right(temp_str, 10);
|
||||
subghz->protocol_result =
|
||||
subghz_protocol_get_by_name(subghz->protocol, string_get_cstr(temp_str));
|
||||
if(subghz->protocol_result == NULL) {
|
||||
subghz->txrx->protocol_result =
|
||||
subghz_protocol_get_by_name(subghz->txrx->protocol, string_get_cstr(temp_str));
|
||||
if(subghz->txrx->protocol_result == NULL) {
|
||||
break;
|
||||
}
|
||||
if(!subghz->protocol_result->to_load_protocol_from_file(
|
||||
file_worker, subghz->protocol_result)) {
|
||||
if(!subghz->txrx->protocol_result->to_load_protocol_from_file(
|
||||
file_worker, subghz->txrx->protocol_result)) {
|
||||
break;
|
||||
}
|
||||
res = true;
|
||||
@ -328,3 +341,57 @@ uint32_t subghz_random_serial(void) {
|
||||
}
|
||||
return (uint32_t)rand();
|
||||
}
|
||||
|
||||
void subghz_hopper_update(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzTxRx* txrx = context;
|
||||
|
||||
switch(txrx->hopper_state) {
|
||||
case SubGhzHopperStateOFF:
|
||||
return;
|
||||
break;
|
||||
case SubGhzHopperStatePause:
|
||||
return;
|
||||
break;
|
||||
case SubGhzHopperStateRSSITimeOut:
|
||||
if(txrx->hopper_timeout != 0) {
|
||||
txrx->hopper_timeout--;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
float rssi = -127.0f;
|
||||
if(txrx->hopper_state != SubGhzHopperStateRSSITimeOut) {
|
||||
// See RSSI Calculation timings in CC1101 17.3 RSSI
|
||||
rssi = furi_hal_subghz_get_rssi();
|
||||
|
||||
// Stay if RSSI is high enough
|
||||
if(rssi > -90.0f) {
|
||||
txrx->hopper_timeout = 10;
|
||||
txrx->hopper_state = SubGhzHopperStateRSSITimeOut;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
txrx->hopper_state = SubGhzHopperStateRunnig;
|
||||
}
|
||||
|
||||
// Select next frequency
|
||||
if(txrx->hopper_idx_frequency < subghz_hopper_frequencies_count - 1) {
|
||||
txrx->hopper_idx_frequency++;
|
||||
} else {
|
||||
txrx->hopper_idx_frequency = 0;
|
||||
}
|
||||
|
||||
if(txrx->txrx_state == SubGhzTxRxStateRx) {
|
||||
subghz_rx_end(txrx->worker);
|
||||
txrx->txrx_state = SubGhzTxRxStateIdle;
|
||||
};
|
||||
if(txrx->txrx_state == SubGhzTxRxStateIdle) {
|
||||
subghz_protocol_reset(txrx->protocol);
|
||||
txrx->frequency = subghz_hopper_frequencies[txrx->hopper_idx_frequency];
|
||||
subghz_rx(txrx->worker, txrx->frequency);
|
||||
txrx->txrx_state = SubGhzTxRxStateRx;
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,9 +15,9 @@
|
||||
#include <notification/notification-messages.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
#include <gui/modules/submenu.h>
|
||||
#include <gui/modules/dialog_ex.h>
|
||||
#include <gui/modules/popup.h>
|
||||
#include <gui/modules/text_input.h>
|
||||
#include <gui/modules/widget.h>
|
||||
|
||||
#include <subghz/scenes/subghz_scene.h>
|
||||
|
||||
@ -26,6 +26,8 @@
|
||||
#include <lib/subghz/protocols/subghz_protocol_common.h>
|
||||
#include "subghz_history.h"
|
||||
|
||||
#include <gui/modules/variable-item-list.h>
|
||||
|
||||
#define SUBGHZ_TEXT_STORE_SIZE 128
|
||||
|
||||
#define NOTIFICATION_STARTING_STATE 0u
|
||||
@ -33,48 +35,81 @@
|
||||
#define NOTIFICATION_TX_STATE 2u
|
||||
#define NOTIFICATION_RX_STATE 3u
|
||||
|
||||
extern const char* const subghz_frequencies_text[];
|
||||
extern const uint32_t subghz_frequencies[];
|
||||
extern const uint32_t subghz_hopper_frequencies[];
|
||||
extern const uint32_t subghz_frequencies_count;
|
||||
extern const uint32_t subghz_hopper_frequencies_count;
|
||||
extern const uint32_t subghz_frequencies_433_92;
|
||||
|
||||
struct SubGhz {
|
||||
Gui* gui;
|
||||
NotificationApp* notifications;
|
||||
/** SubGhzTxRx state */
|
||||
typedef enum {
|
||||
SubGhzTxRxStateIdle,
|
||||
SubGhzTxRxStateRx,
|
||||
SubGhzTxRxStateTx,
|
||||
} SubGhzTxRxState;
|
||||
|
||||
/** SubGhzHopperState state */
|
||||
typedef enum {
|
||||
SubGhzHopperStateOFF,
|
||||
SubGhzHopperStateRunnig,
|
||||
SubGhzHopperStatePause,
|
||||
SubGhzHopperStateRSSITimeOut,
|
||||
} SubGhzHopperState;
|
||||
|
||||
struct SubGhzTxRx {
|
||||
SubGhzWorker* worker;
|
||||
SubGhzProtocol* protocol;
|
||||
SubGhzProtocolCommon* protocol_result;
|
||||
SubGhzProtocolCommonEncoder* encoder;
|
||||
uint32_t frequency;
|
||||
FuriHalSubGhzPreset preset;
|
||||
SubGhzHistory* history;
|
||||
uint16_t idx_menu_chosen;
|
||||
SubGhzTxRxState txrx_state;
|
||||
//bool hopper_runing;
|
||||
SubGhzHopperState hopper_state;
|
||||
uint8_t hopper_timeout;
|
||||
uint8_t hopper_idx_frequency;
|
||||
};
|
||||
|
||||
typedef struct SubGhzTxRx SubGhzTxRx;
|
||||
|
||||
struct SubGhz {
|
||||
Gui* gui;
|
||||
NotificationApp* notifications;
|
||||
|
||||
SubGhzTxRx* txrx;
|
||||
|
||||
SceneManager* scene_manager;
|
||||
ViewDispatcher* view_dispatcher;
|
||||
|
||||
Submenu* submenu;
|
||||
DialogEx* dialog_ex;
|
||||
Popup* popup;
|
||||
TextInput* text_input;
|
||||
Widget* widget;
|
||||
char text_store[SUBGHZ_TEXT_STORE_SIZE + 1];
|
||||
uint8_t state_notifications;
|
||||
|
||||
SubghzReceiver* subghz_receiver;
|
||||
SubghzTransmitter* subghz_transmitter;
|
||||
VariableItemList* variable_item_list;
|
||||
|
||||
SubghzTestStatic* subghz_test_static;
|
||||
SubghzTestCarrier* subghz_test_carrier;
|
||||
SubghzTestPacket* subghz_test_packet;
|
||||
string_t error_str;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
SubGhzViewMenu,
|
||||
|
||||
SubGhzViewDialogEx,
|
||||
SubGhzViewReceiver,
|
||||
SubGhzViewPopup,
|
||||
SubGhzViewTextInput,
|
||||
SubGhzViewWidget,
|
||||
SubGhzViewTransmitter,
|
||||
|
||||
SubGhzViewVariableItemList,
|
||||
SubGhzViewStatic,
|
||||
SubGhzViewTestCarrier,
|
||||
SubGhzViewTestPacket,
|
||||
@ -86,8 +121,8 @@ uint32_t subghz_tx(uint32_t frequency);
|
||||
void subghz_idle(void);
|
||||
void subghz_rx_end(void* context);
|
||||
void subghz_sleep(void);
|
||||
void subghz_transmitter_tx_start(void* context);
|
||||
void subghz_transmitter_tx_stop(void* context);
|
||||
void subghz_tx_start(void* context);
|
||||
void subghz_tx_stop(void* context);
|
||||
bool subghz_key_load(SubGhz* subghz, const char* file_path);
|
||||
bool subghz_save_protocol_to_file(void* context, const char* dev_name);
|
||||
bool subghz_load_protocol_from_file(SubGhz* subghz);
|
||||
|
||||
@ -1,42 +1,32 @@
|
||||
#include "subghz_receiver.h"
|
||||
#include "../subghz_i.h"
|
||||
#include <math.h>
|
||||
#include <furi.h>
|
||||
#include <furi-hal.h>
|
||||
|
||||
#include <input/input.h>
|
||||
#include <gui/elements.h>
|
||||
#include <notification/notification-messages.h>
|
||||
#include <lib/subghz/protocols/subghz_protocol_princeton.h>
|
||||
|
||||
#include <assets_icons.h>
|
||||
#include <m-string.h>
|
||||
#include <m-array.h>
|
||||
|
||||
#define FRAME_HEIGHT 12
|
||||
#define MAX_LEN_PX 100
|
||||
#define MENU_ITEMS 4
|
||||
|
||||
#define COUNT_FREQUNCY_HOPPER 3
|
||||
const uint32_t subghz_frequencies_hopper[] = {
|
||||
/* 300 - 348 */
|
||||
315000000,
|
||||
/* 387 - 464 */
|
||||
433920000, /* LPD433 mid */
|
||||
/* 779 - 928 */
|
||||
868350000,
|
||||
typedef struct {
|
||||
string_t item_str;
|
||||
uint8_t type;
|
||||
} SubGhzReceiverMenuItem;
|
||||
|
||||
ARRAY_DEF(SubGhzReceiverMenuItemArray, SubGhzReceiverMenuItem, M_POD_OPLIST)
|
||||
|
||||
#define M_OPL_SubGhzReceiverMenuItemArray_t() \
|
||||
ARRAY_OPLIST(SubGhzReceiverMenuItemArray, M_POD_OPLIST)
|
||||
|
||||
struct SubGhzReceiverHistory {
|
||||
SubGhzReceiverMenuItemArray_t data;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
ReceiverSceneStart,
|
||||
ReceiverSceneMain,
|
||||
ReceiverSceneConfig,
|
||||
ReceiverSceneInfo,
|
||||
} SubghzReceiverScene;
|
||||
|
||||
typedef enum {
|
||||
SubGhzHopperStateOFF,
|
||||
SubGhzHopperStatePause,
|
||||
SubGhzHopperStateRunnig,
|
||||
SubGhzHopperStateRSSITimeOut,
|
||||
} SubGhzHopperState;
|
||||
typedef struct SubGhzReceiverHistory SubGhzReceiverHistory;
|
||||
|
||||
static const Icon* ReceiverItemIcons[] = {
|
||||
[TYPE_PROTOCOL_UNKNOWN] = &I_Quest_7x8,
|
||||
@ -48,27 +38,16 @@ struct SubghzReceiver {
|
||||
View* view;
|
||||
SubghzReceiverCallback callback;
|
||||
void* context;
|
||||
SubGhzWorker* worker;
|
||||
SubGhzProtocol* protocol;
|
||||
osTimerId timer;
|
||||
SubGhzHopperState hopper_state;
|
||||
uint8_t hopper_timeout;
|
||||
uint32_t event_key_sequence;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
string_t text;
|
||||
uint16_t scene;
|
||||
SubGhzProtocolCommon* protocol_result;
|
||||
SubGhzHistory* history;
|
||||
uint8_t frequency;
|
||||
uint8_t temp_frequency;
|
||||
uint32_t real_frequency;
|
||||
|
||||
string_t frequency_str;
|
||||
string_t preset_str;
|
||||
string_t history_stat_str;
|
||||
SubGhzReceiverHistory* history;
|
||||
uint16_t idx;
|
||||
uint16_t list_offset;
|
||||
uint16_t history_item;
|
||||
bool menu;
|
||||
} SubghzReceiverModel;
|
||||
|
||||
void subghz_receiver_set_callback(
|
||||
@ -81,35 +60,6 @@ void subghz_receiver_set_callback(
|
||||
subghz_receiver->context = context;
|
||||
}
|
||||
|
||||
void subghz_receiver_set_protocol(
|
||||
SubghzReceiver* subghz_receiver,
|
||||
SubGhzProtocolCommon* protocol_result,
|
||||
SubGhzProtocol* protocol) {
|
||||
furi_assert(subghz_receiver);
|
||||
with_view_model(
|
||||
subghz_receiver->view, (SubghzReceiverModel * model) {
|
||||
model->protocol_result = protocol_result;
|
||||
return true;
|
||||
});
|
||||
subghz_receiver->protocol = protocol;
|
||||
}
|
||||
|
||||
SubGhzProtocolCommon* subghz_receiver_get_protocol(SubghzReceiver* subghz_receiver) {
|
||||
furi_assert(subghz_receiver);
|
||||
SubGhzProtocolCommon* result = NULL;
|
||||
with_view_model(
|
||||
subghz_receiver->view, (SubghzReceiverModel * model) {
|
||||
result = model->protocol_result;
|
||||
return false;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
void subghz_receiver_set_worker(SubghzReceiver* subghz_receiver, SubGhzWorker* worker) {
|
||||
furi_assert(subghz_receiver);
|
||||
subghz_receiver->worker = worker;
|
||||
}
|
||||
|
||||
static void subghz_receiver_update_offset(SubghzReceiver* subghz_receiver) {
|
||||
furi_assert(subghz_receiver);
|
||||
|
||||
@ -129,6 +79,38 @@ static void subghz_receiver_update_offset(SubghzReceiver* subghz_receiver) {
|
||||
});
|
||||
}
|
||||
|
||||
void subghz_receiver_add_item_to_menu(
|
||||
SubghzReceiver* subghz_receiver,
|
||||
const char* name,
|
||||
uint8_t type) {
|
||||
furi_assert(subghz_receiver);
|
||||
with_view_model(
|
||||
subghz_receiver->view, (SubghzReceiverModel * model) {
|
||||
SubGhzReceiverMenuItem* item_menu =
|
||||
SubGhzReceiverMenuItemArray_push_raw(model->history->data);
|
||||
string_init_set_str(item_menu->item_str, name);
|
||||
item_menu->type = type;
|
||||
model->history_item++;
|
||||
return true;
|
||||
});
|
||||
subghz_receiver_update_offset(subghz_receiver);
|
||||
}
|
||||
|
||||
void subghz_receiver_add_data_statusbar(
|
||||
SubghzReceiver* subghz_receiver,
|
||||
const char* frequency_str,
|
||||
const char* preset_str,
|
||||
const char* history_stat_str) {
|
||||
furi_assert(subghz_receiver);
|
||||
with_view_model(
|
||||
subghz_receiver->view, (SubghzReceiverModel * model) {
|
||||
string_set(model->frequency_str, frequency_str);
|
||||
string_set(model->preset_str, preset_str);
|
||||
string_set(model->history_stat_str, history_stat_str);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
static void subghz_receiver_draw_frame(Canvas* canvas, uint16_t idx, bool scrollbar) {
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_draw_box(canvas, 0, 0 + idx * FRAME_HEIGHT, scrollbar ? 122 : 127, FRAME_HEIGHT);
|
||||
@ -144,462 +126,113 @@ static void subghz_receiver_draw_frame(Canvas* canvas, uint16_t idx, bool scroll
|
||||
}
|
||||
|
||||
void subghz_receiver_draw(Canvas* canvas, SubghzReceiverModel* model) {
|
||||
bool scrollbar = model->history_item > 4;
|
||||
string_t str_buff;
|
||||
char buffer[64];
|
||||
uint32_t frequency;
|
||||
string_init(str_buff);
|
||||
|
||||
canvas_clear(canvas);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
|
||||
switch(model->scene) {
|
||||
case ReceiverSceneMain:
|
||||
for(size_t i = 0; i < MIN(model->history_item, MENU_ITEMS); ++i) {
|
||||
size_t idx = CLAMP(i + model->list_offset, model->history_item, 0);
|
||||
subghz_history_get_text_item_menu(model->history, str_buff, idx);
|
||||
elements_string_fit_width(canvas, str_buff, scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX);
|
||||
if(model->idx == idx) {
|
||||
subghz_receiver_draw_frame(canvas, i, scrollbar);
|
||||
} else {
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
}
|
||||
canvas_draw_icon(
|
||||
canvas,
|
||||
1,
|
||||
2 + i * FRAME_HEIGHT,
|
||||
ReceiverItemIcons[subghz_history_get_type_protocol(model->history, idx)]);
|
||||
canvas_draw_str(canvas, 15, 9 + i * FRAME_HEIGHT, string_get_cstr(str_buff));
|
||||
string_clean(str_buff);
|
||||
}
|
||||
if(scrollbar) {
|
||||
elements_scrollbar_pos(canvas, 128, 0, 49, model->idx, model->history_item);
|
||||
}
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
elements_button_left(canvas, "Config");
|
||||
|
||||
elements_button_left(canvas, "Config");
|
||||
canvas_draw_line(canvas, 46, 51, 125, 51);
|
||||
if(subghz_history_get_text_space_left(model->history, str_buff)) {
|
||||
canvas_draw_str(canvas, 54, 62, string_get_cstr(str_buff));
|
||||
} else {
|
||||
if((model->real_frequency / 1000 % 10) > 4) {
|
||||
frequency = model->real_frequency + 10000;
|
||||
} else {
|
||||
frequency = model->real_frequency;
|
||||
}
|
||||
snprintf(
|
||||
buffer,
|
||||
sizeof(buffer),
|
||||
"%03ld.%02ld",
|
||||
frequency / 1000000 % 1000,
|
||||
frequency / 10000 % 100);
|
||||
canvas_draw_str(canvas, 44, 62, buffer);
|
||||
canvas_draw_str(canvas, 79, 62, "AM");
|
||||
canvas_draw_str(canvas, 96, 62, string_get_cstr(str_buff));
|
||||
}
|
||||
break;
|
||||
|
||||
case ReceiverSceneStart:
|
||||
canvas_draw_str(canvas, 44, 62, string_get_cstr(model->frequency_str));
|
||||
canvas_draw_str(canvas, 79, 62, string_get_cstr(model->preset_str));
|
||||
canvas_draw_str(canvas, 96, 62, string_get_cstr(model->history_stat_str));
|
||||
if(model->history_item == 0) {
|
||||
canvas_draw_icon(canvas, 0, 0, &I_Scanning_123x52);
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str(canvas, 63, 46, "Scanning...");
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
elements_button_left(canvas, "Config");
|
||||
if((model->real_frequency / 1000 % 10) > 4) {
|
||||
frequency = model->real_frequency + 10000;
|
||||
} else {
|
||||
frequency = model->real_frequency;
|
||||
}
|
||||
snprintf(
|
||||
buffer,
|
||||
sizeof(buffer),
|
||||
"%03ld.%02ld",
|
||||
frequency / 1000000 % 1000,
|
||||
frequency / 10000 % 100);
|
||||
canvas_draw_str(canvas, 44, 62, buffer);
|
||||
canvas_draw_str(canvas, 79, 62, "AM");
|
||||
subghz_history_get_text_space_left(model->history, str_buff);
|
||||
canvas_draw_str(canvas, 96, 62, string_get_cstr(str_buff));
|
||||
canvas_draw_line(canvas, 46, 51, 125, 51);
|
||||
break;
|
||||
|
||||
case ReceiverSceneConfig:
|
||||
if(model->frequency < subghz_frequencies_count) {
|
||||
snprintf(
|
||||
buffer,
|
||||
sizeof(buffer),
|
||||
"Frequency: < %03ld.%03ldMHz >",
|
||||
model->real_frequency / 1000000 % 1000,
|
||||
model->real_frequency / 1000 % 1000);
|
||||
canvas_draw_str(canvas, 0, 8, buffer);
|
||||
canvas_draw_str(canvas, 0, 18, "Frequency Hopping: <OFF>");
|
||||
} else {
|
||||
canvas_draw_str(canvas, 0, 8, "Frequency: < --- >");
|
||||
canvas_draw_str(canvas, 0, 18, "Frequency Hopping: <ON>");
|
||||
}
|
||||
canvas_draw_str(canvas, 0, 28, "Modulation: <AM>");
|
||||
|
||||
elements_button_center(canvas, "Save");
|
||||
break;
|
||||
|
||||
case ReceiverSceneInfo:
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
elements_multiline_text(canvas, 0, 8, string_get_cstr(model->text));
|
||||
snprintf(
|
||||
buffer,
|
||||
sizeof(buffer),
|
||||
"%03ld.%03ld",
|
||||
subghz_history_get_frequency(model->history, model->idx) / 1000000 % 1000,
|
||||
subghz_history_get_frequency(model->history, model->idx) / 1000 % 1000);
|
||||
canvas_draw_str(canvas, 90, 8, buffer);
|
||||
if(model->protocol_result && model->protocol_result->to_save_string &&
|
||||
strcmp(model->protocol_result->name, "KeeLoq")) {
|
||||
elements_button_right(canvas, "Save");
|
||||
elements_button_center(canvas, "Send");
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
break;
|
||||
return;
|
||||
}
|
||||
canvas_draw_line(canvas, 46, 51, 125, 51);
|
||||
|
||||
bool scrollbar = model->history_item > 4;
|
||||
string_t str_buff;
|
||||
string_init(str_buff);
|
||||
|
||||
SubGhzReceiverMenuItem* item_menu;
|
||||
|
||||
for(size_t i = 0; i < MIN(model->history_item, MENU_ITEMS); ++i) {
|
||||
size_t idx = CLAMP(i + model->list_offset, model->history_item, 0);
|
||||
item_menu = SubGhzReceiverMenuItemArray_get(model->history->data, idx);
|
||||
string_set(str_buff, item_menu->item_str);
|
||||
elements_string_fit_width(canvas, str_buff, scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX);
|
||||
if(model->idx == idx) {
|
||||
subghz_receiver_draw_frame(canvas, i, scrollbar);
|
||||
} else {
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
}
|
||||
canvas_draw_icon(canvas, 1, 2 + i * FRAME_HEIGHT, ReceiverItemIcons[item_menu->type]);
|
||||
canvas_draw_str(canvas, 15, 9 + i * FRAME_HEIGHT, string_get_cstr(str_buff));
|
||||
string_clean(str_buff);
|
||||
}
|
||||
if(scrollbar) {
|
||||
elements_scrollbar_pos(canvas, 128, 0, 49, model->idx, model->history_item);
|
||||
}
|
||||
string_clear(str_buff);
|
||||
}
|
||||
|
||||
void subghz_receiver_history_full(void* context) {
|
||||
furi_assert(context);
|
||||
SubghzReceiver* subghz_receiver = context;
|
||||
subghz_receiver->callback(SubghzReceverEventSendHistoryFull, subghz_receiver->context);
|
||||
subghz_receiver->hopper_state = SubGhzHopperStateOFF;
|
||||
}
|
||||
|
||||
bool subghz_receiver_input(InputEvent* event, void* context) {
|
||||
furi_assert(context);
|
||||
|
||||
uint8_t scene = 0;
|
||||
SubghzReceiver* subghz_receiver = context;
|
||||
with_view_model(
|
||||
subghz_receiver->view, (SubghzReceiverModel * model) {
|
||||
scene = model->scene;
|
||||
return false;
|
||||
});
|
||||
|
||||
bool can_be_saved = false;
|
||||
|
||||
switch(scene) {
|
||||
case ReceiverSceneMain:
|
||||
if(event->key == InputKeyBack && event->type == InputTypeShort) {
|
||||
with_view_model(
|
||||
subghz_receiver->view, (SubghzReceiverModel * model) {
|
||||
model->idx = 0;
|
||||
model->list_offset = 0;
|
||||
model->history_item = 0;
|
||||
subghz_history_clean(model->history);
|
||||
return true;
|
||||
});
|
||||
return false;
|
||||
} else if(
|
||||
event->key == InputKeyUp &&
|
||||
(event->type == InputTypeShort || event->type == InputTypeRepeat)) {
|
||||
with_view_model(
|
||||
subghz_receiver->view, (SubghzReceiverModel * model) {
|
||||
if(model->idx != 0) model->idx--;
|
||||
return true;
|
||||
});
|
||||
} else if(
|
||||
event->key == InputKeyDown &&
|
||||
(event->type == InputTypeShort || event->type == InputTypeRepeat)) {
|
||||
with_view_model(
|
||||
subghz_receiver->view, (SubghzReceiverModel * model) {
|
||||
if(model->idx != subghz_history_get_item(model->history) - 1) model->idx++;
|
||||
return true;
|
||||
});
|
||||
} else if(event->key == InputKeyLeft && event->type == InputTypeShort) {
|
||||
subghz_receiver->hopper_state = SubGhzHopperStatePause;
|
||||
with_view_model(
|
||||
subghz_receiver->view, (SubghzReceiverModel * model) {
|
||||
model->scene = ReceiverSceneConfig;
|
||||
model->temp_frequency = model->frequency;
|
||||
return true;
|
||||
});
|
||||
subghz_receiver->callback(SubghzReceverEventConfig, subghz_receiver->context);
|
||||
} else if(event->key == InputKeyOk && event->type == InputTypeShort) {
|
||||
subghz_receiver->event_key_sequence = event->sequence;
|
||||
with_view_model(
|
||||
subghz_receiver->view, (SubghzReceiverModel * model) {
|
||||
string_clean(model->text);
|
||||
model->protocol_result = subghz_protocol_get_by_name(
|
||||
subghz_receiver->protocol,
|
||||
subghz_history_get_name(model->history, model->idx));
|
||||
if(model->protocol_result->to_load_protocol != NULL) {
|
||||
model->protocol_result->to_load_protocol(
|
||||
model->protocol_result,
|
||||
subghz_history_get_raw_data(model->history, model->idx));
|
||||
model->protocol_result->to_string(model->protocol_result, model->text);
|
||||
model->scene = ReceiverSceneInfo;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
case ReceiverSceneInfo:
|
||||
if(event->key == InputKeyBack && event->type == InputTypeShort) {
|
||||
subghz_receiver->callback(SubghzReceverEventBack, subghz_receiver->context);
|
||||
} else if(
|
||||
event->key == InputKeyUp &&
|
||||
(event->type == InputTypeShort || event->type == InputTypeRepeat)) {
|
||||
with_view_model(
|
||||
subghz_receiver->view, (SubghzReceiverModel * model) {
|
||||
can_be_saved =
|
||||
(model->protocol_result && model->protocol_result->to_save_string &&
|
||||
strcmp(model->protocol_result->name, "KeeLoq"));
|
||||
return false;
|
||||
if(model->idx != 0) model->idx--;
|
||||
return true;
|
||||
});
|
||||
if(event->key == InputKeyBack && event->type == InputTypeShort) {
|
||||
with_view_model(
|
||||
subghz_receiver->view, (SubghzReceiverModel * model) {
|
||||
subghz_rx_end(subghz_receiver->worker);
|
||||
model->real_frequency =
|
||||
subghz_rx(subghz_receiver->worker, subghz_frequencies[model->frequency]);
|
||||
subghz_receiver->hopper_state = SubGhzHopperStateRunnig;
|
||||
model->scene = ReceiverSceneMain;
|
||||
return true;
|
||||
});
|
||||
subghz_receiver->callback(SubghzReceverEventMain, subghz_receiver->context);
|
||||
} else if(can_be_saved && event->key == InputKeyRight) {
|
||||
subghz_receiver->callback(SubghzReceverEventSave, subghz_receiver->context);
|
||||
return false;
|
||||
} else if(
|
||||
can_be_saved && event->key == InputKeyOk && event->type == InputTypePress &&
|
||||
subghz_receiver->event_key_sequence != event->sequence) {
|
||||
subghz_receiver->hopper_state = SubGhzHopperStatePause;
|
||||
subghz_rx_end(subghz_receiver->worker);
|
||||
subghz_receiver->callback(SubghzReceverEventSendStart, subghz_receiver->context);
|
||||
return true;
|
||||
} else if(
|
||||
can_be_saved && event->key == InputKeyOk && event->type == InputTypeRelease &&
|
||||
subghz_receiver->event_key_sequence != event->sequence) {
|
||||
subghz_receiver->callback(SubghzReceverEventSendStop, subghz_receiver->context);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case ReceiverSceneConfig:
|
||||
if(event->type != InputTypeShort) return false;
|
||||
if(event->key == InputKeyBack) {
|
||||
with_view_model(
|
||||
subghz_receiver->view, (SubghzReceiverModel * model) {
|
||||
model->frequency = model->temp_frequency;
|
||||
model->real_frequency = subghz_frequencies[model->frequency];
|
||||
subghz_receiver->hopper_state = SubGhzHopperStateRunnig;
|
||||
if(subghz_history_get_item(model->history) == 0) {
|
||||
model->scene = ReceiverSceneStart;
|
||||
} else {
|
||||
model->scene = ReceiverSceneMain;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
subghz_receiver->callback(SubghzReceverEventMain, subghz_receiver->context);
|
||||
} else if(event->key == InputKeyOk) {
|
||||
with_view_model(
|
||||
subghz_receiver->view, (SubghzReceiverModel * model) {
|
||||
if(model->frequency < subghz_frequencies_count) {
|
||||
subghz_rx_end(subghz_receiver->worker);
|
||||
model->real_frequency = subghz_rx(
|
||||
subghz_receiver->worker, subghz_frequencies[model->frequency]);
|
||||
subghz_receiver->hopper_state = SubGhzHopperStateOFF;
|
||||
} else {
|
||||
osTimerStart(subghz_receiver->timer, 1024 / 10);
|
||||
subghz_receiver->hopper_state = SubGhzHopperStateRunnig;
|
||||
}
|
||||
if(subghz_history_get_item(model->history) == 0) {
|
||||
model->scene = ReceiverSceneStart;
|
||||
} else {
|
||||
model->scene = ReceiverSceneMain;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
subghz_receiver->callback(SubghzReceverEventMain, subghz_receiver->context);
|
||||
} else {
|
||||
with_view_model(
|
||||
subghz_receiver->view, (SubghzReceiverModel * model) {
|
||||
bool model_updated = false;
|
||||
|
||||
if(event->key == InputKeyLeft) {
|
||||
if(model->frequency > 0) model->frequency--;
|
||||
model_updated = true;
|
||||
} else if(event->key == InputKeyRight) {
|
||||
if(model->frequency < subghz_frequencies_count) model->frequency++;
|
||||
model_updated = true;
|
||||
}
|
||||
if(model_updated) {
|
||||
model->real_frequency = subghz_frequencies[model->frequency];
|
||||
}
|
||||
return model_updated;
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
case ReceiverSceneStart:
|
||||
if(event->type != InputTypeShort) return false;
|
||||
if(event->key == InputKeyBack) {
|
||||
return false;
|
||||
} else if(event->key == InputKeyLeft) {
|
||||
subghz_receiver->hopper_state = SubGhzHopperStatePause;
|
||||
with_view_model(
|
||||
subghz_receiver->view, (SubghzReceiverModel * model) {
|
||||
model->temp_frequency = model->frequency;
|
||||
model->scene = ReceiverSceneConfig;
|
||||
return true;
|
||||
});
|
||||
subghz_receiver->callback(SubghzReceverEventConfig, subghz_receiver->context);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
subghz_receiver_update_offset(subghz_receiver);
|
||||
if(scene != ReceiverSceneInfo) {
|
||||
} else if(
|
||||
event->key == InputKeyDown &&
|
||||
(event->type == InputTypeShort || event->type == InputTypeRepeat)) {
|
||||
with_view_model(
|
||||
subghz_receiver->view, (SubghzReceiverModel * model) {
|
||||
if(subghz_history_get_text_space_left(model->history, NULL)) {
|
||||
subghz_receiver_history_full(subghz_receiver);
|
||||
if(model->idx != model->history_item - 1) model->idx++;
|
||||
return true;
|
||||
});
|
||||
} else if(event->key == InputKeyLeft && event->type == InputTypeShort) {
|
||||
subghz_receiver->callback(SubghzReceverEventConfig, subghz_receiver->context);
|
||||
} else if(event->key == InputKeyOk && event->type == InputTypeShort) {
|
||||
with_view_model(
|
||||
subghz_receiver->view, (SubghzReceiverModel * model) {
|
||||
if(model->history_item != 0) {
|
||||
subghz_receiver->callback(SubghzReceverEventOK, subghz_receiver->context);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
subghz_receiver_update_offset(subghz_receiver);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void subghz_receiver_text_callback(string_t text, void* context) {
|
||||
furi_assert(context);
|
||||
SubghzReceiver* subghz_receiver = context;
|
||||
|
||||
with_view_model(
|
||||
subghz_receiver->view, (SubghzReceiverModel * model) {
|
||||
string_set(model->text, text);
|
||||
model->scene = ReceiverSceneMain;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
void subghz_receiver_protocol_callback(SubGhzProtocolCommon* parser, void* context) {
|
||||
furi_assert(context);
|
||||
SubghzReceiver* subghz_receiver = context;
|
||||
|
||||
with_view_model(
|
||||
subghz_receiver->view, (SubghzReceiverModel * model) {
|
||||
model->protocol_result = parser;
|
||||
subghz_history_set_frequency_preset(
|
||||
model->history,
|
||||
model->history_item,
|
||||
model->real_frequency,
|
||||
FuriHalSubGhzPresetOok650Async);
|
||||
subghz_history_add_to_history(model->history, parser);
|
||||
|
||||
model->history_item = subghz_history_get_item(model->history);
|
||||
model->scene = ReceiverSceneMain;
|
||||
if(subghz_history_get_text_space_left(model->history, NULL)) {
|
||||
subghz_receiver_history_full(subghz_receiver);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
subghz_protocol_reset(subghz_receiver->protocol);
|
||||
subghz_receiver_update_offset(subghz_receiver);
|
||||
}
|
||||
|
||||
static void subghz_receiver_timer_callback(void* context) {
|
||||
furi_assert(context);
|
||||
SubghzReceiver* subghz_receiver = context;
|
||||
|
||||
switch(subghz_receiver->hopper_state) {
|
||||
case SubGhzHopperStatePause:
|
||||
return;
|
||||
break;
|
||||
case SubGhzHopperStateOFF:
|
||||
osTimerStop(subghz_receiver->timer);
|
||||
return;
|
||||
break;
|
||||
case SubGhzHopperStateRSSITimeOut:
|
||||
if(subghz_receiver->hopper_timeout != 0) {
|
||||
subghz_receiver->hopper_timeout--;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
float rssi = -127.0f;
|
||||
with_view_model(
|
||||
subghz_receiver->view, (SubghzReceiverModel * model) {
|
||||
if(subghz_receiver->hopper_state != SubGhzHopperStateRSSITimeOut) {
|
||||
// See RSSI Calculation timings in CC1101 17.3 RSSI
|
||||
rssi = furi_hal_subghz_get_rssi();
|
||||
|
||||
// Stay if RSSI is high enough
|
||||
if(rssi > -90.0f) {
|
||||
subghz_receiver->hopper_timeout = 10;
|
||||
subghz_receiver->hopper_state = SubGhzHopperStateRSSITimeOut;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
subghz_receiver->hopper_state = SubGhzHopperStateRunnig;
|
||||
}
|
||||
|
||||
// Select next frequency
|
||||
if(model->frequency < COUNT_FREQUNCY_HOPPER - 1) {
|
||||
model->frequency++;
|
||||
} else {
|
||||
model->frequency = 0;
|
||||
}
|
||||
|
||||
// Restart radio
|
||||
furi_hal_subghz_idle();
|
||||
subghz_protocol_reset(subghz_receiver->protocol);
|
||||
model->real_frequency = furi_hal_subghz_set_frequency_and_path(
|
||||
subghz_frequencies_hopper[model->frequency]);
|
||||
furi_hal_subghz_rx();
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
void subghz_receiver_enter(void* context) {
|
||||
furi_assert(context);
|
||||
SubghzReceiver* subghz_receiver = context;
|
||||
//Start CC1101 Rx
|
||||
subghz_begin(FuriHalSubGhzPresetOok650Async);
|
||||
with_view_model(
|
||||
subghz_receiver->view, (SubghzReceiverModel * model) {
|
||||
subghz_rx_end(subghz_receiver->worker);
|
||||
model->frequency = subghz_frequencies_433_92;
|
||||
model->real_frequency =
|
||||
subghz_rx(subghz_receiver->worker, subghz_frequencies[model->frequency]);
|
||||
if(subghz_history_get_item(model->history) == 0) {
|
||||
model->scene = ReceiverSceneStart;
|
||||
} else {
|
||||
model->scene = ReceiverSceneMain;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
subghz_protocol_enable_dump(
|
||||
subghz_receiver->protocol, subghz_receiver_protocol_callback, subghz_receiver);
|
||||
//SubghzReceiver* subghz_receiver = context;
|
||||
}
|
||||
|
||||
void subghz_receiver_exit(void* context) {
|
||||
furi_assert(context);
|
||||
SubghzReceiver* subghz_receiver = context;
|
||||
osTimerStop(subghz_receiver->timer);
|
||||
with_view_model(
|
||||
subghz_receiver->view, (SubghzReceiverModel * model) {
|
||||
string_clean(model->text);
|
||||
return true;
|
||||
string_clean(model->frequency_str);
|
||||
string_clean(model->preset_str);
|
||||
string_clean(model->history_stat_str);
|
||||
for
|
||||
M_EACH(item_menu, model->history->data, SubGhzReceiverMenuItemArray_t) {
|
||||
string_clear(item_menu->item_str);
|
||||
item_menu->type = 0;
|
||||
}
|
||||
SubGhzReceiverMenuItemArray_clean(model->history->data);
|
||||
model->idx = 0;
|
||||
model->list_offset = 0;
|
||||
model->history_item = 0;
|
||||
return false;
|
||||
});
|
||||
// Stop CC1101 Rx
|
||||
subghz_rx_end(subghz_receiver->worker);
|
||||
subghz_sleep();
|
||||
}
|
||||
|
||||
SubghzReceiver* subghz_receiver_alloc() {
|
||||
@ -616,14 +249,14 @@ SubghzReceiver* subghz_receiver_alloc() {
|
||||
|
||||
with_view_model(
|
||||
subghz_receiver->view, (SubghzReceiverModel * model) {
|
||||
string_init(model->text);
|
||||
model->history = subghz_history_alloc();
|
||||
string_init(model->frequency_str);
|
||||
string_init(model->preset_str);
|
||||
string_init(model->history_stat_str);
|
||||
model->history = furi_alloc(sizeof(SubGhzReceiverHistory));
|
||||
SubGhzReceiverMenuItemArray_init(model->history->data);
|
||||
return true;
|
||||
});
|
||||
|
||||
subghz_receiver->timer =
|
||||
osTimerNew(subghz_receiver_timer_callback, osTimerPeriodic, subghz_receiver, NULL);
|
||||
subghz_receiver->hopper_state = SubGhzHopperStateOFF;
|
||||
return subghz_receiver;
|
||||
}
|
||||
|
||||
@ -632,11 +265,18 @@ void subghz_receiver_free(SubghzReceiver* subghz_receiver) {
|
||||
|
||||
with_view_model(
|
||||
subghz_receiver->view, (SubghzReceiverModel * model) {
|
||||
string_clear(model->text);
|
||||
subghz_history_free(model->history);
|
||||
return false;
|
||||
string_clear(model->frequency_str);
|
||||
string_clear(model->preset_str);
|
||||
string_clear(model->history_stat_str);
|
||||
for
|
||||
M_EACH(item_menu, model->history->data, SubGhzReceiverMenuItemArray_t) {
|
||||
string_clear(item_menu->item_str);
|
||||
item_menu->type = 0;
|
||||
}
|
||||
SubGhzReceiverMenuItemArray_clear(model->history->data);
|
||||
free(model->history);
|
||||
return false;
|
||||
});
|
||||
osTimerDelete(subghz_receiver->timer);
|
||||
view_free(subghz_receiver->view);
|
||||
free(subghz_receiver);
|
||||
}
|
||||
@ -646,43 +286,24 @@ View* subghz_receiver_get_view(SubghzReceiver* subghz_receiver) {
|
||||
return subghz_receiver->view;
|
||||
}
|
||||
|
||||
uint32_t subghz_receiver_get_frequency(SubghzReceiver* subghz_receiver) {
|
||||
uint16_t subghz_receiver_get_idx_menu(SubghzReceiver* subghz_receiver) {
|
||||
furi_assert(subghz_receiver);
|
||||
uint32_t frequency;
|
||||
uint32_t idx = 0;
|
||||
with_view_model(
|
||||
subghz_receiver->view, (SubghzReceiverModel * model) {
|
||||
frequency = subghz_history_get_frequency(model->history, model->idx);
|
||||
idx = model->idx;
|
||||
return false;
|
||||
});
|
||||
return frequency;
|
||||
return idx;
|
||||
}
|
||||
|
||||
FuriHalSubGhzPreset subghz_receiver_get_preset(SubghzReceiver* subghz_receiver) {
|
||||
void subghz_receiver_set_idx_menu(SubghzReceiver* subghz_receiver, uint16_t idx) {
|
||||
furi_assert(subghz_receiver);
|
||||
FuriHalSubGhzPreset preset;
|
||||
with_view_model(
|
||||
subghz_receiver->view, (SubghzReceiverModel * model) {
|
||||
preset = subghz_history_get_preset(model->history, model->idx);
|
||||
return false;
|
||||
model->idx = idx;
|
||||
if(model->idx > 2) model->list_offset = idx - 2;
|
||||
return true;
|
||||
});
|
||||
return preset;
|
||||
}
|
||||
|
||||
void subghz_receiver_frequency_preset_to_str(SubghzReceiver* subghz_receiver, string_t output) {
|
||||
furi_assert(subghz_receiver);
|
||||
uint32_t frequency;
|
||||
uint32_t preset;
|
||||
with_view_model(
|
||||
subghz_receiver->view, (SubghzReceiverModel * model) {
|
||||
frequency = subghz_history_get_frequency(model->history, model->idx);
|
||||
preset = (uint32_t)subghz_history_get_preset(model->history, model->idx);
|
||||
return false;
|
||||
});
|
||||
|
||||
string_cat_printf(
|
||||
output,
|
||||
"Frequency: %d\n"
|
||||
"Preset: %d\n",
|
||||
(int)frequency,
|
||||
(int)preset);
|
||||
}
|
||||
subghz_receiver_update_offset(subghz_receiver);
|
||||
}
|
||||
@ -1,21 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <gui/view.h>
|
||||
#include <lib/subghz/protocols/subghz_protocol_common.h>
|
||||
#include <lib/subghz/protocols/subghz_protocol.h>
|
||||
#include <lib/subghz/subghz_worker.h>
|
||||
#include "../subghz_history.h"
|
||||
|
||||
typedef enum {
|
||||
SubghzReceverEventOK,
|
||||
SubghzReceverEventConfig,
|
||||
SubghzReceverEventMain,
|
||||
SubghzReceverEventSave,
|
||||
SubghzReceverEventBack,
|
||||
SubghzReceverEventMore,
|
||||
SubghzReceverEventSendStart,
|
||||
SubghzReceverEventSendStop,
|
||||
SubghzReceverEventSendHistoryFull,
|
||||
} SubghzReceverEvent;
|
||||
|
||||
typedef struct SubghzReceiver SubghzReceiver;
|
||||
@ -33,17 +23,19 @@ void subghz_receiver_free(SubghzReceiver* subghz_receiver);
|
||||
|
||||
View* subghz_receiver_get_view(SubghzReceiver* subghz_receiver);
|
||||
|
||||
void subghz_receiver_set_protocol(
|
||||
void subghz_receiver_add_data_statusbar(
|
||||
SubghzReceiver* subghz_receiver,
|
||||
SubGhzProtocolCommon* protocol_result,
|
||||
SubGhzProtocol* protocol);
|
||||
const char* frequency_str,
|
||||
const char* preset_str,
|
||||
const char* history_stat_str);
|
||||
|
||||
SubGhzProtocolCommon* subghz_receiver_get_protocol(SubghzReceiver* subghz_receiver);
|
||||
void subghz_receiver_add_item_to_menu(
|
||||
SubghzReceiver* subghz_receiver,
|
||||
const char* name,
|
||||
uint8_t type);
|
||||
|
||||
void subghz_receiver_set_worker(SubghzReceiver* subghz_receiver, SubGhzWorker* worker);
|
||||
uint16_t subghz_receiver_get_idx_menu(SubghzReceiver* subghz_receiver);
|
||||
|
||||
uint32_t subghz_receiver_get_frequency(SubghzReceiver* subghz_receiver);
|
||||
void subghz_receiver_set_idx_menu(SubghzReceiver* subghz_receiver, uint16_t idx);
|
||||
|
||||
FuriHalSubGhzPreset subghz_receiver_get_preset(SubghzReceiver* subghz_receiver);
|
||||
|
||||
void subghz_receiver_frequency_preset_to_str(SubghzReceiver* subghz_receiver, string_t output);
|
||||
void subghz_receiver_exit(void* context);
|
||||
@ -1,13 +1,8 @@
|
||||
#include "subghz_transmitter.h"
|
||||
#include "../subghz_i.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <furi.h>
|
||||
#include <furi-hal.h>
|
||||
#include <input/input.h>
|
||||
#include <gui/elements.h>
|
||||
#include <notification/notification-messages.h>
|
||||
#include <lib/subghz/protocols/subghz_protocol_keeloq.h>
|
||||
|
||||
struct SubghzTransmitter {
|
||||
View* view;
|
||||
@ -16,11 +11,10 @@ struct SubghzTransmitter {
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
string_t text;
|
||||
uint16_t scene;
|
||||
uint32_t real_frequency;
|
||||
FuriHalSubGhzPreset preset;
|
||||
SubGhzProtocolCommon* protocol;
|
||||
string_t frequency_str;
|
||||
string_t preset_str;
|
||||
string_t key_str;
|
||||
uint8_t show_button;
|
||||
} SubghzTransmitterModel;
|
||||
|
||||
void subghz_transmitter_set_callback(
|
||||
@ -33,24 +27,19 @@ void subghz_transmitter_set_callback(
|
||||
subghz_transmitter->context = context;
|
||||
}
|
||||
|
||||
void subghz_transmitter_set_protocol(
|
||||
void subghz_transmitter_add_data_to_show(
|
||||
SubghzTransmitter* subghz_transmitter,
|
||||
SubGhzProtocolCommon* protocol) {
|
||||
const char* key_str,
|
||||
const char* frequency_str,
|
||||
const char* preset_str,
|
||||
uint8_t show_button) {
|
||||
furi_assert(subghz_transmitter);
|
||||
with_view_model(
|
||||
subghz_transmitter->view, (SubghzTransmitterModel * model) {
|
||||
model->protocol = protocol;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
void subghz_transmitter_set_frequency_preset(
|
||||
SubghzTransmitter* subghz_transmitter,
|
||||
uint32_t frequency,
|
||||
FuriHalSubGhzPreset preset) {
|
||||
with_view_model(
|
||||
subghz_transmitter->view, (SubghzTransmitterModel * model) {
|
||||
model->real_frequency = frequency;
|
||||
model->preset = preset;
|
||||
string_set(model->key_str, key_str);
|
||||
string_set(model->frequency_str, frequency_str);
|
||||
string_set(model->preset_str, preset_str);
|
||||
model->show_button = show_button;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
@ -87,26 +76,13 @@ static void subghz_transmitter_button_right(Canvas* canvas, const char* str) {
|
||||
}
|
||||
|
||||
void subghz_transmitter_draw(Canvas* canvas, SubghzTransmitterModel* model) {
|
||||
char buffer[64];
|
||||
canvas_clear(canvas);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
elements_multiline_text(canvas, 0, 8, string_get_cstr(model->text));
|
||||
snprintf(
|
||||
buffer,
|
||||
sizeof(buffer),
|
||||
"%03ld.%03ld",
|
||||
model->real_frequency / 1000000 % 1000,
|
||||
model->real_frequency / 1000 % 1000);
|
||||
canvas_draw_str(canvas, 90, 8, buffer);
|
||||
|
||||
if(model->protocol && model->protocol->get_upload_protocol) {
|
||||
if((!strcmp(model->protocol->name, "KeeLoq")) &&
|
||||
(!strcmp(subghz_protocol_keeloq_get_manufacture_name(model->protocol), "Unknown"))) {
|
||||
return;
|
||||
}
|
||||
subghz_transmitter_button_right(canvas, "Send");
|
||||
}
|
||||
elements_multiline_text(canvas, 0, 8, string_get_cstr(model->key_str));
|
||||
canvas_draw_str(canvas, 78, 8, string_get_cstr(model->frequency_str));
|
||||
canvas_draw_str(canvas, 113, 8, string_get_cstr(model->preset_str));
|
||||
if(model->show_button) subghz_transmitter_button_right(canvas, "Send");
|
||||
}
|
||||
|
||||
bool subghz_transmitter_input(InputEvent* event, void* context) {
|
||||
@ -114,26 +90,25 @@ bool subghz_transmitter_input(InputEvent* event, void* context) {
|
||||
SubghzTransmitter* subghz_transmitter = context;
|
||||
bool can_be_sent = false;
|
||||
|
||||
if(event->key == InputKeyBack) {
|
||||
if(event->key == InputKeyBack && event->type == InputTypeShort) {
|
||||
with_view_model(
|
||||
subghz_transmitter->view, (SubghzTransmitterModel * model) {
|
||||
string_clean(model->frequency_str);
|
||||
string_clean(model->preset_str);
|
||||
string_clean(model->key_str);
|
||||
model->show_button = 0;
|
||||
return false;
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
with_view_model(
|
||||
subghz_transmitter->view, (SubghzTransmitterModel * model) {
|
||||
if(model->protocol && model->protocol->get_upload_protocol) {
|
||||
if((!strcmp(model->protocol->name, "KeeLoq")) &&
|
||||
(!strcmp(
|
||||
subghz_protocol_keeloq_get_manufacture_name(model->protocol), "Unknown"))) {
|
||||
return false;
|
||||
}
|
||||
if(model->show_button) {
|
||||
can_be_sent = true;
|
||||
}
|
||||
//can_be_sent = (model->protocol && model->protocol->get_upload_protocol);
|
||||
string_clean(model->text);
|
||||
model->protocol->to_string(model->protocol, model->text);
|
||||
return true;
|
||||
});
|
||||
//if(event->type != InputTypeShort) return false;
|
||||
|
||||
if(can_be_sent && event->key == InputKeyOk && event->type == InputTypePress) {
|
||||
subghz_transmitter->callback(SubghzTransmitterEventSendStart, subghz_transmitter->context);
|
||||
@ -146,37 +121,14 @@ bool subghz_transmitter_input(InputEvent* event, void* context) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void subghz_transmitter_text_callback(string_t text, void* context) {
|
||||
furi_assert(context);
|
||||
SubghzTransmitter* subghz_transmitter = context;
|
||||
|
||||
with_view_model(
|
||||
subghz_transmitter->view, (SubghzTransmitterModel * model) {
|
||||
string_set(model->text, text);
|
||||
model->scene = 0;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
void subghz_transmitter_enter(void* context) {
|
||||
furi_assert(context);
|
||||
SubghzTransmitter* subghz_transmitter = context;
|
||||
with_view_model(
|
||||
subghz_transmitter->view, (SubghzTransmitterModel * model) {
|
||||
string_clean(model->text);
|
||||
model->protocol->to_string(model->protocol, model->text);
|
||||
return true;
|
||||
});
|
||||
// SubghzTransmitter* subghz_transmitter = context;
|
||||
}
|
||||
|
||||
void subghz_transmitter_exit(void* context) {
|
||||
furi_assert(context);
|
||||
SubghzTransmitter* subghz_transmitter = context;
|
||||
with_view_model(
|
||||
subghz_transmitter->view, (SubghzTransmitterModel * model) {
|
||||
string_clean(model->text);
|
||||
return true;
|
||||
});
|
||||
// SubghzTransmitter* subghz_transmitter = context;
|
||||
}
|
||||
|
||||
SubghzTransmitter* subghz_transmitter_alloc() {
|
||||
@ -194,7 +146,9 @@ SubghzTransmitter* subghz_transmitter_alloc() {
|
||||
|
||||
with_view_model(
|
||||
subghz_transmitter->view, (SubghzTransmitterModel * model) {
|
||||
string_init(model->text);
|
||||
string_init(model->frequency_str);
|
||||
string_init(model->preset_str);
|
||||
string_init(model->key_str);
|
||||
return true;
|
||||
});
|
||||
return subghz_transmitter;
|
||||
@ -205,7 +159,9 @@ void subghz_transmitter_free(SubghzTransmitter* subghz_transmitter) {
|
||||
|
||||
with_view_model(
|
||||
subghz_transmitter->view, (SubghzTransmitterModel * model) {
|
||||
string_clear(model->text);
|
||||
string_clear(model->frequency_str);
|
||||
string_clear(model->preset_str);
|
||||
string_clear(model->key_str);
|
||||
return true;
|
||||
});
|
||||
view_free(subghz_transmitter->view);
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <gui/view.h>
|
||||
#include <lib/subghz/protocols/subghz_protocol_common.h>
|
||||
|
||||
typedef enum {
|
||||
SubghzTransmitterEventSendStart,
|
||||
@ -25,10 +24,9 @@ void subghz_transmitter_free(SubghzTransmitter* subghz_transmitter);
|
||||
|
||||
View* subghz_transmitter_get_view(SubghzTransmitter* subghz_transmitter);
|
||||
|
||||
void subghz_transmitter_set_protocol(
|
||||
void subghz_transmitter_add_data_to_show(
|
||||
SubghzTransmitter* subghz_transmitter,
|
||||
SubGhzProtocolCommon* protocol);
|
||||
void subghz_transmitter_set_frequency_preset(
|
||||
SubghzTransmitter* subghz_transmitter,
|
||||
uint32_t frequency,
|
||||
FuriHalSubGhzPreset preset);
|
||||
const char* key_str,
|
||||
const char* frequency_str,
|
||||
const char* preset_str,
|
||||
uint8_t show_button);
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
#include "test_data/irda_samsung_test_data.srcdata"
|
||||
#include "test_data/irda_rc6_test_data.srcdata"
|
||||
#include "test_data/irda_rc5_test_data.srcdata"
|
||||
#include "test_data/irda_sirc_test_data.srcdata"
|
||||
|
||||
#define RUN_ENCODER(data, expected) \
|
||||
run_encoder((data), COUNT_OF(data), (expected), COUNT_OF(expected))
|
||||
@ -14,6 +15,8 @@
|
||||
#define RUN_DECODER(data, expected) \
|
||||
run_decoder((data), COUNT_OF(data), (expected), COUNT_OF(expected))
|
||||
|
||||
#define RUN_ENCODER_DECODER(data) run_encoder_decoder((data), COUNT_OF(data))
|
||||
|
||||
static IrdaDecoderHandler* decoder_handler;
|
||||
static IrdaEncoderHandler* encoder_handler;
|
||||
|
||||
@ -33,27 +36,44 @@ static void compare_message_results(
|
||||
mu_check(message_decoded->protocol == message_expected->protocol);
|
||||
mu_check(message_decoded->command == message_expected->command);
|
||||
mu_check(message_decoded->address == message_expected->address);
|
||||
mu_check(message_decoded->repeat == message_expected->repeat);
|
||||
if((message_expected->protocol == IrdaProtocolSIRC) ||
|
||||
(message_expected->protocol == IrdaProtocolSIRC15) ||
|
||||
(message_expected->protocol == IrdaProtocolSIRC20)) {
|
||||
mu_check(message_decoded->repeat == false);
|
||||
} else {
|
||||
mu_check(message_decoded->repeat == message_expected->repeat);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
run_encoder_fill_array(IrdaEncoderHandler* handler, uint32_t* timings, uint32_t* timings_len) {
|
||||
/* Encodes signal and merges same levels (high+high, low+low) */
|
||||
static void run_encoder_fill_array(
|
||||
IrdaEncoderHandler* handler,
|
||||
uint32_t* timings,
|
||||
uint32_t* timings_len,
|
||||
bool* start_level) {
|
||||
uint32_t duration = 0;
|
||||
bool level = false; // start from space
|
||||
bool level = false;
|
||||
bool level_read;
|
||||
IrdaStatus status = IrdaStatusError;
|
||||
int i = 0;
|
||||
bool first = true;
|
||||
|
||||
while(1) {
|
||||
status = irda_encode(handler, &duration, &level_read);
|
||||
if(level_read != level) {
|
||||
level = level_read;
|
||||
if(first) {
|
||||
if(start_level) *start_level = level_read;
|
||||
first = false;
|
||||
timings[0] = 0;
|
||||
} else if(level_read != level) {
|
||||
++i;
|
||||
furi_assert(i < *timings_len);
|
||||
timings[i] = 0;
|
||||
}
|
||||
level = level_read;
|
||||
timings[i] += duration;
|
||||
|
||||
furi_assert((status == IrdaStatusOk) || (status == IrdaStatusDone));
|
||||
if(status == IrdaStatusDone) break;
|
||||
furi_assert(i < *timings_len);
|
||||
}
|
||||
|
||||
*timings_len = i + 1;
|
||||
@ -66,8 +86,9 @@ static void run_encoder(
|
||||
const uint32_t expected_timings[],
|
||||
uint32_t expected_timings_len) {
|
||||
uint32_t* timings = 0;
|
||||
uint32_t timings_len = 0;
|
||||
uint32_t timings_len = 200;
|
||||
uint32_t j = 0;
|
||||
timings = furi_alloc(sizeof(uint32_t) * timings_len);
|
||||
|
||||
for(uint32_t message_counter = 0; message_counter < input_messages_len; ++message_counter) {
|
||||
const IrdaMessage* message = &input_messages[message_counter];
|
||||
@ -76,44 +97,51 @@ static void run_encoder(
|
||||
}
|
||||
|
||||
timings_len = 200;
|
||||
timings = furi_alloc(sizeof(uint32_t) * timings_len);
|
||||
run_encoder_fill_array(encoder_handler, timings, &timings_len);
|
||||
run_encoder_fill_array(encoder_handler, timings, &timings_len, NULL);
|
||||
furi_assert(timings_len <= 200);
|
||||
|
||||
for(int i = 0; i < timings_len; ++i, ++j) {
|
||||
mu_check(MATCH_BIT_TIMING(timings[i], expected_timings[j], 120));
|
||||
mu_check(MATCH_TIMING(timings[i], expected_timings[j], 120));
|
||||
mu_assert(j < expected_timings_len, "encoded more timings than expected");
|
||||
}
|
||||
|
||||
free(timings);
|
||||
}
|
||||
free(timings);
|
||||
mu_assert(j == expected_timings_len, "encoded less timings than expected");
|
||||
}
|
||||
|
||||
static void run_encoder_decoder(const IrdaMessage input_messages[], uint32_t input_messages_len) {
|
||||
uint32_t* timings = 0;
|
||||
uint32_t timings_len = 0;
|
||||
uint32_t timings_len = 200;
|
||||
bool level = false;
|
||||
timings = furi_alloc(sizeof(uint32_t) * timings_len);
|
||||
|
||||
for(uint32_t message_counter = 0; message_counter < input_messages_len; ++message_counter) {
|
||||
const IrdaMessage* message_encoded = &input_messages[message_counter];
|
||||
if(!message_encoded->repeat) {
|
||||
irda_reset_encoder(encoder_handler, message_encoded);
|
||||
level = false;
|
||||
}
|
||||
|
||||
timings_len = 200;
|
||||
timings = furi_alloc(sizeof(uint32_t) * timings_len);
|
||||
run_encoder_fill_array(encoder_handler, timings, &timings_len);
|
||||
run_encoder_fill_array(encoder_handler, timings, &timings_len, &level);
|
||||
furi_assert(timings_len <= 200);
|
||||
|
||||
const IrdaMessage* message_decoded = 0;
|
||||
for(int i = 0; i < timings_len; ++i) {
|
||||
message_decoded = irda_decode(decoder_handler, level, timings[i]);
|
||||
if(i < timings_len - 1)
|
||||
if((i == timings_len - 2) && level && message_decoded) {
|
||||
/* In case we end with space timing - message can be decoded at last mark.
|
||||
* Exception - SIRC protocol, which has variable message length (12/15/20),
|
||||
* and decoder recognizes protocol by silence time before next message
|
||||
* or by timeout (irda_check_decoder_ready()). */
|
||||
break;
|
||||
} else if(i < timings_len - 1) {
|
||||
mu_check(!message_decoded);
|
||||
else
|
||||
} else {
|
||||
if(!message_decoded) {
|
||||
message_decoded = irda_check_decoder_ready(decoder_handler);
|
||||
}
|
||||
mu_check(message_decoded);
|
||||
}
|
||||
level = !level;
|
||||
}
|
||||
if(message_decoded) {
|
||||
@ -121,9 +149,8 @@ static void run_encoder_decoder(const IrdaMessage input_messages[], uint32_t inp
|
||||
} else {
|
||||
mu_check(0);
|
||||
}
|
||||
|
||||
free(timings);
|
||||
}
|
||||
free(timings);
|
||||
}
|
||||
|
||||
static void run_decoder(
|
||||
@ -131,21 +158,49 @@ static void run_decoder(
|
||||
uint32_t input_delays_len,
|
||||
const IrdaMessage* message_expected,
|
||||
uint32_t message_expected_len) {
|
||||
const IrdaMessage* message_decoded = 0;
|
||||
IrdaMessage message_decoded_check_local;
|
||||
bool level = 0;
|
||||
uint32_t message_counter = 0;
|
||||
const IrdaMessage* message_decoded = 0;
|
||||
|
||||
for(uint32_t i = 0; i < input_delays_len; ++i) {
|
||||
const IrdaMessage* message_decoded_check = 0;
|
||||
|
||||
if(input_delays[i] > IRDA_RAW_RX_TIMING_DELAY_US) {
|
||||
message_decoded_check = irda_check_decoder_ready(decoder_handler);
|
||||
if(message_decoded_check) {
|
||||
/* irda_decode() can reset message, but we have to call irda_decode() to perform real
|
||||
* simulation: irda_check() by timeout, then irda_decode() when meet edge */
|
||||
message_decoded_check_local = *message_decoded_check;
|
||||
message_decoded_check = &message_decoded_check_local;
|
||||
}
|
||||
}
|
||||
|
||||
message_decoded = irda_decode(decoder_handler, level, input_delays[i]);
|
||||
if(message_decoded) {
|
||||
|
||||
if(message_decoded_check || message_decoded) {
|
||||
mu_assert(
|
||||
!(message_decoded_check && message_decoded),
|
||||
"both messages decoded: check_ready() and irda_decode()");
|
||||
|
||||
if(message_decoded_check) {
|
||||
message_decoded = message_decoded_check;
|
||||
}
|
||||
|
||||
mu_assert(message_counter < message_expected_len, "decoded more than expected");
|
||||
if(message_counter >= message_expected_len) break;
|
||||
compare_message_results(message_decoded, &message_expected[message_counter]);
|
||||
|
||||
++message_counter;
|
||||
}
|
||||
level = !level;
|
||||
}
|
||||
|
||||
message_decoded = irda_check_decoder_ready(decoder_handler);
|
||||
if(message_decoded) {
|
||||
compare_message_results(message_decoded, &message_expected[message_counter]);
|
||||
++message_counter;
|
||||
}
|
||||
|
||||
mu_assert(message_counter == message_expected_len, "decoded less than expected");
|
||||
}
|
||||
|
||||
@ -155,6 +210,7 @@ MU_TEST(test_decoder_samsung32) {
|
||||
|
||||
MU_TEST(test_mix) {
|
||||
RUN_DECODER(test_decoder_rc5_input2, test_decoder_rc5_expected2);
|
||||
RUN_DECODER(test_decoder_sirc_input1, test_decoder_sirc_expected1);
|
||||
RUN_DECODER(test_decoder_necext_input1, test_decoder_necext_expected1);
|
||||
// can use encoder data for decoding, but can't do opposite
|
||||
RUN_DECODER(test_encoder_rc6_expected1, test_encoder_rc6_input1);
|
||||
@ -162,12 +218,16 @@ MU_TEST(test_mix) {
|
||||
RUN_DECODER(test_decoder_rc6_input1, test_decoder_rc6_expected1);
|
||||
RUN_DECODER(test_decoder_samsung32_input1, test_decoder_samsung32_expected1);
|
||||
RUN_DECODER(test_decoder_rc5_input1, test_decoder_rc5_expected1);
|
||||
RUN_DECODER(test_decoder_sirc_input2, test_decoder_sirc_expected2);
|
||||
RUN_DECODER(test_decoder_necext_input1, test_decoder_necext_expected1);
|
||||
RUN_DECODER(test_decoder_sirc_input4, test_decoder_sirc_expected4);
|
||||
RUN_DECODER(test_decoder_nec_input2, test_decoder_nec_expected2);
|
||||
RUN_DECODER(test_decoder_rc6_input1, test_decoder_rc6_expected1);
|
||||
RUN_DECODER(test_decoder_necext_input1, test_decoder_necext_expected1);
|
||||
RUN_DECODER(test_decoder_sirc_input5, test_decoder_sirc_expected5);
|
||||
RUN_DECODER(test_decoder_rc5_input5, test_decoder_rc5_expected5);
|
||||
RUN_DECODER(test_decoder_samsung32_input1, test_decoder_samsung32_expected1);
|
||||
RUN_DECODER(test_decoder_sirc_input3, test_decoder_sirc_expected3);
|
||||
}
|
||||
|
||||
MU_TEST(test_decoder_nec1) {
|
||||
@ -191,6 +251,20 @@ MU_TEST(test_decoder_necext1) {
|
||||
RUN_DECODER(test_decoder_necext_input1, test_decoder_necext_expected1);
|
||||
}
|
||||
|
||||
MU_TEST(test_encoder_sirc) {
|
||||
RUN_ENCODER(test_encoder_sirc_input1, test_encoder_sirc_expected1);
|
||||
RUN_ENCODER(test_encoder_sirc_input2, test_encoder_sirc_expected2);
|
||||
}
|
||||
|
||||
MU_TEST(test_decoder_sirc) {
|
||||
RUN_DECODER(test_decoder_sirc_input3, test_decoder_sirc_expected3);
|
||||
RUN_DECODER(test_decoder_sirc_input1, test_decoder_sirc_expected1);
|
||||
RUN_DECODER(test_decoder_sirc_input2, test_decoder_sirc_expected2);
|
||||
RUN_DECODER(test_decoder_sirc_input4, test_decoder_sirc_expected4);
|
||||
RUN_DECODER(test_decoder_sirc_input5, test_decoder_sirc_expected5);
|
||||
RUN_ENCODER_DECODER(test_sirc);
|
||||
}
|
||||
|
||||
MU_TEST(test_decoder_rc5) {
|
||||
RUN_DECODER(test_decoder_rc5x_input1, test_decoder_rc5x_expected1);
|
||||
RUN_DECODER(test_decoder_rc5_input1, test_decoder_rc5_expected1);
|
||||
@ -219,16 +293,19 @@ MU_TEST(test_encoder_rc6) {
|
||||
}
|
||||
|
||||
MU_TEST(test_encoder_decoder_all) {
|
||||
run_encoder_decoder(test_nec_all, COUNT_OF(test_nec_all));
|
||||
run_encoder_decoder(test_necext_all, COUNT_OF(test_necext_all));
|
||||
run_encoder_decoder(test_samsung32_all, COUNT_OF(test_samsung32_all));
|
||||
run_encoder_decoder(test_rc6_all, COUNT_OF(test_rc6_all));
|
||||
run_encoder_decoder(test_rc5_all, COUNT_OF(test_rc5_all));
|
||||
RUN_ENCODER_DECODER(test_nec);
|
||||
RUN_ENCODER_DECODER(test_necext);
|
||||
RUN_ENCODER_DECODER(test_samsung32);
|
||||
RUN_ENCODER_DECODER(test_rc6);
|
||||
RUN_ENCODER_DECODER(test_rc5);
|
||||
RUN_ENCODER_DECODER(test_sirc);
|
||||
}
|
||||
|
||||
MU_TEST_SUITE(test_irda_decoder_encoder) {
|
||||
MU_SUITE_CONFIGURE(&test_setup, &test_teardown);
|
||||
|
||||
MU_RUN_TEST(test_encoder_sirc);
|
||||
MU_RUN_TEST(test_decoder_sirc);
|
||||
MU_RUN_TEST(test_encoder_rc5x);
|
||||
MU_RUN_TEST(test_encoder_rc5);
|
||||
MU_RUN_TEST(test_decoder_rc5);
|
||||
|
||||
@ -178,7 +178,7 @@ const IrdaMessage test_decoder_nec_expected2[] = {
|
||||
{IrdaProtocolNEC, 0x00, 0x0A, true},
|
||||
};
|
||||
|
||||
const IrdaMessage test_nec_all[] = {
|
||||
const IrdaMessage test_nec[] = {
|
||||
{IrdaProtocolNEC, 0x00, 0x00, false},
|
||||
{IrdaProtocolNEC, 0x01, 0x00, false},
|
||||
{IrdaProtocolNEC, 0x01, 0x80, false},
|
||||
|
||||
@ -223,7 +223,7 @@ const IrdaMessage test_decoder_necext_expected1[] = {
|
||||
|
||||
|
||||
|
||||
const IrdaMessage test_necext_all[] = {
|
||||
const IrdaMessage test_necext[] = {
|
||||
{IrdaProtocolNECext, 0x0000, 0x00, false},
|
||||
{IrdaProtocolNECext, 0x0001, 0x00, false},
|
||||
{IrdaProtocolNECext, 0x0001, 0x80, false},
|
||||
|
||||
@ -128,7 +128,7 @@ const IrdaMessage test_decoder_rc5_expected_all_repeats[] = {
|
||||
};
|
||||
|
||||
|
||||
const IrdaMessage test_rc5_all[] = {
|
||||
const IrdaMessage test_rc5[] = {
|
||||
{IrdaProtocolRC5, 0x1F, 0x3F, false},
|
||||
{IrdaProtocolRC5, 0x00, 0x00, false},
|
||||
{IrdaProtocolRC5, 0x10, 0x01, false},
|
||||
|
||||
@ -65,7 +65,7 @@ const uint32_t test_encoder_rc6_expected1[] = {
|
||||
};
|
||||
|
||||
|
||||
const IrdaMessage test_rc6_all[] = {
|
||||
const IrdaMessage test_rc6[] = {
|
||||
{IrdaProtocolRC6, 0x00, 0x00, false}, // t 0
|
||||
{IrdaProtocolRC6, 0x80, 0x00, false}, // t 1
|
||||
{IrdaProtocolRC6, 0x80, 0x01, false}, // t 0
|
||||
|
||||
@ -221,7 +221,7 @@ const IrdaMessage test_decoder_samsung32_expected1[] = {
|
||||
{IrdaProtocolSamsung32, 0x0E, 0x01, false}, {IrdaProtocolSamsung32, 0x0E, 0x01, true},
|
||||
};
|
||||
|
||||
const IrdaMessage test_samsung32_all[] = {
|
||||
const IrdaMessage test_samsung32[] = {
|
||||
{IrdaProtocolSamsung32, 0x00, 0x00, false},
|
||||
{IrdaProtocolSamsung32, 0x01, 0x00, false},
|
||||
{IrdaProtocolSamsung32, 0x01, 0x80, false},
|
||||
|
||||
@ -0,0 +1,485 @@
|
||||
const uint32_t test_decoder_sirc_input1[] = { /* 121 timings */
|
||||
1000000, 2420, 608, 1194, 608, 596, 604, 1198, 603, 591, 610, 1192, 609, 596, 605, 599, 601, 593, 607, 597, 604, 590, 610, 594, 606, 1196,
|
||||
25957, 2426, 603, 1199, 603, 591, 610, 1192, 610, 594, 606, 1196, 606, 599, 603, 591, 609, 595, 606, 598, 602, 592, 609, 596, 605, 1197,
|
||||
25960, 2423, 606, 1196, 606, 599, 602, 1200, 602, 592, 609, 1193, 609, 596, 606, 599, 602, 592, 609, 595, 605, 600, 601, 593, 608, 1194,
|
||||
1000000, 2422, 607, 1195, 607, 598, 603, 1199, 604, 590, 610, 1192, 610, 594, 606, 598, 603, 591, 609, 595, 605, 600, 601, 593, 607, 1195,
|
||||
25955, 2418, 610, 1192, 610, 594, 606, 1196, 606, 599, 602, 1200, 602, 592, 608, 596, 604, 590, 611, 594, 607, 597, 603, 591, 609, 1193,
|
||||
25959, 2424, 604, 1198, 604, 590, 610, 1192, 610, 594, 606, 1196, 605, 600, 601, 593, 608, 597, 603, 591, 610, 595, 606, 598, 602, 1200,
|
||||
1000000, 2424, 605, 599, 601, 593, 607, 597, 603, 591, 610, 594, 606, 1196, 606, 1196, 605, 600, 601, 593, 608, 597, 604, 590, 611, 1191,
|
||||
26586, 2425, 604, 590, 611, 593, 607, 598, 603, 591, 610, 595, 606, 1196, 606, 1196, 606, 599, 602, 592, 608, 596, 604, 590, 611, 1191,
|
||||
26586, 2424, 604, 590, 611, 593, 607, 598, 603, 591, 609, 595, 605, 1197, 605, 1197, 604, 590, 611, 593, 607, 597, 603, 591, 610, 1192,
|
||||
1000000, 2424, 604, 1198, 604, 590, 611, 1191, 610, 594, 606, 598, 603, 1199, 602, 1200, 602, 592, 609, 595, 605, 600, 601, 593, 608, 1194,
|
||||
25386, 2419, 610, 1192, 610, 594, 606, 1196, 607, 597, 603, 591, 610, 1192, 609, 1193, 610, 594, 606, 598, 602, 592, 609, 595, 605, 1197,
|
||||
25385, 2421, 608, 1194, 608, 596, 605, 1197, 605, 599, 601, 593, 608, 1194, 608, 1194, 608, 596, 605, 589, 611, 594, 607, 597, 604, 1198,
|
||||
1000000, 2426, 603, 1199, 602, 1200, 602, 1200, 602, 592, 608, 1194, 608, 596, 604, 590, 611, 594, 607, 597, 603, 591, 610, 594, 606, 1196, 605, 600, 601, 593, 608, 596, 604, 590, 610, 594, 607, 1195, 606, 598, 603, 591,
|
||||
15078, 2419, 610, 1192, 610, 1192, 610, 1192, 610, 594, 606, 1196, 605, 600, 601, 593, 608, 597, 604, 590, 610, 595, 606, 598, 602, 1200, 602, 592, 608, 597, 604, 590, 611, 594, 607, 597, 603, 1199, 603, 591, 609, 595,
|
||||
15075, 2422, 607, 1195, 607, 1195, 607, 1195, 607, 597, 604, 1198, 603, 591, 610, 594, 606, 598, 603, 591, 609, 595, 605, 600, 601, 1191, 611, 594, 607, 597, 603, 591, 610, 594, 606, 598, 602, 1200, 602, 592, 608, 596,
|
||||
1000000, 2422, 607, 1195, 606, 599, 602, 592, 608, 596, 604, 590, 610, 1192, 610, 594, 606, 599, 602, 592, 608, 596, 604, 590, 610, 1192,
|
||||
26585, 2426, 602, 1200, 602, 592, 608, 596, 604, 590, 611, 594, 607, 1195, 607, 598, 603, 591, 610, 594, 606, 598, 603, 591, 609, 1193,
|
||||
26586, 2425, 604, 1198, 603, 591, 610, 594, 606, 598, 602, 592, 609, 1193, 608, 597, 605, 600, 601, 593, 607, 597, 604, 590, 610, 1192,
|
||||
1000000, 2418, 610, 594, 606, 598, 603, 1199, 603, 1199, 603, 1199, 603, 1199, 603, 1199, 603, 591, 610, 1192, 610, 594, 606, 1196, 606, 1196, 606, 1196, 606, 598, 602, 592, 609, 1193, 609, 1193, 609, 1193, 609, 595, 605, 599,
|
||||
11557, 2418, 611, 594, 607, 598, 603, 1199, 603, 1199, 603, 1199, 602, 1200, 602, 1200, 601, 593, 608, 1194, 607, 597, 604, 1198, 603, 1199, 603, 1199, 602, 592, 608, 596, 604, 1198, 603, 1199, 603, 1199, 603, 591, 609, 595,
|
||||
11561, 2424, 604, 590, 610, 594, 607, 1195, 606, 1196, 606, 1196, 606, 1196, 606, 1196, 605, 600, 601, 1191, 611, 594, 607, 1195, 607, 1195, 607, 1195, 607, 597, 603, 591, 610, 1192, 610, 1192, 610, 1192, 610, 594, 606, 598,
|
||||
1000000, 2424, 604, 590, 611, 594, 607, 1195, 607, 1195, 607, 1195, 607, 1195, 607, 1195, 606, 598, 603, 1199, 602, 592, 609, 1193, 608, 1194, 608, 1194, 608, 596, 604, 590, 611, 1191, 611, 1191, 611, 1191, 611, 594, 607, 598,
|
||||
11559, 2427, 602, 592, 608, 596, 605, 1197, 604, 1198, 604, 1198, 604, 1198, 604, 1198, 604, 590, 610, 1192, 610, 595, 606, 1196, 606, 1196, 606, 1196, 606, 599, 603, 591, 609, 1193, 609, 1193, 609, 1193, 608, 597, 605, 589,
|
||||
11567, 2418, 610, 595, 607, 597, 603, 1199, 603, 1199, 602, 1200, 602, 1200, 601, 1201, 601, 593, 608, 1194, 607, 598, 603, 1199, 603, 1199, 603, 1199, 603, 591, 609, 595, 605, 1197, 605, 1197, 605, 1197, 604, 590, 611, 594,
|
||||
1000000, 2421, 608, 597, 604, 590, 610, 1192, 610, 1192, 609, 1193, 609, 1193, 609, 1193, 608, 596, 605, 1197, 604, 590, 610, 1192, 611, 1191, 610, 1192, 610, 594, 606, 598, 603, 1199, 603, 1199, 602, 1200, 602, 592, 608, 596,
|
||||
11561, 2424, 604, 590, 610, 594, 606, 1196, 606, 1196, 606, 1196, 606, 1196, 605, 1197, 605, 600, 601, 1201, 601, 593, 607, 1195, 607, 1195, 606, 1196, 606, 598, 602, 592, 608, 1194, 608, 1194, 607, 1195, 607, 597, 603, 591,
|
||||
11564, 2421, 607, 597, 604, 590, 610, 1192, 610, 1192, 610, 1192, 610, 1192, 610, 1192, 609, 595, 606, 1196, 606, 598, 602, 1200, 601, 1201, 601, 1201, 601, 593, 607, 598, 603, 1199, 603, 1199, 602, 1200, 602, 592, 608, 596,
|
||||
1000000, 2420, 609, 595, 606, 598, 602, 1200, 602, 1200, 602, 1200, 602, 1200, 602, 1200, 602, 592, 608, 1194, 608, 596, 604, 1198, 603, 1199, 603, 1199, 603, 591, 610, 594, 606, 1196, 606, 1196, 606, 1196, 606, 598, 602, 592,
|
||||
11565, 2420, 609, 595, 605, 600, 601, 1201, 601, 1201, 601, 1201, 601, 1201, 601, 1201, 601, 593, 607, 1195, 607, 597, 603, 1199, 603, 1199, 603, 1199, 603, 591, 609, 595, 605, 1197, 605, 1197, 605, 1197, 605, 599, 601, 593,
|
||||
11563, 2422, 607, 597, 603, 591, 609, 1193, 609, 1193, 608, 1194, 608, 1194, 608, 1194, 582, 623, 603, 1199, 603, 591, 610, 1202, 599, 1203, 599, 1203, 599, 595, 581, 623, 577, 1225, 601, 1201, 601, 1201, 601, 593, 582, 623,
|
||||
1000000, 2425, 602, 1200, 602, 1200, 602, 592, 608, 1194, 608, 1194, 607, 1195, 607, 1195, 607, 597, 603, 1199, 602, 592, 609, 1193, 608, 1194, 608, 1194, 607, 597, 603, 601, 575, 1227, 599, 1203, 599, 1203, 573, 621, 580, 625,
|
||||
10931, 2426, 578, 1224, 578, 1224, 578, 616, 585, 1217, 585, 1217, 585, 1217, 585, 1217, 585, 620, 580, 1222, 580, 624, 577, 1225, 577, 1225, 577, 1225, 577, 617, 583, 622, 580, 1222, 580, 1222, 579, 1223, 579, 625, 576, 618,
|
||||
10936, 2421, 583, 1219, 583, 1219, 582, 622, 579, 1223, 578, 1224, 578, 1224, 578, 1224, 578, 616, 584, 1218, 584, 621, 580, 1222, 579, 1223, 579, 1223, 579, 625, 576, 618, 583, 1219, 582, 1220, 582, 1220, 582, 622, 578, 616,
|
||||
1000000, 2419, 584, 620, 580, 1222, 580, 624, 576, 1226, 576, 1226, 576, 1226, 576, 1226, 576, 618, 582, 1220, 582, 622, 579, 1223, 578, 1224, 579, 1223, 578, 616, 585, 619, 581, 1221, 581, 1221, 581, 1221, 580, 624, 576, 618,
|
||||
11563, 2422, 582, 622, 579, 1223, 578, 616, 585, 1217, 585, 1217, 584, 1218, 584, 1218, 583, 622, 579, 1223, 579, 625, 575, 1216, 585, 1217, 585, 1217, 585, 619, 581, 623, 577, 1225, 577, 1225, 577, 1225, 576, 618, 583, 621,
|
||||
11558, 2427, 577, 617, 584, 1218, 583, 621, 579, 1223, 579, 1223, 578, 1224, 578, 1224, 578, 647, 553, 1249, 553, 651, 549, 1253, 548, 1254, 549, 1253, 549, 645, 555, 649, 551, 1251, 551, 1251, 551, 1251, 550, 654, 546, 648,
|
||||
1000000, 2456, 548, 646, 554, 650, 551, 653, 547, 1255, 547, 1255, 547, 1255, 547, 1255, 547, 647, 554, 1248, 554, 650, 551, 1251, 551, 1251, 551, 1251, 551, 653, 547, 647, 554, 1248, 554, 1248, 554, 1248, 553, 651, 550, 644,
|
||||
12112, 2449, 555, 649, 551, 654, 547, 647, 554, 1248, 554, 1248, 553, 1249, 553, 1249, 553, 651, 549, 1253, 549, 645, 555, 1247, 555, 1247, 555, 1247, 554, 650, 550, 655, 547, 1244, 557, 1224, 578, 1224, 579, 615, 585, 619,
|
||||
12139, 2423, 580, 624, 576, 618, 582, 622, 578, 1224, 578, 1224, 578, 1224, 578, 1224, 578, 616, 584, 1218, 584, 620, 580, 1222, 580, 1222, 581, 1221, 580, 624, 577, 617, 584, 1218, 584, 1218, 584, 1218, 584, 620, 581, 623,
|
||||
1000000, 2422, 582, 1220, 581, 623, 578, 616, 584, 1218, 584, 1218, 584, 1218, 584, 1218, 584, 620, 580, 1222, 580, 624, 576, 1226, 577, 1225, 576, 1226, 576, 618, 583, 622, 579, 1223, 578, 1224, 577, 1225, 577, 617, 585, 619,
|
||||
11559, 2426, 577, 1225, 577, 617, 583, 621, 579, 1223, 579, 1223, 578, 1224, 578, 1224, 578, 616, 584, 1218, 584, 620, 580, 1222, 580, 1222, 579, 1223, 579, 625, 575, 650, 550, 1252, 550, 1252, 549, 1253, 549, 645, 556, 648,
|
||||
11531, 2453, 549, 1253, 548, 646, 555, 649, 551, 1251, 551, 1251, 550, 1252, 549, 1253, 549, 645, 556, 1246, 555, 649, 552, 1219, 582, 1220, 582, 1220, 582, 622, 578, 616, 585, 1217, 584, 1218, 584, 1218, 584, 620, 581, 623,
|
||||
1000000, 2428, 576, 618, 582, 1220, 583, 622, 579, 1223, 578, 1224, 578, 1224, 578, 1224, 578, 616, 585, 1217, 585, 619, 582, 1220, 582, 1220, 582, 1220, 582, 622, 578, 616, 585, 1217, 585, 1217, 584, 1218, 584, 620, 581, 623,
|
||||
11557, 2427, 577, 617, 584, 1218, 583, 621, 580, 1222, 580, 1222, 580, 1222, 580, 1222, 580, 624, 576, 1246, 555, 649, 552, 1250, 552, 1250, 551, 1251, 551, 653, 548, 646, 554, 1248, 555, 1247, 555, 1247, 555, 649, 551, 653,
|
||||
11528, 2447, 556, 648, 552, 1250, 552, 653, 548, 1254, 548, 1254, 547, 1245, 557, 1245, 557, 647, 553, 1249, 552, 652, 549, 1253, 548, 1254, 548, 1254, 548, 646, 555, 649, 551, 1251, 551, 1251, 551, 1251, 551, 643, 557, 647,
|
||||
1000000, 2418, 610, 594, 606, 598, 603, 1199, 603, 1199, 602, 1200, 602, 1200, 602, 1200, 602, 592, 608, 1194, 608, 596, 604, 1198, 604, 1198, 604, 1198, 603, 591, 610, 594, 606, 1196, 606, 1196, 605, 1197, 605, 599, 601, 593,
|
||||
11563, 2422, 606, 598, 602, 592, 608, 1194, 608, 1194, 608, 1194, 607, 1195, 607, 1195, 607, 597, 603, 1199, 603, 591, 609, 1193, 609, 1193, 609, 1193, 608, 596, 605, 599, 601, 1201, 600, 1191, 611, 1191, 611, 593, 606, 598,
|
||||
11558, 2427, 601, 593, 607, 597, 603, 1199, 603, 1199, 603, 1199, 602, 1200, 602, 1200, 601, 593, 607, 1195, 607, 597, 603, 1199, 603, 1199, 602, 1200, 602, 592, 608, 596, 604, 1198, 604, 1198, 604, 1198, 603, 591, 609, 595,
|
||||
1000000, 2424, 605, 1197, 604, 600, 600, 1191, 610, 1192, 610, 1192, 610, 1192, 609, 1193, 609, 595, 605, 1197, 606, 598, 602, 1200, 602, 1200, 601, 1201, 601, 593, 607, 597, 603, 1199, 603, 1199, 603, 1199, 603, 591, 609, 595,
|
||||
10937, 2420, 609, 1193, 609, 595, 605, 1197, 605, 1197, 605, 1197, 605, 1197, 605, 1197, 605, 600, 601, 1201, 601, 593, 608, 1194, 608, 1194, 608, 1194, 609, 595, 605, 599, 601, 1201, 601, 1201, 601, 1201, 601, 593, 608, 596,
|
||||
10936, 2420, 608, 1194, 608, 596, 604, 1198, 605, 1197, 605, 1197, 605, 1197, 605, 1197, 605, 600, 601, 1201, 601, 593, 607, 1195, 608, 1194, 608, 1194, 608, 596, 604, 600, 600, 1202, 601, 1201, 601, 1201, 601, 593, 607, 597,
|
||||
1000000, 2420, 609, 1193, 608, 1194, 608, 596, 579, 625, 600, 1202, 600, 1202, 600, 1202, 600, 594, 607, 1195, 607, 597, 603, 1199, 603, 1199, 602, 1200, 602, 592, 609, 595, 605, 1197, 605, 1197, 605, 1197, 604, 600, 600, 594,
|
||||
11561, 2423, 605, 1197, 605, 1197, 605, 599, 601, 593, 607, 1195, 607, 1195, 607, 1195, 607, 597, 604, 1198, 603, 591, 610, 1192, 610, 1192, 610, 1192, 610, 594, 607, 597, 603, 1199, 603, 1199, 603, 1199, 603, 591, 610, 594,
|
||||
11563, 2421, 608, 1194, 608, 1194, 608, 596, 605, 599, 601, 1201, 602, 1200, 602, 1200, 602, 592, 609, 1193, 609, 595, 605, 1197, 606, 1196, 606, 1196, 606, 598, 602, 592, 609, 1193, 609, 1193, 610, 1192, 610, 594, 607, 597,
|
||||
1000000, 2428, 576, 1226, 600, 1192, 610, 594, 606, 598, 602, 1200, 602, 592, 609, 595, 605, 600, 601, 593, 607, 597, 603, 591, 609, 1193,
|
||||
25955, 2427, 601, 1190, 610, 1192, 610, 594, 606, 598, 602, 1200, 601, 593, 607, 597, 603, 591, 610, 594, 606, 598, 602, 592, 608, 1194,
|
||||
25957, 2425, 604, 1198, 603, 1199, 603, 591, 609, 595, 605, 1197, 605, 599, 601, 593, 607, 598, 603, 591, 610, 594, 606, 598, 602, 1200,
|
||||
25952, 2420, 608, 1194, 608, 1194, 608, 596, 604, 601, 600, 1191, 610, 594, 606, 598, 603, 591, 609, 595, 605, 599, 601, 593, 608, 1194,
|
||||
1000000, 2421, 607, 597, 603, 1199, 603, 591, 610, 594, 606, 1196, 605, 600, 576, 618, 583, 621, 604, 601, 575, 619, 606, 598, 603, 1199,
|
||||
26576, 2424, 605, 600, 576, 1226, 601, 593, 608, 596, 604, 1198, 604, 600, 600, 594, 607, 597, 603, 591, 609, 595, 606, 598, 602, 1200,
|
||||
26579, 2420, 583, 621, 605, 1197, 604, 600, 601, 593, 607, 1195, 607, 597, 604, 590, 610, 594, 607, 597, 603, 591, 610, 594, 606, 1196,
|
||||
26584, 2426, 603, 591, 609, 1193, 609, 595, 605, 599, 601, 1201, 601, 593, 607, 597, 603, 591, 610, 594, 606, 598, 603, 591, 609, 1193,
|
||||
1000000, 2418, 610, 594, 606, 598, 602, 592, 609, 595, 605, 1197, 605, 1197, 605, 600, 602, 592, 608, 1194, 608, 596, 605, 1197, 605, 1197, 604, 1198, 604, 600, 601, 593, 607, 1195, 607, 1195, 607, 1195, 607, 597, 603, 591,
|
||||
13368, 2419, 610, 594, 606, 598, 602, 592, 608, 596, 605, 1197, 604, 1198, 604, 600, 601, 593, 607, 1195, 607, 597, 603, 1199, 603, 1199, 603, 1199, 603, 591, 609, 595, 605, 1197, 605, 1197, 605, 1197, 604, 601, 600, 594,
|
||||
13362, 2425, 604, 601, 600, 594, 607, 597, 603, 591, 610, 1192, 610, 1192, 610, 594, 606, 598, 603, 1199, 602, 592, 609, 1193, 609, 1193, 608, 1194, 608, 596, 604, 601, 600, 1191, 610, 1192, 610, 1192, 610, 594, 607, 597,
|
||||
1000000, 2427, 601, 1201, 601, 593, 582, 623, 603, 1199, 578, 1224, 603, 1199, 577, 617, 584, 620, 605, 1197, 605, 600, 601, 1201, 601, 1201, 601, 1201, 601, 593, 582, 622, 603, 1199, 603, 1199, 578, 1224, 603, 591, 610, 594,
|
||||
12139, 2423, 605, 1197, 605, 600, 601, 593, 608, 1194, 608, 1194, 607, 1195, 607, 597, 604, 600, 601, 1201, 601, 593, 607, 1195, 608, 1194, 608, 1194, 583, 622, 604, 601, 600, 1202, 575, 1227, 601, 1201, 576, 618, 607, 597,
|
||||
12137, 2424, 604, 1198, 604, 590, 610, 594, 607, 1195, 607, 1195, 607, 1195, 607, 597, 604, 601, 600, 1202, 600, 594, 607, 1195, 606, 1196, 581, 1221, 607, 597, 603, 591, 610, 1202, 600, 1202, 576, 1226, 602, 592, 609, 595,
|
||||
1000000, 2422, 607, 1195, 607, 597, 603, 591, 585, 619, 606, 1196, 606, 1196, 581, 624, 577, 617, 609, 1193, 584, 621, 605, 1197, 604, 1198, 604, 1198, 604, 590, 610, 595, 581, 1221, 605, 1197, 605, 1197, 605, 599, 601, 593,
|
||||
12763, 2427, 603, 1199, 603, 591, 609, 595, 605, 599, 577, 1225, 602, 1200, 601, 593, 583, 622, 604, 1198, 604, 590, 610, 1192, 610, 1192, 610, 1192, 610, 594, 606, 598, 603, 1199, 603, 1199, 603, 1199, 603, 591, 610, 594,
|
||||
12763, 2427, 602, 1200, 601, 593, 608, 596, 604, 600, 600, 1202, 601, 1201, 601, 593, 607, 597, 604, 1198, 604, 590, 610, 1192, 610, 1192, 611, 1191, 610, 594, 607, 598, 578, 1224, 603, 1199, 603, 1199, 603, 591, 610, 594,
|
||||
1000000, 2422, 607, 597, 603, 591, 610, 1192, 610, 594, 606, 1196, 580, 1222, 605, 600, 601, 593, 583, 1219, 608, 596, 579, 1223, 604, 1198, 604, 1198, 604, 590, 610, 594, 606, 1196, 606, 1196, 606, 1196, 606, 599, 602, 592,
|
||||
12765, 2425, 603, 591, 610, 594, 606, 1196, 606, 598, 602, 1200, 601, 1201, 601, 593, 582, 623, 604, 1198, 604, 590, 610, 1192, 610, 1192, 609, 1193, 609, 596, 605, 599, 601, 1201, 601, 1201, 601, 1201, 600, 594, 607, 597,
|
||||
12758, 2421, 607, 597, 603, 591, 609, 1193, 609, 595, 605, 1197, 605, 1197, 605, 599, 601, 593, 607, 1195, 607, 597, 603, 1199, 603, 1199, 603, 1199, 603, 591, 609, 595, 580, 1222, 605, 1197, 605, 1197, 605, 600, 600, 594,
|
||||
1000000, 2422, 580, 625, 601, 1201, 601, 593, 608, 596, 604, 1198, 604, 1198, 604, 600, 600, 594, 607, 1195, 607, 597, 603, 1199, 603, 1199, 603, 1199, 578, 616, 609, 595, 606, 1196, 606, 1196, 606, 1196, 581, 624, 602, 592,
|
||||
12766, 2424, 605, 599, 601, 1201, 601, 593, 608, 596, 604, 1198, 604, 1198, 579, 625, 601, 593, 608, 1194, 608, 596, 604, 1198, 580, 1222, 605, 1197, 605, 599, 602, 592, 608, 1194, 609, 1193, 609, 1193, 609, 596, 605, 599,
|
||||
12759, 2420, 608, 597, 604, 1198, 604, 590, 610, 594, 606, 1196, 606, 1196, 606, 598, 602, 592, 608, 1194, 607, 597, 604, 1198, 603, 1199, 603, 1199, 602, 592, 609, 595, 605, 1197, 605, 1197, 605, 1197, 604, 600, 601, 593,
|
||||
1000000, 2429, 600, 1202, 575, 1227, 599, 595, 606, 598, 602, 1200, 602, 1200, 602, 592, 609, 595, 605, 1197, 605, 599, 601, 1201, 601, 1201, 601, 1201, 601, 593, 607, 597, 603, 1199, 603, 1199, 603, 1199, 603, 591, 609, 595,
|
||||
12136, 2425, 603, 1199, 603, 1199, 603, 591, 609, 595, 605, 1197, 605, 1197, 605, 599, 601, 593, 608, 1194, 608, 596, 604, 1198, 604, 1198, 603, 1199, 603, 591, 609, 595, 606, 1196, 606, 1196, 605, 1197, 605, 599, 601, 593,
|
||||
12137, 2424, 604, 1198, 603, 1199, 603, 591, 609, 595, 606, 1196, 605, 1197, 605, 599, 601, 593, 607, 1195, 607, 597, 603, 1199, 603, 1199, 603, 1199, 602, 592, 609, 595, 605, 1197, 605, 1197, 604, 1198, 604, 600, 600, 594,
|
||||
1000000, 2429, 574, 1228, 573, 1229, 573, 1229, 573, 1229, 573, 621, 581, 624, 577, 617, 583, 622, 580, 1222, 579, 625, 576, 1226, 577, 1225, 576, 1226, 577, 617, 584, 620, 581, 1221, 580, 1222, 579, 1223, 579, 625, 577, 617,
|
||||
12142, 2419, 584, 1218, 584, 1218, 583, 1219, 583, 1219, 582, 623, 578, 616, 585, 619, 581, 623, 578, 1224, 577, 617, 584, 1218, 584, 1218, 584, 1218, 584, 620, 580, 624, 576, 1226, 575, 1227, 500, 1353, 523, 620, 582, 622, /* failed, noise pollution 500, 1353 timings */
|
||||
12134, 2427, 576, 1226, 576, 1226, 576, 1226, 576, 1226, 576, 618, 583, 622, 579, 625, 576, 618, 583, 1219, 583, 673, 527, 1223, 579, 1223, 579, 1223, 579, 625, 576, 618, 582, 1220, 582, 1220, 582, 1220, 582, 622, 578, 616,
|
||||
12140, 2421, 582, 1220, 581, 1221, 581, 1221, 580, 1222, 580, 624, 576, 618, 582, 622, 578, 616, 585, 1217, 584, 620, 581, 1221, 580, 1222, 580, 1222, 580, 624, 576, 618, 582, 1220, 582, 1220, 582, 1220, 582, 623, 578, 616,
|
||||
1000000, 2419, 584, 672, 528, 625, 577, 617, 583, 1219, 582, 1220, 581, 1221, 582, 622, 578, 616, 585, 1217, 583, 621, 580, 1222, 580, 1222, 580, 1222, 580, 624, 576, 618, 583, 1219, 583, 1219, 582, 1220, 583, 621, 580, 624,
|
||||
12732, 2427, 577, 617, 584, 672, 528, 676, 524, 1226, 576, 1226, 576, 1226, 576, 618, 583, 621, 579, 1223, 579, 625, 575, 1227, 575, 1227, 575, 1227, 575, 671, 529, 675, 526, 1224, 578, 1224, 578, 1224, 578, 616, 585, 619,
|
||||
12738, 2421, 583, 621, 580, 624, 576, 618, 582, 1220, 582, 1220, 583, 1219, 583, 621, 579, 625, 576, 1226, 576, 670, 530, 1220, 582, 1220, 582, 1220, 581, 624, 577, 617, 584, 1218, 583, 1219, 583, 1219, 583, 622, 580, 624,
|
||||
1000000, 2430, 599, 1192, 609, 595, 605, 1197, 580, 624, 601, 1201, 601, 593, 607, 597, 603, 591, 610, 594, 606, 598, 602, 592, 608, 1194,
|
||||
25958, 2423, 605, 1197, 604, 600, 575, 1227, 600, 594, 606, 1196, 606, 598, 602, 592, 608, 596, 604, 601, 600, 594, 607, 597, 603, 1199,
|
||||
25949, 2422, 607, 1195, 606, 598, 603, 1199, 602, 592, 608, 1194, 608, 596, 604, 600, 601, 593, 607, 597, 604, 600, 600, 594, 607, 1195,
|
||||
25958, 2423, 606, 1196, 606, 598, 602, 1200, 602, 592, 608, 1194, 608, 596, 605, 600, 601, 593, 607, 597, 603, 591, 610, 594, 606, 1196,
|
||||
25957, 2425, 604, 1198, 603, 591, 610, 1192, 610, 594, 606, 1196, 606, 598, 602, 592, 609, 595, 605, 599, 602, 592, 608, 596, 604, 1198,
|
||||
25956, 2426, 604, 1198, 603, 591, 610, 1192, 610, 594, 606, 1196, 606, 598, 602, 592, 608, 597, 605, 599, 601, 593, 607, 597, 603, 1199,
|
||||
25952, 2420, 609, 1193, 608, 596, 604, 1198, 604, 590, 610, 1192, 610, 594, 606, 598, 602, 592, 608, 596, 605, 599, 601, 593, 607, 1195,
|
||||
1000000, 2422, 583, 1219, 608, 596, 605, 1197, 580, 624, 601, 1201, 601, 593, 608, 596, 604, 600, 601, 593, 607, 597, 604, 590, 610, 1202,
|
||||
25955, 2427, 603, 1199, 603, 591, 609, 1193, 610, 594, 606, 1196, 607, 597, 603, 591, 609, 595, 606, 598, 602, 592, 609, 595, 605, 1197,
|
||||
25957, 2425, 604, 1198, 604, 590, 610, 1192, 610, 594, 581, 1221, 606, 598, 602, 592, 608, 596, 605, 599, 601, 593, 607, 597, 604, 1198,
|
||||
25954, 2418, 610, 1192, 610, 594, 606, 1196, 605, 599, 601, 1201, 601, 593, 608, 596, 604, 590, 610, 594, 606, 598, 603, 591, 609, 1193,
|
||||
25958, 2424, 604, 1198, 604, 590, 610, 1192, 610, 594, 606, 1196, 606, 598, 602, 592, 609, 595, 605, 600, 601, 593, 607, 597, 603, 1199,
|
||||
25953, 2419, 610, 1192, 609, 595, 605, 1197, 605, 600, 601, 1201, 600, 594, 607, 597, 603, 591, 609, 595, 605, 599, 601, 593, 608, 1194,
|
||||
25954, 2418, 611, 1191, 610, 594, 606, 1196, 606, 598, 602, 1200, 602, 592, 608, 596, 604, 601, 600, 594, 607, 597, 603, 591, 609, 1193,
|
||||
25958, 2424, 605, 1197, 605, 599, 601, 1201, 600, 594, 607, 1195, 607, 597, 603, 591, 609, 596, 605, 599, 602, 592, 608, 596, 605, 1197,
|
||||
25954, 2428, 601, 1190, 610, 594, 607, 1195, 606, 598, 602, 1200, 602, 592, 608, 596, 604, 590, 610, 594, 606, 598, 603, 591, 609, 1193,
|
||||
25960, 2422, 607, 1195, 606, 598, 602, 1200, 602, 592, 609, 1193, 609, 595, 605, 599, 601, 593, 607, 597, 604, 590, 610, 594, 606, 1196,
|
||||
25957, 2424, 605, 1197, 604, 600, 600, 1202, 601, 593, 607, 1195, 607, 597, 603, 591, 610, 594, 606, 598, 603, 591, 609, 595, 606, 1196,
|
||||
1000000, 2421, 608, 1194, 608, 596, 604, 1198, 604, 601, 575, 1227, 601, 593, 607, 598, 604, 600, 600, 594, 607, 598, 604, 601, 600, 1202,
|
||||
25953, 2419, 609, 1193, 609, 596, 605, 1197, 605, 600, 601, 1201, 601, 593, 607, 597, 603, 591, 610, 594, 606, 599, 602, 592, 608, 1194,
|
||||
25958, 2424, 605, 1197, 605, 600, 601, 1201, 600, 594, 607, 1195, 606, 598, 602, 592, 609, 595, 605, 600, 601, 593, 608, 596, 604, 1198,
|
||||
1000000, 2423, 606, 1196, 606, 598, 602, 1200, 602, 592, 608, 1194, 608, 597, 604, 590, 610, 594, 607, 597, 603, 591, 610, 594, 606, 1196,
|
||||
25958, 2424, 605, 1197, 604, 600, 601, 1201, 601, 593, 607, 1195, 607, 597, 604, 590, 610, 595, 607, 597, 603, 591, 610, 594, 607, 1195,
|
||||
25961, 2421, 608, 1194, 608, 596, 605, 1197, 605, 600, 601, 1201, 601, 593, 607, 597, 603, 591, 610, 594, 606, 598, 602, 592, 609, 1193,
|
||||
1000000, 2429, 601, 1201, 601, 593, 607, 1195, 608, 596, 604, 1198, 604, 600, 601, 593, 607, 597, 604, 590, 611, 593, 607, 597, 604, 1198,
|
||||
25959, 2422, 607, 1195, 607, 597, 603, 1199, 603, 591, 609, 1193, 610, 594, 606, 598, 602, 592, 609, 595, 605, 599, 602, 592, 608, 1194,
|
||||
25959, 2421, 608, 1194, 607, 597, 603, 1199, 603, 591, 610, 1192, 610, 594, 606, 598, 603, 591, 609, 595, 605, 599, 602, 592, 608, 1194,
|
||||
25959, 2422, 608, 1194, 608, 596, 604, 1198, 604, 590, 611, 1201, 601, 593, 607, 597, 604, 590, 610, 594, 607, 597, 603, 591, 609, 1193,
|
||||
25962, 2418, 610, 1192, 610, 594, 606, 1196, 607, 597, 603, 1199, 603, 591, 609, 595, 605, 599, 602, 592, 608, 596, 605, 599, 601, 1201,
|
||||
|
||||
1000000, 2357, 610, 1183, 592, 1191, 614, 1179, 595, 1188, 618, 584, 613, 1180, 593, 588, 613, 1190, 612, 590, 605, 587, 613, 589, 605, 587,
|
||||
25398, 2355, 623, 1180, 591, 1181, 627, 1186, 589, 1183, 618, 584, 616, 1187, 587, 584, 614, 1189, 615, 587, 605, 587, 615, 587, 609, 593,
|
||||
1000000, 2383, 609, 1214, 600, 612, 580, 1213, 608, 614, 583, 1210, 605, 607, 586, 616, 611, 1192, 599, 613, 608, 614, 579, 613, 615, 607,
|
||||
26670, 2387, 601, 1212, 600, 612, 589, 1214, 603, 609, 586, 1217, 595, 607, 594, 618, 606, 1187, 602, 610, 609, 613, 588, 614, 610, 612,
|
||||
1000000, 2445, 582, 1221, 603, 548, 602, 1191, 609, 572, 602, 1191, 609, 542, 607, 544, 631, 1172, 603, 568, 606, 545, 605, 566, 608, 543,
|
||||
26263, 2414, 611, 1192, 607, 544, 606, 1197, 602, 569, 606, 1197, 602, 539, 611, 540, 635, 1168, 606, 565, 610, 541, 608, 563, 587, 564,
|
||||
};
|
||||
|
||||
const IrdaMessage test_decoder_sirc_expected1[] = {
|
||||
{IrdaProtocolSIRC, 0x10, 0x15, false},
|
||||
{IrdaProtocolSIRC, 0x10, 0x15, false},
|
||||
{IrdaProtocolSIRC, 0x10, 0x15, false},
|
||||
{IrdaProtocolSIRC, 0x10, 0x15, false},
|
||||
{IrdaProtocolSIRC, 0x10, 0x15, false},
|
||||
{IrdaProtocolSIRC, 0x10, 0x15, false},
|
||||
{IrdaProtocolSIRC, 0x10, 0x60, false},
|
||||
{IrdaProtocolSIRC, 0x10, 0x60, false},
|
||||
{IrdaProtocolSIRC, 0x10, 0x60, false},
|
||||
{IrdaProtocolSIRC, 0x10, 0x65, false},
|
||||
{IrdaProtocolSIRC, 0x10, 0x65, false},
|
||||
{IrdaProtocolSIRC, 0x10, 0x65, false},
|
||||
{IrdaProtocolSIRC20, 0x410, 0x17, false},
|
||||
{IrdaProtocolSIRC20, 0x410, 0x17, false},
|
||||
{IrdaProtocolSIRC20, 0x410, 0x17, false},
|
||||
{IrdaProtocolSIRC, 0x10, 0x21, false},
|
||||
{IrdaProtocolSIRC, 0x10, 0x21, false},
|
||||
{IrdaProtocolSIRC, 0x10, 0x21, false},
|
||||
{IrdaProtocolSIRC20, 0x73A, 0x7C, false},
|
||||
{IrdaProtocolSIRC20, 0x73A, 0x7C, false},
|
||||
{IrdaProtocolSIRC20, 0x73A, 0x7C, false},
|
||||
{IrdaProtocolSIRC20, 0x73A, 0x7C, false},
|
||||
{IrdaProtocolSIRC20, 0x73A, 0x7C, false},
|
||||
{IrdaProtocolSIRC20, 0x73A, 0x7C, false},
|
||||
{IrdaProtocolSIRC20, 0x73A, 0x7C, false},
|
||||
{IrdaProtocolSIRC20, 0x73A, 0x7C, false},
|
||||
{IrdaProtocolSIRC20, 0x73A, 0x7C, false},
|
||||
{IrdaProtocolSIRC20, 0x73A, 0x7C, false},
|
||||
{IrdaProtocolSIRC20, 0x73A, 0x7C, false},
|
||||
{IrdaProtocolSIRC20, 0x73A, 0x7C, false},
|
||||
{IrdaProtocolSIRC20, 0x73A, 0x7B, false},
|
||||
{IrdaProtocolSIRC20, 0x73A, 0x7B, false},
|
||||
{IrdaProtocolSIRC20, 0x73A, 0x7B, false},
|
||||
{IrdaProtocolSIRC20, 0x73A, 0x7A, false},
|
||||
{IrdaProtocolSIRC20, 0x73A, 0x7A, false},
|
||||
{IrdaProtocolSIRC20, 0x73A, 0x7A, false},
|
||||
{IrdaProtocolSIRC20, 0x73A, 0x78, false},
|
||||
{IrdaProtocolSIRC20, 0x73A, 0x78, false},
|
||||
{IrdaProtocolSIRC20, 0x73A, 0x78, false},
|
||||
{IrdaProtocolSIRC20, 0x73A, 0x79, false},
|
||||
{IrdaProtocolSIRC20, 0x73A, 0x79, false},
|
||||
{IrdaProtocolSIRC20, 0x73A, 0x79, false},
|
||||
{IrdaProtocolSIRC20, 0x73A, 0x7A, false},
|
||||
{IrdaProtocolSIRC20, 0x73A, 0x7A, false},
|
||||
{IrdaProtocolSIRC20, 0x73A, 0x7A, false},
|
||||
{IrdaProtocolSIRC20, 0x73A, 0x7C, false},
|
||||
{IrdaProtocolSIRC20, 0x73A, 0x7C, false},
|
||||
{IrdaProtocolSIRC20, 0x73A, 0x7C, false},
|
||||
{IrdaProtocolSIRC20, 0x73A, 0x7D, false},
|
||||
{IrdaProtocolSIRC20, 0x73A, 0x7D, false},
|
||||
{IrdaProtocolSIRC20, 0x73A, 0x7D, false},
|
||||
{IrdaProtocolSIRC20, 0x73A, 0x73, false},
|
||||
{IrdaProtocolSIRC20, 0x73A, 0x73, false},
|
||||
{IrdaProtocolSIRC20, 0x73A, 0x73, false},
|
||||
{IrdaProtocolSIRC, 0x10, 0x13, false},
|
||||
{IrdaProtocolSIRC, 0x10, 0x13, false},
|
||||
{IrdaProtocolSIRC, 0x10, 0x13, false},
|
||||
{IrdaProtocolSIRC, 0x10, 0x13, false},
|
||||
{IrdaProtocolSIRC, 0x10, 0x12, false},
|
||||
{IrdaProtocolSIRC, 0x10, 0x12, false},
|
||||
{IrdaProtocolSIRC, 0x10, 0x12, false},
|
||||
{IrdaProtocolSIRC, 0x10, 0x12, false},
|
||||
{IrdaProtocolSIRC20, 0x73A, 0x30, false},
|
||||
{IrdaProtocolSIRC20, 0x73A, 0x30, false},
|
||||
{IrdaProtocolSIRC20, 0x73A, 0x30, false},
|
||||
{IrdaProtocolSIRC20, 0x73A, 0x39, false},
|
||||
{IrdaProtocolSIRC20, 0x73A, 0x39, false},
|
||||
{IrdaProtocolSIRC20, 0x73A, 0x39, false},
|
||||
{IrdaProtocolSIRC20, 0x73A, 0x31, false},
|
||||
{IrdaProtocolSIRC20, 0x73A, 0x31, false},
|
||||
{IrdaProtocolSIRC20, 0x73A, 0x31, false},
|
||||
{IrdaProtocolSIRC20, 0x73A, 0x34, false},
|
||||
{IrdaProtocolSIRC20, 0x73A, 0x34, false},
|
||||
{IrdaProtocolSIRC20, 0x73A, 0x34, false},
|
||||
{IrdaProtocolSIRC20, 0x73A, 0x32, false},
|
||||
{IrdaProtocolSIRC20, 0x73A, 0x32, false},
|
||||
{IrdaProtocolSIRC20, 0x73A, 0x32, false},
|
||||
{IrdaProtocolSIRC20, 0x73A, 0x33, false},
|
||||
{IrdaProtocolSIRC20, 0x73A, 0x33, false},
|
||||
{IrdaProtocolSIRC20, 0x73A, 0x33, false},
|
||||
{IrdaProtocolSIRC20, 0x73A, 0x0F, false},
|
||||
{IrdaProtocolSIRC20, 0x73A, 0x0F, false},
|
||||
{IrdaProtocolSIRC20, 0x73A, 0x0F, false},
|
||||
{IrdaProtocolSIRC20, 0x73A, 0x38, false},
|
||||
{IrdaProtocolSIRC20, 0x73A, 0x38, false},
|
||||
{IrdaProtocolSIRC20, 0x73A, 0x38, false},
|
||||
{IrdaProtocolSIRC, 0x10, 0x15, false},
|
||||
{IrdaProtocolSIRC, 0x10, 0x15, false},
|
||||
{IrdaProtocolSIRC, 0x10, 0x15, false},
|
||||
{IrdaProtocolSIRC, 0x10, 0x15, false},
|
||||
{IrdaProtocolSIRC, 0x10, 0x15, false},
|
||||
{IrdaProtocolSIRC, 0x10, 0x15, false},
|
||||
{IrdaProtocolSIRC, 0x10, 0x15, false},
|
||||
{IrdaProtocolSIRC, 0x10, 0x15, false},
|
||||
{IrdaProtocolSIRC, 0x10, 0x15, false},
|
||||
{IrdaProtocolSIRC, 0x10, 0x15, false},
|
||||
{IrdaProtocolSIRC, 0x10, 0x15, false},
|
||||
{IrdaProtocolSIRC, 0x10, 0x15, false},
|
||||
{IrdaProtocolSIRC, 0x10, 0x15, false},
|
||||
{IrdaProtocolSIRC, 0x10, 0x15, false},
|
||||
{IrdaProtocolSIRC, 0x10, 0x15, false},
|
||||
{IrdaProtocolSIRC, 0x10, 0x15, false},
|
||||
{IrdaProtocolSIRC, 0x10, 0x15, false},
|
||||
{IrdaProtocolSIRC, 0x10, 0x15, false},
|
||||
{IrdaProtocolSIRC, 0x10, 0x15, false},
|
||||
{IrdaProtocolSIRC, 0x10, 0x15, false},
|
||||
{IrdaProtocolSIRC, 0x10, 0x15, false},
|
||||
{IrdaProtocolSIRC, 0x10, 0x15, false},
|
||||
{IrdaProtocolSIRC, 0x10, 0x15, false},
|
||||
{IrdaProtocolSIRC, 0x10, 0x15, false},
|
||||
{IrdaProtocolSIRC, 0x10, 0x15, false},
|
||||
{IrdaProtocolSIRC, 0x10, 0x15, false},
|
||||
{IrdaProtocolSIRC, 0x10, 0x15, false},
|
||||
{IrdaProtocolSIRC, 0x10, 0x15, false},
|
||||
{IrdaProtocolSIRC, 0x10, 0x15, false},
|
||||
{IrdaProtocolSIRC, 0x01, 0x2F, false},
|
||||
{IrdaProtocolSIRC, 0x01, 0x2F, false},
|
||||
{IrdaProtocolSIRC, 0x01, 0x15, false},
|
||||
{IrdaProtocolSIRC, 0x01, 0x15, false},
|
||||
{IrdaProtocolSIRC, 0x01, 0x15, false},
|
||||
{IrdaProtocolSIRC, 0x01, 0x15, false},
|
||||
};
|
||||
|
||||
|
||||
// command (0x55) address (0x0A) SIRC
|
||||
// 1 0 1 0 1 0 1 0 1 0 1 0
|
||||
// 2400, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600,
|
||||
//
|
||||
// command (0x7F) address (0x1F) SIRC
|
||||
// 1 1 1 1 1 1 1 1 1 1 1 1
|
||||
// 2400, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200,
|
||||
//
|
||||
// command (0x00) address (0x00) SIRC
|
||||
// 0 0 0 0 0 0 0 0 0 0 0 0
|
||||
// 2400, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
|
||||
//
|
||||
// command (0x53) address (0x0D) SIRC
|
||||
// 1 1 0 0 1 0 1 1 0 1 1 0
|
||||
// 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600,
|
||||
|
||||
const uint32_t test_decoder_sirc_input2[] = {
|
||||
1000000, 2400, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600,
|
||||
1000000, 2400, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600,
|
||||
1000000, 2400, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600,
|
||||
1000000, 2400, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 600, /* failed - should be skipped */
|
||||
|
||||
1000000, 2400, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200,
|
||||
1000000, 2400, 600, 1200, 600, 600, /* failed - should be skipped */
|
||||
1000000, 2400, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200,
|
||||
1000000, 2400, 600, 1200, 600, /* failed - should be skipped */
|
||||
2400, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200,
|
||||
1000000, 2400, 600, 1200, 600, 600, /* failed - should be skipped */
|
||||
1000000, 2400, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600,
|
||||
|
||||
1000000, 2400, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
|
||||
|
||||
1000000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600,
|
||||
};
|
||||
|
||||
const IrdaMessage test_decoder_sirc_expected2[] = {
|
||||
{IrdaProtocolSIRC, 0xA, 0x55, false},
|
||||
{IrdaProtocolSIRC, 0xA, 0x55, false},
|
||||
{IrdaProtocolSIRC, 0xA, 0x55, false},
|
||||
/* failed - 13 data bits */
|
||||
|
||||
{IrdaProtocolSIRC, 0x1F, 0x7F, false},
|
||||
/* failed - 2 data bits */
|
||||
{IrdaProtocolSIRC, 0x1F, 0x7F, false},
|
||||
/* failed - sudden end */
|
||||
{IrdaProtocolSIRC, 0x1F, 0x7F, false},
|
||||
/* failed */
|
||||
{IrdaProtocolSIRC, 0x0A, 0x55, false},
|
||||
|
||||
{IrdaProtocolSIRC, 0x00, 0x00, false},
|
||||
|
||||
{IrdaProtocolSIRC, 0x0D, 0x53, false},
|
||||
};
|
||||
|
||||
|
||||
// command (0x13) address (0xFD) SIRC15
|
||||
// 1 1 0 0 1 0 0 1 0 1 1 1 1 1 1
|
||||
// 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200,
|
||||
//
|
||||
// command (0x53) address (0x7D) SIRC15
|
||||
// 1 1 0 0 1 0 1 1 0 1 1 1 1 1 0
|
||||
// 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600,
|
||||
//
|
||||
// command (0x53) address (0x0D) SIRC15
|
||||
// 1 1 0 0 1 0 1 1 0 1 1 0 0 0 0
|
||||
// 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 600, 600, 600,
|
||||
|
||||
const uint32_t test_decoder_sirc_input3[] = {
|
||||
1000000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600,
|
||||
10000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600,
|
||||
10000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600,
|
||||
|
||||
1000000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 600, 600, 600,
|
||||
10000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 600, 600, 600,
|
||||
10000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 600, 600, 600,
|
||||
|
||||
1000000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 600, 600, 600,
|
||||
1000000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 600, 600, 600,
|
||||
|
||||
1000000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600,
|
||||
1000000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600,
|
||||
|
||||
10000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200,
|
||||
};
|
||||
|
||||
const IrdaMessage test_decoder_sirc_expected3[] = {
|
||||
{IrdaProtocolSIRC15, 0x7D, 0x53, false},
|
||||
{IrdaProtocolSIRC15, 0x7D, 0x53, false},
|
||||
{IrdaProtocolSIRC15, 0x7D, 0x53, false},
|
||||
{IrdaProtocolSIRC15, 0x0D, 0x53, false},
|
||||
{IrdaProtocolSIRC15, 0x0D, 0x53, false},
|
||||
{IrdaProtocolSIRC15, 0x0D, 0x53, false},
|
||||
{IrdaProtocolSIRC15, 0x0D, 0x53, false},
|
||||
{IrdaProtocolSIRC15, 0x0D, 0x53, false},
|
||||
{IrdaProtocolSIRC15, 0x7D, 0x53, false},
|
||||
{IrdaProtocolSIRC15, 0x7D, 0x53, false},
|
||||
{IrdaProtocolSIRC15, 0xFD, 0x13, false},
|
||||
};
|
||||
|
||||
const uint32_t test_decoder_sirc_input4[] = {
|
||||
1000000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600,
|
||||
10000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600,
|
||||
10000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600,
|
||||
1000000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600,
|
||||
1000000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600,
|
||||
1000000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600,
|
||||
};
|
||||
|
||||
const IrdaMessage test_decoder_sirc_expected4[] = {
|
||||
{IrdaProtocolSIRC20, 0xFB5, 0x53, false}, // {IrdaProtocolSIRC20, 0x15, 0x3ED3, false},
|
||||
{IrdaProtocolSIRC20, 0xFB5, 0x53, false},
|
||||
{IrdaProtocolSIRC20, 0xFB5, 0x53, false},
|
||||
{IrdaProtocolSIRC20, 0xFB5, 0x53, false},
|
||||
{IrdaProtocolSIRC20, 0xFB5, 0x53, false},
|
||||
{IrdaProtocolSIRC20, 0xFB5, 0x53, false},
|
||||
};
|
||||
|
||||
const uint32_t test_decoder_sirc_input5[] = {
|
||||
1000000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600,
|
||||
1000000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600,
|
||||
1000000, 2400, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600,
|
||||
1000000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600,
|
||||
1000000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600,
|
||||
1000000, 2400, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600,
|
||||
|
||||
10000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600,
|
||||
10000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600,
|
||||
10000, 2400, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600,
|
||||
10000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600,
|
||||
10000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600,
|
||||
10000, 2400, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600,
|
||||
|
||||
10000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600,
|
||||
10000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600,
|
||||
10000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600,
|
||||
10000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600,
|
||||
10000, 2400, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600,
|
||||
10000, 2400, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600,
|
||||
|
||||
10000, 2400, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600,
|
||||
10000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600,
|
||||
10000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600,
|
||||
10000, 2400, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600,
|
||||
10000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600,
|
||||
10000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600,
|
||||
};
|
||||
|
||||
const IrdaMessage test_decoder_sirc_expected5[] = {
|
||||
{IrdaProtocolSIRC20, 0xFB5, 0x53, false},
|
||||
{IrdaProtocolSIRC15, 0x7D, 0x53, false},
|
||||
{IrdaProtocolSIRC, 0xA, 0x55, false},
|
||||
{IrdaProtocolSIRC20, 0xFB5, 0x53, false},
|
||||
{IrdaProtocolSIRC15, 0x7D, 0x53, false},
|
||||
{IrdaProtocolSIRC, 0xA, 0x55, false},
|
||||
|
||||
{IrdaProtocolSIRC20, 0xFB5, 0x53, false},
|
||||
{IrdaProtocolSIRC15, 0x7D, 0x53, false},
|
||||
{IrdaProtocolSIRC, 0xA, 0x55, false},
|
||||
{IrdaProtocolSIRC20, 0xFB5, 0x53, false},
|
||||
{IrdaProtocolSIRC15, 0x7D, 0x53, false},
|
||||
{IrdaProtocolSIRC, 0xA, 0x55, false},
|
||||
|
||||
{IrdaProtocolSIRC20, 0xFB5, 0x53, false},
|
||||
{IrdaProtocolSIRC20, 0xFB5, 0x53, false},
|
||||
{IrdaProtocolSIRC15, 0x7D, 0x53, false},
|
||||
{IrdaProtocolSIRC15, 0x7D, 0x53, false},
|
||||
{IrdaProtocolSIRC, 0xA, 0x55, false},
|
||||
{IrdaProtocolSIRC, 0xA, 0x55, false},
|
||||
|
||||
{IrdaProtocolSIRC, 0xA, 0x55, false},
|
||||
{IrdaProtocolSIRC15, 0x7D, 0x53, false},
|
||||
{IrdaProtocolSIRC20, 0xFB5, 0x53, false},
|
||||
{IrdaProtocolSIRC, 0xA, 0x55, false},
|
||||
{IrdaProtocolSIRC15, 0x7D, 0x53, false},
|
||||
{IrdaProtocolSIRC20, 0xFB5, 0x53, false},
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
const IrdaMessage test_encoder_sirc_input1[] = {
|
||||
{IrdaProtocolSIRC, 0xA, 0x55, false},
|
||||
};
|
||||
|
||||
const uint32_t test_encoder_sirc_expected1[] = {
|
||||
10000, 2400, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600,
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
const IrdaMessage test_encoder_sirc_input2[] = {
|
||||
{IrdaProtocolSIRC15, 0x7D, 0x53, false},
|
||||
{IrdaProtocolSIRC15, 0x7D, 0x53, true},
|
||||
{IrdaProtocolSIRC15, 0x7D, 0x53, true},
|
||||
};
|
||||
|
||||
const uint32_t test_encoder_sirc_expected2[] = {
|
||||
10000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600, 600, /* 2 low levels in row */
|
||||
18000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600, 600, /* 2 low levels in row */
|
||||
18000, 2400, 600, 1200, 600, 1200, 600, 600, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600, 600,
|
||||
};
|
||||
|
||||
const IrdaMessage test_sirc[] = {
|
||||
{IrdaProtocolSIRC20, 0x1FFF, 0x7F, false},
|
||||
{IrdaProtocolSIRC20, 0x1FFF, 0x7F, true},
|
||||
{IrdaProtocolSIRC20, 0x1FFF, 0x7F, true},
|
||||
{IrdaProtocolSIRC, 0x00, 0x00, false},
|
||||
{IrdaProtocolSIRC, 0x00, 0x00, true},
|
||||
{IrdaProtocolSIRC, 0x00, 0x00, true},
|
||||
|
||||
{IrdaProtocolSIRC, 0x1A, 0x22, false},
|
||||
{IrdaProtocolSIRC, 0x1A, 0x22, true},
|
||||
{IrdaProtocolSIRC, 0x1A, 0x22, true},
|
||||
{IrdaProtocolSIRC, 0x17, 0x0A, false},
|
||||
|
||||
{IrdaProtocolSIRC15, 0x7D, 0x53, false},
|
||||
{IrdaProtocolSIRC15, 0x7D, 0x53, true},
|
||||
{IrdaProtocolSIRC15, 0x7D, 0x53, true},
|
||||
{IrdaProtocolSIRC15, 0x71, 0x0, false},
|
||||
{IrdaProtocolSIRC15, 0x15, 0x01, false},
|
||||
{IrdaProtocolSIRC15, 0x01, 0x15, false},
|
||||
|
||||
{IrdaProtocolSIRC20, 0xAA, 0x55, false},
|
||||
{IrdaProtocolSIRC20, 0x331, 0x71, false},
|
||||
|
||||
{IrdaProtocolSIRC, 0x00, 0x00, false},
|
||||
{IrdaProtocolSIRC, 0x1F, 0x7F, false},
|
||||
{IrdaProtocolSIRC15, 0x00, 0x00, false},
|
||||
{IrdaProtocolSIRC15, 0xFF, 0x7F, false},
|
||||
{IrdaProtocolSIRC20, 0x00, 0x00, false},
|
||||
{IrdaProtocolSIRC20, 0x1FFF, 0x7F, false},
|
||||
};
|
||||
|
||||
@ -4,7 +4,7 @@ include $(PROJECT_ROOT)/assets/assets.mk
|
||||
|
||||
$(ASSETS): $(ASSETS_SOURCES) $(ASSETS_COMPILLER)
|
||||
@echo "\tASSETS\t" $@
|
||||
@$(ASSETS_COMPILLER) icons -s $(ASSETS_SOURCE_DIR) -o $(ASSETS_COMPILED_DIR)
|
||||
@$(ASSETS_COMPILLER) icons "$(ASSETS_SOURCE_DIR)" "$(ASSETS_COMPILED_DIR)"
|
||||
|
||||
clean:
|
||||
@echo "\tCLEAN\t"
|
||||
|
||||
@ -56,7 +56,7 @@ _Min_Stack_Size = 0x400; /* required amount of stack */
|
||||
MEMORY
|
||||
{
|
||||
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 32K
|
||||
RAM1 (xrw) : ORIGIN = 0x20000004, LENGTH = 0x2FFFC
|
||||
RAM1 (xrw) : ORIGIN = 0x20000008, LENGTH = 0x2FFF8
|
||||
RAM_SHARED (xrw) : ORIGIN = 0x20030000, LENGTH = 10K
|
||||
}
|
||||
|
||||
|
||||
189
bootloader/targets/f7/furi-hal/furi-hal-gpio.c
Normal file
189
bootloader/targets/f7/furi-hal/furi-hal-gpio.c
Normal file
@ -0,0 +1,189 @@
|
||||
#include <furi-hal-gpio.h>
|
||||
#include <stddef.h>
|
||||
#include <assert.h>
|
||||
|
||||
#define GET_SYSCFG_EXTI_PORT(gpio) \
|
||||
(((gpio) == (GPIOA)) ? LL_SYSCFG_EXTI_PORTA : \
|
||||
((gpio) == (GPIOB)) ? LL_SYSCFG_EXTI_PORTB : \
|
||||
((gpio) == (GPIOC)) ? LL_SYSCFG_EXTI_PORTC : \
|
||||
((gpio) == (GPIOD)) ? LL_SYSCFG_EXTI_PORTD : \
|
||||
((gpio) == (GPIOE)) ? LL_SYSCFG_EXTI_PORTE : \
|
||||
LL_SYSCFG_EXTI_PORTH)
|
||||
|
||||
#define GPIO_PIN_MAP(pin, prefix) \
|
||||
(((pin) == (LL_GPIO_PIN_0)) ? prefix##0 : \
|
||||
((pin) == (LL_GPIO_PIN_1)) ? prefix##1 : \
|
||||
((pin) == (LL_GPIO_PIN_2)) ? prefix##2 : \
|
||||
((pin) == (LL_GPIO_PIN_3)) ? prefix##3 : \
|
||||
((pin) == (LL_GPIO_PIN_4)) ? prefix##4 : \
|
||||
((pin) == (LL_GPIO_PIN_5)) ? prefix##5 : \
|
||||
((pin) == (LL_GPIO_PIN_6)) ? prefix##6 : \
|
||||
((pin) == (LL_GPIO_PIN_7)) ? prefix##7 : \
|
||||
((pin) == (LL_GPIO_PIN_8)) ? prefix##8 : \
|
||||
((pin) == (LL_GPIO_PIN_9)) ? prefix##9 : \
|
||||
((pin) == (LL_GPIO_PIN_10)) ? prefix##10 : \
|
||||
((pin) == (LL_GPIO_PIN_11)) ? prefix##11 : \
|
||||
((pin) == (LL_GPIO_PIN_12)) ? prefix##12 : \
|
||||
((pin) == (LL_GPIO_PIN_13)) ? prefix##13 : \
|
||||
((pin) == (LL_GPIO_PIN_14)) ? prefix##14 : \
|
||||
prefix##15)
|
||||
|
||||
#define GET_SYSCFG_EXTI_LINE(pin) GPIO_PIN_MAP(pin, LL_SYSCFG_EXTI_LINE)
|
||||
#define GET_EXTI_LINE(pin) GPIO_PIN_MAP(pin, LL_EXTI_LINE_)
|
||||
|
||||
static volatile GpioInterrupt gpio_interrupt[GPIO_NUMBER];
|
||||
|
||||
static uint8_t hal_gpio_get_pin_num(const GpioPin* gpio) {
|
||||
uint8_t pin_num = 0;
|
||||
for(pin_num = 0; pin_num < GPIO_NUMBER; pin_num++) {
|
||||
if(gpio->pin & (1 << pin_num)) break;
|
||||
}
|
||||
return pin_num;
|
||||
}
|
||||
|
||||
void hal_gpio_init_simple(const GpioPin* gpio, const GpioMode mode) {
|
||||
hal_gpio_init(gpio, mode, GpioPullNo, GpioSpeedLow);
|
||||
}
|
||||
|
||||
void hal_gpio_init(
|
||||
const GpioPin* gpio,
|
||||
const GpioMode mode,
|
||||
const GpioPull pull,
|
||||
const GpioSpeed speed) {
|
||||
// we cannot set alternate mode in this function
|
||||
assert(mode != GpioModeAltFunctionPushPull);
|
||||
assert(mode != GpioModeAltFunctionOpenDrain);
|
||||
|
||||
hal_gpio_init_ex(gpio, mode, pull, speed, GpioAltFnUnused);
|
||||
}
|
||||
|
||||
void hal_gpio_init_ex(
|
||||
const GpioPin* gpio,
|
||||
const GpioMode mode,
|
||||
const GpioPull pull,
|
||||
const GpioSpeed speed,
|
||||
const GpioAltFn alt_fn) {
|
||||
uint32_t sys_exti_port = GET_SYSCFG_EXTI_PORT(gpio->port);
|
||||
uint32_t sys_exti_line = GET_SYSCFG_EXTI_LINE(gpio->pin);
|
||||
uint32_t exti_line = GET_EXTI_LINE(gpio->pin);
|
||||
|
||||
// Configure gpio with interrupts disabled
|
||||
__disable_irq();
|
||||
// Set gpio speed
|
||||
if(speed == GpioSpeedLow) {
|
||||
LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_LOW);
|
||||
} else if(speed == GpioSpeedMedium) {
|
||||
LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_MEDIUM);
|
||||
} else if(speed == GpioSpeedHigh) {
|
||||
LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_HIGH);
|
||||
} else {
|
||||
LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_VERY_HIGH);
|
||||
}
|
||||
// Set gpio pull mode
|
||||
if(pull == GpioPullNo) {
|
||||
LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_NO);
|
||||
} else if(pull == GpioPullUp) {
|
||||
LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_UP);
|
||||
} else {
|
||||
LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_DOWN);
|
||||
}
|
||||
// Set gpio mode
|
||||
if(mode >= GpioModeInterruptRise) {
|
||||
// Set pin in interrupt mode
|
||||
LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_INPUT);
|
||||
LL_SYSCFG_SetEXTISource(sys_exti_port, sys_exti_line);
|
||||
if(mode == GpioModeInterruptRise || mode == GpioModeInterruptRiseFall) {
|
||||
LL_EXTI_EnableIT_0_31(exti_line);
|
||||
LL_EXTI_EnableRisingTrig_0_31(exti_line);
|
||||
}
|
||||
if(mode == GpioModeInterruptFall || mode == GpioModeInterruptRiseFall) {
|
||||
LL_EXTI_EnableIT_0_31(exti_line);
|
||||
LL_EXTI_EnableFallingTrig_0_31(exti_line);
|
||||
}
|
||||
if(mode == GpioModeEventRise || mode == GpioModeInterruptRiseFall) {
|
||||
LL_EXTI_EnableEvent_0_31(exti_line);
|
||||
LL_EXTI_EnableRisingTrig_0_31(exti_line);
|
||||
}
|
||||
if(mode == GpioModeEventFall || mode == GpioModeInterruptRiseFall) {
|
||||
LL_EXTI_EnableEvent_0_31(exti_line);
|
||||
LL_EXTI_EnableFallingTrig_0_31(exti_line);
|
||||
}
|
||||
} else {
|
||||
// Disable interrupt if it was set
|
||||
if(LL_SYSCFG_GetEXTISource(sys_exti_line) == sys_exti_port &&
|
||||
LL_EXTI_IsEnabledIT_0_31(exti_line)) {
|
||||
LL_EXTI_DisableIT_0_31(exti_line);
|
||||
LL_EXTI_DisableRisingTrig_0_31(exti_line);
|
||||
LL_EXTI_DisableFallingTrig_0_31(exti_line);
|
||||
}
|
||||
// Set not interrupt pin modes
|
||||
if(mode == GpioModeInput) {
|
||||
LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_INPUT);
|
||||
} else if(mode == GpioModeOutputPushPull || mode == GpioModeAltFunctionPushPull) {
|
||||
LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_OUTPUT);
|
||||
LL_GPIO_SetPinOutputType(gpio->port, gpio->pin, LL_GPIO_OUTPUT_PUSHPULL);
|
||||
} else if(mode == GpioModeOutputOpenDrain || mode == GpioModeAltFunctionOpenDrain) {
|
||||
LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_OUTPUT);
|
||||
LL_GPIO_SetPinOutputType(gpio->port, gpio->pin, LL_GPIO_OUTPUT_OPENDRAIN);
|
||||
} else if(mode == GpioModeAnalog) {
|
||||
LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_ANALOG);
|
||||
}
|
||||
}
|
||||
|
||||
if(mode == GpioModeAltFunctionPushPull || mode == GpioModeAltFunctionOpenDrain) {
|
||||
// enable alternate mode
|
||||
LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_ALTERNATE);
|
||||
|
||||
// set alternate function
|
||||
if(hal_gpio_get_pin_num(gpio) < 8) {
|
||||
LL_GPIO_SetAFPin_0_7(gpio->port, gpio->pin, alt_fn);
|
||||
} else {
|
||||
LL_GPIO_SetAFPin_8_15(gpio->port, gpio->pin, alt_fn);
|
||||
}
|
||||
}
|
||||
|
||||
__enable_irq();
|
||||
}
|
||||
|
||||
void hal_gpio_add_int_callback(const GpioPin* gpio, GpioExtiCallback cb, void* ctx) {
|
||||
assert(gpio);
|
||||
assert(cb);
|
||||
|
||||
__disable_irq();
|
||||
uint8_t pin_num = hal_gpio_get_pin_num(gpio);
|
||||
gpio_interrupt[pin_num].callback = cb;
|
||||
gpio_interrupt[pin_num].context = ctx;
|
||||
gpio_interrupt[pin_num].ready = true;
|
||||
__enable_irq();
|
||||
}
|
||||
|
||||
void hal_gpio_enable_int_callback(const GpioPin* gpio) {
|
||||
assert(gpio);
|
||||
|
||||
__disable_irq();
|
||||
uint8_t pin_num = hal_gpio_get_pin_num(gpio);
|
||||
if(gpio_interrupt[pin_num].callback) {
|
||||
gpio_interrupt[pin_num].ready = true;
|
||||
}
|
||||
__enable_irq();
|
||||
}
|
||||
|
||||
void hal_gpio_disable_int_callback(const GpioPin* gpio) {
|
||||
assert(gpio);
|
||||
|
||||
__disable_irq();
|
||||
uint8_t pin_num = hal_gpio_get_pin_num(gpio);
|
||||
gpio_interrupt[pin_num].ready = false;
|
||||
__enable_irq();
|
||||
}
|
||||
|
||||
void hal_gpio_remove_int_callback(const GpioPin* gpio) {
|
||||
assert(gpio);
|
||||
|
||||
__disable_irq();
|
||||
uint8_t pin_num = hal_gpio_get_pin_num(gpio);
|
||||
gpio_interrupt[pin_num].callback = NULL;
|
||||
gpio_interrupt[pin_num].context = NULL;
|
||||
gpio_interrupt[pin_num].ready = false;
|
||||
__enable_irq();
|
||||
}
|
||||
264
bootloader/targets/f7/furi-hal/furi-hal-gpio.h
Normal file
264
bootloader/targets/f7/furi-hal/furi-hal-gpio.h
Normal file
@ -0,0 +1,264 @@
|
||||
#pragma once
|
||||
#include "main.h"
|
||||
#include "stdbool.h"
|
||||
#include <stm32wbxx_ll_gpio.h>
|
||||
#include <stm32wbxx_ll_system.h>
|
||||
#include <stm32wbxx_ll_exti.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Number of gpio on one port
|
||||
*/
|
||||
#define GPIO_NUMBER (16U)
|
||||
|
||||
/**
|
||||
* Interrupt callback prototype
|
||||
*/
|
||||
typedef void (*GpioExtiCallback)(void* ctx);
|
||||
|
||||
/**
|
||||
* Gpio interrupt type
|
||||
*/
|
||||
typedef struct {
|
||||
GpioExtiCallback callback;
|
||||
void* context;
|
||||
volatile bool ready;
|
||||
} GpioInterrupt;
|
||||
|
||||
/**
|
||||
* Gpio modes
|
||||
*/
|
||||
typedef enum {
|
||||
GpioModeInput,
|
||||
GpioModeOutputPushPull,
|
||||
GpioModeOutputOpenDrain,
|
||||
GpioModeAltFunctionPushPull,
|
||||
GpioModeAltFunctionOpenDrain,
|
||||
GpioModeAnalog,
|
||||
GpioModeInterruptRise,
|
||||
GpioModeInterruptFall,
|
||||
GpioModeInterruptRiseFall,
|
||||
GpioModeEventRise,
|
||||
GpioModeEventFall,
|
||||
GpioModeEventRiseFall,
|
||||
} GpioMode;
|
||||
|
||||
/**
|
||||
* Gpio pull modes
|
||||
*/
|
||||
typedef enum {
|
||||
GpioPullNo,
|
||||
GpioPullUp,
|
||||
GpioPullDown,
|
||||
} GpioPull;
|
||||
|
||||
/**
|
||||
* Gpio speed modes
|
||||
*/
|
||||
typedef enum {
|
||||
GpioSpeedLow,
|
||||
GpioSpeedMedium,
|
||||
GpioSpeedHigh,
|
||||
GpioSpeedVeryHigh,
|
||||
} GpioSpeed;
|
||||
|
||||
/**
|
||||
* Gpio alternate functions
|
||||
*/
|
||||
typedef enum {
|
||||
GpioAltFn0MCO = 0, /*!< MCO Alternate Function mapping */
|
||||
GpioAltFn0LSCO = 0, /*!< LSCO Alternate Function mapping */
|
||||
GpioAltFn0JTMS_SWDIO = 0, /*!< JTMS-SWDIO Alternate Function mapping */
|
||||
GpioAltFn0JTCK_SWCLK = 0, /*!< JTCK-SWCLK Alternate Function mapping */
|
||||
GpioAltFn0JTDI = 0, /*!< JTDI Alternate Function mapping */
|
||||
GpioAltFn0RTC_OUT = 0, /*!< RCT_OUT Alternate Function mapping */
|
||||
GpioAltFn0JTD_TRACE = 0, /*!< JTDO-TRACESWO Alternate Function mapping */
|
||||
GpioAltFn0NJTRST = 0, /*!< NJTRST Alternate Function mapping */
|
||||
GpioAltFn0RTC_REFIN = 0, /*!< RTC_REFIN Alternate Function mapping */
|
||||
GpioAltFn0TRACED0 = 0, /*!< TRACED0 Alternate Function mapping */
|
||||
GpioAltFn0TRACED1 = 0, /*!< TRACED1 Alternate Function mapping */
|
||||
GpioAltFn0TRACED2 = 0, /*!< TRACED2 Alternate Function mapping */
|
||||
GpioAltFn0TRACED3 = 0, /*!< TRACED3 Alternate Function mapping */
|
||||
GpioAltFn0TRIG_INOUT = 0, /*!< TRIG_INOUT Alternate Function mapping */
|
||||
GpioAltFn0TRACECK = 0, /*!< TRACECK Alternate Function mapping */
|
||||
GpioAltFn0SYS = 0, /*!< System Function mapping */
|
||||
|
||||
GpioAltFn1TIM1 = 1, /*!< TIM1 Alternate Function mapping */
|
||||
GpioAltFn1TIM2 = 1, /*!< TIM2 Alternate Function mapping */
|
||||
GpioAltFn1LPTIM1 = 1, /*!< LPTIM1 Alternate Function mapping */
|
||||
|
||||
GpioAltFn2TIM2 = 2, /*!< TIM2 Alternate Function mapping */
|
||||
GpioAltFn2TIM1 = 2, /*!< TIM1 Alternate Function mapping */
|
||||
|
||||
GpioAltFn3SAI1 = 3, /*!< SAI1_CK1 Alternate Function mapping */
|
||||
GpioAltFn3SPI2 = 3, /*!< SPI2 Alternate Function mapping */
|
||||
GpioAltFn3TIM1 = 3, /*!< TIM1 Alternate Function mapping */
|
||||
|
||||
GpioAltFn4I2C1 = 4, /*!< I2C1 Alternate Function mapping */
|
||||
GpioAltFn4I2C3 = 4, /*!< I2C3 Alternate Function mapping */
|
||||
|
||||
GpioAltFn5SPI1 = 5, /*!< SPI1 Alternate Function mapping */
|
||||
GpioAltFn5SPI2 = 5, /*!< SPI2 Alternate Function mapping */
|
||||
|
||||
GpioAltFn6MCO = 6, /*!< MCO Alternate Function mapping */
|
||||
GpioAltFn6LSCO = 6, /*!< LSCO Alternate Function mapping */
|
||||
GpioAltFn6RF_DTB0 = 6, /*!< RF_DTB0 Alternate Function mapping */
|
||||
GpioAltFn6RF_DTB1 = 6, /*!< RF_DTB1 Alternate Function mapping */
|
||||
GpioAltFn6RF_DTB2 = 6, /*!< RF_DTB2 Alternate Function mapping */
|
||||
GpioAltFn6RF_DTB3 = 6, /*!< RF_DTB3 Alternate Function mapping */
|
||||
GpioAltFn6RF_DTB4 = 6, /*!< RF_DTB4 Alternate Function mapping */
|
||||
GpioAltFn6RF_DTB5 = 6, /*!< RF_DTB5 Alternate Function mapping */
|
||||
GpioAltFn6RF_DTB6 = 6, /*!< RF_DTB6 Alternate Function mapping */
|
||||
GpioAltFn6RF_DTB7 = 6, /*!< RF_DTB7 Alternate Function mapping */
|
||||
GpioAltFn6RF_DTB8 = 6, /*!< RF_DTB8 Alternate Function mapping */
|
||||
GpioAltFn6RF_DTB9 = 6, /*!< RF_DTB9 Alternate Function mapping */
|
||||
GpioAltFn6RF_DTB10 = 6, /*!< RF_DTB10 Alternate Function mapping */
|
||||
GpioAltFn6RF_DTB11 = 6, /*!< RF_DTB11 Alternate Function mapping */
|
||||
GpioAltFn6RF_DTB12 = 6, /*!< RF_DTB12 Alternate Function mapping */
|
||||
GpioAltFn6RF_DTB13 = 6, /*!< RF_DTB13 Alternate Function mapping */
|
||||
GpioAltFn6RF_DTB14 = 6, /*!< RF_DTB14 Alternate Function mapping */
|
||||
GpioAltFn6RF_DTB15 = 6, /*!< RF_DTB15 Alternate Function mapping */
|
||||
GpioAltFn6RF_DTB16 = 6, /*!< RF_DTB16 Alternate Function mapping */
|
||||
GpioAltFn6RF_DTB17 = 6, /*!< RF_DTB17 Alternate Function mapping */
|
||||
GpioAltFn6RF_DTB18 = 6, /*!< RF_DTB18 Alternate Function mapping */
|
||||
GpioAltFn6RF_MISO = 6, /*!< RF_MISO Alternate Function mapping */
|
||||
GpioAltFn6RF_MOSI = 6, /*!< RF_MOSI Alternate Function mapping */
|
||||
GpioAltFn6RF_SCK = 6, /*!< RF_SCK Alternate Function mapping */
|
||||
GpioAltFn6RF_NSS = 6, /*!< RF_NSS Alternate Function mapping */
|
||||
|
||||
GpioAltFn7USART1 = 7, /*!< USART1 Alternate Function mapping */
|
||||
|
||||
GpioAltFn8LPUART1 = 8, /*!< LPUART1 Alternate Function mapping */
|
||||
GpioAltFn8IR = 8, /*!< IR Alternate Function mapping */
|
||||
|
||||
GpioAltFn9TSC = 9, /*!< TSC Alternate Function mapping */
|
||||
|
||||
GpioAltFn10QUADSPI = 10, /*!< QUADSPI Alternate Function mapping */
|
||||
GpioAltFn10USB = 10, /*!< USB Alternate Function mapping */
|
||||
|
||||
GpioAltFn11LCD = 11, /*!< LCD Alternate Function mapping */
|
||||
|
||||
GpioAltFn12COMP1 = 12, /*!< COMP1 Alternate Function mapping */
|
||||
GpioAltFn12COMP2 = 12, /*!< COMP2 Alternate Function mapping */
|
||||
GpioAltFn12TIM1 = 12, /*!< TIM1 Alternate Function mapping */
|
||||
|
||||
GpioAltFn13SAI1 = 13, /*!< SAI1 Alternate Function mapping */
|
||||
|
||||
GpioAltFn14TIM2 = 14, /*!< TIM2 Alternate Function mapping */
|
||||
GpioAltFn14TIM16 = 14, /*!< TIM16 Alternate Function mapping */
|
||||
GpioAltFn14TIM17 = 14, /*!< TIM17 Alternate Function mapping */
|
||||
GpioAltFn14LPTIM2 = 14, /*!< LPTIM2 Alternate Function mapping */
|
||||
|
||||
GpioAltFn15EVENTOUT = 15, /*!< EVENTOUT Alternate Function mapping */
|
||||
|
||||
GpioAltFnUnused = 16, /*!< just dummy value */
|
||||
} GpioAltFn;
|
||||
|
||||
/**
|
||||
* Gpio structure
|
||||
*/
|
||||
typedef struct {
|
||||
GPIO_TypeDef* port;
|
||||
uint16_t pin;
|
||||
} GpioPin;
|
||||
|
||||
/**
|
||||
* GPIO initialization function, simple version
|
||||
* @param gpio GpioPin
|
||||
* @param mode GpioMode
|
||||
*/
|
||||
void hal_gpio_init_simple(const GpioPin* gpio, const GpioMode mode);
|
||||
|
||||
/**
|
||||
* GPIO initialization function, normal version
|
||||
* @param gpio GpioPin
|
||||
* @param mode GpioMode
|
||||
* @param pull GpioPull
|
||||
* @param speed GpioSpeed
|
||||
*/
|
||||
void hal_gpio_init(
|
||||
const GpioPin* gpio,
|
||||
const GpioMode mode,
|
||||
const GpioPull pull,
|
||||
const GpioSpeed speed);
|
||||
|
||||
/**
|
||||
* GPIO initialization function, extended version
|
||||
* @param gpio GpioPin
|
||||
* @param mode GpioMode
|
||||
* @param pull GpioPull
|
||||
* @param speed GpioSpeed
|
||||
* @param alt_fn GpioAltFn
|
||||
*/
|
||||
void hal_gpio_init_ex(
|
||||
const GpioPin* gpio,
|
||||
const GpioMode mode,
|
||||
const GpioPull pull,
|
||||
const GpioSpeed speed,
|
||||
const GpioAltFn alt_fn);
|
||||
|
||||
/**
|
||||
* Add and enable interrupt
|
||||
* @param gpio GpioPin
|
||||
* @param cb GpioExtiCallback
|
||||
* @param ctx context for callback
|
||||
*/
|
||||
void hal_gpio_add_int_callback(const GpioPin* gpio, GpioExtiCallback cb, void* ctx);
|
||||
|
||||
/**
|
||||
* Enable interrupt
|
||||
* @param gpio GpioPin
|
||||
*/
|
||||
void hal_gpio_enable_int_callback(const GpioPin* gpio);
|
||||
|
||||
/**
|
||||
* Disable interrupt
|
||||
* @param gpio GpioPin
|
||||
*/
|
||||
void hal_gpio_disable_int_callback(const GpioPin* gpio);
|
||||
|
||||
/**
|
||||
* Remove interrupt
|
||||
* @param gpio GpioPin
|
||||
*/
|
||||
void hal_gpio_remove_int_callback(const GpioPin* gpio);
|
||||
|
||||
/**
|
||||
* GPIO write pin
|
||||
* @param gpio GpioPin
|
||||
* @param state true / false
|
||||
*/
|
||||
static inline void hal_gpio_write(const GpioPin* gpio, const bool state) {
|
||||
// writing to BSSR is an atomic operation
|
||||
if(state == true) {
|
||||
gpio->port->BSRR = gpio->pin;
|
||||
} else {
|
||||
gpio->port->BSRR = (uint32_t)gpio->pin << GPIO_NUMBER;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* GPIO read pin
|
||||
* @param gpio GpioPin
|
||||
* @return true / false
|
||||
*/
|
||||
static inline bool hal_gpio_read(const GpioPin* gpio) {
|
||||
if((gpio->port->IDR & gpio->pin) != 0x00U) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get RFID IN level
|
||||
* @return false = LOW, true = HIGH
|
||||
*/
|
||||
bool hal_gpio_get_rfid_in_level();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
137
bootloader/targets/f7/furi-hal/furi-hal-i2c.c
Normal file
137
bootloader/targets/f7/furi-hal/furi-hal-i2c.c
Normal file
@ -0,0 +1,137 @@
|
||||
#include <furi-hal-i2c.h>
|
||||
|
||||
#include <stm32wbxx_ll_bus.h>
|
||||
#include <stm32wbxx_ll_i2c.h>
|
||||
#include <stm32wbxx_ll_rcc.h>
|
||||
#include <stm32wbxx_ll_gpio.h>
|
||||
#include <stm32wbxx_ll_cortex.h>
|
||||
|
||||
void furi_hal_i2c_init() {
|
||||
LL_I2C_InitTypeDef I2C_InitStruct = {0};
|
||||
LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
|
||||
|
||||
LL_RCC_SetI2CClockSource(LL_RCC_I2C1_CLKSOURCE_PCLK1);
|
||||
|
||||
LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOA);
|
||||
GPIO_InitStruct.Pin = POWER_I2C_SCL_Pin | POWER_I2C_SDA_Pin;
|
||||
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
|
||||
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
|
||||
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_OPENDRAIN;
|
||||
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
|
||||
GPIO_InitStruct.Alternate = LL_GPIO_AF_4;
|
||||
LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
|
||||
|
||||
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_I2C1);
|
||||
|
||||
I2C_InitStruct.PeripheralMode = LL_I2C_MODE_I2C;
|
||||
I2C_InitStruct.Timing = POWER_I2C_TIMINGS;
|
||||
I2C_InitStruct.AnalogFilter = LL_I2C_ANALOGFILTER_ENABLE;
|
||||
I2C_InitStruct.DigitalFilter = 0;
|
||||
I2C_InitStruct.OwnAddress1 = 0;
|
||||
I2C_InitStruct.TypeAcknowledge = LL_I2C_ACK;
|
||||
I2C_InitStruct.OwnAddrSize = LL_I2C_OWNADDRESS1_7BIT;
|
||||
LL_I2C_Init(I2C1, &I2C_InitStruct);
|
||||
LL_I2C_EnableAutoEndMode(I2C1);
|
||||
LL_I2C_SetOwnAddress2(I2C1, 0, LL_I2C_OWNADDRESS2_NOMASK);
|
||||
LL_I2C_DisableOwnAddress2(I2C1);
|
||||
LL_I2C_DisableGeneralCall(I2C1);
|
||||
LL_I2C_EnableClockStretching(I2C1);
|
||||
}
|
||||
|
||||
bool furi_hal_i2c_tx(
|
||||
I2C_TypeDef* instance,
|
||||
uint8_t address,
|
||||
const uint8_t* data,
|
||||
uint8_t size,
|
||||
uint32_t timeout) {
|
||||
uint32_t time_left = timeout;
|
||||
bool ret = true;
|
||||
|
||||
while(LL_I2C_IsActiveFlag_BUSY(instance))
|
||||
;
|
||||
|
||||
LL_I2C_HandleTransfer(
|
||||
instance,
|
||||
address,
|
||||
LL_I2C_ADDRSLAVE_7BIT,
|
||||
size,
|
||||
LL_I2C_MODE_AUTOEND,
|
||||
LL_I2C_GENERATE_START_WRITE);
|
||||
|
||||
while(!LL_I2C_IsActiveFlag_STOP(instance) || size > 0) {
|
||||
if(LL_I2C_IsActiveFlag_TXIS(instance)) {
|
||||
LL_I2C_TransmitData8(instance, (*data));
|
||||
data++;
|
||||
size--;
|
||||
time_left = timeout;
|
||||
}
|
||||
|
||||
if(LL_SYSTICK_IsActiveCounterFlag()) {
|
||||
if(--time_left == 0) {
|
||||
ret = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LL_I2C_ClearFlag_STOP(instance);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool furi_hal_i2c_rx(
|
||||
I2C_TypeDef* instance,
|
||||
uint8_t address,
|
||||
uint8_t* data,
|
||||
uint8_t size,
|
||||
uint32_t timeout) {
|
||||
uint32_t time_left = timeout;
|
||||
bool ret = true;
|
||||
|
||||
while(LL_I2C_IsActiveFlag_BUSY(instance))
|
||||
;
|
||||
|
||||
LL_I2C_HandleTransfer(
|
||||
instance,
|
||||
address,
|
||||
LL_I2C_ADDRSLAVE_7BIT,
|
||||
size,
|
||||
LL_I2C_MODE_AUTOEND,
|
||||
LL_I2C_GENERATE_START_READ);
|
||||
|
||||
while(!LL_I2C_IsActiveFlag_STOP(instance) || size > 0) {
|
||||
if(LL_I2C_IsActiveFlag_RXNE(instance)) {
|
||||
*data = LL_I2C_ReceiveData8(instance);
|
||||
data++;
|
||||
size--;
|
||||
time_left = timeout;
|
||||
}
|
||||
|
||||
if(LL_SYSTICK_IsActiveCounterFlag()) {
|
||||
if(--time_left == 0) {
|
||||
ret = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LL_I2C_ClearFlag_STOP(instance);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool furi_hal_i2c_trx(
|
||||
I2C_TypeDef* instance,
|
||||
uint8_t address,
|
||||
const uint8_t* tx_data,
|
||||
uint8_t tx_size,
|
||||
uint8_t* rx_data,
|
||||
uint8_t rx_size,
|
||||
uint32_t timeout) {
|
||||
if(furi_hal_i2c_tx(instance, address, tx_data, tx_size, timeout) &&
|
||||
furi_hal_i2c_rx(instance, address, rx_data, rx_size, timeout)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
43
bootloader/targets/f7/furi-hal/furi-hal-i2c.h
Normal file
43
bootloader/targets/f7/furi-hal/furi-hal-i2c.h
Normal file
@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <furi-hal-resources.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void furi_hal_i2c_init();
|
||||
|
||||
bool furi_hal_i2c_tx(
|
||||
I2C_TypeDef* instance,
|
||||
const uint8_t address,
|
||||
const uint8_t* data,
|
||||
const uint8_t size,
|
||||
uint32_t timeout);
|
||||
|
||||
bool furi_hal_i2c_rx(
|
||||
I2C_TypeDef* instance,
|
||||
const uint8_t address,
|
||||
uint8_t* data,
|
||||
const uint8_t size,
|
||||
uint32_t timeout);
|
||||
|
||||
bool furi_hal_i2c_trx(
|
||||
I2C_TypeDef* instance,
|
||||
const uint8_t address,
|
||||
const uint8_t* tx_data,
|
||||
const uint8_t tx_size,
|
||||
uint8_t* rx_data,
|
||||
const uint8_t rx_size,
|
||||
uint32_t timeout);
|
||||
|
||||
#define with_furi_hal_i2c(type, pointer, function_body) \
|
||||
{ \
|
||||
*pointer = ({ type __fn__ function_body __fn__; })(); \
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
43
bootloader/targets/f7/furi-hal/furi-hal-light.c
Normal file
43
bootloader/targets/f7/furi-hal/furi-hal-light.c
Normal file
@ -0,0 +1,43 @@
|
||||
#include <furi-hal-light.h>
|
||||
#include <lp5562.h>
|
||||
|
||||
#define LED_CURRENT_RED 50
|
||||
#define LED_CURRENT_GREEN 50
|
||||
#define LED_CURRENT_BLUE 50
|
||||
#define LED_CURRENT_WHITE 150
|
||||
|
||||
void furi_hal_light_init() {
|
||||
lp5562_reset();
|
||||
|
||||
lp5562_set_channel_current(LP5562ChannelRed, LED_CURRENT_RED);
|
||||
lp5562_set_channel_current(LP5562ChannelGreen, LED_CURRENT_GREEN);
|
||||
lp5562_set_channel_current(LP5562ChannelBlue, LED_CURRENT_BLUE);
|
||||
lp5562_set_channel_current(LP5562ChannelWhite, LED_CURRENT_WHITE);
|
||||
|
||||
lp5562_set_channel_value(LP5562ChannelRed, 0x00);
|
||||
lp5562_set_channel_value(LP5562ChannelGreen, 0x00);
|
||||
lp5562_set_channel_value(LP5562ChannelBlue, 0x00);
|
||||
lp5562_set_channel_value(LP5562ChannelWhite, 0x00);
|
||||
|
||||
lp5562_enable();
|
||||
lp5562_configure();
|
||||
}
|
||||
|
||||
void furi_hal_light_set(Light light, uint8_t value) {
|
||||
switch(light) {
|
||||
case LightRed:
|
||||
lp5562_set_channel_value(LP5562ChannelRed, value);
|
||||
break;
|
||||
case LightGreen:
|
||||
lp5562_set_channel_value(LP5562ChannelGreen, value);
|
||||
break;
|
||||
case LightBlue:
|
||||
lp5562_set_channel_value(LP5562ChannelBlue, value);
|
||||
break;
|
||||
case LightBacklight:
|
||||
lp5562_set_channel_value(LP5562ChannelWhite, value);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
17
bootloader/targets/f7/furi-hal/furi-hal-light.h
Normal file
17
bootloader/targets/f7/furi-hal/furi-hal-light.h
Normal file
@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <furi-hal-resources.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void furi_hal_light_init();
|
||||
|
||||
void furi_hal_light_set(Light light, uint8_t value);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
41
bootloader/targets/f7/furi-hal/furi-hal-resources.c
Normal file
41
bootloader/targets/f7/furi-hal/furi-hal-resources.c
Normal file
@ -0,0 +1,41 @@
|
||||
#include "furi-hal-resources.h"
|
||||
#include "main.h"
|
||||
|
||||
const GpioPin vibro_gpio = {.port = VIBRO_GPIO_Port, .pin = VIBRO_Pin};
|
||||
const GpioPin ibutton_gpio = {.port = iBTN_GPIO_Port, .pin = iBTN_Pin};
|
||||
|
||||
const GpioPin gpio_cc1101_g0 = {.port = CC1101_G0_GPIO_Port, .pin = CC1101_G0_Pin};
|
||||
const GpioPin gpio_rf_sw_0 = {.port = RF_SW_0_GPIO_Port, .pin = RF_SW_0_Pin};
|
||||
|
||||
const GpioPin gpio_subghz_cs = {.port = CC1101_CS_GPIO_Port, .pin = CC1101_CS_Pin};
|
||||
const GpioPin gpio_display_cs = {.port = DISPLAY_CS_GPIO_Port, .pin = DISPLAY_CS_Pin};
|
||||
const GpioPin gpio_display_rst = {.port = DISPLAY_RST_GPIO_Port, .pin = DISPLAY_RST_Pin};
|
||||
const GpioPin gpio_display_di = {.port = DISPLAY_DI_GPIO_Port, .pin = DISPLAY_DI_Pin};
|
||||
const GpioPin gpio_sdcard_cs = {.port = SD_CS_GPIO_Port, .pin = SD_CS_Pin};
|
||||
const GpioPin gpio_nfc_cs = {.port = NFC_CS_GPIO_Port, .pin = NFC_CS_Pin};
|
||||
|
||||
const GpioPin gpio_spi_d_miso = {.port = SPI_D_MISO_GPIO_Port, .pin = SPI_D_MISO_Pin};
|
||||
const GpioPin gpio_spi_d_mosi = {.port = SPI_D_MOSI_GPIO_Port, .pin = SPI_D_MOSI_Pin};
|
||||
const GpioPin gpio_spi_d_sck = {.port = SPI_D_SCK_GPIO_Port, .pin = SPI_D_SCK_Pin};
|
||||
const GpioPin gpio_spi_r_miso = {.port = SPI_R_MISO_GPIO_Port, .pin = SPI_R_MISO_Pin};
|
||||
const GpioPin gpio_spi_r_mosi = {.port = SPI_R_MOSI_GPIO_Port, .pin = SPI_R_MOSI_Pin};
|
||||
const GpioPin gpio_spi_r_sck = {.port = SPI_R_SCK_GPIO_Port, .pin = SPI_R_SCK_Pin};
|
||||
|
||||
const GpioPin gpio_ext_pc0 = {.port = GPIOC, .pin = LL_GPIO_PIN_0};
|
||||
const GpioPin gpio_ext_pc1 = {.port = GPIOC, .pin = LL_GPIO_PIN_1};
|
||||
const GpioPin gpio_ext_pc3 = {.port = GPIOC, .pin = LL_GPIO_PIN_3};
|
||||
const GpioPin gpio_ext_pb2 = {.port = GPIOB, .pin = LL_GPIO_PIN_2};
|
||||
const GpioPin gpio_ext_pb3 = {.port = GPIOB, .pin = LL_GPIO_PIN_3};
|
||||
const GpioPin gpio_ext_pa4 = {.port = GPIOA, .pin = LL_GPIO_PIN_4};
|
||||
const GpioPin gpio_ext_pa6 = {.port = GPIOA, .pin = LL_GPIO_PIN_6};
|
||||
const GpioPin gpio_ext_pa7 = {.port = GPIOA, .pin = LL_GPIO_PIN_7};
|
||||
|
||||
const GpioPin gpio_rfid_pull = {.port = RFID_PULL_GPIO_Port, .pin = RFID_PULL_Pin};
|
||||
const GpioPin gpio_rfid_carrier_out = {.port = RFID_OUT_GPIO_Port, .pin = RFID_OUT_Pin};
|
||||
const GpioPin gpio_rfid_data_in = {.port = RFID_RF_IN_GPIO_Port, .pin = RFID_RF_IN_Pin};
|
||||
|
||||
const GpioPin gpio_irda_rx = {.port = IR_RX_GPIO_Port, .pin = IR_RX_Pin};
|
||||
const GpioPin gpio_irda_tx = {.port = IR_TX_GPIO_Port, .pin = IR_TX_Pin};
|
||||
|
||||
const GpioPin gpio_usart_tx = {.port = USART1_TX_Port, .pin = USART1_TX_Pin};
|
||||
const GpioPin gpio_usart_rx = {.port = USART1_RX_Port, .pin = USART1_RX_Pin};
|
||||
82
bootloader/targets/f7/furi-hal/furi-hal-resources.h
Normal file
82
bootloader/targets/f7/furi-hal/furi-hal-resources.h
Normal file
@ -0,0 +1,82 @@
|
||||
#pragma once
|
||||
|
||||
#include <stm32wbxx.h>
|
||||
#include <stm32wbxx_ll_gpio.h>
|
||||
#include <furi-hal-gpio.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define POWER_I2C_SCL_Pin LL_GPIO_PIN_9
|
||||
#define POWER_I2C_SCL_GPIO_Port GPIOA
|
||||
#define POWER_I2C_SDA_Pin LL_GPIO_PIN_10
|
||||
#define POWER_I2C_SDA_GPIO_Port GPIOA
|
||||
|
||||
#define POWER_I2C I2C1
|
||||
/* Timing register value is computed with the STM32CubeMX Tool,
|
||||
* Fast Mode @100kHz with I2CCLK = 64 MHz,
|
||||
* rise time = 0ns, fall time = 0ns
|
||||
*/
|
||||
#define POWER_I2C_TIMINGS 0x10707DBC
|
||||
|
||||
/* Input Keys */
|
||||
typedef enum {
|
||||
InputKeyUp,
|
||||
InputKeyDown,
|
||||
InputKeyRight,
|
||||
InputKeyLeft,
|
||||
InputKeyOk,
|
||||
InputKeyBack,
|
||||
} InputKey;
|
||||
|
||||
/* Light */
|
||||
typedef enum {
|
||||
LightRed,
|
||||
LightGreen,
|
||||
LightBlue,
|
||||
LightBacklight,
|
||||
} Light;
|
||||
|
||||
extern const GpioPin vibro_gpio;
|
||||
extern const GpioPin ibutton_gpio;
|
||||
|
||||
extern const GpioPin gpio_cc1101_g0;
|
||||
extern const GpioPin gpio_rf_sw_0;
|
||||
|
||||
extern const GpioPin gpio_subghz_cs;
|
||||
extern const GpioPin gpio_display_cs;
|
||||
extern const GpioPin gpio_display_rst;
|
||||
extern const GpioPin gpio_display_di;
|
||||
extern const GpioPin gpio_sdcard_cs;
|
||||
extern const GpioPin gpio_nfc_cs;
|
||||
|
||||
extern const GpioPin gpio_spi_d_miso;
|
||||
extern const GpioPin gpio_spi_d_mosi;
|
||||
extern const GpioPin gpio_spi_d_sck;
|
||||
extern const GpioPin gpio_spi_r_miso;
|
||||
extern const GpioPin gpio_spi_r_mosi;
|
||||
extern const GpioPin gpio_spi_r_sck;
|
||||
|
||||
extern const GpioPin gpio_ext_pc0;
|
||||
extern const GpioPin gpio_ext_pc1;
|
||||
extern const GpioPin gpio_ext_pc3;
|
||||
extern const GpioPin gpio_ext_pb2;
|
||||
extern const GpioPin gpio_ext_pb3;
|
||||
extern const GpioPin gpio_ext_pa4;
|
||||
extern const GpioPin gpio_ext_pa6;
|
||||
extern const GpioPin gpio_ext_pa7;
|
||||
|
||||
extern const GpioPin gpio_rfid_pull;
|
||||
extern const GpioPin gpio_rfid_carrier_out;
|
||||
extern const GpioPin gpio_rfid_data_in;
|
||||
|
||||
extern const GpioPin gpio_irda_rx;
|
||||
extern const GpioPin gpio_irda_tx;
|
||||
|
||||
extern const GpioPin gpio_usart_tx;
|
||||
extern const GpioPin gpio_usart_rx;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
114
bootloader/targets/f7/furi-hal/furi-hal-spi-config.c
Normal file
114
bootloader/targets/f7/furi-hal/furi-hal-spi-config.c
Normal file
@ -0,0 +1,114 @@
|
||||
#include <furi-hal-spi-config.h>
|
||||
#include <furi-hal-resources.h>
|
||||
|
||||
#define SPI_R SPI1
|
||||
#define SPI_D SPI2
|
||||
|
||||
const LL_SPI_InitTypeDef furi_hal_spi_config_nfc = {
|
||||
.Mode = LL_SPI_MODE_MASTER,
|
||||
.TransferDirection = LL_SPI_FULL_DUPLEX,
|
||||
.DataWidth = LL_SPI_DATAWIDTH_8BIT,
|
||||
.ClockPolarity = LL_SPI_POLARITY_LOW,
|
||||
.ClockPhase = LL_SPI_PHASE_2EDGE,
|
||||
.NSS = LL_SPI_NSS_SOFT,
|
||||
.BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV8,
|
||||
.BitOrder = LL_SPI_MSB_FIRST,
|
||||
.CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE,
|
||||
.CRCPoly = 7,
|
||||
};
|
||||
|
||||
const LL_SPI_InitTypeDef furi_hal_spi_config_subghz = {
|
||||
.Mode = LL_SPI_MODE_MASTER,
|
||||
.TransferDirection = LL_SPI_FULL_DUPLEX,
|
||||
.DataWidth = LL_SPI_DATAWIDTH_8BIT,
|
||||
.ClockPolarity = LL_SPI_POLARITY_LOW,
|
||||
.ClockPhase = LL_SPI_PHASE_1EDGE,
|
||||
.NSS = LL_SPI_NSS_SOFT,
|
||||
.BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV8,
|
||||
.BitOrder = LL_SPI_MSB_FIRST,
|
||||
.CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE,
|
||||
.CRCPoly = 7,
|
||||
};
|
||||
|
||||
const LL_SPI_InitTypeDef furi_hal_spi_config_display = {
|
||||
.Mode = LL_SPI_MODE_MASTER,
|
||||
.TransferDirection = LL_SPI_FULL_DUPLEX,
|
||||
.DataWidth = LL_SPI_DATAWIDTH_8BIT,
|
||||
.ClockPolarity = LL_SPI_POLARITY_LOW,
|
||||
.ClockPhase = LL_SPI_PHASE_1EDGE,
|
||||
.NSS = LL_SPI_NSS_SOFT,
|
||||
.BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV16,
|
||||
.BitOrder = LL_SPI_MSB_FIRST,
|
||||
.CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE,
|
||||
.CRCPoly = 7,
|
||||
};
|
||||
|
||||
/**
|
||||
* SD Card in fast mode (after init)
|
||||
*/
|
||||
const LL_SPI_InitTypeDef furi_hal_spi_config_sd_fast = {
|
||||
.Mode = LL_SPI_MODE_MASTER,
|
||||
.TransferDirection = LL_SPI_FULL_DUPLEX,
|
||||
.DataWidth = LL_SPI_DATAWIDTH_8BIT,
|
||||
.ClockPolarity = LL_SPI_POLARITY_LOW,
|
||||
.ClockPhase = LL_SPI_PHASE_1EDGE,
|
||||
.NSS = LL_SPI_NSS_SOFT,
|
||||
.BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV2,
|
||||
.BitOrder = LL_SPI_MSB_FIRST,
|
||||
.CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE,
|
||||
.CRCPoly = 7,
|
||||
};
|
||||
|
||||
/**
|
||||
* SD Card in slow mode (before init)
|
||||
*/
|
||||
const LL_SPI_InitTypeDef furi_hal_spi_config_sd_slow = {
|
||||
.Mode = LL_SPI_MODE_MASTER,
|
||||
.TransferDirection = LL_SPI_FULL_DUPLEX,
|
||||
.DataWidth = LL_SPI_DATAWIDTH_8BIT,
|
||||
.ClockPolarity = LL_SPI_POLARITY_LOW,
|
||||
.ClockPhase = LL_SPI_PHASE_1EDGE,
|
||||
.NSS = LL_SPI_NSS_SOFT,
|
||||
.BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV32,
|
||||
.BitOrder = LL_SPI_MSB_FIRST,
|
||||
.CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE,
|
||||
.CRCPoly = 7,
|
||||
};
|
||||
|
||||
const FuriHalSpiBus spi_r = {
|
||||
.spi = SPI_R,
|
||||
.miso = &gpio_spi_r_miso,
|
||||
.mosi = &gpio_spi_r_mosi,
|
||||
.clk = &gpio_spi_r_sck,
|
||||
};
|
||||
|
||||
const FuriHalSpiBus spi_d = {
|
||||
.spi = SPI_D,
|
||||
.miso = &gpio_spi_d_miso,
|
||||
.mosi = &gpio_spi_d_mosi,
|
||||
.clk = &gpio_spi_d_sck,
|
||||
};
|
||||
|
||||
const FuriHalSpiDevice furi_hal_spi_devices[FuriHalSpiDeviceIdMax] = {
|
||||
{
|
||||
.bus = &spi_r,
|
||||
.config = &furi_hal_spi_config_subghz,
|
||||
.chip_select = &gpio_subghz_cs,
|
||||
},
|
||||
{
|
||||
.bus = &spi_d,
|
||||
.config = &furi_hal_spi_config_display,
|
||||
.chip_select = &gpio_display_cs,
|
||||
},
|
||||
{
|
||||
.bus = &spi_d,
|
||||
.config = &furi_hal_spi_config_sd_fast,
|
||||
.chip_select = &gpio_sdcard_cs,
|
||||
},
|
||||
{
|
||||
.bus = &spi_d,
|
||||
.config = &furi_hal_spi_config_sd_slow,
|
||||
.chip_select = &gpio_sdcard_cs,
|
||||
},
|
||||
{.bus = &spi_r, .config = &furi_hal_spi_config_nfc, .chip_select = &gpio_nfc_cs},
|
||||
};
|
||||
61
bootloader/targets/f7/furi-hal/furi-hal-spi-config.h
Normal file
61
bootloader/targets/f7/furi-hal/furi-hal-spi-config.h
Normal file
@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
|
||||
#include <furi-hal-gpio.h>
|
||||
#include <stm32wbxx_ll_spi.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern const LL_SPI_InitTypeDef furi_hal_spi_config_nfc;
|
||||
extern const LL_SPI_InitTypeDef furi_hal_spi_config_subghz;
|
||||
extern const LL_SPI_InitTypeDef furi_hal_spi_config_display;
|
||||
extern const LL_SPI_InitTypeDef furi_hal_spi_config_sd_fast;
|
||||
extern const LL_SPI_InitTypeDef furi_hal_spi_config_sd_slow;
|
||||
|
||||
/** FURI HAL SPI BUS handler
|
||||
* Structure content may change at some point
|
||||
*/
|
||||
typedef struct {
|
||||
const SPI_TypeDef* spi;
|
||||
const GpioPin* miso;
|
||||
const GpioPin* mosi;
|
||||
const GpioPin* clk;
|
||||
} FuriHalSpiBus;
|
||||
|
||||
/** FURI HAL SPI Device handler
|
||||
* Structure content may change at some point
|
||||
*/
|
||||
typedef struct {
|
||||
const FuriHalSpiBus* bus;
|
||||
const LL_SPI_InitTypeDef* config;
|
||||
const GpioPin* chip_select;
|
||||
} FuriHalSpiDevice;
|
||||
|
||||
/** FURI HAL SPI Standard Device IDs */
|
||||
typedef enum {
|
||||
FuriHalSpiDeviceIdSubGhz, /** SubGhz: CC1101, non-standard SPI usage */
|
||||
FuriHalSpiDeviceIdDisplay, /** Display: ERC12864, only have MOSI */
|
||||
FuriHalSpiDeviceIdSdCardFast, /** SDCARD: fast mode, after initialization */
|
||||
FuriHalSpiDeviceIdSdCardSlow, /** SDCARD: slow mode, before initialization */
|
||||
FuriHalSpiDeviceIdNfc, /** NFC: ST25R3916, pretty standard, but RFAL makes it complex */
|
||||
|
||||
FuriHalSpiDeviceIdMax, /** Service Value, do not use */
|
||||
} FuriHalSpiDeviceId;
|
||||
|
||||
/** Furi Hal Spi Bus R
|
||||
* CC1101, Nfc
|
||||
*/
|
||||
extern const FuriHalSpiBus spi_r;
|
||||
|
||||
/** Furi Hal Spi Bus D
|
||||
* Display, SdCard
|
||||
*/
|
||||
extern const FuriHalSpiBus spi_d;
|
||||
|
||||
/** Furi Hal Spi devices */
|
||||
extern const FuriHalSpiDevice furi_hal_spi_devices[FuriHalSpiDeviceIdMax];
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
240
bootloader/targets/f7/furi-hal/furi-hal-spi.c
Normal file
240
bootloader/targets/f7/furi-hal/furi-hal-spi.c
Normal file
@ -0,0 +1,240 @@
|
||||
#include "furi-hal-spi.h"
|
||||
#include "furi-hal-resources.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <stm32wbxx_ll_spi.h>
|
||||
#include <stm32wbxx_ll_utils.h>
|
||||
#include <stm32wbxx_ll_cortex.h>
|
||||
|
||||
extern void Enable_SPI(SPI_TypeDef* spi);
|
||||
|
||||
void furi_hal_spi_init() {
|
||||
for(size_t i = 0; i < FuriHalSpiDeviceIdMax; ++i) {
|
||||
hal_gpio_write(furi_hal_spi_devices[i].chip_select, true);
|
||||
hal_gpio_init(
|
||||
furi_hal_spi_devices[i].chip_select,
|
||||
GpioModeOutputPushPull,
|
||||
GpioPullNo,
|
||||
GpioSpeedVeryHigh);
|
||||
}
|
||||
|
||||
hal_gpio_init_ex(
|
||||
&gpio_spi_r_miso,
|
||||
GpioModeAltFunctionPushPull,
|
||||
GpioPullNo,
|
||||
GpioSpeedVeryHigh,
|
||||
GpioAltFn5SPI1);
|
||||
hal_gpio_init_ex(
|
||||
&gpio_spi_r_mosi,
|
||||
GpioModeAltFunctionPushPull,
|
||||
GpioPullNo,
|
||||
GpioSpeedVeryHigh,
|
||||
GpioAltFn5SPI1);
|
||||
hal_gpio_init_ex(
|
||||
&gpio_spi_r_sck,
|
||||
GpioModeAltFunctionPushPull,
|
||||
GpioPullNo,
|
||||
GpioSpeedVeryHigh,
|
||||
GpioAltFn5SPI1);
|
||||
|
||||
hal_gpio_init_ex(
|
||||
&gpio_spi_d_miso,
|
||||
GpioModeAltFunctionPushPull,
|
||||
GpioPullUp,
|
||||
GpioSpeedVeryHigh,
|
||||
GpioAltFn5SPI2);
|
||||
hal_gpio_init_ex(
|
||||
&gpio_spi_d_mosi,
|
||||
GpioModeAltFunctionPushPull,
|
||||
GpioPullUp,
|
||||
GpioSpeedVeryHigh,
|
||||
GpioAltFn5SPI2);
|
||||
hal_gpio_init_ex(
|
||||
&gpio_spi_d_sck,
|
||||
GpioModeAltFunctionPushPull,
|
||||
GpioPullUp,
|
||||
GpioSpeedVeryHigh,
|
||||
GpioAltFn5SPI2);
|
||||
}
|
||||
|
||||
void furi_hal_spi_bus_lock(const FuriHalSpiBus* bus) {
|
||||
assert(bus);
|
||||
}
|
||||
|
||||
void furi_hal_spi_bus_unlock(const FuriHalSpiBus* bus) {
|
||||
assert(bus);
|
||||
}
|
||||
|
||||
void furi_hal_spi_bus_configure(const FuriHalSpiBus* bus, const LL_SPI_InitTypeDef* config) {
|
||||
assert(bus);
|
||||
LL_SPI_DeInit((SPI_TypeDef*)bus->spi);
|
||||
LL_SPI_Init((SPI_TypeDef*)bus->spi, (LL_SPI_InitTypeDef*)config);
|
||||
LL_SPI_SetRxFIFOThreshold((SPI_TypeDef*)bus->spi, LL_SPI_RX_FIFO_TH_QUARTER);
|
||||
LL_SPI_Enable((SPI_TypeDef*)bus->spi);
|
||||
}
|
||||
|
||||
void furi_hal_spi_bus_end_txrx(const FuriHalSpiBus* bus, uint32_t timeout) {
|
||||
while(LL_SPI_GetTxFIFOLevel((SPI_TypeDef*)bus->spi) != LL_SPI_TX_FIFO_EMPTY)
|
||||
;
|
||||
while(LL_SPI_IsActiveFlag_BSY((SPI_TypeDef*)bus->spi))
|
||||
;
|
||||
while(LL_SPI_GetRxFIFOLevel((SPI_TypeDef*)bus->spi) != LL_SPI_RX_FIFO_EMPTY) {
|
||||
LL_SPI_ReceiveData8((SPI_TypeDef*)bus->spi);
|
||||
}
|
||||
}
|
||||
|
||||
bool furi_hal_spi_bus_rx(const FuriHalSpiBus* bus, uint8_t* buffer, size_t size, uint32_t timeout) {
|
||||
assert(bus);
|
||||
assert(buffer);
|
||||
assert(size > 0);
|
||||
|
||||
return furi_hal_spi_bus_trx(bus, buffer, buffer, size, timeout);
|
||||
}
|
||||
|
||||
bool furi_hal_spi_bus_tx(const FuriHalSpiBus* bus, uint8_t* buffer, size_t size, uint32_t timeout) {
|
||||
assert(bus);
|
||||
assert(buffer);
|
||||
assert(size > 0);
|
||||
bool ret = true;
|
||||
|
||||
while(size > 0) {
|
||||
if(LL_SPI_IsActiveFlag_TXE((SPI_TypeDef*)bus->spi)) {
|
||||
LL_SPI_TransmitData8((SPI_TypeDef*)bus->spi, *buffer);
|
||||
buffer++;
|
||||
size--;
|
||||
}
|
||||
}
|
||||
|
||||
furi_hal_spi_bus_end_txrx(bus, timeout);
|
||||
LL_SPI_ClearFlag_OVR((SPI_TypeDef*)bus->spi);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool furi_hal_spi_bus_trx(
|
||||
const FuriHalSpiBus* bus,
|
||||
uint8_t* tx_buffer,
|
||||
uint8_t* rx_buffer,
|
||||
size_t size,
|
||||
uint32_t timeout) {
|
||||
assert(bus);
|
||||
assert(tx_buffer);
|
||||
assert(rx_buffer);
|
||||
assert(size > 0);
|
||||
bool ret = true;
|
||||
size_t tx_size = size;
|
||||
bool tx_allowed = true;
|
||||
|
||||
while(size > 0) {
|
||||
if(tx_size > 0 && LL_SPI_IsActiveFlag_TXE((SPI_TypeDef*)bus->spi) && tx_allowed) {
|
||||
LL_SPI_TransmitData8((SPI_TypeDef*)bus->spi, *tx_buffer);
|
||||
tx_buffer++;
|
||||
tx_size--;
|
||||
tx_allowed = false;
|
||||
}
|
||||
|
||||
if(LL_SPI_IsActiveFlag_RXNE((SPI_TypeDef*)bus->spi)) {
|
||||
*rx_buffer = LL_SPI_ReceiveData8((SPI_TypeDef*)bus->spi);
|
||||
rx_buffer++;
|
||||
size--;
|
||||
tx_allowed = true;
|
||||
}
|
||||
}
|
||||
|
||||
furi_hal_spi_bus_end_txrx(bus, timeout);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void furi_hal_spi_device_configure(const FuriHalSpiDevice* device) {
|
||||
assert(device);
|
||||
assert(device->config);
|
||||
|
||||
furi_hal_spi_bus_configure(device->bus, device->config);
|
||||
}
|
||||
|
||||
const FuriHalSpiDevice* furi_hal_spi_device_get(FuriHalSpiDeviceId device_id) {
|
||||
assert(device_id < FuriHalSpiDeviceIdMax);
|
||||
|
||||
const FuriHalSpiDevice* device = &furi_hal_spi_devices[device_id];
|
||||
assert(device);
|
||||
|
||||
furi_hal_spi_bus_lock(device->bus);
|
||||
furi_hal_spi_device_configure(device);
|
||||
|
||||
return device;
|
||||
}
|
||||
|
||||
void furi_hal_spi_device_return(const FuriHalSpiDevice* device) {
|
||||
furi_hal_spi_bus_unlock(device->bus);
|
||||
}
|
||||
|
||||
bool furi_hal_spi_device_rx(
|
||||
const FuriHalSpiDevice* device,
|
||||
uint8_t* buffer,
|
||||
size_t size,
|
||||
uint32_t timeout) {
|
||||
assert(device);
|
||||
assert(buffer);
|
||||
assert(size > 0);
|
||||
|
||||
if(device->chip_select) {
|
||||
hal_gpio_write(device->chip_select, false);
|
||||
}
|
||||
|
||||
bool ret = furi_hal_spi_bus_rx(device->bus, buffer, size, timeout);
|
||||
|
||||
if(device->chip_select) {
|
||||
hal_gpio_write(device->chip_select, true);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool furi_hal_spi_device_tx(
|
||||
const FuriHalSpiDevice* device,
|
||||
uint8_t* buffer,
|
||||
size_t size,
|
||||
uint32_t timeout) {
|
||||
assert(device);
|
||||
assert(buffer);
|
||||
assert(size > 0);
|
||||
|
||||
if(device->chip_select) {
|
||||
hal_gpio_write(device->chip_select, false);
|
||||
}
|
||||
|
||||
bool ret = furi_hal_spi_bus_tx(device->bus, buffer, size, timeout);
|
||||
|
||||
if(device->chip_select) {
|
||||
hal_gpio_write(device->chip_select, true);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool furi_hal_spi_device_trx(
|
||||
const FuriHalSpiDevice* device,
|
||||
uint8_t* tx_buffer,
|
||||
uint8_t* rx_buffer,
|
||||
size_t size,
|
||||
uint32_t timeout) {
|
||||
assert(device);
|
||||
assert(tx_buffer);
|
||||
assert(rx_buffer);
|
||||
assert(size > 0);
|
||||
|
||||
if(device->chip_select) {
|
||||
hal_gpio_write(device->chip_select, false);
|
||||
}
|
||||
|
||||
bool ret = furi_hal_spi_bus_trx(device->bus, tx_buffer, rx_buffer, size, timeout);
|
||||
|
||||
if(device->chip_select) {
|
||||
hal_gpio_write(device->chip_select, true);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
129
bootloader/targets/f7/furi-hal/furi-hal-spi.h
Normal file
129
bootloader/targets/f7/furi-hal/furi-hal-spi.h
Normal file
@ -0,0 +1,129 @@
|
||||
#pragma once
|
||||
|
||||
#include "main.h"
|
||||
|
||||
#include "furi-hal-spi-config.h"
|
||||
#include <furi-hal-gpio.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Init SPI API
|
||||
*/
|
||||
void furi_hal_spi_init();
|
||||
|
||||
/* Bus Level API */
|
||||
|
||||
/** Lock SPI bus
|
||||
* Takes bus mutex, if used
|
||||
*/
|
||||
void furi_hal_spi_bus_lock(const FuriHalSpiBus* bus);
|
||||
|
||||
/** Unlock SPI bus
|
||||
* Releases BUS mutex, if used
|
||||
*/
|
||||
void furi_hal_spi_bus_unlock(const FuriHalSpiBus* bus);
|
||||
|
||||
/**
|
||||
* Configure SPI bus
|
||||
* @param bus - spi bus handler
|
||||
* @param config - spi configuration structure
|
||||
*/
|
||||
void furi_hal_spi_bus_configure(const FuriHalSpiBus* bus, const LL_SPI_InitTypeDef* config);
|
||||
|
||||
/** SPI Receive
|
||||
* @param bus - spi bus handler
|
||||
* @param buffer - receive buffer
|
||||
* @param size - transaction size
|
||||
* @param timeout - bus operation timeout in ms
|
||||
*/
|
||||
bool furi_hal_spi_bus_rx(const FuriHalSpiBus* bus, uint8_t* buffer, size_t size, uint32_t timeout);
|
||||
|
||||
/** SPI Transmit
|
||||
* @param bus - spi bus handler
|
||||
* @param buffer - transmit buffer
|
||||
* @param size - transaction size
|
||||
* @param timeout - bus operation timeout in ms
|
||||
*/
|
||||
bool furi_hal_spi_bus_tx(const FuriHalSpiBus* bus, uint8_t* buffer, size_t size, uint32_t timeout);
|
||||
|
||||
/** SPI Transmit and Receive
|
||||
* @param bus - spi bus handlere
|
||||
* @param tx_buffer - device handle
|
||||
* @param rx_buffer - device handle
|
||||
* @param size - transaction size
|
||||
* @param timeout - bus operation timeout in ms
|
||||
*/
|
||||
bool furi_hal_spi_bus_trx(
|
||||
const FuriHalSpiBus* bus,
|
||||
uint8_t* tx_buffer,
|
||||
uint8_t* rx_buffer,
|
||||
size_t size,
|
||||
uint32_t timeout);
|
||||
|
||||
/* Device Level API */
|
||||
|
||||
/** Reconfigure SPI bus for device
|
||||
* @param device - device description
|
||||
*/
|
||||
void furi_hal_spi_device_configure(const FuriHalSpiDevice* device);
|
||||
|
||||
/** Get Device handle
|
||||
* And lock access to the corresponding SPI BUS
|
||||
* @param device_id - device identifier
|
||||
* @return device handle
|
||||
*/
|
||||
const FuriHalSpiDevice* furi_hal_spi_device_get(FuriHalSpiDeviceId device_id);
|
||||
|
||||
/** Return Device handle
|
||||
* And unlock access to the corresponding SPI BUS
|
||||
* @param device - device handle
|
||||
*/
|
||||
void furi_hal_spi_device_return(const FuriHalSpiDevice* device);
|
||||
|
||||
/** SPI Recieve
|
||||
* @param device - device handle
|
||||
* @param buffer - receive buffer
|
||||
* @param size - transaction size
|
||||
* @param timeout - bus operation timeout in ms
|
||||
*/
|
||||
bool furi_hal_spi_device_rx(
|
||||
const FuriHalSpiDevice* device,
|
||||
uint8_t* buffer,
|
||||
size_t size,
|
||||
uint32_t timeout);
|
||||
|
||||
/** SPI Transmit
|
||||
* @param device - device handle
|
||||
* @param buffer - transmit buffer
|
||||
* @param size - transaction size
|
||||
* @param timeout - bus operation timeout in ms
|
||||
*/
|
||||
bool furi_hal_spi_device_tx(
|
||||
const FuriHalSpiDevice* device,
|
||||
uint8_t* buffer,
|
||||
size_t size,
|
||||
uint32_t timeout);
|
||||
|
||||
/** SPI Transmit and Receive
|
||||
* @param device - device handle
|
||||
* @param tx_buffer - device handle
|
||||
* @param rx_buffer - device handle
|
||||
* @param size - transaction size
|
||||
* @param timeout - bus operation timeout in ms
|
||||
*/
|
||||
bool furi_hal_spi_device_trx(
|
||||
const FuriHalSpiDevice* device,
|
||||
uint8_t* tx_buffer,
|
||||
uint8_t* rx_buffer,
|
||||
size_t size,
|
||||
uint32_t timeout);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
7
bootloader/targets/f7/furi-hal/furi-hal.c
Normal file
7
bootloader/targets/f7/furi-hal/furi-hal.c
Normal file
@ -0,0 +1,7 @@
|
||||
#include <furi-hal.h>
|
||||
|
||||
void furi_hal_init() {
|
||||
furi_hal_i2c_init();
|
||||
furi_hal_light_init();
|
||||
furi_hal_spi_init();
|
||||
}
|
||||
8
bootloader/targets/f7/furi-hal/furi-hal.h
Normal file
8
bootloader/targets/f7/furi-hal/furi-hal.h
Normal file
@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <furi-hal-i2c.h>
|
||||
#include <furi-hal-light.h>
|
||||
#include <furi-hal-resources.h>
|
||||
#include <furi-hal-spi.h>
|
||||
|
||||
void furi_hal_init();
|
||||
108
bootloader/targets/f7/furi-hal/main.h
Normal file
108
bootloader/targets/f7/furi-hal/main.h
Normal file
@ -0,0 +1,108 @@
|
||||
#pragma once
|
||||
|
||||
#include <stm32wbxx.h>
|
||||
#include <stm32wbxx_ll_gpio.h>
|
||||
#include <stm32wbxx_ll_spi.h>
|
||||
|
||||
#define BUTTON_BACK_GPIO_Port GPIOC
|
||||
#define BUTTON_BACK_Pin LL_GPIO_PIN_13
|
||||
#define BUTTON_DOWN_GPIO_Port GPIOC
|
||||
#define BUTTON_DOWN_Pin LL_GPIO_PIN_6
|
||||
#define BUTTON_LEFT_GPIO_Port GPIOB
|
||||
#define BUTTON_LEFT_Pin LL_GPIO_PIN_11
|
||||
#define BUTTON_OK_GPIO_Port GPIOH
|
||||
#define BUTTON_OK_Pin LL_GPIO_PIN_3
|
||||
#define BUTTON_RIGHT_GPIO_Port GPIOB
|
||||
#define BUTTON_RIGHT_Pin LL_GPIO_PIN_12
|
||||
#define BUTTON_UP_GPIO_Port GPIOB
|
||||
#define BUTTON_UP_Pin LL_GPIO_PIN_10
|
||||
|
||||
#define CC1101_CS_GPIO_Port GPIOD
|
||||
#define CC1101_CS_Pin LL_GPIO_PIN_0
|
||||
#define CC1101_G0_GPIO_Port GPIOA
|
||||
#define CC1101_G0_Pin LL_GPIO_PIN_1
|
||||
|
||||
#define DISPLAY_CS_GPIO_Port GPIOC
|
||||
#define DISPLAY_CS_Pin LL_GPIO_PIN_11
|
||||
#define DISPLAY_DI_GPIO_Port GPIOB
|
||||
#define DISPLAY_DI_Pin LL_GPIO_PIN_1
|
||||
#define DISPLAY_RST_GPIO_Port GPIOB
|
||||
#define DISPLAY_RST_Pin LL_GPIO_PIN_0
|
||||
|
||||
#define IR_RX_GPIO_Port GPIOA
|
||||
#define IR_RX_Pin LL_GPIO_PIN_0
|
||||
#define IR_TX_GPIO_Port GPIOB
|
||||
#define IR_TX_Pin LL_GPIO_PIN_9
|
||||
|
||||
#define NFC_CS_GPIO_Port GPIOE
|
||||
#define NFC_CS_Pin LL_GPIO_PIN_4
|
||||
|
||||
#define PA4_GPIO_Port GPIOA
|
||||
#define PA4_Pin LL_GPIO_PIN_4
|
||||
#define PA6_GPIO_Port GPIOA
|
||||
#define PA6_Pin LL_GPIO_PIN_6
|
||||
#define PA7_GPIO_Port GPIOA
|
||||
#define PA7_Pin LL_GPIO_PIN_7
|
||||
#define PB2_GPIO_Port GPIOB
|
||||
#define PB2_Pin LL_GPIO_PIN_2
|
||||
#define PB3_GPIO_Port GPIOB
|
||||
#define PB3_Pin LL_GPIO_PIN_3
|
||||
#define PC0_GPIO_Port GPIOC
|
||||
#define PC0_Pin LL_GPIO_PIN_0
|
||||
#define PC1_GPIO_Port GPIOC
|
||||
#define PC1_Pin LL_GPIO_PIN_1
|
||||
#define PC3_GPIO_Port GPIOC
|
||||
#define PC3_Pin LL_GPIO_PIN_3
|
||||
|
||||
#define PERIPH_POWER_GPIO_Port GPIOA
|
||||
#define PERIPH_POWER_Pin LL_GPIO_PIN_3
|
||||
|
||||
#define QUARTZ_32MHZ_IN_GPIO_Port GPIOC
|
||||
#define QUARTZ_32MHZ_IN_Pin LL_GPIO_PIN_14
|
||||
#define QUARTZ_32MHZ_OUT_GPIO_Port GPIOC
|
||||
#define QUARTZ_32MHZ_OUT_Pin LL_GPIO_PIN_15
|
||||
|
||||
#define RFID_OUT_GPIO_Port GPIOB
|
||||
#define RFID_OUT_Pin LL_GPIO_PIN_13
|
||||
#define RFID_PULL_GPIO_Port GPIOA
|
||||
#define RFID_PULL_Pin LL_GPIO_PIN_2
|
||||
#define RFID_RF_IN_GPIO_Port GPIOC
|
||||
#define RFID_RF_IN_Pin LL_GPIO_PIN_5
|
||||
#define RFID_TUNE_GPIO_Port GPIOA
|
||||
#define RFID_TUNE_Pin LL_GPIO_PIN_8
|
||||
|
||||
#define RF_SW_0_GPIO_Port GPIOC
|
||||
#define RF_SW_0_Pin LL_GPIO_PIN_4
|
||||
|
||||
#define SD_CD_GPIO_Port GPIOC
|
||||
#define SD_CD_Pin LL_GPIO_PIN_10
|
||||
#define SD_CS_GPIO_Port GPIOC
|
||||
#define SD_CS_Pin LL_GPIO_PIN_12
|
||||
|
||||
#define SPEAKER_GPIO_Port GPIOB
|
||||
#define SPEAKER_Pin LL_GPIO_PIN_8
|
||||
|
||||
#define VIBRO_GPIO_Port GPIOA
|
||||
#define VIBRO_Pin LL_GPIO_PIN_15
|
||||
|
||||
#define iBTN_GPIO_Port GPIOB
|
||||
#define iBTN_Pin LL_GPIO_PIN_14
|
||||
|
||||
#define USART1_TX_Pin LL_GPIO_PIN_6
|
||||
#define USART1_TX_Port GPIOB
|
||||
#define USART1_RX_Pin LL_GPIO_PIN_7
|
||||
#define USART1_RX_Port GPIOB
|
||||
|
||||
#define SPI_D_MISO_GPIO_Port GPIOC
|
||||
#define SPI_D_MISO_Pin LL_GPIO_PIN_2
|
||||
#define SPI_D_MOSI_GPIO_Port GPIOB
|
||||
#define SPI_D_MOSI_Pin LL_GPIO_PIN_15
|
||||
#define SPI_D_SCK_GPIO_Port GPIOD
|
||||
#define SPI_D_SCK_Pin LL_GPIO_PIN_1
|
||||
|
||||
#define SPI_R_MISO_GPIO_Port GPIOB
|
||||
#define SPI_R_MISO_Pin LL_GPIO_PIN_4
|
||||
#define SPI_R_MOSI_GPIO_Port GPIOB
|
||||
#define SPI_R_MOSI_Pin LL_GPIO_PIN_5
|
||||
#define SPI_R_SCK_GPIO_Port GPIOA
|
||||
#define SPI_R_SCK_Pin LL_GPIO_PIN_5
|
||||
69
bootloader/targets/f7/furi-hal/u8g2_periphery.c
Normal file
69
bootloader/targets/f7/furi-hal/u8g2_periphery.c
Normal file
@ -0,0 +1,69 @@
|
||||
#include <u8g2.h>
|
||||
#include <assert.h>
|
||||
#include <furi-hal.h>
|
||||
#include <stm32wbxx_ll_utils.h>
|
||||
|
||||
static FuriHalSpiDevice* u8g2_periphery_display = NULL;
|
||||
|
||||
uint8_t u8g2_gpio_and_delay_stm32(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr) {
|
||||
switch(msg) {
|
||||
case U8X8_MSG_GPIO_AND_DELAY_INIT:
|
||||
/* HAL initialization contains all what we need so we can skip this part. */
|
||||
break;
|
||||
|
||||
case U8X8_MSG_DELAY_MILLI:
|
||||
LL_mDelay(arg_int);
|
||||
break;
|
||||
|
||||
case U8X8_MSG_DELAY_10MICRO:
|
||||
LL_mDelay(1);
|
||||
break;
|
||||
|
||||
case U8X8_MSG_DELAY_100NANO:
|
||||
asm("nop");
|
||||
break;
|
||||
|
||||
case U8X8_MSG_GPIO_RESET:
|
||||
hal_gpio_write(&gpio_display_rst, arg_int);
|
||||
break;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint8_t u8x8_hw_spi_stm32(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr) {
|
||||
switch(msg) {
|
||||
case U8X8_MSG_BYTE_SEND:
|
||||
furi_hal_spi_bus_tx(u8g2_periphery_display->bus, (uint8_t*)arg_ptr, arg_int, 10000);
|
||||
break;
|
||||
|
||||
case U8X8_MSG_BYTE_SET_DC:
|
||||
hal_gpio_write(&gpio_display_di, arg_int);
|
||||
break;
|
||||
|
||||
case U8X8_MSG_BYTE_INIT:
|
||||
break;
|
||||
|
||||
case U8X8_MSG_BYTE_START_TRANSFER:
|
||||
assert(u8g2_periphery_display == NULL);
|
||||
u8g2_periphery_display =
|
||||
(FuriHalSpiDevice*)furi_hal_spi_device_get(FuriHalSpiDeviceIdDisplay);
|
||||
hal_gpio_write(u8g2_periphery_display->chip_select, false);
|
||||
break;
|
||||
|
||||
case U8X8_MSG_BYTE_END_TRANSFER:
|
||||
assert(u8g2_periphery_display);
|
||||
hal_gpio_write(u8g2_periphery_display->chip_select, true);
|
||||
furi_hal_spi_device_return(u8g2_periphery_display);
|
||||
u8g2_periphery_display = NULL;
|
||||
break;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
187
bootloader/targets/f7/stm32wb55xx_flash_cm4.ld
Normal file
187
bootloader/targets/f7/stm32wb55xx_flash_cm4.ld
Normal file
@ -0,0 +1,187 @@
|
||||
/**
|
||||
*****************************************************************************
|
||||
**
|
||||
** File : stm32wb55xx_flash_cm4.ld
|
||||
**
|
||||
** Abstract : System Workbench Minimal System calls file
|
||||
**
|
||||
** For more information about which c-functions
|
||||
** need which of these lowlevel functions
|
||||
** please consult the Newlib libc-manual
|
||||
**
|
||||
** Environment : System Workbench for MCU
|
||||
**
|
||||
** Distribution: The file is distributed “as is,” without any warranty
|
||||
** of any kind.
|
||||
**
|
||||
*****************************************************************************
|
||||
**
|
||||
** <h2><center>© COPYRIGHT(c) 2019 Ac6</center></h2>
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without modification,
|
||||
** are permitted provided that the following conditions are met:
|
||||
** 1. Redistributions of source code must retain the above copyright notice,
|
||||
** this list of conditions and the following disclaimer.
|
||||
** 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
** this list of conditions and the following disclaimer in the documentation
|
||||
** and/or other materials provided with the distribution.
|
||||
** 3. Neither the name of Ac6 nor the names of its contributors
|
||||
** may be used to endorse or promote products derived from this software
|
||||
** without specific prior written permission.
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
** AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
** DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
** OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
**
|
||||
*****************************************************************************
|
||||
*/
|
||||
|
||||
/* Entry Point */
|
||||
ENTRY(Reset_Handler)
|
||||
|
||||
/* Highest address of the user mode stack */
|
||||
_estack = 0x20030000; /* end of RAM */
|
||||
/* Generate a link error if heap and stack don't fit into RAM */
|
||||
_Min_Heap_Size = 0x200; /* required amount of heap */
|
||||
_Min_Stack_Size = 0x400; /* required amount of stack */
|
||||
|
||||
/* Specify the memory areas */
|
||||
MEMORY
|
||||
{
|
||||
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 32K
|
||||
RAM1 (xrw) : ORIGIN = 0x20000008, LENGTH = 0x2FFF8
|
||||
RAM_SHARED (xrw) : ORIGIN = 0x20030000, LENGTH = 10K
|
||||
}
|
||||
|
||||
/* Define output sections */
|
||||
SECTIONS
|
||||
{
|
||||
/* The startup code goes first into FLASH */
|
||||
.isr_vector :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
KEEP(*(.isr_vector)) /* Startup code */
|
||||
. = ALIGN(4);
|
||||
} >FLASH
|
||||
|
||||
/* The program code and other data goes into FLASH */
|
||||
.text :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
*(.text) /* .text sections (code) */
|
||||
*(.text*) /* .text* sections (code) */
|
||||
*(.glue_7) /* glue arm to thumb code */
|
||||
*(.glue_7t) /* glue thumb to arm code */
|
||||
*(.eh_frame)
|
||||
|
||||
KEEP (*(.init))
|
||||
KEEP (*(.fini))
|
||||
|
||||
. = ALIGN(4);
|
||||
_etext = .; /* define a global symbols at end of code */
|
||||
} >FLASH
|
||||
|
||||
/* Constant data goes into FLASH */
|
||||
.rodata :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
*(.rodata) /* .rodata sections (constants, strings, etc.) */
|
||||
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
|
||||
. = ALIGN(4);
|
||||
} >FLASH
|
||||
|
||||
.ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH
|
||||
.ARM : {
|
||||
__exidx_start = .;
|
||||
*(.ARM.exidx*)
|
||||
__exidx_end = .;
|
||||
} >FLASH
|
||||
|
||||
.preinit_array :
|
||||
{
|
||||
PROVIDE_HIDDEN (__preinit_array_start = .);
|
||||
KEEP (*(.preinit_array*))
|
||||
PROVIDE_HIDDEN (__preinit_array_end = .);
|
||||
} >FLASH
|
||||
.init_array :
|
||||
{
|
||||
PROVIDE_HIDDEN (__init_array_start = .);
|
||||
KEEP (*(SORT(.init_array.*)))
|
||||
KEEP (*(.init_array*))
|
||||
PROVIDE_HIDDEN (__init_array_end = .);
|
||||
} >FLASH
|
||||
.fini_array :
|
||||
{
|
||||
PROVIDE_HIDDEN (__fini_array_start = .);
|
||||
KEEP (*(SORT(.fini_array.*)))
|
||||
KEEP (*(.fini_array*))
|
||||
PROVIDE_HIDDEN (__fini_array_end = .);
|
||||
} >FLASH
|
||||
|
||||
/* used by the startup to initialize data */
|
||||
_sidata = LOADADDR(.data);
|
||||
|
||||
/* Initialized data sections goes into RAM, load LMA copy after code */
|
||||
.data :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
_sdata = .; /* create a global symbol at data start */
|
||||
*(.data) /* .data sections */
|
||||
*(.data*) /* .data* sections */
|
||||
|
||||
. = ALIGN(4);
|
||||
_edata = .; /* define a global symbol at data end */
|
||||
} >RAM1 AT> FLASH
|
||||
|
||||
|
||||
/* Uninitialized data section */
|
||||
. = ALIGN(4);
|
||||
.bss :
|
||||
{
|
||||
/* This is used by the startup in order to initialize the .bss secion */
|
||||
_sbss = .; /* define a global symbol at bss start */
|
||||
__bss_start__ = _sbss;
|
||||
*(.bss)
|
||||
*(.bss*)
|
||||
*(COMMON)
|
||||
|
||||
. = ALIGN(4);
|
||||
_ebss = .; /* define a global symbol at bss end */
|
||||
__bss_end__ = _ebss;
|
||||
} >RAM1
|
||||
|
||||
/* User_heap_stack section, used to check that there is enough RAM left */
|
||||
._user_heap_stack :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
PROVIDE ( end = . );
|
||||
PROVIDE ( _end = . );
|
||||
. = . + _Min_Heap_Size;
|
||||
. = . + _Min_Stack_Size;
|
||||
. = ALIGN(8);
|
||||
} >RAM1
|
||||
|
||||
|
||||
|
||||
/* Remove information from the standard libraries */
|
||||
/DISCARD/ :
|
||||
{
|
||||
libc.a ( * )
|
||||
libm.a ( * )
|
||||
libgcc.a ( * )
|
||||
}
|
||||
|
||||
.ARM.attributes 0 : { *(.ARM.attributes) }
|
||||
MAPPING_TABLE (NOLOAD) : { *(MAPPING_TABLE) } >RAM_SHARED
|
||||
MB_MEM1 (NOLOAD) : { *(MB_MEM1) } >RAM_SHARED
|
||||
MB_MEM2 (NOLOAD) : { _sMB_MEM2 = . ; *(MB_MEM2) ; _eMB_MEM2 = . ; } >RAM_SHARED
|
||||
}
|
||||
|
||||
|
||||
264
bootloader/targets/f7/target.c
Normal file
264
bootloader/targets/f7/target.c
Normal file
@ -0,0 +1,264 @@
|
||||
#include <target.h>
|
||||
#include <stm32wbxx.h>
|
||||
#include <stm32wbxx_ll_system.h>
|
||||
#include <stm32wbxx_ll_bus.h>
|
||||
#include <stm32wbxx_ll_utils.h>
|
||||
#include <stm32wbxx_ll_rcc.h>
|
||||
#include <stm32wbxx_ll_rtc.h>
|
||||
#include <stm32wbxx_ll_pwr.h>
|
||||
#include <stm32wbxx_ll_gpio.h>
|
||||
#include <stm32wbxx_hal_flash.h>
|
||||
|
||||
#include <lib/toolbox/version.h>
|
||||
#include <furi-hal.h>
|
||||
#include <u8g2.h>
|
||||
|
||||
const uint8_t I_DFU_128x50[] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x07, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x38, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0xC0, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8F, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x7F, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x75, 0x00, 0x00, 0xF0, 0x3F, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x0A, 0x00, 0x00, 0x0F, 0x60, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0xE0, 0x0F, 0x00, 0xC0, 0xE0, 0x4F, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x18, 0x00, 0x30, 0x1E, 0x90, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x18, 0x00, 0x8C, 0x01, 0xA0, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x81, 0xFF, 0x19, 0x00, 0x63, 0x00, 0xC0, 0xF0, 0x07,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x60, 0x5E, 0x1F, 0x80, 0x18, 0x00, 0xE0, 0x0E, 0x18,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x18, 0xAF, 0x0F, 0x40, 0x06, 0x00, 0xF8, 0x01, 0x20,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x06, 0x57, 0x01, 0x20, 0x01, 0x00, 0x78, 0x00, 0x3E,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x81, 0xAF, 0x02, 0x90, 0x00, 0x00, 0x38, 0x80, 0x41,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x80, 0x57, 0x01, 0x48, 0x00, 0x00, 0x10, 0x60, 0x40,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x10, 0x80, 0xAB, 0x00, 0x24, 0x00, 0x00, 0x08, 0x10, 0x40,
|
||||
0x3F, 0x00, 0x00, 0x00, 0x00, 0x38, 0x0C, 0xC0, 0x57, 0x01, 0x12, 0x00, 0x00, 0x04, 0x08, 0x40,
|
||||
0xC0, 0x0F, 0x00, 0x00, 0xC0, 0x07, 0x03, 0xF0, 0xAB, 0x00, 0x0A, 0x00, 0x00, 0x02, 0x04, 0x40,
|
||||
0x00, 0xF0, 0x1F, 0x80, 0x3F, 0xC0, 0x00, 0xFC, 0x55, 0x01, 0x05, 0xE0, 0x00, 0x01, 0x04, 0x40,
|
||||
0x00, 0x00, 0xE0, 0x7F, 0x00, 0x30, 0x00, 0xFF, 0xAB, 0x00, 0x05, 0xE0, 0x80, 0x00, 0x02, 0x40,
|
||||
0x0F, 0x00, 0x00, 0x00, 0x80, 0x0F, 0xE0, 0xCF, 0x55, 0x81, 0x02, 0xF0, 0x40, 0x00, 0x02, 0x40,
|
||||
0xF0, 0x0F, 0x00, 0x00, 0x7F, 0x00, 0xFE, 0xC3, 0xAB, 0x80, 0x02, 0x78, 0x20, 0x00, 0x01, 0x40,
|
||||
0x00, 0xF0, 0xFF, 0xFF, 0x00, 0xF0, 0xFF, 0xC0, 0xD5, 0x81, 0x01, 0x7E, 0x10, 0x80, 0x00, 0x20,
|
||||
0x00, 0x00, 0x00, 0x00, 0xC0, 0xFF, 0x0F, 0xE0, 0xFA, 0x83, 0xC1, 0x3F, 0x08, 0x80, 0x00, 0x20,
|
||||
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x01, 0xD8, 0x07, 0x83, 0xF1, 0x1F, 0x04, 0x40, 0x00, 0x20,
|
||||
0x00, 0xE0, 0xFF, 0xFF, 0xFF, 0x0F, 0x80, 0xC7, 0x01, 0x83, 0xF1, 0x0F, 0x00, 0x20, 0x00, 0x10,
|
||||
0xE0, 0xFF, 0xFF, 0xFF, 0x3F, 0xC0, 0x7F, 0x40, 0x80, 0x83, 0xE1, 0x01, 0x00, 0x20, 0x00, 0x18,
|
||||
0xFC, 0xFF, 0xFF, 0xFF, 0x03, 0x3F, 0x00, 0x20, 0xFC, 0x83, 0x01, 0x00, 0x00, 0x10, 0x00, 0x18,
|
||||
0xFF, 0xFF, 0xFF, 0x3F, 0xF0, 0x00, 0x00, 0x10, 0xD7, 0x01, 0x03, 0x00, 0x00, 0x08, 0x00, 0x1C,
|
||||
0xFF, 0xFF, 0x01, 0x00, 0x0F, 0x00, 0x00, 0x88, 0xAB, 0x02, 0xE3, 0x01, 0x00, 0x08, 0x00, 0x0C,
|
||||
0xFF, 0x07, 0x00, 0xE0, 0x00, 0x00, 0x00, 0xC4, 0x55, 0x05, 0x1E, 0x00, 0x00, 0x04, 0x00, 0x0E,
|
||||
0x7F, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0xA3, 0xAB, 0x02, 0x06, 0x00, 0x00, 0x02, 0x00, 0x0F,
|
||||
0x0F, 0x00, 0x80, 0x03, 0x00, 0x00, 0xC0, 0x10, 0x57, 0x05, 0x02, 0x00, 0x00, 0x01, 0x80, 0x07,
|
||||
0x03, 0x00, 0x70, 0x00, 0x00, 0x00, 0x30, 0x08, 0xAB, 0x0A, 0x02, 0x00, 0xC0, 0x00, 0xC0, 0x07,
|
||||
0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x84, 0x57, 0x15, 0x01, 0x00, 0x30, 0x00, 0xE0, 0x07,
|
||||
0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0xC3, 0xFF, 0x2A, 0x01, 0x00, 0x0C, 0x00, 0xF0, 0x0F,
|
||||
0x00, 0xC0, 0x00, 0x00, 0x00, 0xE0, 0xC0, 0xE0, 0xFE, 0x55, 0x01, 0x82, 0x03, 0x00, 0xF8, 0x15,
|
||||
0x00, 0x30, 0x00, 0x00, 0x00, 0x1C, 0x30, 0x78, 0xFE, 0xAA, 0x01, 0x7C, 0x00, 0x00, 0xFC, 0x23,
|
||||
0x00, 0x0E, 0x00, 0x00, 0xC0, 0x03, 0x0C, 0x3C, 0x7F, 0x5D, 0x01, 0x00, 0x00, 0x00, 0xFF, 0x45,
|
||||
0xC0, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x02, 0x8F, 0xBF, 0xAE, 0x03, 0x00, 0x00, 0xC0, 0xFF, 0x82,
|
||||
0x30, 0x00, 0x00, 0xC0, 0x01, 0x80, 0xC1, 0x43, 0xFE, 0x5D, 0x01, 0x00, 0x00, 0xF0, 0xFF, 0x05,
|
||||
0x0F, 0x00, 0x80, 0x3F, 0x00, 0x60, 0xF0, 0x31, 0xF6, 0xAE, 0x03, 0x00, 0x00, 0xFA, 0xAF, 0x02,
|
||||
0xFC, 0xFF, 0x7F, 0x00, 0x00, 0x18, 0x7C, 0x08, 0x23, 0xFF, 0x05, 0x00, 0x00, 0xFD, 0x55, 0x01,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0x1F, 0x84, 0x30, 0xFE, 0x0A, 0x00, 0x00, 0xAA, 0xAA, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x80, 0xF1, 0x07, 0x43, 0x18, 0xFF, 0x15, 0x00, 0x00, 0x54, 0x15, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x80, 0x20, 0x8C, 0xFF, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
// Boot request enum
|
||||
#define BOOT_REQUEST_TAINTED 0x00000000
|
||||
#define BOOT_REQUEST_CLEAN 0xDADEDADE
|
||||
#define BOOT_REQUEST_DFU 0xDF00B000
|
||||
// Boot to DFU pin
|
||||
#define BOOT_DFU_PORT GPIOB
|
||||
#define BOOT_DFU_PIN LL_GPIO_PIN_11
|
||||
// USB pins
|
||||
#define BOOT_USB_PORT GPIOA
|
||||
#define BOOT_USB_DM_PIN LL_GPIO_PIN_11
|
||||
#define BOOT_USB_DP_PIN LL_GPIO_PIN_12
|
||||
#define BOOT_USB_PIN (BOOT_USB_DM_PIN | BOOT_USB_DP_PIN)
|
||||
|
||||
#define RTC_CLOCK_IS_READY() (LL_RCC_LSE_IsReady() && LL_RCC_LSI1_IsReady())
|
||||
|
||||
uint8_t u8g2_gpio_and_delay_stm32(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);
|
||||
uint8_t u8x8_hw_spi_stm32(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);
|
||||
|
||||
void target_led_control(char* c) {
|
||||
furi_hal_light_set(LightRed, 0x00);
|
||||
furi_hal_light_set(LightGreen, 0x00);
|
||||
furi_hal_light_set(LightBlue, 0x00);
|
||||
do {
|
||||
if(*c == 'R') {
|
||||
furi_hal_light_set(LightRed, 0xFF);
|
||||
} else if(*c == 'G') {
|
||||
furi_hal_light_set(LightGreen, 0xFF);
|
||||
} else if(*c == 'B') {
|
||||
furi_hal_light_set(LightBlue, 0xFF);
|
||||
} else if(*c == '.') {
|
||||
LL_mDelay(125);
|
||||
furi_hal_light_set(LightRed, 0x00);
|
||||
furi_hal_light_set(LightGreen, 0x00);
|
||||
furi_hal_light_set(LightBlue, 0x00);
|
||||
LL_mDelay(125);
|
||||
} else if(*c == '-') {
|
||||
LL_mDelay(250);
|
||||
furi_hal_light_set(LightRed, 0x00);
|
||||
furi_hal_light_set(LightGreen, 0x00);
|
||||
furi_hal_light_set(LightBlue, 0x00);
|
||||
LL_mDelay(250);
|
||||
} else if(*c == '|') {
|
||||
furi_hal_light_set(LightRed, 0x00);
|
||||
furi_hal_light_set(LightGreen, 0x00);
|
||||
furi_hal_light_set(LightBlue, 0x00);
|
||||
}
|
||||
c++;
|
||||
} while(*c != 0);
|
||||
}
|
||||
|
||||
void target_clock_init() {
|
||||
LL_Init1msTick(4000000);
|
||||
LL_SetSystemCoreClock(4000000);
|
||||
|
||||
LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOA);
|
||||
LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOB);
|
||||
LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOC);
|
||||
LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOD);
|
||||
LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOE);
|
||||
LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOH);
|
||||
|
||||
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SPI1);
|
||||
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_SPI2);
|
||||
}
|
||||
|
||||
void target_gpio_init() {
|
||||
// USB D+
|
||||
LL_GPIO_SetPinMode(BOOT_USB_PORT, BOOT_USB_DP_PIN, LL_GPIO_MODE_OUTPUT);
|
||||
LL_GPIO_SetPinSpeed(BOOT_USB_PORT, BOOT_USB_DP_PIN, LL_GPIO_SPEED_FREQ_VERY_HIGH);
|
||||
LL_GPIO_SetPinOutputType(BOOT_USB_PORT, BOOT_USB_DP_PIN, LL_GPIO_OUTPUT_OPENDRAIN);
|
||||
// USB D-
|
||||
LL_GPIO_SetPinMode(BOOT_USB_PORT, BOOT_USB_DM_PIN, LL_GPIO_MODE_OUTPUT);
|
||||
LL_GPIO_SetPinSpeed(BOOT_USB_PORT, BOOT_USB_DM_PIN, LL_GPIO_SPEED_FREQ_VERY_HIGH);
|
||||
LL_GPIO_SetPinOutputType(BOOT_USB_PORT, BOOT_USB_DM_PIN, LL_GPIO_OUTPUT_OPENDRAIN);
|
||||
// Button: back
|
||||
LL_GPIO_SetPinMode(BOOT_DFU_PORT, BOOT_DFU_PIN, LL_GPIO_MODE_INPUT);
|
||||
LL_GPIO_SetPinPull(BOOT_DFU_PORT, BOOT_DFU_PIN, LL_GPIO_PULL_UP);
|
||||
}
|
||||
|
||||
void target_rtc_init() {
|
||||
// LSE and RTC
|
||||
LL_PWR_EnableBkUpAccess();
|
||||
if(!RTC_CLOCK_IS_READY()) {
|
||||
// Start LSI1 needed for CSS
|
||||
LL_RCC_LSI1_Enable();
|
||||
// Try to start LSE normal way
|
||||
LL_RCC_LSE_SetDriveCapability(LL_RCC_LSEDRIVE_HIGH);
|
||||
LL_RCC_LSE_Enable();
|
||||
uint32_t c = 0;
|
||||
while(!RTC_CLOCK_IS_READY() && c < 200) {
|
||||
LL_mDelay(10);
|
||||
c++;
|
||||
}
|
||||
// Plan B: reset backup domain
|
||||
if(!RTC_CLOCK_IS_READY()) {
|
||||
target_led_control("-R.R.R.");
|
||||
LL_RCC_ForceBackupDomainReset();
|
||||
LL_RCC_ReleaseBackupDomainReset();
|
||||
NVIC_SystemReset();
|
||||
}
|
||||
// Set RTC domain clock to LSE
|
||||
LL_RCC_SetRTCClockSource(LL_RCC_RTC_CLKSOURCE_LSE);
|
||||
// Enable LSE CSS
|
||||
LL_RCC_LSE_EnableCSS();
|
||||
}
|
||||
// Enable clocking
|
||||
LL_RCC_EnableRTC();
|
||||
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_RTCAPB);
|
||||
}
|
||||
|
||||
void target_version_save(void) {
|
||||
LL_RTC_BAK_SetRegister(RTC, LL_RTC_BKP_DR1, (uint32_t)version_get());
|
||||
}
|
||||
|
||||
void target_usb_wire_reset() {
|
||||
LL_GPIO_ResetOutputPin(BOOT_USB_PORT, BOOT_USB_PIN);
|
||||
}
|
||||
|
||||
void target_display_init() {
|
||||
// Prepare gpio
|
||||
hal_gpio_init_simple(&gpio_display_rst, GpioModeOutputPushPull);
|
||||
hal_gpio_init_simple(&gpio_display_di, GpioModeOutputPushPull);
|
||||
// Initialize
|
||||
u8g2_t fb;
|
||||
u8g2_Setup_st7565_erc12864_alt_f(&fb, U8G2_R0, u8x8_hw_spi_stm32, u8g2_gpio_and_delay_stm32);
|
||||
u8g2_InitDisplay(&fb);
|
||||
u8g2_SetContrast(&fb, 36);
|
||||
// Create payload
|
||||
u8g2_ClearBuffer(&fb);
|
||||
u8g2_SetDrawColor(&fb, 0x01);
|
||||
u8g2_SetFont(&fb, u8g2_font_helvB08_tf);
|
||||
u8g2_DrawXBM(&fb, 0, 64 - 50, 128, 50, I_DFU_128x50);
|
||||
u8g2_DrawStr(&fb, 2, 8, "Update & Recovery Mode");
|
||||
u8g2_DrawStr(&fb, 2, 21, "DFU started");
|
||||
// Send buffer
|
||||
u8g2_SetPowerSave(&fb, 0);
|
||||
u8g2_SendBuffer(&fb);
|
||||
}
|
||||
|
||||
void target_init() {
|
||||
target_clock_init();
|
||||
target_gpio_init();
|
||||
furi_hal_init();
|
||||
target_led_control("RGB");
|
||||
target_rtc_init();
|
||||
target_version_save();
|
||||
target_usb_wire_reset();
|
||||
|
||||
// Errata 2.2.9, Flash OPTVERR flag is always set after system reset
|
||||
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_ALL_ERRORS);
|
||||
}
|
||||
|
||||
int target_is_dfu_requested() {
|
||||
if(LL_RTC_BAK_GetRegister(RTC, LL_RTC_BKP_DR0) == BOOT_REQUEST_TAINTED) {
|
||||
// Default system state is tainted
|
||||
// We must ensure that MCU is cleanly booted
|
||||
LL_RTC_BAK_SetRegister(RTC, LL_RTC_BKP_DR0, BOOT_REQUEST_CLEAN);
|
||||
NVIC_SystemReset();
|
||||
} else if(LL_RTC_BAK_GetRegister(RTC, LL_RTC_BKP_DR0) == BOOT_REQUEST_DFU) {
|
||||
return 1;
|
||||
}
|
||||
LL_mDelay(100);
|
||||
if(!LL_GPIO_IsInputPinSet(BOOT_DFU_PORT, BOOT_DFU_PIN)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void target_switch(void* offset) {
|
||||
asm volatile("ldr r3, [%0] \n"
|
||||
"msr msp, r3 \n"
|
||||
"ldr r3, [%1] \n"
|
||||
"mov pc, r3 \n"
|
||||
:
|
||||
: "r"(offset), "r"(offset + 0x4)
|
||||
: "r3");
|
||||
}
|
||||
|
||||
void target_switch2dfu() {
|
||||
target_led_control("B");
|
||||
furi_hal_light_set(LightBacklight, 0xFF);
|
||||
target_display_init();
|
||||
// Mark system as tainted, it will be soon
|
||||
LL_RTC_BAK_SetRegister(RTC, LL_RTC_BKP_DR0, BOOT_REQUEST_TAINTED);
|
||||
// Remap memory to system bootloader
|
||||
LL_SYSCFG_SetRemapMemory(LL_SYSCFG_REMAP_SYSTEMFLASH);
|
||||
// Jump
|
||||
target_switch(0x0);
|
||||
}
|
||||
|
||||
void target_switch2os() {
|
||||
target_led_control("G");
|
||||
SCB->VTOR = OS_OFFSET;
|
||||
target_switch((void*)(BOOT_ADDRESS + OS_OFFSET));
|
||||
}
|
||||
48
bootloader/targets/f7/target.mk
Normal file
48
bootloader/targets/f7/target.mk
Normal file
@ -0,0 +1,48 @@
|
||||
TOOLCHAIN = arm
|
||||
|
||||
BOOT_ADDRESS = 0x08000000
|
||||
FW_ADDRESS = 0x08008000
|
||||
OS_OFFSET = 0x00008000
|
||||
FLASH_ADDRESS = 0x08000000
|
||||
|
||||
OPENOCD_OPTS = -f interface/stlink.cfg -c "transport select hla_swd" -f ../debug/stm32wbx.cfg -c "init"
|
||||
BOOT_CFLAGS = -DBOOT_ADDRESS=$(BOOT_ADDRESS) -DFW_ADDRESS=$(FW_ADDRESS) -DOS_OFFSET=$(OS_OFFSET)
|
||||
MCU_FLAGS = -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard
|
||||
|
||||
CFLAGS += $(MCU_FLAGS) $(BOOT_CFLAGS) -DSTM32WB55xx -Wall -fdata-sections -ffunction-sections
|
||||
LDFLAGS += $(MCU_FLAGS) -specs=nosys.specs -specs=nano.specs
|
||||
|
||||
CUBE_DIR = $(PROJECT_ROOT)/lib/STM32CubeWB
|
||||
|
||||
# ST HAL
|
||||
CFLAGS += -DUSE_FULL_LL_DRIVER
|
||||
ASM_SOURCES += $(CUBE_DIR)/Drivers/CMSIS/Device/ST/STM32WBxx/Source/Templates/gcc/startup_stm32wb55xx_cm4.s
|
||||
C_SOURCES += $(CUBE_DIR)/Drivers/CMSIS/Device/ST/STM32WBxx/Source/Templates/system_stm32wbxx.c
|
||||
C_SOURCES += $(CUBE_DIR)/Drivers/STM32WBxx_HAL_Driver/Src/stm32wbxx_ll_utils.c
|
||||
C_SOURCES += $(CUBE_DIR)/Drivers/STM32WBxx_HAL_Driver/Src/stm32wbxx_ll_gpio.c
|
||||
C_SOURCES += $(CUBE_DIR)/Drivers/STM32WBxx_HAL_Driver/Src/stm32wbxx_ll_i2c.c
|
||||
C_SOURCES += $(CUBE_DIR)/Drivers/STM32WBxx_HAL_Driver/Src/stm32wbxx_ll_spi.c
|
||||
|
||||
CFLAGS += -I$(CUBE_DIR)/Drivers/CMSIS/Include
|
||||
CFLAGS += -I$(CUBE_DIR)/Drivers/CMSIS/Device/ST/STM32WBxx/Include
|
||||
CFLAGS += -I$(CUBE_DIR)/Drivers/STM32WBxx_HAL_Driver/Inc
|
||||
|
||||
LDFLAGS += -T$(TARGET_DIR)/stm32wb55xx_flash_cm4.ld
|
||||
|
||||
# Drivers
|
||||
DRIVERS_DIR = $(PROJECT_ROOT)//lib/drivers
|
||||
CFLAGS += -I$(DRIVERS_DIR)
|
||||
C_SOURCES += $(DRIVERS_DIR)/lp5562.c
|
||||
|
||||
# API-HAL
|
||||
CFLAGS += -I$(TARGET_DIR)/furi-hal
|
||||
C_SOURCES += $(wildcard $(TARGET_DIR)/furi-hal/*.c)
|
||||
|
||||
# Version generation
|
||||
C_SOURCES += $(PROJECT_ROOT)/lib/toolbox/version.c
|
||||
|
||||
ASM_SOURCES += $(wildcard $(TARGET_DIR)/*.s)
|
||||
C_SOURCES += $(wildcard $(TARGET_DIR)/*.c)
|
||||
CPP_SOURCES += $(wildcard $(TARGET_DIR)/*.cpp)
|
||||
|
||||
SVD_FILE = $(PROJECT_ROOT)/debug/STM32WB55_CM4.svd
|
||||
@ -19,6 +19,15 @@
|
||||
})
|
||||
#endif
|
||||
|
||||
#ifndef ROUND_UP_TO
|
||||
#define ROUND_UP_TO(a, b) \
|
||||
({ \
|
||||
__typeof__(a) _a = (a); \
|
||||
__typeof__(b) _b = (b); \
|
||||
_a / _b + !!(_a % _b); \
|
||||
})
|
||||
#endif
|
||||
|
||||
#ifndef CLAMP
|
||||
#define CLAMP(x, upper, lower) (MIN(upper, MAX(x, lower)))
|
||||
#endif
|
||||
@ -40,3 +49,11 @@
|
||||
y = SWAP; \
|
||||
} while(0)
|
||||
#endif
|
||||
|
||||
#ifndef PLACE_IN_SECTION
|
||||
#define PLACE_IN_SECTION(x) __attribute__((section(x)))
|
||||
#endif
|
||||
|
||||
#ifndef ALIGN
|
||||
#define ALIGN(n) __attribute__((aligned(n)))
|
||||
#endif
|
||||
|
||||
@ -6,7 +6,7 @@ CLANG_FORMAT_BIN="/usr/bin/clang-format-12"
|
||||
|
||||
PROJECT_DIR=$(pwd)
|
||||
|
||||
cd $PROJECT_DIR
|
||||
cd "$PROJECT_DIR" || exit
|
||||
|
||||
echo "RUN C\C++ SYNTAX CHECK"
|
||||
C_FILES=$(find . \
|
||||
@ -34,7 +34,7 @@ fi
|
||||
|
||||
read -p "Do you want fix syntax? (y/n): " confirm && [[ $confirm == [yY] || $confirm == [yY][eE][sS] ]] || exit 1
|
||||
|
||||
cd $PROJECT_DIR
|
||||
cd "$PROJECT_DIR" || exit
|
||||
|
||||
# We use root in container and clang-format rewriting files. We'll need change owner to original
|
||||
local_user=$(stat -c '%u' .clang-format)
|
||||
|
||||
@ -118,6 +118,14 @@ to exclude the API function. */
|
||||
#define INCLUDE_xTaskGetSchedulerState 1
|
||||
#define INCLUDE_xTimerPendFunctionCall 1
|
||||
|
||||
/* CMSIS-RTOS V2 flags */
|
||||
#define configUSE_OS2_THREAD_SUSPEND_RESUME 1
|
||||
#define configUSE_OS2_THREAD_ENUMERATE 1
|
||||
#define configUSE_OS2_EVENTFLAGS_FROM_ISR 1
|
||||
#define configUSE_OS2_THREAD_FLAGS 1
|
||||
#define configUSE_OS2_TIMER 1
|
||||
#define configUSE_OS2_MUTEX 1
|
||||
|
||||
/*
|
||||
* The CMSIS-RTOS V2 FreeRTOS wrapper is dependent on the heap implementation used
|
||||
* by the application thus the correct define need to be enabled below
|
||||
|
||||
@ -6,105 +6,20 @@
|
||||
#include "ble.h"
|
||||
#include "tl.h"
|
||||
#include "app_ble.h"
|
||||
|
||||
#include "cmsis_os.h"
|
||||
#include "shci.h"
|
||||
#include "otp.h"
|
||||
#include "dis_app.h"
|
||||
#include "hrs_app.h"
|
||||
#include "cmsis_os.h"
|
||||
|
||||
#include <furi-hal.h>
|
||||
|
||||
typedef struct _tSecurityParams {
|
||||
uint8_t ioCapability;
|
||||
uint8_t mitm_mode;
|
||||
uint8_t bonding_mode;
|
||||
uint8_t Use_Fixed_Pin;
|
||||
uint8_t encryptionKeySizeMin;
|
||||
uint8_t encryptionKeySizeMax;
|
||||
uint32_t Fixed_Pin;
|
||||
uint8_t initiateSecurity;
|
||||
} tSecurityParams;
|
||||
|
||||
typedef struct _tBLEProfileGlobalContext {
|
||||
tSecurityParams bleSecurityParam;
|
||||
uint16_t gapServiceHandle;
|
||||
uint16_t devNameCharHandle;
|
||||
uint16_t appearanceCharHandle;
|
||||
uint16_t connectionHandle;
|
||||
uint8_t advtServUUIDlen;
|
||||
uint8_t advtServUUID[100];
|
||||
} BleGlobalContext_t;
|
||||
|
||||
typedef struct {
|
||||
BleGlobalContext_t BleApplicationContext_legacy;
|
||||
APP_BLE_ConnStatus_t Device_Connection_Status;
|
||||
uint8_t Advertising_mgr_timer_Id;
|
||||
} BleApplicationContext_t;
|
||||
|
||||
|
||||
#define FAST_ADV_TIMEOUT (30*1000*1000/CFG_TS_TICK_VAL) /**< 30s */
|
||||
#define INITIAL_ADV_TIMEOUT (60*1000*1000/CFG_TS_TICK_VAL) /**< 60s */
|
||||
|
||||
#define BD_ADDR_SIZE_LOCAL 6
|
||||
|
||||
#define LED_ON_TIMEOUT (0.005*1000*1000/CFG_TS_TICK_VAL) /**< 5ms */
|
||||
|
||||
PLACE_IN_SECTION("MB_MEM1") ALIGN(4) static TL_CmdPacket_t BleCmdBuffer;
|
||||
|
||||
static const uint8_t M_bd_addr[BD_ADDR_SIZE_LOCAL] =
|
||||
{
|
||||
(uint8_t)((CFG_ADV_BD_ADDRESS & 0x0000000000FF)),
|
||||
(uint8_t)((CFG_ADV_BD_ADDRESS & 0x00000000FF00) >> 8),
|
||||
(uint8_t)((CFG_ADV_BD_ADDRESS & 0x000000FF0000) >> 16),
|
||||
(uint8_t)((CFG_ADV_BD_ADDRESS & 0x0000FF000000) >> 24),
|
||||
(uint8_t)((CFG_ADV_BD_ADDRESS & 0x00FF00000000) >> 32),
|
||||
(uint8_t)((CFG_ADV_BD_ADDRESS & 0xFF0000000000) >> 40)
|
||||
};
|
||||
|
||||
static uint8_t bd_addr_udn[BD_ADDR_SIZE_LOCAL];
|
||||
|
||||
static const uint8_t BLE_CFG_IR_VALUE[16] = CFG_BLE_IRK;
|
||||
static const uint8_t BLE_CFG_ER_VALUE[16] = CFG_BLE_ERK;
|
||||
|
||||
PLACE_IN_SECTION("TAG_OTA_END") const uint32_t MagicKeywordValue = 0x94448A29 ;
|
||||
PLACE_IN_SECTION("TAG_OTA_START") const uint32_t MagicKeywordAddress = (uint32_t)&MagicKeywordValue;
|
||||
|
||||
PLACE_IN_SECTION("BLE_APP_CONTEXT") static BleApplicationContext_t BleApplicationContext;
|
||||
PLACE_IN_SECTION("BLE_APP_CONTEXT") static uint16_t AdvIntervalMin, AdvIntervalMax;
|
||||
|
||||
uint8_t manuf_data[14] = {
|
||||
sizeof(manuf_data)-1, AD_TYPE_MANUFACTURER_SPECIFIC_DATA,
|
||||
0x01/*SKD version */,
|
||||
0x00 /* Generic*/,
|
||||
0x00 /* GROUP A Feature */,
|
||||
0x00 /* GROUP A Feature */,
|
||||
0x00 /* GROUP B Feature */,
|
||||
0x00 /* GROUP B Feature */,
|
||||
0x00, /* BLE MAC start -MSB */
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00, /* BLE MAC stop */
|
||||
|
||||
};
|
||||
// PLACE_IN_SECTION("TAG_OTA_END") const uint32_t MagicKeywordValue = 0x94448A29 ;
|
||||
// PLACE_IN_SECTION("TAG_OTA_START") const uint32_t MagicKeywordAddress = (uint32_t)&MagicKeywordValue;
|
||||
|
||||
osMutexId_t MtxHciId;
|
||||
osSemaphoreId_t SemHciId;
|
||||
osThreadId_t AdvUpdateProcessId;
|
||||
osThreadId_t HciUserEvtProcessId;
|
||||
|
||||
const osThreadAttr_t AdvUpdateProcess_attr = {
|
||||
.name = CFG_ADV_UPDATE_PROCESS_NAME,
|
||||
.attr_bits = CFG_ADV_UPDATE_PROCESS_ATTR_BITS,
|
||||
.cb_mem = CFG_ADV_UPDATE_PROCESS_CB_MEM,
|
||||
.cb_size = CFG_ADV_UPDATE_PROCESS_CB_SIZE,
|
||||
.stack_mem = CFG_ADV_UPDATE_PROCESS_STACK_MEM,
|
||||
.priority = CFG_ADV_UPDATE_PROCESS_PRIORITY,
|
||||
.stack_size = CFG_ADV_UPDATE_PROCESS_STACK_SIZE
|
||||
};
|
||||
|
||||
const osThreadAttr_t HciUserEvtProcess_attr = {
|
||||
.name = CFG_HCI_USER_EVT_PROCESS_NAME,
|
||||
.attr_bits = CFG_HCI_USER_EVT_PROCESS_ATTR_BITS,
|
||||
@ -120,14 +35,6 @@ static void HciUserEvtProcess(void *argument);
|
||||
static void BLE_UserEvtRx( void * pPayload );
|
||||
static void BLE_StatusNot( HCI_TL_CmdStatus_t status );
|
||||
static void Ble_Tl_Init( void );
|
||||
static void Ble_Hci_Gap_Gatt_Init();
|
||||
static const uint8_t* BleGetBdAddress( void );
|
||||
static void Adv_Request( APP_BLE_ConnStatus_t New_Status );
|
||||
static void Add_Advertisment_Service_UUID( uint16_t servUUID );
|
||||
static void Adv_Mgr( void );
|
||||
static void AdvUpdateProcess(void *argument);
|
||||
static void Adv_Update( void );
|
||||
|
||||
|
||||
bool APP_BLE_Init() {
|
||||
SHCI_C2_Ble_Init_Cmd_Packet_t ble_init_cmd_packet = {
|
||||
@ -160,278 +67,6 @@ bool APP_BLE_Init() {
|
||||
return (SHCI_C2_BLE_Init( &ble_init_cmd_packet ) == SHCI_Success);
|
||||
}
|
||||
|
||||
bool APP_BLE_Start() {
|
||||
if (APPE_Status() != BleGlueStatusStarted) {
|
||||
return false;
|
||||
}
|
||||
// Initialization of HCI & GATT & GAP layer
|
||||
Ble_Hci_Gap_Gatt_Init();
|
||||
// Initialization of the BLE Services
|
||||
SVCCTL_Init();
|
||||
// Initialization of the BLE App Context
|
||||
BleApplicationContext.Device_Connection_Status = APP_BLE_IDLE;
|
||||
BleApplicationContext.BleApplicationContext_legacy.connectionHandle = 0xFFFF;
|
||||
// From here, all initialization are BLE application specific
|
||||
AdvUpdateProcessId = osThreadNew(AdvUpdateProcess, NULL, &AdvUpdateProcess_attr);
|
||||
|
||||
// Initialization of ADV - Ad Manufacturer Element - Support OTA Bit Masks
|
||||
#if(BLE_CFG_OTA_REBOOT_CHAR != 0)
|
||||
manuf_data[sizeof(manuf_data)-8] = CFG_FEATURE_OTA_REBOOT;
|
||||
#endif
|
||||
|
||||
// Initialize DIS Application
|
||||
DISAPP_Init();
|
||||
// Initialize HRS Application
|
||||
HRSAPP_Init();
|
||||
// Create timer to handle the connection state machine
|
||||
HW_TS_Create(CFG_TIM_PROC_ID_ISR, &(BleApplicationContext.Advertising_mgr_timer_Id), hw_ts_SingleShot, Adv_Mgr);
|
||||
|
||||
// Make device discoverable
|
||||
BleApplicationContext.BleApplicationContext_legacy.advtServUUID[0] = AD_TYPE_16_BIT_SERV_UUID;
|
||||
BleApplicationContext.BleApplicationContext_legacy.advtServUUIDlen = 1;
|
||||
Add_Advertisment_Service_UUID(HEART_RATE_SERVICE_UUID);
|
||||
/* Initialize intervals for reconnexion without intervals update */
|
||||
AdvIntervalMin = CFG_FAST_CONN_ADV_INTERVAL_MIN;
|
||||
AdvIntervalMax = CFG_FAST_CONN_ADV_INTERVAL_MAX;
|
||||
|
||||
Adv_Request(APP_BLE_FAST_ADV);
|
||||
return true;
|
||||
}
|
||||
|
||||
SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification( void *pckt )
|
||||
{
|
||||
hci_event_pckt *event_pckt;
|
||||
evt_le_meta_event *meta_evt;
|
||||
evt_blue_aci *blue_evt;
|
||||
hci_le_phy_update_complete_event_rp0 *evt_le_phy_update_complete;
|
||||
uint8_t TX_PHY, RX_PHY;
|
||||
tBleStatus ret = BLE_STATUS_INVALID_PARAMS;
|
||||
|
||||
event_pckt = (hci_event_pckt*) ((hci_uart_pckt *) pckt)->data;
|
||||
|
||||
switch (event_pckt->evt) {
|
||||
case EVT_DISCONN_COMPLETE:
|
||||
{
|
||||
hci_disconnection_complete_event_rp0 *disconnection_complete_event;
|
||||
disconnection_complete_event = (hci_disconnection_complete_event_rp0 *) event_pckt->data;
|
||||
|
||||
if (disconnection_complete_event->Connection_Handle == BleApplicationContext.BleApplicationContext_legacy.connectionHandle) {
|
||||
BleApplicationContext.BleApplicationContext_legacy.connectionHandle = 0;
|
||||
BleApplicationContext.Device_Connection_Status = APP_BLE_IDLE;
|
||||
APP_DBG_MSG("\r\n\r** DISCONNECTION EVENT WITH CLIENT \r\n");
|
||||
}
|
||||
/* restart advertising */
|
||||
Adv_Request(APP_BLE_FAST_ADV);
|
||||
furi_hal_power_insomnia_exit();
|
||||
}
|
||||
break; /* EVT_DISCONN_COMPLETE */
|
||||
|
||||
case EVT_LE_META_EVENT:
|
||||
{
|
||||
meta_evt = (evt_le_meta_event*) event_pckt->data;
|
||||
switch (meta_evt->subevent)
|
||||
{
|
||||
case EVT_LE_CONN_UPDATE_COMPLETE:
|
||||
APP_DBG_MSG("\r\n\r** CONNECTION UPDATE EVENT WITH CLIENT \r\n");
|
||||
|
||||
/* USER CODE BEGIN EVT_LE_CONN_UPDATE_COMPLETE */
|
||||
|
||||
/* USER CODE END EVT_LE_CONN_UPDATE_COMPLETE */
|
||||
break;
|
||||
case EVT_LE_PHY_UPDATE_COMPLETE:
|
||||
APP_DBG_MSG("EVT_UPDATE_PHY_COMPLETE \r\n");
|
||||
evt_le_phy_update_complete = (hci_le_phy_update_complete_event_rp0*)meta_evt->data;
|
||||
if (evt_le_phy_update_complete->Status == 0)
|
||||
{
|
||||
APP_DBG_MSG("EVT_UPDATE_PHY_COMPLETE, status ok \r\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
APP_DBG_MSG("EVT_UPDATE_PHY_COMPLETE, status nok \r\n");
|
||||
}
|
||||
|
||||
ret = hci_le_read_phy(BleApplicationContext.BleApplicationContext_legacy.connectionHandle,&TX_PHY,&RX_PHY);
|
||||
if (ret == BLE_STATUS_SUCCESS)
|
||||
{
|
||||
APP_DBG_MSG("Read_PHY success \r\n");
|
||||
|
||||
if ((TX_PHY == TX_2M) && (RX_PHY == RX_2M))
|
||||
{
|
||||
APP_DBG_MSG("PHY Param TX= %d, RX= %d \r\n", TX_PHY, RX_PHY);
|
||||
}
|
||||
else
|
||||
{
|
||||
APP_DBG_MSG("PHY Param TX= %d, RX= %d \r\n", TX_PHY, RX_PHY);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
APP_DBG_MSG("Read conf not succeess \r\n");
|
||||
}
|
||||
break;
|
||||
case EVT_LE_CONN_COMPLETE:
|
||||
{
|
||||
furi_hal_power_insomnia_enter();
|
||||
hci_le_connection_complete_event_rp0 *connection_complete_event;
|
||||
|
||||
/**
|
||||
* The connection is done, there is no need anymore to schedule the LP ADV
|
||||
*/
|
||||
connection_complete_event = (hci_le_connection_complete_event_rp0 *) meta_evt->data;
|
||||
|
||||
HW_TS_Stop(BleApplicationContext.Advertising_mgr_timer_Id);
|
||||
|
||||
APP_DBG_MSG("EVT_LE_CONN_COMPLETE for connection handle 0x%x\r\n", connection_complete_event->Connection_Handle);
|
||||
if (BleApplicationContext.Device_Connection_Status == APP_BLE_LP_CONNECTING)
|
||||
{
|
||||
/* Connection as client */
|
||||
BleApplicationContext.Device_Connection_Status = APP_BLE_CONNECTED_CLIENT;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Connection as server */
|
||||
BleApplicationContext.Device_Connection_Status = APP_BLE_CONNECTED_SERVER;
|
||||
}
|
||||
BleApplicationContext.BleApplicationContext_legacy.connectionHandle = connection_complete_event->Connection_Handle;
|
||||
}
|
||||
break; /* HCI_EVT_LE_CONN_COMPLETE */
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
break; /* HCI_EVT_LE_META_EVENT */
|
||||
|
||||
case EVT_VENDOR:
|
||||
blue_evt = (evt_blue_aci*) event_pckt->data;
|
||||
switch (blue_evt->ecode) {
|
||||
aci_gap_pairing_complete_event_rp0 *pairing_complete;
|
||||
|
||||
case EVT_BLUE_GAP_LIMITED_DISCOVERABLE:
|
||||
APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_LIMITED_DISCOVERABLE \r\n");
|
||||
break; /* EVT_BLUE_GAP_LIMITED_DISCOVERABLE */
|
||||
|
||||
case EVT_BLUE_GAP_PASS_KEY_REQUEST:
|
||||
APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_PASS_KEY_REQUEST \r\n");
|
||||
|
||||
aci_gap_pass_key_resp(BleApplicationContext.BleApplicationContext_legacy.connectionHandle,123456);
|
||||
|
||||
APP_DBG_MSG("\r\n\r** aci_gap_pass_key_resp \r\n");
|
||||
break; /* EVT_BLUE_GAP_PASS_KEY_REQUEST */
|
||||
|
||||
case EVT_BLUE_GAP_AUTHORIZATION_REQUEST:
|
||||
APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_AUTHORIZATION_REQUEST \r\n");
|
||||
break; /* EVT_BLUE_GAP_AUTHORIZATION_REQUEST */
|
||||
|
||||
case EVT_BLUE_GAP_SLAVE_SECURITY_INITIATED:
|
||||
APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_SLAVE_SECURITY_INITIATED \r\n");
|
||||
break; /* EVT_BLUE_GAP_SLAVE_SECURITY_INITIATED */
|
||||
|
||||
case EVT_BLUE_GAP_BOND_LOST:
|
||||
APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_BOND_LOST \r\n");
|
||||
aci_gap_allow_rebond(BleApplicationContext.BleApplicationContext_legacy.connectionHandle);
|
||||
APP_DBG_MSG("\r\n\r** Send allow rebond \r\n");
|
||||
break; /* EVT_BLUE_GAP_BOND_LOST */
|
||||
|
||||
case EVT_BLUE_GAP_DEVICE_FOUND:
|
||||
APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_DEVICE_FOUND \r\n");
|
||||
break; /* EVT_BLUE_GAP_DEVICE_FOUND */
|
||||
|
||||
case EVT_BLUE_GAP_ADDR_NOT_RESOLVED:
|
||||
APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_DEVICE_FOUND \r\n");
|
||||
break; /* EVT_BLUE_GAP_DEVICE_FOUND */
|
||||
|
||||
case (EVT_BLUE_GAP_KEYPRESS_NOTIFICATION):
|
||||
APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_KEYPRESS_NOTIFICATION \r\n");
|
||||
break; /* EVT_BLUE_GAP_KEY_PRESS_NOTIFICATION */
|
||||
|
||||
case (EVT_BLUE_GAP_NUMERIC_COMPARISON_VALUE):
|
||||
APP_DBG_MSG("numeric_value = %ld\r\n",
|
||||
((aci_gap_numeric_comparison_value_event_rp0 *)(blue_evt->data))->Numeric_Value);
|
||||
|
||||
APP_DBG_MSG("Hex_value = %lx\r\n",
|
||||
((aci_gap_numeric_comparison_value_event_rp0 *)(blue_evt->data))->Numeric_Value);
|
||||
|
||||
aci_gap_numeric_comparison_value_confirm_yesno(BleApplicationContext.BleApplicationContext_legacy.connectionHandle, 1); /* CONFIRM_YES = 1 */
|
||||
|
||||
APP_DBG_MSG("\r\n\r** aci_gap_numeric_comparison_value_confirm_yesno-->YES \r\n");
|
||||
break;
|
||||
|
||||
case (EVT_BLUE_GAP_PAIRING_CMPLT):
|
||||
{
|
||||
pairing_complete = (aci_gap_pairing_complete_event_rp0*)blue_evt->data;
|
||||
|
||||
APP_DBG_MSG("BLE_CTRL_App_Notification: EVT_BLUE_GAP_PAIRING_CMPLT, pairing_complete->Status = %d\r\n",pairing_complete->Status);
|
||||
if (pairing_complete->Status == 0) {
|
||||
APP_DBG_MSG("\r\n\r** Pairing OK \r\n");
|
||||
} else {
|
||||
APP_DBG_MSG("\r\n\r** Pairing KO \r\n");
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
/* USER CODE END ecode */
|
||||
case EVT_BLUE_GAP_PROCEDURE_COMPLETE:
|
||||
APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_PROCEDURE_COMPLETE \r\n");
|
||||
break;
|
||||
}
|
||||
break; /* EVT_VENDOR */
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return (SVCCTL_UserEvtFlowEnable);
|
||||
}
|
||||
|
||||
APP_BLE_ConnStatus_t APP_BLE_Get_Server_Connection_Status() {
|
||||
return BleApplicationContext.Device_Connection_Status;
|
||||
}
|
||||
|
||||
/* USER CODE BEGIN FD*/
|
||||
void APP_BLE_Key_Button1_Action() {
|
||||
tBleStatus ret = BLE_STATUS_INVALID_PARAMS;
|
||||
ret = aci_gap_clear_security_db();
|
||||
if (ret == BLE_STATUS_SUCCESS) {
|
||||
APP_DBG_MSG("Successfully aci_gap_clear_security_db()\r\n");
|
||||
} else {
|
||||
APP_DBG_MSG("aci_gap_clear_security_db() Failed , result: %d \r\n", ret);
|
||||
}
|
||||
}
|
||||
|
||||
void APP_BLE_Key_Button2_Action() {
|
||||
tBleStatus ret = BLE_STATUS_INVALID_PARAMS;
|
||||
ret = aci_gap_slave_security_req(BleApplicationContext.BleApplicationContext_legacy.connectionHandle);
|
||||
if (ret == BLE_STATUS_SUCCESS) {
|
||||
APP_DBG_MSG("Successfully aci_gap_slave_security_req()");
|
||||
} else {
|
||||
APP_DBG_MSG("aci_gap_slave_security_req() Failed , result: %d \r\n", ret);
|
||||
}
|
||||
}
|
||||
|
||||
void APP_BLE_Key_Button3_Action() {
|
||||
uint8_t TX_PHY, RX_PHY;
|
||||
tBleStatus ret = BLE_STATUS_INVALID_PARAMS;
|
||||
ret = hci_le_read_phy(BleApplicationContext.BleApplicationContext_legacy.connectionHandle,&TX_PHY,&RX_PHY);
|
||||
if (ret == BLE_STATUS_SUCCESS) {
|
||||
APP_DBG_MSG("Read_PHY success \r\n");
|
||||
APP_DBG_MSG("PHY Param TX= %d, RX= %d \r\n", TX_PHY, RX_PHY);
|
||||
if ((TX_PHY == TX_2M) && (RX_PHY == RX_2M)) {
|
||||
APP_DBG_MSG("hci_le_set_phy PHY Param TX= %d, RX= %d \r\n", TX_1M, RX_1M);
|
||||
ret = hci_le_set_phy(BleApplicationContext.BleApplicationContext_legacy.connectionHandle,ALL_PHYS_PREFERENCE,TX_1M,RX_1M,0);
|
||||
} else {
|
||||
APP_DBG_MSG("hci_le_set_phy PHY Param TX= %d, RX= %d \r\n", TX_2M_PREFERRED, RX_2M_PREFERRED);
|
||||
ret = hci_le_set_phy(BleApplicationContext.BleApplicationContext_legacy.connectionHandle,ALL_PHYS_PREFERENCE,TX_2M_PREFERRED,RX_2M_PREFERRED,0);
|
||||
}
|
||||
} else {
|
||||
APP_DBG_MSG("Read conf not succeess \r\n");
|
||||
}
|
||||
|
||||
if (ret == BLE_STATUS_SUCCESS) {
|
||||
APP_DBG_MSG("set PHY cmd ok\r\n");
|
||||
} else {
|
||||
APP_DBG_MSG("set PHY cmd NOK\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void Ble_Tl_Init( void ) {
|
||||
HCI_TL_HciInitConf_t Hci_Tl_Init_Conf;
|
||||
|
||||
@ -443,308 +78,6 @@ static void Ble_Tl_Init( void ) {
|
||||
hci_init(BLE_UserEvtRx, (void*) &Hci_Tl_Init_Conf);
|
||||
}
|
||||
|
||||
static void Ble_Hci_Gap_Gatt_Init() {
|
||||
uint8_t role;
|
||||
uint16_t gap_service_handle, gap_dev_name_char_handle, gap_appearance_char_handle;
|
||||
const uint8_t *bd_addr;
|
||||
uint32_t srd_bd_addr[2];
|
||||
uint16_t appearance[1] = { BLE_CFG_GAP_APPEARANCE };
|
||||
|
||||
/*HCI Reset to synchronise BLE Stack*/
|
||||
hci_reset();
|
||||
|
||||
/**
|
||||
* Write the BD Address
|
||||
*/
|
||||
bd_addr = BleGetBdAddress();
|
||||
aci_hal_write_config_data(CONFIG_DATA_PUBADDR_OFFSET,
|
||||
CONFIG_DATA_PUBADDR_LEN,
|
||||
(uint8_t*) bd_addr);
|
||||
|
||||
/* BLE MAC in ADV Packet */
|
||||
manuf_data[ sizeof(manuf_data)-6] = bd_addr[5];
|
||||
manuf_data[ sizeof(manuf_data)-5] = bd_addr[4];
|
||||
manuf_data[ sizeof(manuf_data)-4] = bd_addr[3];
|
||||
manuf_data[ sizeof(manuf_data)-3] = bd_addr[2];
|
||||
manuf_data[ sizeof(manuf_data)-2] = bd_addr[1];
|
||||
manuf_data[ sizeof(manuf_data)-1] = bd_addr[0];
|
||||
|
||||
/**
|
||||
* Write Identity root key used to derive LTK and CSRK
|
||||
*/
|
||||
aci_hal_write_config_data(CONFIG_DATA_IR_OFFSET,
|
||||
CONFIG_DATA_IR_LEN,
|
||||
(uint8_t*) BLE_CFG_IR_VALUE);
|
||||
|
||||
/**
|
||||
* Write Encryption root key used to derive LTK and CSRK
|
||||
*/
|
||||
aci_hal_write_config_data(CONFIG_DATA_ER_OFFSET,
|
||||
CONFIG_DATA_ER_LEN,
|
||||
(uint8_t*) BLE_CFG_ER_VALUE);
|
||||
|
||||
/**
|
||||
* Write random bd_address
|
||||
*/
|
||||
/* random_bd_address = R_bd_address;
|
||||
aci_hal_write_config_data(CONFIG_DATA_RANDOM_ADDRESS_WR,
|
||||
CONFIG_DATA_RANDOM_ADDRESS_LEN,
|
||||
(uint8_t*) random_bd_address);
|
||||
*/
|
||||
|
||||
/**
|
||||
* Static random Address
|
||||
* The two upper bits shall be set to 1
|
||||
* The lowest 32bits is read from the UDN to differentiate between devices
|
||||
* The RNG may be used to provide a random number on each power on
|
||||
*/
|
||||
srd_bd_addr[1] = 0x0000ED6E;
|
||||
srd_bd_addr[0] = LL_FLASH_GetUDN( );
|
||||
aci_hal_write_config_data( CONFIG_DATA_RANDOM_ADDRESS_OFFSET, CONFIG_DATA_RANDOM_ADDRESS_LEN, (uint8_t*)srd_bd_addr );
|
||||
|
||||
/**
|
||||
* Write Identity root key used to derive LTK and CSRK
|
||||
*/
|
||||
aci_hal_write_config_data( CONFIG_DATA_IR_OFFSET, CONFIG_DATA_IR_LEN, (uint8_t*)BLE_CFG_IR_VALUE );
|
||||
|
||||
/**
|
||||
* Write Encryption root key used to derive LTK and CSRK
|
||||
*/
|
||||
aci_hal_write_config_data( CONFIG_DATA_ER_OFFSET, CONFIG_DATA_ER_LEN, (uint8_t*)BLE_CFG_ER_VALUE );
|
||||
|
||||
/**
|
||||
* Set TX Power to 0dBm.
|
||||
*/
|
||||
aci_hal_set_tx_power_level(1, CFG_TX_POWER);
|
||||
|
||||
/**
|
||||
* Initialize GATT interface
|
||||
*/
|
||||
aci_gatt_init();
|
||||
|
||||
/**
|
||||
* Initialize GAP interface
|
||||
*/
|
||||
role = 0;
|
||||
|
||||
#if (BLE_CFG_PERIPHERAL == 1)
|
||||
role |= GAP_PERIPHERAL_ROLE;
|
||||
#endif
|
||||
|
||||
#if (BLE_CFG_CENTRAL == 1)
|
||||
role |= GAP_CENTRAL_ROLE;
|
||||
#endif
|
||||
|
||||
if (role > 0)
|
||||
{
|
||||
const char *name = furi_hal_version_get_device_name_ptr();
|
||||
aci_gap_init(role, 0,
|
||||
strlen(name),
|
||||
&gap_service_handle, &gap_dev_name_char_handle, &gap_appearance_char_handle);
|
||||
|
||||
if (aci_gatt_update_char_value(gap_service_handle, gap_dev_name_char_handle, 0, strlen(name), (uint8_t *) name))
|
||||
{
|
||||
BLE_DBG_SVCCTL_MSG("Device Name aci_gatt_update_char_value failed.\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
if(aci_gatt_update_char_value(gap_service_handle,
|
||||
gap_appearance_char_handle,
|
||||
0,
|
||||
2,
|
||||
(uint8_t *)&appearance))
|
||||
{
|
||||
BLE_DBG_SVCCTL_MSG("Appearance aci_gatt_update_char_value failed.\r\n");
|
||||
}
|
||||
/**
|
||||
* Initialize Default PHY
|
||||
*/
|
||||
hci_le_set_default_phy(ALL_PHYS_PREFERENCE,TX_2M_PREFERRED,RX_2M_PREFERRED);
|
||||
|
||||
/**
|
||||
* Initialize IO capability
|
||||
*/
|
||||
BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.ioCapability = CFG_IO_CAPABILITY;
|
||||
aci_gap_set_io_capability(BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.ioCapability);
|
||||
|
||||
/**
|
||||
* Initialize authentication
|
||||
*/
|
||||
BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.mitm_mode = CFG_MITM_PROTECTION;
|
||||
BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.encryptionKeySizeMin = CFG_ENCRYPTION_KEY_SIZE_MIN;
|
||||
BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.encryptionKeySizeMax = CFG_ENCRYPTION_KEY_SIZE_MAX;
|
||||
BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.Use_Fixed_Pin = CFG_USED_FIXED_PIN;
|
||||
BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.Fixed_Pin = CFG_FIXED_PIN;
|
||||
BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.bonding_mode = CFG_BONDING_MODE;
|
||||
|
||||
aci_gap_set_authentication_requirement(BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.bonding_mode,
|
||||
BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.mitm_mode,
|
||||
CFG_SC_SUPPORT,
|
||||
CFG_KEYPRESS_NOTIFICATION_SUPPORT,
|
||||
BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.encryptionKeySizeMin,
|
||||
BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.encryptionKeySizeMax,
|
||||
BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.Use_Fixed_Pin,
|
||||
BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.Fixed_Pin,
|
||||
PUBLIC_ADDR
|
||||
);
|
||||
|
||||
/**
|
||||
* Initialize whitelist
|
||||
*/
|
||||
if (BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.bonding_mode)
|
||||
{
|
||||
aci_gap_configure_whitelist();
|
||||
}
|
||||
}
|
||||
|
||||
static void Adv_Request(APP_BLE_ConnStatus_t New_Status)
|
||||
{
|
||||
tBleStatus ret = BLE_STATUS_INVALID_PARAMS;
|
||||
uint16_t Min_Inter, Max_Inter;
|
||||
|
||||
if (New_Status == APP_BLE_FAST_ADV)
|
||||
{
|
||||
Min_Inter = AdvIntervalMin;
|
||||
Max_Inter = AdvIntervalMax;
|
||||
}
|
||||
else
|
||||
{
|
||||
Min_Inter = CFG_LP_CONN_ADV_INTERVAL_MIN;
|
||||
Max_Inter = CFG_LP_CONN_ADV_INTERVAL_MAX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the timer, it will be restarted for a new shot
|
||||
* It does not hurt if the timer was not running
|
||||
*/
|
||||
HW_TS_Stop(BleApplicationContext.Advertising_mgr_timer_Id);
|
||||
|
||||
APP_DBG_MSG("First index in %d state \r\n", BleApplicationContext.Device_Connection_Status);
|
||||
|
||||
if ((New_Status == APP_BLE_LP_ADV)
|
||||
&& ((BleApplicationContext.Device_Connection_Status == APP_BLE_FAST_ADV)
|
||||
|| (BleApplicationContext.Device_Connection_Status == APP_BLE_LP_ADV)))
|
||||
{
|
||||
/* Connection in ADVERTISE mode have to stop the current advertising */
|
||||
ret = aci_gap_set_non_discoverable();
|
||||
if (ret == BLE_STATUS_SUCCESS)
|
||||
{
|
||||
APP_DBG_MSG("Successfully Stopped Advertising \r\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
APP_DBG_MSG("Stop Advertising Failed , result: %d \r\n", ret);
|
||||
}
|
||||
}
|
||||
|
||||
BleApplicationContext.Device_Connection_Status = New_Status;
|
||||
|
||||
const char* name = furi_hal_version_get_ble_local_device_name_ptr();
|
||||
|
||||
/* Start Fast or Low Power Advertising */
|
||||
ret = aci_gap_set_discoverable(
|
||||
ADV_IND,
|
||||
Min_Inter,
|
||||
Max_Inter,
|
||||
PUBLIC_ADDR,
|
||||
NO_WHITE_LIST_USE, /* use white list */
|
||||
strlen(name),
|
||||
(uint8_t*)name,
|
||||
BleApplicationContext.BleApplicationContext_legacy.advtServUUIDlen,
|
||||
BleApplicationContext.BleApplicationContext_legacy.advtServUUID,
|
||||
0,
|
||||
0);
|
||||
|
||||
/* Update Advertising data */
|
||||
ret = aci_gap_update_adv_data(sizeof(manuf_data), (uint8_t*) manuf_data);
|
||||
if (ret == BLE_STATUS_SUCCESS) {
|
||||
if (New_Status == APP_BLE_FAST_ADV) {
|
||||
APP_DBG_MSG("Successfully Start Fast Advertising \r\n" );
|
||||
/* Start Timer to STOP ADV - TIMEOUT */
|
||||
HW_TS_Start(BleApplicationContext.Advertising_mgr_timer_Id, INITIAL_ADV_TIMEOUT);
|
||||
} else {
|
||||
APP_DBG_MSG("Successfully Start Low Power Advertising \r\n");
|
||||
}
|
||||
} else {
|
||||
if (New_Status == APP_BLE_FAST_ADV) {
|
||||
APP_DBG_MSG("Start Fast Advertising Failed , result: %d \r\n", ret);
|
||||
} else {
|
||||
APP_DBG_MSG("Start Low Power Advertising Failed , result: %d \r\n", ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const uint8_t* BleGetBdAddress( void ) {
|
||||
uint8_t *otp_addr;
|
||||
const uint8_t *bd_addr;
|
||||
uint32_t udn;
|
||||
uint32_t company_id;
|
||||
uint32_t device_id;
|
||||
|
||||
udn = LL_FLASH_GetUDN();
|
||||
|
||||
if(udn != 0xFFFFFFFF) {
|
||||
company_id = LL_FLASH_GetSTCompanyID();
|
||||
device_id = LL_FLASH_GetDeviceID();
|
||||
|
||||
bd_addr_udn[0] = (uint8_t)(udn & 0x000000FF);
|
||||
bd_addr_udn[1] = (uint8_t)( (udn & 0x0000FF00) >> 8 );
|
||||
bd_addr_udn[2] = (uint8_t)( (udn & 0x00FF0000) >> 16 );
|
||||
bd_addr_udn[3] = (uint8_t)device_id;
|
||||
bd_addr_udn[4] = (uint8_t)(company_id & 0x000000FF);;
|
||||
bd_addr_udn[5] = (uint8_t)( (company_id & 0x0000FF00) >> 8 );
|
||||
|
||||
bd_addr = (const uint8_t *)bd_addr_udn;
|
||||
} else {
|
||||
otp_addr = OTP_Read(0);
|
||||
if(otp_addr) {
|
||||
bd_addr = ((OTP_ID0_t*)otp_addr)->bd_address;
|
||||
} else {
|
||||
bd_addr = M_bd_addr;
|
||||
}
|
||||
}
|
||||
|
||||
return bd_addr;
|
||||
}
|
||||
|
||||
/*************************************************************
|
||||
*
|
||||
*SPECIFIC FUNCTIONS
|
||||
*
|
||||
*************************************************************/
|
||||
static void Add_Advertisment_Service_UUID( uint16_t servUUID ) {
|
||||
BleApplicationContext.BleApplicationContext_legacy.advtServUUID[BleApplicationContext.BleApplicationContext_legacy.advtServUUIDlen] =
|
||||
(uint8_t) (servUUID & 0xFF);
|
||||
BleApplicationContext.BleApplicationContext_legacy.advtServUUIDlen++;
|
||||
BleApplicationContext.BleApplicationContext_legacy.advtServUUID[BleApplicationContext.BleApplicationContext_legacy.advtServUUIDlen] =
|
||||
(uint8_t) (servUUID >> 8) & 0xFF;
|
||||
BleApplicationContext.BleApplicationContext_legacy.advtServUUIDlen++;
|
||||
|
||||
}
|
||||
|
||||
static void Adv_Mgr( void ) {
|
||||
/**
|
||||
* The code shall be executed in the background as an aci command may be sent
|
||||
* The background is the only place where the application can make sure a new aci command
|
||||
* is not sent if there is a pending one
|
||||
*/
|
||||
osThreadFlagsSet( AdvUpdateProcessId, 1 );
|
||||
}
|
||||
|
||||
static void AdvUpdateProcess(void *argument) {
|
||||
UNUSED(argument);
|
||||
|
||||
for(;;) {
|
||||
osThreadFlagsWait( 1, osFlagsWaitAny, osWaitForever);
|
||||
Adv_Update( );
|
||||
}
|
||||
}
|
||||
|
||||
static void Adv_Update( void ) {
|
||||
Adv_Request(APP_BLE_LP_ADV);
|
||||
|
||||
}
|
||||
|
||||
static void HciUserEvtProcess(void *argument) {
|
||||
UNUSED(argument);
|
||||
|
||||
|
||||
@ -7,24 +7,7 @@ extern "C" {
|
||||
#include <stdbool.h>
|
||||
#include "hci_tl.h"
|
||||
|
||||
typedef enum {
|
||||
APP_BLE_IDLE,
|
||||
APP_BLE_FAST_ADV,
|
||||
APP_BLE_LP_ADV,
|
||||
APP_BLE_SCAN,
|
||||
APP_BLE_LP_CONNECTING,
|
||||
APP_BLE_CONNECTED_SERVER,
|
||||
APP_BLE_CONNECTED_CLIENT
|
||||
} APP_BLE_ConnStatus_t;
|
||||
|
||||
bool APP_BLE_Init();
|
||||
bool APP_BLE_Start();
|
||||
|
||||
APP_BLE_ConnStatus_t APP_BLE_Get_Server_Connection_Status();
|
||||
|
||||
void APP_BLE_Key_Button1_Action();
|
||||
void APP_BLE_Key_Button2_Action();
|
||||
void APP_BLE_Key_Button3_Action();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@ -32,81 +32,12 @@ extern "C"{
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include <furi/common_defines.h>
|
||||
|
||||
#include "app_conf.h"
|
||||
|
||||
/* -------------------------------- *
|
||||
* Basic definitions *
|
||||
* -------------------------------- */
|
||||
|
||||
#undef NULL
|
||||
#define NULL 0
|
||||
|
||||
#undef FALSE
|
||||
#define FALSE 0
|
||||
|
||||
#undef TRUE
|
||||
#define TRUE (!0)
|
||||
|
||||
/* -------------------------------- *
|
||||
* Critical Section definition *
|
||||
* -------------------------------- */
|
||||
#define BACKUP_PRIMASK() uint32_t primask_bit= __get_PRIMASK()
|
||||
#define DISABLE_IRQ() __disable_irq()
|
||||
#define RESTORE_PRIMASK() __set_PRIMASK(primask_bit)
|
||||
|
||||
/* -------------------------------- *
|
||||
* Macro delimiters *
|
||||
* -------------------------------- */
|
||||
|
||||
#define M_BEGIN do {
|
||||
|
||||
#define M_END } while(0)
|
||||
|
||||
/* -------------------------------- *
|
||||
* Some useful macro definitions *
|
||||
* -------------------------------- */
|
||||
|
||||
|
||||
#define MODINC( a, m ) M_BEGIN (a)++; if ((a)>=(m)) (a)=0; M_END
|
||||
|
||||
#define MODDEC( a, m ) M_BEGIN if ((a)==0) (a)=(m); (a)--; M_END
|
||||
|
||||
#define MODADD( a, b, m ) M_BEGIN (a)+=(b); if ((a)>=(m)) (a)-=(m); M_END
|
||||
|
||||
#define MODSUB( a, b, m ) MODADD( a, (m)-(b), m )
|
||||
|
||||
#define PAUSE( t ) M_BEGIN \
|
||||
__IO int _i; \
|
||||
for ( _i = t; _i > 0; _i -- ); \
|
||||
M_END
|
||||
|
||||
#define DIVF( x, y ) ((x)/(y))
|
||||
|
||||
#define DIVC( x, y ) (((x)+(y)-1)/(y))
|
||||
|
||||
#define DIVR( x, y ) (((x)+((y)/2))/(y))
|
||||
|
||||
#define SHRR( x, n ) ((((x)>>((n)-1))+1)>>1)
|
||||
|
||||
#define BITN( w, n ) (((w)[(n)/32] >> ((n)%32)) & 1)
|
||||
|
||||
#define BITNSET( w, n, b ) M_BEGIN (w)[(n)/32] |= ((U32)(b))<<((n)%32); M_END
|
||||
|
||||
/* -------------------------------- *
|
||||
* Compiler *
|
||||
* -------------------------------- */
|
||||
#define PLACE_IN_SECTION( __x__ ) __attribute__((section (__x__)))
|
||||
|
||||
#ifdef WIN32
|
||||
#define ALIGN(n)
|
||||
#else
|
||||
#define ALIGN(n) __attribute__((aligned(n)))
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /*APP_COMMON_H */
|
||||
|
||||
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
|
||||
#endif
|
||||
@ -139,7 +139,7 @@
|
||||
/**
|
||||
* Maximum supported ATT_MTU size
|
||||
*/
|
||||
#define CFG_BLE_MAX_ATT_MTU (156)
|
||||
#define CFG_BLE_MAX_ATT_MTU (251)
|
||||
|
||||
/**
|
||||
* Size of the storage area for Attribute values
|
||||
|
||||
@ -1,367 +0,0 @@
|
||||
/* USER CODE BEGIN Header */
|
||||
/**
|
||||
******************************************************************************
|
||||
* File Name : app_debug.c
|
||||
* Description : Debug capabilities source file for STM32WPAN Middleware
|
||||
******************************************************************************
|
||||
* @attention
|
||||
*
|
||||
* <h2><center>© Copyright (c) 2020 STMicroelectronics.
|
||||
* All rights reserved.</center></h2>
|
||||
*
|
||||
* This software component is licensed by ST under Ultimate Liberty license
|
||||
* SLA0044, the "License"; You may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at:
|
||||
* www.st.com/SLA0044
|
||||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
/* USER CODE END Header */
|
||||
|
||||
/* Includes ------------------------------------------------------------------*/
|
||||
/* USER CODE BEGIN Includes */
|
||||
#include "app_common.h"
|
||||
|
||||
#include "app_debug.h"
|
||||
#include "utilities_common.h"
|
||||
#include "shci.h"
|
||||
#include "tl.h"
|
||||
#include "dbg_trace.h"
|
||||
#include <furi-hal.h>
|
||||
/* USER CODE END Includes */
|
||||
|
||||
/* Private typedef -----------------------------------------------------------*/
|
||||
/* USER CODE BEGIN PTD */
|
||||
typedef PACKED_STRUCT
|
||||
{
|
||||
GPIO_TypeDef* port;
|
||||
uint16_t pin;
|
||||
uint8_t enable;
|
||||
uint8_t reserved;
|
||||
} APPD_GpioConfig_t;
|
||||
/* USER CODE END PTD */
|
||||
|
||||
/* Private defines -----------------------------------------------------------*/
|
||||
/* USER CODE BEGIN PD */
|
||||
#define GPIO_NBR_OF_RF_SIGNALS 9
|
||||
#define GPIO_CFG_NBR_OF_FEATURES 34
|
||||
#define NBR_OF_TRACES_CONFIG_PARAMETERS 4
|
||||
#define NBR_OF_GENERAL_CONFIG_PARAMETERS 4
|
||||
|
||||
/**
|
||||
* THIS SHALL BE SET TO A VALUE DIFFERENT FROM 0 ONLY ON REQUEST FROM ST SUPPORT
|
||||
*/
|
||||
#define BLE_DTB_CFG 0
|
||||
#define SYS_DBG_CFG1 (SHCI_C2_DEBUG_OPTIONS_IPCORE_LP | SHCI_C2_DEBUG_OPTIONS_CPU2_STOP_EN)
|
||||
/* USER CODE END PD */
|
||||
|
||||
/* Private variables ---------------------------------------------------------*/
|
||||
/* USER CODE BEGIN PV */
|
||||
PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static SHCI_C2_DEBUG_TracesConfig_t APPD_TracesConfig={0, 0, 0, 0};
|
||||
PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static SHCI_C2_DEBUG_GeneralConfig_t APPD_GeneralConfig={BLE_DTB_CFG, SYS_DBG_CFG1, {0, 0}};
|
||||
|
||||
/**
|
||||
* THE DEBUG ON GPIO FOR CPU2 IS INTENDED TO BE USED ONLY ON REQUEST FROM ST SUPPORT
|
||||
* It provides timing information on the CPU2 activity.
|
||||
* All configuration of (port, pin) is supported for each features and can be selected by the user
|
||||
* depending on the availability
|
||||
*/
|
||||
static const APPD_GpioConfig_t aGpioConfigList[GPIO_CFG_NBR_OF_FEATURES] =
|
||||
{
|
||||
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* BLE_ISR - Set on Entry / Reset on Exit */
|
||||
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* BLE_STACK_TICK - Set on Entry / Reset on Exit */
|
||||
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* BLE_CMD_PROCESS - Set on Entry / Reset on Exit */
|
||||
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* BLE_ACL_DATA_PROCESS - Set on Entry / Reset on Exit */
|
||||
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* SYS_CMD_PROCESS - Set on Entry / Reset on Exit */
|
||||
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* RNG_PROCESS - Set on Entry / Reset on Exit */
|
||||
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* NVM_PROCESS - Set on Entry / Reset on Exit */
|
||||
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IPCC_GENERAL - Set on Entry / Reset on Exit */
|
||||
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IPCC_BLE_CMD_RX - Set on Entry / Reset on Exit */
|
||||
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IPCC_BLE_EVT_TX - Set on Entry / Reset on Exit */
|
||||
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IPCC_BLE_ACL_DATA_RX - Set on Entry / Reset on Exit */
|
||||
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IPCC_SYS_CMD_RX - Set on Entry / Reset on Exit */
|
||||
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IPCC_SYS_EVT_TX - Set on Entry / Reset on Exit */
|
||||
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IPCC_CLI_CMD_RX - Set on Entry / Reset on Exit */
|
||||
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IPCC_OT_CMD_RX - Set on Entry / Reset on Exit */
|
||||
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IPCC_OT_ACK_TX - Set on Entry / Reset on Exit */
|
||||
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IPCC_CLI_ACK_TX - Set on Entry / Reset on Exit */
|
||||
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IPCC_MEM_MANAGER_RX - Set on Entry / Reset on Exit */
|
||||
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IPCC_TRACES_TX - Set on Entry / Reset on Exit */
|
||||
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* HARD_FAULT - Set on Entry / Reset on Exit */
|
||||
/* From v1.1.1 */
|
||||
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IP_CORE_LP_STATUS - Set on Entry / Reset on Exit */
|
||||
/* From v1.2.0 */
|
||||
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* END_OF_CONNECTION_EVENT - Set on Entry / Reset on Exit */
|
||||
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* TIMER_SERVER_CALLBACK - Toggle on Entry */
|
||||
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* PES_ACTIVITY - Set on Entry / Reset on Exit */
|
||||
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* MB_BLE_SEND_EVT - Set on Entry / Reset on Exit */
|
||||
/* From v1.3.0 */
|
||||
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* BLE_NO_DELAY - Set on Entry / Reset on Exit */
|
||||
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* BLE_STACK_STORE_NVM_CB - Set on Entry / Reset on Exit */
|
||||
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* NVMA_WRITE_ONGOING - Set on Entry / Reset on Exit */
|
||||
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* NVMA_WRITE_COMPLETE - Set on Entry / Reset on Exit */
|
||||
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* NVMA_CLEANUP - Set on Entry / Reset on Exit */
|
||||
/* From v1.4.0 */
|
||||
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* NVMA_START - Set on Entry / Reset on Exit */
|
||||
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* FLASH_EOP - Set on Entry / Reset on Exit */
|
||||
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* FLASH_WRITE - Set on Entry / Reset on Exit */
|
||||
{ GPIOA, LL_GPIO_PIN_0, 0, 0}, /* FLASH_ERASE - Set on Entry / Reset on Exit */
|
||||
};
|
||||
|
||||
/**
|
||||
* THE DEBUG ON GPIO FOR CPU2 IS INTENDED TO BE USED ONLY ON REQUEST FROM ST SUPPORT
|
||||
* This table is relevant only for BLE
|
||||
* It provides timing information on BLE RF activity.
|
||||
* New signals may be allocated at any location when requested by ST
|
||||
* The GPIO allocated to each signal depend on the BLE_DTB_CFG value and cannot be changed
|
||||
*/
|
||||
#if( BLE_DTB_CFG == 7)
|
||||
static const APPD_GpioConfig_t aRfConfigList[GPIO_NBR_OF_RF_SIGNALS] =
|
||||
{
|
||||
{ GPIOB, LL_GPIO_PIN_2, 0, 0}, /* DTB10 - Tx/Rx SPI */
|
||||
{ GPIOB, LL_GPIO_PIN_7, 0, 0}, /* DTB11 - Tx/Tx SPI Clk */
|
||||
{ GPIOA, LL_GPIO_PIN_8, 0, 0}, /* DTB12 - Tx/Rx Ready & SPI Select */
|
||||
{ GPIOA, LL_GPIO_PIN_9, 0, 0}, /* DTB13 - Tx/Rx Start */
|
||||
{ GPIOA, LL_GPIO_PIN_10, 0, 0}, /* DTB14 - FSM0 */
|
||||
{ GPIOA, LL_GPIO_PIN_11, 0, 0}, /* DTB15 - FSM1 */
|
||||
{ GPIOB, LL_GPIO_PIN_8, 0, 0}, /* DTB16 - FSM2 */
|
||||
{ GPIOB, LL_GPIO_PIN_11, 0, 0}, /* DTB17 - FSM3 */
|
||||
{ GPIOB, LL_GPIO_PIN_10, 0, 0}, /* DTB18 - FSM4 */
|
||||
};
|
||||
#endif
|
||||
/* USER CODE END PV */
|
||||
|
||||
/* Global variables ----------------------------------------------------------*/
|
||||
/* USER CODE BEGIN GV */
|
||||
/* USER CODE END GV */
|
||||
|
||||
/* Private function prototypes -----------------------------------------------*/
|
||||
/* USER CODE BEGIN PFP */
|
||||
static void APPD_SetCPU2GpioConfig( void );
|
||||
static void APPD_BleDtbCfg( void );
|
||||
/* USER CODE END PFP */
|
||||
|
||||
/* Functions Definition ------------------------------------------------------*/
|
||||
void APPD_Init( void )
|
||||
{
|
||||
/* USER CODE BEGIN APPD_Init */
|
||||
#if (CFG_DEBUGGER_SUPPORTED == 1)
|
||||
/**
|
||||
* Keep debugger enabled while in any low power mode
|
||||
*/
|
||||
HAL_DBGMCU_EnableDBGSleepMode();
|
||||
HAL_DBGMCU_EnableDBGStopMode();
|
||||
|
||||
/***************** ENABLE DEBUGGER *************************************/
|
||||
LL_EXTI_EnableIT_32_63(LL_EXTI_LINE_48);
|
||||
|
||||
#else
|
||||
GPIO_InitTypeDef gpio_config = {0};
|
||||
|
||||
gpio_config.Pull = GPIO_NOPULL;
|
||||
gpio_config.Mode = GPIO_MODE_ANALOG;
|
||||
|
||||
gpio_config.Pin = GPIO_PIN_15 | GPIO_PIN_14 | GPIO_PIN_13;
|
||||
__HAL_RCC_GPIOA_CLK_ENABLE();
|
||||
HAL_GPIO_Init(GPIOA, &gpio_config);
|
||||
__HAL_RCC_GPIOA_CLK_DISABLE();
|
||||
|
||||
gpio_config.Pin = GPIO_PIN_4 | GPIO_PIN_3;
|
||||
__HAL_RCC_GPIOB_CLK_ENABLE();
|
||||
HAL_GPIO_Init(GPIOB, &gpio_config);
|
||||
__HAL_RCC_GPIOB_CLK_DISABLE();
|
||||
|
||||
HAL_DBGMCU_DisableDBGSleepMode();
|
||||
HAL_DBGMCU_DisableDBGStopMode();
|
||||
HAL_DBGMCU_DisableDBGStandbyMode();
|
||||
|
||||
#endif /* (CFG_DEBUGGER_SUPPORTED == 1) */
|
||||
|
||||
#if(CFG_DEBUG_TRACE != 0)
|
||||
DbgTraceInit();
|
||||
#endif
|
||||
|
||||
APPD_SetCPU2GpioConfig( );
|
||||
APPD_BleDtbCfg( );
|
||||
|
||||
/* USER CODE END APPD_Init */
|
||||
return;
|
||||
}
|
||||
|
||||
void APPD_EnableCPU2( void )
|
||||
{
|
||||
/* USER CODE BEGIN APPD_EnableCPU2 */
|
||||
SHCI_C2_DEBUG_Init_Cmd_Packet_t DebugCmdPacket =
|
||||
{
|
||||
{{0,0,0}}, /**< Does not need to be initialized */
|
||||
{(uint8_t *)aGpioConfigList,
|
||||
(uint8_t *)&APPD_TracesConfig,
|
||||
(uint8_t *)&APPD_GeneralConfig,
|
||||
GPIO_CFG_NBR_OF_FEATURES,
|
||||
NBR_OF_TRACES_CONFIG_PARAMETERS,
|
||||
NBR_OF_GENERAL_CONFIG_PARAMETERS}
|
||||
};
|
||||
|
||||
/**< Traces channel initialization */
|
||||
TL_TRACES_Init( );
|
||||
|
||||
/** GPIO DEBUG Initialization */
|
||||
SHCI_C2_DEBUG_Init( &DebugCmdPacket );
|
||||
|
||||
/* USER CODE END APPD_EnableCPU2 */
|
||||
return;
|
||||
}
|
||||
|
||||
/*************************************************************
|
||||
*
|
||||
* LOCAL FUNCTIONS
|
||||
*
|
||||
*************************************************************/
|
||||
static void APPD_SetCPU2GpioConfig( void )
|
||||
{
|
||||
/* USER CODE BEGIN APPD_SetCPU2GpioConfig */
|
||||
GPIO_InitTypeDef gpio_config = {0};
|
||||
uint8_t local_loop;
|
||||
uint16_t gpioa_pin_list;
|
||||
uint16_t gpiob_pin_list;
|
||||
uint16_t gpioc_pin_list;
|
||||
|
||||
gpioa_pin_list = 0;
|
||||
gpiob_pin_list = 0;
|
||||
gpioc_pin_list = 0;
|
||||
|
||||
for(local_loop = 0 ; local_loop < GPIO_CFG_NBR_OF_FEATURES; local_loop++)
|
||||
{
|
||||
if( aGpioConfigList[local_loop].enable != 0)
|
||||
{
|
||||
switch((uint32_t)aGpioConfigList[local_loop].port)
|
||||
{
|
||||
case (uint32_t)GPIOA:
|
||||
gpioa_pin_list |= aGpioConfigList[local_loop].pin;
|
||||
break;
|
||||
|
||||
case (uint32_t)GPIOB:
|
||||
gpiob_pin_list |= aGpioConfigList[local_loop].pin;
|
||||
break;
|
||||
|
||||
case (uint32_t)GPIOC:
|
||||
gpioc_pin_list |= aGpioConfigList[local_loop].pin;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gpio_config.Pull = GPIO_NOPULL;
|
||||
gpio_config.Mode = GPIO_MODE_OUTPUT_PP;
|
||||
gpio_config.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
|
||||
|
||||
if(gpioa_pin_list != 0)
|
||||
{
|
||||
gpio_config.Pin = gpioa_pin_list;
|
||||
__HAL_RCC_GPIOA_CLK_ENABLE();
|
||||
__HAL_RCC_C2GPIOA_CLK_ENABLE();
|
||||
HAL_GPIO_Init(GPIOA, &gpio_config);
|
||||
HAL_GPIO_WritePin(GPIOA, gpioa_pin_list, GPIO_PIN_RESET);
|
||||
}
|
||||
|
||||
if(gpiob_pin_list != 0)
|
||||
{
|
||||
gpio_config.Pin = gpiob_pin_list;
|
||||
__HAL_RCC_GPIOB_CLK_ENABLE();
|
||||
__HAL_RCC_C2GPIOB_CLK_ENABLE();
|
||||
HAL_GPIO_Init(GPIOB, &gpio_config);
|
||||
HAL_GPIO_WritePin(GPIOB, gpiob_pin_list, GPIO_PIN_RESET);
|
||||
}
|
||||
|
||||
if(gpioc_pin_list != 0)
|
||||
{
|
||||
gpio_config.Pin = gpioc_pin_list;
|
||||
__HAL_RCC_GPIOC_CLK_ENABLE();
|
||||
__HAL_RCC_C2GPIOC_CLK_ENABLE();
|
||||
HAL_GPIO_Init(GPIOC, &gpio_config);
|
||||
HAL_GPIO_WritePin(GPIOC, gpioc_pin_list, GPIO_PIN_RESET);
|
||||
}
|
||||
|
||||
/* USER CODE END APPD_SetCPU2GpioConfig */
|
||||
return;
|
||||
}
|
||||
|
||||
static void APPD_BleDtbCfg( void )
|
||||
{
|
||||
/* USER CODE BEGIN APPD_BleDtbCfg */
|
||||
#if (BLE_DTB_CFG != 0)
|
||||
GPIO_InitTypeDef gpio_config = {0};
|
||||
uint8_t local_loop;
|
||||
uint16_t gpioa_pin_list;
|
||||
uint16_t gpiob_pin_list;
|
||||
|
||||
gpioa_pin_list = 0;
|
||||
gpiob_pin_list = 0;
|
||||
|
||||
for(local_loop = 0 ; local_loop < GPIO_NBR_OF_RF_SIGNALS; local_loop++)
|
||||
{
|
||||
if( aRfConfigList[local_loop].enable != 0)
|
||||
{
|
||||
switch((uint32_t)aRfConfigList[local_loop].port)
|
||||
{
|
||||
case (uint32_t)GPIOA:
|
||||
gpioa_pin_list |= aRfConfigList[local_loop].pin;
|
||||
break;
|
||||
|
||||
case (uint32_t)GPIOB:
|
||||
gpiob_pin_list |= aRfConfigList[local_loop].pin;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gpio_config.Pull = GPIO_NOPULL;
|
||||
gpio_config.Mode = GPIO_MODE_AF_PP;
|
||||
gpio_config.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
|
||||
gpio_config.Alternate = GPIO_AF6_RF_DTB7;
|
||||
|
||||
if(gpioa_pin_list != 0)
|
||||
{
|
||||
gpio_config.Pin = gpioa_pin_list;
|
||||
__HAL_RCC_GPIOA_CLK_ENABLE();
|
||||
__HAL_RCC_C2GPIOA_CLK_ENABLE();
|
||||
HAL_GPIO_Init(GPIOA, &gpio_config);
|
||||
}
|
||||
|
||||
if(gpiob_pin_list != 0)
|
||||
{
|
||||
gpio_config.Pin = gpiob_pin_list;
|
||||
__HAL_RCC_GPIOB_CLK_ENABLE();
|
||||
__HAL_RCC_C2GPIOB_CLK_ENABLE();
|
||||
HAL_GPIO_Init(GPIOB, &gpio_config);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* USER CODE END APPD_BleDtbCfg */
|
||||
return;
|
||||
}
|
||||
|
||||
/*************************************************************
|
||||
*
|
||||
* WRAP FUNCTIONS
|
||||
*
|
||||
*************************************************************/
|
||||
#if(CFG_DEBUG_TRACE != 0)
|
||||
void DbgOutputInit( void )
|
||||
{
|
||||
}
|
||||
|
||||
void DbgOutputTraces( uint8_t *p_data, uint16_t size, void (*cb)(void) )
|
||||
{
|
||||
furi_hal_console_tx(p_data, size);
|
||||
cb();
|
||||
}
|
||||
#endif
|
||||
|
||||
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
|
||||
@ -6,7 +6,6 @@
|
||||
#include "tl.h"
|
||||
#include "cmsis_os.h"
|
||||
#include "shci_tl.h"
|
||||
#include "app_debug.h"
|
||||
#include <furi-hal.h>
|
||||
|
||||
extern RTC_HandleTypeDef hrtc;
|
||||
@ -139,8 +138,10 @@ static void APPE_SysUserEvtRx( void * pPayload ) {
|
||||
// APPD_EnableCPU2( );
|
||||
|
||||
if (APP_BLE_Init()) {
|
||||
FURI_LOG_I("Core2", "BLE stack started");
|
||||
ble_glue_status = BleGlueStatusStarted;
|
||||
} else {
|
||||
FURI_LOG_E("Core2", "BLE stack startup failed");
|
||||
ble_glue_status = BleGlueStatusBroken;
|
||||
}
|
||||
furi_hal_power_insomnia_exit();
|
||||
@ -178,3 +179,16 @@ void shci_cmd_resp_wait(uint32_t timeout) {
|
||||
UNUSED(timeout);
|
||||
osSemaphoreAcquire( SemShciId, osWaitForever );
|
||||
}
|
||||
|
||||
#if(CFG_DEBUG_TRACE != 0)
|
||||
void DbgOutputInit( void )
|
||||
{
|
||||
}
|
||||
|
||||
void DbgOutputTraces( uint8_t *p_data, uint16_t size, void (*cb)(void) )
|
||||
{
|
||||
furi_hal_console_tx(p_data, size);
|
||||
cb();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
78
firmware/targets/f6/ble-glue/battery_service.c
Normal file
78
firmware/targets/f6/ble-glue/battery_service.c
Normal file
@ -0,0 +1,78 @@
|
||||
#include "battery_service.h"
|
||||
#include "app_common.h"
|
||||
#include "ble.h"
|
||||
|
||||
#include <furi.h>
|
||||
|
||||
#define BATTERY_SERVICE_TAG "battery service"
|
||||
|
||||
typedef struct {
|
||||
uint16_t svc_handle;
|
||||
uint16_t char_level_handle;
|
||||
} BatterySvc;
|
||||
|
||||
static BatterySvc* battery_svc = NULL;
|
||||
|
||||
static const uint16_t service_uuid = BATTERY_SERVICE_UUID;
|
||||
static const uint16_t char_battery_level_uuid = BATTERY_LEVEL_CHAR_UUID;
|
||||
|
||||
void battery_svc_start() {
|
||||
battery_svc = furi_alloc(sizeof(BatterySvc));
|
||||
tBleStatus status;
|
||||
|
||||
// Add Battery service
|
||||
status = aci_gatt_add_service(UUID_TYPE_16, (Service_UUID_t*)&service_uuid, PRIMARY_SERVICE, 4, &battery_svc->svc_handle);
|
||||
if(status) {
|
||||
FURI_LOG_E(BATTERY_SERVICE_TAG, "Failed to add Battery service: %d", status);
|
||||
}
|
||||
// Add Battery level characteristic
|
||||
status = aci_gatt_add_char(battery_svc->svc_handle,
|
||||
UUID_TYPE_16,
|
||||
(Char_UUID_t *) &char_battery_level_uuid,
|
||||
1,
|
||||
CHAR_PROP_READ | CHAR_PROP_NOTIFY,
|
||||
ATTR_PERMISSION_NONE,
|
||||
GATT_DONT_NOTIFY_EVENTS,
|
||||
10,
|
||||
CHAR_VALUE_LEN_CONSTANT,
|
||||
&battery_svc->char_level_handle);
|
||||
if(status) {
|
||||
FURI_LOG_E(BATTERY_SERVICE_TAG, "Failed to add Battery level characteristic: %d", status);
|
||||
}
|
||||
}
|
||||
|
||||
void battery_svc_stop() {
|
||||
tBleStatus status;
|
||||
if(battery_svc) {
|
||||
// Delete Battery level characteristic
|
||||
status = aci_gatt_del_char(battery_svc->svc_handle, battery_svc->char_level_handle);
|
||||
if(status) {
|
||||
FURI_LOG_E(BATTERY_SERVICE_TAG, "Failed to delete Battery level characteristic: %d", status);
|
||||
}
|
||||
// Delete Battery service
|
||||
status = aci_gatt_del_service(battery_svc->svc_handle);
|
||||
if(status) {
|
||||
FURI_LOG_E(BATTERY_SERVICE_TAG, "Failed to delete Battery service: %d", status);
|
||||
}
|
||||
free(battery_svc);
|
||||
battery_svc = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
bool battery_svc_update_level(uint8_t battery_charge) {
|
||||
// Check if service was started
|
||||
if(battery_svc == NULL) {
|
||||
return false;
|
||||
}
|
||||
// Update battery level characteristic
|
||||
FURI_LOG_I(BATTERY_SERVICE_TAG, "Updating battery level characteristic");
|
||||
tBleStatus result = aci_gatt_update_char_value(battery_svc->svc_handle,
|
||||
battery_svc->char_level_handle,
|
||||
0,
|
||||
1,
|
||||
&battery_charge);
|
||||
if(result) {
|
||||
FURI_LOG_E(BATTERY_SERVICE_TAG, "Failed updating RX characteristic: %d", result);
|
||||
}
|
||||
return result != BLE_STATUS_SUCCESS;
|
||||
}
|
||||
18
firmware/targets/f6/ble-glue/battery_service.h
Normal file
18
firmware/targets/f6/ble-glue/battery_service.h
Normal file
@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void battery_svc_start();
|
||||
|
||||
void battery_svc_stop();
|
||||
|
||||
bool battery_svc_update_level(uint8_t battery_level);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@ -53,47 +53,12 @@
|
||||
|
||||
#define BLE_CFG_CLT_MAX_NBR_CB 0
|
||||
|
||||
/******************************************************************************
|
||||
* Device Information Service (DIS)
|
||||
******************************************************************************/
|
||||
/**< Options: Supported(1) or Not Supported(0) */
|
||||
#define BLE_CFG_DIS_MANUFACTURER_NAME_STRING 1
|
||||
#define BLE_CFG_DIS_MODEL_NUMBER_STRING 1
|
||||
#define BLE_CFG_DIS_SERIAL_NUMBER_STRING 0
|
||||
#define BLE_CFG_DIS_HARDWARE_REVISION_STRING 0
|
||||
#define BLE_CFG_DIS_FIRMWARE_REVISION_STRING 1
|
||||
#define BLE_CFG_DIS_SOFTWARE_REVISION_STRING 1
|
||||
#define BLE_CFG_DIS_SYSTEM_ID 0
|
||||
#define BLE_CFG_DIS_IEEE_CERTIFICATION 0
|
||||
#define BLE_CFG_DIS_PNP_ID 0
|
||||
|
||||
/**
|
||||
* device information service characteristic lengths
|
||||
*/
|
||||
#define BLE_CFG_DIS_SYSTEM_ID_LEN_MAX (8)
|
||||
#define BLE_CFG_DIS_MODEL_NUMBER_STRING_LEN_MAX (32)
|
||||
#define BLE_CFG_DIS_SERIAL_NUMBER_STRING_LEN_MAX (32)
|
||||
#define BLE_CFG_DIS_FIRMWARE_REVISION_STRING_LEN_MAX (32)
|
||||
#define BLE_CFG_DIS_HARDWARE_REVISION_STRING_LEN_MAX (32)
|
||||
#define BLE_CFG_DIS_SOFTWARE_REVISION_STRING_LEN_MAX (64)
|
||||
#define BLE_CFG_DIS_MANUFACTURER_NAME_STRING_LEN_MAX (32)
|
||||
#define BLE_CFG_DIS_IEEE_CERTIFICATION_LEN_MAX (32)
|
||||
#define BLE_CFG_DIS_PNP_ID_LEN_MAX (7)
|
||||
|
||||
/******************************************************************************
|
||||
* Heart Rate Service (HRS)
|
||||
******************************************************************************/
|
||||
#define BLE_CFG_HRS_BODY_SENSOR_LOCATION_CHAR 1/**< BODY SENSOR LOCATION CHARACTERISTIC */
|
||||
#define BLE_CFG_HRS_ENERGY_EXPENDED_INFO_FLAG 1/**< ENERGY EXTENDED INFO FLAG */
|
||||
#define BLE_CFG_HRS_ENERGY_RR_INTERVAL_FLAG 1/**< Max number of RR interval values - Shall not be greater than 9 */
|
||||
|
||||
/******************************************************************************
|
||||
* GAP Service - Apprearance
|
||||
******************************************************************************/
|
||||
|
||||
#define BLE_CFG_UNKNOWN_APPEARANCE (0)
|
||||
#define BLE_CFG_HR_SENSOR_APPEARANCE (832)
|
||||
#define BLE_CFG_GAP_APPEARANCE (BLE_CFG_HR_SENSOR_APPEARANCE)
|
||||
#define BLE_CFG_GAP_APPEARANCE (0x0086)
|
||||
|
||||
/******************************************************************************
|
||||
* Over The Air Feature (OTA) - STM Proprietary
|
||||
|
||||
156
firmware/targets/f6/ble-glue/dev_info_service.c
Normal file
156
firmware/targets/f6/ble-glue/dev_info_service.c
Normal file
@ -0,0 +1,156 @@
|
||||
#include "dev_info_service.h"
|
||||
#include "app_common.h"
|
||||
#include "ble.h"
|
||||
|
||||
#include <furi.h>
|
||||
|
||||
#define DEV_INFO_SVC_TAG "dev info service"
|
||||
|
||||
typedef struct {
|
||||
uint16_t service_handle;
|
||||
uint16_t man_name_char_handle;
|
||||
uint16_t serial_num_char_handle;
|
||||
uint16_t firmware_rev_char_handle;
|
||||
uint16_t software_rev_char_handle;
|
||||
} DevInfoSvc;
|
||||
|
||||
static DevInfoSvc* dev_info_svc = NULL;
|
||||
|
||||
static const char dev_info_man_name[] = "Flipper Devices Inc.";
|
||||
static const char dev_info_serial_num[] = "1.0";
|
||||
static const char dev_info_firmware_rev_num[] = TARGET;
|
||||
static const char dev_info_software_rev_num[] = GIT_COMMIT " " GIT_BRANCH " " GIT_BRANCH_NUM " " BUILD_DATE;
|
||||
|
||||
void dev_info_svc_start() {
|
||||
dev_info_svc = furi_alloc(sizeof(DevInfoSvc));
|
||||
tBleStatus status;
|
||||
|
||||
// Add Device Information Service
|
||||
uint16_t uuid = DEVICE_INFORMATION_SERVICE_UUID;
|
||||
status = aci_gatt_add_service(UUID_TYPE_16, (Service_UUID_t*)&uuid, PRIMARY_SERVICE, 9, &dev_info_svc->service_handle);
|
||||
if(status) {
|
||||
FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to add Device Information Service: %d", status);
|
||||
}
|
||||
|
||||
// Add characteristics
|
||||
uuid = MANUFACTURER_NAME_UUID;
|
||||
status = aci_gatt_add_char(dev_info_svc->service_handle,
|
||||
UUID_TYPE_16,
|
||||
(Char_UUID_t*)&uuid,
|
||||
strlen(dev_info_man_name),
|
||||
CHAR_PROP_READ,
|
||||
ATTR_PERMISSION_NONE,
|
||||
GATT_DONT_NOTIFY_EVENTS,
|
||||
10,
|
||||
CHAR_VALUE_LEN_CONSTANT,
|
||||
&dev_info_svc->man_name_char_handle);
|
||||
if(status) {
|
||||
FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to add manufacturer name char: %d", status);
|
||||
}
|
||||
uuid = SERIAL_NUMBER_UUID;
|
||||
status = aci_gatt_add_char(dev_info_svc->service_handle,
|
||||
UUID_TYPE_16,
|
||||
(Char_UUID_t*)&uuid,
|
||||
strlen(dev_info_serial_num),
|
||||
CHAR_PROP_READ,
|
||||
ATTR_PERMISSION_NONE,
|
||||
GATT_DONT_NOTIFY_EVENTS,
|
||||
10,
|
||||
CHAR_VALUE_LEN_CONSTANT,
|
||||
&dev_info_svc->serial_num_char_handle);
|
||||
if(status) {
|
||||
FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to add serial number char: %d", status);
|
||||
}
|
||||
uuid = FIRMWARE_REVISION_UUID;
|
||||
status = aci_gatt_add_char(dev_info_svc->service_handle,
|
||||
UUID_TYPE_16,
|
||||
(Char_UUID_t*)&uuid,
|
||||
strlen(dev_info_firmware_rev_num),
|
||||
CHAR_PROP_READ,
|
||||
ATTR_PERMISSION_NONE,
|
||||
GATT_DONT_NOTIFY_EVENTS,
|
||||
10,
|
||||
CHAR_VALUE_LEN_CONSTANT,
|
||||
&dev_info_svc->firmware_rev_char_handle);
|
||||
if(status) {
|
||||
FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to add firmware revision char: %d", status);
|
||||
}
|
||||
uuid = SOFTWARE_REVISION_UUID;
|
||||
status = aci_gatt_add_char(dev_info_svc->service_handle,
|
||||
UUID_TYPE_16,
|
||||
(Char_UUID_t*)&uuid,
|
||||
strlen(dev_info_software_rev_num),
|
||||
CHAR_PROP_READ,
|
||||
ATTR_PERMISSION_NONE,
|
||||
GATT_DONT_NOTIFY_EVENTS,
|
||||
10,
|
||||
CHAR_VALUE_LEN_CONSTANT,
|
||||
&dev_info_svc->software_rev_char_handle);
|
||||
if(status) {
|
||||
FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to add software revision char: %d", status);
|
||||
}
|
||||
|
||||
// Update characteristics
|
||||
status = aci_gatt_update_char_value(dev_info_svc->service_handle,
|
||||
dev_info_svc->man_name_char_handle,
|
||||
0,
|
||||
strlen(dev_info_man_name),
|
||||
(uint8_t*)dev_info_man_name);
|
||||
if(status) {
|
||||
FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to update manufacturer name char: %d", status);
|
||||
}
|
||||
status = aci_gatt_update_char_value(dev_info_svc->service_handle,
|
||||
dev_info_svc->serial_num_char_handle,
|
||||
0,
|
||||
strlen(dev_info_serial_num),
|
||||
(uint8_t*)dev_info_serial_num);
|
||||
if(status) {
|
||||
FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to update serial number char: %d", status);
|
||||
}
|
||||
status = aci_gatt_update_char_value(dev_info_svc->service_handle,
|
||||
dev_info_svc->firmware_rev_char_handle,
|
||||
0,
|
||||
strlen(dev_info_firmware_rev_num),
|
||||
(uint8_t*)dev_info_firmware_rev_num);
|
||||
if(status) {
|
||||
FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to update firmware revision char: %d", status);
|
||||
}
|
||||
status = aci_gatt_update_char_value(dev_info_svc->service_handle,
|
||||
dev_info_svc->software_rev_char_handle,
|
||||
0,
|
||||
strlen(dev_info_software_rev_num),
|
||||
(uint8_t*)dev_info_software_rev_num);
|
||||
if(status) {
|
||||
FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to update software revision char: %d", status);
|
||||
}
|
||||
}
|
||||
|
||||
void dev_info_svc_stop() {
|
||||
tBleStatus status;
|
||||
if(dev_info_svc) {
|
||||
// Delete service characteristics
|
||||
status = aci_gatt_del_char(dev_info_svc->service_handle, dev_info_svc->man_name_char_handle);
|
||||
if(status) {
|
||||
FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to delete manufacturer name char: %d", status);
|
||||
}
|
||||
status = aci_gatt_del_char(dev_info_svc->service_handle, dev_info_svc->serial_num_char_handle);
|
||||
if(status) {
|
||||
FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to delete serial number char: %d", status);
|
||||
}
|
||||
status = aci_gatt_del_char(dev_info_svc->service_handle, dev_info_svc->firmware_rev_char_handle);
|
||||
if(status) {
|
||||
FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to delete firmware revision char: %d", status);
|
||||
}
|
||||
status = aci_gatt_del_char(dev_info_svc->service_handle, dev_info_svc->software_rev_char_handle);
|
||||
if(status) {
|
||||
FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to delete software revision char: %d", status);
|
||||
}
|
||||
// Delete service
|
||||
status = aci_gatt_del_service(dev_info_svc->service_handle);
|
||||
if(status) {
|
||||
FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to delete device info service: %d", status);
|
||||
}
|
||||
free(dev_info_svc);
|
||||
dev_info_svc = NULL;
|
||||
}
|
||||
}
|
||||
21
firmware/targets/f6/ble-glue/dev_info_service.h
Normal file
21
firmware/targets/f6/ble-glue/dev_info_service.h
Normal file
@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define DEV_INFO_MANUFACTURER_NAME "Flipper Devices Inc."
|
||||
#define DEV_INFO_SERIAL_NUMBER "1.0"
|
||||
#define DEV_INFO_FIRMWARE_REVISION_NUMBER TARGET
|
||||
#define DEV_INFO_SOFTWARE_REVISION_NUMBER GIT_COMMIT " " GIT_BRANCH " " GIT_BRANCH_NUM " " BUILD_DATE
|
||||
|
||||
void dev_info_svc_start();
|
||||
|
||||
void dev_info_svc_stop();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@ -1,157 +0,0 @@
|
||||
#include "app_common.h"
|
||||
#include "ble.h"
|
||||
#include "dis_app.h"
|
||||
#include <furi-hal-version.h>
|
||||
|
||||
#if ((BLE_CFG_DIS_SYSTEM_ID != 0) || (CFG_MENU_DEVICE_INFORMATION != 0))
|
||||
static const uint8_t system_id[BLE_CFG_DIS_SYSTEM_ID_LEN_MAX] = {
|
||||
(uint8_t)((DISAPP_MANUFACTURER_ID & 0xFF0000) >> 16),
|
||||
(uint8_t)((DISAPP_MANUFACTURER_ID & 0x00FF00) >> 8),
|
||||
(uint8_t)(DISAPP_MANUFACTURER_ID & 0x0000FF),
|
||||
0xFE,
|
||||
0xFF,
|
||||
(uint8_t)((DISAPP_OUI & 0xFF0000) >> 16),
|
||||
(uint8_t)((DISAPP_OUI & 0x00FF00) >> 8),
|
||||
(uint8_t)(DISAPP_OUI & 0x0000FF)
|
||||
};
|
||||
#endif
|
||||
|
||||
#if ((BLE_CFG_DIS_IEEE_CERTIFICATION != 0) || (CFG_MENU_DEVICE_INFORMATION != 0))
|
||||
static const uint8_t ieee_id[BLE_CFG_DIS_IEEE_CERTIFICATION_LEN_MAX] = {
|
||||
0xFE, 0xCA, 0xFE, 0xCA, 0xFE, 0xCA, 0xFE, 0xCA,
|
||||
0xFE, 0xCA, 0xFE, 0xCA, 0xFE, 0xCA, 0xFE, 0xCA,
|
||||
0xFE, 0xCA, 0xFE, 0xCA, 0xFE, 0xCA, 0xFE, 0xCA,
|
||||
0xFE, 0xCA, 0xFE, 0xCA, 0xFE, 0xCA, 0xFE, 0xCA,
|
||||
};
|
||||
#endif
|
||||
|
||||
#if ((BLE_CFG_DIS_PNP_ID != 0) || (CFG_MENU_DEVICE_INFORMATION != 0))
|
||||
static const uint8_t pnp_id[BLE_CFG_DIS_PNP_ID_LEN_MAX] = {
|
||||
0x1,
|
||||
0xAD, 0xDE,
|
||||
0xDE, 0xDA,
|
||||
0x01, 0x00
|
||||
};
|
||||
#endif
|
||||
|
||||
void DISAPP_Init(void) {
|
||||
DIS_Data_t dis_information_data;
|
||||
|
||||
#if ((BLE_CFG_DIS_MANUFACTURER_NAME_STRING != 0) || (CFG_MENU_DEVICE_INFORMATION != 0))
|
||||
/**
|
||||
* Update MANUFACTURER NAME Information
|
||||
*
|
||||
* @param UUID
|
||||
* @param pPData
|
||||
* @return
|
||||
*/
|
||||
dis_information_data.pPayload = (uint8_t*)DISAPP_MANUFACTURER_NAME;
|
||||
dis_information_data.Length = sizeof(DISAPP_MANUFACTURER_NAME);
|
||||
DIS_UpdateChar(MANUFACTURER_NAME_UUID, &dis_information_data);
|
||||
#endif
|
||||
|
||||
#if ((BLE_CFG_DIS_MODEL_NUMBER_STRING != 0) || (CFG_MENU_DEVICE_INFORMATION != 0))
|
||||
/**
|
||||
* Update MODEL NUMBERInformation
|
||||
*
|
||||
* @param UUID
|
||||
* @param pPData
|
||||
* @return
|
||||
*/
|
||||
const char* name = furi_hal_version_get_device_name_ptr();
|
||||
dis_information_data.pPayload = (uint8_t*)name;
|
||||
dis_information_data.Length = strlen(name) + 1;
|
||||
DIS_UpdateChar(MODEL_NUMBER_UUID, &dis_information_data);
|
||||
#endif
|
||||
|
||||
#if ((BLE_CFG_DIS_SERIAL_NUMBER_STRING != 0) || (CFG_MENU_DEVICE_INFORMATION != 0))
|
||||
/**
|
||||
* Update SERIAL NUMBERInformation
|
||||
*
|
||||
* @param UUID
|
||||
* @param pPData
|
||||
* @return
|
||||
*/
|
||||
dis_information_data.pPayload = (uint8_t*)DISAPP_SERIAL_NUMBER;
|
||||
dis_information_data.Length = sizeof(DISAPP_SERIAL_NUMBER);
|
||||
DIS_UpdateChar(SERIAL_NUMBER_UUID, &dis_information_data);
|
||||
#endif
|
||||
|
||||
#if ((BLE_CFG_DIS_HARDWARE_REVISION_STRING != 0) || (CFG_MENU_DEVICE_INFORMATION != 0))
|
||||
/**
|
||||
* Update HARDWARE REVISION NUMBERInformation
|
||||
*
|
||||
* @param UUID
|
||||
* @param pPData
|
||||
* @return
|
||||
*/
|
||||
dis_information_data.pPayload = (uint8_t*)DISAPP_HARDWARE_REVISION_NUMBER;
|
||||
dis_information_data.Length = sizeof(DISAPP_HARDWARE_REVISION_NUMBER);
|
||||
DIS_UpdateChar(HARDWARE_REVISION_UUID, &dis_information_data);
|
||||
#endif
|
||||
|
||||
#if ((BLE_CFG_DIS_FIRMWARE_REVISION_STRING != 0) || (CFG_MENU_DEVICE_INFORMATION != 0))
|
||||
/**
|
||||
* Update FIRMWARE REVISION NUMBERInformation
|
||||
*
|
||||
* @param UUID
|
||||
* @param pPData
|
||||
* @return
|
||||
*/
|
||||
dis_information_data.pPayload = (uint8_t*)DISAPP_FIRMWARE_REVISION_NUMBER;
|
||||
dis_information_data.Length = sizeof(DISAPP_FIRMWARE_REVISION_NUMBER);
|
||||
DIS_UpdateChar(FIRMWARE_REVISION_UUID, &dis_information_data);
|
||||
#endif
|
||||
|
||||
#if ((BLE_CFG_DIS_SOFTWARE_REVISION_STRING != 0) || (CFG_MENU_DEVICE_INFORMATION != 0))
|
||||
/**
|
||||
* Update SOFTWARE REVISION NUMBERInformation
|
||||
*
|
||||
* @param UUID
|
||||
* @param pPData
|
||||
* @return
|
||||
*/
|
||||
dis_information_data.pPayload = (uint8_t*)DISAPP_SOFTWARE_REVISION_NUMBER;
|
||||
dis_information_data.Length = sizeof(DISAPP_SOFTWARE_REVISION_NUMBER);
|
||||
DIS_UpdateChar(SOFTWARE_REVISION_UUID, &dis_information_data);
|
||||
#endif
|
||||
|
||||
#if ((BLE_CFG_DIS_SYSTEM_ID != 0) || (CFG_MENU_DEVICE_INFORMATION != 0))
|
||||
/**
|
||||
* Update SYSTEM ID Information
|
||||
*
|
||||
* @param UUID
|
||||
* @param pPData
|
||||
* @return
|
||||
*/
|
||||
dis_information_data.pPayload = (uint8_t *)system_id;
|
||||
dis_information_data.Length = BLE_CFG_DIS_SYSTEM_ID_LEN_MAX;
|
||||
DIS_UpdateChar(SYSTEM_ID_UUID, &dis_information_data);
|
||||
#endif
|
||||
|
||||
#if ((BLE_CFG_DIS_IEEE_CERTIFICATION != 0) || (CFG_MENU_DEVICE_INFORMATION != 0))
|
||||
/**
|
||||
* Update IEEE CERTIFICATION ID Information
|
||||
*
|
||||
* @param UUID
|
||||
* @param pPData
|
||||
* @return
|
||||
*/
|
||||
dis_information_data.pPayload = (uint8_t *)ieee_id;
|
||||
dis_information_data.Length = BLE_CFG_DIS_IEEE_CERTIFICATION_LEN_MAX;
|
||||
DIS_UpdateChar(IEEE_CERTIFICATION_UUID, &dis_information_data);
|
||||
#endif
|
||||
|
||||
#if ((BLE_CFG_DIS_PNP_ID != 0) || (CFG_MENU_DEVICE_INFORMATION != 0))
|
||||
/**
|
||||
* Update PNP ID Information
|
||||
*
|
||||
* @param UUID
|
||||
* @param pPData
|
||||
* @return
|
||||
*/
|
||||
dis_information_data.pPayload = (uint8_t *)pnp_id;
|
||||
dis_information_data.Length = BLE_CFG_DIS_PNP_ID_LEN_MAX;
|
||||
DIS_UpdateChar(PNP_ID_UUID, &dis_information_data);
|
||||
#endif
|
||||
}
|
||||
@ -1,20 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define DISAPP_MANUFACTURER_NAME "Flipperdevice Inc."
|
||||
//#define DISAPP_MODEL_NUMBER "FlipperZero"
|
||||
#define DISAPP_SERIAL_NUMBER "1.0"
|
||||
#define DISAPP_HARDWARE_REVISION_NUMBER "1.0"
|
||||
#define DISAPP_FIRMWARE_REVISION_NUMBER TARGET
|
||||
#define DISAPP_SOFTWARE_REVISION_NUMBER GIT_COMMIT " " GIT_BRANCH " " GIT_BRANCH_NUM " " BUILD_DATE
|
||||
#define DISAPP_OUI 0x123456
|
||||
#define DISAPP_MANUFACTURER_ID 0x9ABCDE
|
||||
|
||||
void DISAPP_Init(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
387
firmware/targets/f6/ble-glue/gap.c
Normal file
387
firmware/targets/f6/ble-glue/gap.c
Normal file
@ -0,0 +1,387 @@
|
||||
#include "gap.h"
|
||||
|
||||
#include "app_entry.h"
|
||||
#include "ble.h"
|
||||
|
||||
#include "cmsis_os.h"
|
||||
#include "otp.h"
|
||||
#include "dev_info_service.h"
|
||||
#include "battery_service.h"
|
||||
#include "serial_service.h"
|
||||
|
||||
#include <applications/bt/bt_service/bt.h>
|
||||
#include <furi-hal.h>
|
||||
|
||||
#define GAP_TAG "BLE"
|
||||
|
||||
#define FAST_ADV_TIMEOUT 30000
|
||||
#define INITIAL_ADV_TIMEOUT 60000
|
||||
|
||||
#define BD_ADDR_SIZE_LOCAL 6
|
||||
|
||||
typedef struct {
|
||||
uint16_t gap_svc_handle;
|
||||
uint16_t dev_name_char_handle;
|
||||
uint16_t appearance_char_handle;
|
||||
uint16_t connection_handle;
|
||||
uint8_t adv_svc_uuid_len;
|
||||
uint8_t adv_svc_uuid[20];
|
||||
} GapSvc;
|
||||
|
||||
typedef struct {
|
||||
GapSvc gap_svc;
|
||||
GapState state;
|
||||
uint8_t mac_address[BD_ADDR_SIZE_LOCAL];
|
||||
Bt* bt;
|
||||
osTimerId advertise_timer;
|
||||
osThreadAttr_t thread_attr;
|
||||
osThreadId_t thread_id;
|
||||
} Gap;
|
||||
|
||||
// Identity root key
|
||||
static const uint8_t gap_irk[16] = {0x12,0x34,0x56,0x78,0x9a,0xbc,0xde,0xf0,0x12,0x34,0x56,0x78,0x9a,0xbc,0xde,0xf0};
|
||||
// Encryption root key
|
||||
static const uint8_t gap_erk[16] = {0xfe,0xdc,0xba,0x09,0x87,0x65,0x43,0x21,0xfe,0xdc,0xba,0x09,0x87,0x65,0x43,0x21};
|
||||
// Appearence characteristic UUID
|
||||
static const uint8_t gap_appearence_char_uuid[] = {0x00, 0x86};
|
||||
// Default MAC address
|
||||
static const uint8_t gap_default_mac_addr[] = {0x6c, 0x7a, 0xd8, 0xac, 0x57, 0x72};
|
||||
|
||||
static Gap* gap = NULL;
|
||||
|
||||
static void gap_advertise(GapState new_state);
|
||||
static void gap_app(void *arg);
|
||||
|
||||
SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification( void *pckt )
|
||||
{
|
||||
hci_event_pckt *event_pckt;
|
||||
evt_le_meta_event *meta_evt;
|
||||
evt_blue_aci *blue_evt;
|
||||
hci_le_phy_update_complete_event_rp0 *evt_le_phy_update_complete;
|
||||
uint8_t tx_phy;
|
||||
uint8_t rx_phy;
|
||||
tBleStatus ret = BLE_STATUS_INVALID_PARAMS;
|
||||
|
||||
event_pckt = (hci_event_pckt*) ((hci_uart_pckt *) pckt)->data;
|
||||
|
||||
switch (event_pckt->evt) {
|
||||
case EVT_DISCONN_COMPLETE:
|
||||
{
|
||||
hci_disconnection_complete_event_rp0 *disconnection_complete_event = (hci_disconnection_complete_event_rp0 *) event_pckt->data;
|
||||
if (disconnection_complete_event->Connection_Handle == gap->gap_svc.connection_handle) {
|
||||
gap->gap_svc.connection_handle = 0;
|
||||
gap->state = GapStateIdle;
|
||||
FURI_LOG_I(GAP_TAG, "Disconnect from client");
|
||||
}
|
||||
// Restart advertising
|
||||
gap_advertise(GapStateAdvFast);
|
||||
furi_hal_power_insomnia_exit();
|
||||
}
|
||||
break;
|
||||
|
||||
case EVT_LE_META_EVENT:
|
||||
meta_evt = (evt_le_meta_event*) event_pckt->data;
|
||||
switch (meta_evt->subevent) {
|
||||
case EVT_LE_CONN_UPDATE_COMPLETE:
|
||||
FURI_LOG_D(GAP_TAG, "Connection update event");
|
||||
break;
|
||||
|
||||
case EVT_LE_PHY_UPDATE_COMPLETE:
|
||||
evt_le_phy_update_complete = (hci_le_phy_update_complete_event_rp0*)meta_evt->data;
|
||||
if(evt_le_phy_update_complete->Status) {
|
||||
FURI_LOG_E(GAP_TAG, "Update PHY failed, status %d", evt_le_phy_update_complete->Status);
|
||||
} else {
|
||||
FURI_LOG_I(GAP_TAG, "Update PHY succeed");
|
||||
}
|
||||
ret = hci_le_read_phy(gap->gap_svc.connection_handle,&tx_phy,&rx_phy);
|
||||
if(ret) {
|
||||
FURI_LOG_E(GAP_TAG, "Read PHY failed, status: %d", ret);
|
||||
} else {
|
||||
FURI_LOG_I(GAP_TAG, "PHY Params TX= %d, RX= %d ", tx_phy, rx_phy);
|
||||
}
|
||||
break;
|
||||
|
||||
case EVT_LE_CONN_COMPLETE:
|
||||
furi_hal_power_insomnia_enter();
|
||||
hci_le_connection_complete_event_rp0* connection_complete_event = (hci_le_connection_complete_event_rp0 *) meta_evt->data;
|
||||
FURI_LOG_I(GAP_TAG, "Connection complete for connection handle 0x%x", connection_complete_event->Connection_Handle);
|
||||
|
||||
// Stop advertising as connection completed
|
||||
osTimerStop(gap->advertise_timer);
|
||||
|
||||
// Update connection status and handle
|
||||
gap->state = GapStateConnected;
|
||||
gap->gap_svc.connection_handle = connection_complete_event->Connection_Handle;
|
||||
|
||||
// Start pairing by sending security request
|
||||
aci_gap_slave_security_req(connection_complete_event->Connection_Handle);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case EVT_VENDOR:
|
||||
blue_evt = (evt_blue_aci*) event_pckt->data;
|
||||
switch (blue_evt->ecode) {
|
||||
aci_gap_pairing_complete_event_rp0 *pairing_complete;
|
||||
|
||||
case EVT_BLUE_GAP_LIMITED_DISCOVERABLE:
|
||||
FURI_LOG_I(GAP_TAG, "Limited discoverable event");
|
||||
break;
|
||||
|
||||
case EVT_BLUE_GAP_PASS_KEY_REQUEST:
|
||||
{
|
||||
// Generate random PIN code
|
||||
uint32_t pin = rand() % 999999;
|
||||
aci_gap_pass_key_resp(gap->gap_svc.connection_handle, pin);
|
||||
FURI_LOG_I(GAP_TAG, "Pass key request event. Pin: %d", pin);
|
||||
bt_pin_code_show(gap->bt, pin);
|
||||
}
|
||||
break;
|
||||
|
||||
case EVT_BLUE_GAP_AUTHORIZATION_REQUEST:
|
||||
FURI_LOG_I(GAP_TAG, "Authorization request event");
|
||||
break;
|
||||
|
||||
case EVT_BLUE_GAP_SLAVE_SECURITY_INITIATED:
|
||||
FURI_LOG_I(GAP_TAG, "Slave security initiated");
|
||||
break;
|
||||
|
||||
case EVT_BLUE_GAP_BOND_LOST:
|
||||
FURI_LOG_I(GAP_TAG, "Bond lost event. Start rebonding");
|
||||
aci_gap_allow_rebond(gap->gap_svc.connection_handle);
|
||||
break;
|
||||
|
||||
case EVT_BLUE_GAP_DEVICE_FOUND:
|
||||
FURI_LOG_I(GAP_TAG, "Device found event");
|
||||
break;
|
||||
|
||||
case EVT_BLUE_GAP_ADDR_NOT_RESOLVED:
|
||||
FURI_LOG_I(GAP_TAG, "Address not resolved event");
|
||||
break;
|
||||
|
||||
case EVT_BLUE_GAP_KEYPRESS_NOTIFICATION:
|
||||
FURI_LOG_I(GAP_TAG, "Key press notification event");
|
||||
break;
|
||||
|
||||
case EVT_BLUE_GAP_NUMERIC_COMPARISON_VALUE:
|
||||
FURI_LOG_I(GAP_TAG, "Hex_value = %lx",
|
||||
((aci_gap_numeric_comparison_value_event_rp0 *)(blue_evt->data))->Numeric_Value);
|
||||
aci_gap_numeric_comparison_value_confirm_yesno(gap->gap_svc.connection_handle, 1);
|
||||
break;
|
||||
|
||||
case (EVT_BLUE_GAP_PAIRING_CMPLT):
|
||||
{
|
||||
pairing_complete = (aci_gap_pairing_complete_event_rp0*)blue_evt->data;
|
||||
if (pairing_complete->Status) {
|
||||
FURI_LOG_E(GAP_TAG, "Pairing failed with status: %d. Terminating connection", pairing_complete->Status);
|
||||
aci_gap_terminate(gap->gap_svc.connection_handle, 5);
|
||||
} else {
|
||||
FURI_LOG_I(GAP_TAG, "Pairing complete");
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case EVT_BLUE_GAP_PROCEDURE_COMPLETE:
|
||||
FURI_LOG_I(GAP_TAG, "Procedure complete event");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return SVCCTL_UserEvtFlowEnable;
|
||||
}
|
||||
|
||||
void SVCCTL_SvcInit() {
|
||||
// Dummy function to prevent unused services initialization
|
||||
// TODO refactor (disable all services in WPAN config)
|
||||
}
|
||||
|
||||
static void set_advertisment_service_uid(uint8_t* uid, uint8_t uid_len) {
|
||||
gap->gap_svc.adv_svc_uuid_len = 1;
|
||||
if(uid_len == 2) {
|
||||
gap->gap_svc.adv_svc_uuid[0] = AD_TYPE_16_BIT_SERV_UUID;
|
||||
} else if (uid_len == 4) {
|
||||
gap->gap_svc.adv_svc_uuid[0] = AD_TYPE_32_BIT_SERV_UUID;
|
||||
} else if(uid_len == 16) {
|
||||
gap->gap_svc.adv_svc_uuid[0] = AD_TYPE_128_BIT_SERV_UUID_CMPLT_LIST;
|
||||
}
|
||||
memcpy(&gap->gap_svc.adv_svc_uuid[1], uid, uid_len);
|
||||
gap->gap_svc.adv_svc_uuid_len += uid_len;
|
||||
}
|
||||
|
||||
GapState gap_get_status() {
|
||||
return gap->state;
|
||||
}
|
||||
|
||||
void gap_init_mac_address(Gap* gap) {
|
||||
uint8_t *otp_addr;
|
||||
uint32_t udn;
|
||||
uint32_t company_id;
|
||||
uint32_t device_id;
|
||||
|
||||
udn = LL_FLASH_GetUDN();
|
||||
if(udn != 0xFFFFFFFF) {
|
||||
company_id = LL_FLASH_GetSTCompanyID();
|
||||
device_id = LL_FLASH_GetDeviceID();
|
||||
gap->mac_address[0] = (uint8_t)(udn & 0x000000FF);
|
||||
gap->mac_address[1] = (uint8_t)( (udn & 0x0000FF00) >> 8 );
|
||||
gap->mac_address[2] = (uint8_t)( (udn & 0x00FF0000) >> 16 );
|
||||
gap->mac_address[3] = (uint8_t)device_id;
|
||||
gap->mac_address[4] = (uint8_t)(company_id & 0x000000FF);;
|
||||
gap->mac_address[5] = (uint8_t)( (company_id & 0x0000FF00) >> 8 );
|
||||
} else {
|
||||
otp_addr = OTP_Read(0);
|
||||
if(otp_addr) {
|
||||
memcpy(gap->mac_address, ((OTP_ID0_t*)otp_addr)->bd_address, sizeof(gap->mac_address));
|
||||
} else {
|
||||
memcpy(gap->mac_address, gap_default_mac_addr, sizeof(gap->mac_address));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void gap_init_svc(Gap* gap) {
|
||||
tBleStatus status;
|
||||
uint32_t srd_bd_addr[2];
|
||||
|
||||
//HCI Reset to synchronise BLE Stack*/
|
||||
hci_reset();
|
||||
// Configure mac address
|
||||
gap_init_mac_address(gap);
|
||||
aci_hal_write_config_data(CONFIG_DATA_PUBADDR_OFFSET, CONFIG_DATA_PUBADDR_LEN, (uint8_t*)gap->mac_address);
|
||||
|
||||
/* Static random Address
|
||||
* The two upper bits shall be set to 1
|
||||
* The lowest 32bits is read from the UDN to differentiate between devices
|
||||
* The RNG may be used to provide a random number on each power on
|
||||
*/
|
||||
srd_bd_addr[1] = 0x0000ED6E;
|
||||
srd_bd_addr[0] = LL_FLASH_GetUDN();
|
||||
aci_hal_write_config_data( CONFIG_DATA_RANDOM_ADDRESS_OFFSET, CONFIG_DATA_RANDOM_ADDRESS_LEN, (uint8_t*)srd_bd_addr );
|
||||
// Set Identity root key used to derive LTK and CSRK
|
||||
aci_hal_write_config_data( CONFIG_DATA_IR_OFFSET, CONFIG_DATA_IR_LEN, (uint8_t*)gap_irk );
|
||||
// Set Encryption root key used to derive LTK and CSRK
|
||||
aci_hal_write_config_data( CONFIG_DATA_ER_OFFSET, CONFIG_DATA_ER_LEN, (uint8_t*)gap_erk );
|
||||
// Set TX Power to 0 dBm
|
||||
aci_hal_set_tx_power_level(1, 0x19);
|
||||
// Initialize GATT interface
|
||||
aci_gatt_init();
|
||||
// Initialize GAP interface
|
||||
const char *name = furi_hal_version_get_device_name_ptr();
|
||||
aci_gap_init(GAP_PERIPHERAL_ROLE, 0, strlen(name),
|
||||
&gap->gap_svc.gap_svc_handle, &gap->gap_svc.dev_name_char_handle, &gap->gap_svc.appearance_char_handle);
|
||||
|
||||
// Set GAP characteristics
|
||||
status = aci_gatt_update_char_value(gap->gap_svc.gap_svc_handle, gap->gap_svc.dev_name_char_handle, 0, strlen(name), (uint8_t *) name);
|
||||
if (status) {
|
||||
FURI_LOG_E(GAP_TAG, "Failed updating name characteristic: %d", status);
|
||||
}
|
||||
status = aci_gatt_update_char_value(gap->gap_svc.gap_svc_handle, gap->gap_svc.appearance_char_handle, 0, 2, gap_appearence_char_uuid);
|
||||
if(status) {
|
||||
FURI_LOG_E(GAP_TAG, "Failed updating appearence characteristic: %d", status);
|
||||
}
|
||||
// Set default PHY
|
||||
hci_le_set_default_phy(ALL_PHYS_PREFERENCE, TX_2M_PREFERRED, RX_2M_PREFERRED);
|
||||
// Set I/O capability
|
||||
aci_gap_set_io_capability(IO_CAP_DISPLAY_ONLY);
|
||||
// Setup authentication
|
||||
aci_gap_set_authentication_requirement(1, 1, 1, 0, 8, 16, 1, 0, PUBLIC_ADDR);
|
||||
// Configure whitelist
|
||||
aci_gap_configure_whitelist();
|
||||
}
|
||||
|
||||
static void gap_advertise(GapState new_state)
|
||||
{
|
||||
tBleStatus status;
|
||||
uint16_t min_interval;
|
||||
uint16_t max_interval;
|
||||
|
||||
if (new_state == GapStateAdvFast) {
|
||||
min_interval = 0x80; // 80 ms
|
||||
max_interval = 0xa0; // 100 ms
|
||||
} else {
|
||||
min_interval = 0x0640; // 1 s
|
||||
max_interval = 0x0fa0; // 2.5 s
|
||||
}
|
||||
// Stop advertising timer
|
||||
osTimerStop(gap->advertise_timer);
|
||||
|
||||
if ((new_state == GapStateAdvLowPower) && ((gap->state == GapStateAdvFast) || (gap->state == GapStateAdvLowPower))) {
|
||||
// Stop advertising
|
||||
status = aci_gap_set_non_discoverable();
|
||||
if (status) {
|
||||
FURI_LOG_E(GAP_TAG, "Stop Advertising Failed, result: %d", status);
|
||||
}
|
||||
}
|
||||
// Configure advertising
|
||||
gap->state = new_state;
|
||||
const char* name = furi_hal_version_get_ble_local_device_name_ptr();
|
||||
status = aci_gap_set_discoverable(ADV_IND, min_interval, max_interval, PUBLIC_ADDR, 0,
|
||||
strlen(name), (uint8_t*)name,
|
||||
gap->gap_svc.adv_svc_uuid_len, gap->gap_svc.adv_svc_uuid, 0, 0);
|
||||
if(status) {
|
||||
FURI_LOG_E(GAP_TAG, "Set discoverable err: %d", status);
|
||||
}
|
||||
osTimerStart(gap->advertise_timer, INITIAL_ADV_TIMEOUT);
|
||||
}
|
||||
|
||||
static void gap_advertise_request(Gap* gap) {
|
||||
osThreadFlagsSet(gap->thread_id, 1);
|
||||
}
|
||||
|
||||
static void gap_advetise_timer_callback(void* context) {
|
||||
furi_assert(context);
|
||||
Gap* gap = context;
|
||||
gap_advertise_request(gap);
|
||||
}
|
||||
|
||||
bool gap_init() {
|
||||
if (APPE_Status() != BleGlueStatusStarted) {
|
||||
return false;
|
||||
}
|
||||
|
||||
gap = furi_alloc(sizeof(Gap));
|
||||
srand(DWT->CYCCNT);
|
||||
// Open Bt record
|
||||
gap->bt = furi_record_open("bt");
|
||||
// Create advertising timer
|
||||
gap->advertise_timer = osTimerNew(gap_advetise_timer_callback, osTimerOnce, &gap, NULL);
|
||||
// Initialization of HCI & GATT & GAP layer
|
||||
gap_init_svc(gap);
|
||||
// Initialization of the BLE Services
|
||||
SVCCTL_Init();
|
||||
// Initialization of the BLE App Context
|
||||
gap->state = GapStateIdle;
|
||||
gap->gap_svc.connection_handle = 0xFFFF;
|
||||
|
||||
// Thread configuration
|
||||
gap->thread_attr.name = "BLE advertising";
|
||||
gap->thread_attr.stack_size = 512;
|
||||
gap->thread_id = osThreadNew(gap_app, NULL, &gap->thread_attr);
|
||||
|
||||
// Start Device Information service
|
||||
dev_info_svc_start();
|
||||
// Start Battery service
|
||||
battery_svc_start();
|
||||
// Start Serial application
|
||||
serial_svc_start();
|
||||
// Configure advirtise service UUID
|
||||
uint8_t adv_service_uid[2];
|
||||
adv_service_uid[0] = 0x80 | furi_hal_version_get_hw_color();
|
||||
adv_service_uid[1] = 0x30;
|
||||
|
||||
set_advertisment_service_uid(adv_service_uid, sizeof(adv_service_uid));
|
||||
gap_advertise(GapStateAdvFast);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void gap_app(void *arg) {
|
||||
// TODO Exit from app, stop service, clean memory
|
||||
while(1) {
|
||||
osThreadFlagsWait(1, osFlagsWaitAny, osWaitForever);
|
||||
gap_advertise(GapStateAdvLowPower);
|
||||
}
|
||||
}
|
||||
22
firmware/targets/f6/ble-glue/gap.h
Normal file
22
firmware/targets/f6/ble-glue/gap.h
Normal file
@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
GapStateIdle,
|
||||
GapStateAdvFast,
|
||||
GapStateAdvLowPower,
|
||||
GapStateConnected,
|
||||
} GapState;
|
||||
|
||||
bool gap_init();
|
||||
|
||||
GapState gap_get_status();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@ -1,256 +0,0 @@
|
||||
/* USER CODE BEGIN Header */
|
||||
/**
|
||||
******************************************************************************
|
||||
* @file hrs_app.c
|
||||
* @author MCD Application Team
|
||||
* @brief Heart Rate Service Application
|
||||
******************************************************************************
|
||||
* @attention
|
||||
*
|
||||
* <h2><center>© Copyright (c) 2019 STMicroelectronics.
|
||||
* All rights reserved.</center></h2>
|
||||
*
|
||||
* This software component is licensed by ST under Ultimate Liberty license
|
||||
* SLA0044, the "License"; You may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at:
|
||||
* www.st.com/SLA0044
|
||||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
/* USER CODE END Header */
|
||||
|
||||
/* Includes ------------------------------------------------------------------*/
|
||||
#include "app_common.h"
|
||||
|
||||
#include "ble.h"
|
||||
#include "hrs_app.h"
|
||||
#include "cmsis_os.h"
|
||||
|
||||
/* Private includes ----------------------------------------------------------*/
|
||||
/* USER CODE BEGIN Includes */
|
||||
|
||||
/* USER CODE END Includes */
|
||||
|
||||
/* Private typedef -----------------------------------------------------------*/
|
||||
typedef struct
|
||||
{
|
||||
HRS_BodySensorLocation_t BodySensorLocationChar;
|
||||
HRS_MeasVal_t MeasurementvalueChar;
|
||||
uint8_t ResetEnergyExpended;
|
||||
uint8_t TimerMeasurement_Id;
|
||||
|
||||
} HRSAPP_Context_t;
|
||||
/* USER CODE BEGIN PTD */
|
||||
|
||||
/* USER CODE END PTD */
|
||||
|
||||
/* Private defines ------------------------------------------------------------*/
|
||||
/* USER CODE BEGIN PD */
|
||||
|
||||
/* USER CODE END PD */
|
||||
|
||||
/* Private macros ------------------------------------------------------------*/
|
||||
#define HRSAPP_MEASUREMENT_INTERVAL (1000000/CFG_TS_TICK_VAL) /**< 1s */
|
||||
/* USER CODE BEGIN PM */
|
||||
|
||||
/* USER CODE END PM */
|
||||
|
||||
/* Private variables ---------------------------------------------------------*/
|
||||
/**
|
||||
* START of Section BLE_APP_CONTEXT
|
||||
*/
|
||||
|
||||
PLACE_IN_SECTION("BLE_APP_CONTEXT") static HRSAPP_Context_t HRSAPP_Context;
|
||||
|
||||
/**
|
||||
* END of Section BLE_APP_CONTEXT
|
||||
*/
|
||||
|
||||
osThreadId_t HrsProcessId;
|
||||
|
||||
const osThreadAttr_t HrsProcess_attr = {
|
||||
.name = CFG_HRS_PROCESS_NAME,
|
||||
.attr_bits = CFG_HRS_PROCESS_ATTR_BITS,
|
||||
.cb_mem = CFG_HRS_PROCESS_CB_MEM,
|
||||
.cb_size = CFG_HRS_PROCESS_CB_SIZE,
|
||||
.stack_mem = CFG_HRS_PROCESS_STACK_MEM,
|
||||
.priority = CFG_HRS_PROCESS_PRIORITY,
|
||||
.stack_size = CFG_HRS_PROCESS_STACK_SIZE
|
||||
};
|
||||
|
||||
/* USER CODE BEGIN PV */
|
||||
|
||||
/* USER CODE END PV */
|
||||
|
||||
/* Private functions prototypes-----------------------------------------------*/
|
||||
static void HrMeas( void );
|
||||
static void HrsProcess(void *argument);
|
||||
static void HRSAPP_Measurement(void);
|
||||
static uint32_t HRSAPP_Read_RTC_SSR_SS ( void );
|
||||
/* USER CODE BEGIN PFP */
|
||||
|
||||
/* USER CODE END PFP */
|
||||
|
||||
/* Functions Definition ------------------------------------------------------*/
|
||||
void HRS_Notification(HRS_App_Notification_evt_t *pNotification)
|
||||
{
|
||||
/* USER CODE BEGIN HRS_Notification_1 */
|
||||
|
||||
/* USER CODE END HRS_Notification_1 */
|
||||
switch(pNotification->HRS_Evt_Opcode)
|
||||
{
|
||||
/* USER CODE BEGIN HRS_Notification_HRS_Evt_Opcode */
|
||||
|
||||
/* USER CODE END HRS_Notification_HRS_Evt_Opcode */
|
||||
#if (BLE_CFG_HRS_ENERGY_EXPENDED_INFO_FLAG != 0)
|
||||
case HRS_RESET_ENERGY_EXPENDED_EVT:
|
||||
/* USER CODE BEGIN HRS_RESET_ENERGY_EXPENDED_EVT */
|
||||
HRSAPP_Context.MeasurementvalueChar.EnergyExpended = 0;
|
||||
HRSAPP_Context.ResetEnergyExpended = 1;
|
||||
/* USER CODE END HRS_RESET_ENERGY_EXPENDED_EVT */
|
||||
break;
|
||||
#endif
|
||||
|
||||
case HRS_NOTIFICATION_ENABLED:
|
||||
/* USER CODE BEGIN HRS_NOTIFICATION_ENABLED */
|
||||
/**
|
||||
* It could be the enable notification is received twice without the disable notification in between
|
||||
*/
|
||||
HW_TS_Stop(HRSAPP_Context.TimerMeasurement_Id);
|
||||
HW_TS_Start(HRSAPP_Context.TimerMeasurement_Id, HRSAPP_MEASUREMENT_INTERVAL);
|
||||
/* USER CODE END HRS_NOTIFICATION_ENABLED */
|
||||
break;
|
||||
|
||||
case HRS_NOTIFICATION_DISABLED:
|
||||
/* USER CODE BEGIN HRS_NOTIFICATION_DISABLED */
|
||||
HW_TS_Stop(HRSAPP_Context.TimerMeasurement_Id);
|
||||
/* USER CODE END HRS_NOTIFICATION_DISABLED */
|
||||
break;
|
||||
|
||||
#if (BLE_CFG_OTA_REBOOT_CHAR != 0)
|
||||
case HRS_STM_BOOT_REQUEST_EVT:
|
||||
/* USER CODE BEGIN HRS_STM_BOOT_REQUEST_EVT */
|
||||
*(uint32_t*)SRAM1_BASE = *(uint32_t*)pNotification->DataTransfered.pPayload;
|
||||
NVIC_SystemReset();
|
||||
/* USER CODE END HRS_STM_BOOT_REQUEST_EVT */
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
/* USER CODE BEGIN HRS_Notification_Default */
|
||||
|
||||
/* USER CODE END HRS_Notification_Default */
|
||||
break;
|
||||
}
|
||||
/* USER CODE BEGIN HRS_Notification_2 */
|
||||
|
||||
/* USER CODE END HRS_Notification_2 */
|
||||
return;
|
||||
}
|
||||
|
||||
void HRSAPP_Init(void)
|
||||
{
|
||||
HrsProcessId = osThreadNew(HrsProcess, NULL, &HrsProcess_attr);
|
||||
/* USER CODE BEGIN HRSAPP_Init */
|
||||
/**
|
||||
* Set Body Sensor Location
|
||||
*/
|
||||
HRSAPP_Context.ResetEnergyExpended = 0;
|
||||
HRSAPP_Context.BodySensorLocationChar = HRS_BODY_SENSOR_LOCATION_HAND;
|
||||
HRS_UpdateChar(SENSOR_LOCATION_UUID, (uint8_t *)&HRSAPP_Context.BodySensorLocationChar);
|
||||
|
||||
|
||||
/**
|
||||
* Set Flags for measurement value
|
||||
*/
|
||||
|
||||
HRSAPP_Context.MeasurementvalueChar.Flags = ( HRS_HRM_VALUE_FORMAT_UINT16 |
|
||||
HRS_HRM_SENSOR_CONTACTS_PRESENT |
|
||||
HRS_HRM_SENSOR_CONTACTS_SUPPORTED |
|
||||
HRS_HRM_ENERGY_EXPENDED_PRESENT |
|
||||
HRS_HRM_RR_INTERVAL_PRESENT );
|
||||
|
||||
#if (BLE_CFG_HRS_ENERGY_EXPENDED_INFO_FLAG != 0)
|
||||
if(HRSAPP_Context.MeasurementvalueChar.Flags & HRS_HRM_ENERGY_EXPENDED_PRESENT)
|
||||
HRSAPP_Context.MeasurementvalueChar.EnergyExpended = 10;
|
||||
#endif
|
||||
|
||||
#if (BLE_CFG_HRS_ENERGY_RR_INTERVAL_FLAG != 0)
|
||||
if(HRSAPP_Context.MeasurementvalueChar.Flags & HRS_HRM_RR_INTERVAL_PRESENT)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
HRSAPP_Context.MeasurementvalueChar.NbreOfValidRRIntervalValues = BLE_CFG_HRS_ENERGY_RR_INTERVAL_FLAG;
|
||||
for(i = 0; i < BLE_CFG_HRS_ENERGY_RR_INTERVAL_FLAG; i++)
|
||||
HRSAPP_Context.MeasurementvalueChar.aRRIntervalValues[i] = 1024;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Create timer for Heart Rate Measurement
|
||||
*/
|
||||
HW_TS_Create(CFG_TIM_PROC_ID_ISR, &(HRSAPP_Context.TimerMeasurement_Id), hw_ts_Repeated, HrMeas);
|
||||
|
||||
/* USER CODE END HRSAPP_Init */
|
||||
return;
|
||||
}
|
||||
|
||||
static void HrsProcess(void *argument)
|
||||
{
|
||||
UNUSED(argument);
|
||||
|
||||
for(;;)
|
||||
{
|
||||
osThreadFlagsWait( 1, osFlagsWaitAny, osWaitForever);
|
||||
HRSAPP_Measurement( );
|
||||
}
|
||||
}
|
||||
|
||||
static void HRSAPP_Measurement(void)
|
||||
{
|
||||
/* USER CODE BEGIN HRSAPP_Measurement */
|
||||
uint32_t measurement;
|
||||
|
||||
measurement = ((HRSAPP_Read_RTC_SSR_SS()) & 0x07) + 65;
|
||||
|
||||
HRSAPP_Context.MeasurementvalueChar.MeasurementValue = measurement;
|
||||
#if (BLE_CFG_HRS_ENERGY_EXPENDED_INFO_FLAG != 0)
|
||||
if((HRSAPP_Context.MeasurementvalueChar.Flags & HRS_HRM_ENERGY_EXPENDED_PRESENT) &&
|
||||
(HRSAPP_Context.ResetEnergyExpended == 0))
|
||||
HRSAPP_Context.MeasurementvalueChar.EnergyExpended += 5;
|
||||
else if(HRSAPP_Context.ResetEnergyExpended == 1)
|
||||
HRSAPP_Context.ResetEnergyExpended = 0;
|
||||
#endif
|
||||
|
||||
HRS_UpdateChar(HEART_RATE_MEASURMENT_UUID, (uint8_t *)&HRSAPP_Context.MeasurementvalueChar);
|
||||
|
||||
/* USER CODE END HRSAPP_Measurement */
|
||||
return;
|
||||
}
|
||||
|
||||
static void HrMeas( void )
|
||||
{
|
||||
/**
|
||||
* The code shall be executed in the background as aci command may be sent
|
||||
* The background is the only place where the application can make sure a new aci command
|
||||
* is not sent if there is a pending one
|
||||
*/
|
||||
osThreadFlagsSet( HrsProcessId, 1 );
|
||||
|
||||
/* USER CODE BEGIN HrMeas */
|
||||
|
||||
/* USER CODE END HrMeas */
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static uint32_t HRSAPP_Read_RTC_SSR_SS ( void )
|
||||
{
|
||||
return ((uint32_t)(READ_BIT(RTC->SSR, RTC_SSR_SS)));
|
||||
}
|
||||
|
||||
/* USER CODE BEGIN FD */
|
||||
|
||||
/* USER CODE END FD */
|
||||
|
||||
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
|
||||
122
firmware/targets/f6/ble-glue/serial_service.c
Normal file
122
firmware/targets/f6/ble-glue/serial_service.c
Normal file
@ -0,0 +1,122 @@
|
||||
#include "serial_service.h"
|
||||
#include "app_common.h"
|
||||
#include "ble.h"
|
||||
|
||||
#include <furi.h>
|
||||
|
||||
#define SERIAL_SERVICE_TAG "serial service"
|
||||
|
||||
#define SERIAL_SVC_DATA_LEN_MAX 245
|
||||
|
||||
typedef struct {
|
||||
uint16_t svc_handle;
|
||||
uint16_t rx_char_handle;
|
||||
uint16_t tx_char_handle;
|
||||
} SerialSvc;
|
||||
|
||||
static SerialSvc* serial_svc;
|
||||
|
||||
static const uint8_t service_uuid[] = {0x00, 0x00, 0xfe, 0x60, 0xcc, 0x7a, 0x48, 0x2a, 0x98, 0x4a, 0x7f, 0x2e, 0xd5, 0xb3, 0xe5, 0x8f};
|
||||
static const uint8_t char_rx_uuid[] = {0x00, 0x00, 0xfe, 0x61, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19};
|
||||
static const uint8_t char_tx_uuid[] = {0x00, 0x00, 0xfe, 0x62, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19};
|
||||
|
||||
static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void *event) {
|
||||
SVCCTL_EvtAckStatus_t ret = SVCCTL_EvtNotAck;
|
||||
hci_event_pckt* event_pckt = (hci_event_pckt *)(((hci_uart_pckt*)event)->data);
|
||||
evt_blecore_aci* blecore_evt = (evt_blecore_aci*)event_pckt->data;
|
||||
aci_gatt_attribute_modified_event_rp0* attribute_modified;
|
||||
if(event_pckt->evt == HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE) {
|
||||
if(blecore_evt->ecode == ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE) {
|
||||
attribute_modified = (aci_gatt_attribute_modified_event_rp0*)blecore_evt->data;
|
||||
if(attribute_modified->Attr_Handle == serial_svc->tx_char_handle + 2) {
|
||||
// Descriptor handle
|
||||
ret = SVCCTL_EvtAckFlowEnable;
|
||||
FURI_LOG_D(SERIAL_SERVICE_TAG, "TX descriptor event");
|
||||
} else if(attribute_modified->Attr_Handle == serial_svc->tx_char_handle + 1) {
|
||||
FURI_LOG_D(SERIAL_SERVICE_TAG, "Received %d bytes", attribute_modified->Attr_Data_Length);
|
||||
serial_svc_update_rx(attribute_modified->Attr_Data, attribute_modified->Attr_Data_Length);
|
||||
ret = SVCCTL_EvtAckFlowEnable;
|
||||
}
|
||||
} else if(blecore_evt->ecode == ACI_GATT_SERVER_CONFIRMATION_VSEVT_CODE) {
|
||||
FURI_LOG_D(SERIAL_SERVICE_TAG, "Ack received", blecore_evt->ecode);
|
||||
ret = SVCCTL_EvtAckFlowEnable;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void serial_svc_start() {
|
||||
tBleStatus status;
|
||||
serial_svc = furi_alloc(sizeof(SerialSvc));
|
||||
// Register event handler
|
||||
SVCCTL_RegisterSvcHandler(serial_svc_event_handler);
|
||||
|
||||
// Add service
|
||||
status = aci_gatt_add_service(UUID_TYPE_128, (Service_UUID_t *)service_uuid, PRIMARY_SERVICE, 6, &serial_svc->svc_handle);
|
||||
if(status) {
|
||||
FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to add Serial service: %d", status);
|
||||
}
|
||||
|
||||
// Add TX characteristics
|
||||
status = aci_gatt_add_char(serial_svc->svc_handle, UUID_TYPE_128, (const Char_UUID_t*)char_tx_uuid,
|
||||
SERIAL_SVC_DATA_LEN_MAX,
|
||||
CHAR_PROP_WRITE_WITHOUT_RESP | CHAR_PROP_WRITE | CHAR_PROP_READ,
|
||||
ATTR_PERMISSION_NONE,
|
||||
GATT_NOTIFY_ATTRIBUTE_WRITE,
|
||||
10,
|
||||
CHAR_VALUE_LEN_VARIABLE,
|
||||
&serial_svc->tx_char_handle);
|
||||
if(status) {
|
||||
FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to add TX characteristic: %d", status);
|
||||
}
|
||||
|
||||
// Add RX characteristic
|
||||
status = aci_gatt_add_char(serial_svc->svc_handle, UUID_TYPE_128, (const Char_UUID_t*)char_rx_uuid,
|
||||
SERIAL_SVC_DATA_LEN_MAX,
|
||||
CHAR_PROP_READ | CHAR_PROP_INDICATE,
|
||||
ATTR_PERMISSION_NONE,
|
||||
GATT_DONT_NOTIFY_EVENTS,
|
||||
10,
|
||||
CHAR_VALUE_LEN_VARIABLE,
|
||||
&serial_svc->rx_char_handle);
|
||||
if(status) {
|
||||
FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to add RX characteristic: %d", status);
|
||||
}
|
||||
}
|
||||
|
||||
void serial_svc_stop() {
|
||||
tBleStatus status;
|
||||
if(serial_svc) {
|
||||
// Delete characteristics
|
||||
status = aci_gatt_del_char(serial_svc->svc_handle, serial_svc->tx_char_handle);
|
||||
if(status) {
|
||||
FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to delete TX characteristic: %d", status);
|
||||
}
|
||||
status = aci_gatt_del_char(serial_svc->svc_handle, serial_svc->rx_char_handle);
|
||||
if(status) {
|
||||
FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to delete RX characteristic: %d", status);
|
||||
}
|
||||
// Delete service
|
||||
status = aci_gatt_del_service(serial_svc->svc_handle);
|
||||
if(status) {
|
||||
FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to delete Serial service: %d", status);
|
||||
}
|
||||
free(serial_svc);
|
||||
serial_svc = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool serial_svc_update_rx(uint8_t* data, uint8_t data_len) {
|
||||
furi_assert(data_len < SERIAL_SVC_DATA_LEN_MAX);
|
||||
|
||||
tBleStatus result = aci_gatt_update_char_value(serial_svc->svc_handle,
|
||||
serial_svc->rx_char_handle,
|
||||
0,
|
||||
data_len,
|
||||
data);
|
||||
if(result) {
|
||||
FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed updating RX characteristic: %d", result);
|
||||
}
|
||||
return result != BLE_STATUS_SUCCESS;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user