Update UniTemp and run fbt format

This commit is contained in:
MX 2023-01-18 22:25:39 +03:00
parent e4aad248cf
commit 5a9da13d84
No known key found for this signature in database
GPG key ID: 6C4C311DFD4B4AB5
36 changed files with 1478 additions and 966 deletions

View file

@ -51,25 +51,32 @@ static void render_callback(Canvas *const canvas, void *ctx) {
/* Call who is in charge right now. */
switch(app->current_view) {
case ViewRawPulses: render_view_raw_pulses(canvas,app); break;
case ViewInfo: render_view_info(canvas,app); break;
case ViewRawPulses:
render_view_raw_pulses(canvas, app);
break;
case ViewInfo:
render_view_info(canvas, app);
break;
case ViewFrequencySettings:
case ViewModulationSettings:
render_view_settings(canvas,app); break;
case ViewDirectSampling: render_view_direct_sampling(canvas,app); break;
case ViewLast: furi_crash(TAG " ViewLast selected"); break;
render_view_settings(canvas, app);
break;
case ViewDirectSampling:
render_view_direct_sampling(canvas, app);
break;
case ViewLast:
furi_crash(TAG " ViewLast selected");
break;
}
}
/* Here all we do is putting the events into the queue that will be handled
* in the while() loop of the app entry point function. */
static void input_callback(InputEvent* input_event, void* ctx)
{
static void input_callback(InputEvent* input_event, void* ctx) {
ProtoViewApp* app = ctx;
furi_message_queue_put(app->event_queue, input_event, FuriWaitForever);
}
/* Called to switch view (when left/right is pressed). Handles
* changing the current view ID and calling the enter/exit view
* callbacks if needed. */
@ -155,13 +162,10 @@ ProtoViewApp* protoview_app_alloc() {
app->txrx->environment = subghz_environment_alloc();
subghz_environment_set_protocol_registry(
app->txrx->environment, (void*)&protoview_protocol_registry);
app->txrx->receiver =
subghz_receiver_alloc_init(app->txrx->environment);
subghz_receiver_set_filter(app->txrx->receiver,
SubGhzProtocolFlag_Decodable);
app->txrx->receiver = subghz_receiver_alloc_init(app->txrx->environment);
subghz_receiver_set_filter(app->txrx->receiver, SubGhzProtocolFlag_Decodable);
subghz_worker_set_overrun_callback(
app->txrx->worker,
(SubGhzWorkerOverrunCallback)subghz_receiver_reset);
app->txrx->worker, (SubGhzWorkerOverrunCallback)subghz_receiver_reset);
subghz_worker_set_pair_callback(
app->txrx->worker, (SubGhzWorkerPairCallback)subghz_receiver_decode);
subghz_worker_set_context(app->txrx->worker, app->txrx->receiver);
@ -254,26 +258,22 @@ int32_t protoview_app_entry(void* p) {
while(app->running) {
FuriStatus qstat = furi_message_queue_get(app->event_queue, &input, 100);
if(qstat == FuriStatusOk) {
if (DEBUG_MSG) FURI_LOG_E(TAG, "Main Loop - Input: type %d key %u",
input.type, input.key);
if(DEBUG_MSG)
FURI_LOG_E(TAG, "Main Loop - Input: type %d key %u", input.type, input.key);
/* Handle navigation here. Then handle view-specific inputs
* in the view specific handling function. */
if (input.type == InputTypeShort &&
input.key == InputKeyBack)
{
if(input.type == InputTypeShort && input.key == InputKeyBack) {
/* Exit the app. */
app->running = 0;
} else if (input.type == InputTypeShort &&
input.key == InputKeyRight &&
get_current_subview(app) == 0)
{
} else if(
input.type == InputTypeShort && input.key == InputKeyRight &&
get_current_subview(app) == 0) {
/* Go to the next view. */
app_switch_view(app, AppNextView);
} else if (input.type == InputTypeShort &&
input.key == InputKeyLeft &&
get_current_subview(app) == 0)
{
} else if(
input.type == InputTypeShort && input.key == InputKeyLeft &&
get_current_subview(app) == 0) {
/* Go to the previous view. */
app_switch_view(app, AppPrevView);
} else {
@ -293,14 +293,17 @@ int32_t protoview_app_entry(void* p) {
case ViewDirectSampling:
process_input_direct_sampling(app, input);
break;
case ViewLast: furi_crash(TAG " ViewLast selected"); break;
case ViewLast:
furi_crash(TAG " ViewLast selected");
break;
}
}
} else {
/* Useful to understand if the app is still alive when it
* does not respond because of bugs. */
if(DEBUG_MSG) {
static int c = 0; c++;
static int c = 0;
c++;
if(!(c % 20)) FURI_LOG_E(TAG, "Loop timeout");
}
}
@ -315,7 +318,8 @@ int32_t protoview_app_entry(void* p) {
view_dispatcher_enable_queue(app->view_dispatcher);
app->text_input = text_input_alloc();
view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
view_dispatcher_add_view(app->view_dispatcher, 0, text_input_get_view(app->text_input));
view_dispatcher_add_view(
app->view_dispatcher, 0, text_input_get_view(app->text_input));
view_dispatcher_switch_to_view(app->view_dispatcher, 0);
/* Setup the text input view. The different parameters are set
@ -331,7 +335,8 @@ int32_t protoview_app_entry(void* p) {
false);
/* Run the dispatcher with the keyboard. */
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
view_dispatcher_attach_to_gui(
app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
view_dispatcher_run(app->view_dispatcher);
/* Undo all it: remove the view from the dispatcher, free it
@ -359,4 +364,3 @@ int32_t protoview_app_entry(void* p) {
protoview_app_free(app);
return 0;
}

View file

@ -50,10 +50,7 @@ typedef enum {
} ProtoViewCurrentView;
/* Used by app_switch_view() */
typedef enum {
AppNextView,
AppPrevView
} SwitchViewDirection;
typedef enum { AppNextView, AppPrevView } SwitchViewDirection;
typedef struct {
const char* name; // Name to show to the user.
@ -192,13 +189,38 @@ void reset_current_signal(ProtoViewApp *app);
void scan_for_signal(ProtoViewApp* app);
bool bitmap_get(uint8_t* b, uint32_t blen, uint32_t bitpos);
void bitmap_set(uint8_t* b, uint32_t blen, uint32_t bitpos, bool val);
void bitmap_copy(uint8_t *d, uint32_t dlen, uint32_t doff, uint8_t *s, uint32_t slen, uint32_t soff, uint32_t count);
void bitmap_copy(
uint8_t* d,
uint32_t dlen,
uint32_t doff,
uint8_t* s,
uint32_t slen,
uint32_t soff,
uint32_t count);
void bitmap_set_pattern(uint8_t* b, uint32_t blen, uint32_t off, const char* pat);
void bitmap_reverse_bytes(uint8_t* p, uint32_t len);
bool bitmap_match_bits(uint8_t* b, uint32_t blen, uint32_t bitpos, const char* bits);
uint32_t bitmap_seek_bits(uint8_t *b, uint32_t blen, uint32_t startpos, uint32_t maxbits, const char *bits);
uint32_t convert_from_line_code(uint8_t *buf, uint64_t buflen, uint8_t *bits, uint32_t len, uint32_t offset, const char *zero_pattern, const char *one_pattern);
uint32_t convert_from_diff_manchester(uint8_t *buf, uint64_t buflen, uint8_t *bits, uint32_t len, uint32_t off, bool previous);
uint32_t bitmap_seek_bits(
uint8_t* b,
uint32_t blen,
uint32_t startpos,
uint32_t maxbits,
const char* bits);
uint32_t convert_from_line_code(
uint8_t* buf,
uint64_t buflen,
uint8_t* bits,
uint32_t len,
uint32_t offset,
const char* zero_pattern,
const char* one_pattern);
uint32_t convert_from_diff_manchester(
uint8_t* buf,
uint64_t buflen,
uint8_t* bits,
uint32_t len,
uint32_t off,
bool previous);
void init_msg_info(ProtoViewMsgInfo* i, ProtoViewApp* app);
void free_msg_info(ProtoViewMsgInfo* i);
@ -222,9 +244,14 @@ void view_exit_settings(ProtoViewApp *app);
int get_current_subview(ProtoViewApp* app);
void show_available_subviews(Canvas* canvas, ProtoViewApp* app, int last_subview);
bool process_subview_updown(ProtoViewApp* app, InputEvent input, int last_subview);
void canvas_draw_str_with_border(Canvas* canvas, uint8_t x, uint8_t y, const char* str, Color text_color, Color border_color);
void show_keyboard(ProtoViewApp *app, char *buffer, uint32_t buflen,
void (*done_callback)(void*));
void canvas_draw_str_with_border(
Canvas* canvas,
uint8_t x,
uint8_t y,
const char* str,
Color text_color,
Color border_color);
void show_keyboard(ProtoViewApp* app, char* buffer, uint32_t buflen, void (*done_callback)(void*));
void dismiss_keyboard(ProtoViewApp* app);
/* crc.c */

View file

@ -50,8 +50,7 @@ void raw_samples_add(RawSamplesBuffer *s, bool level, uint32_t dur) {
/* Get the sample from the buffer. It is possible to use out of range indexes
* as 'idx' because the modulo operation will rewind back from the start. */
void raw_samples_get(RawSamplesBuffer *s, uint32_t idx, bool *level, uint32_t *dur)
{
void raw_samples_get(RawSamplesBuffer* s, uint32_t idx, bool* level, uint32_t* dur) {
furi_mutex_acquire(s->mutex, FuriWaitForever);
idx = (s->idx + idx) % RAW_SAMPLES_NUM;
*level = s->samples[idx].level;

View file

@ -4,7 +4,8 @@
/* Our circular buffer of raw samples, used in order to display
* the signal. */
#define RAW_SAMPLES_NUM 2048 /* Use a power of two: we take the modulo
#define RAW_SAMPLES_NUM \
2048 /* Use a power of two: we take the modulo
of the index quite often to normalize inside
the range, and division is slow. */
typedef struct RawSamplesBuffer {

View file

@ -13,14 +13,16 @@ void raw_sampling_worker_start(ProtoViewApp *app);
void raw_sampling_worker_stop(ProtoViewApp* app);
ProtoViewModulation ProtoViewModulations[] = {
{"OOK 650Khz", "FuriHalSubGhzPresetOok650Async",
FuriHalSubGhzPresetOok650Async, NULL},
{"OOK 270Khz", "FuriHalSubGhzPresetOok270Async",
FuriHalSubGhzPresetOok270Async, NULL},
{"2FSK 2.38Khz", "FuriHalSubGhzPreset2FSKDev238Async",
FuriHalSubGhzPreset2FSKDev238Async, NULL},
{"2FSK 47.6Khz", "FuriHalSubGhzPreset2FSKDev476Async",
FuriHalSubGhzPreset2FSKDev476Async, NULL},
{"OOK 650Khz", "FuriHalSubGhzPresetOok650Async", FuriHalSubGhzPresetOok650Async, NULL},
{"OOK 270Khz", "FuriHalSubGhzPresetOok270Async", FuriHalSubGhzPresetOok270Async, NULL},
{"2FSK 2.38Khz",
"FuriHalSubGhzPreset2FSKDev238Async",
FuriHalSubGhzPreset2FSKDev238Async,
NULL},
{"2FSK 47.6Khz",
"FuriHalSubGhzPreset2FSKDev476Async",
FuriHalSubGhzPreset2FSKDev476Async,
NULL},
{"TPMS 1 (FSK)", NULL, 0, (uint8_t*)protoview_subghz_tpms1_fsk_async_regs},
{"TPMS 2 (OOK)", NULL, 0, (uint8_t*)protoview_subghz_tpms2_ook_async_regs},
{"TPMS 3 (FSK)", NULL, 0, (uint8_t*)protoview_subghz_tpms3_fsk_async_regs},
@ -64,9 +66,7 @@ uint32_t radio_rx(ProtoViewApp* app) {
furi_hal_subghz_flush_rx();
furi_hal_subghz_rx();
if(!app->txrx->debug_timer_sampling) {
furi_hal_subghz_start_async_rx(subghz_worker_rx_callback,
app->txrx->worker);
furi_hal_subghz_start_async_rx(subghz_worker_rx_callback, app->txrx->worker);
subghz_worker_start(app->txrx->worker);
} else {
raw_sampling_worker_start(app);

View file

@ -3,8 +3,7 @@
/* CRC8 with the specified initialization value 'init' and
* polynomial 'poly'. */
uint8_t crc8(const uint8_t *data, size_t len, uint8_t init, uint8_t poly)
{
uint8_t crc8(const uint8_t* data, size_t len, uint8_t init, uint8_t poly) {
uint8_t crc = init;
size_t i, j;
for(i = 0; i < len; i++) {

View file

@ -76,7 +76,8 @@ static uint8_t protoview_subghz_tpms1_fsk_async_regs[][2] = {
// // Modem Configuration
{CC1101_MDMCFG0, 0x00},
{CC1101_MDMCFG1, 0x02},
{CC1101_MDMCFG2, 0x04}, // Format 2-FSK/FM, No preamble/sync, Disable (current optimized). Other code reading TPMS uses GFSK, but should be the same when in RX mode.
{CC1101_MDMCFG2,
0x04}, // Format 2-FSK/FM, No preamble/sync, Disable (current optimized). Other code reading TPMS uses GFSK, but should be the same when in RX mode.
{CC1101_MDMCFG3, 0x93}, // Data rate is 20kBaud
{CC1101_MDMCFG4, 0x59}, // Rx bandwidth filter is 325 kHz
{CC1101_DEVIATN, 0x41}, // Deviation 28.56 kHz
@ -168,7 +169,8 @@ static uint8_t protoview_subghz_tpms3_fsk_async_regs[][2] = {
// // Modem Configuration
{CC1101_MDMCFG0, 0x00},
{CC1101_MDMCFG1, 0x02},
{CC1101_MDMCFG2, 0x04}, // Format 2-FSK/FM, No preamble/sync, Disable (current optimized). Other code reading TPMS uses GFSK, but should be the same when in RX mode.
{CC1101_MDMCFG2,
0x04}, // Format 2-FSK/FM, No preamble/sync, Disable (current optimized). Other code reading TPMS uses GFSK, but should be the same when in RX mode.
{CC1101_MDMCFG3, 0x93}, // Data rate is 40kBaud
{CC1101_MDMCFG4, 0x6A}, // 6 = BW filter 270kHz, A = Data rate exp
{CC1101_DEVIATN, 0x41}, // Deviation 28kHz
@ -240,5 +242,3 @@ static uint8_t protoview_subghz_tpms4_fsk_async_regs[][2] = {
/* End */
{0, 0},
};

View file

@ -26,8 +26,7 @@ typedef struct SubGhzProtocolDecoderprotoview {
void* subghz_protocol_decoder_protoview_alloc(SubGhzEnvironment* environment) {
UNUSED(environment);
SubGhzProtocolDecoderprotoview* instance =
malloc(sizeof(SubGhzProtocolDecoderprotoview));
SubGhzProtocolDecoderprotoview* instance = malloc(sizeof(SubGhzProtocolDecoderprotoview));
instance->base.protocol = &subghz_protocol_protoview;
return instance;
}
@ -66,8 +65,7 @@ uint8_t subghz_protocol_decoder_protoview_get_hash_data(void* context) {
bool subghz_protocol_decoder_protoview_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzRadioPreset* preset)
{
SubGhzRadioPreset* preset) {
UNUSED(context);
UNUSED(flipper_format);
UNUSED(preset);
@ -75,15 +73,13 @@ bool subghz_protocol_decoder_protoview_serialize(
}
/* Not used. */
bool subghz_protocol_decoder_protoview_deserialize(void* context, FlipperFormat* flipper_format)
{
bool subghz_protocol_decoder_protoview_deserialize(void* context, FlipperFormat* flipper_format) {
UNUSED(context);
UNUSED(flipper_format);
return false;
}
void subhz_protocol_decoder_protoview_get_string(void* context, FuriString* output)
{
void subhz_protocol_decoder_protoview_get_string(void* context, FuriString* output) {
furi_assert(context);
furi_string_cat_printf(output, "Protoview");
}
@ -116,5 +112,4 @@ const SubGhzProtocol* protoview_protocol_registry_items[] = {
const SubGhzProtocolRegistry protoview_protocol_registry = {
.items = protoview_protocol_registry_items,
.size = COUNT_OF(protoview_protocol_registry_items)
};
.size = COUNT_OF(protoview_protocol_registry_items)};

View file

@ -31,8 +31,7 @@ static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoView
off += strlen(sync_patterns[j]) - 1;
uint8_t d[3]; /* 24 bits of data. */
uint32_t decoded =
convert_from_line_code(d,sizeof(d),bits,numbytes,off,"1000","1110");
uint32_t decoded = convert_from_line_code(d, sizeof(d), bits, numbytes, off, "1000", "1110");
if(DEBUG_MSG) FURI_LOG_E(TAG, "B4B1 decoded: %lu", decoded);
if(decoded < 24) return false;
@ -45,6 +44,4 @@ static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoView
return true;
}
ProtoViewDecoder B4B1Decoder = {
"B4B1", decode
};
ProtoViewDecoder B4B1Decoder = {"B4B1", decode};

View file

@ -25,11 +25,11 @@
#include "../app.h"
static bool decode(uint8_t* bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo* info) {
/* In the sync pattern, we require the 12 high/low pulses and at least
* half the gap we expect (5 pulses times, one is the final zero in the
* 24 symbols high/low sequence, then other 4). */
const char *sync_pattern = "101010101010101010101010" "0000";
const char* sync_pattern = "101010101010101010101010"
"0000";
uint8_t sync_len = 24 + 4;
if(numbits - sync_len + sync_len < 3 * 66) return false;
uint32_t off = bitmap_seek_bits(bits, numbytes, 0, numbits, sync_pattern);
@ -42,17 +42,15 @@ static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoView
* symbols of gap, to avoid missing the signal for a matter of wrong
* timing. */
uint8_t gap_len = 0;
while(gap_len <= 7 && bitmap_get(bits,numbytes,off+gap_len) == 0)
gap_len++;
while(gap_len <= 7 && bitmap_get(bits, numbytes, off + gap_len) == 0) gap_len++;
if(gap_len < 3 || gap_len > 7) return false;
off += gap_len;
FURI_LOG_E(TAG, "Keeloq preamble+sync found");
uint8_t raw[9] = {0};
uint32_t decoded =
convert_from_line_code(raw,sizeof(raw),bits,numbytes,off,
"110","100"); /* Pulse width modulation. */
uint32_t decoded = convert_from_line_code(
raw, sizeof(raw), bits, numbytes, off, "110", "100"); /* Pulse width modulation. */
FURI_LOG_E(TAG, "Keeloq decoded bits: %lu", decoded);
if(decoded < 66) return false; /* Require the full 66 bits. */
@ -66,26 +64,35 @@ static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoView
int s1 = (buttons & 4) != 0;
int s2 = (buttons & 8) != 0;
int remote_id = ((raw[7]&0x0f) << 24) |
(raw[6] << 16) |
(raw[5] << 8) |
(raw[4] << 0);
int remote_id = ((raw[7] & 0x0f) << 24) | (raw[6] << 16) | (raw[5] << 8) | (raw[4] << 0);
int lowbat = raw[8] & 0x80;
snprintf(info->name, sizeof(info->name), "%s", "Keeloq remote");
snprintf(info->raw,sizeof(info->raw),"%02X%02X%02X%02X%02X%02X%02X%02X%02X",
raw[0],raw[1],raw[2],raw[3],raw[4],raw[5],
raw[6],raw[7],raw[8]);
snprintf(info->info1,sizeof(info->info1),"Encrpyted %02X%02X%02X%02X",
raw[3],raw[2],raw[1],raw[0]);
snprintf(
info->raw,
sizeof(info->raw),
"%02X%02X%02X%02X%02X%02X%02X%02X%02X",
raw[0],
raw[1],
raw[2],
raw[3],
raw[4],
raw[5],
raw[6],
raw[7],
raw[8]);
snprintf(
info->info1,
sizeof(info->info1),
"Encrpyted %02X%02X%02X%02X",
raw[3],
raw[2],
raw[1],
raw[0]);
snprintf(info->info2, sizeof(info->info2), "ID %08X", remote_id);
snprintf(info->info3,sizeof(info->info3),"s0-s3: %d%d%d%d",
s0,s1,s2,s3);
snprintf(info->info4,sizeof(info->info4),"Low battery? %s",
lowbat ? "yes" : "no");
snprintf(info->info3, sizeof(info->info3), "s0-s3: %d%d%d%d", s0, s1, s2, s3);
snprintf(info->info4, sizeof(info->info4), "Low battery? %s", lowbat ? "yes" : "no");
return true;
}
ProtoViewDecoder KeeloqDecoder = {
"Keeloq", decode
};
ProtoViewDecoder KeeloqDecoder = {"Keeloq", decode};

View file

@ -8,7 +8,10 @@
static bool decode(uint8_t* bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo* info) {
if(numbits < 32) return false;
const char *sync_pattern = "01100110" "01100110" "10010110" "10010110";
const char* sync_pattern = "01100110"
"01100110"
"10010110"
"10010110";
uint64_t off = bitmap_seek_bits(bits, numbytes, 0, numbits, sync_pattern);
if(off == BITMAP_SEEK_NOT_FOUND) return false;
FURI_LOG_E(TAG, "Oregon2 preamble+sync found");
@ -27,41 +30,62 @@ static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoView
char temp[3] = {0}, deviceid[2] = {0}, hum[2] = {0};
for(int j = 0; j < 64; j += 4) {
uint8_t nib[1];
nib[0] = (bitmap_get(buffer,8,j+0) |
bitmap_get(buffer,8,j+1) << 1 |
bitmap_get(buffer,8,j+2) << 2 |
bitmap_get(buffer,8,j+3) << 3);
nib[0] =
(bitmap_get(buffer, 8, j + 0) | bitmap_get(buffer, 8, j + 1) << 1 |
bitmap_get(buffer, 8, j + 2) << 2 | bitmap_get(buffer, 8, j + 3) << 3);
if(DEBUG_MSG) FURI_LOG_E(TAG, "Not inverted nibble[%d]: %x", j / 4, (unsigned int)nib[0]);
raw[j / 8] |= nib[0] << (4 - (j % 4));
switch(j / 4) {
case 1: deviceid[0] |= nib[0]; break;
case 0: deviceid[0] |= nib[0] << 4; break;
case 3: deviceid[1] |= nib[0]; break;
case 2: deviceid[1] |= nib[0] << 4; break;
case 10: temp[0] = nib[0]; break;
case 1:
deviceid[0] |= nib[0];
break;
case 0:
deviceid[0] |= nib[0] << 4;
break;
case 3:
deviceid[1] |= nib[0];
break;
case 2:
deviceid[1] |= nib[0] << 4;
break;
case 10:
temp[0] = nib[0];
break;
/* Fixme: take the temperature sign from nibble 11. */
case 9: temp[1] = nib[0]; break;
case 8: temp[2] = nib[0]; break;
case 13: hum[0] = nib[0]; break;
case 12: hum[1] = nib[0]; break;
case 9:
temp[1] = nib[0];
break;
case 8:
temp[2] = nib[0];
break;
case 13:
hum[0] = nib[0];
break;
case 12:
hum[1] = nib[0];
break;
}
}
snprintf(info->name, sizeof(info->name), "%s", "Oregon v2.1");
/* The following line crashes the Flipper because of broken
* snprintf() implementation. */
snprintf(info->raw,sizeof(info->raw),"%02X%02X%02X%02X%02X%02X%02X%02X",
raw[0],raw[1],raw[2],raw[3],raw[4],raw[5],
raw[6],raw[7]);
snprintf(info->info1,sizeof(info->info1),"Sensor ID %02X%02X",
deviceid[0], deviceid[1]);
snprintf(info->info2,sizeof(info->info2),"Temperature %d%d.%d",
temp[0],temp[1],temp[2]);
snprintf(info->info3,sizeof(info->info3),"Humidity %d%d",
hum[0],hum[1]);
snprintf(
info->raw,
sizeof(info->raw),
"%02X%02X%02X%02X%02X%02X%02X%02X",
raw[0],
raw[1],
raw[2],
raw[3],
raw[4],
raw[5],
raw[6],
raw[7]);
snprintf(info->info1, sizeof(info->info1), "Sensor ID %02X%02X", deviceid[0], deviceid[1]);
snprintf(info->info2, sizeof(info->info2), "Temperature %d%d.%d", temp[0], temp[1], temp[2]);
snprintf(info->info3, sizeof(info->info3), "Humidity %d%d", hum[0], hum[1]);
return true;
}
ProtoViewDecoder Oregon2Decoder = {
"Oregon2", decode
};
ProtoViewDecoder Oregon2Decoder = {"Oregon2", decode};

View file

@ -8,7 +8,6 @@
#include "../../app.h"
static bool decode(uint8_t* bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo* info) {
/* We consider a preamble of 17 symbols. They are more, but the decoding
* is more likely to happen if we don't pretend to receive from the
* very start of the message. */
@ -24,9 +23,8 @@ static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoView
off += sync_len; /* Skip preamble + sync. */
uint8_t raw[10];
uint32_t decoded =
convert_from_line_code(raw,sizeof(raw),bits,numbytes,off,
"01","10"); /* Manchester. */
uint32_t decoded = convert_from_line_code(
raw, sizeof(raw), bits, numbytes, off, "01", "10"); /* Manchester. */
FURI_LOG_E(TAG, "Citroen TPMS decoded bits: %lu", decoded);
if(decoded < 8 * 10) return false; /* Require the full 10 bytes. */
@ -46,18 +44,32 @@ static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoView
int battery = raw[8]; /* This may be the battery. It's not clear. */
snprintf(info->name, sizeof(info->name), "%s", "Citroen TPMS");
snprintf(info->raw,sizeof(info->raw),
snprintf(
info->raw,
sizeof(info->raw),
"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
raw[0],raw[1],raw[2],raw[3],raw[4],raw[5],
raw[6],raw[7],raw[8],raw[9]);
snprintf(info->info1,sizeof(info->info1),"Tire ID %02X%02X%02X%02X",
raw[1],raw[2],raw[3],raw[4]);
raw[0],
raw[1],
raw[2],
raw[3],
raw[4],
raw[5],
raw[6],
raw[7],
raw[8],
raw[9]);
snprintf(
info->info1,
sizeof(info->info1),
"Tire ID %02X%02X%02X%02X",
raw[1],
raw[2],
raw[3],
raw[4]);
snprintf(info->info2, sizeof(info->info2), "Pressure %.2f kpa", (double)kpa);
snprintf(info->info3, sizeof(info->info3), "Temperature %d C", temp);
snprintf(info->info4, sizeof(info->info4), "Repeat %d, Bat %d", repeat, battery);
return true;
}
ProtoViewDecoder CitroenTPMSDecoder = {
"Citroen TPMS", decode
};
ProtoViewDecoder CitroenTPMSDecoder = {"Citroen TPMS", decode};

View file

@ -11,8 +11,8 @@
#include "../../app.h"
static bool decode(uint8_t* bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo* info) {
const char *sync_pattern = "010101010101" "0110";
const char* sync_pattern = "010101010101"
"0110";
uint8_t sync_len = 12 + 4; /* We just use 12 preamble symbols + sync. */
if(numbits - sync_len < 8 * 8) return false;
@ -24,9 +24,8 @@ static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoView
off += sync_len; /* Skip preamble and sync. */
uint8_t raw[8];
uint32_t decoded =
convert_from_line_code(raw,sizeof(raw),bits,numbytes,off,
"01","10"); /* Manchester. */
uint32_t decoded = convert_from_line_code(
raw, sizeof(raw), bits, numbytes, off, "01", "10"); /* Manchester. */
FURI_LOG_E(TAG, "Ford TPMS decoded bits: %lu", decoded);
if(decoded < 8 * 8) return false; /* Require the full 8 bytes. */
@ -48,11 +47,26 @@ static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoView
int car_moving = (raw[6] & 0x44) == 0x44;
snprintf(info->name, sizeof(info->name), "%s", "Ford TPMS");
snprintf(info->raw,sizeof(info->raw),"%02X%02X%02X%02X%02X%02X%02X%02X",
raw[0],raw[1],raw[2],raw[3],raw[4],raw[5],
raw[6],raw[7]);
snprintf(info->info1,sizeof(info->info1),"Tire ID %02X%02X%02X%02X",
raw[0],raw[1],raw[2],raw[3]);
snprintf(
info->raw,
sizeof(info->raw),
"%02X%02X%02X%02X%02X%02X%02X%02X",
raw[0],
raw[1],
raw[2],
raw[3],
raw[4],
raw[5],
raw[6],
raw[7]);
snprintf(
info->info1,
sizeof(info->info1),
"Tire ID %02X%02X%02X%02X",
raw[0],
raw[1],
raw[2],
raw[3]);
snprintf(info->info2, sizeof(info->info2), "Pressure %.2f psi", (double)psi);
if(temp)
snprintf(info->info3, sizeof(info->info3), "Temperature %d C", temp);
@ -62,6 +76,4 @@ static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoView
return true;
}
ProtoViewDecoder FordTPMSDecoder = {
"Ford TPMS", decode
};
ProtoViewDecoder FordTPMSDecoder = {"Ford TPMS", decode};

View file

@ -23,7 +23,6 @@ static const char *test_vector =
"0110010101010101"; // CRC8 with (poly 7, initialization 0).
static bool decode(uint8_t* bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo* info) {
if(USE_TEST_VECTOR) { /* Test vector to check that decoding works. */
bitmap_set_pattern(bits, numbytes, 0, test_vector);
numbits = strlen(test_vector);
@ -40,9 +39,8 @@ static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoView
off += 20; /* Skip preamble. */
uint8_t raw[9];
uint32_t decoded =
convert_from_line_code(raw,sizeof(raw),bits,numbytes,off,
"01","10"); /* Manchester. */
uint32_t decoded = convert_from_line_code(
raw, sizeof(raw), bits, numbytes, off, "01", "10"); /* Manchester. */
FURI_LOG_E(TAG, "Renault TPMS decoded bits: %lu", decoded);
if(decoded < 8 * 9) return false; /* Require the full 9 bytes. */
@ -54,16 +52,23 @@ static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoView
int temp = raw[2] - 30;
snprintf(info->name, sizeof(info->name), "%s", "Renault TPMS");
snprintf(info->raw,sizeof(info->raw),"%02X%02X%02X%02X%02X%02X%02X%02X%02X",
raw[0],raw[1],raw[2],raw[3],raw[4],raw[5],
raw[6],raw[7],raw[8]);
snprintf(info->info1,sizeof(info->info1),"Tire ID %02X%02X%02X",
raw[3],raw[4],raw[5]);
snprintf(
info->raw,
sizeof(info->raw),
"%02X%02X%02X%02X%02X%02X%02X%02X%02X",
raw[0],
raw[1],
raw[2],
raw[3],
raw[4],
raw[5],
raw[6],
raw[7],
raw[8]);
snprintf(info->info1, sizeof(info->info1), "Tire ID %02X%02X%02X", raw[3], raw[4], raw[5]);
snprintf(info->info2, sizeof(info->info2), "Pressure %.2f kpa", (double)kpa);
snprintf(info->info3, sizeof(info->info3), "Temperature %d C", temp);
return true;
}
ProtoViewDecoder RenaultTPMSDecoder = {
"Renault TPMS", decode
};
ProtoViewDecoder RenaultTPMSDecoder = {"Renault TPMS", decode};

View file

@ -11,10 +11,10 @@
#include "../../app.h"
#define USE_TEST_VECTOR 0
static const char *test_vector = "000000111101010101011010010110010110101001010110100110011001100101010101011010100110100110011010101010101010101010101010101010101010101010101010";
static const char* test_vector =
"000000111101010101011010010110010110101001010110100110011001100101010101011010100110100110011010101010101010101010101010101010101010101010101010";
static bool decode(uint8_t* bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo* info) {
if(USE_TEST_VECTOR) { /* Test vector to check that decoding works. */
bitmap_set_pattern(bits, numbytes, 0, test_vector);
numbits = strlen(test_vector);
@ -22,7 +22,8 @@ static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoView
if(numbits < 64) return false; /* Preamble + data. */
const char *sync_pattern = "1111010101" "01011010";
const char* sync_pattern = "1111010101"
"01011010";
uint64_t off = bitmap_seek_bits(bits, numbytes, 0, numbits, sync_pattern);
if(off == BITMAP_SEEK_NOT_FOUND) return false;
FURI_LOG_E(TAG, "Schrader TPMS gap+preamble found");
@ -33,9 +34,8 @@ static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoView
0011 = 0x3. */
uint8_t raw[8];
uint32_t decoded =
convert_from_line_code(raw,sizeof(raw),bits,numbytes,off,
"01","10"); /* Manchester code. */
uint32_t decoded = convert_from_line_code(
raw, sizeof(raw), bits, numbytes, off, "01", "10"); /* Manchester code. */
FURI_LOG_E(TAG, "Schrader TPMS decoded bits: %lu", decoded);
if(decoded < 64) return false; /* Require the full 8 bytes. */
@ -53,16 +53,29 @@ static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoView
int temp = raw[6] - 50;
snprintf(info->name, sizeof(info->name), "%s", "Schrader TPMS");
snprintf(info->raw,sizeof(info->raw),"%02X%02X%02X%02X%02X%02X%02X%02X",
raw[0],raw[1],raw[2],raw[3],raw[4],raw[5],
raw[6],raw[7]);
snprintf(info->info1,sizeof(info->info1),"Tire ID %01X%02X%02X%02X",
raw[1]&7,raw[2],raw[3],raw[4]); /* Only 28 bits of ID, not 32. */
snprintf(
info->raw,
sizeof(info->raw),
"%02X%02X%02X%02X%02X%02X%02X%02X",
raw[0],
raw[1],
raw[2],
raw[3],
raw[4],
raw[5],
raw[6],
raw[7]);
snprintf(
info->info1,
sizeof(info->info1),
"Tire ID %01X%02X%02X%02X",
raw[1] & 7,
raw[2],
raw[3],
raw[4]); /* Only 28 bits of ID, not 32. */
snprintf(info->info2, sizeof(info->info2), "Pressure %.2f kpa", (double)kpa);
snprintf(info->info3, sizeof(info->info3), "Temperature %d C", temp);
return true;
}
ProtoViewDecoder SchraderTPMSDecoder = {
"Schrader TPMS", decode
};
ProtoViewDecoder SchraderTPMSDecoder = {"Schrader TPMS", decode};

View file

@ -16,8 +16,8 @@
#include "../../app.h"
static bool decode(uint8_t* bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo* info) {
const char *sync_pattern = "010101010101" "01100101";
const char* sync_pattern = "010101010101"
"01100101";
uint8_t sync_len = 12 + 8; /* We just use 12 preamble symbols + sync. */
if(numbits - sync_len + 8 < 8 * 10) return false;
@ -29,9 +29,8 @@ static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoView
off += sync_len - 8; /* Skip preamble, not sync that is part of the data. */
uint8_t raw[10];
uint32_t decoded =
convert_from_line_code(raw,sizeof(raw),bits,numbytes,off,
"01","10"); /* Manchester code. */
uint32_t decoded = convert_from_line_code(
raw, sizeof(raw), bits, numbytes, off, "01", "10"); /* Manchester code. */
FURI_LOG_E(TAG, "Schrader EG53MA4 TPMS decoded bits: %lu", decoded);
if(decoded < 10 * 8) return false; /* Require the full 10 bytes. */
@ -51,16 +50,30 @@ static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoView
int temp_c = (temp_f - 32) * 5 / 9; /* Convert Fahrenheit to Celsius. */
snprintf(info->name, sizeof(info->name), "%s", "Schrader EG53MA4 TPMS");
snprintf(info->raw,sizeof(info->raw),"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
raw[0],raw[1],raw[2],raw[3],raw[4],raw[5],
raw[6],raw[7],raw[8],raw[9]);
snprintf(info->info1,sizeof(info->info1),"Tire ID %02X%02X%02X",
raw[4],raw[5],raw[6]); /* Only 28 bits of ID, not 32. */
snprintf(
info->raw,
sizeof(info->raw),
"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
raw[0],
raw[1],
raw[2],
raw[3],
raw[4],
raw[5],
raw[6],
raw[7],
raw[8],
raw[9]);
snprintf(
info->info1,
sizeof(info->info1),
"Tire ID %02X%02X%02X",
raw[4],
raw[5],
raw[6]); /* Only 28 bits of ID, not 32. */
snprintf(info->info2, sizeof(info->info2), "Pressure %.2f kpa", (double)kpa);
snprintf(info->info3, sizeof(info->info3), "Temperature %d C", temp_c);
return true;
}
ProtoViewDecoder SchraderEG53MA4TPMSDecoder = {
"Schrader EG53MA4 TPMS", decode
};
ProtoViewDecoder SchraderEG53MA4TPMSDecoder = {"Schrader EG53MA4 TPMS", decode};

View file

@ -25,17 +25,11 @@
#include "../../app.h"
static bool decode(uint8_t* bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo* info) {
if (numbits-6 < 64*2) return false; /* Ask for 64 bit of data (each bit
if(numbits - 6 < 64 * 2)
return false; /* Ask for 64 bit of data (each bit
is two symbols in the bitmap). */
char *sync[] = {
"00111100",
"001111100",
"00111101",
"001111101",
NULL
};
char* sync[] = {"00111100", "001111100", "00111101", "001111101", NULL};
int j;
uint32_t off = 0;
@ -52,8 +46,7 @@ static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoView
FURI_LOG_E(TAG, "Toyota TPMS sync[%s] found", sync[j]);
uint8_t raw[9];
uint32_t decoded =
convert_from_diff_manchester(raw,sizeof(raw),bits,numbytes,off,true);
uint32_t decoded = convert_from_diff_manchester(raw, sizeof(raw), bits, numbytes, off, true);
FURI_LOG_E(TAG, "Toyota TPMS decoded bits: %lu", decoded);
if(decoded < 8 * 9) return false; /* Require the full 8 bytes. */
@ -76,16 +69,30 @@ static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoView
int temp = ((raw[5] & 0x7f) << 1 | raw[6] >> 7) - 40;
snprintf(info->name, sizeof(info->name), "%s", "Toyota TPMS");
snprintf(info->raw,sizeof(info->raw),"%02X%02X%02X%02X%02X%02X%02X%02X%02X",
raw[0],raw[1],raw[2],raw[3],raw[4],raw[5],
raw[6],raw[7],raw[8]);
snprintf(info->info1,sizeof(info->info1),"Tire ID %02X%02X%02X%02X",
raw[0],raw[1],raw[2],raw[3]);
snprintf(
info->raw,
sizeof(info->raw),
"%02X%02X%02X%02X%02X%02X%02X%02X%02X",
raw[0],
raw[1],
raw[2],
raw[3],
raw[4],
raw[5],
raw[6],
raw[7],
raw[8]);
snprintf(
info->info1,
sizeof(info->info1),
"Tire ID %02X%02X%02X%02X",
raw[0],
raw[1],
raw[2],
raw[3]);
snprintf(info->info2, sizeof(info->info2), "Pressure %.2f psi", (double)kpa);
snprintf(info->info3, sizeof(info->info3), "Temperature %d C", temp);
return true;
}
ProtoViewDecoder ToyotaTPMSDecoder = {
"Toyota TPMS", decode
};
ProtoViewDecoder ToyotaTPMSDecoder = {"Toyota TPMS", decode};

View file

@ -102,9 +102,7 @@ uint32_t search_coherent_signal(RawSamplesBuffer *s, uint32_t idx) {
for(int level = 0; level < 2; level++) {
if(classes[j].dur[level] == 0) continue;
if(classes[j].count[level] < 3) continue;
if (short_dur[level] == 0 ||
short_dur[level] > classes[j].dur[level])
{
if(short_dur[level] == 0 || short_dur[level] > classes[j].dur[level]) {
short_dur[level] = classes[j].dur[level];
}
}
@ -161,17 +159,19 @@ void scan_for_signal(ProtoViewApp *app) {
/* Accept this signal as the new signal if either it's longer
* than the previous undecoded one, or the previous one was
* unknown and this is decoded. */
if ((thislen > app->signal_bestlen && app->signal_decoded == false)
|| (app->signal_decoded == false && decoded))
{
if((thislen > app->signal_bestlen && app->signal_decoded == false) ||
(app->signal_decoded == false && decoded)) {
free_msg_info(app->msg_info);
app->msg_info = info;
app->signal_bestlen = thislen;
app->signal_decoded = decoded;
raw_samples_copy(DetectedSamples, copy);
raw_samples_center(DetectedSamples, i);
FURI_LOG_E(TAG, "===> Displayed sample updated (%d samples %lu us)",
(int)thislen, DetectedSamples->short_pulse_dur);
FURI_LOG_E(
TAG,
"===> Displayed sample updated (%d samples %lu us)",
(int)thislen,
DetectedSamples->short_pulse_dur);
/* Adjust raw view scale if the signal has an high
* data rate. */
@ -229,10 +229,14 @@ bool bitmap_get(uint8_t *b, uint32_t blen, uint32_t bitpos) {
* bitmap 'd' of 'dlen' total bytes. The bits are copied starting from
* offset 'soff' of the source bitmap to the offset 'doff' of the
* destination bitmap. */
void bitmap_copy(uint8_t *d, uint32_t dlen, uint32_t doff,
uint8_t *s, uint32_t slen, uint32_t soff,
uint32_t count)
{
void bitmap_copy(
uint8_t* d,
uint32_t dlen,
uint32_t doff,
uint8_t* s,
uint32_t slen,
uint32_t soff,
uint32_t count) {
/* If we are byte-aligned in both source and destination, use a fast
* path for the number of bytes we can consume this way. */
if((doff & 7) == 0 && (soff & 7) == 0) {
@ -300,8 +304,7 @@ void bitmap_copy(uint8_t *d, uint32_t dlen, uint32_t doff,
uint32_t didx = doff / 8;
uint32_t sidx = soff / 8;
while(count > 8 && didx < dlen && sidx < slen) {
d[didx] = ((s[sidx] << skew) |
(s[sidx+1] >> (8-skew)));
d[didx] = ((s[sidx] << skew) | (s[sidx + 1] >> (8 - skew)));
sidx++;
didx++;
soff += 8;
@ -354,7 +357,12 @@ bool bitmap_match_bits(uint8_t *b, uint32_t blen, uint32_t bitpos, const char *b
* Note: there are better algorithms, such as Boyer-Moore. Here we hope that
* for the kind of patterns we search we'll have a lot of early stops so
* we use a vanilla approach. */
uint32_t bitmap_seek_bits(uint8_t *b, uint32_t blen, uint32_t startpos, uint32_t maxbits, const char *bits) {
uint32_t bitmap_seek_bits(
uint8_t* b,
uint32_t blen,
uint32_t startpos,
uint32_t maxbits,
const char* bits) {
uint32_t endpos = startpos + blen * 8;
uint32_t end2 = startpos + maxbits;
if(end2 < endpos) endpos = end2;
@ -405,7 +413,13 @@ void bitmap_set_pattern(uint8_t *b, uint32_t blen, uint32_t off, const char *pat
* bits set into the buffer 'b'. The 'rate' argument, in microseconds, is
* the detected short-pulse duration. We expect the line code to be
* meaningful when interpreted at multiples of 'rate'. */
uint32_t convert_signal_to_bits(uint8_t *b, uint32_t blen, RawSamplesBuffer *s, uint32_t idx, uint32_t count, uint32_t rate) {
uint32_t convert_signal_to_bits(
uint8_t* b,
uint32_t blen,
RawSamplesBuffer* s,
uint32_t idx,
uint32_t count,
uint32_t rate) {
if(rate == 0) return 0; /* We can't perform the conversion. */
uint32_t bitpos = 0;
for(uint32_t j = 0; j < count; j++) {
@ -422,8 +436,7 @@ uint32_t convert_signal_to_bits(uint8_t *b, uint32_t blen, RawSamplesBuffer *s,
if(numbits > 1024) numbits = 1024;
if(0) /* Super verbose, so not under the DEBUG_MSG define. */
FURI_LOG_E(TAG, "%lu converted into %lu (%d) bits",
dur,numbits,(int)level);
FURI_LOG_E(TAG, "%lu converted into %lu (%d) bits", dur, numbits, (int)level);
/* If the signal is too short, let's claim it an interference
* and ignore it completely. */
@ -446,8 +459,14 @@ uint32_t convert_signal_to_bits(uint8_t *b, uint32_t blen, RawSamplesBuffer *s,
* specified in bytes by the caller, via the 'len' parameters).
*
* The decoding starts at the specified offset (in bits) 'off'. */
uint32_t convert_from_line_code(uint8_t *buf, uint64_t buflen, uint8_t *bits, uint32_t len, uint32_t off, const char *zero_pattern, const char *one_pattern)
{
uint32_t convert_from_line_code(
uint8_t* buf,
uint64_t buflen,
uint8_t* bits,
uint32_t len,
uint32_t off,
const char* zero_pattern,
const char* one_pattern) {
uint32_t decoded = 0; /* Number of bits extracted. */
len *= 8; /* Convert bytes to bits. */
while(off < len) {
@ -473,8 +492,13 @@ uint32_t convert_from_line_code(uint8_t *buf, uint64_t buflen, uint8_t *bits, ui
* in differential codings the next bits depend on the previous one.
*
* Parameters and return values are like convert_from_line_code(). */
uint32_t convert_from_diff_manchester(uint8_t *buf, uint64_t buflen, uint8_t *bits, uint32_t len, uint32_t off, bool previous)
{
uint32_t convert_from_diff_manchester(
uint8_t* buf,
uint64_t buflen,
uint8_t* bits,
uint32_t len,
uint32_t off,
bool previous) {
uint32_t decoded = 0;
len *= 8; /* Conver to bits. */
for(uint32_t j = off; j < len; j += 2) {
@ -511,8 +535,7 @@ ProtoViewDecoder *Decoders[] = {
&CitroenTPMSDecoder, /* Citroen TPMS. */
&FordTPMSDecoder, /* Ford TPMS. */
&KeeloqDecoder, /* Keeloq remote. */
NULL
};
NULL};
/* Free the message info and allocated data. */
void free_msg_info(ProtoViewMsgInfo* i) {
@ -543,7 +566,13 @@ bool decode_signal(RawSamplesBuffer *s, uint64_t len, ProtoViewMsgInfo *info) {
uint32_t after_samples = 100;
uint8_t* bitmap = malloc(bitmap_size);
uint32_t bits = convert_signal_to_bits(bitmap,bitmap_size,s,-before_samples,len+before_samples+after_samples,s->short_pulse_dur);
uint32_t bits = convert_signal_to_bits(
bitmap,
bitmap_size,
s,
-before_samples,
len + before_samples + after_samples,
s->short_pulse_dur);
if(DEBUG_MSG) { /* Useful for debugging purposes. Don't remove. */
char* str = malloc(1024);
@ -564,8 +593,7 @@ bool decode_signal(RawSamplesBuffer *s, uint64_t len, ProtoViewMsgInfo *info) {
uint32_t start_time = furi_get_tick();
decoded = Decoders[j]->decode(bitmap, bitmap_size, bits, info);
uint32_t delta = furi_get_tick() - start_time;
FURI_LOG_E(TAG, "Decoder %s took %lu ms",
Decoders[j]->name, (unsigned long)delta);
FURI_LOG_E(TAG, "Decoder %s took %lu ms", Decoders[j]->name, (unsigned long)delta);
if(decoded) break;
j++;
}
@ -573,9 +601,15 @@ bool decode_signal(RawSamplesBuffer *s, uint64_t len, ProtoViewMsgInfo *info) {
if(!decoded) {
FURI_LOG_E(TAG, "No decoding possible");
} else {
FURI_LOG_E(TAG, "Decoded %s, raw=%s info=[%s,%s,%s,%s]",
info->name, info->raw, info->info1, info->info2,
info->info3, info->info4);
FURI_LOG_E(
TAG,
"Decoded %s, raw=%s info=[%s,%s,%s,%s]",
info->name,
info->raw,
info->info1,
info->info2,
info->info3,
info->info4);
/* The message was correctly decoded: fill the info structure
* with the decoded signal. The decoder may not implement offset/len
* filling of the structure. In such case we have no info and
@ -583,8 +617,13 @@ bool decode_signal(RawSamplesBuffer *s, uint64_t len, ProtoViewMsgInfo *info) {
if(info->pulses_count) {
info->bits_bytes = (info->pulses_count + 7) / 8; // Round to full byte.
info->bits = malloc(info->bits_bytes);
bitmap_copy(info->bits,info->bits_bytes,0,
bitmap,bitmap_size,info->start_off,
bitmap_copy(
info->bits,
info->bits_bytes,
0,
bitmap,
bitmap_size,
info->start_off,
info->pulses_count);
}
}

View file

@ -28,7 +28,8 @@ bool save_signal(ProtoViewApp *app, const char *filename) {
FuriString* file_content = furi_string_alloc();
const char* preset_id = ProtoViewModulations[app->modulation].id;
furi_string_printf(file_content,
furi_string_printf(
file_content,
"Filetype: Flipper SubGhz RAW File\n"
"Version: 1\n"
"Frequency: %ld\n"
@ -40,12 +41,12 @@ bool save_signal(ProtoViewApp *app, const char *filename) {
if(preset_id == NULL) {
FuriString* custom = furi_string_alloc();
uint8_t* regs = ProtoViewModulations[app->modulation].custom;
furi_string_printf(custom,
furi_string_printf(
custom,
"Custom_preset_module: CC1101\n"
"Custom_preset_data: ");
for(int j = 0; regs[j]; j += 2) {
furi_string_cat_printf(custom, "%02X %02X ",
(int)regs[j], (int)regs[j+1]);
furi_string_cat_printf(custom, "%02X %02X ", (int)regs[j], (int)regs[j + 1]);
}
size_t len = furi_string_size(file_content);
furi_string_set_char(custom, len - 1, '\n');
@ -54,16 +55,14 @@ bool save_signal(ProtoViewApp *app, const char *filename) {
}
/* We always save raw files. */
furi_string_cat_printf(file_content,
furi_string_cat_printf(
file_content,
"Protocol: RAW\n"
"RAW_Data: -10000\n"); // Start with 10 ms of gap
/* Write header. */
size_t len = furi_string_size(file_content);
if (stream_write(stream,
(uint8_t*) furi_string_get_cstr(file_content), len)
!= len)
{
if(stream_write(stream, (uint8_t*)furi_string_get_cstr(file_content), len) != len) {
FURI_LOG_W(TAG, "Short write to file");
success = false;
goto write_err;
@ -82,9 +81,7 @@ bool save_signal(ProtoViewApp *app, const char *filename) {
uint32_t te_times = 1;
idx++;
/* Count the duration of the current pulse/gap. */
while(idx < i->pulses_count &&
bitmap_get(i->bits,i->bits_bytes,idx) == level)
{
while(idx < i->pulses_count && bitmap_get(i->bits, i->bits_bytes, idx) == level) {
te_times++;
idx++;
}
@ -96,8 +93,7 @@ bool save_signal(ProtoViewApp *app, const char *filename) {
/* Emit the sample. If this is the first sample of the line,
* also emit the RAW_Data: field. */
if (this_line_samples == 0)
furi_string_cat_printf(file_content,"RAW_Data: ");
if(this_line_samples == 0) furi_string_cat_printf(file_content, "RAW_Data: ");
furi_string_cat_printf(file_content, "%d ", (int)dur);
this_line_samples++;
@ -114,10 +110,8 @@ bool save_signal(ProtoViewApp *app, const char *filename) {
size_t len = furi_string_size(file_content);
furi_string_set_char(file_content, len - 1, '\n');
if (stream_write(stream,
(uint8_t*) furi_string_get_cstr(file_content),
len) != len)
{
if(stream_write(stream, (uint8_t*)furi_string_get_cstr(file_content), len) !=
len) {
FURI_LOG_W(TAG, "Short write to file");
success = false;
goto write_err;

View file

@ -17,12 +17,9 @@ int get_current_subview(ProtoViewApp *app) {
/* Called by view rendering callback that has subviews, to show small triangles
* facing down/up if there are other subviews the user can access with up
* and down. */
void show_available_subviews(Canvas *canvas, ProtoViewApp *app,
int last_subview)
{
void show_available_subviews(Canvas* canvas, ProtoViewApp* app, int last_subview) {
int subview = get_current_subview(app);
if (subview != 0)
canvas_draw_triangle(canvas,120,5,8,5,CanvasDirectionBottomToTop);
if(subview != 0) canvas_draw_triangle(canvas, 120, 5, 8, 5, CanvasDirectionBottomToTop);
if(subview != last_subview - 1)
canvas_draw_triangle(canvas, 120, 59, 8, 5, CanvasDirectionTopToBottom);
}
@ -34,12 +31,10 @@ bool process_subview_updown(ProtoViewApp *app, InputEvent input, int last_subvie
int subview = get_current_subview(app);
if(input.type == InputTypePress) {
if(input.key == InputKeyUp) {
if (subview != 0)
app->current_subview[app->current_view]--;
if(subview != 0) app->current_subview[app->current_view]--;
return true;
} else if(input.key == InputKeyDown) {
if (subview != last_subview-1)
app->current_subview[app->current_view]++;
if(subview != last_subview - 1) app->current_subview[app->current_view]++;
return true;
}
}
@ -62,9 +57,7 @@ bool process_subview_updown(ProtoViewApp *app, InputEvent input, int last_subvie
*
* Note: if the buffer is not a null-termined zero string, what it contains will
* be used as initial input for the user. */
void show_keyboard(ProtoViewApp *app, char *buffer, uint32_t buflen,
void (*done_callback)(void*))
{
void show_keyboard(ProtoViewApp* app, char* buffer, uint32_t buflen, void (*done_callback)(void*)) {
app->show_text_input = true;
app->text_input_buffer = buffer;
app->text_input_buffer_len = buflen;
@ -77,27 +70,23 @@ void dismiss_keyboard(ProtoViewApp *app) {
/* =========================== Canvas extensions ============================ */
void canvas_draw_str_with_border(Canvas* canvas, uint8_t x, uint8_t y, const char* str, Color text_color, Color border_color)
{
void canvas_draw_str_with_border(
Canvas* canvas,
uint8_t x,
uint8_t y,
const char* str,
Color text_color,
Color border_color) {
struct {
uint8_t x; uint8_t y;
} dir[8] = {
{-1,-1},
{0,-1},
{1,-1},
{1,0},
{1,1},
{0,1},
{-1,1},
{-1,0}
};
uint8_t x;
uint8_t y;
} dir[8] = {{-1, -1}, {0, -1}, {1, -1}, {1, 0}, {1, 1}, {0, 1}, {-1, 1}, {-1, 0}};
/* Rotate in all the directions writing the same string to create a
* border, then write the actual string in the other color in the
* middle. */
canvas_set_color(canvas, border_color);
for (int j = 0; j < 8; j++)
canvas_draw_str(canvas,x+dir[j].x,y+dir[j].y,str);
for(int j = 0; j < 8; j++) canvas_draw_str(canvas, x + dir[j].x, y + dir[j].y, str);
canvas_set_color(canvas, text_color);
canvas_draw_str(canvas, x, y, str);
canvas_set_color(canvas, ColorBlack);

View file

@ -27,12 +27,13 @@ void render_view_direct_sampling(Canvas *const canvas, ProtoViewApp *app) {
/* Busy loop: this is a terrible approach as it blocks
* everything else, but for now it's the best we can do
* to obtain direct data with some spacing. */
uint32_t x = 250; while(x--);
uint32_t x = 250;
while(x--)
;
}
}
canvas_set_font(canvas, FontSecondary);
canvas_draw_str_with_border(canvas,36,60,"Direct sampling",
ColorWhite,ColorBlack);
canvas_draw_str_with_border(canvas, 36, 60, "Direct sampling", ColorWhite, ColorBlack);
}
/* Handle input */
@ -45,9 +46,7 @@ void process_input_direct_sampling(ProtoViewApp *app, InputEvent input) {
/* Enter view. Stop the subghz thread to prevent access as we read
* the CC1101 data directly. */
void view_enter_direct_sampling(ProtoViewApp* app) {
if (app->txrx->txrx_state == TxRxStateRx &&
!app->txrx->debug_timer_sampling)
{
if(app->txrx->txrx_state == TxRxStateRx && !app->txrx->debug_timer_sampling) {
subghz_worker_stop(app->txrx->worker);
} else {
raw_sampling_worker_stop(app);
@ -56,9 +55,7 @@ void view_enter_direct_sampling(ProtoViewApp *app) {
/* Exit view. Restore the subghz thread. */
void view_exit_direct_sampling(ProtoViewApp* app) {
if (app->txrx->txrx_state == TxRxStateRx &&
!app->txrx->debug_timer_sampling)
{
if(app->txrx->txrx_state == TxRxStateRx && !app->txrx->debug_timer_sampling) {
subghz_worker_start(app->txrx->worker);
} else {
raw_sampling_worker_start(app);

View file

@ -37,17 +37,25 @@ static void render_subview_main(Canvas *const canvas, ProtoViewApp *app) {
canvas_draw_str(canvas, 0, y, buf);
y += lineheight;
}
canvas_draw_str(canvas, 0, y, app->msg_info->info1); y += lineheight;
canvas_draw_str(canvas, 0, y, app->msg_info->info2); y += lineheight;
canvas_draw_str(canvas, 0, y, app->msg_info->info3); y += lineheight;
canvas_draw_str(canvas, 0, y, app->msg_info->info4); y += lineheight;
canvas_draw_str(canvas, 0, y, app->msg_info->info1);
y += lineheight;
canvas_draw_str(canvas, 0, y, app->msg_info->info2);
y += lineheight;
canvas_draw_str(canvas, 0, y, app->msg_info->info3);
y += lineheight;
canvas_draw_str(canvas, 0, y, app->msg_info->info4);
y += lineheight;
y = 37;
lineheight = 7;
canvas_draw_str(canvas, 119, y, "s"); y += lineheight;
canvas_draw_str(canvas, 119, y, "a"); y += lineheight;
canvas_draw_str(canvas, 119, y, "v"); y += lineheight;
canvas_draw_str(canvas, 119, y, "e"); y += lineheight;
canvas_draw_str(canvas, 119, y, "s");
y += lineheight;
canvas_draw_str(canvas, 119, y, "a");
y += lineheight;
canvas_draw_str(canvas, 119, y, "v");
y += lineheight;
canvas_draw_str(canvas, 119, y, "e");
y += lineheight;
}
/* Render view with save option. */
@ -65,8 +73,7 @@ static void render_subview_save(Canvas *const canvas, ProtoViewApp *app) {
bool prevbit = false;
for(uint8_t y = bitheight + 12; y <= rows * rowheight; y += rowheight) {
for(uint8_t x = 0; x < 128; x += 4) {
bool bit = bitmap_get(app->msg_info->bits,
app->msg_info->bits_bytes,idx);
bool bit = bitmap_get(app->msg_info->bits, app->msg_info->bits_bytes, idx);
uint8_t prevy = y + prevbit * (bitheight * -1) - 1;
uint8_t thisy = y + bit * (bitheight * -1) - 1;
canvas_draw_line(canvas, x, prevy, x, thisy);
@ -96,8 +103,12 @@ void render_view_info(Canvas *const canvas, ProtoViewApp *app) {
show_available_subviews(canvas, app, SubViewInfoLast);
switch(app->current_subview[app->current_view]) {
case SubViewInfoMain: render_subview_main(canvas,app); break;
case SubViewInfoSave: render_subview_save(canvas,app); break;
case SubViewInfoMain:
render_subview_main(canvas, app);
break;
case SubViewInfoSave:
render_subview_save(canvas, app);
break;
}
}
@ -107,8 +118,8 @@ void text_input_done_callback(void* context) {
ProtoViewApp* app = context;
InfoViewPrivData* privdata = app->view_privdata;
FuriString *save_path = furi_string_alloc_printf(
"%s/%s.sub", EXT_PATH("subghz"), privdata->filename);
FuriString* save_path =
furi_string_alloc_printf("%s/%s.sub", EXT_PATH("subghz"), privdata->filename);
save_signal(app, furi_string_get_cstr(save_path));
furi_string_free(save_path);
@ -153,13 +164,11 @@ void process_input_info(ProtoViewApp *app, InputEvent input) {
if(input.type == InputTypePress && input.key == InputKeyRight) {
privdata->signal_display_start_row++;
} else if(input.type == InputTypePress && input.key == InputKeyLeft) {
if (privdata->signal_display_start_row != 0)
privdata->signal_display_start_row--;
if(privdata->signal_display_start_row != 0) privdata->signal_display_start_row--;
} else if(input.type == InputTypePress && input.key == InputKeyOk) {
privdata->filename = malloc(SAVE_FILENAME_LEN);
set_signal_random_filename(app, privdata->filename, SAVE_FILENAME_LEN);
show_keyboard(app, privdata->filename, SAVE_FILENAME_LEN,
text_input_done_callback);
show_keyboard(app, privdata->filename, SAVE_FILENAME_LEN, text_input_done_callback);
}
}
}

View file

@ -32,9 +32,7 @@ void render_signal(ProtoViewApp *app, Canvas *const canvas, RawSamplesBuffer *bu
canvas_draw_line(canvas, x, y, x, y - (level * 3));
/* Write a small triangle under the last sample detected. */
if (app->signal_bestlen != 0 &&
sample_num+start_idx == app->signal_bestlen+1)
{
if(app->signal_bestlen != 0 && sample_num + start_idx == app->signal_bestlen + 1) {
canvas_draw_dot(canvas, x, y + 2);
canvas_draw_dot(canvas, x - 1, y + 3);
canvas_draw_dot(canvas, x, y + 3);
@ -59,8 +57,7 @@ void render_view_raw_pulses(Canvas *const canvas, ProtoViewApp *app) {
/* Show signal information. */
char buf[64];
snprintf(buf,sizeof(buf),"%luus",
(unsigned long)DetectedSamples->short_pulse_dur);
snprintf(buf, sizeof(buf), "%luus", (unsigned long)DetectedSamples->short_pulse_dur);
canvas_set_font(canvas, FontSecondary);
canvas_draw_str_with_border(canvas, 97, 63, buf, ColorWhite, ColorBlack);
if(app->signal_decoded) {
@ -75,8 +72,10 @@ void process_input_raw_pulses(ProtoViewApp *app, InputEvent input) {
/* Handle panning of the signal window. Long pressing
* right will show successive samples, long pressing left
* previous samples. */
if (input.key == InputKeyRight) app->signal_offset++;
else if (input.key == InputKeyLeft) app->signal_offset--;
if(input.key == InputKeyRight)
app->signal_offset++;
else if(input.key == InputKeyLeft)
app->signal_offset--;
else if(input.key == InputKeyOk) {
app->signal_offset = 0;
app->us_scale = PROTOVIEW_RAW_VIEW_DEFAULT_SCALE;

View file

@ -55,9 +55,7 @@ void process_input_settings(ProtoViewApp *app, InputEvent input) {
app->txrx->debug_timer_sampling = !app->txrx->debug_timer_sampling;
radio_begin(app);
radio_rx(app);
} else if (input.type == InputTypePress &&
(input.key != InputKeyDown || input.key != InputKeyUp))
{
} else if(input.type == InputTypePress && (input.key != InputKeyDown || input.key != InputKeyUp)) {
/* Handle up and down to change frequency or modulation. */
if(app->current_view == ViewFrequencySettings) {
size_t curidx = 0, i;
@ -108,7 +106,11 @@ void process_input_settings(ProtoViewApp *app, InputEvent input) {
* we need to restart the radio with the right frequency and modulation. */
void view_exit_settings(ProtoViewApp* app) {
if(app->txrx->freq_mod_changed) {
FURI_LOG_E(TAG, "Setting view, setting frequency/modulation to %lu %s", app->frequency, ProtoViewModulations[app->modulation].name);
FURI_LOG_E(
TAG,
"Setting view, setting frequency/modulation to %lu %s",
app->frequency,
ProtoViewModulations[app->modulation].name);
radio_rx_end(app);
radio_begin(app);
radio_rx(app);

View file

@ -228,14 +228,16 @@ int32_t text_viewer_app(void* p) {
if(input.key == InputKeyBack) {
break;
} else if(input.key == InputKeyUp) {
furi_check(furi_mutex_acquire(text_viewer->mutex, FuriWaitForever) == FuriStatusOk);
furi_check(
furi_mutex_acquire(text_viewer->mutex, FuriWaitForever) == FuriStatusOk);
if(text_viewer->model->file_offset > 0) {
text_viewer->model->file_offset -= TEXT_VIEWER_BYTES_PER_LINE;
if(!text_viewer_read_file(text_viewer)) break;
}
furi_mutex_release(text_viewer->mutex);
} else if(input.key == InputKeyDown) {
furi_check(furi_mutex_acquire(text_viewer->mutex, FuriWaitForever) == FuriStatusOk);
furi_check(
furi_mutex_acquire(text_viewer->mutex, FuriWaitForever) == FuriStatusOk);
uint32_t last_byte_on_screen =
text_viewer->model->file_offset + text_viewer->model->file_read_bytes;
@ -245,7 +247,8 @@ int32_t text_viewer_app(void* p) {
}
furi_mutex_release(text_viewer->mutex);
} else if(input.key == InputKeyLeft) {
furi_check(furi_mutex_acquire(text_viewer->mutex, FuriWaitForever) == FuriStatusOk);
furi_check(
furi_mutex_acquire(text_viewer->mutex, FuriWaitForever) == FuriStatusOk);
text_viewer->model->mode = !text_viewer->model->mode;
furi_mutex_release(text_viewer->mutex);
} else if(input.key == InputKeyRight) {

View file

@ -66,9 +66,14 @@ const Interface ONE_WIRE = {
.allocator = unitemp_onewire_sensor_alloc,
.mem_releaser = unitemp_onewire_sensor_free,
.updater = unitemp_onewire_sensor_update};
const Interface SPI = {
.name = "SPI",
.allocator = unitemp_spi_sensor_alloc,
.mem_releaser = unitemp_spi_sensor_free,
.updater = unitemp_spi_sensor_update};
//Перечень интерфейсов подключения
//static const Interface* interfaces[] = {&SINGLE_WIRE, &I2C, &ONE_WIRE};
//static const Interface* interfaces[] = {&SINGLE_WIRE, &I2C, &ONE_WIRE, &SPI};
//Перечень датчиков
static const SensorType* sensorTypes[] = {
&DHT11,
@ -87,7 +92,8 @@ static const SensorType* sensorTypes[] = {
&HDC1080,
&BMP180,
&BMP280,
&BME280};
&BME280,
&MAX31855};
const SensorType* unitemp_sensors_getTypeFromInt(uint8_t index) {
if(index > SENSOR_TYPES_COUNT) return NULL;
@ -166,7 +172,7 @@ uint8_t unitemp_gpio_getAviablePortsCount(const Interface* interface, const GPIO
}
//Проверка для single wire
if(interface == &SINGLE_WIRE) {
if(interface == &SINGLE_WIRE || interface == &SPI) {
if(gpio_interfaces_list[i] == NULL || (unitemp_gpio_getFromIndex(i) == extraport)) {
aviable_ports_count++;
}
@ -205,6 +211,13 @@ const GPIO*
return NULL;
}
}
if(interface == &SPI) {
if(!((gpio_interfaces_list[0] == NULL || gpio_interfaces_list[0] == &SPI) &&
(gpio_interfaces_list[1] == NULL || gpio_interfaces_list[1] == &SPI) &&
(gpio_interfaces_list[3] == NULL || gpio_interfaces_list[3] == &SPI))) {
return NULL;
}
}
uint8_t aviable_index = 0;
for(uint8_t i = 0; i < GPIO_ITEMS; i++) {
@ -222,7 +235,7 @@ const GPIO*
}
}
//Проверка для single wire
if(interface == &SINGLE_WIRE) {
if(interface == &SINGLE_WIRE || interface == &SPI) {
if(gpio_interfaces_list[i] == NULL || unitemp_gpio_getFromIndex(i) == extraport) {
if(aviable_index == index) {
return unitemp_gpio_getFromIndex(i);
@ -435,6 +448,11 @@ bool unitemp_sensors_save(void) {
stream_write_format(
app->file_stream, "%d\n", unitemp_singlewire_sensorGetGPIO(sensor)->num);
}
if(sensor->type->interface == &SPI) {
uint8_t gpio_num = ((SPISensor*)sensor->instance)->CS_pin->num;
stream_write_format(app->file_stream, "%d\n", gpio_num);
}
if(sensor->type->interface == &I2C) {
stream_write_format(
app->file_stream, "%X\n", ((I2CSensor*)sensor->instance)->currentI2CAdr);
@ -512,7 +530,7 @@ Sensor* unitemp_sensor_alloc(char* name, const SensorType* type, char* args) {
//Выход если датчик успешно развёрнут
if(status) {
FURI_LOG_I(APP_NAME, "Sensor %s allocated", name);
UNITEMP_DEBUG("Sensor %s allocated", name);
return sensor;
}
//Выход с очисткой если память для датчика не была выделена
@ -545,7 +563,6 @@ void unitemp_sensor_free(Sensor* sensor) {
FURI_LOG_E(APP_NAME, "Sensor %s memory is not released", sensor->name);
}
free(sensor->name);
//free(sensor);
}
void unitemp_sensors_free(void) {
@ -573,7 +590,7 @@ bool unitemp_sensors_init(void) {
app->sensors[i]->name);
result = false;
}
UNITEMP_DEBUG("Sensor %s successfully initialized", app->sensors[i]->name);
FURI_LOG_I(APP_NAME, "Sensor %s successfully initialized", app->sensors[i]->name);
}
app->sensors_ready = true;
return result;

View file

@ -136,7 +136,7 @@ typedef struct Sensor {
extern const Interface SINGLE_WIRE; //Собственный однопроводной протокол датчиков DHTXX и AM23XX
extern const Interface ONE_WIRE; //Однопроводной протокол Dallas
extern const Interface I2C; //I2C_2 (PC0, PC1)
//extern const Interface SPI;
extern const Interface SPI; //SPI_1 (MOSI - 2, MISO - 3, CS - 4, SCK - 5)
/* ============================= Датчик(и) ============================= */
/**
@ -326,4 +326,5 @@ const GPIO*
#include "./sensors/BMP180.h"
#include "./sensors/HTU21x.h"
#include "./sensors/HDC1080.h"
#include "./sensors/MAX31855.h"
#endif

View file

@ -0,0 +1,89 @@
/*
Unitemp - Universal temperature reader
Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <furi.h>
#include <furi_hal.h>
#include "SPISensor.h"
static uint8_t sensors_count = 0;
bool unitemp_spi_sensor_alloc(Sensor* sensor, char* args) {
if(args == NULL) return false;
//Создание инстанса датчика SPI
SPISensor* instance = malloc(sizeof(SPISensor));
if(instance == NULL) {
FURI_LOG_E(APP_NAME, "Sensor %s instance allocation error", sensor->name);
return false;
}
sensor->instance = instance;
//Определение GPIO chip select
int gpio = 255;
sscanf(args, "%d", &gpio);
instance->CS_pin = unitemp_gpio_getFromInt(gpio);
if(instance->CS_pin == NULL) {
FURI_LOG_E(APP_NAME, "Sensor %s GPIO setting error", sensor->name);
free(instance);
return false;
}
instance->spi = malloc(sizeof(FuriHalSpiBusHandle));
memcpy(instance->spi, &furi_hal_spi_bus_handle_external, sizeof(FuriHalSpiBusHandle));
instance->spi->cs = instance->CS_pin->pin;
bool status = sensor->type->allocator(sensor, args);
//Блокировка портов GPIO
sensors_count++;
unitemp_gpio_lock(unitemp_gpio_getFromInt(2), &SPI);
unitemp_gpio_lock(unitemp_gpio_getFromInt(3), &SPI);
unitemp_gpio_lock(unitemp_gpio_getFromInt(5), &SPI);
unitemp_gpio_lock(instance->CS_pin, &SPI);
return status;
}
bool unitemp_spi_sensor_free(Sensor* sensor) {
bool status = sensor->type->mem_releaser(sensor);
unitemp_gpio_unlock(((SPISensor*)sensor->instance)->CS_pin);
free(((SPISensor*)(sensor->instance))->spi);
free(sensor->instance);
if(--sensors_count == 0) {
unitemp_gpio_unlock(unitemp_gpio_getFromInt(2));
unitemp_gpio_unlock(unitemp_gpio_getFromInt(3));
unitemp_gpio_unlock(unitemp_gpio_getFromInt(5));
}
return status;
}
bool unitemp_spi_sensor_init(Sensor* sensor) {
return sensor->type->initializer(sensor);
}
bool unitemp_spi_sensor_deinit(Sensor* sensor) {
UNUSED(sensor);
return true;
}
UnitempStatus unitemp_spi_sensor_update(Sensor* sensor) {
return sensor->type->updater(sensor);
}

View file

@ -0,0 +1,66 @@
/*
Unitemp - Universal temperature reader
Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef UNITEMP_SPI
#define UNITEMP_SPI
#include "../unitemp.h"
#include <furi_hal_spi.h>
//Структура SPI датчика
typedef struct SPISensor {
//Указатель на интерфейс SPI
FuriHalSpiBusHandle* spi;
//Порт подключения CS
const GPIO* CS_pin;
} SPISensor;
/**
* @brief Выделение памяти для датчика с интерфейсом SPI
* @param sensor Указатель на датчик
* @param args Указатель на массив аргументов с параметрами датчика
* @return Истина если всё ок
*/
bool unitemp_spi_sensor_alloc(Sensor* sensor, char* args);
/**
* @brief Высвобождение памяти инстанса датчика
* @param sensor Указатель на датчик
*/
bool unitemp_spi_sensor_free(Sensor* sensor);
/**
* @brief Инициализации датчика с интерфейсом one wire
* @param sensor Указатель на датчик
* @return Истина если инициализация упспешная
*/
bool unitemp_spi_sensor_init(Sensor* sensor);
/**
* @brief Деинициализация датчика
* @param sensor Указатель на датчик
*/
bool unitemp_spi_sensor_deinit(Sensor* sensor);
/**
* @brief Обновить значение с датчка
* @param sensor Указатель на датчик
* @return Статус обновления
*/
UnitempStatus unitemp_spi_sensor_update(Sensor* sensor);
#endif

View file

@ -0,0 +1,93 @@
/*
Unitemp - Universal temperature reader
Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "MAX31855.h"
const SensorType MAX31855 = {
.typename = "MAX31855",
.altname = "MAX31855 (Thermocouple)",
.interface = &SPI,
.datatype = UT_TEMPERATURE,
.pollingInterval = 500,
.allocator = unitemp_MAX31855_alloc,
.mem_releaser = unitemp_MAX31855_free,
.initializer = unitemp_MAX31855_init,
.deinitializer = unitemp_MAX31855_deinit,
.updater = unitemp_MAX31855_update};
bool unitemp_MAX31855_alloc(Sensor* sensor, char* args) {
UNUSED(sensor);
UNUSED(args);
return true;
}
bool unitemp_MAX31855_free(Sensor* sensor) {
UNUSED(sensor);
return true;
}
bool unitemp_MAX31855_init(Sensor* sensor) {
SPISensor* instance = sensor->instance;
furi_hal_spi_bus_handle_init(instance->spi);
UNUSED(instance);
return true;
}
bool unitemp_MAX31855_deinit(Sensor* sensor) {
UNUSED(sensor);
return true;
}
UnitempStatus unitemp_MAX31855_update(Sensor* sensor) {
SPISensor* instance = sensor->instance;
furi_hal_spi_acquire(instance->spi);
furi_hal_gpio_write(instance->CS_pin->pin, false);
uint8_t buff[4] = {0};
furi_hal_spi_bus_rx(instance->spi, buff, 4, 0xFF);
furi_hal_spi_release(instance->spi);
uint32_t raw = (buff[0] << 24) | (buff[1] << 16) | (buff[2] << 8) | buff[3];
if(raw == 0xFFFFFFFF || raw == 0) return UT_SENSORSTATUS_TIMEOUT;
//Определение состояния термопары
uint8_t state = raw & 0b111;
//Обрыв
if(state == 0x01) {
UNITEMP_DEBUG("%s has thermocouple open circuit", sensor->name);
return UT_SENSORSTATUS_ERROR;
}
//Короткое замыкание к земле
if(state == 0x02) {
UNITEMP_DEBUG("%s has thermocouple short to GND", sensor->name);
return UT_SENSORSTATUS_ERROR;
}
//Короткое замыкание к питанию
if(state == 0x04) {
UNITEMP_DEBUG("%s has thermocouple short to VCC", sensor->name);
return UT_SENSORSTATUS_ERROR;
}
raw = (raw >> 16) & 0xFFFC;
sensor->temp = (int16_t)(raw) / 16.0f;
return UT_SENSORSTATUS_OK;
}

View file

@ -0,0 +1,65 @@
/*
Unitemp - Universal temperature reader
Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef UNITEMP_MAX31855
#define UNITEMP_MAX31855
#include "../unitemp.h"
#include "../Sensors.h"
#include "../interfaces/SPISensor.h"
extern const SensorType MAX31855;
/**
* @brief Выделение памяти и установка начальных значений датчика MAX31855
*
* @param sensor Указатель на создаваемый датчик
* @return Истина при успехе
*/
bool unitemp_MAX31855_alloc(Sensor* sensor, char* args);
/**
* @brief Инициализации датчика MAX31855
*
* @param sensor Указатель на датчик
* @return Истина если инициализация упспешная
*/
bool unitemp_MAX31855_init(Sensor* sensor);
/**
* @brief Деинициализация датчика
*
* @param sensor Указатель на датчик
*/
bool unitemp_MAX31855_deinit(Sensor* sensor);
/**
* @brief Обновление значений из датчика
*
* @param sensor Указатель на датчик
* @return Статус обновления
*/
UnitempStatus unitemp_MAX31855_update(Sensor* sensor);
/**
* @brief Высвободить память датчика
*
* @param sensor Указатель на датчик
*/
bool unitemp_MAX31855_free(Sensor* sensor);
#endif

View file

@ -76,7 +76,7 @@ static void _draw_temperature(Canvas* canvas, Sensor* sensor, uint8_t x, uint8_t
app->buff[0] = '-';
offset = 1;
}
snprintf((char*)(app->buff + offset), BUFF_SIZE, "%d", (int8_t)sensor->temp);
snprintf((char*)(app->buff + offset), BUFF_SIZE, "%d", (int16_t)sensor->temp);
canvas_set_font(canvas, FontBigNumbers);
canvas_draw_str_aligned(
canvas,
@ -237,6 +237,7 @@ static void _draw_carousel_values(Canvas* canvas) {
canvas_draw_icon(canvas, 34, 23, frames[furi_get_tick() % 2250 / 750]);
canvas_set_font(canvas, FontSecondary);
//TODO: Оптимизировать эту срань
if(unitemp_sensor_getActive(generalview_sensor_index)->type->interface == &SINGLE_WIRE) {
snprintf(
app->buff,
@ -256,6 +257,9 @@ static void _draw_carousel_values(Canvas* canvas) {
if(unitemp_sensor_getActive(generalview_sensor_index)->type->interface == &I2C) {
snprintf(app->buff, BUFF_SIZE, "Waiting for module on I2C pins");
}
if(unitemp_sensor_getActive(generalview_sensor_index)->type->interface == &SPI) {
snprintf(app->buff, BUFF_SIZE, "Waiting for module on SPI pins");
}
canvas_draw_str_aligned(canvas, 64, 19, AlignCenter, AlignCenter, app->buff);
return;
}
@ -304,6 +308,8 @@ static void _draw_carousel_values(Canvas* canvas) {
break;
}
}
//TODO: Оптимизировать вывод информации
static void _draw_carousel_info(Canvas* canvas) {
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, 10, 23, "Type:");
@ -351,6 +357,25 @@ static void _draw_carousel_info(Canvas* canvas) {
->gpio->name);
}
if(unitemp_sensor_getActive(generalview_sensor_index)->type->interface == &SPI) {
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, 10, 35, "MISO pin:");
canvas_draw_str(canvas, 10, 46, "CS pin:");
canvas_draw_str(canvas, 10, 58, "SCK pin:");
canvas_set_font(canvas, FontSecondary);
canvas_draw_str(
canvas, 41, 23, unitemp_sensor_getActive(generalview_sensor_index)->type->typename);
canvas_draw_str(canvas, 60, 35, unitemp_gpio_getFromInt(3)->name);
canvas_draw_str(
canvas,
47,
46,
((SPISensor*)unitemp_sensor_getActive(generalview_sensor_index)->instance)
->CS_pin->name);
canvas_draw_str(canvas, 54, 58, unitemp_gpio_getFromInt(5)->name);
}
if(unitemp_sensor_getActive(generalview_sensor_index)->type->interface == &I2C) {
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, 10, 35, "I2C addr:");

View file

@ -193,6 +193,12 @@ static void _gpio_change_callback(VariableItem* item) {
unitemp_gpio_getAviablePort(editable_sensor->type->interface, index, initial_gpio);
variable_item_set_current_value_text(item, instance->gpio->name);
}
if(editable_sensor->type->interface == &SPI) {
SPISensor* instance = editable_sensor->instance;
instance->CS_pin =
unitemp_gpio_getAviablePort(editable_sensor->type->interface, index, initial_gpio);
variable_item_set_current_value_text(item, instance->CS_pin->name);
}
if(editable_sensor->type->interface == &ONE_WIRE) {
OneWireSensor* instance = editable_sensor->instance;
instance->bus->gpio =
@ -296,12 +302,15 @@ void unitemp_SensorEdit_switch(Sensor* sensor) {
offset_buff, OFFSET_BUFF_SIZE, "%+1.1f", (double)(editable_sensor->temp_offset / 10.0));
variable_item_set_current_value_text(temp_offset_item, offset_buff);
//Порт подключения датчка (для one wire и single wire)
if(sensor->type->interface == &ONE_WIRE || sensor->type->interface == &SINGLE_WIRE) {
//Порт подключения датчка (для one wire, SPI и single wire)
if(sensor->type->interface == &ONE_WIRE || sensor->type->interface == &SINGLE_WIRE ||
sensor->type->interface == &SPI) {
if(sensor->type->interface == &ONE_WIRE) {
initial_gpio = ((OneWireSensor*)editable_sensor->instance)->bus->gpio;
} else {
} else if(sensor->type->interface == &SINGLE_WIRE) {
initial_gpio = ((SingleWireSensor*)editable_sensor->instance)->gpio;
} else if(sensor->type->interface == &SPI) {
initial_gpio = ((SPISensor*)editable_sensor->instance)->CS_pin;
}
uint8_t aviable_gpio_count =

View file

@ -86,8 +86,8 @@ static void _enter_callback(void* context, uint32_t index) {
return;
}
//Выбор первого доступного порта для датчика single wire
if(type->interface == &SINGLE_WIRE) {
//Выбор первого доступного порта для датчика single wire и SPI
if(type->interface == &SINGLE_WIRE || type->interface == &SPI) {
snprintf(
args,
4,

View file

@ -16,8 +16,7 @@
#define FURI_HAL_SPEAKER_CHANNEL LL_TIM_CHANNEL_CH1
#define DMA_INSTANCE DMA1, LL_DMA_CHANNEL_1
void wav_player_speaker_init(uint32_t sample_rate)
{
void wav_player_speaker_init(uint32_t sample_rate) {
LL_TIM_InitTypeDef TIM_InitStruct = {0};
//TIM_InitStruct.Prescaler = 4;
TIM_InitStruct.Prescaler = 1;
@ -51,7 +50,12 @@ void wav_player_speaker_init(uint32_t sample_rate)
//furi_hal_gpio_init_ex(&gpio_ext_pa6, (GpioMode)GpioPullNo, (GpioPull)GpioModeAltFunctionPushPull, GpioSpeedVeryHigh, GpioAltFn14TIM16);
//furi_hal_gpio_init_ex(&gpio_ext_pa6, (GpioMode)GpioPullNo, (GpioPull)GpioModeAltFunctionPushPull, GpioSpeedLow, GpioAltFn14TIM16);
furi_hal_gpio_init_ex(&gpio_ext_pa6, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedVeryHigh, GpioAltFn14TIM16);
furi_hal_gpio_init_ex(
&gpio_ext_pa6,
GpioModeAltFunctionPushPull,
GpioPullNo,
GpioSpeedVeryHigh,
GpioAltFn14TIM16);
//furi_hal_gpio_init_simple(&gpio_ext_pa6, GpioModeOutputPushPull);
//furi_hal_gpio_write(&gpio_ext_pa6, false);
}

View file

@ -45,10 +45,6 @@ typedef struct {
uint8_t data[DATA_COUNT];
} WavPlayerViewModel;
WavPlayerView* wav_player_view_alloc();
void wav_player_view_free(WavPlayerView* wav_view);