mirror of
https://github.com/i12bp8/TagTinker.git
synced 2026-09-25 21:13:33 +00:00
Merge pull request #62 from Hoggormino/custom-size
Add a custom-size option for unrecognized infrared tags
This commit is contained in:
@@ -34,6 +34,7 @@ App(
|
||||
"views/numlock_input.c",
|
||||
"nfc/tagtinker_nfc.c",
|
||||
"scenes/tagtinker_scene_nfc_scan.c",
|
||||
"scenes/tagtinker_scene_custom_size.c",
|
||||
"wifi/tagtinker_wifi.c",
|
||||
"wifi/tagtinker_wifi_bmp.c",
|
||||
"scenes/tagtinker_scene_wifi_plugins.c",
|
||||
|
||||
@@ -119,6 +119,10 @@ bool tagtinker_barcode_to_plid(const char* barcode, uint8_t plid[4]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tagtinker_type_is_known(uint16_t type_code) {
|
||||
return find_profile_entry(type_code) != NULL;
|
||||
}
|
||||
|
||||
bool tagtinker_barcode_to_type(const char* barcode, uint16_t* type_code) {
|
||||
if(!barcode || strlen(barcode) != 17 || !type_code) return false;
|
||||
uint16_t type = 0;
|
||||
|
||||
@@ -105,6 +105,14 @@ static inline void tagtinker_profile_glass_size(
|
||||
bool tagtinker_is_barcode_valid(const char* barcode);
|
||||
bool tagtinker_barcode_to_plid(const char* barcode, uint8_t plid[4]);
|
||||
bool tagtinker_barcode_to_type(const char* barcode, uint16_t* type_code);
|
||||
|
||||
/* True when this type code has an entry in the built-in profile table. */
|
||||
bool tagtinker_type_is_known(uint16_t type_code);
|
||||
|
||||
/* Bounds for a user-entered custom tag size (pixels). Width is byte-aligned. */
|
||||
#define TAGTINKER_CUSTOM_SIZE_MIN 8U
|
||||
#define TAGTINKER_CUSTOM_SIZE_MAX 800U
|
||||
#define TAGTINKER_CUSTOM_SIZE_STEP 8U
|
||||
bool tagtinker_barcode_to_profile(const char* barcode, TagTinkerTagProfile* profile);
|
||||
|
||||
typedef struct {
|
||||
|
||||
@@ -25,6 +25,7 @@ void(*const tagtinker_scene_on_enter_handlers[])(void*) = {
|
||||
tagtinker_scene_wifi_plugins_on_enter,
|
||||
tagtinker_scene_wifi_setup_on_enter,
|
||||
tagtinker_scene_wifi_run_on_enter,
|
||||
tagtinker_scene_custom_size_on_enter,
|
||||
};
|
||||
|
||||
bool(*const tagtinker_scene_on_event_handlers[])(void*, SceneManagerEvent) = {
|
||||
@@ -48,6 +49,7 @@ bool(*const tagtinker_scene_on_event_handlers[])(void*, SceneManagerEvent) = {
|
||||
tagtinker_scene_wifi_plugins_on_event,
|
||||
tagtinker_scene_wifi_setup_on_event,
|
||||
tagtinker_scene_wifi_run_on_event,
|
||||
tagtinker_scene_custom_size_on_event,
|
||||
};
|
||||
|
||||
void(*const tagtinker_scene_on_exit_handlers[])(void*) = {
|
||||
@@ -71,6 +73,7 @@ void(*const tagtinker_scene_on_exit_handlers[])(void*) = {
|
||||
tagtinker_scene_wifi_plugins_on_exit,
|
||||
tagtinker_scene_wifi_setup_on_exit,
|
||||
tagtinker_scene_wifi_run_on_exit,
|
||||
tagtinker_scene_custom_size_on_exit,
|
||||
};
|
||||
|
||||
const SceneManagerHandlers tagtinker_scene_handlers = {
|
||||
|
||||
@@ -27,6 +27,7 @@ typedef enum {
|
||||
TagTinkerSceneWifiPlugins,
|
||||
TagTinkerSceneWifiSetup,
|
||||
TagTinkerSceneWifiRun,
|
||||
TagTinkerSceneCustomSize,
|
||||
TagTinkerSceneCount,
|
||||
} TagTinkerScene;
|
||||
|
||||
@@ -109,3 +110,7 @@ void tagtinker_scene_wifi_setup_on_exit(void* ctx);
|
||||
void tagtinker_scene_wifi_run_on_enter(void* ctx);
|
||||
bool tagtinker_scene_wifi_run_on_event(void* ctx, SceneManagerEvent event);
|
||||
void tagtinker_scene_wifi_run_on_exit(void* ctx);
|
||||
|
||||
void tagtinker_scene_custom_size_on_enter(void* ctx);
|
||||
bool tagtinker_scene_custom_size_on_event(void* ctx, SceneManagerEvent event);
|
||||
void tagtinker_scene_custom_size_on_exit(void* ctx);
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Custom size scene.
|
||||
*
|
||||
* Lets the user give a width, height and colour to a target whose type code is
|
||||
* not in the profile table, so Set Text and Set Image work for it. The IR
|
||||
* framing is identical to a table profile; only the screen geometry differs.
|
||||
* This does not guess any protocol and only applies to unknown-type targets.
|
||||
*/
|
||||
|
||||
#include "../tagtinker_app.h"
|
||||
#include "../protocol/tagtinker_proto.h"
|
||||
|
||||
enum {
|
||||
CustomSizeWidth,
|
||||
CustomSizeHeight,
|
||||
CustomSizeColor,
|
||||
CustomSizeSave,
|
||||
};
|
||||
|
||||
/* index 0 -> MIN, then MIN + i*STEP, up to MAX. */
|
||||
static uint32_t size_value_count(void) {
|
||||
return (TAGTINKER_CUSTOM_SIZE_MAX - TAGTINKER_CUSTOM_SIZE_MIN) / TAGTINKER_CUSTOM_SIZE_STEP +
|
||||
1U;
|
||||
}
|
||||
|
||||
static uint16_t size_index_to_px(uint8_t index) {
|
||||
return (uint16_t)(TAGTINKER_CUSTOM_SIZE_MIN + (uint32_t)index * TAGTINKER_CUSTOM_SIZE_STEP);
|
||||
}
|
||||
|
||||
static uint8_t size_px_to_index(uint16_t px) {
|
||||
if(px < TAGTINKER_CUSTOM_SIZE_MIN) return 0U;
|
||||
uint16_t clamped = px > TAGTINKER_CUSTOM_SIZE_MAX ? TAGTINKER_CUSTOM_SIZE_MAX : px;
|
||||
return (uint8_t)((clamped - TAGTINKER_CUSTOM_SIZE_MIN) / TAGTINKER_CUSTOM_SIZE_STEP);
|
||||
}
|
||||
|
||||
static void width_changed(VariableItem* item) {
|
||||
TagTinkerApp* app = variable_item_get_context(item);
|
||||
app->custom_size_w = size_index_to_px(variable_item_get_current_value_index(item));
|
||||
char buf[8];
|
||||
snprintf(buf, sizeof(buf), "%u", app->custom_size_w);
|
||||
variable_item_set_current_value_text(item, buf);
|
||||
}
|
||||
|
||||
static void height_changed(VariableItem* item) {
|
||||
TagTinkerApp* app = variable_item_get_context(item);
|
||||
app->custom_size_h = size_index_to_px(variable_item_get_current_value_index(item));
|
||||
char buf[8];
|
||||
snprintf(buf, sizeof(buf), "%u", app->custom_size_h);
|
||||
variable_item_set_current_value_text(item, buf);
|
||||
}
|
||||
|
||||
static void color_changed(VariableItem* item) {
|
||||
TagTinkerApp* app = variable_item_get_context(item);
|
||||
app->custom_size_color = (uint8_t)variable_item_get_current_value_index(item);
|
||||
variable_item_set_current_value_text(
|
||||
item, tagtinker_profile_color_label((TagTinkerTagColor)app->custom_size_color));
|
||||
}
|
||||
|
||||
static void custom_size_cb(void* ctx, uint32_t index) {
|
||||
TagTinkerApp* app = ctx;
|
||||
if(index != CustomSizeSave) return;
|
||||
if(app->selected_target < 0 || app->selected_target >= app->target_count) return;
|
||||
|
||||
TagTinkerTarget* target = &app->targets[app->selected_target];
|
||||
target->custom_width = app->custom_size_w;
|
||||
target->custom_height = app->custom_size_h;
|
||||
target->custom_color = app->custom_size_color;
|
||||
tagtinker_target_refresh_profile(target);
|
||||
tagtinker_targets_save(app);
|
||||
|
||||
/* Keep the app's working size in step with the target we just sized. */
|
||||
app->esl_width = target->profile.width;
|
||||
app->esl_height = target->profile.height;
|
||||
|
||||
scene_manager_previous_scene(app->scene_manager);
|
||||
}
|
||||
|
||||
void tagtinker_scene_custom_size_on_enter(void* ctx) {
|
||||
TagTinkerApp* app = ctx;
|
||||
|
||||
uint16_t w = TAGTINKER_CUSTOM_SIZE_MIN;
|
||||
uint16_t h = TAGTINKER_CUSTOM_SIZE_MIN;
|
||||
uint8_t color = 0U;
|
||||
if(app->selected_target >= 0 && app->selected_target < app->target_count) {
|
||||
const TagTinkerTarget* t = &app->targets[app->selected_target];
|
||||
if(t->custom_width > 0) w = t->custom_width;
|
||||
if(t->custom_height > 0) h = t->custom_height;
|
||||
color = t->custom_color;
|
||||
}
|
||||
app->custom_size_w = w;
|
||||
app->custom_size_h = h;
|
||||
app->custom_size_color = color;
|
||||
|
||||
variable_item_list_reset(app->var_item_list);
|
||||
|
||||
char buf[8];
|
||||
VariableItem* item_w = variable_item_list_add(
|
||||
app->var_item_list, "Width", (uint8_t)size_value_count(), width_changed, app);
|
||||
variable_item_set_current_value_index(item_w, size_px_to_index(w));
|
||||
snprintf(buf, sizeof(buf), "%u", w);
|
||||
variable_item_set_current_value_text(item_w, buf);
|
||||
|
||||
VariableItem* item_h = variable_item_list_add(
|
||||
app->var_item_list, "Height", (uint8_t)size_value_count(), height_changed, app);
|
||||
variable_item_set_current_value_index(item_h, size_px_to_index(h));
|
||||
snprintf(buf, sizeof(buf), "%u", h);
|
||||
variable_item_set_current_value_text(item_h, buf);
|
||||
|
||||
VariableItem* item_c =
|
||||
variable_item_list_add(app->var_item_list, "Color", 3, color_changed, app);
|
||||
variable_item_set_current_value_index(item_c, color);
|
||||
variable_item_set_current_value_text(
|
||||
item_c, tagtinker_profile_color_label((TagTinkerTagColor)color));
|
||||
|
||||
variable_item_list_add(app->var_item_list, ">> Save <<", 0, NULL, app);
|
||||
variable_item_list_set_enter_callback(app->var_item_list, custom_size_cb, app);
|
||||
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, TagTinkerViewVarItemList);
|
||||
}
|
||||
|
||||
bool tagtinker_scene_custom_size_on_event(void* ctx, SceneManagerEvent event) {
|
||||
UNUSED(ctx);
|
||||
UNUSED(event);
|
||||
return false;
|
||||
}
|
||||
|
||||
void tagtinker_scene_custom_size_on_exit(void* ctx) {
|
||||
TagTinkerApp* app = ctx;
|
||||
variable_item_list_reset(app->var_item_list);
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
*/
|
||||
|
||||
#include "../tagtinker_app.h"
|
||||
#include "../protocol/tagtinker_proto.h"
|
||||
|
||||
static void target_actions_cb(void* ctx, uint32_t index) {
|
||||
TagTinkerApp* app = ctx;
|
||||
@@ -98,6 +99,14 @@ void tagtinker_scene_target_actions_on_enter(void* ctx) {
|
||||
submenu_add_item(app->submenu, "WiFi Plugins", TagTinkerTargetWifiPlugins, target_actions_cb, app);
|
||||
}
|
||||
|
||||
/* An unknown type has no table profile; let the user give it a size so the
|
||||
* graphics actions above become usable. Shown whether or not a size is set,
|
||||
* so it can be changed later. */
|
||||
if(target && !tagtinker_type_is_known(target->profile.type_code)) {
|
||||
submenu_add_item(
|
||||
app->submenu, "Set Custom Size", TagTinkerTargetCustomSize, target_actions_cb, app);
|
||||
}
|
||||
|
||||
submenu_add_item(app->submenu, "LED Test", TagTinkerTargetPingFlash, target_actions_cb, app);
|
||||
submenu_add_item(app->submenu, "Delete Tag", TagTinkerTargetDeleteTag, target_actions_cb, app);
|
||||
|
||||
@@ -129,6 +138,9 @@ bool tagtinker_scene_target_actions_on_event(void* ctx, SceneManagerEvent event)
|
||||
if(!tagtinker_target_supports_graphics(&app->targets[app->selected_target])) return true;
|
||||
scene_manager_next_scene(app->scene_manager, TagTinkerSceneWifiPlugins);
|
||||
return true;
|
||||
case TagTinkerTargetCustomSize:
|
||||
scene_manager_next_scene(app->scene_manager, TagTinkerSceneCustomSize);
|
||||
return true;
|
||||
case TagTinkerTargetPingFlash:
|
||||
{
|
||||
TagTinkerTarget* target = &app->targets[app->selected_target];
|
||||
|
||||
+37
-10
@@ -59,6 +59,17 @@ void tagtinker_target_refresh_profile(TagTinkerTarget* target) {
|
||||
|
||||
memset(&target->profile, 0, sizeof(target->profile));
|
||||
tagtinker_barcode_to_profile(target->barcode, &target->profile);
|
||||
|
||||
/* If the barcode's type is not in the table but the user gave a size, build
|
||||
* a profile from it. A real table profile is left untouched. */
|
||||
if(!target->profile.known && target->custom_width > 0 && target->custom_height > 0) {
|
||||
target->profile.width = target->custom_width;
|
||||
target->profile.height = target->custom_height;
|
||||
target->profile.color = (TagTinkerTagColor)target->custom_color;
|
||||
target->profile.kind = TagTinkerTagKindDotMatrix;
|
||||
target->profile.model_name = "Custom";
|
||||
target->profile.known = true;
|
||||
}
|
||||
}
|
||||
|
||||
void tagtinker_target_set_default_name(TagTinkerApp* app, TagTinkerTarget* target) {
|
||||
@@ -512,22 +523,35 @@ void tagtinker_targets_load(TagTinkerApp* app) {
|
||||
if(nl) *nl = '\0';
|
||||
|
||||
if(*line) {
|
||||
char* sep = strchr(line, '|');
|
||||
if(sep) *sep = '\0';
|
||||
/* Split into up to 5 fields: barcode|name|cw|ch|ccolor.
|
||||
* Older files have only barcode|name; the rest default to 0. */
|
||||
char* fields[5] = {line, NULL, NULL, NULL, NULL};
|
||||
char* p = line;
|
||||
for(uint8_t f = 1; f < 5; f++) {
|
||||
char* sep = strchr(p, '|');
|
||||
if(!sep) break;
|
||||
*sep = '\0';
|
||||
fields[f] = sep + 1;
|
||||
p = sep + 1;
|
||||
}
|
||||
|
||||
if(tagtinker_barcode_to_plid(line, app->targets[app->target_count].plid)) {
|
||||
if(tagtinker_barcode_to_plid(fields[0], app->targets[app->target_count].plid)) {
|
||||
TagTinkerTarget* target = &app->targets[app->target_count];
|
||||
strncpy(target->barcode, line, TAGTINKER_BC_LEN);
|
||||
memset(target, 0, sizeof(*target));
|
||||
strncpy(target->barcode, fields[0], TAGTINKER_BC_LEN);
|
||||
target->barcode[TAGTINKER_BC_LEN] = '\0';
|
||||
memset(target->name, 0, sizeof(target->name));
|
||||
|
||||
if(sep && *(sep + 1)) {
|
||||
strncpy(target->name, sep + 1, TAGTINKER_TARGET_NAME_LEN);
|
||||
if(fields[1] && *fields[1]) {
|
||||
strncpy(target->name, fields[1], TAGTINKER_TARGET_NAME_LEN);
|
||||
target->name[TAGTINKER_TARGET_NAME_LEN] = '\0';
|
||||
} else {
|
||||
tagtinker_target_set_default_name(app, target);
|
||||
}
|
||||
|
||||
if(fields[2]) target->custom_width = (uint16_t)atoi(fields[2]);
|
||||
if(fields[3]) target->custom_height = (uint16_t)atoi(fields[3]);
|
||||
if(fields[4]) target->custom_color = (uint8_t)atoi(fields[4]);
|
||||
|
||||
tagtinker_target_refresh_profile(target);
|
||||
app->target_count++;
|
||||
}
|
||||
@@ -551,13 +575,16 @@ bool tagtinker_targets_save(const TagTinkerApp* app) {
|
||||
if(storage_file_open(file, APP_DATA_PATH("targets.txt"), FSAM_WRITE, FSOM_CREATE_ALWAYS)) {
|
||||
ok = true;
|
||||
for(uint8_t i = 0; i < app->target_count; i++) {
|
||||
char line[64];
|
||||
char line[96];
|
||||
int len = snprintf(
|
||||
line,
|
||||
sizeof(line),
|
||||
"%s|%s\n",
|
||||
"%s|%s|%u|%u|%u\n",
|
||||
app->targets[i].barcode,
|
||||
app->targets[i].name);
|
||||
app->targets[i].name,
|
||||
app->targets[i].custom_width,
|
||||
app->targets[i].custom_height,
|
||||
app->targets[i].custom_color);
|
||||
|
||||
if(len <= 0 || !storage_file_write(file, line, (uint16_t)len)) {
|
||||
ok = false;
|
||||
|
||||
@@ -105,6 +105,12 @@ typedef struct {
|
||||
char barcode[TAGTINKER_BC_LEN + 1];
|
||||
uint8_t plid[4];
|
||||
TagTinkerTagProfile profile;
|
||||
/* User-entered geometry for a tag whose type code is not in the profile
|
||||
* table. 0x0 means unset. Applied by tagtinker_target_refresh_profile only
|
||||
* when the barcode's type is unknown, so a real profile is never overridden. */
|
||||
uint16_t custom_width;
|
||||
uint16_t custom_height;
|
||||
uint8_t custom_color; /* TagTinkerTagColor */
|
||||
} TagTinkerTarget;
|
||||
|
||||
struct TagTinkerApp {
|
||||
@@ -156,6 +162,11 @@ struct TagTinkerApp {
|
||||
|
||||
/* Saved targets */
|
||||
TagTinkerTarget targets[TAGTINKER_MAX_TARGETS];
|
||||
|
||||
/* Custom-size scene working values. */
|
||||
uint16_t custom_size_w;
|
||||
uint16_t custom_size_h;
|
||||
uint8_t custom_size_color;
|
||||
uint8_t target_count;
|
||||
|
||||
/* Text to push */
|
||||
@@ -294,6 +305,7 @@ typedef enum {
|
||||
TagTinkerTargetDeleteSyncedImages,
|
||||
TagTinkerTargetPingFlash,
|
||||
TagTinkerTargetDeleteTag,
|
||||
TagTinkerTargetCustomSize,
|
||||
} TagTinkerTargetActionItem;
|
||||
|
||||
void tagtinker_target_refresh_profile(TagTinkerTarget* target);
|
||||
|
||||
Reference in New Issue
Block a user