diff --git a/applications/plugins/protoview/README.md b/applications/plugins/protoview/README.md index 8359c073d..a12aec979 100644 --- a/applications/plugins/protoview/README.md +++ b/applications/plugins/protoview/README.md @@ -14,8 +14,9 @@ and if the selected modulation is correct. Other than that, ProtoView is able to decode a few interesting protocols: * TPMS sensors: Renault, Toyota, Schrader, Citroen, Ford. +* Microchip HSC200/300/301 Keeloq protocol. * Oregon thermometer protocol 2. -* PTxxxx/SCxxxx based remotes. +* PT2262, SC5262 based remotes. * ... more will be implemented soon, hopefully. Send PRs :) ![ProtoView screenshot Renault TPMS data](/images/protoview_2.jpg) diff --git a/applications/plugins/protoview/app.c b/applications/plugins/protoview/app.c index 3f9c7ced1..ea4e366b8 100644 --- a/applications/plugins/protoview/app.c +++ b/applications/plugins/protoview/app.c @@ -40,8 +40,8 @@ extern const SubGhzProtocolRegistry protoview_protocol_registry; /* The callback actually just passes the control to the actual active * view callback, after setting up basic stuff like cleaning the screen * and setting color to black. */ -static void render_callback(Canvas* const canvas, void* ctx) { - ProtoViewApp* app = ctx; +static void render_callback(Canvas *const canvas, void *ctx) { + ProtoViewApp *app = ctx; /* Clear screen. */ canvas_set_color(canvas, ColorWhite); @@ -51,63 +51,56 @@ 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) { - ProtoViewApp* app = ctx; - furi_message_queue_put(app->event_queue, input_event, FuriWaitForever); +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. */ -static void app_switch_view(ProtoViewApp* app, SwitchViewDirection dir) { +static void app_switch_view(ProtoViewApp *app, SwitchViewDirection dir) { ProtoViewCurrentView old = app->current_view; - if(dir == AppNextView) { + if (dir == AppNextView) { app->current_view++; - if(app->current_view == ViewLast) app->current_view = 0; - } else if(dir == AppPrevView) { - if(app->current_view == 0) - app->current_view = ViewLast - 1; + if (app->current_view == ViewLast) app->current_view = 0; + } else if (dir == AppPrevView) { + if (app->current_view == 0) + app->current_view = ViewLast-1; else app->current_view--; } ProtoViewCurrentView new = app->current_view; /* Call the enter/exit view callbacks if needed. */ - if(old == ViewDirectSampling) view_exit_direct_sampling(app); - if(new == ViewDirectSampling) view_enter_direct_sampling(app); + if (old == ViewDirectSampling) view_exit_direct_sampling(app); + if (new == ViewDirectSampling) view_enter_direct_sampling(app); /* The frequency/modulation settings are actually a single view: * as long as the user stays between the two modes of this view we * don't need to call the exit-view callback. */ - if((old == ViewFrequencySettings && new != ViewModulationSettings) || - (old == ViewModulationSettings && new != ViewFrequencySettings)) + if ((old == ViewFrequencySettings && new != ViewModulationSettings) || + (old == ViewModulationSettings && new != ViewFrequencySettings)) view_exit_settings(app); } /* Allocate the application state and initialize a number of stuff. * This is called in the entry point to create the application state. */ ProtoViewApp* protoview_app_alloc() { - ProtoViewApp* app = malloc(sizeof(ProtoViewApp)); + ProtoViewApp *app = malloc(sizeof(ProtoViewApp)); // Init shared data structures RawSamples = raw_samples_alloc(); @@ -149,14 +142,17 @@ 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); - + app->frequency = subghz_setting_get_default_frequency(app->setting); app->modulation = 0; /* Defaults to ProtoViewModulations[0]. */ @@ -169,7 +165,7 @@ ProtoViewApp* protoview_app_alloc() { /* Free what the application allocated. It is not clear to me if the * Flipper OS, once the application exits, will be able to reclaim space * even if we forget to free something here. */ -void protoview_app_free(ProtoViewApp* app) { +void protoview_app_free(ProtoViewApp *app) { furi_assert(app); // Put CC1101 on sleep. @@ -187,7 +183,7 @@ void protoview_app_free(ProtoViewApp* app) { subghz_setting_free(app->setting); // Worker stuff. - if(!app->txrx->debug_timer_sampling) { + if (!app->txrx->debug_timer_sampling) { subghz_receiver_free(app->txrx->receiver); subghz_environment_free(app->txrx->environment); subghz_worker_free(app->txrx->worker); @@ -205,8 +201,8 @@ void protoview_app_free(ProtoViewApp* app) { /* Called periodically. Do signal processing here. Data we process here * will be later displayed by the render callback. The side effect of this * function is to scan for signals and set DetectedSamples. */ -static void timer_callback(void* ctx) { - ProtoViewApp* app = ctx; +static void timer_callback(void *ctx) { + ProtoViewApp *app = ctx; uint32_t delta, lastidx = app->signal_last_scan_idx; /* scan_for_signal(), called by this function, deals with a @@ -214,22 +210,22 @@ static void timer_callback(void* ctx) { * cross-boundaries, it is enough if we scan each time the buffer fills * for 50% more compared to the last scan. Thanks to this check we * can avoid scanning too many times to just find the same data. */ - if(lastidx < RawSamples->idx) { + if (lastidx < RawSamples->idx) { delta = RawSamples->idx - lastidx; } else { delta = RawSamples->total - lastidx + RawSamples->idx; } - if(delta < RawSamples->total / 2) return; + if (delta < RawSamples->total/2) return; app->signal_last_scan_idx = RawSamples->idx; scan_for_signal(app); } int32_t protoview_app_entry(void* p) { UNUSED(p); - ProtoViewApp* app = protoview_app_alloc(); + ProtoViewApp *app = protoview_app_alloc(); /* Create a timer. We do data analysis in the callback. */ - FuriTimer* timer = furi_timer_alloc(timer_callback, FuriTimerTypePeriodic, app); + FuriTimer *timer = furi_timer_alloc(timer_callback, FuriTimerTypePeriodic, app); furi_timer_start(timer, furi_kernel_get_tick_frequency() / 8); /* Start listening to signals immediately. */ @@ -244,57 +240,60 @@ int32_t protoview_app_entry(void* p) { InputEvent input; 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 (qstat == FuriStatusOk) { + 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) { + } else if (input.type == InputTypeShort && + input.key == InputKeyRight) + { /* Go to the next view. */ - app_switch_view(app, AppNextView); - } else if(input.type == InputTypeShort && input.key == InputKeyLeft) { + app_switch_view(app,AppNextView); + } else if (input.type == InputTypeShort && + input.key == InputKeyLeft) + { /* Go to the previous view. */ - app_switch_view(app, AppPrevView); + app_switch_view(app,AppPrevView); } else { /* This is where we pass the control to the currently * active view input processing. */ switch(app->current_view) { case ViewRawPulses: - process_input_raw_pulses(app, input); + process_input_raw_pulses(app,input); break; case ViewInfo: - process_input_info(app, input); + process_input_info(app,input); break; case ViewFrequencySettings: case ViewModulationSettings: - process_input_settings(app, input); + process_input_settings(app,input); break; case ViewDirectSampling: - process_input_direct_sampling(app, input); - break; - case ViewLast: - furi_crash(TAG " ViewLast selected"); + process_input_direct_sampling(app,input); 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++; - if(!(c % 20)) FURI_LOG_E(TAG, "Loop timeout"); + if (DEBUG_MSG) { + static int c = 0; c++; + if (!(c % 20)) FURI_LOG_E(TAG, "Loop timeout"); } } view_port_update(app->view_port); } /* App no longer running. Shut down and free. */ - if(app->txrx->txrx_state == TxRxStateRx) { + if (app->txrx->txrx_state == TxRxStateRx) { FURI_LOG_E(TAG, "Putting CC1101 to sleep before exiting."); radio_rx_end(app); radio_sleep(app); @@ -304,3 +303,4 @@ int32_t protoview_app_entry(void* p) { protoview_app_free(app); return 0; } + diff --git a/applications/plugins/protoview/app.h b/applications/plugins/protoview/app.h index 735515cd9..52b0bff5b 100644 --- a/applications/plugins/protoview/app.h +++ b/applications/plugins/protoview/app.h @@ -48,12 +48,15 @@ typedef enum { } ProtoViewCurrentView; /* Used by app_switch_view() */ -typedef enum { AppNextView, AppPrevView } SwitchViewDirection; +typedef enum { + AppNextView, + AppPrevView +} SwitchViewDirection; typedef struct { - const char* name; + const char *name; FuriHalSubGhzPreset preset; - uint8_t* custom; + uint8_t *custom; } ProtoViewModulation; extern ProtoViewModulation ProtoViewModulations[]; /* In app_subghz.c */ @@ -62,19 +65,19 @@ extern ProtoViewModulation ProtoViewModulations[]; /* In app_subghz.c */ * It receives data and we get our protocol "feed" callback called * with the level (1 or 0) and duration. */ struct ProtoViewTxRx { - bool freq_mod_changed; /* The user changed frequency and/or modulation + bool freq_mod_changed; /* The user changed frequency and/or modulation from the interface. There is to restart the radio with the right parameters. */ - SubGhzWorker* worker; /* Our background worker. */ + SubGhzWorker* worker; /* Our background worker. */ SubGhzEnvironment* environment; SubGhzReceiver* receiver; TxRxState txrx_state; /* Receiving, idle or sleeping? */ /* Timer sampling mode state. */ - bool debug_timer_sampling; /* Read data from GDO0 in a busy loop. Only + bool debug_timer_sampling; /* Read data from GDO0 in a busy loop. Only for testing. */ uint32_t last_g0_change_time; /* Last high->low (or reverse) switch. */ - bool last_g0_value; /* Current value (high or low): we are + bool last_g0_value; /* Current value (high or low): we are checking the duration in the timer handler. */ }; @@ -94,43 +97,43 @@ typedef struct ProtoViewMsgInfo { char info2[PROTOVIEW_MSG_STR_LEN]; /* Protocol specific info line 2. */ char info3[PROTOVIEW_MSG_STR_LEN]; /* Protocol specific info line 3. */ char info4[PROTOVIEW_MSG_STR_LEN]; /* Protocol specific info line 4. */ - uint64_t len; /* Bits consumed from the stream. */ + uint64_t len; /* Bits consumed from the stream. */ } ProtoViewMsgInfo; struct ProtoViewApp { /* GUI */ - Gui* gui; - ViewPort* view_port; /* We just use a raw viewport and we render + Gui *gui; + ViewPort *view_port; /* We just use a raw viewport and we render everything into the low level canvas. */ - ProtoViewCurrentView current_view; /* Active view ID. */ - FuriMessageQueue* event_queue; /* Keypress events go here. */ + ProtoViewCurrentView current_view; /* Active view ID. */ + FuriMessageQueue *event_queue; /* Keypress events go here. */ /* Radio related. */ - ProtoViewTxRx* txrx; /* Radio state. */ - SubGhzSetting* setting; /* A list of valid frequencies. */ + ProtoViewTxRx *txrx; /* Radio state. */ + SubGhzSetting *setting; /* A list of valid frequencies. */ /* Generic app state. */ - int running; /* Once false exists the app. */ + int running; /* Once false exists the app. */ uint32_t signal_bestlen; /* Longest coherent signal observed so far. */ uint32_t signal_last_scan_idx; /* Index of the buffer last time we performed the scan. */ - bool signal_decoded; /* Was the current signal decoded? */ + bool signal_decoded; /* Was the current signal decoded? */ ProtoViewMsgInfo signal_info; /* Decoded message, if signal_decoded true. */ bool direct_sampling_enabled; /* This special view needs an explicit acknowledge to work. */ /* Raw view apps state. */ - uint32_t us_scale; /* microseconds per pixel. */ - uint32_t signal_offset; /* Long press left/right panning in raw view. */ + uint32_t us_scale; /* microseconds per pixel. */ + uint32_t signal_offset; /* Long press left/right panning in raw view. */ /* Configuration view app state. */ - uint32_t frequency; /* Current frequency. */ - uint8_t modulation; /* Current modulation ID, array index in the + uint32_t frequency; /* Current frequency. */ + uint8_t modulation; /* Current modulation ID, array index in the ProtoViewModulations table. */ }; typedef struct ProtoViewDecoder { - const char* name; /* Protocol name. */ + const char *name; /* Protocol name. */ /* The decode function takes a buffer that is actually a bitmap, with * high and low levels represented as 0 and 1. The number of high/low * pulses represented by the bitmap is passed as the 'numbits' argument, @@ -138,7 +141,7 @@ typedef struct ProtoViewDecoder { * 'bits'. So 'numbytes' is mainly useful to pass as argument to other * functions that perform bit extraction with bound checking, such as * bitmap_get() and so forth. */ - bool (*decode)(uint8_t* bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo* info); + bool (*decode)(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo *info); } ProtoViewDecoder; extern RawSamplesBuffer *RawSamples, *DetectedSamples; @@ -149,61 +152,37 @@ uint32_t radio_rx(ProtoViewApp* app); void radio_idle(ProtoViewApp* app); void radio_rx_end(ProtoViewApp* app); void radio_sleep(ProtoViewApp* app); -void raw_sampling_worker_start(ProtoViewApp* app); -void raw_sampling_worker_stop(ProtoViewApp* app); +void raw_sampling_worker_start(ProtoViewApp *app); +void raw_sampling_worker_stop(ProtoViewApp *app); /* signal.c */ uint32_t duration_delta(uint32_t a, uint32_t b); -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_set_pattern(uint8_t* b, uint32_t blen, const char* pat); -void bitmap_invert_bytes_bits(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); +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_set_pattern(uint8_t *b, uint32_t blen, 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); /* view_*.c */ -void render_view_raw_pulses(Canvas* const canvas, ProtoViewApp* app); -void process_input_raw_pulses(ProtoViewApp* app, InputEvent input); -void render_view_settings(Canvas* const canvas, ProtoViewApp* app); -void process_input_settings(ProtoViewApp* app, InputEvent input); -void render_view_info(Canvas* const canvas, ProtoViewApp* app); -void process_input_info(ProtoViewApp* app, InputEvent input); -void render_view_direct_sampling(Canvas* const canvas, ProtoViewApp* app); -void process_input_direct_sampling(ProtoViewApp* app, InputEvent input); -void view_enter_direct_sampling(ProtoViewApp* app); -void view_exit_direct_sampling(ProtoViewApp* app); -void view_exit_settings(ProtoViewApp* app); +void render_view_raw_pulses(Canvas *const canvas, ProtoViewApp *app); +void process_input_raw_pulses(ProtoViewApp *app, InputEvent input); +void render_view_settings(Canvas *const canvas, ProtoViewApp *app); +void process_input_settings(ProtoViewApp *app, InputEvent input); +void render_view_info(Canvas *const canvas, ProtoViewApp *app); +void process_input_info(ProtoViewApp *app, InputEvent input); +void render_view_direct_sampling(Canvas *const canvas, ProtoViewApp *app); +void process_input_direct_sampling(ProtoViewApp *app, InputEvent input); +void view_enter_direct_sampling(ProtoViewApp *app); +void view_exit_direct_sampling(ProtoViewApp *app); +void view_exit_settings(ProtoViewApp *app); /* ui.c */ -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); /* crc.c */ -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); diff --git a/applications/plugins/protoview/app_buffer.c b/applications/plugins/protoview/app_buffer.c index 3954587a4..7c069fd91 100644 --- a/applications/plugins/protoview/app_buffer.c +++ b/applications/plugins/protoview/app_buffer.c @@ -8,15 +8,15 @@ #include "app_buffer.h" /* Allocate and initialize a samples buffer. */ -RawSamplesBuffer* raw_samples_alloc(void) { - RawSamplesBuffer* buf = malloc(sizeof(*buf)); +RawSamplesBuffer *raw_samples_alloc(void) { + RawSamplesBuffer *buf = malloc(sizeof(*buf)); buf->mutex = furi_mutex_alloc(FuriMutexTypeNormal); raw_samples_reset(buf); return buf; } /* Free a sample buffer. Should be called when the mutex is released. */ -void raw_samples_free(RawSamplesBuffer* s) { +void raw_samples_free(RawSamplesBuffer *s) { furi_mutex_free(s->mutex); free(s); } @@ -24,49 +24,48 @@ void raw_samples_free(RawSamplesBuffer* s) { /* This just set all the samples to zero and also resets the internal * index. There is no need to call it after raw_samples_alloc(), but only * when one wants to reset the whole buffer of samples. */ -void raw_samples_reset(RawSamplesBuffer* s) { - furi_mutex_acquire(s->mutex, FuriWaitForever); +void raw_samples_reset(RawSamplesBuffer *s) { + furi_mutex_acquire(s->mutex,FuriWaitForever); s->total = RAW_SAMPLES_NUM; s->idx = 0; s->short_pulse_dur = 0; - memset(s->level, 0, sizeof(s->level)); - memset(s->dur, 0, sizeof(s->dur)); + memset(s->samples,0,sizeof(s->samples)); furi_mutex_release(s->mutex); } /* Set the raw sample internal index so that what is currently at * offset 'offset', will appear to be at 0 index. */ -void raw_samples_center(RawSamplesBuffer* s, uint32_t offset) { - s->idx = (s->idx + offset) % RAW_SAMPLES_NUM; +void raw_samples_center(RawSamplesBuffer *s, uint32_t offset) { + s->idx = (s->idx+offset) % RAW_SAMPLES_NUM; } /* Add the specified sample in the circular buffer. */ -void raw_samples_add(RawSamplesBuffer* s, bool level, uint32_t dur) { - furi_mutex_acquire(s->mutex, FuriWaitForever); - s->level[s->idx] = level; - s->dur[s->idx] = dur; - s->idx = (s->idx + 1) % RAW_SAMPLES_NUM; +void raw_samples_add(RawSamplesBuffer *s, bool level, uint32_t dur) { + furi_mutex_acquire(s->mutex,FuriWaitForever); + s->samples[s->idx].level = level; + s->samples[s->idx].dur = dur; + s->idx = (s->idx+1) % RAW_SAMPLES_NUM; furi_mutex_release(s->mutex); } /* 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) { - furi_mutex_acquire(s->mutex, FuriWaitForever); +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->level[idx]; - *dur = s->dur[idx]; + *level = s->samples[idx].level; + *dur = s->samples[idx].dur; furi_mutex_release(s->mutex); } /* Copy one buffer to the other, including current index. */ -void raw_samples_copy(RawSamplesBuffer* dst, RawSamplesBuffer* src) { - furi_mutex_acquire(src->mutex, FuriWaitForever); - furi_mutex_acquire(dst->mutex, FuriWaitForever); +void raw_samples_copy(RawSamplesBuffer *dst, RawSamplesBuffer *src) { + furi_mutex_acquire(src->mutex,FuriWaitForever); + furi_mutex_acquire(dst->mutex,FuriWaitForever); dst->idx = src->idx; dst->short_pulse_dur = src->short_pulse_dur; - memcpy(dst->level, src->level, sizeof(dst->level)); - memcpy(dst->dur, src->dur, sizeof(dst->dur)); + memcpy(dst->samples,src->samples,sizeof(dst->samples)); furi_mutex_release(src->mutex); furi_mutex_release(dst->mutex); } diff --git a/applications/plugins/protoview/app_buffer.h b/applications/plugins/protoview/app_buffer.h index 4448464ae..3a34d50f8 100644 --- a/applications/plugins/protoview/app_buffer.h +++ b/applications/plugins/protoview/app_buffer.h @@ -4,16 +4,16 @@ /* 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 { - FuriMutex* mutex; - uint8_t level[RAW_SAMPLES_NUM]; - uint32_t dur[RAW_SAMPLES_NUM]; - uint32_t idx; /* Current idx (next to write). */ + FuriMutex *mutex; + struct { + uint16_t level:1; + uint16_t dur:15; + } samples[RAW_SAMPLES_NUM]; + uint32_t idx; /* Current idx (next to write). */ uint32_t total; /* Total samples: same as RAW_SAMPLES_NUM, we provide this field for a cleaner interface with the user, but we always use RAW_SAMPLES_NUM when taking the modulo so @@ -22,10 +22,10 @@ typedef struct RawSamplesBuffer { uint32_t short_pulse_dur; /* Duration of the shortest pulse. */ } RawSamplesBuffer; -RawSamplesBuffer* raw_samples_alloc(void); -void raw_samples_reset(RawSamplesBuffer* s); -void raw_samples_center(RawSamplesBuffer* s, uint32_t offset); -void raw_samples_add(RawSamplesBuffer* s, bool level, uint32_t dur); -void raw_samples_get(RawSamplesBuffer* s, uint32_t idx, bool* level, uint32_t* dur); -void raw_samples_copy(RawSamplesBuffer* dst, RawSamplesBuffer* src); -void raw_samples_free(RawSamplesBuffer* s); +RawSamplesBuffer *raw_samples_alloc(void); +void raw_samples_reset(RawSamplesBuffer *s); +void raw_samples_center(RawSamplesBuffer *s, uint32_t offset); +void raw_samples_add(RawSamplesBuffer *s, bool level, uint32_t dur); +void raw_samples_get(RawSamplesBuffer *s, uint32_t idx, bool *level, uint32_t *dur); +void raw_samples_copy(RawSamplesBuffer *dst, RawSamplesBuffer *src); +void raw_samples_free(RawSamplesBuffer *s); diff --git a/applications/plugins/protoview/app_subghz.c b/applications/plugins/protoview/app_subghz.c index 40c7dcfa2..ec7724b13 100644 --- a/applications/plugins/protoview/app_subghz.c +++ b/applications/plugins/protoview/app_subghz.c @@ -9,8 +9,8 @@ #include #include -void raw_sampling_worker_start(ProtoViewApp* app); -void raw_sampling_worker_stop(ProtoViewApp* app); +void raw_sampling_worker_start(ProtoViewApp *app); +void raw_sampling_worker_stop(ProtoViewApp *app); ProtoViewModulation ProtoViewModulations[] = { {"OOK 650Khz", FuriHalSubGhzPresetOok650Async, NULL}, @@ -19,9 +19,10 @@ ProtoViewModulation ProtoViewModulations[] = { {"2FSK 47.6Khz", FuriHalSubGhzPreset2FSKDev476Async, NULL}, {"MSK", FuriHalSubGhzPresetMSK99_97KbAsync, NULL}, {"GFSK", FuriHalSubGhzPresetGFSK9_99KbAsync, NULL}, - {"TPMS 1 (FSK)", 0, (uint8_t*)protoview_subghz_tpms1_async_regs}, - {"TPMS 2 (FSK)", 0, (uint8_t*)protoview_subghz_tpms2_async_regs}, - {"TPMS 3 (FSK)", 0, (uint8_t*)protoview_subghz_tpms3_async_regs}, + {"TPMS 1 (FSK)", 0, (uint8_t*)protoview_subghz_tpms1_fsk_async_regs}, + {"TPMS 2 (OOK)", 0, (uint8_t*)protoview_subghz_tpms2_ook_async_regs}, + {"TPMS 3 (FSK)", 0, (uint8_t*)protoview_subghz_tpms3_fsk_async_regs}, + {"TPMS 4 (FSK)", 0, (uint8_t*)protoview_subghz_tpms4_fsk_async_regs}, {NULL, 0, NULL} /* End of list sentinel. */ }; @@ -37,7 +38,7 @@ void radio_begin(ProtoViewApp* app) { /* The CC1101 preset can be either one of the standard presets, if * the modulation "custom" field is NULL, or a custom preset we * defined in custom_presets.h. */ - if(ProtoViewModulations[app->modulation].custom == NULL) + if (ProtoViewModulations[app->modulation].custom == NULL) furi_hal_subghz_load_preset(ProtoViewModulations[app->modulation].preset); else furi_hal_subghz_load_custom_preset(ProtoViewModulations[app->modulation].custom); @@ -49,10 +50,10 @@ void radio_begin(ProtoViewApp* app) { uint32_t radio_rx(ProtoViewApp* app) { furi_assert(app); if(!furi_hal_subghz_is_frequency_valid(app->frequency)) { - furi_crash(TAG " Incorrect RX frequency."); + furi_crash(TAG" Incorrect RX frequency."); } - if(app->txrx->txrx_state == TxRxStateRx) return app->frequency; + if (app->txrx->txrx_state == TxRxStateRx) return app->frequency; furi_hal_subghz_idle(); /* Put it into idle state in case it is sleeping. */ uint32_t value = furi_hal_subghz_set_frequency_and_path(app->frequency); @@ -60,8 +61,10 @@ uint32_t radio_rx(ProtoViewApp* app) { furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); 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); + if (!app->txrx->debug_timer_sampling) { + + 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); @@ -73,8 +76,8 @@ uint32_t radio_rx(ProtoViewApp* app) { /* Stop subghz worker (if active), put radio on idle state. */ void radio_rx_end(ProtoViewApp* app) { furi_assert(app); - if(app->txrx->txrx_state == TxRxStateRx) { - if(!app->txrx->debug_timer_sampling) { + if (app->txrx->txrx_state == TxRxStateRx) { + if (!app->txrx->debug_timer_sampling) { if(subghz_worker_is_running(app->txrx->worker)) { subghz_worker_stop(app->txrx->worker); furi_hal_subghz_stop_async_rx(); @@ -90,7 +93,7 @@ void radio_rx_end(ProtoViewApp* app) { /* Put radio on sleep. */ void radio_sleep(ProtoViewApp* app) { furi_assert(app); - if(app->txrx->txrx_state == TxRxStateRx) { + if (app->txrx->txrx_state == TxRxStateRx) { /* We can't go from having an active RX worker to sleeping. * Stop the RX subsystems first. */ radio_rx_end(app); @@ -106,15 +109,15 @@ void radio_sleep(ProtoViewApp* app) { * Flipper system. * ===========================================================================*/ -void protoview_timer_isr(void* ctx) { - ProtoViewApp* app = ctx; +void protoview_timer_isr(void *ctx) { + ProtoViewApp *app = ctx; bool level = furi_hal_gpio_read(&gpio_cc1101_g0); - if(app->txrx->last_g0_value != level) { + if (app->txrx->last_g0_value != level) { uint32_t now = DWT->CYCCNT; uint32_t dur = now - app->txrx->last_g0_change_time; dur /= furi_hal_cortex_instructions_per_microsecond(); - if(dur > 15000) dur = 15000; + if (dur > 15000) dur = 15000; raw_samples_add(RawSamples, app->txrx->last_g0_value, dur); app->txrx->last_g0_value = level; app->txrx->last_g0_change_time = now; @@ -122,13 +125,13 @@ void protoview_timer_isr(void* ctx) { LL_TIM_ClearFlag_UPDATE(TIM2); } -void raw_sampling_worker_start(ProtoViewApp* app) { +void raw_sampling_worker_start(ProtoViewApp *app) { UNUSED(app); LL_TIM_InitTypeDef tim_init = { - .Prescaler = 63, /* CPU frequency is ~64Mhz. */ + .Prescaler = 63, /* CPU frequency is ~64Mhz. */ .CounterMode = LL_TIM_COUNTERMODE_UP, - .Autoreload = 5, /* Sample every 5 us */ + .Autoreload = 5, /* Sample every 5 us */ }; LL_TIM_Init(TIM2, &tim_init); @@ -141,7 +144,7 @@ void raw_sampling_worker_start(ProtoViewApp* app) { FURI_LOG_E(TAG, "Timer enabled"); } -void raw_sampling_worker_stop(ProtoViewApp* app) { +void raw_sampling_worker_stop(ProtoViewApp *app) { UNUSED(app); FURI_CRITICAL_ENTER(); LL_TIM_DisableCounter(TIM2); diff --git a/applications/plugins/protoview/crc.c b/applications/plugins/protoview/crc.c index 94d482972..38a809e10 100644 --- a/applications/plugins/protoview/crc.c +++ b/applications/plugins/protoview/crc.c @@ -3,13 +3,14 @@ /* 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++) { + for (i = 0; i < len; i++) { crc ^= data[i]; - for(j = 0; j < 8; j++) { - if((crc & 0x80) != 0) + for (j = 0; j < 8; j++) { + if ((crc & 0x80) != 0) crc = (uint8_t)((crc << 1) ^ poly); else crc <<= 1; diff --git a/applications/plugins/protoview/custom_presets.h b/applications/plugins/protoview/custom_presets.h index 804c4e629..713827d22 100644 --- a/applications/plugins/protoview/custom_presets.h +++ b/applications/plugins/protoview/custom_presets.h @@ -1,8 +1,8 @@ #include -/* This is how to configure registers MDMCFG3 and MDMCFG4. +/* ========================== DATA RATE SETTINGS =============================== * - * Data rate kBaud setting: + * This is how to configure registers MDMCFG3 and MDMCFG4. * * MDMCFG3 is the data rate mantissa, the exponent is in MDMCFG4, * last 4 bits of the register. @@ -15,6 +15,8 @@ * * ((256+34)*(2^12))/(2^28)*26000000 = 115051.2688000000, that is 115KBaud * + * ============================ BANDWIDTH FILTER =============================== + * * Bandwidth filter setting: * * BW filter as just 16 possibilities depending on how the first nibble @@ -39,6 +41,8 @@ * e 68 khz * f 58 khz * + * ============================== FSK DEVIATION ================================ + * * FSK deviation is controlled by the DEVIATION register. In Ruby: * * dev = (26000000.0/2**17)*(8+(deviation&7))*(2**(deviation>>4&7)) @@ -58,7 +62,7 @@ */ /* 20 KBaud, 2FSK, 28.56 kHz deviation, 325 Khz bandwidth filter. */ -static uint8_t protoview_subghz_tpms1_async_regs[][2] = { +static uint8_t protoview_subghz_tpms1_fsk_async_regs[][2] = { /* GPIO GD0 */ {CC1101_IOCFG0, 0x0D}, // GD0 as async serial data output/input @@ -72,8 +76,7 @@ static uint8_t protoview_subghz_tpms1_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 @@ -103,8 +106,55 @@ static uint8_t protoview_subghz_tpms1_async_regs[][2] = { {0, 0}, }; -/* 40 KBaud, 2FSK, 19 kHz deviation, 102 Khz bandwidth filter. */ -static uint8_t protoview_subghz_tpms2_async_regs[][2] = { +/* This is like the default Flipper OOK 640Khz bandwidth preset, but + * the bandwidth is changed to 10kBaud to accomodate TPMS frequency. */ +static const uint8_t protoview_subghz_tpms2_ook_async_regs[][2] = { + /* GPIO GD0 */ + {CC1101_IOCFG0, 0x0D}, // GD0 as async serial data output/input + + /* FIFO and internals */ + {CC1101_FIFOTHR, 0x07}, // The only important bit is ADC_RETENTION + + /* Packet engine */ + {CC1101_PKTCTRL0, 0x32}, // Async, continious, no whitening + + /* Frequency Synthesizer Control */ + {CC1101_FSCTRL1, 0x06}, // IF = (26*10^6) / (2^10) * 0x06 = 152343.75Hz + + // Modem Configuration + {CC1101_MDMCFG0, 0x00}, // Channel spacing is 25kHz + {CC1101_MDMCFG1, 0x00}, // Channel spacing is 25kHz + {CC1101_MDMCFG2, 0x30}, // Format ASK/OOK, No preamble/sync + {CC1101_MDMCFG3, 0x93}, // Data rate is 10kBaud + {CC1101_MDMCFG4, 0x18}, // Rx BW filter is 650.000kHz + + /* Main Radio Control State Machine */ + {CC1101_MCSM0, 0x18}, // Autocalibrate on idle-to-rx/tx, PO_TIMEOUT is 64 cycles(149-155us) + + /* Frequency Offset Compensation Configuration */ + {CC1101_FOCCFG, + 0x18}, // no frequency offset compensation, POST_K same as PRE_K, PRE_K is 4K, GATE is off + + /* Automatic Gain Control */ + {CC1101_AGCCTRL0, + 0x91}, // 10 - Medium hysteresis, medium asymmetric dead zone, medium gain ; 01 - 16 samples agc; 00 - Normal AGC, 01 - 8dB boundary + {CC1101_AGCCTRL1, + 0x0}, // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 0000 - RSSI to MAIN_TARGET + {CC1101_AGCCTRL2, 0x07}, // 00 - DVGA all; 000 - MAX LNA+LNA2; 111 - MAIN_TARGET 42 dB + + /* Wake on radio and timeouts control */ + {CC1101_WORCTRL, 0xFB}, // WOR_RES is 2^15 periods (0.91 - 0.94 s) 16.5 - 17.2 hours + + /* Frontend configuration */ + {CC1101_FREND0, 0x11}, // Adjusts current TX LO buffer + high is PATABLE[1] + {CC1101_FREND1, 0xB6}, // + + /* End */ + {0, 0}, +}; + +/* 40 KBaud, 2FSK, 28 kHz deviation, 270 Khz bandwidth filter. */ +static uint8_t protoview_subghz_tpms3_fsk_async_regs[][2] = { /* GPIO GD0 */ {CC1101_IOCFG0, 0x0D}, // GD0 as async serial data output/input @@ -118,11 +168,10 @@ static uint8_t protoview_subghz_tpms2_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 19.042 kHz + {CC1101_DEVIATN, 0x41}, // Deviation 28kHz /* Main Radio Control State Machine */ {CC1101_MCSM0, 0x18}, // Autocalibrate on idle-to-rx/tx, PO_TIMEOUT is 64 cycles(149-155us) @@ -149,8 +198,8 @@ static uint8_t protoview_subghz_tpms2_async_regs[][2] = { {0, 0}, }; -/* Parameters that should work well for the TPMS PVM C210 sensor. */ -static uint8_t protoview_subghz_tpms3_async_regs[][2] = { +/* FSK 19k dev, 325 Khz filter, 20kBaud. Works well with Toyota. */ +static uint8_t protoview_subghz_tpms4_fsk_async_regs[][2] = { /* GPIO GD0 */ {CC1101_IOCFG0, 0x0D}, // GD0 as async serial data output/input @@ -167,7 +216,7 @@ static uint8_t protoview_subghz_tpms3_async_regs[][2] = { {CC1101_MDMCFG2, 0x10}, // GFSK without any other check {CC1101_MDMCFG3, 0x93}, // Data rate is 20kBaud {CC1101_MDMCFG4, 0x59}, // Rx bandwidth filter is 325 kHz - {CC1101_DEVIATN, 0x34}, // Deviation 19.04 Khz, works well with TPMS + {CC1101_DEVIATN, 0x34}, // Deviation 19.04 Khz. /* Main Radio Control State Machine */ {CC1101_MCSM0, 0x18}, // Autocalibrate on idle-to-rx/tx, PO_TIMEOUT is 64 cycles(149-155us) @@ -191,3 +240,5 @@ static uint8_t protoview_subghz_tpms3_async_regs[][2] = { /* End */ {0, 0}, }; + + diff --git a/applications/plugins/protoview/data_feed.c b/applications/plugins/protoview/data_feed.c index 686bd356e..c3387fc4c 100644 --- a/applications/plugins/protoview/data_feed.c +++ b/applications/plugins/protoview/data_feed.c @@ -14,7 +14,7 @@ const SubGhzProtocol subghz_protocol_protoview; /* The feed() method puts data in the RawSamples global (protected by * a mutex). */ -extern RawSamplesBuffer* RawSamples; +extern RawSamplesBuffer *RawSamples; /* This is totally dummy: we just define the decoder base for the async * system to work but we don't really use it if not to collect raw @@ -26,7 +26,8 @@ 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; } @@ -65,7 +66,8 @@ 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); @@ -73,13 +75,15 @@ 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"); } @@ -112,4 +116,5 @@ 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) +}; diff --git a/applications/plugins/protoview/protocols/b4b1.c b/applications/plugins/protoview/protocols/b4b1.c index 234b0158e..2bed2ac32 100644 --- a/applications/plugins/protoview/protocols/b4b1.c +++ b/applications/plugins/protoview/protocols/b4b1.c @@ -9,9 +9,9 @@ #include "../app.h" -static bool decode(uint8_t* bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo* info) { - if(numbits < 30) return false; - const char* sync_patterns[3] = { +static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo *info) { + if (numbits < 30) return false; + const char *sync_patterns[3] = { "10000000000000000000000000000001", /* 30 zero bits. */ "100000000000000000000000000000001", /* 31 zero bits. */ "1000000000000000000000000000000001", /* 32 zero bits. */ @@ -19,23 +19,26 @@ static bool decode(uint8_t* bits, uint32_t numbytes, uint32_t numbits, ProtoView uint32_t off; int j; - for(j = 0; j < 3; j++) { - off = bitmap_seek_bits(bits, numbytes, 0, numbits, sync_patterns[j]); - if(off != BITMAP_SEEK_NOT_FOUND) break; + for (j = 0; j < 3; j++) { + off = bitmap_seek_bits(bits,numbytes,0,numbits,sync_patterns[j]); + if (off != BITMAP_SEEK_NOT_FOUND) break; } - if(off == BITMAP_SEEK_NOT_FOUND) return false; - if(DEBUG_MSG) FURI_LOG_E(TAG, "B4B1 preamble at: %lu", off); - off += strlen(sync_patterns[j]) - 1; + if (off == BITMAP_SEEK_NOT_FOUND) return false; + if (DEBUG_MSG) FURI_LOG_E(TAG, "B4B1 preamble at: %lu",off); + 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; - snprintf(info->name, PROTOVIEW_MSG_STR_LEN, "PT/SC remote"); - snprintf(info->raw, PROTOVIEW_MSG_STR_LEN, "%02X%02X%02X", d[0], d[1], d[2]); - info->len = off + (4 * 24); + if (DEBUG_MSG) FURI_LOG_E(TAG, "B4B1 decoded: %lu",decoded); + if (decoded != 24) return false; + snprintf(info->name,PROTOVIEW_MSG_STR_LEN,"PT/SC remote"); + snprintf(info->raw,PROTOVIEW_MSG_STR_LEN,"%02X%02X%02X",d[0],d[1],d[2]); + info->len = off+(4*24); return true; } -ProtoViewDecoder B4B1Decoder = {"B4B1", decode}; +ProtoViewDecoder B4B1Decoder = { + "B4B1", decode +}; diff --git a/applications/plugins/protoview/protocols/citroen_tpms.c b/applications/plugins/protoview/protocols/citroen_tpms.c deleted file mode 100644 index 8831c22f4..000000000 --- a/applications/plugins/protoview/protocols/citroen_tpms.c +++ /dev/null @@ -1,72 +0,0 @@ -/* Citroen TPMS. Usually 443.92 Mhz FSK. - * - * Preamble of ~14 high/low 52 us pulses - * Sync of high 100us pulse then 50us low - * Then Manchester bits, 10 bytes total. - * Simple XOR checksum. */ - -#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. */ - uint32_t sync_len = 17; - const char* sync_pattern = "10101010101010110"; - if(numbits - sync_len < 8 * 10) return false; /* Expect 10 bytes. */ - - uint64_t off = bitmap_seek_bits(bits, numbytes, 0, numbits, sync_pattern); - if(off == BITMAP_SEEK_NOT_FOUND) return false; - FURI_LOG_E(TAG, "Renault TPMS preamble+sync found"); - - 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. */ - FURI_LOG_E(TAG, "Citroen TPMS decoded bits: %lu", decoded); - - if(decoded < 8 * 10) return false; /* Require the full 10 bytes. */ - - /* Check the CRC. It's a simple XOR of bytes 1-9, the first byte - * is not included. The meaning of the first byte is unknown and - * we don't display it. */ - uint8_t crc = 0; - for(int j = 1; j < 10; j++) crc ^= raw[j]; - if(crc != 0) return false; /* Require sane checksum. */ - - int repeat = raw[5] & 0xf; - float kpa = (float)raw[6] * 1.364; - int temp = raw[7] - 50; - 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), - "%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]); - 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}; diff --git a/applications/plugins/protoview/protocols/ford_tpms.c b/applications/plugins/protoview/protocols/ford_tpms.c deleted file mode 100644 index 9d3e66ff8..000000000 --- a/applications/plugins/protoview/protocols/ford_tpms.c +++ /dev/null @@ -1,76 +0,0 @@ -/* Ford tires TPMS. Usually 443.92 Mhz FSK (in Europe). - * - * 52 us short pules - * Preamble: 0101010101010101010101010101 - * Sync: 0110 (that is 52 us gap + 104 us pulse + 52 us gap) - * Data: 8 bytes Manchester encoded - * 01 = zero - * 10 = one - */ - -#include "../app.h" - -static bool decode(uint8_t* bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo* info) { - 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; - - uint64_t off = bitmap_seek_bits(bits, numbytes, 0, numbits, sync_pattern); - if(off == BITMAP_SEEK_NOT_FOUND) return false; - FURI_LOG_E(TAG, "Fort TPMS preamble+sync found"); - - 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. */ - FURI_LOG_E(TAG, "Ford TPMS decoded bits: %lu", decoded); - - if(decoded < 8 * 8) return false; /* Require the full 8 bytes. */ - - /* CRC is just the sum of the first 7 bytes MOD 256. */ - uint8_t crc = 0; - for(int j = 0; j < 7; j++) crc += raw[j]; - if(crc != raw[7]) return false; /* Require sane CRC. */ - - float psi = 0.25 * (((raw[6] & 0x20) << 3) | raw[4]); - - /* Temperature apperas to be valid only if the most significant - * bit of the value is not set. Otherwise its meaning is unknown. - * Likely useful to alternatively send temperature or other info. */ - int temp = raw[5] & 0x80 ? 0 : raw[5] - 56; - int flags = raw[5] & 0x7f; - 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->info2, sizeof(info->info2), "Pressure %.2f psi", (double)psi); - if(temp) - snprintf(info->info3, sizeof(info->info3), "Temperature %d C", temp); - else - snprintf(info->info3, sizeof(info->info3), "Flags %d", flags); - snprintf(info->info4, sizeof(info->info4), "Moving %s", car_moving ? "yes" : "no"); - return true; -} - -ProtoViewDecoder FordTPMSDecoder = {"Ford TPMS", decode}; diff --git a/applications/plugins/protoview/protocols/keeloq.c b/applications/plugins/protoview/protocols/keeloq.c new file mode 100644 index 000000000..cf0b7682f --- /dev/null +++ b/applications/plugins/protoview/protocols/keeloq.c @@ -0,0 +1,87 @@ +/* Microchip HCS200/HCS300/HSC301 KeeLoq, rolling code remotes. + * + * Usually 443.92 Mhz OOK, ~200us or ~400us pulse len, depending + * on the configuration. + * + * Preamble: 12 pairs of alternating pulse/gap. + * Sync: long gap of around 10 times the duration of the short-pulse. + * Data: pulse width encoded data. Each bit takes three cycles: + * + * 0 = 110 + * 1 = 100 + * + * There are a total of 66 bits transmitted. + * 0..31: 32 bits of encrypted rolling code. + * 32..59: Remote ID, 28 bits + * 60..63: Buttons pressed + * 64..64: Low battery if set + * 65..65: Always set to 1 + * + * Bits in bytes are inverted: least significant bit is first. + * For some reason there is no checksum whatsoever, so we only decode + * if we find everything well formed. + */ + +#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"; + uint8_t sync_len = 24+4; + if (numbits-sync_len+sync_len < 3*66) return false; + uint64_t off = bitmap_seek_bits(bits,numbytes,0,numbits,sync_pattern); + if (off == BITMAP_SEEK_NOT_FOUND) return false; + off += sync_len; + + /* Now there is half the gap left, but we allow from 3 to 7, instead of 5 + * 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++; + 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. */ + FURI_LOG_E(TAG, "Keeloq decoded bits: %lu", decoded); + + if (decoded < 66) return false; /* Require the full 66 bits. */ + bitmap_reverse_bytes(raw,sizeof(raw)); /* Keeloq is LSB first. */ + + int buttons = raw[7]>>4; + int s3 = (buttons&1) != 0; + int s0 = (buttons&2) != 0; + 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 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->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"); + return true; +} + +ProtoViewDecoder KeeloqDecoder = { + "Keeloq", decode +}; diff --git a/applications/plugins/protoview/protocols/oregon2.c b/applications/plugins/protoview/protocols/oregon2.c index fbb87d3be..3aa57c72d 100644 --- a/applications/plugins/protoview/protocols/oregon2.c +++ b/applications/plugins/protoview/protocols/oregon2.c @@ -6,84 +6,60 @@ #include "../app.h" -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"; - uint64_t off = bitmap_seek_bits(bits, numbytes, 0, numbits, sync_pattern); - if(off == BITMAP_SEEK_NOT_FOUND) return false; +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"; + 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"); off += 32; /* Skip preamble. */ uint8_t buffer[8], raw[8] = {0}; uint32_t decoded = - convert_from_line_code(buffer, sizeof(buffer), bits, numbytes, off, "1001", "0110"); + convert_from_line_code(buffer,sizeof(buffer),bits,numbytes,off,"1001","0110"); FURI_LOG_E(TAG, "Oregon2 decoded bits: %lu", decoded); - if(decoded < 11 * 4) return false; /* Minimum len to extract some data. */ + if (decoded < 11*4) return false; /* Minimum len to extract some data. */ char temp[3] = {0}, deviceid[2] = {0}, hum[2] = {0}; - for(int j = 0; j < 64; j += 4) { + 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); - 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; + 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; /* 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"); + 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 +}; diff --git a/applications/plugins/protoview/protocols/oregon2.txt b/applications/plugins/protoview/protocols/oregon2.txt deleted file mode 100644 index 362631431..000000000 --- a/applications/plugins/protoview/protocols/oregon2.txt +++ /dev/null @@ -1,6 +0,0 @@ -11001100110011001100110011001100110011001100110011001100110 (Preamble) -10 01 01 10 10 01 01 10 (Sync) -01 10 10 01 10 01 10 01 01 10 10 01 01 10 01 10 10 01 01 10 10 01 10 01 10 01 10 01 10 01 10 01 01 10 10 01 10 01 10 01 01 10 01 10 01 10 01 10 01 10 01 10 10 01 01 10 01 10 10 01 10 01 10 01 10 01 10 01 01 10 10 01 10 01 01 10 01 10 10 01 01 10 10 01 10 01 10 01 10 01 10 01 10 01 11 0 - -We need to seek the following bytes: 01100110 01100110 10010110 10010110 - 0x66 0x66 96 96 diff --git a/applications/plugins/protoview/protocols/renault_tpms.c b/applications/plugins/protoview/protocols/renault_tpms.c deleted file mode 100644 index b876d2731..000000000 --- a/applications/plugins/protoview/protocols/renault_tpms.c +++ /dev/null @@ -1,71 +0,0 @@ -/* Renault tires TPMS. Usually 443.92 Mhz FSK. - * - * Preamble + sync + Manchester bits. ~48us short pulse. - * 9 Bytes in total not counting the preamble. */ - -#include "../app.h" - -#define USE_TEST_VECTOR 0 -static const char* test_vector = - "...01010101010101010110" // Preamble + sync - - /* The following is Marshal encoded, so each two characters are - * actaully one bit. 01 = 0, 10 = 1. */ - "010110010110" // Flags. - "10011001101010011001" // Pressure, multiply by 0.75 to obtain kpa. - // 244 kpa here. - "1010010110011010" // Temperature, subtract 30 to obtain celsius. 22C here. - "1001010101101001" - "0101100110010101" - "1001010101100110" // Tire ID. 0x7AD779 here. - "0101010101010101" - "0101010101010101" // Two FF bytes (usually). Unknown. - "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, test_vector); - numbits = strlen(test_vector); - } - - if(numbits - 12 < 9 * 8) return false; - - const char* sync_pattern = "01010101010101010110"; - uint64_t off = bitmap_seek_bits(bits, numbytes, 0, numbits, sync_pattern); - if(off == BITMAP_SEEK_NOT_FOUND) return false; - FURI_LOG_E(TAG, "Renault TPMS preamble+sync found"); - - off += 20; /* Skip preamble. */ - - uint8_t raw[9]; - 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. */ - if(crc8(raw, 8, 0, 7) != raw[8]) return false; /* Require sane CRC. */ - - float kpa = 0.75 * ((uint32_t)((raw[0] & 3) << 8) | raw[1]); - 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->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}; diff --git a/applications/plugins/protoview/protocols/schrader_tpms.c b/applications/plugins/protoview/protocols/schrader_tpms.c deleted file mode 100644 index cb2cf52c4..000000000 --- a/applications/plugins/protoview/protocols/schrader_tpms.c +++ /dev/null @@ -1,78 +0,0 @@ -/* Schrader TPMS. Usually 443.92 Mhz OOK, 120us pulse len. - * - * 500us high pulse + Preamble + Manchester coded bits where: - * 1 = 10 - * 0 = 01 - * - * 60 bits of data total (first 4 nibbles is the preamble, 0xF). - * - * Used in FIAT-Chrysler, Mercedes, ... */ - -#include "../app.h" - -#define USE_TEST_VECTOR 0 -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, test_vector); - numbits = strlen(test_vector); - } - - if(numbits < 64) return false; /* Preamble + data. */ - - 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"); - - off += 10; /* Skip just the long pulse and the first 3 bits of sync, so - that we have the first byte of data with the sync nibble - 0011 = 0x3. */ - - uint8_t raw[8]; - 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. */ - - raw[0] |= 0xf0; // Fix the preamble nibble for checksum computation. - uint8_t cksum = crc8(raw, sizeof(raw) - 1, 0xf0, 0x7); - if(cksum != raw[7]) { - FURI_LOG_E(TAG, "Schrader TPMS checksum mismatch"); - return false; - } - - float kpa = (float)raw[5] * 2.5; - 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->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}; diff --git a/applications/plugins/protoview/protocols/toyota_tpms.c b/applications/plugins/protoview/protocols/toyota_tpms.c deleted file mode 100644 index 4e2376b96..000000000 --- a/applications/plugins/protoview/protocols/toyota_tpms.c +++ /dev/null @@ -1,84 +0,0 @@ -/* Toyota tires TPMS. Usually 443.92 Mhz FSK (In Europe). - * - * Preamble + sync + 64 bits of data. ~48us short pulse length. - * - * The preamble + sync is something like: - * - * 10101010101 (preamble) + 001111[1] (sync) - * - * Note: the final [1] means that sometimes it is four 1s, sometimes - * five, depending on the short pulse length detection and the exact - * duration of the high long pulse. After the sync, a differential - * Manchester encoded payload follows. However the Flipper's CC1101 - * often can't decode correctly the initial alternating pattern 101010101, - * so what we do is to seek just the sync, that is "001111" or "0011111", - * however we now that it must be followed by one differenitally encoded - * bit, so we can use also the first bit of data to force a more robust - * detection, and look for one of the following: - * - * [001111]00 - * [0011111]00 - * [001111]01 - * [0011111]01 - */ - -#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 - is two symbols in the bitmap). */ - - char* sync[] = {"00111100", "001111100", "00111101", "001111101", NULL}; - - int j; - uint32_t off = 0; - for(j = 0; sync[j]; j++) { - off = bitmap_seek_bits(bits, numbytes, 0, numbits, sync[j]); - if(off != BITMAP_SEEK_NOT_FOUND) { - off += strlen(sync[j]) - 2; - break; - } - } - if(off == BITMAP_SEEK_NOT_FOUND) return false; - - 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); - FURI_LOG_E(TAG, "Toyota TPMS decoded bits: %lu", decoded); - - if(decoded < 8 * 9) return false; /* Require the full 8 bytes. */ - if(crc8(raw, 8, 0x80, 7) != raw[8]) return false; /* Require sane CRC. */ - - float kpa = (float)((raw[4] & 0x7f) << 1 | raw[5] >> 7) * 0.25 - 7; - 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->info1, sizeof(info->info1), "Pressure %.2f psi", (double)kpa); - snprintf(info->info2, sizeof(info->info2), "Temperature %d C", temp); - return true; -} - -ProtoViewDecoder ToyotaTPMSDecoder = {"Toyota TPMS", decode}; diff --git a/applications/plugins/protoview/protocols/tpms/citroen.c b/applications/plugins/protoview/protocols/tpms/citroen.c new file mode 100644 index 000000000..809dc0a25 --- /dev/null +++ b/applications/plugins/protoview/protocols/tpms/citroen.c @@ -0,0 +1,60 @@ +/* Citroen TPMS. Usually 443.92 Mhz FSK. + * + * Preamble of ~14 high/low 52 us pulses + * Sync of high 100us pulse then 50us low + * Then Manchester bits, 10 bytes total. + * Simple XOR checksum. */ + +#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. */ + uint32_t sync_len = 17; + const char *sync_pattern = "10101010101010110"; + if (numbits-sync_len < 8*10) return false; /* Expect 10 bytes. */ + + uint64_t off = bitmap_seek_bits(bits,numbytes,0,numbits,sync_pattern); + if (off == BITMAP_SEEK_NOT_FOUND) return false; + FURI_LOG_E(TAG, "Renault TPMS preamble+sync found"); + + 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. */ + FURI_LOG_E(TAG, "Citroen TPMS decoded bits: %lu", decoded); + + if (decoded < 8*10) return false; /* Require the full 10 bytes. */ + + /* Check the CRC. It's a simple XOR of bytes 1-9, the first byte + * is not included. The meaning of the first byte is unknown and + * we don't display it. */ + uint8_t crc = 0; + for (int j = 1; j < 10; j++) crc ^= raw[j]; + if (crc != 0) return false; /* Require sane checksum. */ + + int repeat = raw[5] & 0xf; + float kpa = (float)raw[6]*1.364; + int temp = raw[7]-50; + 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), + "%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]); + 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 +}; diff --git a/applications/plugins/protoview/protocols/tpms/ford.c b/applications/plugins/protoview/protocols/tpms/ford.c new file mode 100644 index 000000000..a9c687075 --- /dev/null +++ b/applications/plugins/protoview/protocols/tpms/ford.c @@ -0,0 +1,64 @@ +/* Ford tires TPMS. Usually 443.92 Mhz FSK (in Europe). + * + * 52 us short pules + * Preamble: 0101010101010101010101010101 + * Sync: 0110 (that is 52 us gap + 104 us pulse + 52 us gap) + * Data: 8 bytes Manchester encoded + * 01 = zero + * 10 = one + */ + +#include "../../app.h" + +static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo *info) { + + 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; + + uint64_t off = bitmap_seek_bits(bits,numbytes,0,numbits,sync_pattern); + if (off == BITMAP_SEEK_NOT_FOUND) return false; + FURI_LOG_E(TAG, "Fort TPMS preamble+sync found"); + + 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. */ + FURI_LOG_E(TAG, "Ford TPMS decoded bits: %lu", decoded); + + if (decoded < 8*8) return false; /* Require the full 8 bytes. */ + + /* CRC is just the sum of the first 7 bytes MOD 256. */ + uint8_t crc = 0; + for (int j = 0; j < 7; j++) crc += raw[j]; + if (crc != raw[7]) return false; /* Require sane CRC. */ + + float psi = 0.25 * (((raw[6]&0x20)<<3)|raw[4]); + + /* Temperature apperas to be valid only if the most significant + * bit of the value is not set. Otherwise its meaning is unknown. + * Likely useful to alternatively send temperature or other info. */ + int temp = raw[5] & 0x80 ? 0 : raw[5]-56; + int flags = raw[5] & 0x7f; + 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->info2,sizeof(info->info2),"Pressure %.2f psi", (double)psi); + if (temp) + snprintf(info->info3,sizeof(info->info3),"Temperature %d C", temp); + else + snprintf(info->info3,sizeof(info->info3),"Flags %d", flags); + snprintf(info->info4,sizeof(info->info4),"Moving %s", car_moving ? "yes" : "no"); + return true; +} + +ProtoViewDecoder FordTPMSDecoder = { + "Ford TPMS", decode +}; diff --git a/applications/plugins/protoview/protocols/tpms/renault.c b/applications/plugins/protoview/protocols/tpms/renault.c new file mode 100644 index 000000000..4bbe55e42 --- /dev/null +++ b/applications/plugins/protoview/protocols/tpms/renault.c @@ -0,0 +1,66 @@ +/* Renault tires TPMS. Usually 443.92 Mhz FSK. + * + * Preamble + sync + Manchester bits. ~48us short pulse. + * 9 Bytes in total not counting the preamble. */ + +#include "../../app.h" + +#define USE_TEST_VECTOR 0 +static const char *test_vector = + "...01010101010101010110" // Preamble + sync + + /* The following is Marshal encoded, so each two characters are + * actaully one bit. 01 = 0, 10 = 1. */ + "010110010110" // Flags. + "10011001101010011001" // Pressure, multiply by 0.75 to obtain kpa. + // 244 kpa here. + "1010010110011010" // Temperature, subtract 30 to obtain celsius. 22C here. + "1001010101101001" + "0101100110010101" + "1001010101100110" // Tire ID. 0x7AD779 here. + "0101010101010101" + "0101010101010101" // Two FF bytes (usually). Unknown. + "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,test_vector); + numbits = strlen(test_vector); + } + + if (numbits-12 < 9*8) return false; + + const char *sync_pattern = "01010101010101010110"; + uint64_t off = bitmap_seek_bits(bits,numbytes,0,numbits,sync_pattern); + if (off == BITMAP_SEEK_NOT_FOUND) return false; + FURI_LOG_E(TAG, "Renault TPMS preamble+sync found"); + + off += 20; /* Skip preamble. */ + + uint8_t raw[9]; + 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. */ + if (crc8(raw,8,0,7) != raw[8]) return false; /* Require sane CRC. */ + + float kpa = 0.75 *((uint32_t)((raw[0]&3)<<8) | raw[1]); + 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->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 +}; diff --git a/applications/plugins/protoview/protocols/tpms/schrader.c b/applications/plugins/protoview/protocols/tpms/schrader.c new file mode 100644 index 000000000..ab65a92d3 --- /dev/null +++ b/applications/plugins/protoview/protocols/tpms/schrader.c @@ -0,0 +1,65 @@ +/* Schrader TPMS. Usually 443.92 Mhz OOK, 120us pulse len. + * + * 500us high pulse + Preamble + Manchester coded bits where: + * 1 = 10 + * 0 = 01 + * + * 60 bits of data total (first 4 nibbles is the preamble, 0xF). + * + * Used in FIAT-Chrysler, Mercedes, ... */ + +#include "../../app.h" + +#define USE_TEST_VECTOR 0 +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,test_vector); + numbits = strlen(test_vector); + } + + if (numbits < 64) return false; /* Preamble + data. */ + + 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"); + + off += 10; /* Skip just the long pulse and the first 3 bits of sync, so + that we have the first byte of data with the sync nibble + 0011 = 0x3. */ + + uint8_t raw[8]; + 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. */ + + raw[0] |= 0xf0; // Fix the preamble nibble for checksum computation. + uint8_t cksum = crc8(raw,sizeof(raw)-1,0xf0,0x7); + if (cksum != raw[7]) { + FURI_LOG_E(TAG, "Schrader TPMS checksum mismatch"); + return false; + } + + float kpa = (float)raw[5]*2.5; + 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->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 +}; diff --git a/applications/plugins/protoview/protocols/tpms/schrader_eg53ma4.c b/applications/plugins/protoview/protocols/tpms/schrader_eg53ma4.c new file mode 100644 index 000000000..6fce40d64 --- /dev/null +++ b/applications/plugins/protoview/protocols/tpms/schrader_eg53ma4.c @@ -0,0 +1,63 @@ +/* Schrader variant EG53MA4 TPMS. + * Usually 443.92 Mhz OOK, 100us pulse len. + * + * Preamble: alternating pulse/gap, 100us. + * Sync (as pulses and gaps): "01100101", already part of the data stream + * (first nibble) corresponding to 0x4 + * + * A total of 10 bytes payload, Manchester encoded. + * + * 0 = 01 + * 1 = 10 + * + * Used in certain Open cars and others. + */ + +#include "../../app.h" + +static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo *info) { + + 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; + + 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 EG53MA4 TPMS preamble+sync found"); + + 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. */ + FURI_LOG_E(TAG, "Schrader EG53MA4 TPMS decoded bits: %lu", decoded); + + if (decoded < 10*8) return false; /* Require the full 10 bytes. */ + + /* CRC is just all bytes added mod 256. */ + uint8_t crc = 0; + for (int j = 0; j < 9; j++) crc += raw[j]; + if (crc != raw[9]) return false; /* Require sane CRC. */ + + /* To convert the raw pressure to kPa, RTL433 uses 2.5, but is likely + * wrong. Searching on Google for users experimenting with the value + * reported, the value appears to be 2.75. */ + float kpa = (float)raw[7]*2.75; + int temp_f = raw[8]; + 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->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 +}; diff --git a/applications/plugins/protoview/protocols/tpms/toyota.c b/applications/plugins/protoview/protocols/tpms/toyota.c new file mode 100644 index 000000000..b27353739 --- /dev/null +++ b/applications/plugins/protoview/protocols/tpms/toyota.c @@ -0,0 +1,77 @@ +/* Toyota tires TPMS. Usually 443.92 Mhz FSK (In Europe). + * + * Preamble + sync + 64 bits of data. ~48us short pulse length. + * + * The preamble + sync is something like: + * + * 10101010101 (preamble) + 001111[1] (sync) + * + * Note: the final [1] means that sometimes it is four 1s, sometimes + * five, depending on the short pulse length detection and the exact + * duration of the high long pulse. After the sync, a differential + * Manchester encoded payload follows. However the Flipper's CC1101 + * often can't decode correctly the initial alternating pattern 101010101, + * so what we do is to seek just the sync, that is "001111" or "0011111", + * however we now that it must be followed by one differenitally encoded + * bit, so we can use also the first symbol of data to force a more robust + * detection, and look for one of the following: + * + * [001111]00 + * [0011111]00 + * [001111]01 + * [0011111]01 + */ + +#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 + is two symbols in the bitmap). */ + + char *sync[] = { + "00111100", + "001111100", + "00111101", + "001111101", + NULL + }; + + int j; + uint32_t off = 0; + for (j = 0; sync[j]; j++) { + off = bitmap_seek_bits(bits,numbytes,0,numbits,sync[j]); + if (off != BITMAP_SEEK_NOT_FOUND) { + off += strlen(sync[j])-2; + break; + } + } + if (off == BITMAP_SEEK_NOT_FOUND) return false; + + 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); + FURI_LOG_E(TAG, "Toyota TPMS decoded bits: %lu", decoded); + + if (decoded < 8*9) return false; /* Require the full 8 bytes. */ + if (crc8(raw,8,0x80,7) != raw[8]) return false; /* Require sane CRC. */ + + float kpa = (float)((raw[4]&0x7f)<<1 | raw[5]>>7) * 0.25 - 7; + 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->info1,sizeof(info->info1),"Pressure %.2f psi", (double)kpa); + snprintf(info->info2,sizeof(info->info2),"Temperature %d C", temp); + return true; +} + +ProtoViewDecoder ToyotaTPMSDecoder = { + "Toyota TPMS", decode +}; diff --git a/applications/plugins/protoview/signal.c b/applications/plugins/protoview/signal.c index a06ec1d74..06e2197c2 100644 --- a/applications/plugins/protoview/signal.c +++ b/applications/plugins/protoview/signal.c @@ -3,8 +3,8 @@ #include "app.h" -bool decode_signal(RawSamplesBuffer* s, uint64_t len, ProtoViewMsgInfo* info); -void initialize_msg_info(ProtoViewMsgInfo* i); +bool decode_signal(RawSamplesBuffer *s, uint64_t len, ProtoViewMsgInfo *info); +void initialize_msg_info(ProtoViewMsgInfo *i); /* ============================================================================= * Raw signal detection @@ -17,7 +17,7 @@ uint32_t duration_delta(uint32_t a, uint32_t b) { } /* Reset the current signal, so that a new one can be detected. */ -void reset_current_signal(ProtoViewApp* app) { +void reset_current_signal(ProtoViewApp *app) { app->signal_bestlen = 0; app->signal_offset = 0; app->signal_decoded = false; @@ -38,47 +38,47 @@ void reset_current_signal(ProtoViewApp* app) { * For instance Oregon2 sensors, in the case of protocol 2.1 will send * pulses of ~400us (RF on) VS ~580us (RF off). */ #define SEARCH_CLASSES 3 -uint32_t search_coherent_signal(RawSamplesBuffer* s, uint32_t idx) { +uint32_t search_coherent_signal(RawSamplesBuffer *s, uint32_t idx) { struct { - uint32_t dur[2]; /* dur[0] = low, dur[1] = high */ - uint32_t count[2]; /* Associated observed frequency. */ + uint32_t dur[2]; /* dur[0] = low, dur[1] = high */ + uint32_t count[2]; /* Associated observed frequency. */ } classes[SEARCH_CLASSES]; - memset(classes, 0, sizeof(classes)); + memset(classes,0,sizeof(classes)); uint32_t minlen = 30, maxlen = 4000; /* Depends on data rate, here we allow for high and low. */ uint32_t len = 0; /* Observed len of coherent samples. */ s->short_pulse_dur = 0; - for(uint32_t j = idx; j < idx + 500; j++) { + for (uint32_t j = idx; j < idx+500; j++) { bool level; uint32_t dur; raw_samples_get(s, j, &level, &dur); - if(dur < minlen || dur > maxlen) break; /* return. */ + if (dur < minlen || dur > maxlen) break; /* return. */ /* Let's see if it matches a class we already have or if we * can populate a new (yet empty) class. */ uint32_t k; - for(k = 0; k < SEARCH_CLASSES; k++) { - if(classes[k].count[level] == 0) { + for (k = 0; k < SEARCH_CLASSES; k++) { + if (classes[k].count[level] == 0) { classes[k].dur[level] = dur; classes[k].count[level] = 1; break; /* Sample accepted. */ } else { uint32_t classavg = classes[k].dur[level]; uint32_t count = classes[k].count[level]; - uint32_t delta = duration_delta(dur, classavg); + uint32_t delta = duration_delta(dur,classavg); /* Is the difference in duration between this signal and * the class we are inspecting less than a given percentage? * If so, accept this signal. */ - if(delta < classavg / 5) { /* 100%/5 = 20%. */ + if (delta < classavg/5) { /* 100%/5 = 20%. */ /* It is useful to compute the average of the class * we are observing. We know how many samples we got so * far, so we can recompute the average easily. * By always having a better estimate of the pulse len * we can avoid missing next samples in case the first * observed samples are too off. */ - classavg = ((classavg * count) + dur) / (count + 1); + classavg = ((classavg * count) + dur) / (count+1); classes[k].dur[level] = classavg; classes[k].count[level]++; break; /* Sample accepted. */ @@ -86,7 +86,7 @@ uint32_t search_coherent_signal(RawSamplesBuffer* s, uint32_t idx) { } } - if(k == SEARCH_CLASSES) break; /* No match, return. */ + if (k == SEARCH_CLASSES) break; /* No match, return. */ /* If we are here, we accepted this sample. Try with the next * one. */ @@ -96,12 +96,14 @@ uint32_t search_coherent_signal(RawSamplesBuffer* s, uint32_t idx) { /* Update the buffer setting the shortest pulse we found * among the three classes. This will be used when scaling * for visualization. */ - uint32_t short_dur[2] = {0, 0}; - for(int j = 0; j < SEARCH_CLASSES; j++) { - 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]) { + uint32_t short_dur[2] = {0,0}; + for (int j = 0; j < SEARCH_CLASSES; j++) { + 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]) + { short_dur[level] = classes[j].dur[level]; } } @@ -110,9 +112,9 @@ uint32_t search_coherent_signal(RawSamplesBuffer* s, uint32_t idx) { /* Use the average between high and low short pulses duration. * Often they are a bit different, and using the average is more robust * when we do decoding sampling at short_pulse_dur intervals. */ - if(short_dur[0] == 0) short_dur[0] = short_dur[1]; - if(short_dur[1] == 0) short_dur[1] = short_dur[0]; - s->short_pulse_dur = (short_dur[0] + short_dur[1]) / 2; + if (short_dur[0] == 0) short_dur[0] = short_dur[1]; + if (short_dur[1] == 0) short_dur[1] = short_dur[0]; + s->short_pulse_dur = (short_dur[0]+short_dur[1])/2; return len; } @@ -121,56 +123,54 @@ uint32_t search_coherent_signal(RawSamplesBuffer* s, uint32_t idx) { * in order to find a coherent signal. If a signal that does not appear to * be just noise is found, it is set in DetectedSamples global signal * buffer, that is what is rendered on the screen. */ -void scan_for_signal(ProtoViewApp* app) { +void scan_for_signal(ProtoViewApp *app) { /* We need to work on a copy: the RawSamples buffer is populated * by the background thread receiving data. */ - RawSamplesBuffer* copy = raw_samples_alloc(); - raw_samples_copy(copy, RawSamples); + RawSamplesBuffer *copy = raw_samples_alloc(); + raw_samples_copy(copy,RawSamples); /* Try to seek on data that looks to have a regular high low high low * pattern. */ - uint32_t minlen = 18; /* Min run of coherent samples. With less + uint32_t minlen = 18; /* Min run of coherent samples. With less than a few samples it's very easy to mistake noise for signal. */ - ProtoViewMsgInfo* info = malloc(sizeof(ProtoViewMsgInfo)); + ProtoViewMsgInfo *info = malloc(sizeof(ProtoViewMsgInfo)); uint32_t i = 0; - while(i < copy->total - 1) { - uint32_t thislen = search_coherent_signal(copy, i); + while (i < copy->total-1) { + uint32_t thislen = search_coherent_signal(copy,i); /* For messages that are long enough, attempt decoding. */ - if(thislen > minlen) { + if (thislen > minlen) { initialize_msg_info(info); uint32_t saved_idx = copy->idx; /* Save index, see later. */ /* decode_signal() expects the detected signal to start * from index .*/ - raw_samples_center(copy, i); - bool decoded = decode_signal(copy, thislen, info); + raw_samples_center(copy,i); + bool decoded = decode_signal(copy,thislen,info); copy->idx = saved_idx; /* Restore the index as we are scanning the signal in the loop. */ /* 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)) + { app->signal_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); + 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); /* Adjust raw view scale if the signal has an high * data rate. */ - if(DetectedSamples->short_pulse_dur < 75) + if (DetectedSamples->short_pulse_dur < 75) app->us_scale = 10; - else if(DetectedSamples->short_pulse_dur < 145) + else if (DetectedSamples->short_pulse_dur < 145) app->us_scale = 30; } } @@ -196,43 +196,48 @@ void scan_for_signal(ProtoViewApp* app) { /* Set the 'bitpos' bit to value 'val', in the specified bitmap * 'b' of len 'blen'. * Out of range bits will silently be discarded. */ -void bitmap_set(uint8_t* b, uint32_t blen, uint32_t bitpos, bool val) { - uint32_t byte = bitpos / 8; - uint32_t bit = 7 - (bitpos & 7); - if(byte >= blen) return; - if(val) - b[byte] |= 1 << bit; +void bitmap_set(uint8_t *b, uint32_t blen, uint32_t bitpos, bool val) { + uint32_t byte = bitpos/8; + uint32_t bit = 7-(bitpos&7); + if (byte >= blen) return; + if (val) + b[byte] |= 1<= blen) return 0; - return (b[byte] & (1 << bit)) != 0; +bool bitmap_get(uint8_t *b, uint32_t blen, uint32_t bitpos) { + uint32_t byte = bitpos/8; + uint32_t bit = 7-(bitpos&7); + if (byte >= blen) return 0; + return (b[byte] & (1< 56781234 */ + b = (b&0xf0)>>4 | (b&0x0f)<<4; + /* Step 2: swap adjacent pairs : 56781234 -> 78563412 */ + b = (b&0xcc)>>2 | (b&0x33)<<2; + /* Step 3: swap adjacent bits : 78563412 -> 87654321 */ + b = (b&0xaa)>>1 | (b&0x55)<<1; + p[j] = b; } } /* Return true if the specified sequence of bits, provided as a string in the * form "11010110..." is found in the 'b' bitmap of 'blen' bits at 'bitpos' * position. */ -bool bitmap_match_bits(uint8_t* b, uint32_t blen, uint32_t bitpos, const char* bits) { - for(size_t j = 0; bits[j]; j++) { +bool bitmap_match_bits(uint8_t *b, uint32_t blen, uint32_t bitpos, const char *bits) { + for (size_t j = 0; bits[j]; j++) { bool expected = (bits[j] == '1') ? true : false; - if(bitmap_get(b, blen, bitpos + j) != expected) return false; + if (bitmap_get(b,blen,bitpos+j) != expected) return false; } return true; } @@ -245,17 +250,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 endpos = startpos + blen * 8; - uint32_t end2 = startpos + maxbits; - if(end2 < endpos) endpos = end2; - for(uint32_t j = startpos; j < endpos; j++) - if(bitmap_match_bits(b, blen, j, bits)) return j; +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; + for (uint32_t j = startpos; j < endpos; j++) + if (bitmap_match_bits(b,blen,j,bits)) return j; return BITMAP_SEEK_NOT_FOUND; } @@ -264,10 +264,10 @@ uint32_t bitmap_seek_bits( * This function is useful in order to set the test vectors in the protocol * decoders, to see if the decoding works regardless of the fact we are able * to actually receive a given signal. */ -void bitmap_set_pattern(uint8_t* b, uint32_t blen, const char* pat) { +void bitmap_set_pattern(uint8_t *b, uint32_t blen, const char *pat) { uint32_t i = 0; while(pat[i]) { - bitmap_set(b, blen, i, pat[i] == '1'); + bitmap_set(b,blen,i,pat[i] == '1'); i++; } } @@ -299,36 +299,31 @@ void bitmap_set_pattern(uint8_t* b, uint32_t blen, 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) { - if(rate == 0) return 0; /* We can't perform the conversion. */ +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++) { + for (uint32_t j = 0; j < count; j++) { uint32_t dur; bool level; - raw_samples_get(s, j + idx, &level, &dur); + raw_samples_get(s, j+idx, &level, &dur); uint32_t numbits = dur / rate; /* full bits that surely fit. */ - uint32_t rest = dur % rate; /* How much we are left with. */ - if(rest > rate / 2) numbits++; /* There is another one. */ + uint32_t rest = dur % rate; /* How much we are left with. */ + if (rest > rate/2) numbits++; /* There is another one. */ /* Limit how much a single sample can spawn. There are likely no * protocols doing such long pulses when the rate is low. */ - if(numbits > 1024) numbits = 1024; + 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); + 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); /* If the signal is too short, let's claim it an interference * and ignore it completely. */ - if(numbits == 0) continue; + if (numbits == 0) continue; - while(numbits--) bitmap_set(b, blen, bitpos++, level); + while(numbits--) bitmap_set(b,blen,bitpos++,level); } return bitpos; } @@ -345,29 +340,23 @@ uint32_t convert_signal_to_bits( * 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) { bool bitval; - if(bitmap_match_bits(bits, len, off, zero_pattern)) { + if (bitmap_match_bits(bits,len,off,zero_pattern)) { bitval = false; off += strlen(zero_pattern); - } else if(bitmap_match_bits(bits, len, off, one_pattern)) { + } else if (bitmap_match_bits(bits,len,off,one_pattern)) { bitval = true; off += strlen(one_pattern); } else { break; } - bitmap_set(buf, buflen, decoded++, bitval); - if(decoded / 8 == buflen) break; /* No space left on target buffer. */ + bitmap_set(buf,buflen,decoded++,bitval); + if (decoded/8 == buflen) break; /* No space left on target buffer. */ } return decoded; } @@ -378,22 +367,17 @@ uint32_t convert_from_line_code( * 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) { - bool b0 = bitmap_get(bits, len, j); - bool b1 = bitmap_get(bits, len, j + 1); - if(b0 == previous) break; /* Each new bit must switch value. */ - bitmap_set(buf, buflen, decoded++, b0 == b1); + for (uint32_t j = off; j < len; j += 2) { + bool b0 = bitmap_get(bits,len,j); + bool b1 = bitmap_get(bits,len,j+1); + if (b0 == previous) break; /* Each new bit must switch value. */ + bitmap_set(buf,buflen,decoded++,b0 == b1); previous = b1; - if(decoded / 8 == buflen) break; /* No space left on target buffer. */ + if (decoded/8 == buflen) break; /* No space left on target buffer. */ } return decoded; } @@ -406,52 +390,51 @@ extern ProtoViewDecoder B4B1Decoder; extern ProtoViewDecoder RenaultTPMSDecoder; extern ProtoViewDecoder ToyotaTPMSDecoder; extern ProtoViewDecoder SchraderTPMSDecoder; +extern ProtoViewDecoder SchraderEG53MA4TPMSDecoder; extern ProtoViewDecoder CitroenTPMSDecoder; extern ProtoViewDecoder FordTPMSDecoder; +extern ProtoViewDecoder KeeloqDecoder; -ProtoViewDecoder* Decoders[] = { - &Oregon2Decoder, /* Oregon sensors v2.1 protocol. */ - &B4B1Decoder, /* PT, SC, ... 24 bits remotes. */ - &RenaultTPMSDecoder, /* Renault TPMS. */ - &ToyotaTPMSDecoder, /* Toyota TPMS. */ - &SchraderTPMSDecoder, /* Schrader TPMS. */ - &CitroenTPMSDecoder, /* Citroen TPMS. */ - &FordTPMSDecoder, /* Ford TPMS. */ - NULL}; +ProtoViewDecoder *Decoders[] = { + &Oregon2Decoder, /* Oregon sensors v2.1 protocol. */ + &B4B1Decoder, /* PT, SC, ... 24 bits remotes. */ + &RenaultTPMSDecoder, /* Renault TPMS. */ + &ToyotaTPMSDecoder, /* Toyota TPMS. */ + &SchraderTPMSDecoder, /* Schrader TPMS. */ + &SchraderEG53MA4TPMSDecoder, /* Schrader EG53MA4 TPMS. */ + &CitroenTPMSDecoder, /* Citroen TPMS. */ + &FordTPMSDecoder, /* Ford TPMS. */ + &KeeloqDecoder, /* Keeloq remote. */ + NULL +}; /* Reset the message info structure before passing it to the decoding * functions. */ -void initialize_msg_info(ProtoViewMsgInfo* i) { - memset(i, 0, sizeof(ProtoViewMsgInfo)); +void initialize_msg_info(ProtoViewMsgInfo *i) { + memset(i,0,sizeof(ProtoViewMsgInfo)); } /* This function is called when a new signal is detected. It converts it * to a bitstream, and the calls the protocol specific functions for * decoding. If the signal was decoded correctly by some protocol, true * is returned. Otherwise false is returned. */ -bool decode_signal(RawSamplesBuffer* s, uint64_t len, ProtoViewMsgInfo* info) { - uint32_t bitmap_bits_size = 4096 * 8; - uint32_t bitmap_size = bitmap_bits_size / 8; +bool decode_signal(RawSamplesBuffer *s, uint64_t len, ProtoViewMsgInfo *info) { + uint32_t bitmap_bits_size = 4096*8; + uint32_t bitmap_size = bitmap_bits_size/8; /* We call the decoders with an offset a few samples before the actual * signal detected and for a len of a few bits after its end. */ uint32_t before_samples = 20; 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); + 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); - if(DEBUG_MSG) { /* Useful for debugging purposes. Don't remove. */ - char* str = malloc(1024); + if (DEBUG_MSG) { /* Useful for debugging purposes. Don't remove. */ + char *str = malloc(1024); uint32_t j; - for(j = 0; j < bits && j < 1023; j++) { - str[j] = bitmap_get(bitmap, bitmap_size, j) ? '1' : '0'; + for (j = 0; j < bits && j < 1023; j++) { + str[j] = bitmap_get(bitmap,bitmap_size,j) ? '1' : '0'; } str[j] = 0; FURI_LOG_E(TAG, "%lu bits sampled: %s", bits, str); @@ -464,25 +447,18 @@ bool decode_signal(RawSamplesBuffer* s, uint64_t len, ProtoViewMsgInfo* info) { bool decoded = false; while(Decoders[j]) { uint32_t start_time = furi_get_tick(); - decoded = Decoders[j]->decode(bitmap, bitmap_size, bits, info); + 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); - if(decoded) break; + FURI_LOG_E(TAG, "Decoder %s took %lu ms", + Decoders[j]->name, (unsigned long)delta); + if (decoded) break; j++; } - if(!decoded) { + 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); } free(bitmap); return decoded; diff --git a/applications/plugins/protoview/ui.c b/applications/plugins/protoview/ui.c index 2a974756b..e22e4d57e 100644 --- a/applications/plugins/protoview/ui.c +++ b/applications/plugins/protoview/ui.c @@ -3,24 +3,28 @@ #include "app.h" -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_draw_str(canvas,x,y,str); canvas_set_color(canvas, ColorBlack); } diff --git a/applications/plugins/protoview/view_direct_sampling.c b/applications/plugins/protoview/view_direct_sampling.c index 1ab90f096..251a289b8 100644 --- a/applications/plugins/protoview/view_direct_sampling.c +++ b/applications/plugins/protoview/view_direct_sampling.c @@ -7,46 +7,47 @@ /* Read directly from the G0 CC1101 pin, and draw a black or white * dot depending on the level. */ -void render_view_direct_sampling(Canvas* const canvas, ProtoViewApp* app) { - if(!app->direct_sampling_enabled) { +void render_view_direct_sampling(Canvas *const canvas, ProtoViewApp *app) { + if (!app->direct_sampling_enabled) { canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas, 2, 9, "Direct sampling is a special"); - canvas_draw_str(canvas, 2, 18, "mode that displays the signal"); - canvas_draw_str(canvas, 2, 27, "captured in real time. Like in"); - canvas_draw_str(canvas, 2, 36, "a old CRT TV. It's very slow."); - canvas_draw_str(canvas, 2, 45, "Can crash your Flipper."); + canvas_draw_str(canvas,2,9,"Direct sampling is a special"); + canvas_draw_str(canvas,2,18,"mode that displays the signal"); + canvas_draw_str(canvas,2,27,"captured in real time. Like in"); + canvas_draw_str(canvas,2,36,"a old CRT TV. It's very slow."); + canvas_draw_str(canvas,2,45,"Can crash your Flipper."); canvas_set_font(canvas, FontPrimary); - canvas_draw_str(canvas, 14, 60, "To enable press OK"); + canvas_draw_str(canvas,14,60,"To enable press OK"); return; } - for(int y = 0; y < 64; y++) { - for(int x = 0; x < 128; x++) { + for (int y = 0; y < 64; y++) { + for (int x = 0; x < 128; x++) { bool level = furi_hal_gpio_read(&gpio_cc1101_g0); - if(level) canvas_draw_dot(canvas, x, y); + if (level) canvas_draw_dot(canvas,x,y); /* 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 */ -void process_input_direct_sampling(ProtoViewApp* app, InputEvent input) { - if(input.type == InputTypePress && input.key == InputKeyOk) { +void process_input_direct_sampling(ProtoViewApp *app, InputEvent input) { + if (input.type == InputTypePress && input.key == InputKeyOk) { app->direct_sampling_enabled = !app->direct_sampling_enabled; } } /* 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) { +void view_enter_direct_sampling(ProtoViewApp *app) { + if (app->txrx->txrx_state == TxRxStateRx && + !app->txrx->debug_timer_sampling) + { subghz_worker_stop(app->txrx->worker); } else { raw_sampling_worker_stop(app); @@ -54,8 +55,10 @@ 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) { +void view_exit_direct_sampling(ProtoViewApp *app) { + if (app->txrx->txrx_state == TxRxStateRx && + !app->txrx->debug_timer_sampling) + { subghz_worker_start(app->txrx->worker); } else { raw_sampling_worker_start(app); diff --git a/applications/plugins/protoview/view_info.c b/applications/plugins/protoview/view_info.c index aeca36668..775c8abc2 100644 --- a/applications/plugins/protoview/view_info.c +++ b/applications/plugins/protoview/view_info.c @@ -4,10 +4,10 @@ #include "app.h" /* Renders the view with the detected message information. */ -void render_view_info(Canvas* const canvas, ProtoViewApp* app) { - if(app->signal_decoded == false) { +void render_view_info(Canvas *const canvas, ProtoViewApp *app) { + if (app->signal_decoded == false) { canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas, 30, 36, "No signal decoded"); + canvas_draw_str(canvas, 30,36,"No signal decoded"); return; } @@ -20,25 +20,21 @@ void render_view_info(Canvas* const canvas, ProtoViewApp* app) { /* Info fields. */ char buf[128]; canvas_set_font(canvas, FontSecondary); - if(app->signal_info.raw[0]) { - snprintf(buf, sizeof(buf), "Raw: %s", app->signal_info.raw); + if (app->signal_info.raw[0]) { + snprintf(buf,sizeof(buf),"Raw: %s", app->signal_info.raw); canvas_draw_str(canvas, 0, y, buf); y += lineheight; } - canvas_draw_str(canvas, 0, y, app->signal_info.info1); - y += lineheight; - canvas_draw_str(canvas, 0, y, app->signal_info.info2); - y += lineheight; - canvas_draw_str(canvas, 0, y, app->signal_info.info3); - y += lineheight; - canvas_draw_str(canvas, 0, y, app->signal_info.info4); - y += lineheight; + canvas_draw_str(canvas, 0, y, app->signal_info.info1); y += lineheight; + canvas_draw_str(canvas, 0, y, app->signal_info.info2); y += lineheight; + canvas_draw_str(canvas, 0, y, app->signal_info.info3); y += lineheight; + canvas_draw_str(canvas, 0, y, app->signal_info.info4); y += lineheight; } /* Handle input for the info view. */ -void process_input_info(ProtoViewApp* app, InputEvent input) { - if(input.type == InputTypeShort) { - if(input.key == InputKeyOk) { +void process_input_info(ProtoViewApp *app, InputEvent input) { + if (input.type == InputTypeShort) { + if (input.key == InputKeyOk) { /* Reset the current sample to capture the next. */ reset_current_signal(app); } diff --git a/applications/plugins/protoview/view_raw_signal.c b/applications/plugins/protoview/view_raw_signal.c index c20e3a69d..58d23e8ee 100644 --- a/applications/plugins/protoview/view_raw_signal.c +++ b/applications/plugins/protoview/view_raw_signal.c @@ -12,7 +12,7 @@ * * The 'idx' argument is the first sample to render in the circular * buffer. */ -void render_signal(ProtoViewApp* app, Canvas* const canvas, RawSamplesBuffer* buf, uint32_t idx) { +void render_signal(ProtoViewApp *app, Canvas *const canvas, RawSamplesBuffer *buf, uint32_t idx) { canvas_set_color(canvas, ColorBlack); int rows = 8; @@ -20,29 +20,31 @@ void render_signal(ProtoViewApp* app, Canvas* const canvas, RawSamplesBuffer* bu uint32_t start_idx = idx; bool level = 0; uint32_t dur = 0, sample_num = 0; - for(int row = 0; row < rows; row++) { - for(int x = 0; x < 128; x++) { - int y = 3 + row * 8; - if(dur < time_per_pixel / 2) { + for (int row = 0; row < rows ; row++) { + for (int x = 0; x < 128; x++) { + int y = 3 + row*8; + if (dur < time_per_pixel/2) { /* Get more data. */ raw_samples_get(buf, idx++, &level, &dur); sample_num++; } - canvas_draw_line(canvas, x, y, x, y - (level * 3)); + 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) { - canvas_draw_dot(canvas, x, y + 2); - canvas_draw_dot(canvas, x - 1, y + 3); - canvas_draw_dot(canvas, x, y + 3); - canvas_draw_dot(canvas, x + 1, y + 3); + 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); + canvas_draw_dot(canvas,x+1,y+3); sample_num++; /* Make sure we don't mark the next, too. */ } /* Remove from the current level duration the time we * just plot. */ - if(dur > time_per_pixel) + if (dur > time_per_pixel) dur -= time_per_pixel; else dur = 0; @@ -51,46 +53,45 @@ void render_signal(ProtoViewApp* app, Canvas* const canvas, RawSamplesBuffer* bu } /* Raw pulses rendering. This is our default view. */ -void render_view_raw_pulses(Canvas* const canvas, ProtoViewApp* app) { +void render_view_raw_pulses(Canvas *const canvas, ProtoViewApp *app) { /* Show signal. */ render_signal(app, canvas, DetectedSamples, app->signal_offset); /* 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) { + if (app->signal_decoded) { canvas_set_font(canvas, FontPrimary); canvas_draw_str_with_border(canvas, 1, 61, app->signal_info.name, ColorWhite, ColorBlack); } } /* Handle input for the raw pulses view. */ -void process_input_raw_pulses(ProtoViewApp* app, InputEvent input) { - if(input.type == InputTypeRepeat) { +void process_input_raw_pulses(ProtoViewApp *app, InputEvent input) { + if (input.type == InputTypeRepeat) { /* 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--; - else if(input.key == InputKeyOk) { + 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; } - } else if(input.type == InputTypeShort) { - if(input.key == InputKeyOk) { + } else if (input.type == InputTypeShort) { + if (input.key == InputKeyOk) { /* Reset the current sample to capture the next. */ reset_current_signal(app); - } else if(input.key == InputKeyDown) { + } else if (input.key == InputKeyDown) { /* Rescaling. The set becomes finer under 50us per pixel. */ uint32_t scale_step = app->us_scale >= 50 ? 50 : 10; - if(app->us_scale < 500) app->us_scale += scale_step; - } else if(input.key == InputKeyUp) { + if (app->us_scale < 500) app->us_scale += scale_step; + } else if (input.key == InputKeyUp) { uint32_t scale_step = app->us_scale > 50 ? 50 : 10; - if(app->us_scale > 10) app->us_scale -= scale_step; + if (app->us_scale > 10) app->us_scale -= scale_step; } } } diff --git a/applications/plugins/protoview/view_settings.c b/applications/plugins/protoview/view_settings.c index 09abf5a2a..1e2dce226 100644 --- a/applications/plugins/protoview/view_settings.c +++ b/applications/plugins/protoview/view_settings.c @@ -6,30 +6,30 @@ /* Renders a single view with frequency and modulation setting. However * this are logically two different views, and only one of the settings * will be highlighted. */ -void render_view_settings(Canvas* const canvas, ProtoViewApp* app) { +void render_view_settings(Canvas *const canvas, ProtoViewApp *app) { canvas_set_font(canvas, FontPrimary); - if(app->current_view == ViewFrequencySettings) - canvas_draw_str_with_border(canvas, 1, 10, "Frequency", ColorWhite, ColorBlack); + if (app->current_view == ViewFrequencySettings) + canvas_draw_str_with_border(canvas,1,10,"Frequency",ColorWhite,ColorBlack); else - canvas_draw_str(canvas, 1, 10, "Frequency"); + canvas_draw_str(canvas,1,10,"Frequency"); - if(app->current_view == ViewModulationSettings) - canvas_draw_str_with_border(canvas, 70, 10, "Modulation", ColorWhite, ColorBlack); + if (app->current_view == ViewModulationSettings) + canvas_draw_str_with_border(canvas,70,10,"Modulation",ColorWhite,ColorBlack); else - canvas_draw_str(canvas, 70, 10, "Modulation"); + canvas_draw_str(canvas,70,10,"Modulation"); canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas, 10, 61, "Use up and down to modify"); + canvas_draw_str(canvas,10,61,"Use up and down to modify"); - if(app->txrx->debug_timer_sampling) - canvas_draw_str(canvas, 3, 52, "(DEBUG timer sampling is ON)"); + if (app->txrx->debug_timer_sampling) + canvas_draw_str(canvas,3,52,"(DEBUG timer sampling is ON)"); /* Show frequency. We can use big numbers font since it's just a number. */ - if(app->current_view == ViewFrequencySettings) { + if (app->current_view == ViewFrequencySettings) { char buf[16]; - snprintf(buf, sizeof(buf), "%.2f", (double)app->frequency / 1000000); + snprintf(buf,sizeof(buf),"%.2f",(double)app->frequency/1000000); canvas_set_font(canvas, FontBigNumbers); canvas_draw_str(canvas, 30, 40, buf); - } else if(app->current_view == ViewModulationSettings) { + } else if (app->current_view == ViewModulationSettings) { int current = app->modulation; canvas_set_font(canvas, FontPrimary); canvas_draw_str(canvas, 33, 39, ProtoViewModulations[current].name); @@ -37,13 +37,13 @@ void render_view_settings(Canvas* const canvas, ProtoViewApp* app) { } /* Handle input for the settings view. */ -void process_input_settings(ProtoViewApp* app, InputEvent input) { - if(input.type == InputTypeLong && input.key == InputKeyOk) { +void process_input_settings(ProtoViewApp *app, InputEvent input) { + if (input.type == InputTypeLong && input.key == InputKeyOk) { /* Long pressing to OK sets the default frequency and * modulation. */ app->frequency = subghz_setting_get_default_frequency(app->setting); app->modulation = 0; - } else if(0 && input.type == InputTypeLong && input.key == InputKeyDown) { + } else if (0 && input.type == InputTypeLong && input.key == InputKeyDown) { /* Long pressing to down switches between normal and debug * timer sampling mode. NOTE: this feature is disabled for users, * only useful for devs (if useful at all). */ @@ -55,40 +55,42 @@ 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) { + if (app->current_view == ViewFrequencySettings) { size_t curidx = 0, i; size_t count = subghz_setting_get_frequency_count(app->setting); /* Scan the list of frequencies to check for the index of the * currently set frequency. */ for(i = 0; i < count; i++) { - uint32_t freq = subghz_setting_get_frequency(app->setting, i); - if(freq == app->frequency) { + uint32_t freq = subghz_setting_get_frequency(app->setting,i); + if (freq == app->frequency) { curidx = i; break; } } - if(i == count) return; /* Should never happen. */ + if (i == count) return; /* Should never happen. */ - if(input.key == InputKeyUp) { - curidx = curidx == 0 ? count - 1 : curidx - 1; - } else if(input.key == InputKeyDown) { - curidx = (curidx + 1) % count; + if (input.key == InputKeyUp) { + curidx = curidx == 0 ? count-1 : curidx-1; + } else if (input.key == InputKeyDown) { + curidx = (curidx+1) % count; } else { return; } - app->frequency = subghz_setting_get_frequency(app->setting, curidx); - } else if(app->current_view == ViewModulationSettings) { + app->frequency = subghz_setting_get_frequency(app->setting,curidx); + } else if (app->current_view == ViewModulationSettings) { uint32_t count = 0; uint32_t modid = app->modulation; while(ProtoViewModulations[count].name != NULL) count++; - if(input.key == InputKeyUp) { - modid = modid == 0 ? count - 1 : modid - 1; - } else if(input.key == InputKeyDown) { - modid = (modid + 1) % count; + if (input.key == InputKeyUp) { + modid = modid == 0 ? count-1 : modid-1; + } else if (input.key == InputKeyDown) { + modid = (modid+1) % count; } else { return; } @@ -104,13 +106,9 @@ void process_input_settings(ProtoViewApp* app, InputEvent input) { /* When the user switches to some other view, if they changed the parameters * 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); +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); radio_rx_end(app); radio_begin(app); radio_rx(app);