From 361e300ad90620ee97c2bdd624680af546e18f22 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 12 Nov 2023 08:49:53 +0000 Subject: [PATCH] Squashed 'protoview/' changes from e0d6e9779..66744dcdf 66744dcdf fix catalog versions bca106015 New naming fixes part 1 25842ee7f replace protoview pa table hotfix with proper code 2b628708f combine 1 98960dc21 move base pack here REVERT: e0d6e9779 Binary file updated. REVERT: 7143455a2 Chat: use 300us short pulse for better reception. REVERT: a7c85209f Binary file updated. REVERT: df527db83 Chat: protocol changed a bit. Rationale added. REVERT: 0bf9ff815 Binary file updated. REVERT: 8e32ef6a7 Chat fixed, hopefully. REVERT: 9352b0b6e General decoder improved. REVERT: 79ef4f5d0 Replace decoded signal with one decoded by the "unknown" decoder. REVERT: 911cb6795 Initial work for a generic protocol decoder. REVERT: 6456f39b4 Minor comment improvement. REVERT: 705cbd1d9 Remove hardcoded limit from search_coherent_signal(). REVERT: cf4b75e6d Chat: Fix message len. REVERT: 48fb26f4b ProtoView chat protocol description improved. REVERT: 594560170 Binary file updated. REVERT: 3f78a13e0 ProtoView chat protocol. REVERT: d78079abf Add copyright information. REVERT: cc32c5e6f b4b1: more robust message detection. REVERT: 32b3f76a8 Binary file updated. REVERT: b07fd75f2 Icon updated. REVERT: e7a81d178 Duration filter now changes according to preset data rate. REVERT: 6a9baf2f4 Binary file updated. REVERT: b060c507c Direct sampling: brand new implementation. REVERT: da2f928b2 Update comments and function names in app_subghz.c REVERT: d53188d11 Direct sampling fixes and ability to pause. REVERT: 2a791b176 Protect screen refresh from switch_view() with a mutex. REVERT: 05899c261 Direct sampling: different improvements. REVERT: 7579471ff Fix view switching calls order. REVERT: 6b4be8800 CC1101 custom presets updated. REVERT: d1bcd09a7 Restore TPMS2 OOK setting, modified for error. REVERT: d1c86b349 Fix direct sampling after subghz worker deletion. REVERT: 63ea90835 Subghz worker stuff removed. 50k of free memory won. REVERT: 2f003976f Renault TPMS encoder. REVERT: 8887f5147 README: fix markdown. REVERT: 5ec69f0d1 README updated. REVERT: 873d638f0 Binary file updated. REVERT: acd4329e0 Message builder: auto select decoder and current decoded values. REVERT: de085c87c Message builder: < > to incr/decr fields. REVERT: d19ffe64b Keeloq encoder: set default low battery to 0. REVERT: 9f9115ed2 Rename function to bitmap_reverse_bytes_bits() for clarity. REVERT: 118af1a74 Keeloq encoder: clean data array. REVERT: 9d020c0fa Fix field_set_from_string() for binary type. REVERT: 96a5e7dc7 Keeloq signal building: a few details to fix. REVERT: 9344e86e8 Fieldset: more fixes to bytes field type. REVERT: 00819ac61 Message builder: Generate signal and decode it. REVERT: f75067cc4 Implement signal creation for PT remotes. REVERT: 6d6c5e9f8 Fieldset: fix a few to/from string conversions. REVERT: e38ad0f7c Message builder: User input progresses. REVERT: baecc6c26 Message builder: Some more UI element. REVERT: 7ed361417 Message builder: Decoder selection and basic text drawing. REVERT: 23a879361 Build message view introduced. REVERT: 6606d8ce7 README: RX -> TX. REVERT: 524a21948 README improved a bit more. REVERT: c750f3376 Merge branch 'edit' REVERT: 4fc3a4e89 README updated. REVERT: b651d54cf Binary file updated. REVERT: bc8f8a29d Fix sending in custom presets: add PATABLE. REVERT: 88a43ece0 Make keyboard able to be dismissed with back key. REVERT: 0d1184444 Multi page INFO. Long press to reset signal. REVERT: 2a053c149 Conversion to field set: WIP 2. REVERT: 69053858c Conversion to field set: WIP 1. REVERT: 03f56561a Message building: fields types and functions. REVERT: 4092d3fb2 Merge pull request #5 from dopsi/main REVERT: 8ef1f05ae Use public header file REVERT: c93ae3ca7 UI alert + long press to exit. REVERT: 86c5c71a9 Generalize app_switch_view() to switch to specific views. REVERT: f2633b078 Binary file updated. REVERT: 2bf380d0f Suppress charging to improve RF performances. REVERT: 736421d06 Blink blue led while sending. REVERT: cb88bdcca Notification when receiving/sending. REVERT: 2fe4ce34d Tx: data feeder of current signal refactored. REVERT: 362ef7c58 Sending back messages. REVERT: a04f4fb90 Merge branch 'save_subview' REVERT: 9a31419b9 Save text to make UI more obvious. REVERT: 0f8ef0aba Fix start/len reporting in all the decoders. REVERT: 8fe8d93ca Remove useless debugging logs. REVERT: 10762482b Saving signal now works. REVERT: b9ece7896 Random file name. REVERT: 8ca076c23 Refactoring of code to show/hide the keyboard. REVERT: 2f1327eef Show text input when needed. REVERT: 56b968979 Fix bitmap copy and oscilloscope view. REVERT: 2459e55db View privdata. Oscilliscope alike view row change. REVERT: f885491bc Decoded signal square wave WIP. REVERT: 004a457ec Fix typo in comment. REVERT: 56eb5288a Public TODO removed. I change idea every 5 seconds. REVERT: 25bc81ac4 Copy signal representation in the info structure. REVERT: b76b6d85e TODO: add roadmap. REVERT: 47586fc7a Binary file updated. REVERT: 2de5c2af0 Very basic subview system introduced. REVERT: 1e4bbfaa2 README updated. REVERT: ea48c7f89 Microchip HCS series, Keeloq protocol. REVERT: 21a2ef4a3 Put TPMS decoders under a separated directory. REVERT: b1ce34580 Buffers: use 2 bytes per sample instead of 5. REVERT: 2fa23ee15 Improve Schrader EG53MA4 implementation. REVERT: 67471e72a Schrader EG53MA4 TPMS. REVERT: c1f3ee3d2 More custom presets comments fixed. REVERT: 41a92599c Improve custom settings comments. REVERT: 54eea1ded CC1101 preset that works well with OOK TPMS. REVERT: c25645a75 README: add Ford TPMS. REVERT: 7f9805228 Binary file updated. REVERT: 10d17fe21 Pressing OK resets current signal in info view. REVERT: a711ad10d Ford TPMS. REVERT: a49ebe178 Binary file updated. REVERT: bdcaa93b1 Add Citroen in README. REVERT: 1fe8a7f5f Citroen TPMS. REVERT: 86c9cdbc7 README: update protocols list. REVERT: b1477afb8 Move one of the screenshot down. Makes more sense. REVERT: 68b158c59 Screenshots updated. REVERT: d57c7331e rtl433 credits. REVERT: 7ad07e9d3 Binary file updated. REVERT: cf2fdc6a8 Check CRC for TPMS protocols. REVERT: a631025c6 TODO updated. REVERT: 0e85d13d8 Try to scan more often, but only scan if needed. REVERT: 86ca7ffe0 Check checksum in Schrader TPMS. REVERT: 715661cd9 Better raw view scale selection. REVERT: 2a91ac8c7 Schrader TPMS implementation + CRC8 implementation. REVERT: bcc399133 Merge pull request #3 from erjanmx/fix-readme-typo REVERT: be080711b TODO updated. REVERT: 80cb1d3ab Binary file updated. REVERT: a81a3f2c1 Enable/disable and explain direct sampling. REVERT: ec5bae763 Readme updated. REVERT: 65d7c7007 Disable debug timer sampling mode. REVERT: e84beb098 Improve signal substitution rules. REVERT: 44354500b Show Renault TPMS tire ID. REVERT: 9b6067b6f Toyota TPMS. REVERT: 126f1f332 Differential Manchester encoding. REVERT: 015ee09bb Improve naming of two variables. REVERT: 8f63eb431 Raise min length of pulses. REVERT: 12694698e Allow for more time difference. Decode more bits than detected. REVERT: 5174bf14d Reanult TPMS fixed. REVERT: 0d62d893d Third TPMS custom preset. Start with subghz feeding. REVERT: 616943ace Remove busy loop DS. Timer is better. REVERT: b61cbf690 Fix switching between direct sampling and subghz. REVERT: 549f83a30 Direct sampling with timer working. REVERT: 13fdeb10e Signal detection in DS. REVERT: 1065de9ed Setup timer. REVERT: b879bb945 Allow switching between normal and debug DS mode. REVERT: 06997d72d Apply frequency/modulation changes on view exit. REVERT: 0cc1128ac Direct sampling: kinda working. REVERT: 5f29902f8 Fix readme typo REVERT: 27edce5f6 Exit/enter view callbacks. Stop subghz worker in direct sampling mode. REVERT: 6ba370282 Direct sampling mode, reading GDO0 directly. REVERT: 4e36d0385 TPMS2 preset changed. Few CC1101 registers documented. REVERT: 811d8f3c0 Define to disable the subghz worker filter. REVERT: a0c78b837 Invert settings up/down keys effect. REVERT: f0a2607dd Add a second TPMS-specific mode. REVERT: 5fc223cde Merge pull request #1 from Illogico/patch-1 REVERT: cafc1694a Binary file updated. REVERT: 79c9481b5 Update README.md REVERT: 6da6c33ca TPMS: comment about 2FSK / GFSK. REVERT: be293ca5c Optimize preamble matching. REVERT: 6ee735148 Log decoders latecy. REVERT: 1d8ca65a6 Support for custom CC1101 presets. REVERT: d373b5d64 Renault TPMS and other improvements. REVERT: 341ceda23 Store bits in the same order they are received. REVERT: a5f3ce425 Reduce min pulse length detected. REVERT: 0da1b7aec Oregon2: remove debugging messages. REVERT: 24071c10f Signal info view. REVERT: de4d3eed5 Show detected protocol in raw view. REVERT: 864cb6618 Oregon2: get humidity. REVERT: 0f442527f Fixes, comments, Oregon2 basic protocol. REVERT: 31fd6df02 Decoding: limit huge pulses. UI settings: long OK press sets defaults. REVERT: 49a0bc46f PT/SC remote protocol working. REVERT: 10227a45b Protocol decoding, work in progress 2. REVERT: 4ce568a65 More robust short signal us guess. REVERT: 630290f36 Protocol decoding, work in progress. REVERT: 52e60b572 Binary file updated. REVERT: 6cd2b34fe Fix settings key press handling. REVERT: 29d4a753a Initial signal processing code. REVERT: 5464f99a7 Long press OK to center the signal again. REVERT: 6c5171557 Refactoring. REVERT: 4635c8fcc Binary update script. REVERT: 979bc84cc Binary file updated. REVERT: 9503bde42 Long press left/right for panning in raw view. REVERT: 999cc4831 A few more comments in app.c REVERT: de3dee113 TODO updated. REVERT: 9667d3df5 Binary distribution. REVERT: 8062fb014 Avoid rescanning detected signal parts. REVERT: e921036d3 duration_delta(): missing space in a one liner is too much. REVERT: b116d4333 Fix comment after code change. REVERT: 22f22a64b README: fipper -> Flipper. REVERT: b596a86ab README improved. REVERT: 1e6eaf553 README: added section about Flipper code quality. REVERT: a173c5389 README: more typos. REVERT: 74943a744 Copyright notice added. REVERT: bf9eeda9d README: typo. REVERT: 69d4f869d README: specify we classify RF on/off separately. REVERT: d7b2d6390 README: typos. REVERT: 8df02815d TODO updated. REVERT: f26954d8f Screenshot added. REVERT: 2e9a4c4d2 README updated. REVERT: eea8b4ef4 Set freq and modulation + other improvements. REVERT: be41b405f View specific input handling. REVERT: d750c80b3 Multi view handling. REVERT: d57370e39 A few more comments here and there. REVERT: 7026e4204 README added. REVERT: 2679be780 Skip almost empty classes in short pulse len calculation. REVERT: bbe40eab7 Skip empty classes in pulse len calculation. REVERT: 09990d61e Fix scale change. REVERT: 863dc844d Reset sample history when OK is pressed. REVERT: 860879eec Up/down change scale. Better short pulse computation. REVERT: a81c90b64 TODO: added pulse length improvement needed. REVERT: bdaebc51e Display short pulse length. REVERT: 04826a8f4 Move processing into timer. REVERT: 4f8f7d8ca Initial commit with working minimal app. git-subtree-dir: protoview git-subtree-split: 66744dcdfd72d0ffb2036f83bc4303c17cac5dd0 --- README.md | 154 +----------- app.c | 185 +++++++------- app.h | 291 ++++++++++++---------- app_subghz.c | 144 +++++------ application.fam | 6 +- binaries/README.md | 10 - binaries/protoview.fap | Bin 51588 -> 0 bytes binaries/update.sh | 4 - crc.c | 17 +- custom_presets.h | 39 +-- fields.c | 352 ++++++++++++++------------- helpers/radio_device_loader.c | 64 +++++ helpers/radio_device_loader.h | 15 ++ img/1.png | Bin 0 -> 1465 bytes img/2.png | Bin 0 -> 2133 bytes img/3.png | Bin 0 -> 2601 bytes protocols/b4b1.c | 67 +++--- protocols/keeloq.c | 99 ++++---- protocols/oregon2.c | 90 ++++--- protocols/pvchat.c | 93 ++++--- protocols/tpms/citroen.c | 46 ++-- protocols/tpms/ford.c | 51 ++-- protocols/tpms/renault.c | 104 ++++---- protocols/tpms/schrader.c | 52 ++-- protocols/tpms/schrader_eg53ma4.c | 49 ++-- protocols/tpms/toyota.c | 57 ++--- protocols/unknown.c | 210 ++++++++-------- raw_samples.c | 47 ++-- raw_samples.h | 27 ++- signal.c | 387 ++++++++++++++++-------------- signal_file.c | 110 +++++---- ui.c | 99 ++++---- view_build.c | 211 ++++++++-------- view_direct_sampling.c | 134 +++++------ view_info.c | 210 ++++++++-------- view_raw_signal.c | 76 +++--- view_settings.c | 74 +++--- 37 files changed, 1785 insertions(+), 1789 deletions(-) delete mode 100644 binaries/README.md delete mode 100644 binaries/protoview.fap delete mode 100755 binaries/update.sh create mode 100644 helpers/radio_device_loader.c create mode 100644 helpers/radio_device_loader.h create mode 100644 img/1.png create mode 100644 img/2.png create mode 100644 img/3.png diff --git a/README.md b/README.md index 7523913ca5d..dc0628dd4cc 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,8 @@ -ProtoView is a digital signal detection, visualization, editing and reply tool for the [Flipper Zero](https://flipperzero.one/). The Flipper default application, called Subghz, is able to identify certain RF protocols, but when the exact protocol is not implemented (and there are many undocumented and unimplemented ones, such as the ones in use in TPMS systems, car keys and many others), the curious person is left wondering what the device is sending at all. Using ProtoView she or he can visualize the high and low pulses like in the example image below (showing a TPMS signal produced by a Renault tire): - -![ProtoView screenshot raw signal](/images/protoview_1.jpg) - +ProtoView is a digital signal detection, visualization, editing and reply tool for the Flipper Zero. The Flipper default application, called Subghz, is able to identify certain RF protocols, but when the exact protocol is not implemented (and there are many undocumented and unimplemented ones, such as the ones in use in TPMS systems, car keys and many others), the curious person is left wondering what the device is sending at all. Using ProtoView she or he can visualize the high and low pulses This is often enough to make an initial idea about the encoding used and if the selected modulation is correct. For example, in the signal above you can see a set of regular pulses and gaps used for synchronization, and then -a sequence of bits encoded in [Manchester](https://en.wikipedia.org/wiki/Manchester_code) line code. If you study these things for five minutes, you'll find yourself able to decode the bits with naked eyes. +a sequence of bits encoded in Manchester - https://en.wikipedia.org/wiki/Manchester_code line code. If you study these things for five minutes, you'll find yourself able to decode the bits with naked eyes. ## Decoding capabilities @@ -15,13 +12,12 @@ Other than showing the raw signal, ProtoView is able to decode a few interesting * Microchip HSC200/300/301 Keeloq protocol. * Oregon thermometer protocol 2. * PT2262, SC5262 based remotes. -* ... more will be implemented soon, hopefully. Send PRs :) +* ... more will be implemented soon, hopefully. Send PRs -![ProtoView screenshot Renault TPMS data](/images/protoview_2.jpg) The app implements a framework that makes adding and experimenting with new -protocols very simple. Check the `protocols` directory to see how the -API works, or read the full documentation at the end of this `README` file. +protocols very simple. Check the (protocols) directory to see how the +API works, or read the full documentation at the end of (README) file. The gist of it is that the decoder receives the signal already converted into a bitmap, where each bit represents a short pulse duration. Then there are functions to seek specific sync/preamble sequences inside the bitmap, to decode @@ -44,9 +40,9 @@ values. ## A well-documented app for the Flipper The secondary goal of ProtoView is to provide a well-documented application for the Flipper (even if ProtoView is a pretty atypical application: it doesn't make use of the standard widgets and other abstractions provided by the framework). -Most apps dealing with the *subghz subsystem* of the Flipper (the abstraction used to work with the [CC1101 chip](https://www.ti.com/product/CC1101)) tend to be complicated and completely undocumented. +Most apps dealing with the *subghz subsystem* of the Flipper (the abstraction used to work with the CC1101 chip - https://www.ti.com/product/CC1101) tend to be complicated and completely undocumented. Unfortunately, this is also true for the firmware of the device. -It's a shame, because especially in the case of code that talks with hardware peripherals there are tons of assumptions and hard-gained lessons that can [only be captured by comments and are in the code only implicitly](http://antirez.com/news/124). +It's a shame, because especially in the case of code that talks with hardware peripherals there are tons of assumptions and hard-gained lessons that can only be captured by comments and are in the code only implicitly - http://antirez.com/news/124 However, the Flipper firmware source code is well written even if it lacks comments and documentation (and sometimes makes use of abstractions more convoluted than needed), so it is possible to make some ideas of how things work just grepping inside. In order to develop this application, I ended reading most parts of the firmware of the device. @@ -97,8 +93,8 @@ with the right arrow will show information about the decoded signal. In the bottom-right corner the application displays an amount of time in microseconds. This is the average length of the shortest pulse length detected among the three classes. Usually the *data rate* of the protocol -is something like `1000000/this-number*2`, but it depends on the encoding -and could actually be `1000000/this-number*N` with `N > 2` (here 1000000 +is something like (1000000/this-number*2), but it depends on the encoding +and could actually be (1000000/this-number*N) with (N > 2) (here 1000000 is the number of microseconds in one second, and N is the number of clock cycles needed to represent a bit). @@ -107,7 +103,7 @@ cycles needed to represent a bit). If a signal was detected, the info view will show the details about the signal. If the signal has more data that a single screen can fit, pressing OK will show the next fields. Pressing DOWN will go to a sub-view with an oscilloscope-alike representation of the signal, from there you can: 1. Resend the signal, by pressing OK. -2. Save the signal as `.sub` file, by long pressing OK. +2. Save the signal as (.sub) file, by long pressing OK. When resending, you can select a different frequency and modulation if you wish. @@ -144,132 +140,6 @@ RF chip, the CC1101, is receiving. This will makes very easy to understand if a given frequency is targeted by something other than noise. This mode is fun to watch, resembling an old CRT TV set. -# Installing the app from source - -* Download the Flipper Zero dev kit and build it: -``` -mkdir -p ~/flipperZero/official/ -cd ~/flipperZero/official/ -git clone --recursive https://github.com/flipperdevices/flipperzero-firmware.git ./ -./fbt -``` -* Copy this application folder in `official/applications_user`. -* Connect your Flipper via USB. -* Build and install with: `./fbt launch_app APPSRC=protoview`. - -# Installing the binary file (no build needed) - -Drop the `protoview.fap` file you can find in the `binaries` folder into the -following Flipper Zero location: - - /ext/apps/Tools - -The `ext` part means that we are in the SD card. So if you don't want -to use the Android (or other) application to upload the file, -you can just take out the SD card, insert it in your computer, -copy the fine into `apps/Tools`, and that's it. - -# Protocols decoders API - -Writing a protocol decoder is not hard, and requires to write three -different methods. - -1. `decode()`. This is mandatory, and is used in order to turn a known signal into a set of fields containing certain informations. For instance for a thermometer sending data via RF, a raw message will be decoded into fields like temperature, humidity, station ID and so forth. -2. `get_fields()`. Optional, only needed if the protocol supports creating and editing signals. This method just returns the fields names, types and defaults. The app will use this list in order to allow the user to set values. The populated fields will be passed to the `build_message()` method later. -3. `build_message()`. This method gets a set of fields representing the parameters of the protocol, as set by the user, and will create a low level signal composed of pulses and gaps of specific durations. - -## `decode()` method - - bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo *info); - -The method gets a bitmap `bits` long `numbytes` bytes but actually containing `bumbits` valid bits. Each bit represents a pulse of gap of the duration of the shortest time detected in the protocol (this is often called *te*, in the RF protocols jargon). So, for instance, if a signal is composed of pulses and gaps of around 500 and 1000 microseconds, each bit in the bitmap will represent 500 microseconds. - -Continuing with the example above, if the received signal was composed of a 1000 microseconds gap, then a 500 microsecond pulse, then a 500 microsecond gap and finally a 1000 microseconds pulse, its bitmap representation will be: - - 001011 - -To access the bitmap, the decoder can use the following API: - - bool bitmap_get(uint8_t *b, uint32_t blen, uint32_t bitpos); - -The `blen` parameter will be set to what the decode method gets -as `numbytes`, and is used to prevent overflows. This way if `bitpos` -is out of range, nothing bad happens. - -There are function to match and seek specific patterns inside the signal: - - 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); - -Finally, there are functions to convert from different line codes: - - 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); - -This method can also access the short pulse duration by inspecting the -`info->short_pulse_dur` field (in microseconds). - -Please check the `b4b1.c` file for an easy to understand example of the decoder implementation. - -If the decoder actually detected a message, it will return `true` and will return a set of fields, like thata: - - fieldset_add_bytes(info->fieldset,"id",d,5); - fieldset_add_uint(info->fieldset,"button",d[2]&0xf,4); - -## `get_fields()` method. - - static void get_fields(ProtoViewFieldSet *fieldset); - -This method will be basically a copy of the final part of `decode()`, as -it also needs to return the set of fields this protocol is composed of. -However instead of returning the values of an actual decoded message, it -will just provide their default values for the signal creator view. - -Note that the `build_message()` method is guaranteed to receive the -same exact fields in the same exact order. - -## `build_message()` method. - - void build_message(RawSamplesBuffer *samples, ProtoViewFieldSet *fs); - -This method is responsible of creating a signal from scratch, by -appending gaps and pulses of the specific duration into `samples` -using the following API: - - raw_samples_add(RawSamplesBuffer *samples, bool level, uint32_t duration); - -Level can be true (pules) or false (gap). Duration is in microseconds. -The method receives a set of fields in `fs`. Each field is accessible -directly accessing `fs->fields[... field index ...]`, where the field -index is 0, 1, 2, ... in the same order as `get_fields()` returned the -fields. - -For now, you can access the fields in the raw way, by getting the -values directly from the data structure representing each field: - -``` -typedef struct { - ProtoViewFieldType type; - uint32_t len; // Depends on type: - // Bits for integers (signed,unsigned,binary,hex). - // Number of characters for strings. - // Number of nibbles for bytes (1 for each 4 bits). - // Number of digits after dot for floats. - char *name; // Field name. - union { - char *str; // String type. - int64_t value; // Signed integer type. - uint64_t uvalue; // Unsigned integer type. - uint8_t *bytes; // Raw bytes type. - float fvalue; // Float type. - }; -} ProtoViewField; - -``` - -However later the app will likely provide a set of macros to do it -in a more future-proof way. - # License The code is released under the BSD license. @@ -280,10 +150,10 @@ This application is only provided as an educational tool. The author is not liab # Credits -A big thank you to the RTL433 author, [Benjamin Larsson](https://github.com/merbanan). I used the code and tools he developed in many ways: +A big thank you to the RTL433 author, Benjamin Larsson - https://github.com/merbanan I used the code and tools he developed in many ways: * To capture TPMS data with rtl433 and save to a file, to later play the IQ files and speedup the development. * As a sourve of documentation for protocols. -* As an awesome way to visualize and understand protocols, via [these great web tools](https://triq.org/). +* As an awesome way to visualize and understand protocols, via these great web tools - https://triq.org/ * To have tons of fun with RTLSDR in general, now and in the past. The application icon was designed by Stefano Liuzzo. diff --git a/app.c b/app.c index 358ace58967..868ec8f1687 100644 --- a/app.c +++ b/app.c @@ -14,9 +14,9 @@ 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; - furi_mutex_acquire(app->view_updating_mutex,FuriWaitForever); +static void render_callback(Canvas* const canvas, void* ctx) { + ProtoViewApp* app = ctx; + furi_mutex_acquire(app->view_updating_mutex, FuriWaitForever); /* Clear screen. */ canvas_set_color(canvas, ColorWhite); @@ -26,14 +26,25 @@ 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 ViewBuildMessage: render_view_build_message(canvas,app); break; - default: furi_crash(TAG "Invalid view selected"); break; + render_view_settings(canvas, app); + break; + case ViewDirectSampling: + render_view_direct_sampling(canvas, app); + break; + case ViewBuildMessage: + render_view_build_message(canvas, app); + break; + default: + furi_crash(TAG "Invalid view selected"); + break; } /* Draw the alert box if set. */ @@ -43,10 +54,9 @@ static void render_callback(Canvas *const canvas, void *ctx) { /* 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 @@ -56,17 +66,17 @@ static void input_callback(InputEvent* input_event, void* ctx) * The 'switchto' parameter can be the identifier of a view, or the * special views ViewGoNext and ViewGoPrev in order to move to * the logical next/prev view. */ -static void app_switch_view(ProtoViewApp *app, ProtoViewCurrentView switchto) { - furi_mutex_acquire(app->view_updating_mutex,FuriWaitForever); +static void app_switch_view(ProtoViewApp* app, ProtoViewCurrentView switchto) { + furi_mutex_acquire(app->view_updating_mutex, FuriWaitForever); /* Switch to the specified view. */ ProtoViewCurrentView old = app->current_view; - if (switchto == ViewGoNext) { + if(switchto == ViewGoNext) { app->current_view++; - if (app->current_view == ViewLast) app->current_view = 0; - } else if (switchto == ViewGoPrev) { - if (app->current_view == 0) - app->current_view = ViewLast-1; + if(app->current_view == ViewLast) app->current_view = 0; + } else if(switchto == ViewGoPrev) { + if(app->current_view == 0) + app->current_view = ViewLast - 1; else app->current_view--; } else { @@ -75,24 +85,24 @@ static void app_switch_view(ProtoViewApp *app, ProtoViewCurrentView switchto) { ProtoViewCurrentView new = app->current_view; /* Call the exit view callbacks. */ - if (old == ViewDirectSampling) view_exit_direct_sampling(app); - if (old == ViewBuildMessage) view_exit_build_message(app); - if (old == ViewInfo) view_exit_info(app); + if(old == ViewDirectSampling) view_exit_direct_sampling(app); + if(old == ViewBuildMessage) view_exit_build_message(app); + if(old == ViewInfo) view_exit_info(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); /* Reset the view private data each time, before calling the enter * callbacks that may want to setup some state. */ - memset(app->view_privdata,0,PROTOVIEW_VIEW_PRIVDATA_LEN); + memset(app->view_privdata, 0, PROTOVIEW_VIEW_PRIVDATA_LEN); /* Call the enter view callbacks after all the exit callback * of the old view was already executed. */ - if (new == ViewDirectSampling) view_enter_direct_sampling(app); - if (new == ViewBuildMessage) view_enter_build_message(app); + if(new == ViewDirectSampling) view_enter_direct_sampling(app); + if(new == ViewBuildMessage) view_enter_build_message(app); /* Set the current subview of the view we just left to zero. This is * the main subview of the old view. When we re-enter the view we are @@ -108,7 +118,9 @@ static void app_switch_view(ProtoViewApp *app, ProtoViewCurrentView switchto) { /* 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)); + furi_hal_power_suppress_charge_enter(); + + ProtoViewApp* app = malloc(sizeof(ProtoViewApp)); // Init shared data structures RawSamples = raw_samples_alloc(); @@ -132,10 +144,10 @@ ProtoViewApp* protoview_app_alloc() { app->alert_dismiss_time = 0; app->current_view = ViewRawPulses; app->view_updating_mutex = furi_mutex_alloc(FuriMutexTypeNormal); - for (int j = 0; j < ViewLast; j++) app->current_subview[j] = 0; + for(int j = 0; j < ViewLast; j++) app->current_subview[j] = 0; app->direct_sampling_enabled = false; app->view_privdata = malloc(PROTOVIEW_VIEW_PRIVDATA_LEN); - memset(app->view_privdata,0,PROTOVIEW_VIEW_PRIVDATA_LEN); + memset(app->view_privdata, 0, PROTOVIEW_VIEW_PRIVDATA_LEN); // Signal found and visualization defaults app->signal_bestlen = 0; @@ -153,11 +165,18 @@ ProtoViewApp* protoview_app_alloc() { app->txrx->debug_timer_sampling = false; app->txrx->last_g0_change_time = DWT->CYCCNT; app->txrx->last_g0_value = false; - + app->frequency = subghz_setting_get_default_frequency(app->setting); app->modulation = 0; /* Defaults to ProtoViewModulations[0]. */ - furi_hal_power_suppress_charge_enter(); + // Init & set radio_device + subghz_devices_init(); + app->radio_device = + radio_device_loader_set(app->radio_device, SubGhzRadioDeviceTypeExternalCC1101); + + subghz_devices_reset(app->radio_device); + subghz_devices_idle(app->radio_device); + app->running = 1; return app; @@ -166,11 +185,13 @@ 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, this also restores charging. - radio_sleep(app); + subghz_devices_sleep(app->radio_device); + radio_device_loader_end(app->radio_device); + + subghz_devices_deinit(); // View related. view_port_enabled_set(app->view_port, false); @@ -199,8 +220,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 @@ -208,15 +229,14 @@ 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,RawSamples, - ProtoViewModulations[app->modulation].duration_filter); + scan_for_signal(app, RawSamples, ProtoViewModulations[app->modulation].duration_filter); } /* This is the navigation callback we use in the view dispatcher used @@ -229,7 +249,7 @@ static void timer_callback(void *ctx) { * We just need a dummy callback returning false. We believe the * implementation should be changed and if no callback is set, it should be * the same as returning false. */ -static bool keyboard_view_dispatcher_navigation_callback(void *ctx) { +static bool keyboard_view_dispatcher_navigation_callback(void* ctx) { UNUSED(ctx); return false; } @@ -237,10 +257,10 @@ static bool keyboard_view_dispatcher_navigation_callback(void *ctx) { /* App entry point, as specified in application.fam. */ 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. */ @@ -255,71 +275,68 @@ 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 (app->current_view != ViewRawPulses) { + if(input.type == InputTypeShort && input.key == InputKeyBack) { + if(app->current_view != ViewRawPulses) { /* If this is not the main app view, go there. */ - app_switch_view(app,ViewRawPulses); + app_switch_view(app, ViewRawPulses); } else { /* If we are in the main app view, warn the user * they needs to long press to really quit. */ - ui_show_alert(app,"Long press to exit",1000); + ui_show_alert(app, "Long press to exit", 1000); } - } else if (input.type == InputTypeLong && - input.key == InputKeyBack) - { + } else if(input.type == InputTypeLong && input.key == InputKeyBack) { app->running = 0; - } else if (input.type == InputTypeShort && - input.key == InputKeyRight && - ui_get_current_subview(app) == 0) - { + } else if( + input.type == InputTypeShort && input.key == InputKeyRight && + ui_get_current_subview(app) == 0) { /* Go to the next view. */ - app_switch_view(app,ViewGoNext); - } else if (input.type == InputTypeShort && - input.key == InputKeyLeft && - ui_get_current_subview(app) == 0) - { + app_switch_view(app, ViewGoNext); + } else if( + input.type == InputTypeShort && input.key == InputKeyLeft && + ui_get_current_subview(app) == 0) { /* Go to the previous view. */ - app_switch_view(app,ViewGoPrev); + app_switch_view(app, ViewGoPrev); } 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); + process_input_direct_sampling(app, input); break; case ViewBuildMessage: - process_input_build_message(app,input); + process_input_build_message(app, input); + break; + default: + furi_crash(TAG "Invalid view selected"); break; - default: furi_crash(TAG "Invalid view 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"); } } - if (app->show_text_input) { + if(app->show_text_input) { /* Remove our viewport: we need to use a view dispatcher * in order to show the standard Flipper keyboard. */ gui_remove_view_port(app->gui, app->view_port); @@ -332,11 +349,11 @@ int32_t protoview_app_entry(void* p) { * otherwise when the user presses back on the keyboard to * abort, the dispatcher will not stop. */ view_dispatcher_set_navigation_event_callback( - app->view_dispatcher, - keyboard_view_dispatcher_navigation_callback); + app->view_dispatcher, keyboard_view_dispatcher_navigation_callback); app->text_input = text_input_alloc(); - view_dispatcher_set_event_callback_context(app->view_dispatcher,app); - view_dispatcher_add_view(app->view_dispatcher, 0, text_input_get_view(app->text_input)); + view_dispatcher_set_event_callback_context(app->view_dispatcher, app); + view_dispatcher_add_view( + app->view_dispatcher, 0, text_input_get_view(app->text_input)); view_dispatcher_switch_to_view(app->view_dispatcher, 0); /* Setup the text input view. The different parameters are set @@ -352,7 +369,8 @@ int32_t protoview_app_entry(void* p) { false); /* Run the dispatcher with the keyboard. */ - view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + view_dispatcher_attach_to_gui( + app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); view_dispatcher_run(app->view_dispatcher); /* Undo all it: remove the view from the dispatcher, free it @@ -370,7 +388,7 @@ int32_t protoview_app_entry(void* p) { } /* 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); @@ -380,4 +398,3 @@ int32_t protoview_app_entry(void* p) { protoview_app_free(app); return 0; } - diff --git a/app.h b/app.h index 2528de2583f..5fb0adf34a0 100644 --- a/app.h +++ b/app.h @@ -19,13 +19,14 @@ #include #include #include "raw_samples.h" +#include "helpers/radio_device_loader.h" #define TAG "ProtoView" #define PROTOVIEW_RAW_VIEW_DEFAULT_SCALE 100 // 100us is 1 pixel by default #define BITMAP_SEEK_NOT_FOUND UINT32_MAX // Returned by function as sentinel #define PROTOVIEW_VIEW_PRIVDATA_LEN 64 // View specific private data len -#define DEBUG_MSG 1 +#define DEBUG_MSG 0 /* Forward declarations. */ @@ -63,12 +64,12 @@ typedef enum { /* ================================== RX/TX ================================= */ typedef struct { - const char *name; // Name to show to the user. - const char *id; // Identifier in the Flipper API/file. - FuriHalSubGhzPreset preset; // The preset ID. - uint8_t *custom; /* If not null, a set of registers for + const char* name; // Name to show to the user. + const char* id; // Identifier in the Flipper API/file. + FuriHalSubGhzPreset preset; // The preset ID. + uint8_t* custom; /* If not null, a set of registers for the CC1101, specifying a custom preset.*/ - uint32_t duration_filter; /* Ignore pulses and gaps that are less + uint32_t duration_filter; /* Ignore pulses and gaps that are less than the specified microseconds. This depends on the data rate. */ } ProtoViewModulation; @@ -79,16 +80,16 @@ 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. */ - TxRxState txrx_state; /* Receiving, idle or sleeping? */ + 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. */ }; @@ -100,48 +101,50 @@ typedef struct ProtoViewTxRx ProtoViewTxRx; #define ALERT_MAX_LEN 32 struct ProtoViewApp { /* GUI */ - Gui *gui; - NotificationApp *notification; - ViewPort *view_port; /* We just use a raw viewport and we render + Gui* gui; + NotificationApp* notification; + ViewPort* view_port; /* We just use a raw viewport and we render everything into the low level canvas. */ - ProtoViewCurrentView current_view; /* Active left-right view ID. */ - FuriMutex *view_updating_mutex; /* The Flipper GUI calls the screen redraw + ProtoViewCurrentView current_view; /* Active left-right view ID. */ + FuriMutex* view_updating_mutex; /* The Flipper GUI calls the screen redraw callback in a different thread. We use this mutex to protect the redraw from changes in app->view_privdata. */ - int current_subview[ViewLast]; /* Active up-down subview ID. */ - FuriMessageQueue *event_queue; /* Keypress events go here. */ + int current_subview[ViewLast]; /* Active up-down subview ID. */ + FuriMessageQueue* event_queue; /* Keypress events go here. */ /* Input text state. */ - ViewDispatcher *view_dispatcher; /* Used only when we want to show + ViewDispatcher* view_dispatcher; /* Used only when we want to show the text_input view for a moment. Otherwise it is set to null. */ - TextInput *text_input; + TextInput* text_input; bool show_text_input; - char *text_input_buffer; + char* text_input_buffer; uint32_t text_input_buffer_len; void (*text_input_done_callback)(void*); /* Alert state. */ - uint32_t alert_dismiss_time; /* Millisecond when the alert will be + uint32_t alert_dismiss_time; /* Millisecond when the alert will be no longer shown. Or zero if the alert is currently not set at all. */ char alert_text[ALERT_MAX_LEN]; /* Alert content. */ /* Radio related. */ - ProtoViewTxRx *txrx; /* Radio state. */ - SubGhzSetting *setting; /* A list of valid frequencies. */ + ProtoViewTxRx* txrx; /* Radio state. */ + SubGhzSetting* setting; /* A list of valid frequencies. */ + + const SubGhzDevice* radio_device; /* 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? */ - ProtoViewMsgInfo *msg_info; /* Decoded message info if not NULL. */ + bool signal_decoded; /* Was the current signal decoded? */ + ProtoViewMsgInfo* msg_info; /* Decoded message info if not NULL. */ bool direct_sampling_enabled; /* This special view needs an explicit acknowledge to work. */ - void *view_privdata; /* This is a piece of memory of total size + void* view_privdata; /* This is a piece of memory of total size PROTOVIEW_VIEW_PRIVDATA_LEN that it is initialized to zero when we switch to a a new view. While the view we are using @@ -150,12 +153,12 @@ struct ProtoViewApp { the pointer to a few specific-data structure. */ /* 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. */ }; @@ -166,18 +169,18 @@ struct ProtoViewApp { * in the message info view. */ #define PROTOVIEW_MSG_STR_LEN 32 typedef struct ProtoViewMsgInfo { - ProtoViewDecoder *decoder; /* The decoder that decoded the message. */ - ProtoViewFieldSet *fieldset; /* Decoded fields. */ + ProtoViewDecoder* decoder; /* The decoder that decoded the message. */ + ProtoViewFieldSet* fieldset; /* Decoded fields. */ /* Low level information of the detected signal: the following are filled * by the protocol decoding function: */ - uint32_t start_off; /* Pulses start offset in the bitmap. */ - uint32_t pulses_count; /* Number of pulses of the full message. */ + uint32_t start_off; /* Pulses start offset in the bitmap. */ + uint32_t pulses_count; /* Number of pulses of the full message. */ /* The following are passed already filled to the decoder. */ - uint32_t short_pulse_dur; /* Microseconds duration of the short pulse. */ + uint32_t short_pulse_dur; /* Microseconds duration of the short pulse. */ /* The following are filled by ProtoView core after the decoder returned * success. */ - uint8_t *bits; /* Bitmap with the signal. */ - uint32_t bits_bytes; /* Number of full bytes in the bitmap, that + uint8_t* bits; /* Bitmap with the signal. */ + uint32_t bits_bytes; /* Number of full bytes in the bitmap, that is 'pulses_count/8' rounded to the next integer. */ } ProtoViewMsgInfo; @@ -197,28 +200,28 @@ typedef enum { typedef struct { ProtoViewFieldType type; - uint32_t len; // Depends on type: - // Bits for integers (signed,unsigned,binary,hex). - // Number of characters for strings. - // Number of nibbles for bytes (1 for each 4 bits). - // Number of digits after dot for floats. - char *name; // Field name. + uint32_t len; // Depends on type: + // Bits for integers (signed,unsigned,binary,hex). + // Number of characters for strings. + // Number of nibbles for bytes (1 for each 4 bits). + // Number of digits after dot for floats. + char* name; // Field name. union { - char *str; // String type. - int64_t value; // Signed integer type. - uint64_t uvalue; // Unsigned integer type. - uint8_t *bytes; // Raw bytes type. - float fvalue; // Float type. + char* str; // String type. + int64_t value; // Signed integer type. + uint64_t uvalue; // Unsigned integer type. + uint8_t* bytes; // Raw bytes type. + float fvalue; // Float type. }; } ProtoViewField; typedef struct ProtoViewFieldSet { - ProtoViewField **fields; + ProtoViewField** fields; uint32_t numfields; } ProtoViewFieldSet; 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, @@ -226,15 +229,15 @@ 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); /* This method is used by the decoder to return the fields it needs * in order to build a new message. This way the message builder view * can ask the user to fill the right set of fields of the specified * type. */ - void (*get_fields)(ProtoViewFieldSet *fields); + void (*get_fields)(ProtoViewFieldSet* fields); /* This method takes the fields supported by the decoder, and * renders a message in 'samples'. */ - void (*build_message)(RawSamplesBuffer *samples, ProtoViewFieldSet *fields); + void (*build_message)(RawSamplesBuffer* samples, ProtoViewFieldSet* fields); } ProtoViewDecoder; extern RawSamplesBuffer *RawSamples, *DetectedSamples; @@ -245,84 +248,130 @@ 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 radio_tx_signal(ProtoViewApp *app, FuriHalSubGhzAsyncTxCallback data_feeder, void *ctx); +void raw_sampling_worker_start(ProtoViewApp* app); +void raw_sampling_worker_stop(ProtoViewApp* app); +void radio_tx_signal(ProtoViewApp* app, FuriHalSubGhzAsyncTxCallback data_feeder, void* ctx); void protoview_rx_callback(bool level, uint32_t duration, void* context); /* signal.c */ uint32_t duration_delta(uint32_t a, uint32_t b); -void reset_current_signal(ProtoViewApp *app); -void scan_for_signal(ProtoViewApp *app,RawSamplesBuffer *source,uint32_t min_duration); -bool bitmap_get(uint8_t *b, uint32_t blen, uint32_t bitpos); -void bitmap_set(uint8_t *b, uint32_t blen, uint32_t bitpos, bool val); -void bitmap_copy(uint8_t *d, uint32_t dlen, uint32_t doff, uint8_t *s, uint32_t slen, uint32_t soff, uint32_t count); -void bitmap_set_pattern(uint8_t *b, uint32_t blen, uint32_t off, const char *pat); -void bitmap_reverse_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); -bool bitmap_match_bitmap(uint8_t *b1, uint32_t b1len, uint32_t b1off, - uint8_t *b2, uint32_t b2len, uint32_t b2off, - uint32_t cmplen); -void bitmap_to_string(char *dst, uint8_t *b, uint32_t blen, - uint32_t off, uint32_t len); -uint32_t convert_from_line_code(uint8_t *buf, uint64_t buflen, uint8_t *bits, uint32_t len, uint32_t offset, const char *zero_pattern, const char *one_pattern); -uint32_t convert_from_diff_manchester(uint8_t *buf, uint64_t buflen, uint8_t *bits, uint32_t len, uint32_t off, bool previous); -void init_msg_info(ProtoViewMsgInfo *i, ProtoViewApp *app); -void free_msg_info(ProtoViewMsgInfo *i); +void reset_current_signal(ProtoViewApp* app); +void scan_for_signal(ProtoViewApp* app, RawSamplesBuffer* source, uint32_t min_duration); +bool bitmap_get(uint8_t* b, uint32_t blen, uint32_t bitpos); +void bitmap_set(uint8_t* b, uint32_t blen, uint32_t bitpos, bool val); +void bitmap_copy( + uint8_t* d, + uint32_t dlen, + uint32_t doff, + uint8_t* s, + uint32_t slen, + uint32_t soff, + uint32_t count); +void bitmap_set_pattern(uint8_t* b, uint32_t blen, uint32_t off, const char* pat); +void bitmap_reverse_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); +bool bitmap_match_bitmap( + uint8_t* b1, + uint32_t b1len, + uint32_t b1off, + uint8_t* b2, + uint32_t b2len, + uint32_t b2off, + uint32_t cmplen); +void bitmap_to_string(char* dst, uint8_t* b, uint32_t blen, uint32_t off, uint32_t len); +uint32_t convert_from_line_code( + uint8_t* buf, + uint64_t buflen, + uint8_t* bits, + uint32_t len, + uint32_t offset, + const char* zero_pattern, + const char* one_pattern); +uint32_t convert_from_diff_manchester( + uint8_t* buf, + uint64_t buflen, + uint8_t* bits, + uint32_t len, + uint32_t off, + bool previous); +void init_msg_info(ProtoViewMsgInfo* i, ProtoViewApp* app); +void free_msg_info(ProtoViewMsgInfo* i); /* signal_file.c */ -bool save_signal(ProtoViewApp *app, const char *filename); +bool save_signal(ProtoViewApp* app, const char* filename); /* 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 render_view_build_message(Canvas *const canvas, ProtoViewApp *app); -void process_input_build_message(ProtoViewApp *app, InputEvent input); -void view_enter_build_message(ProtoViewApp *app); -void view_exit_build_message(ProtoViewApp *app); -void view_enter_direct_sampling(ProtoViewApp *app); -void view_exit_direct_sampling(ProtoViewApp *app); -void view_exit_settings(ProtoViewApp *app); -void view_exit_info(ProtoViewApp *app); -void adjust_raw_view_scale(ProtoViewApp *app, uint32_t short_pulse_dur); +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 render_view_build_message(Canvas* const canvas, ProtoViewApp* app); +void process_input_build_message(ProtoViewApp* app, InputEvent input); +void view_enter_build_message(ProtoViewApp* app); +void view_exit_build_message(ProtoViewApp* app); +void view_enter_direct_sampling(ProtoViewApp* app); +void view_exit_direct_sampling(ProtoViewApp* app); +void view_exit_settings(ProtoViewApp* app); +void view_exit_info(ProtoViewApp* app); +void adjust_raw_view_scale(ProtoViewApp* app, uint32_t short_pulse_dur); /* ui.c */ -int ui_get_current_subview(ProtoViewApp *app); -void ui_show_available_subviews(Canvas *canvas, ProtoViewApp *app, int last_subview); -bool ui_process_subview_updown(ProtoViewApp *app, InputEvent input, int last_subview); -void ui_show_keyboard(ProtoViewApp *app, char *buffer, uint32_t buflen, - void (*done_callback)(void*)); -void ui_dismiss_keyboard(ProtoViewApp *app); -void ui_show_alert(ProtoViewApp *app, const char *text, uint32_t ttl); -void ui_dismiss_alert(ProtoViewApp *app); -void ui_draw_alert_if_needed(Canvas *canvas, ProtoViewApp *app); -void canvas_draw_str_with_border(Canvas* canvas, uint8_t x, uint8_t y, const char* str, Color text_color, Color border_color); +int ui_get_current_subview(ProtoViewApp* app); +void ui_show_available_subviews(Canvas* canvas, ProtoViewApp* app, int last_subview); +bool ui_process_subview_updown(ProtoViewApp* app, InputEvent input, int last_subview); +void ui_show_keyboard( + ProtoViewApp* app, + char* buffer, + uint32_t buflen, + void (*done_callback)(void*)); +void ui_dismiss_keyboard(ProtoViewApp* app); +void ui_show_alert(ProtoViewApp* app, const char* text, uint32_t ttl); +void ui_dismiss_alert(ProtoViewApp* app); +void ui_draw_alert_if_needed(Canvas* canvas, ProtoViewApp* app); +void canvas_draw_str_with_border( + Canvas* canvas, + uint8_t x, + uint8_t y, + const char* str, + Color text_color, + Color border_color); /* fields.c */ -void fieldset_free(ProtoViewFieldSet *fs); -ProtoViewFieldSet *fieldset_new(void); -void fieldset_add_int(ProtoViewFieldSet *fs, const char *name, int64_t val, uint8_t bits); -void fieldset_add_uint(ProtoViewFieldSet *fs, const char *name, uint64_t uval, uint8_t bits); -void fieldset_add_hex(ProtoViewFieldSet *fs, const char *name, uint64_t uval, uint8_t bits); -void fieldset_add_bin(ProtoViewFieldSet *fs, const char *name, uint64_t uval, uint8_t bits); -void fieldset_add_str(ProtoViewFieldSet *fs, const char *name, const char *s, size_t len); -void fieldset_add_bytes(ProtoViewFieldSet *fs, const char *name, const uint8_t *bytes, uint32_t count); -void fieldset_add_float(ProtoViewFieldSet *fs, const char *name, float val, uint32_t digits_after_dot); -const char *field_get_type_name(ProtoViewField *f); -int field_to_string(char *buf, size_t len, ProtoViewField *f); -bool field_set_from_string(ProtoViewField *f, char *buf, size_t len); -bool field_incr_value(ProtoViewField *f, int incr); -void fieldset_copy_matching_fields(ProtoViewFieldSet *dst, ProtoViewFieldSet *src); -void field_set_from_field(ProtoViewField *dst, ProtoViewField *src); +void fieldset_free(ProtoViewFieldSet* fs); +ProtoViewFieldSet* fieldset_new(void); +void fieldset_add_int(ProtoViewFieldSet* fs, const char* name, int64_t val, uint8_t bits); +void fieldset_add_uint(ProtoViewFieldSet* fs, const char* name, uint64_t uval, uint8_t bits); +void fieldset_add_hex(ProtoViewFieldSet* fs, const char* name, uint64_t uval, uint8_t bits); +void fieldset_add_bin(ProtoViewFieldSet* fs, const char* name, uint64_t uval, uint8_t bits); +void fieldset_add_str(ProtoViewFieldSet* fs, const char* name, const char* s, size_t len); +void fieldset_add_bytes( + ProtoViewFieldSet* fs, + const char* name, + const uint8_t* bytes, + uint32_t count); +void fieldset_add_float( + ProtoViewFieldSet* fs, + const char* name, + float val, + uint32_t digits_after_dot); +const char* field_get_type_name(ProtoViewField* f); +int field_to_string(char* buf, size_t len, ProtoViewField* f); +bool field_set_from_string(ProtoViewField* f, char* buf, size_t len); +bool field_incr_value(ProtoViewField* f, int incr); +void fieldset_copy_matching_fields(ProtoViewFieldSet* dst, ProtoViewFieldSet* src); +void field_set_from_field(ProtoViewField* dst, ProtoViewField* src); /* crc.c */ -uint8_t crc8(const uint8_t *data, size_t len, uint8_t init, uint8_t poly); -uint8_t sum_bytes(const uint8_t *data, size_t len, uint8_t init); -uint8_t xor_bytes(const uint8_t *data, size_t len, uint8_t init); +uint8_t crc8(const uint8_t* data, size_t len, uint8_t init, uint8_t poly); +uint8_t sum_bytes(const uint8_t* data, size_t len, uint8_t init); +uint8_t xor_bytes(const uint8_t* data, size_t len, uint8_t init); diff --git a/app_subghz.c b/app_subghz.c index b17d36f18cd..204dcba0d89 100644 --- a/app_subghz.c +++ b/app_subghz.c @@ -9,28 +9,27 @@ #include #include -void raw_sampling_timer_start(ProtoViewApp *app); -void raw_sampling_timer_stop(ProtoViewApp *app); +void raw_sampling_timer_start(ProtoViewApp* app); +void raw_sampling_timer_stop(ProtoViewApp* app); ProtoViewModulation ProtoViewModulations[] = { - {"OOK 650Khz", "FuriHalSubGhzPresetOok650Async", - FuriHalSubGhzPresetOok650Async, NULL, 30}, - {"OOK 270Khz", "FuriHalSubGhzPresetOok270Async", - FuriHalSubGhzPresetOok270Async, NULL, 30}, - {"2FSK 2.38Khz", "FuriHalSubGhzPreset2FSKDev238Async", - FuriHalSubGhzPreset2FSKDev238Async, NULL, 30}, - {"2FSK 47.6Khz", "FuriHalSubGhzPreset2FSKDev476Async", - FuriHalSubGhzPreset2FSKDev476Async, NULL, 30}, - {"TPMS 1 (FSK)", NULL, - 0, (uint8_t*)protoview_subghz_tpms1_fsk_async_regs, 30}, - {"TPMS 2 (OOK)", NULL, - 0, (uint8_t*)protoview_subghz_tpms2_ook_async_regs, 30}, - {"TPMS 3 (GFSK)", NULL, - 0, (uint8_t*)protoview_subghz_tpms3_gfsk_async_regs, 30}, - {"OOK 40kBaud", NULL, - 0, (uint8_t*)protoview_subghz_40k_ook_async_regs, 15}, - {"FSK 40kBaud", NULL, - 0, (uint8_t*)protoview_subghz_40k_fsk_async_regs, 15}, + {"OOK 650Khz", "FuriHalSubGhzPresetOok650Async", FuriHalSubGhzPresetOok650Async, NULL, 30}, + {"OOK 270Khz", "FuriHalSubGhzPresetOok270Async", FuriHalSubGhzPresetOok270Async, NULL, 30}, + {"2FSK 2.38Khz", + "FuriHalSubGhzPreset2FSKDev238Async", + FuriHalSubGhzPreset2FSKDev238Async, + NULL, + 30}, + {"2FSK 47.6Khz", + "FuriHalSubGhzPreset2FSKDev476Async", + FuriHalSubGhzPreset2FSKDev476Async, + NULL, + 30}, + {"TPMS 1 (FSK)", NULL, 0, (uint8_t*)protoview_subghz_tpms1_fsk_async_regs, 30}, + {"TPMS 2 (OOK)", NULL, 0, (uint8_t*)protoview_subghz_tpms2_ook_async_regs, 30}, + {"TPMS 3 (GFSK)", NULL, 0, (uint8_t*)protoview_subghz_tpms3_gfsk_async_regs, 30}, + {"OOK 40kBaud", NULL, 0, (uint8_t*)protoview_subghz_40k_ook_async_regs, 15}, + {"FSK 40kBaud", NULL, 0, (uint8_t*)protoview_subghz_40k_fsk_async_regs, 15}, {NULL, NULL, 0, NULL, 0} /* End of list sentinel. */ }; @@ -38,24 +37,23 @@ ProtoViewModulation ProtoViewModulations[] = { * subghz system and put it into idle state. */ void radio_begin(ProtoViewApp* app) { furi_assert(app); - furi_hal_subghz_reset(); - furi_hal_subghz_idle(); - - /* Power circuits are noisy. Suppressing the charge while we use - * ProtoView will improve the RF performances. */ - furi_hal_power_suppress_charge_enter(); + subghz_devices_reset(app->radio_device); + subghz_devices_idle(app->radio_device); /* 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) { - furi_hal_subghz_load_preset( - ProtoViewModulations[app->modulation].preset); + if(ProtoViewModulations[app->modulation].custom == NULL) { + subghz_devices_load_preset( + app->radio_device, ProtoViewModulations[app->modulation].preset, NULL); } else { - furi_hal_subghz_load_custom_preset( + subghz_devices_load_preset( + app->radio_device, + FuriHalSubGhzPresetCustom, ProtoViewModulations[app->modulation].custom); } - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init( + subghz_devices_get_data_gpio(app->radio_device), GpioModeInput, GpioPullNo, GpioSpeedLow); app->txrx->txrx_state = TxRxStateIDLE; } @@ -63,7 +61,7 @@ void radio_begin(ProtoViewApp* app) { /* We avoid the subghz provided abstractions and put the data in our * simple abstraction: the RawSamples circular buffer. */ -void protoview_rx_callback(bool level, uint32_t duration, void *context) { +void protoview_rx_callback(bool level, uint32_t duration, void* context) { UNUSED(context); /* Add data to the circular buffer. */ raw_samples_add(RawSamples, level, duration); @@ -74,21 +72,28 @@ void protoview_rx_callback(bool level, uint32_t duration, void *context) { /* Setup the CC1101 to start receiving using a background worker. */ uint32_t radio_rx(ProtoViewApp* app) { furi_assert(app); - if(!furi_hal_subghz_is_frequency_valid(app->frequency)) { - furi_crash(TAG" Incorrect RX frequency."); + + if(!subghz_devices_is_frequency_valid(app->radio_device, app->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); + subghz_devices_idle(app->radio_device); /* Put it into idle state in case it is sleeping. */ + uint32_t value = subghz_devices_set_frequency(app->radio_device, app->frequency); FURI_LOG_E(TAG, "Switched to frequency: %lu", value); - 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(protoview_rx_callback, NULL); + + subghz_devices_flush_rx(app->radio_device); + subghz_devices_set_rx(app->radio_device); + + if(!app->txrx->debug_timer_sampling) { + subghz_devices_start_async_rx(app->radio_device, protoview_rx_callback, NULL); } else { + furi_hal_gpio_init( + subghz_devices_get_data_gpio(app->radio_device), + GpioModeInput, + GpioPullNo, + GpioSpeedLow); raw_sampling_worker_start(app); } app->txrx->txrx_state = TxRxStateRx; @@ -99,28 +104,27 @@ uint32_t radio_rx(ProtoViewApp* app) { void radio_rx_end(ProtoViewApp* app) { furi_assert(app); - if (app->txrx->txrx_state == TxRxStateRx) { - if (!app->txrx->debug_timer_sampling) { - furi_hal_subghz_stop_async_rx(); + if(app->txrx->txrx_state == TxRxStateRx) { + if(!app->txrx->debug_timer_sampling) { + subghz_devices_stop_async_rx(app->radio_device); } else { raw_sampling_worker_stop(app); } } - furi_hal_subghz_idle(); + subghz_devices_idle(app->radio_device); app->txrx->txrx_state = TxRxStateIDLE; } /* Put radio on sleep. */ void radio_sleep(ProtoViewApp* app) { furi_assert(app); - if (app->txrx->txrx_state == TxRxStateRx) { + if(app->txrx->txrx_state == TxRxStateRx) { /* Stop the asynchronous receiving system before putting the * chip into sleep. */ radio_rx_end(app); } - furi_hal_subghz_sleep(); + subghz_devices_sleep(app->radio_device); app->txrx->txrx_state = TxRxStateSleep; - furi_hal_power_suppress_charge_exit(); } /* =============================== Transmission ============================= */ @@ -128,25 +132,23 @@ void radio_sleep(ProtoViewApp* app) { /* This function suspends the current RX state, switches to TX mode, * transmits the signal provided by the callback data_feeder, and later * restores the RX state if there was one. */ -void radio_tx_signal(ProtoViewApp *app, FuriHalSubGhzAsyncTxCallback data_feeder, void *ctx) { +void radio_tx_signal(ProtoViewApp* app, FuriHalSubGhzAsyncTxCallback data_feeder, void* ctx) { TxRxState oldstate = app->txrx->txrx_state; - if (oldstate == TxRxStateRx) radio_rx_end(app); + if(oldstate == TxRxStateRx) radio_rx_end(app); radio_begin(app); - furi_hal_subghz_idle(); - uint32_t value = furi_hal_subghz_set_frequency_and_path(app->frequency); + subghz_devices_idle(app->radio_device); + uint32_t value = subghz_devices_set_frequency(app->radio_device, app->frequency); FURI_LOG_E(TAG, "Switched to frequency: %lu", value); - furi_hal_gpio_write(&gpio_cc1101_g0, false); - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); - furi_hal_subghz_start_async_tx(data_feeder, ctx); - while(!furi_hal_subghz_is_async_tx_complete()) furi_delay_ms(10); - furi_hal_subghz_stop_async_tx(); - furi_hal_subghz_idle(); + subghz_devices_start_async_tx(app->radio_device, data_feeder, ctx); + while(!subghz_devices_is_async_complete_tx(app->radio_device)) furi_delay_ms(10); + subghz_devices_stop_async_tx(app->radio_device); + subghz_devices_idle(app->radio_device); radio_begin(app); - if (oldstate == TxRxStateRx) radio_rx(app); + if(oldstate == TxRxStateRx) radio_rx(app); } /* ============================= Raw sampling mode ============================= @@ -156,15 +158,15 @@ void radio_tx_signal(ProtoViewApp *app, FuriHalSubGhzAsyncTxCallback data_feeder * 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) { + bool level = furi_hal_gpio_read(subghz_devices_get_data_gpio(app->radio_device)); + 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; @@ -172,13 +174,15 @@ 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); + furi_hal_bus_enable(FuriHalBusTIM2); + 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); @@ -191,12 +195,12 @@ 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); LL_TIM_DisableIT_UPDATE(TIM2); furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, NULL, NULL); - LL_TIM_DeInit(TIM2); + furi_hal_bus_disable(FuriHalBusTIM2); FURI_CRITICAL_EXIT(); } diff --git a/application.fam b/application.fam index 6cd31372e2e..705fb1b0bfc 100644 --- a/application.fam +++ b/application.fam @@ -3,10 +3,12 @@ App( name="ProtoView", apptype=FlipperAppType.EXTERNAL, entry_point="protoview_app_entry", - cdefines=["APP_PROTOVIEW"], requires=["gui"], stack_size=8*1024, order=50, fap_icon="appicon.png", - fap_category="Tools", + fap_category="Sub-GHz", + fap_author="@antirez & (fixes by @xMasterX)", + fap_version="1.1", + fap_description="Digital signal detection, visualization, editing and reply tool", ) diff --git a/binaries/README.md b/binaries/README.md deleted file mode 100644 index 58113d77989..00000000000 --- a/binaries/README.md +++ /dev/null @@ -1,10 +0,0 @@ -This is the binary distribution of the application. If you don't want -to build it from source, just take `protoview.fap` file and drop it into the -following Flipper Zero location: - - /ext/apps/Tools - -The `ext` part means that we are in the SD card. So if you don't want -to use the Android (or other) application to upload the file, -you can just take out the SD card, insert it in your computer, -copy the fine into `apps/Tools`, and that's it. diff --git a/binaries/protoview.fap b/binaries/protoview.fap deleted file mode 100644 index a462f6c34bc04ea224d1610e5288da693ad4b86a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 51588 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
    pM>yyGNroMshU;+OPFK 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 +#include /* ========================== DATA RATE SETTINGS =============================== * * This is how to configure registers MDMCFG3 and MDMCFG4. @@ -75,7 +75,8 @@ static uint8_t protoview_subghz_tpms1_fsk_async_regs[][2] = { // // Modem Configuration {CC1101_MDMCFG0, 0x00}, {CC1101_MDMCFG1, 0x02}, - {CC1101_MDMCFG2, 0x04}, // Format 2-FSK/FM, No preamble/sync, Disable (current optimized). Other code reading TPMS uses GFSK, but should be the same when in RX mode. + {CC1101_MDMCFG2, + 0x04}, // Format 2-FSK/FM, No preamble/sync, Disable (current optimized). Other code reading TPMS uses GFSK, but should be the same when in RX mode. {CC1101_MDMCFG3, 0x93}, // Data rate is 20kBaud {CC1101_MDMCFG4, 0x59}, // Rx bandwidth filter is 325 kHz {CC1101_DEVIATN, 0x41}, // Deviation 28.56 kHz @@ -105,8 +106,10 @@ static uint8_t protoview_subghz_tpms1_fsk_async_regs[][2] = { {0, 0}, /* CC1101 2FSK PATABLE. */ - {0xC0, 0}, {0,0}, {0,0}, {0,0} -}; + {0xC0, 0}, + {0, 0}, + {0, 0}, + {0, 0}}; /* This is like the default Flipper OOK 640Khz bandwidth preset, but * the bandwidth is changed to 10kBaud to accomodate TPMS frequency. */ @@ -155,8 +158,10 @@ static const uint8_t protoview_subghz_tpms2_ook_async_regs[][2] = { {0, 0}, /* CC1101 OOK PATABLE. */ - {0, 0xC0}, {0,0}, {0,0}, {0,0} -}; + {0, 0xC0}, + {0, 0}, + {0, 0}, + {0, 0}}; /* GFSK 19k dev, 325 Khz filter, 20kBaud. Different AGI settings. * Works well with Toyota. */ @@ -202,8 +207,10 @@ static uint8_t protoview_subghz_tpms3_gfsk_async_regs[][2] = { {0, 0}, /* CC1101 2FSK PATABLE. */ - {0xC0, 0}, {0,0}, {0,0}, {0,0} -}; + {0xC0, 0}, + {0, 0}, + {0, 0}, + {0, 0}}; /* 40 KBaud, 2FSK, 28 kHz deviation, 270 Khz bandwidth filter. */ static uint8_t protoview_subghz_40k_fsk_async_regs[][2] = { @@ -220,7 +227,8 @@ static uint8_t protoview_subghz_40k_fsk_async_regs[][2] = { // // Modem Configuration {CC1101_MDMCFG0, 0x00}, {CC1101_MDMCFG1, 0x02}, - {CC1101_MDMCFG2, 0x04}, // Format 2-FSK/FM, No preamble/sync, Disable (current optimized). Other code reading TPMS uses GFSK, but should be the same when in RX mode. + {CC1101_MDMCFG2, + 0x04}, // Format 2-FSK/FM, No preamble/sync, Disable (current optimized). Other code reading TPMS uses GFSK, but should be the same when in RX mode. {CC1101_MDMCFG3, 0x93}, // Data rate is 40kBaud {CC1101_MDMCFG4, 0x6A}, // 6 = BW filter 270kHz, A = Data rate exp {CC1101_DEVIATN, 0x41}, // Deviation 28kHz @@ -250,8 +258,10 @@ static uint8_t protoview_subghz_40k_fsk_async_regs[][2] = { {0, 0}, /* CC1101 2FSK PATABLE. */ - {0xC0, 0}, {0,0}, {0,0}, {0,0} -}; + {0xC0, 0}, + {0, 0}, + {0, 0}, + {0, 0}}; /* This is like the default Flipper OOK 640Khz bandwidth preset, but * the bandwidth is changed to 40kBaud, in order to receive signals @@ -301,6 +311,7 @@ static const uint8_t protoview_subghz_40k_ook_async_regs[][2] = { {0, 0}, /* CC1101 OOK PATABLE. */ - {0, 0xC0}, {0,0}, {0,0}, {0,0} -}; - + {0, 0xC0}, + {0, 0}, + {0, 0}, + {0, 0}}; diff --git a/fields.c b/fields.c index 12db417d384..18dee65024e 100644 --- a/fields.c +++ b/fields.c @@ -7,8 +7,8 @@ /* Create a new field of the specified type. Without populating its * type-specific value. */ -static ProtoViewField *field_new(ProtoViewFieldType type, const char *name) { - ProtoViewField *f = malloc(sizeof(*f)); +static ProtoViewField* field_new(ProtoViewFieldType type, const char* name) { + ProtoViewField* f = malloc(sizeof(*f)); f->type = type; f->name = strdup(name); return f; @@ -16,72 +16,80 @@ static ProtoViewField *field_new(ProtoViewFieldType type, const char *name) { /* Free only the auxiliary data of a field, used to represent the * current type. Name and type are not touched. */ -static void field_free_aux_data(ProtoViewField *f) { +static void field_free_aux_data(ProtoViewField* f) { switch(f->type) { - case FieldTypeStr: free(f->str); break; - case FieldTypeBytes: free(f->bytes); break; - default: break; // Nothing to free for other types. + case FieldTypeStr: + free(f->str); + break; + case FieldTypeBytes: + free(f->bytes); + break; + default: + break; // Nothing to free for other types. } } /* Free a field an associated data. */ -static void field_free(ProtoViewField *f) { +static void field_free(ProtoViewField* f) { field_free_aux_data(f); free(f->name); free(f); } /* Return the type of the field as string. */ -const char *field_get_type_name(ProtoViewField *f) { +const char* field_get_type_name(ProtoViewField* f) { switch(f->type) { - case FieldTypeStr: return "str"; - case FieldTypeSignedInt: return "int"; - case FieldTypeUnsignedInt: return "uint"; - case FieldTypeBinary: return "bin"; - case FieldTypeHex: return "hex"; - case FieldTypeBytes: return "bytes"; - case FieldTypeFloat: return "float"; + case FieldTypeStr: + return "str"; + case FieldTypeSignedInt: + return "int"; + case FieldTypeUnsignedInt: + return "uint"; + case FieldTypeBinary: + return "bin"; + case FieldTypeHex: + return "hex"; + case FieldTypeBytes: + return "bytes"; + case FieldTypeFloat: + return "float"; } return "unknown"; } /* Set a string representation of the specified field in buf. */ -int field_to_string(char *buf, size_t len, ProtoViewField *f) { +int field_to_string(char* buf, size_t len, ProtoViewField* f) { switch(f->type) { case FieldTypeStr: - return snprintf(buf,len,"%s", f->str); + return snprintf(buf, len, "%s", f->str); case FieldTypeSignedInt: - return snprintf(buf,len,"%lld", (long long) f->value); + return snprintf(buf, len, "%lld", (long long)f->value); case FieldTypeUnsignedInt: - return snprintf(buf,len,"%llu", (unsigned long long) f->uvalue); - case FieldTypeBinary: - { - uint64_t test_bit = (1 << (f->len-1)); - uint64_t idx = 0; - while(idx < len-1 && test_bit) { - buf[idx++] = (f->uvalue & test_bit) ? '1' : '0'; - test_bit >>= 1; - } - buf[idx] = 0; - return idx; + return snprintf(buf, len, "%llu", (unsigned long long)f->uvalue); + case FieldTypeBinary: { + uint64_t test_bit = (1 << (f->len - 1)); + uint64_t idx = 0; + while(idx < len - 1 && test_bit) { + buf[idx++] = (f->uvalue & test_bit) ? '1' : '0'; + test_bit >>= 1; } + buf[idx] = 0; + return idx; + } case FieldTypeHex: - return snprintf(buf, len, "%*llX", (int)(f->len+7)/8, f->uvalue); + return snprintf(buf, len, "%*llX", (int)(f->len + 7) / 8, f->uvalue); case FieldTypeFloat: return snprintf(buf, len, "%.*f", (int)f->len, (double)f->fvalue); - case FieldTypeBytes: - { - uint64_t idx = 0; - while(idx < len-1 && idx < f->len) { - const char *charset = "0123456789ABCDEF"; - uint32_t nibble = idx & 1 ? - (f->bytes[idx/2] & 0xf) : - (f->bytes[idx/2] >> 4); - buf[idx++] = charset[nibble]; - } - buf[idx] = 0; - return idx; + case FieldTypeBytes: { + uint64_t idx = 0; + while(idx < len - 1 && idx < f->len) { + const char* charset = "0123456789ABCDEF"; + uint32_t nibble = idx & 1 ? (f->bytes[idx / 2] & 0xf) : (f->bytes[idx / 2] >> 4); + buf[idx++] = charset[nibble]; } + buf[idx] = 0; + return idx; + } } return 0; } @@ -96,7 +104,7 @@ int field_to_string(char *buf, size_t len, ProtoViewField *f) { * The function returns true if the filed was successfully set to the * new value, otherwise if the specified value is invalid for the * field type, false is returned. */ -bool field_set_from_string(ProtoViewField *f, char *buf, size_t len) { +bool field_set_from_string(ProtoViewField* f, char* buf, size_t len) { // Initialize values to zero since the Flipper sscanf() implementation // is fuzzy... may populate only part of the value. long long val = 0; @@ -107,80 +115,78 @@ bool field_set_from_string(ProtoViewField *f, char *buf, size_t len) { case FieldTypeStr: free(f->str); f->len = len; - f->str = malloc(len+1); - memcpy(f->str,buf,len+1); + f->str = malloc(len + 1); + memcpy(f->str, buf, len + 1); break; case FieldTypeSignedInt: - if (!sscanf(buf,"%lld",&val)) return false; + if(!sscanf(buf, "%lld", &val)) return false; f->value = val; break; case FieldTypeUnsignedInt: - if (!sscanf(buf,"%llu",&uval)) return false; + if(!sscanf(buf, "%llu", &uval)) return false; f->uvalue = uval; break; - case FieldTypeBinary: - { - uint64_t bit_to_set = (1 << (len-1)); - uint64_t idx = 0; - uval = 0; - while(buf[idx]) { - if (buf[idx] == '1') uval |= bit_to_set; - else if (buf[idx] != '0') return false; - bit_to_set >>= 1; - idx++; - } - f->uvalue = uval; + case FieldTypeBinary: { + uint64_t bit_to_set = (1 << (len - 1)); + uint64_t idx = 0; + uval = 0; + while(buf[idx]) { + if(buf[idx] == '1') + uval |= bit_to_set; + else if(buf[idx] != '0') + return false; + bit_to_set >>= 1; + idx++; } - break; + f->uvalue = uval; + } break; case FieldTypeHex: - if (!sscanf(buf,"%llx",&uval) && - !sscanf(buf,"%llX",&uval)) return false; + if(!sscanf(buf, "%llx", &uval) && !sscanf(buf, "%llX", &uval)) return false; f->uvalue = uval; break; case FieldTypeFloat: - if (!sscanf(buf,"%f",&fval)) return false; + if(!sscanf(buf, "%f", &fval)) return false; f->fvalue = fval; break; - case FieldTypeBytes: - { - if (len > f->len) return false; - uint64_t idx = 0; - while(buf[idx]) { - uint8_t nibble = 0; - char c = toupper(buf[idx]); - if (c >= '0' && c <= '9') nibble = c-'0'; - else if (c >= 'A' && c <= 'F') nibble = 10+(c-'A'); - else return false; + case FieldTypeBytes: { + if(len > f->len) return false; + uint64_t idx = 0; + while(buf[idx]) { + uint8_t nibble = 0; + char c = toupper(buf[idx]); + if(c >= '0' && c <= '9') + nibble = c - '0'; + else if(c >= 'A' && c <= 'F') + nibble = 10 + (c - 'A'); + else + return false; - if (idx & 1) { - f->bytes[idx/2] = - (f->bytes[idx/2] & 0xF0) | nibble; - } else { - f->bytes[idx/2] = - (f->bytes[idx/2] & 0x0F) | (nibble<<4); - } - idx++; + if(idx & 1) { + f->bytes[idx / 2] = (f->bytes[idx / 2] & 0xF0) | nibble; + } else { + f->bytes[idx / 2] = (f->bytes[idx / 2] & 0x0F) | (nibble << 4); } - buf[idx] = 0; + idx++; } - break; + buf[idx] = 0; + } break; } return true; } /* Set the 'dst' field to contain a copy of the value of the 'src' * field. The field name is not modified. */ -void field_set_from_field(ProtoViewField *dst, ProtoViewField *src) { +void field_set_from_field(ProtoViewField* dst, ProtoViewField* src) { field_free_aux_data(dst); dst->type = src->type; dst->len = src->len; - switch(src->type) { + switch(src->type) { case FieldTypeStr: dst->str = strdup(src->str); break; case FieldTypeBytes: dst->bytes = malloc(src->len); - memcpy(dst->bytes,src->bytes,dst->len); + memcpy(dst->bytes, src->bytes, dst->len); break; case FieldTypeSignedInt: dst->value = src->value; @@ -199,164 +205,164 @@ void field_set_from_field(ProtoViewField *dst, ProtoViewField *src) { /* Increment the specified field value of 'incr'. If the field type * does not support increments false is returned, otherwise the * action is performed. */ -bool field_incr_value(ProtoViewField *f, int incr) { +bool field_incr_value(ProtoViewField* f, int incr) { switch(f->type) { - case FieldTypeStr: return false; - case FieldTypeSignedInt: { - /* Wrap around depending on the number of bits (f->len) + case FieldTypeStr: + return false; + case FieldTypeSignedInt: { + /* Wrap around depending on the number of bits (f->len) * the integer was declared to have. */ - int64_t max = (1ULL << (f->len-1))-1; - int64_t min = -max-1; - int64_t v = (int64_t)f->value + incr; - if (v > max) v = min+(v-max-1); - if (v < min) v = max+(v-min+1); - f->value = v; - break; - } - case FieldTypeBinary: - case FieldTypeHex: - case FieldTypeUnsignedInt: { - /* Wrap around like for the unsigned case, but here + int64_t max = (1ULL << (f->len - 1)) - 1; + int64_t min = -max - 1; + int64_t v = (int64_t)f->value + incr; + if(v > max) v = min + (v - max - 1); + if(v < min) v = max + (v - min + 1); + f->value = v; + break; + } + case FieldTypeBinary: + case FieldTypeHex: + case FieldTypeUnsignedInt: { + /* Wrap around like for the unsigned case, but here * is simpler. */ - uint64_t max = (1ULL << f->len)-1; // Broken for 64 bits. - uint64_t uv = (uint64_t)f->value + incr; - if (uv > max) uv = uv & max; - f->uvalue = uv; - break; - } - case FieldTypeFloat: - f->fvalue += incr; - break; - case FieldTypeBytes: { - // For bytes we only support single unit increments. - if (incr != -1 && incr != 1) return false; - for (int j = f->len-1; j >= 0; j--) { - uint8_t nibble = (j&1) ? (f->bytes[j/2] & 0x0F) : - ((f->bytes[j/2] & 0xF0) >> 4); + uint64_t max = (1ULL << f->len) - 1; // Broken for 64 bits. + uint64_t uv = (uint64_t)f->value + incr; + if(uv > max) uv = uv & max; + f->uvalue = uv; + break; + } + case FieldTypeFloat: + f->fvalue += incr; + break; + case FieldTypeBytes: { + // For bytes we only support single unit increments. + if(incr != -1 && incr != 1) return false; + for(int j = f->len - 1; j >= 0; j--) { + uint8_t nibble = (j & 1) ? (f->bytes[j / 2] & 0x0F) : ((f->bytes[j / 2] & 0xF0) >> 4); - nibble += incr; - nibble &= 0x0F; + nibble += incr; + nibble &= 0x0F; - f->bytes[j/2] = (j&1) ? ((f->bytes[j/2] & 0xF0) | nibble) : - ((f->bytes[j/2] & 0x0F) | (nibble<<4)); + f->bytes[j / 2] = (j & 1) ? ((f->bytes[j / 2] & 0xF0) | nibble) : + ((f->bytes[j / 2] & 0x0F) | (nibble << 4)); - /* Propagate the operation on overflow of this nibble. */ - if ((incr == 1 && nibble == 0) || - (incr == -1 && nibble == 0xf)) - { - continue; - } - break; // Otherwise stop the loop here. + /* Propagate the operation on overflow of this nibble. */ + if((incr == 1 && nibble == 0) || (incr == -1 && nibble == 0xf)) { + continue; } - break; + break; // Otherwise stop the loop here. } + break; + } } return true; } - /* Free a field set and its contained fields. */ -void fieldset_free(ProtoViewFieldSet *fs) { - for (uint32_t j = 0; j < fs->numfields; j++) - field_free(fs->fields[j]); +void fieldset_free(ProtoViewFieldSet* fs) { + for(uint32_t j = 0; j < fs->numfields; j++) field_free(fs->fields[j]); free(fs->fields); free(fs); } /* Allocate and init an empty field set. */ -ProtoViewFieldSet *fieldset_new(void) { - ProtoViewFieldSet *fs = malloc(sizeof(*fs)); +ProtoViewFieldSet* fieldset_new(void) { + ProtoViewFieldSet* fs = malloc(sizeof(*fs)); fs->numfields = 0; fs->fields = NULL; return fs; } /* Append an already allocated field at the end of the specified field set. */ -static void fieldset_add_field(ProtoViewFieldSet *fs, ProtoViewField *field) { +static void fieldset_add_field(ProtoViewFieldSet* fs, ProtoViewField* field) { fs->numfields++; - fs->fields = realloc(fs->fields,sizeof(ProtoViewField*)*fs->numfields); - fs->fields[fs->numfields-1] = field; + fs->fields = realloc(fs->fields, sizeof(ProtoViewField*) * fs->numfields); + fs->fields[fs->numfields - 1] = field; } /* Allocate and append an integer field. */ -void fieldset_add_int(ProtoViewFieldSet *fs, const char *name, int64_t val, uint8_t bits) { - ProtoViewField *f = field_new(FieldTypeSignedInt,name); +void fieldset_add_int(ProtoViewFieldSet* fs, const char* name, int64_t val, uint8_t bits) { + ProtoViewField* f = field_new(FieldTypeSignedInt, name); f->value = val; f->len = bits; - fieldset_add_field(fs,f); + fieldset_add_field(fs, f); } /* Allocate and append an unsigned field. */ -void fieldset_add_uint(ProtoViewFieldSet *fs, const char *name, uint64_t uval, uint8_t bits) { - ProtoViewField *f = field_new(FieldTypeUnsignedInt,name); +void fieldset_add_uint(ProtoViewFieldSet* fs, const char* name, uint64_t uval, uint8_t bits) { + ProtoViewField* f = field_new(FieldTypeUnsignedInt, name); f->uvalue = uval; f->len = bits; - fieldset_add_field(fs,f); + fieldset_add_field(fs, f); } /* Allocate and append a hex field. This is an unsigned number but * with an hex representation. */ -void fieldset_add_hex(ProtoViewFieldSet *fs, const char *name, uint64_t uval, uint8_t bits) { - ProtoViewField *f = field_new(FieldTypeHex,name); +void fieldset_add_hex(ProtoViewFieldSet* fs, const char* name, uint64_t uval, uint8_t bits) { + ProtoViewField* f = field_new(FieldTypeHex, name); f->uvalue = uval; f->len = bits; - fieldset_add_field(fs,f); + fieldset_add_field(fs, f); } /* Allocate and append a bin field. This is an unsigned number but * with a binary representation. */ -void fieldset_add_bin(ProtoViewFieldSet *fs, const char *name, uint64_t uval, uint8_t bits) { - ProtoViewField *f = field_new(FieldTypeBinary,name); +void fieldset_add_bin(ProtoViewFieldSet* fs, const char* name, uint64_t uval, uint8_t bits) { + ProtoViewField* f = field_new(FieldTypeBinary, name); f->uvalue = uval; f->len = bits; - fieldset_add_field(fs,f); + fieldset_add_field(fs, f); } /* Allocate and append a string field. The string 's' does not need to point * to a null terminated string, but must have at least 'len' valid bytes * starting from the pointer. The field object will be correctly null * terminated. */ -void fieldset_add_str(ProtoViewFieldSet *fs, const char *name, const char *s, size_t len) { - ProtoViewField *f = field_new(FieldTypeStr,name); +void fieldset_add_str(ProtoViewFieldSet* fs, const char* name, const char* s, size_t len) { + ProtoViewField* f = field_new(FieldTypeStr, name); f->len = len; - f->str = malloc(len+1); - memcpy(f->str,s,len); + f->str = malloc(len + 1); + memcpy(f->str, s, len); f->str[len] = 0; - fieldset_add_field(fs,f); + fieldset_add_field(fs, f); } /* Allocate and append a bytes field. Note that 'count' is specified in * nibbles (bytes*2). */ -void fieldset_add_bytes(ProtoViewFieldSet *fs, const char *name, const uint8_t *bytes, uint32_t count_nibbles) { - uint32_t numbytes = (count_nibbles+count_nibbles%2)/2; - ProtoViewField *f = field_new(FieldTypeBytes,name); +void fieldset_add_bytes( + ProtoViewFieldSet* fs, + const char* name, + const uint8_t* bytes, + uint32_t count_nibbles) { + uint32_t numbytes = (count_nibbles + count_nibbles % 2) / 2; + ProtoViewField* f = field_new(FieldTypeBytes, name); f->bytes = malloc(numbytes); - memcpy(f->bytes,bytes,numbytes); + memcpy(f->bytes, bytes, numbytes); f->len = count_nibbles; - fieldset_add_field(fs,f); + fieldset_add_field(fs, f); } /* Allocate and append a float field. */ -void fieldset_add_float(ProtoViewFieldSet *fs, const char *name, float val, uint32_t digits_after_dot) { - ProtoViewField *f = field_new(FieldTypeFloat,name); +void fieldset_add_float( + ProtoViewFieldSet* fs, + const char* name, + float val, + uint32_t digits_after_dot) { + ProtoViewField* f = field_new(FieldTypeFloat, name); f->fvalue = val; f->len = digits_after_dot; - fieldset_add_field(fs,f); + fieldset_add_field(fs, f); } /* For each field of the destination filedset 'dst', look for a matching * field name/type in the source fieldset 'src', and if one is found copy * its value into the 'dst' field. */ -void fieldset_copy_matching_fields(ProtoViewFieldSet *dst, - ProtoViewFieldSet *src) -{ - for (uint32_t j = 0; j < dst->numfields; j++) { - for (uint32_t i = 0; i < src->numfields; i++) { - if (dst->fields[j]->type == src->fields[i]->type && - !strcmp(dst->fields[j]->name,src->fields[i]->name)) - { - field_set_from_field(dst->fields[j], - src->fields[i]); +void fieldset_copy_matching_fields(ProtoViewFieldSet* dst, ProtoViewFieldSet* src) { + for(uint32_t j = 0; j < dst->numfields; j++) { + for(uint32_t i = 0; i < src->numfields; i++) { + if(dst->fields[j]->type == src->fields[i]->type && + !strcmp(dst->fields[j]->name, src->fields[i]->name)) { + field_set_from_field(dst->fields[j], src->fields[i]); } } } diff --git a/helpers/radio_device_loader.c b/helpers/radio_device_loader.c new file mode 100644 index 00000000000..d2cffde5830 --- /dev/null +++ b/helpers/radio_device_loader.c @@ -0,0 +1,64 @@ +#include "radio_device_loader.h" + +#include +#include + +static void radio_device_loader_power_on() { + uint8_t attempts = 0; + while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) { + furi_hal_power_enable_otg(); + //CC1101 power-up time + furi_delay_ms(10); + } +} + +static void radio_device_loader_power_off() { + if(furi_hal_power_is_otg_enabled()) furi_hal_power_disable_otg(); +} + +bool radio_device_loader_is_connect_external(const char* name) { + bool is_connect = false; + bool is_otg_enabled = furi_hal_power_is_otg_enabled(); + + if(!is_otg_enabled) { + radio_device_loader_power_on(); + } + + const SubGhzDevice* device = subghz_devices_get_by_name(name); + if(device) { + is_connect = subghz_devices_is_connect(device); + } + + if(!is_otg_enabled) { + radio_device_loader_power_off(); + } + return is_connect; +} + +const SubGhzDevice* radio_device_loader_set( + const SubGhzDevice* current_radio_device, + SubGhzRadioDeviceType radio_device_type) { + const SubGhzDevice* radio_device; + + if(radio_device_type == SubGhzRadioDeviceTypeExternalCC1101 && + radio_device_loader_is_connect_external(SUBGHZ_DEVICE_CC1101_EXT_NAME)) { + radio_device_loader_power_on(); + radio_device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_EXT_NAME); + subghz_devices_begin(radio_device); + } else if(current_radio_device == NULL) { + radio_device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_INT_NAME); + } else { + radio_device_loader_end(current_radio_device); + radio_device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_INT_NAME); + } + + return radio_device; +} + +void radio_device_loader_end(const SubGhzDevice* radio_device) { + furi_assert(radio_device); + radio_device_loader_power_off(); + if(radio_device != subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_INT_NAME)) { + subghz_devices_end(radio_device); + } +} \ No newline at end of file diff --git a/helpers/radio_device_loader.h b/helpers/radio_device_loader.h new file mode 100644 index 00000000000..bee4e2c362b --- /dev/null +++ b/helpers/radio_device_loader.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +/** SubGhzRadioDeviceType */ +typedef enum { + SubGhzRadioDeviceTypeInternal, + SubGhzRadioDeviceTypeExternalCC1101, +} SubGhzRadioDeviceType; + +const SubGhzDevice* radio_device_loader_set( + const SubGhzDevice* current_radio_device, + SubGhzRadioDeviceType radio_device_type); + +void radio_device_loader_end(const SubGhzDevice* radio_device); \ No newline at end of file diff --git a/img/1.png b/img/1.png new file mode 100644 index 0000000000000000000000000000000000000000..68e8688a103f9cf08a0dcd8b5536635c5c6caa25 GIT binary patch literal 1465 zcmeAS@N?(olHy`uVBq!ia0y~yU;;8388|?c*QZDWAjMhW5n0T@z%2~Ij105pNH8$4 zW_!9ghE&XXd)Ir?Eh`3xi|j{>Hra_y{JL@8mD!URuZI1Zyz{xwvd$Avl&;6-gS7=|yL1fngB~ zgMt@s3g{{zc+h<(t=i6%;dp_lfWs691{Y2ShbxcYa59AK*&kie_BGs{fgzBIK|mEq zy<=v$aq)NMfB$p8Y~OIe4L>Et;4np*p+SR@p+krS3gUnU{YP^TFa)k+Vj@X1*tV)U zrXT+P{8czx^}SZarunLUGONwrF`=UXrl<1~naekg5U zJ8w7dfvr2ZIPirZg>n$ffWiOo&%e@dG7J`ts~ZSK85S$$()}M~e1D#u@mux1x5VA$ z8^1||!=N|HX#33nveRe0HQv|%6cJg(B#3Vn$IKZ%7b6k`G+huuU3q+29+*IV**@%J zY_PN>DcH@=zh=)UBG1-0oFPDK<+n$E%4<{oEm`_x$Kv<%EsA22&l+EQvBUe!H{%`0 zcSpF{FD5FT!cu0@?dk^8^LylK-+hxif1T||7*ZDT80sv$w9`}UX?l^ zx<2v#|&za9+uFv{>E82E-`jxQHlCs%n_cFA@>#>6Eu_y8S)T{l(b~ZxZ{Tzwgi6oc7;)+x_Hui_dfYK7!<*vpZuS=wz=; zzyB)g$KL0g)9e?27mZ|QNDzb;JJ}2eMAjAgpAmAu_j+^I+2q5O341J TyB3Lk1x2l=tDnm{r-UW|CD8F) literal 0 HcmV?d00001 diff --git a/img/2.png b/img/2.png new file mode 100644 index 0000000000000000000000000000000000000000..ff8ea0d6136a913960657007271b85263a04cd21 GIT binary patch literal 2133 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 N-jKXrxo+>j{s#s;amxSz literal 0 HcmV?d00001 diff --git a/protocols/b4b1.c b/protocols/b4b1.c index d3c3d405d90..6d0b8237a19 100644 --- a/protocols/b4b1.c +++ b/protocols/b4b1.c @@ -12,11 +12,11 @@ #include "../app.h" -static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo *info) { - if (numbits < 30) return false; +static bool decode(uint8_t* bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo* info) { + if(numbits < 30) return false; /* Test different pulse + gap + first byte possibilities. */ - const char *sync_patterns[6] = { + const char* sync_patterns[6] = { "100000000000000000000000000000011101", /* 30 times gap + one. */ "100000000000000000000000000000010001", /* 30 times gap + zero. */ "1000000000000000000000000000000011101", /* 31 times gap + one. */ @@ -27,70 +27,67 @@ 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 id:%d at: %lu",j,off); + if(off == BITMAP_SEEK_NOT_FOUND) return false; + if(DEBUG_MSG) FURI_LOG_E(TAG, "B4B1 preamble id:%d at: %lu", j, off); info->start_off = off; // Seek data setction. Why -5? Last 5 half-bit-times are data. - off += strlen(sync_patterns[j])-5; + off += strlen(sync_patterns[j]) - 5; 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; + if(DEBUG_MSG) FURI_LOG_E(TAG, "B4B1 decoded: %lu", decoded); + if(decoded < 24) return false; - off += 24*4; // seek to end symbol offset to calculate the length. + off += 24 * 4; // seek to end symbol offset to calculate the length. off++; // In this protocol there is a final pulse as terminator. info->pulses_count = off - info->start_off; - fieldset_add_bytes(info->fieldset,"id",d,5); - fieldset_add_uint(info->fieldset,"button",d[2]&0xf,4); + fieldset_add_bytes(info->fieldset, "id", d, 5); + fieldset_add_uint(info->fieldset, "button", d[2] & 0xf, 4); return true; } /* Give fields and defaults for the signal creator. */ -static void get_fields(ProtoViewFieldSet *fieldset) { - uint8_t default_id[3]= {0xAB, 0xCD, 0xE0}; - fieldset_add_bytes(fieldset,"id",default_id,5); - fieldset_add_uint(fieldset,"button",1,4); +static void get_fields(ProtoViewFieldSet* fieldset) { + uint8_t default_id[3] = {0xAB, 0xCD, 0xE0}; + fieldset_add_bytes(fieldset, "id", default_id, 5); + fieldset_add_uint(fieldset, "button", 1, 4); } /* Create a signal. */ -static void build_message(RawSamplesBuffer *samples, ProtoViewFieldSet *fs) -{ +static void build_message(RawSamplesBuffer* samples, ProtoViewFieldSet* fs) { uint32_t te = 334; // Short pulse duration in microseconds. // Sync: 1 te pulse, 31 te gap. - raw_samples_add(samples,true,te); - raw_samples_add(samples,false,te*31); + raw_samples_add(samples, true, te); + raw_samples_add(samples, false, te * 31); // ID + button state uint8_t data[3]; - memcpy(data,fs->fields[0]->bytes,3); - data[2] = (data[2]&0xF0) | (fs->fields[1]->uvalue & 0xF); - for (uint32_t j = 0; j < 24; j++) { - if (bitmap_get(data,sizeof(data),j)) { - raw_samples_add(samples,true,te*3); - raw_samples_add(samples,false,te); + memcpy(data, fs->fields[0]->bytes, 3); + data[2] = (data[2] & 0xF0) | (fs->fields[1]->uvalue & 0xF); + for(uint32_t j = 0; j < 24; j++) { + if(bitmap_get(data, sizeof(data), j)) { + raw_samples_add(samples, true, te * 3); + raw_samples_add(samples, false, te); } else { - raw_samples_add(samples,true,te); - raw_samples_add(samples,false,te*3); + raw_samples_add(samples, true, te); + raw_samples_add(samples, false, te * 3); } } // Signal terminator. Just a single short pulse. - raw_samples_add(samples,true,te); + raw_samples_add(samples, true, te); } ProtoViewDecoder B4B1Decoder = { .name = "PT/SC remote", .decode = decode, .get_fields = get_fields, - .build_message = build_message -}; + .build_message = build_message}; diff --git a/protocols/keeloq.c b/protocols/keeloq.c index 78ad10d2a8a..c09cc35fcb0 100644 --- a/protocols/keeloq.c +++ b/protocols/keeloq.c @@ -27,16 +27,16 @@ #include "../app.h" -static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo *info) { - +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; - uint32_t off = bitmap_seek_bits(bits,numbytes,0,numbits,sync_pattern); - if (off == BITMAP_SEEK_NOT_FOUND) return false; + const char* sync_pattern = "101010101010101010101010" + "0000"; + uint8_t sync_len = 24 + 4; + if(numbits - sync_len + sync_len < 3 * 66) return false; + uint32_t off = bitmap_seek_bits(bits, numbytes, 0, numbits, sync_pattern); + if(off == BITMAP_SEEK_NOT_FOUND) return false; info->start_off = off; off += sync_len; // Seek start of message. @@ -45,84 +45,77 @@ static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoView * symbols of gap, to avoid missing the signal for a matter of wrong * timing. */ uint8_t gap_len = 0; - while(gap_len <= 7 && bitmap_get(bits,numbytes,off+gap_len) == 0) - gap_len++; - if (gap_len < 3 || gap_len > 7) return false; + while(gap_len <= 7 && bitmap_get(bits, numbytes, off + gap_len) == 0) gap_len++; + if(gap_len < 3 || gap_len > 7) return false; off += gap_len; FURI_LOG_E(TAG, "Keeloq preamble+sync found"); uint8_t raw[9] = {0}; - uint32_t decoded = - convert_from_line_code(raw,sizeof(raw),bits,numbytes,off, - "110","100"); /* Pulse width modulation. */ + uint32_t decoded = convert_from_line_code( + raw, sizeof(raw), bits, numbytes, off, "110", "100"); /* Pulse width modulation. */ FURI_LOG_E(TAG, "Keeloq decoded bits: %lu", decoded); - if (decoded < 66) return false; /* Require the full 66 bits. */ + if(decoded < 66) return false; /* Require the full 66 bits. */ - info->pulses_count = (off+66*3) - info->start_off; + info->pulses_count = (off + 66 * 3) - info->start_off; - bitmap_reverse_bytes_bits(raw,sizeof(raw)); /* Keeloq is LSB first. */ + bitmap_reverse_bytes_bits(raw, sizeof(raw)); /* Keeloq is LSB first. */ - int buttons = raw[7]>>4; - int lowbat = (raw[8]&0x1) == 0; // Actual bit meaning: good battery level - int alwaysone = (raw[8]&0x2) != 0; + int buttons = raw[7] >> 4; + int lowbat = (raw[8] & 0x1) == 0; // Actual bit meaning: good battery level + int alwaysone = (raw[8] & 0x2) != 0; - fieldset_add_bytes(info->fieldset,"encr",raw,8); - raw[7] = raw[7]<<4; // Make ID bits contiguous - fieldset_add_bytes(info->fieldset,"id",raw+4,7); // 28 bits, 7 nibbles - fieldset_add_bin(info->fieldset,"s[2,1,0,3]",buttons,4); - fieldset_add_bin(info->fieldset,"low battery",lowbat,1); - fieldset_add_bin(info->fieldset,"always one",alwaysone,1); + fieldset_add_bytes(info->fieldset, "encr", raw, 8); + raw[7] = raw[7] << 4; // Make ID bits contiguous + fieldset_add_bytes(info->fieldset, "id", raw + 4, 7); // 28 bits, 7 nibbles + fieldset_add_bin(info->fieldset, "s[2,1,0,3]", buttons, 4); + fieldset_add_bin(info->fieldset, "low battery", lowbat, 1); + fieldset_add_bin(info->fieldset, "always one", alwaysone, 1); return true; } -static void get_fields(ProtoViewFieldSet *fieldset) { +static void get_fields(ProtoViewFieldSet* fieldset) { uint8_t remote_id[4] = {0xab, 0xcd, 0xef, 0xa0}; uint8_t encr[4] = {0xab, 0xab, 0xab, 0xab}; - fieldset_add_bytes(fieldset,"encr",encr,8); - fieldset_add_bytes(fieldset,"id",remote_id,7); - fieldset_add_bin(fieldset,"s[2,1,0,3]",2,4); - fieldset_add_bin(fieldset,"low battery",0,1); - fieldset_add_bin(fieldset,"always one",1,1); + fieldset_add_bytes(fieldset, "encr", encr, 8); + fieldset_add_bytes(fieldset, "id", remote_id, 7); + fieldset_add_bin(fieldset, "s[2,1,0,3]", 2, 4); + fieldset_add_bin(fieldset, "low battery", 0, 1); + fieldset_add_bin(fieldset, "always one", 1, 1); } -static void build_message(RawSamplesBuffer *samples, ProtoViewFieldSet *fieldset) -{ +static void build_message(RawSamplesBuffer* samples, ProtoViewFieldSet* fieldset) { uint32_t te = 380; // Short pulse duration in microseconds. // Sync: 12 pairs of pulse/gap + 9 times gap - for (int j = 0; j < 12; j++) { - raw_samples_add(samples,true,te); - raw_samples_add(samples,false,te); + for(int j = 0; j < 12; j++) { + raw_samples_add(samples, true, te); + raw_samples_add(samples, false, te); } - raw_samples_add(samples,false,te*9); + raw_samples_add(samples, false, te * 9); // Data, 66 bits. uint8_t data[9] = {0}; - memcpy(data,fieldset->fields[0]->bytes,4); // Encrypted part. - memcpy(data+4,fieldset->fields[1]->bytes,4); // ID. - data[7] = data[7]>>4 | fieldset->fields[2]->uvalue << 4; // s[2,1,0,3] + memcpy(data, fieldset->fields[0]->bytes, 4); // Encrypted part. + memcpy(data + 4, fieldset->fields[1]->bytes, 4); // ID. + data[7] = data[7] >> 4 | fieldset->fields[2]->uvalue << 4; // s[2,1,0,3] int low_battery = fieldset->fields[3] != 0; int always_one = fieldset->fields[4] != 0; low_battery = !low_battery; // Bit real meaning is good battery level. data[8] |= low_battery; data[8] |= (always_one << 1); - bitmap_reverse_bytes_bits(data,sizeof(data)); /* Keeloq is LSB first. */ + bitmap_reverse_bytes_bits(data, sizeof(data)); /* Keeloq is LSB first. */ - for (int j = 0; j < 66; j++) { - if (bitmap_get(data,9,j)) { - raw_samples_add(samples,true,te); - raw_samples_add(samples,false,te*2); + for(int j = 0; j < 66; j++) { + if(bitmap_get(data, 9, j)) { + raw_samples_add(samples, true, te); + raw_samples_add(samples, false, te * 2); } else { - raw_samples_add(samples,true,te*2); - raw_samples_add(samples,false,te); + raw_samples_add(samples, true, te * 2); + raw_samples_add(samples, false, te); } } } -ProtoViewDecoder KeeloqDecoder = { - .name = "Keeloq", - .decode = decode, - .get_fields = get_fields, - .build_message = build_message -}; +ProtoViewDecoder KeeloqDecoder = + {.name = "Keeloq", .decode = decode, .get_fields = get_fields, .build_message = build_message}; diff --git a/protocols/oregon2.c b/protocols/oregon2.c index 752bfcf9657..f7b123deb32 100644 --- a/protocols/oregon2.c +++ b/protocols/oregon2.c @@ -9,11 +9,14 @@ #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"); info->start_off = off; @@ -21,50 +24,61 @@ static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoView 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. */ - info->pulses_count = (off+11*4*4) - info->start_off; + if(decoded < 11 * 4) return false; /* Minimum len to extract some data. */ + info->pulses_count = (off + 11 * 4 * 4) - info->start_off; char temp[3] = {0}, hum[2] = {0}; uint8_t deviceid[2]; - 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; } } - float tempval = ((temp[0]-'0')*10) + - (temp[1]-'0') + - ((float)(temp[2]-'0')*0.1); - int humval = (hum[0]-'0')*10 + (hum[1]-'0'); + float tempval = ((temp[0] - '0') * 10) + (temp[1] - '0') + ((float)(temp[2] - '0') * 0.1); + int humval = (hum[0] - '0') * 10 + (hum[1] - '0'); - fieldset_add_bytes(info->fieldset,"Sensor ID",deviceid,4); - fieldset_add_float(info->fieldset,"Temperature",tempval,1); - fieldset_add_uint(info->fieldset,"Humidity",humval,7); + fieldset_add_bytes(info->fieldset, "Sensor ID", deviceid, 4); + fieldset_add_float(info->fieldset, "Temperature", tempval, 1); + fieldset_add_uint(info->fieldset, "Humidity", humval, 7); return true; } -ProtoViewDecoder Oregon2Decoder = { - .name = "Oregon2", - .decode = decode, - .get_fields = NULL, - .build_message = NULL -}; +ProtoViewDecoder Oregon2Decoder = + {.name = "Oregon2", .decode = decode, .get_fields = NULL, .build_message = NULL}; diff --git a/protocols/pvchat.c b/protocols/pvchat.c index 260e7ccb67c..779248c5b74 100644 --- a/protocols/pvchat.c +++ b/protocols/pvchat.c @@ -75,24 +75,24 @@ * second. More than enough for the simple chat we have here. */ -static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo *info) { - const char *sync_pattern = "1010101010101010" // Preamble - "1100110011001010"; // Sync +static bool decode(uint8_t* bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo* info) { + const char* sync_pattern = "1010101010101010" // Preamble + "1100110011001010"; // Sync uint8_t sync_len = 32; /* This is a variable length message, however the minimum length * requires a sender len byte (of value zero) and the terminator * FF 00 plus checksum: a total of 4 bytes. */ - if (numbits-sync_len < 8*4) return false; + if(numbits - sync_len < 8 * 4) return false; - uint64_t off = bitmap_seek_bits(bits,numbytes,0,numbits,sync_pattern); - if (off == BITMAP_SEEK_NOT_FOUND) 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, "Chat preamble+sync found"); /* If there is room on the left, let's mark the start of the message * a bit before: we don't try to detect all the preamble, but only * the first part, however it is likely present. */ - if (off >= 16) { + if(off >= 16) { off -= 16; sync_len += 16; } @@ -102,99 +102,97 @@ static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoView uint8_t raw[64] = {(uint8_t)'.'}; uint32_t decoded = - convert_from_line_code(raw,sizeof(raw),bits,numbytes,off, - "100","110"); /* PWM */ + convert_from_line_code(raw, sizeof(raw), bits, numbytes, off, "100", "110"); /* PWM */ FURI_LOG_E(TAG, "Chat decoded bits: %lu", decoded); - if (decoded < 8*4) return false; /* Min message len. */ + if(decoded < 8 * 4) return false; /* Min message len. */ // The message needs to have a two bytes terminator before // the checksum. uint32_t j; - for (j = 0; j < sizeof(raw)-1; j++) - if (raw[j] == 0xff && raw[j+1] == 0xaa) break; + for(j = 0; j < sizeof(raw) - 1; j++) + if(raw[j] == 0xff && raw[j + 1] == 0xaa) break; - if (j == sizeof(raw)-1) { + if(j == sizeof(raw) - 1) { FURI_LOG_E(TAG, "Chat: terminator not found"); return false; // No terminator found. } - uint32_t datalen = j+3; // If the terminator was found at j, then - // we need to sum three more bytes to have - // the len: FF itself, AA, checksum. - info->pulses_count = sync_len + 8*3*datalen; + uint32_t datalen = j + 3; // If the terminator was found at j, then + // we need to sum three more bytes to have + // the len: FF itself, AA, checksum. + info->pulses_count = sync_len + 8 * 3 * datalen; // Check if the control sum matches. - if (sum_bytes(raw,datalen-1,0) != raw[datalen-1]) { + if(sum_bytes(raw, datalen - 1, 0) != raw[datalen - 1]) { FURI_LOG_E(TAG, "Chat: checksum mismatch"); return false; } // Check if the length of the sender looks sane uint8_t senderlen = raw[0]; - if (senderlen >= sizeof(raw)) { + if(senderlen >= sizeof(raw)) { FURI_LOG_E(TAG, "Chat: invalid sender length"); return false; // Overflow } - fieldset_add_str(info->fieldset,"sender",(char*)raw+1,senderlen); - fieldset_add_str(info->fieldset,"message",(char*)raw+1+senderlen, - datalen-senderlen-4); + fieldset_add_str(info->fieldset, "sender", (char*)raw + 1, senderlen); + fieldset_add_str( + info->fieldset, "message", (char*)raw + 1 + senderlen, datalen - senderlen - 4); return true; } /* Give fields and defaults for the signal creator. */ -static void get_fields(ProtoViewFieldSet *fieldset) { - fieldset_add_str(fieldset,"sender","Carol",5); - fieldset_add_str(fieldset,"message","Anyone hearing?",15); +static void get_fields(ProtoViewFieldSet* fieldset) { + fieldset_add_str(fieldset, "sender", "Carol", 5); + fieldset_add_str(fieldset, "message", "Anyone hearing?", 15); } /* Create a signal. */ -static void build_message(RawSamplesBuffer *samples, ProtoViewFieldSet *fs) -{ +static void build_message(RawSamplesBuffer* samples, ProtoViewFieldSet* fs) { uint32_t te = 300; /* Short pulse duration in microseconds. Our protocol needs three symbol times to send a bit, so 300 us per bit = 3.33 kBaud. */ // Preamble: 24 alternating 300us pulse/gap pairs. - for (int j = 0; j < 24; j++) { - raw_samples_add(samples,true,te); - raw_samples_add(samples,false,te); + for(int j = 0; j < 24; j++) { + raw_samples_add(samples, true, te); + raw_samples_add(samples, false, te); } // Sync: 3 alternating 600 us pulse/gap pairs. - for (int j = 0; j < 3; j++) { - raw_samples_add(samples,true,te*2); - raw_samples_add(samples,false,te*2); + for(int j = 0; j < 3; j++) { + raw_samples_add(samples, true, te * 2); + raw_samples_add(samples, false, te * 2); } // Sync: plus 2 alternating 300 us pluse/gap pairs. - for (int j = 0; j < 2; j++) { - raw_samples_add(samples,true,te); - raw_samples_add(samples,false,te); + for(int j = 0; j < 2; j++) { + raw_samples_add(samples, true, te); + raw_samples_add(samples, false, te); } // Data: build the array. uint32_t datalen = 1 + fs->fields[0]->len + // Userlen + Username - fs->fields[1]->len + 3; // Message + FF + 00 + CRC + fs->fields[1]->len + 3; // Message + FF + 00 + CRC uint8_t *data = malloc(datalen), *p = data; *p++ = fs->fields[0]->len; - memcpy(p,fs->fields[0]->str,fs->fields[0]->len); + memcpy(p, fs->fields[0]->str, fs->fields[0]->len); p += fs->fields[0]->len; - memcpy(p,fs->fields[1]->str,fs->fields[1]->len); + memcpy(p, fs->fields[1]->str, fs->fields[1]->len); p += fs->fields[1]->len; *p++ = 0xff; *p++ = 0xaa; - *p = sum_bytes(data,datalen-1,0); + *p = sum_bytes(data, datalen - 1, 0); // Emit bits - for (uint32_t j = 0; j < datalen*8; j++) { - if (bitmap_get(data,datalen,j)) { - raw_samples_add(samples,true,te*2); - raw_samples_add(samples,false,te); + for(uint32_t j = 0; j < datalen * 8; j++) { + if(bitmap_get(data, datalen, j)) { + raw_samples_add(samples, true, te * 2); + raw_samples_add(samples, false, te); } else { - raw_samples_add(samples,true,te); - raw_samples_add(samples,false,te*2); + raw_samples_add(samples, true, te); + raw_samples_add(samples, false, te * 2); } } free(data); @@ -204,5 +202,4 @@ ProtoViewDecoder ProtoViewChatDecoder = { .name = "ProtoView chat", .decode = decode, .get_fields = get_fields, - .build_message = build_message -}; + .build_message = build_message}; diff --git a/protocols/tpms/citroen.c b/protocols/tpms/citroen.c index 27de2a88b0f..a86e5f7d6c2 100644 --- a/protocols/tpms/citroen.c +++ b/protocols/tpms/citroen.c @@ -10,55 +10,49 @@ #include "../../app.h" -static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo *info) { - +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. */ + 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; + 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"); info->start_off = off; off += sync_len; /* Skip preamble + sync. */ uint8_t raw[10]; - uint32_t decoded = - convert_from_line_code(raw,sizeof(raw),bits,numbytes,off, - "01","10"); /* Manchester. */ + uint32_t decoded = convert_from_line_code( + raw, sizeof(raw), bits, numbytes, off, "01", "10"); /* Manchester. */ FURI_LOG_E(TAG, "Citroen TPMS decoded bits: %lu", decoded); - if (decoded < 8*10) return false; /* Require the full 10 bytes. */ + 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. */ + for(int j = 1; j < 10; j++) crc ^= raw[j]; + if(crc != 0) return false; /* Require sane checksum. */ - info->pulses_count = (off+8*10*2) - info->start_off; + info->pulses_count = (off + 8 * 10 * 2) - info->start_off; int repeat = raw[5] & 0xf; - float kpa = (float)raw[6]*1.364; - int temp = raw[7]-50; + 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. */ - fieldset_add_bytes(info->fieldset,"Tire ID",raw+1,4*2); - fieldset_add_float(info->fieldset,"Pressure kpa",kpa,2); - fieldset_add_int(info->fieldset,"Temperature C",temp,8); - fieldset_add_uint(info->fieldset,"Repeat",repeat,4); - fieldset_add_uint(info->fieldset,"Battery",battery,8); + fieldset_add_bytes(info->fieldset, "Tire ID", raw + 1, 4 * 2); + fieldset_add_float(info->fieldset, "Pressure kpa", kpa, 2); + fieldset_add_int(info->fieldset, "Temperature C", temp, 8); + fieldset_add_uint(info->fieldset, "Repeat", repeat, 4); + fieldset_add_uint(info->fieldset, "Battery", battery, 8); return true; } -ProtoViewDecoder CitroenTPMSDecoder = { - .name = "Citroen TPMS", - .decode = decode, - .get_fields = NULL, - .build_message = NULL -}; +ProtoViewDecoder CitroenTPMSDecoder = + {.name = "Citroen TPMS", .decode = decode, .get_fields = NULL, .build_message = NULL}; diff --git a/protocols/tpms/ford.c b/protocols/tpms/ford.c index d7f26884cb2..06633259089 100644 --- a/protocols/tpms/ford.c +++ b/protocols/tpms/ford.c @@ -13,54 +13,49 @@ #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; +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"); info->start_off = off; off += sync_len; /* Skip preamble and sync. */ uint8_t raw[8]; - uint32_t decoded = - convert_from_line_code(raw,sizeof(raw),bits,numbytes,off, - "01","10"); /* Manchester. */ + uint32_t decoded = convert_from_line_code( + raw, sizeof(raw), bits, numbytes, off, "01", "10"); /* Manchester. */ FURI_LOG_E(TAG, "Ford TPMS decoded bits: %lu", decoded); - if (decoded < 8*8) return false; /* Require the full 8 bytes. */ + 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. */ + for(int j = 0; j < 7; j++) crc += raw[j]; + if(crc != raw[7]) return false; /* Require sane CRC. */ - info->pulses_count = (off+8*8*2) - info->start_off; + info->pulses_count = (off + 8 * 8 * 2) - info->start_off; - float psi = 0.25 * (((raw[6]&0x20)<<3)|raw[4]); + 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 temp = raw[5] & 0x80 ? 0 : raw[5] - 56; int flags = raw[5] & 0x7f; int car_moving = (raw[6] & 0x44) == 0x44; - fieldset_add_bytes(info->fieldset,"Tire ID",raw,4*2); - fieldset_add_float(info->fieldset,"Pressure psi",psi,2); - fieldset_add_int(info->fieldset,"Temperature C",temp,8); - fieldset_add_hex(info->fieldset,"Flags",flags,7); - fieldset_add_uint(info->fieldset,"Moving",car_moving,1); + fieldset_add_bytes(info->fieldset, "Tire ID", raw, 4 * 2); + fieldset_add_float(info->fieldset, "Pressure psi", psi, 2); + fieldset_add_int(info->fieldset, "Temperature C", temp, 8); + fieldset_add_hex(info->fieldset, "Flags", flags, 7); + fieldset_add_uint(info->fieldset, "Moving", car_moving, 1); return true; } -ProtoViewDecoder FordTPMSDecoder = { - .name = "Ford TPMS", - .decode = decode, - .get_fields = NULL, - .build_message = NULL -}; +ProtoViewDecoder FordTPMSDecoder = + {.name = "Ford TPMS", .decode = decode, .get_fields = NULL, .build_message = NULL}; diff --git a/protocols/tpms/renault.c b/protocols/tpms/renault.c index 34cc3a8e31e..0755a515fcb 100644 --- a/protocols/tpms/renault.c +++ b/protocols/tpms/renault.c @@ -9,85 +9,82 @@ #include "../../app.h" #define USE_TEST_VECTOR 0 -static const char *test_vector = +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. + // 244 kpa here. + "1010010110011010" // Temperature, subtract 30 to obtain celsius. 22C here. "1001010101101001" "0101100110010101" - "1001010101100110" // Tire ID. 0x7AD779 here. + "1001010101100110" // Tire ID. 0x7AD779 here. "0101010101010101" - "0101010101010101" // Two FF bytes (usually). Unknown. + "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,0,test_vector); +static bool decode(uint8_t* bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo* info) { + if(USE_TEST_VECTOR) { /* Test vector to check that decoding works. */ + bitmap_set_pattern(bits, numbytes, 0, test_vector); numbits = strlen(test_vector); } - if (numbits-12 < 9*8) return false; + 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; + 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"); info->start_off = off; off += 20; /* Skip preamble. */ uint8_t raw[9]; - uint32_t decoded = - convert_from_line_code(raw,sizeof(raw),bits,numbytes,off, - "01","10"); /* Manchester. */ + uint32_t decoded = convert_from_line_code( + raw, sizeof(raw), bits, numbytes, off, "01", "10"); /* Manchester. */ FURI_LOG_E(TAG, "Renault TPMS decoded bits: %lu", decoded); - if (decoded < 8*9) return false; /* Require the full 9 bytes. */ - if (crc8(raw,8,0,7) != raw[8]) return false; /* Require sane CRC. */ + if(decoded < 8 * 9) return false; /* Require the full 9 bytes. */ + if(crc8(raw, 8, 0, 7) != raw[8]) return false; /* Require sane CRC. */ - info->pulses_count = (off+8*9*2) - info->start_off; + info->pulses_count = (off + 8 * 9 * 2) - info->start_off; - uint8_t flags = raw[0]>>2; - float kpa = 0.75 * ((uint32_t)((raw[0]&3)<<8) | raw[1]); - int temp = raw[2]-30; + uint8_t flags = raw[0] >> 2; + float kpa = 0.75 * ((uint32_t)((raw[0] & 3) << 8) | raw[1]); + int temp = raw[2] - 30; - fieldset_add_bytes(info->fieldset,"Tire ID",raw+3,3*2); - fieldset_add_float(info->fieldset,"Pressure kpa",kpa,2); - fieldset_add_int(info->fieldset,"Temperature C",temp,8); - fieldset_add_hex(info->fieldset,"Flags",flags,6); - fieldset_add_bytes(info->fieldset,"Unknown1",raw+6,2); - fieldset_add_bytes(info->fieldset,"Unknown2",raw+7,2); + fieldset_add_bytes(info->fieldset, "Tire ID", raw + 3, 3 * 2); + fieldset_add_float(info->fieldset, "Pressure kpa", kpa, 2); + fieldset_add_int(info->fieldset, "Temperature C", temp, 8); + fieldset_add_hex(info->fieldset, "Flags", flags, 6); + fieldset_add_bytes(info->fieldset, "Unknown1", raw + 6, 2); + fieldset_add_bytes(info->fieldset, "Unknown2", raw + 7, 2); return true; } /* Give fields and defaults for the signal creator. */ -static void get_fields(ProtoViewFieldSet *fieldset) { - uint8_t default_id[3]= {0xAB, 0xCD, 0xEF}; - fieldset_add_bytes(fieldset,"Tire ID",default_id,3*2); - fieldset_add_float(fieldset,"Pressure kpa",123,2); - fieldset_add_int(fieldset,"Temperature C",20,8); +static void get_fields(ProtoViewFieldSet* fieldset) { + uint8_t default_id[3] = {0xAB, 0xCD, 0xEF}; + fieldset_add_bytes(fieldset, "Tire ID", default_id, 3 * 2); + fieldset_add_float(fieldset, "Pressure kpa", 123, 2); + fieldset_add_int(fieldset, "Temperature C", 20, 8); // We don't know what flags are, but 1B is a common value. - fieldset_add_hex(fieldset,"Flags",0x1b,6); - fieldset_add_bytes(fieldset,"Unknown1",(uint8_t*)"\xff",2); - fieldset_add_bytes(fieldset,"Unknown2",(uint8_t*)"\xff",2); + fieldset_add_hex(fieldset, "Flags", 0x1b, 6); + fieldset_add_bytes(fieldset, "Unknown1", (uint8_t*)"\xff", 2); + fieldset_add_bytes(fieldset, "Unknown2", (uint8_t*)"\xff", 2); } /* Create a Renault TPMS signal, according to the fields provided. */ -static void build_message(RawSamplesBuffer *samples, ProtoViewFieldSet *fieldset) -{ +static void build_message(RawSamplesBuffer* samples, ProtoViewFieldSet* fieldset) { uint32_t te = 50; // Short pulse duration in microseconds. // Preamble + sync - const char *psync = "01010101010101010101010101010110"; - const char *p = psync; + const char* psync = "01010101010101010101010101010110"; + const char* p = psync; while(*p) { - raw_samples_add_or_update(samples,*p == '1',te); + raw_samples_add_or_update(samples, *p == '1', te); p++; } @@ -96,21 +93,21 @@ static void build_message(RawSamplesBuffer *samples, ProtoViewFieldSet *fieldset unsigned int raw_pressure = fieldset->fields[1]->fvalue * 4 / 3; data[0] = fieldset->fields[3]->uvalue << 2; // Flags data[0] |= (raw_pressure >> 8) & 3; // Pressure kpa high 2 bits - data[1] = raw_pressure & 0xff; // Pressure kpa low 8 bits + data[1] = raw_pressure & 0xff; // Pressure kpa low 8 bits data[2] = fieldset->fields[2]->value + 30; // Temperature C - memcpy(data+3,fieldset->fields[0]->bytes,6); // ID, 24 bits. - data[6] = fieldset->fields[4]->bytes[0]; // Unknown 1 - data[7] = fieldset->fields[5]->bytes[0]; // Unknown 2 - data[8] = crc8(data,8,0,7); + memcpy(data + 3, fieldset->fields[0]->bytes, 6); // ID, 24 bits. + data[6] = fieldset->fields[4]->bytes[0]; // Unknown 1 + data[7] = fieldset->fields[5]->bytes[0]; // Unknown 2 + data[8] = crc8(data, 8, 0, 7); // Generate Manchester code for each bit - for (uint32_t j = 0; j < 9*8; j++) { - if (bitmap_get(data,sizeof(data),j)) { - raw_samples_add_or_update(samples,true,te); - raw_samples_add_or_update(samples,false,te); + for(uint32_t j = 0; j < 9 * 8; j++) { + if(bitmap_get(data, sizeof(data), j)) { + raw_samples_add_or_update(samples, true, te); + raw_samples_add_or_update(samples, false, te); } else { - raw_samples_add_or_update(samples,false,te); - raw_samples_add_or_update(samples,true,te); + raw_samples_add_or_update(samples, false, te); + raw_samples_add_or_update(samples, true, te); } } } @@ -119,5 +116,4 @@ ProtoViewDecoder RenaultTPMSDecoder = { .name = "Renault TPMS", .decode = decode, .get_fields = get_fields, - .build_message = build_message -}; + .build_message = build_message}; diff --git a/protocols/tpms/schrader.c b/protocols/tpms/schrader.c index 8cf7b6b6f89..2b53d313c80 100644 --- a/protocols/tpms/schrader.c +++ b/protocols/tpms/schrader.c @@ -14,20 +14,21 @@ #include "../../app.h" #define USE_TEST_VECTOR 0 -static const char *test_vector = "000000111101010101011010010110010110101001010110100110011001100101010101011010100110100110011010101010101010101010101010101010101010101010101010"; +static const char* test_vector = + "000000111101010101011010010110010110101001010110100110011001100101010101011010100110100110011010101010101010101010101010101010101010101010101010"; -static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo *info) { - - if (USE_TEST_VECTOR) { /* Test vector to check that decoding works. */ - bitmap_set_pattern(bits,numbytes,0,test_vector); +static bool decode(uint8_t* bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo* info) { + if(USE_TEST_VECTOR) { /* Test vector to check that decoding works. */ + bitmap_set_pattern(bits, numbytes, 0, test_vector); numbits = strlen(test_vector); } - if (numbits < 64) return false; /* Preamble + data. */ + 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; + 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"); info->start_off = off; @@ -37,38 +38,33 @@ static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoView uint8_t raw[8]; uint8_t id[4]; - uint32_t decoded = - convert_from_line_code(raw,sizeof(raw),bits,numbytes,off, - "01","10"); /* Manchester code. */ + uint32_t decoded = convert_from_line_code( + raw, sizeof(raw), bits, numbytes, off, "01", "10"); /* Manchester code. */ FURI_LOG_E(TAG, "Schrader TPMS decoded bits: %lu", decoded); - if (decoded < 64) return false; /* Require the full 8 bytes. */ + 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]) { + uint8_t cksum = crc8(raw, sizeof(raw) - 1, 0xf0, 0x7); + if(cksum != raw[7]) { FURI_LOG_E(TAG, "Schrader TPMS checksum mismatch"); return false; } - info->pulses_count = (off+8*8*2) - info->start_off; + info->pulses_count = (off + 8 * 8 * 2) - info->start_off; - float kpa = (float)raw[5]*2.5; - int temp = raw[6]-50; - id[0] = raw[1]&7; + float kpa = (float)raw[5] * 2.5; + int temp = raw[6] - 50; + id[0] = raw[1] & 7; id[1] = raw[2]; id[2] = raw[3]; id[3] = raw[4]; - fieldset_add_bytes(info->fieldset,"Tire ID",id,4*2); - fieldset_add_float(info->fieldset,"Pressure kpa",kpa,2); - fieldset_add_int(info->fieldset,"Temperature C",temp,8); + fieldset_add_bytes(info->fieldset, "Tire ID", id, 4 * 2); + fieldset_add_float(info->fieldset, "Pressure kpa", kpa, 2); + fieldset_add_int(info->fieldset, "Temperature C", temp, 8); return true; } -ProtoViewDecoder SchraderTPMSDecoder = { - .name = "Schrader TPMS", - .decode = decode, - .get_fields = NULL, - .build_message = NULL -}; +ProtoViewDecoder SchraderTPMSDecoder = + {.name = "Schrader TPMS", .decode = decode, .get_fields = NULL, .build_message = NULL}; diff --git a/protocols/tpms/schrader_eg53ma4.c b/protocols/tpms/schrader_eg53ma4.c index c7e95cbfb61..b186f57b187 100644 --- a/protocols/tpms/schrader_eg53ma4.c +++ b/protocols/tpms/schrader_eg53ma4.c @@ -18,50 +18,45 @@ #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; +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"); info->start_off = off; - off += sync_len-8; /* Skip preamble, not sync that is part of the data. */ + off += sync_len - 8; /* Skip preamble, not sync that is part of the data. */ uint8_t raw[10]; - uint32_t decoded = - convert_from_line_code(raw,sizeof(raw),bits,numbytes,off, - "01","10"); /* Manchester code. */ + uint32_t decoded = convert_from_line_code( + raw, sizeof(raw), bits, numbytes, off, "01", "10"); /* Manchester code. */ FURI_LOG_E(TAG, "Schrader EG53MA4 TPMS decoded bits: %lu", decoded); - if (decoded < 10*8) return false; /* Require the full 10 bytes. */ + 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. */ + for(int j = 0; j < 9; j++) crc += raw[j]; + if(crc != raw[9]) return false; /* Require sane CRC. */ - info->pulses_count = (off+10*8*2) - info->start_off; + info->pulses_count = (off + 10 * 8 * 2) - info->start_off; /* 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; + float kpa = (float)raw[7] * 2.75; int temp_f = raw[8]; - int temp_c = (temp_f-32)*5/9; /* Convert Fahrenheit to Celsius. */ + int temp_c = (temp_f - 32) * 5 / 9; /* Convert Fahrenheit to Celsius. */ - fieldset_add_bytes(info->fieldset,"Tire ID",raw+4,3*2); - fieldset_add_float(info->fieldset,"Pressure kpa",kpa,2); - fieldset_add_int(info->fieldset,"Temperature C",temp_c,8); + fieldset_add_bytes(info->fieldset, "Tire ID", raw + 4, 3 * 2); + fieldset_add_float(info->fieldset, "Pressure kpa", kpa, 2); + fieldset_add_int(info->fieldset, "Temperature C", temp_c, 8); return true; } -ProtoViewDecoder SchraderEG53MA4TPMSDecoder = { - .name = "Schrader EG53MA4 TPMS", - .decode = decode, - .get_fields = NULL, - .build_message = NULL -}; +ProtoViewDecoder SchraderEG53MA4TPMSDecoder = + {.name = "Schrader EG53MA4 TPMS", .decode = decode, .get_fields = NULL, .build_message = NULL}; diff --git a/protocols/tpms/toyota.c b/protocols/tpms/toyota.c index 6a7e79ac579..3a02447b7df 100644 --- a/protocols/tpms/toyota.c +++ b/protocols/tpms/toyota.c @@ -27,40 +27,33 @@ #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 +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 - }; + 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) { + for(j = 0; sync[j]; j++) { + off = bitmap_seek_bits(bits, numbytes, 0, numbits, sync[j]); + if(off != BITMAP_SEEK_NOT_FOUND) { info->start_off = off; - off += strlen(sync[j])-2; + off += strlen(sync[j]) - 2; break; - } + } } - if (off == BITMAP_SEEK_NOT_FOUND) return false; + 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); + 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. */ + if(decoded < 8 * 9) return false; /* Require the full 8 bytes. */ + if(crc8(raw, 8, 0x80, 7) != raw[8]) return false; /* Require sane CRC. */ /* We detected a valid signal. However now info->start_off is actually * pointing to the sync part, not the preamble of alternating 0 and 1. @@ -68,25 +61,21 @@ static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoView * for the decoder itself to fix the signal if neeeded, so that its * logical representation will be more accurate and better to save * and retransmit. */ - if (info->start_off >= 12) { + if(info->start_off >= 12) { info->start_off -= 12; - bitmap_set_pattern(bits,numbytes,info->start_off,"010101010101"); + bitmap_set_pattern(bits, numbytes, info->start_off, "010101010101"); } - info->pulses_count = (off+8*9*2) - info->start_off; + info->pulses_count = (off + 8 * 9 * 2) - info->start_off; - float psi = (float)((raw[4]&0x7f)<<1 | raw[5]>>7) * 0.25 - 7; - int temp = ((raw[5]&0x7f)<<1 | raw[6]>>7) - 40; + float psi = (float)((raw[4] & 0x7f) << 1 | raw[5] >> 7) * 0.25 - 7; + int temp = ((raw[5] & 0x7f) << 1 | raw[6] >> 7) - 40; - fieldset_add_bytes(info->fieldset,"Tire ID",raw,4*2); - fieldset_add_float(info->fieldset,"Pressure psi",psi,2); - fieldset_add_int(info->fieldset,"Temperature C",temp,8); + fieldset_add_bytes(info->fieldset, "Tire ID", raw, 4 * 2); + fieldset_add_float(info->fieldset, "Pressure psi", psi, 2); + fieldset_add_int(info->fieldset, "Temperature C", temp, 8); return true; } -ProtoViewDecoder ToyotaTPMSDecoder = { - .name = "Toyota TPMS", - .decode = decode, - .get_fields = NULL, - .build_message = NULL -}; +ProtoViewDecoder ToyotaTPMSDecoder = + {.name = "Toyota TPMS", .decode = decode, .get_fields = NULL, .build_message = NULL}; diff --git a/protocols/unknown.c b/protocols/unknown.c index 708cda2feee..ec87d59b9e6 100644 --- a/protocols/unknown.c +++ b/protocols/unknown.c @@ -14,7 +14,6 @@ * ---------------------------------------------------------------------------- */ - /* Scan the signal bitmap looking for a PWM modulation. In this case * for PWM we are referring to two exact patterns of high and low * signal (each bit in the bitmap is worth the smallest gap/pulse duration @@ -32,13 +31,17 @@ * in *s1i the offset of the first symbol found, and in *s2i the offset * of the second symbol. The function can't tell which is one and which * zero. */ -static uint32_t find_pwm(uint8_t *bits, uint32_t numbytes, uint32_t numbits, - uint32_t symlen, uint32_t *s1i, uint32_t *s2i) -{ +static uint32_t find_pwm( + uint8_t* bits, + uint32_t numbytes, + uint32_t numbits, + uint32_t symlen, + uint32_t* s1i, + uint32_t* s2i) { uint32_t best_count = 0; /* Max number of symbols found in this try. */ - uint32_t best_idx1 = 0; /* First symbol offset of longest sequence found. + uint32_t best_idx1 = 0; /* First symbol offset of longest sequence found. * This is also the start sequence offset. */ - uint32_t best_idx2 = 0; /* Second symbol offset. */ + uint32_t best_idx2 = 0; /* Second symbol offset. */ /* Try all the possible symbol offsets that are less of our * symbol len. This is likely not really useful but we take @@ -47,39 +50,34 @@ static uint32_t find_pwm(uint8_t *bits, uint32_t numbytes, uint32_t numbits, * that is choerent at different offsets, but out-of-sync. * * Anyway at the end of the function we try to fix the sync. */ - for (uint32_t off = 0; off < symlen; off++) { + for(uint32_t off = 0; off < symlen; off++) { uint32_t c = 0; // Number of contiguous symbols found. uint32_t c1 = 0, c2 = 0; // Occurrences of first/second symbol. - *s1i = off; // Assume we start at one symbol boundaty. + *s1i = off; // Assume we start at one symbol boundaty. *s2i = UINT32_MAX; // Second symbol first index still unknown. uint32_t next = off; /* We scan the whole bitmap in one pass, resetting the state * each time we find a pattern that is not one of the two * symbols we found so far. */ - while(next < numbits-symlen) { - bool match1 = bitmap_match_bitmap(bits,numbytes,next, - bits,numbytes,*s1i, - symlen); - if (!match1 && *s2i == UINT32_MAX) { + while(next < numbits - symlen) { + bool match1 = bitmap_match_bitmap(bits, numbytes, next, bits, numbytes, *s1i, symlen); + if(!match1 && *s2i == UINT32_MAX) { /* It's not the first sybol. We don't know how the * second look like. Assume we found an occurrence of * the second symbol. */ *s2i = next; } - bool match2 = bitmap_match_bitmap(bits,numbytes,next, - bits,numbytes,*s2i, - symlen); + bool match2 = bitmap_match_bitmap(bits, numbytes, next, bits, numbytes, *s2i, symlen); /* One or the other should match. */ - if (match1 || match2) { + if(match1 || match2) { c++; - if (match1) c1++; - if (match2) c2++; - if (c > best_count && - c1 >= best_count/5 && // Require enough presence of both - c2 >= best_count/5) // zero and one. + if(match1) c1++; + if(match2) c2++; + if(c > best_count && c1 >= best_count / 5 && // Require enough presence of both + c2 >= best_count / 5) // zero and one. { best_count = c; best_idx1 = *s1i; @@ -103,11 +101,12 @@ static uint32_t find_pwm(uint8_t *bits, uint32_t numbytes, uint32_t numbits, * However PWD line codes normally start with a pulse in both symbols. * If that is the case, let's align. */ uint32_t shift; - for (shift = 0; shift < symlen; shift++) { - if (bitmap_get(bits,numbytes,best_idx1+shift) && - bitmap_get(bits,numbytes,best_idx2+shift)) break; + for(shift = 0; shift < symlen; shift++) { + if(bitmap_get(bits, numbytes, best_idx1 + shift) && + bitmap_get(bits, numbytes, best_idx2 + shift)) + break; } - if (shift != symlen) { + if(shift != symlen) { best_idx1 += shift; best_idx2 += shift; } @@ -140,23 +139,24 @@ static uint32_t find_pwm(uint8_t *bits, uint32_t numbytes, uint32_t numbits, * If the 'only_raising' parameter is true, the function detects * only sequences going from gap to pulse: this is useful in order * to locate preambles of alternating gaps and pulses. */ -static uint32_t find_alternating_bits(uint8_t *bits, uint32_t numbytes, - uint32_t numbits, uint32_t *start, bool only_raising) -{ +static uint32_t find_alternating_bits( + uint8_t* bits, + uint32_t numbytes, + uint32_t numbits, + uint32_t* start, + bool only_raising) { uint32_t best_count = 0; // Max number of symbols found - uint32_t best_off = 0; // Max symbols start offset. - for (int odd = 0; odd < 2; odd++) { + uint32_t best_off = 0; // Max symbols start offset. + for(int odd = 0; odd < 2; odd++) { uint32_t count = 0; // Symbols found so far uint32_t start_off = odd; uint32_t j = odd; - while (j < numbits-1) { - bool bit1 = bitmap_get(bits,numbytes,j); - bool bit2 = bitmap_get(bits,numbytes,j+1); - if ((!only_raising && bit1 != bit2) || - (only_raising && !bit1 && bit2)) - { + while(j < numbits - 1) { + bool bit1 = bitmap_get(bits, numbytes, j); + bool bit2 = bitmap_get(bits, numbytes, j + 1); + if((!only_raising && bit1 != bit2) || (only_raising && !bit1 && bit2)) { count++; - if (count > best_count) { + if(count > best_count) { best_count = count; best_off = start_off; } @@ -174,17 +174,15 @@ static uint32_t find_alternating_bits(uint8_t *bits, uint32_t numbytes, } /* Wrapper to find Manchester code. */ -static uint32_t find_manchester(uint8_t *bits, uint32_t numbytes, - uint32_t numbits, uint32_t *start) -{ - return find_alternating_bits(bits,numbytes,numbits,start,false); +static uint32_t + find_manchester(uint8_t* bits, uint32_t numbytes, uint32_t numbits, uint32_t* start) { + return find_alternating_bits(bits, numbytes, numbits, start, false); } /* Wrapper to find preamble sections. */ -static uint32_t find_preamble(uint8_t *bits, uint32_t numbytes, - uint32_t numbits, uint32_t *start) -{ - return find_alternating_bits(bits,numbytes,numbits,start,true); +static uint32_t + find_preamble(uint8_t* bits, uint32_t numbytes, uint32_t numbits, uint32_t* start) { + return find_alternating_bits(bits, numbytes, numbits, start, true); } typedef enum { @@ -194,18 +192,21 @@ typedef enum { LineCodePWM4, } LineCodeGuess; -static char *get_linecode_name(LineCodeGuess lc) { +static char* get_linecode_name(LineCodeGuess lc) { switch(lc) { - case LineCodeNone: return "none"; - case LineCodeManchester: return "Manchester"; - case LineCodePWM3: return "PWM3"; - case LineCodePWM4: return "PWM4"; + case LineCodeNone: + return "none"; + case LineCodeManchester: + return "Manchester"; + case LineCodePWM3: + return "PWM3"; + case LineCodePWM4: + return "PWM4"; } return "unknown"; } -static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo *info) { - +static bool decode(uint8_t* bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo* info) { /* No decoder was able to detect this message. Let's try if we can * find some structure. To start, we'll see if it looks like is * manchester coded, or PWM with symbol len of 3 or 4. */ @@ -216,52 +217,49 @@ static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoView * for Manchester, that does not have two separated symbols like * PWM. */ uint32_t start1 = 0, start2 = 0; - uint32_t msgbits; // Number of message bits in the bitmap, so - // this will be the number of symbols, not actual - // bits after the message is decoded. - uint32_t tmp1, tmp2; // Temp vars to store the start. + uint32_t msgbits; // Number of message bits in the bitmap, so + // this will be the number of symbols, not actual + // bits after the message is decoded. + uint32_t tmp1, tmp2; // Temp vars to store the start. uint32_t minbits = 16; // Less than that gets undetected. - uint32_t pwm_len; // Bits per symbol, in the case of PWM. + uint32_t pwm_len; // Bits per symbol, in the case of PWM. LineCodeGuess linecode = LineCodeNone; // Try PWM3 - uint32_t pwm3_bits = find_pwm(bits,numbytes,numbits,3,&tmp1,&tmp2); - if (pwm3_bits >= minbits) { + uint32_t pwm3_bits = find_pwm(bits, numbytes, numbits, 3, &tmp1, &tmp2); + if(pwm3_bits >= minbits) { linecode = LineCodePWM3; start1 = tmp1; start2 = tmp2; pwm_len = 3; - msgbits = pwm3_bits*pwm_len; + msgbits = pwm3_bits * pwm_len; } // Try PWM4 - uint32_t pwm4_bits = find_pwm(bits,numbytes,numbits,4,&tmp1,&tmp2); - if (pwm4_bits >= minbits && pwm4_bits > pwm3_bits) { + uint32_t pwm4_bits = find_pwm(bits, numbytes, numbits, 4, &tmp1, &tmp2); + if(pwm4_bits >= minbits && pwm4_bits > pwm3_bits) { linecode = LineCodePWM4; start1 = tmp1; start2 = tmp2; pwm_len = 4; - msgbits = pwm3_bits*pwm_len; + msgbits = pwm3_bits * pwm_len; } // Try Manchester - uint32_t manchester_bits = find_manchester(bits,numbytes,numbits,&tmp1); - if (manchester_bits > minbits && - manchester_bits > pwm3_bits && - manchester_bits > pwm4_bits) - { + uint32_t manchester_bits = find_manchester(bits, numbytes, numbits, &tmp1); + if(manchester_bits > minbits && manchester_bits > pwm3_bits && manchester_bits > pwm4_bits) { linecode = LineCodeManchester; start1 = tmp1; - msgbits = manchester_bits*2; - FURI_LOG_E(TAG, "MANCHESTER START: %lu", tmp1); + msgbits = manchester_bits * 2; + //FURI_LOG_T(TAG, "MANCHESTER START: %lu", tmp1); } - if (linecode == LineCodeNone) return false; + if(linecode == LineCodeNone) return false; /* Often there is a preamble before the signal. We'll try to find * it, and if it is not too far away from our signal, we'll claim * our signal starts at the preamble. */ - uint32_t preamble_len = find_preamble(bits,numbytes,numbits,&tmp1); + uint32_t preamble_len = find_preamble(bits, numbytes, numbits, &tmp1); uint32_t min_preamble_len = 10; uint32_t max_preamble_distance = 32; uint32_t preamble_start = 0; @@ -271,27 +269,26 @@ static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoView * detected the preamble bits as data, we are ok with that, since it * means that the synchronization is not designed to "break" the bits * flow. In this case we ignore the preamble and return all as data. */ - if (preamble_len >= min_preamble_len && // Not too short. - tmp1 < start1 && // Should be before the data. - start1-tmp1 <= max_preamble_distance) // Not too far. + if(preamble_len >= min_preamble_len && // Not too short. + tmp1 < start1 && // Should be before the data. + start1 - tmp1 <= max_preamble_distance) // Not too far. { preamble_start = tmp1; preamble_found = true; } info->start_off = preamble_found ? preamble_start : start1; - info->pulses_count = (start1+msgbits) - info->start_off; + info->pulses_count = (start1 + msgbits) - info->start_off; info->pulses_count += 20; /* Add a few more, so that if the user resends * the message, it is more likely we will * transfer all that is needed, like a message * terminator (that we don't detect). */ - if (preamble_found) - FURI_LOG_E(TAG, "PREAMBLE AT: %lu", preamble_start); - FURI_LOG_E(TAG, "START: %lu", info->start_off); - FURI_LOG_E(TAG, "MSGBITS: %lu", msgbits); - FURI_LOG_E(TAG, "DATASTART: %lu", start1); - FURI_LOG_E(TAG, "PULSES: %lu", info->pulses_count); + /*if(preamble_found) FURI_LOG_T(TAG, "PREAMBLE AT: %lu", preamble_start); + FURI_LOG_T(TAG, "START: %lu", info->start_off); + FURI_LOG_T(TAG, "MSGBITS: %lu", msgbits); + FURI_LOG_T(TAG, "DATASTART: %lu", start1); + FURI_LOG_T(TAG, "PULSES: %lu", info->pulses_count);*/ /* We think there is a message and we know where it starts and the * line code used. We can turn it into bits and bytes. */ @@ -300,37 +297,30 @@ static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoView uint32_t datalen; char symbol1[5], symbol2[5]; - if (linecode == LineCodePWM3 || linecode == LineCodePWM4) { - bitmap_to_string(symbol1,bits,numbytes,start1,pwm_len); - bitmap_to_string(symbol2,bits,numbytes,start2,pwm_len); - } else if (linecode == LineCodeManchester) { - memcpy(symbol1,"01",3); - memcpy(symbol2,"10",3); + if(linecode == LineCodePWM3 || linecode == LineCodePWM4) { + bitmap_to_string(symbol1, bits, numbytes, start1, pwm_len); + bitmap_to_string(symbol2, bits, numbytes, start2, pwm_len); + } else if(linecode == LineCodeManchester) { + memcpy(symbol1, "01", 3); + memcpy(symbol2, "10", 3); } - decoded = convert_from_line_code(data,sizeof(data),bits,numbytes,start1, - symbol1,symbol2); - datalen = (decoded+7)/8; - - char *linecode_name = get_linecode_name(linecode); - fieldset_add_str(info->fieldset,"line code", - linecode_name,strlen(linecode_name)); - fieldset_add_uint(info->fieldset,"data bits",decoded,8); - if (preamble_found) - fieldset_add_uint(info->fieldset,"preamble len",preamble_len,8); - fieldset_add_str(info->fieldset,"first symbol",symbol1,strlen(symbol1)); - fieldset_add_str(info->fieldset,"second symbol",symbol2,strlen(symbol2)); - for (uint32_t j = 0; j < datalen; j++) { + decoded = convert_from_line_code(data, sizeof(data), bits, numbytes, start1, symbol1, symbol2); + datalen = (decoded + 7) / 8; + + char* linecode_name = get_linecode_name(linecode); + fieldset_add_str(info->fieldset, "line code", linecode_name, strlen(linecode_name)); + fieldset_add_uint(info->fieldset, "data bits", decoded, 8); + if(preamble_found) fieldset_add_uint(info->fieldset, "preamble len", preamble_len, 8); + fieldset_add_str(info->fieldset, "first symbol", symbol1, strlen(symbol1)); + fieldset_add_str(info->fieldset, "second symbol", symbol2, strlen(symbol2)); + for(uint32_t j = 0; j < datalen; j++) { char label[16]; - snprintf(label,sizeof(label),"data[%lu]",j); - fieldset_add_bytes(info->fieldset,label,data+j,2); + snprintf(label, sizeof(label), "data[%lu]", j); + fieldset_add_bytes(info->fieldset, label, data + j, 2); } return true; } -ProtoViewDecoder UnknownDecoder = { - .name = "Unknown", - .decode = decode, - .get_fields = NULL, - .build_message = NULL -}; +ProtoViewDecoder UnknownDecoder = + {.name = "Unknown", .decode = decode, .get_fields = NULL, .build_message = NULL}; diff --git a/raw_samples.c b/raw_samples.c index f83cca3619d..54773f43f34 100644 --- a/raw_samples.c +++ b/raw_samples.c @@ -8,15 +8,15 @@ #include "raw_samples.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,27 +24,27 @@ 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->samples,0,sizeof(s->samples)); + 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); +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; + s->idx = (s->idx + 1) % RAW_SAMPLES_NUM; furi_mutex_release(s->mutex); } @@ -56,28 +56,25 @@ void raw_samples_add(RawSamplesBuffer *s, bool level, uint32_t dur) { * * This function is a bit slower so the internal data sampling should * be performed with raw_samples_add(). */ -void raw_samples_add_or_update(RawSamplesBuffer *s, bool level, uint32_t dur) { - furi_mutex_acquire(s->mutex,FuriWaitForever); - uint32_t previdx = (s->idx-1) % RAW_SAMPLES_NUM; - if (s->samples[previdx].level == level && - s->samples[previdx].dur != 0) - { +void raw_samples_add_or_update(RawSamplesBuffer* s, bool level, uint32_t dur) { + furi_mutex_acquire(s->mutex, FuriWaitForever); + uint32_t previdx = (s->idx - 1) % RAW_SAMPLES_NUM; + if(s->samples[previdx].level == level && s->samples[previdx].dur != 0) { /* Update the last sample: it has the same level. */ s->samples[previdx].dur += dur; } else { /* Add a new sample. */ s->samples[s->idx].level = level; s->samples[s->idx].dur = dur; - s->idx = (s->idx+1) % RAW_SAMPLES_NUM; + 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->samples[idx].level; *dur = s->samples[idx].dur; @@ -85,12 +82,12 @@ void raw_samples_get(RawSamplesBuffer *s, uint32_t idx, bool *level, uint32_t *d } /* 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->samples,src->samples,sizeof(dst->samples)); + memcpy(dst->samples, src->samples, sizeof(dst->samples)); furi_mutex_release(src->mutex); furi_mutex_release(dst->mutex); } diff --git a/raw_samples.h b/raw_samples.h index 0b042202572..3493f07fdb0 100644 --- a/raw_samples.h +++ b/raw_samples.h @@ -4,16 +4,17 @@ /* 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; + FuriMutex* mutex; struct { - uint16_t level:1; - uint16_t dur:15; + uint16_t level : 1; + uint16_t dur : 15; } samples[RAW_SAMPLES_NUM]; - uint32_t idx; /* Current idx (next to write). */ + 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,11 +23,11 @@ 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_add_or_update(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_add_or_update(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/signal.c b/signal.c index b1ab872514d..71fbe823fc0 100644 --- a/signal.c +++ b/signal.c @@ -3,7 +3,7 @@ #include "app.h" -bool decode_signal(RawSamplesBuffer *s, uint64_t len, ProtoViewMsgInfo *info); +bool decode_signal(RawSamplesBuffer* s, uint64_t len, ProtoViewMsgInfo* info); /* ============================================================================= * Protocols table. @@ -24,24 +24,23 @@ extern ProtoViewDecoder KeeloqDecoder; extern ProtoViewDecoder ProtoViewChatDecoder; extern ProtoViewDecoder UnknownDecoder; -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. */ - &ProtoViewChatDecoder, /* Protoview simple text messages. */ +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. */ + &ProtoViewChatDecoder, /* Protoview simple text messages. */ /* Warning: the following decoder must stay at the end of the * list. Otherwise would detect most signals and prevent the actaul * decoders from handling them. */ - &UnknownDecoder, /* General protocol detector. */ - NULL -}; + &UnknownDecoder, /* General protocol detector. */ + NULL}; /* ============================================================================= * Raw signal detection @@ -54,7 +53,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; @@ -77,13 +76,13 @@ 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 min_duration) { +uint32_t search_coherent_signal(RawSamplesBuffer* s, uint32_t idx, uint32_t min_duration) { 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)); // Set a min/max duration limit for samples to be considered part of a // coherent signal. The maximum length is fixed while the minimum @@ -93,36 +92,36 @@ uint32_t search_coherent_signal(RawSamplesBuffer *s, uint32_t idx, uint32_t min_ uint32_t len = 0; /* Observed len of coherent samples. */ s->short_pulse_dur = 0; - for (uint32_t j = idx; j < idx+s->total; j++) { + for(uint32_t j = idx; j < idx + s->total; j++) { bool level; uint32_t dur; raw_samples_get(s, j, &level, &dur); - if (dur < min_duration || dur > max_duration) break; /* return. */ + if(dur < min_duration || dur > max_duration) 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. */ @@ -130,7 +129,7 @@ uint32_t search_coherent_signal(RawSamplesBuffer *s, uint32_t idx, uint32_t min_ } } - 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. */ @@ -140,14 +139,12 @@ uint32_t search_coherent_signal(RawSamplesBuffer *s, uint32_t idx, uint32_t min_ /* 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]; } } @@ -156,33 +153,28 @@ uint32_t search_coherent_signal(RawSamplesBuffer *s, uint32_t idx, uint32_t min_ /* 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; } /* Called when we detect a message. Just blinks when the message was * not decoded. Vibrates, too, when the message was correctly decoded. */ -void notify_signal_detected(ProtoViewApp *app, bool decoded) { +void notify_signal_detected(ProtoViewApp* app, bool decoded) { static const NotificationSequence decoded_seq = { &message_vibro_on, &message_green_255, &message_delay_50, &message_green_0, &message_vibro_off, - NULL - }; + NULL}; static const NotificationSequence unknown_seq = { - &message_red_255, - &message_delay_50, - &message_red_0, - NULL - }; + &message_red_255, &message_delay_50, &message_red_0, NULL}; - if (decoded) + if(decoded) notification_message(app->notification, &decoded_seq); else notification_message(app->notification, &unknown_seq); @@ -192,37 +184,37 @@ void notify_signal_detected(ProtoViewApp *app, bool decoded) { * 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, RawSamplesBuffer *source, uint32_t min_duration) { +void scan_for_signal(ProtoViewApp* app, RawSamplesBuffer* source, uint32_t min_duration) { /* We need to work on a copy: the source buffer may be populated * by the background thread receiving data. */ - RawSamplesBuffer *copy = raw_samples_alloc(); - raw_samples_copy(copy,source); + RawSamplesBuffer* copy = raw_samples_alloc(); + raw_samples_copy(copy, source); /* 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. */ uint32_t i = 0; - while (i < copy->total-1) { - uint32_t thislen = search_coherent_signal(copy,i,min_duration); + while(i < copy->total - 1) { + uint32_t thislen = search_coherent_signal(copy, i, min_duration); /* For messages that are long enough, attempt decoding. */ - if (thislen > minlen) { + if(thislen > minlen) { /* Allocate the message information that some decoder may * fill, in case it is able to decode a message. */ - ProtoViewMsgInfo *info = malloc(sizeof(ProtoViewMsgInfo)); - init_msg_info(info,app); + ProtoViewMsgInfo* info = malloc(sizeof(ProtoViewMsgInfo)); + init_msg_info(info, app); info->short_pulse_dur = copy->short_pulse_dur; uint32_t saved_idx = copy->idx; /* Save index, see later. */ /* decode_signal() expects the detected signal to start * from index zero .*/ - 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. */ @@ -230,24 +222,24 @@ void scan_for_signal(ProtoViewApp *app, RawSamplesBuffer *source, uint32_t min_d * than the previous undecoded one, or the previous one was * unknown and this is decoded. */ bool oldsignal_not_decoded = app->signal_decoded == false || - app->msg_info->decoder == &UnknownDecoder; + app->msg_info->decoder == &UnknownDecoder; - if (oldsignal_not_decoded && - (thislen > app->signal_bestlen || - (decoded && info->decoder != &UnknownDecoder))) - { + if(oldsignal_not_decoded && + (thislen > app->signal_bestlen || (decoded && info->decoder != &UnknownDecoder))) { free_msg_info(app->msg_info); app->msg_info = info; app->signal_bestlen = thislen; app->signal_decoded = decoded; - raw_samples_copy(DetectedSamples,copy); - raw_samples_center(DetectedSamples,i); - FURI_LOG_E(TAG, "===> Displayed sample updated (%d samples %lu us)", - (int)thislen, DetectedSamples->short_pulse_dur); - - adjust_raw_view_scale(app,DetectedSamples->short_pulse_dur); - if (app->msg_info->decoder != &UnknownDecoder) - notify_signal_detected(app,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); + + adjust_raw_view_scale(app, DetectedSamples->short_pulse_dur); + if(app->msg_info->decoder != &UnknownDecoder) notify_signal_detected(app, decoded); } else { /* If the structure was not filled, discard it. Otherwise * now the owner is app->msg_info. */ @@ -275,38 +267,42 @@ void scan_for_signal(ProtoViewApp *app, RawSamplesBuffer *source, uint32_t min_d /* 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<= blen) return; + if(val) + b[byte] |= 1 << bit; else - b[byte] &= ~(1<= blen) return 0; - return (b[byte] & (1<= blen) return 0; + return (b[byte] & (1 << bit)) != 0; } /* Copy 'count' bits from the bitmap 's' of 'slen' total bytes, to the * bitmap 'd' of 'dlen' total bytes. The bits are copied starting from * offset 'soff' of the source bitmap to the offset 'doff' of the * destination bitmap. */ -void bitmap_copy(uint8_t *d, uint32_t dlen, uint32_t doff, - uint8_t *s, uint32_t slen, uint32_t soff, - uint32_t count) -{ +void bitmap_copy( + uint8_t* d, + uint32_t dlen, + uint32_t doff, + uint8_t* s, + uint32_t slen, + uint32_t soff, + uint32_t count) { /* If we are byte-aligned in both source and destination, use a fast * path for the number of bytes we can consume this way. */ - if ((doff & 7) == 0 && (soff & 7) == 0) { - uint32_t didx = doff/8; - uint32_t sidx = soff/8; + if((doff & 7) == 0 && (soff & 7) == 0) { + uint32_t didx = doff / 8; + uint32_t sidx = soff / 8; while(count > 8 && didx < dlen && sidx < slen) { d[didx++] = s[sidx++]; count -= 8; @@ -319,9 +315,9 @@ void bitmap_copy(uint8_t *d, uint32_t dlen, uint32_t doff, /* Copy the bits needed to reach an offset where we can copy * two half bytes of src to a full byte of destination. */ - while(count > 8 && (doff&7) != 0) { - bool bit = bitmap_get(s,slen,soff++); - bitmap_set(d,dlen,doff++,bit); + while(count > 8 && (doff & 7) != 0) { + bool bit = bitmap_get(s, slen, soff++); + bitmap_set(d, dlen, doff++, bit); count--; } @@ -364,13 +360,12 @@ void bitmap_copy(uint8_t *d, uint32_t dlen, uint32_t doff, * src[2] << 5, that is "WORLDS!!" >> 5 = ".....WOR" * That is "HELLOWOR" */ - if (count > 8) { + if(count > 8) { uint8_t skew = soff % 8; /* Don't worry, compiler will optimize. */ - uint32_t didx = doff/8; - uint32_t sidx = soff/8; + uint32_t didx = doff / 8; + uint32_t sidx = soff / 8; while(count > 8 && didx < dlen && sidx < slen) { - d[didx] = ((s[sidx] << skew) | - (s[sidx+1] >> (8-skew))); + d[didx] = ((s[sidx] << skew) | (s[sidx + 1] >> (8 - skew))); sidx++; didx++; soff += 8; @@ -382,8 +377,8 @@ void bitmap_copy(uint8_t *d, uint32_t dlen, uint32_t doff, /* Here count is guaranteed to be < 8. * Copy the final bits bit by bit. */ while(count) { - bool bit = bitmap_get(s,slen,soff++); - bitmap_set(d,dlen,doff++,bit); + bool bit = bitmap_get(s, slen, soff++); + bitmap_set(d, dlen, doff++, bit); count--; } } @@ -391,15 +386,15 @@ void bitmap_copy(uint8_t *d, uint32_t dlen, uint32_t doff, /* We decode bits assuming the first bit we receive is the MSB * (see bitmap_set/get functions). Certain devices send data * encoded in the reverse way. */ -void bitmap_reverse_bytes_bits(uint8_t *p, uint32_t len) { - for (uint32_t j = 0; j < len; j++) { +void bitmap_reverse_bytes_bits(uint8_t* p, uint32_t len) { + for(uint32_t j = 0; j < len; j++) { uint32_t b = p[j]; /* Step 1: swap the two nibbles: 12345678 -> 56781234 */ - b = (b&0xf0)>>4 | (b&0x0f)<<4; + b = (b & 0xf0) >> 4 | (b & 0x0f) << 4; /* Step 2: swap adjacent pairs : 56781234 -> 78563412 */ - b = (b&0xcc)>>2 | (b&0x33)<<2; + b = (b & 0xcc) >> 2 | (b & 0x33) << 2; /* Step 3: swap adjacent bits : 78563412 -> 87654321 */ - b = (b&0xaa)>>1 | (b&0x55)<<1; + b = (b & 0xaa) >> 1 | (b & 0x55) << 1; p[j] = b; } } @@ -407,10 +402,10 @@ void bitmap_reverse_bytes_bits(uint8_t *p, uint32_t len) { /* 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; } @@ -423,26 +418,35 @@ 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; } /* Compare bitmaps b1 and b2 (possibly overlapping or the same bitmap), * at the specified offsets, for cmplen bits. Returns true if the * exact same bits are found, otherwise false. */ -bool bitmap_match_bitmap(uint8_t *b1, uint32_t b1len, uint32_t b1off, - uint8_t *b2, uint32_t b2len, uint32_t b2off, - uint32_t cmplen) -{ - for (uint32_t j = 0; j < cmplen; j++) { - bool bit1 = bitmap_get(b1,b1len,b1off+j); - bool bit2 = bitmap_get(b2,b2len,b2off+j); - if (bit1 != bit2) return false; +bool bitmap_match_bitmap( + uint8_t* b1, + uint32_t b1len, + uint32_t b1off, + uint8_t* b2, + uint32_t b2len, + uint32_t b2off, + uint32_t cmplen) { + for(uint32_t j = 0; j < cmplen; j++) { + bool bit1 = bitmap_get(b1, b1len, b1off + j); + bool bit2 = bitmap_get(b2, b2len, b2off + j); + if(bit1 != bit2) return false; } return true; } @@ -450,11 +454,8 @@ bool bitmap_match_bitmap(uint8_t *b1, uint32_t b1len, uint32_t b1off, /* Convert 'len' bitmap bits of the bitmap 'bitmap' into a null terminated * string, stored at 'dst', that must have space at least for len+1 bytes. * The bits are extracted from the specified offset. */ -void bitmap_to_string(char *dst, uint8_t *b, uint32_t blen, - uint32_t off, uint32_t len) -{ - for (uint32_t j = 0; j < len; j++) - dst[j] = bitmap_get(b,blen,off+j) ? '1' : '0'; +void bitmap_to_string(char* dst, uint8_t* b, uint32_t blen, uint32_t off, uint32_t len) { + for(uint32_t j = 0; j < len; j++) dst[j] = bitmap_get(b, blen, off + j) ? '1' : '0'; dst[len] = 0; } @@ -465,10 +466,10 @@ void bitmap_to_string(char *dst, uint8_t *b, uint32_t blen, * 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, uint32_t off, const char *pat) { +void bitmap_set_pattern(uint8_t* b, uint32_t blen, uint32_t off, const char* pat) { uint32_t i = 0; while(pat[i]) { - bitmap_set(b,blen,i+off,pat[i] == '1'); + bitmap_set(b, blen, i + off, pat[i] == '1'); i++; } } @@ -500,31 +501,36 @@ void bitmap_set_pattern(uint8_t *b, uint32_t blen, uint32_t off, const char *pat * bits set into the buffer 'b'. The 'rate' argument, in microseconds, is * the detected short-pulse duration. We expect the line code to be * meaningful when interpreted at multiples of 'rate'. */ -uint32_t convert_signal_to_bits(uint8_t *b, uint32_t blen, RawSamplesBuffer *s, uint32_t idx, uint32_t count, uint32_t rate) { - 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; } @@ -541,23 +547,29 @@ uint32_t convert_signal_to_bits(uint8_t *b, uint32_t blen, RawSamplesBuffer *s, * specified in bytes by the caller, via the 'len' parameters). * * The decoding starts at the specified offset (in bits) 'off'. */ -uint32_t convert_from_line_code(uint8_t *buf, uint64_t buflen, uint8_t *bits, uint32_t len, uint32_t off, const char *zero_pattern, const char *one_pattern) -{ +uint32_t convert_from_line_code( + uint8_t* buf, + uint64_t buflen, + uint8_t* bits, + uint32_t len, + uint32_t off, + const char* zero_pattern, + const char* one_pattern) { uint32_t decoded = 0; /* Number of bits extracted. */ len *= 8; /* Convert bytes to bits. */ while(off < len) { 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; } @@ -568,24 +580,29 @@ uint32_t convert_from_line_code(uint8_t *buf, uint64_t buflen, uint8_t *bits, ui * in differential codings the next bits depend on the previous one. * * Parameters and return values are like convert_from_line_code(). */ -uint32_t convert_from_diff_manchester(uint8_t *buf, uint64_t buflen, uint8_t *bits, uint32_t len, uint32_t off, bool previous) -{ +uint32_t convert_from_diff_manchester( + uint8_t* buf, + uint64_t buflen, + uint8_t* bits, + uint32_t len, + uint32_t off, + bool previous) { uint32_t decoded = 0; len *= 8; /* Conver to bits. */ - for (uint32_t j = off; j < len; j += 2) { - 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; } /* Free the message info and allocated data. */ -void free_msg_info(ProtoViewMsgInfo *i) { - if (i == NULL) return; +void free_msg_info(ProtoViewMsgInfo* i) { + if(i == NULL) return; fieldset_free(i->fieldset); free(i->bits); free(i); @@ -593,9 +610,9 @@ void free_msg_info(ProtoViewMsgInfo *i) { /* Reset the message info structure before passing it to the decoding * functions. */ -void init_msg_info(ProtoViewMsgInfo *i, ProtoViewApp *app) { +void init_msg_info(ProtoViewMsgInfo* i, ProtoViewApp* app) { UNUSED(app); - memset(i,0,sizeof(ProtoViewMsgInfo)); + memset(i, 0, sizeof(ProtoViewMsgInfo)); i->bits = NULL; i->fieldset = fieldset_new(); } @@ -604,23 +621,29 @@ void init_msg_info(ProtoViewMsgInfo *i, ProtoViewApp *app) { * 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 = 32; 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); - - if (DEBUG_MSG) { /* Useful for debugging purposes. Don't remove. */ - char *str = malloc(1024); + 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); 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); @@ -633,18 +656,17 @@ 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) { + FURI_LOG_E(TAG, "Decoder %s took %lu ms", Decoders[j]->name, (unsigned long)delta); + if(decoded) { info->decoder = Decoders[j]; break; } j++; } - if (!decoded) { + if(!decoded) { FURI_LOG_E(TAG, "No decoding possible"); } else { FURI_LOG_E(TAG, "+++ Decoded %s", info->decoder->name); @@ -652,12 +674,17 @@ bool decode_signal(RawSamplesBuffer *s, uint64_t len, ProtoViewMsgInfo *info) { * with the decoded signal. The decoder may not implement offset/len * filling of the structure. In such case we have no info and * pulses_count will be set to zero. */ - if (info->pulses_count) { - info->bits_bytes = (info->pulses_count+7)/8; // Round to full byte. + if(info->pulses_count) { + info->bits_bytes = (info->pulses_count + 7) / 8; // Round to full byte. info->bits = malloc(info->bits_bytes); - bitmap_copy(info->bits,info->bits_bytes,0, - bitmap,bitmap_size,info->start_off, - info->pulses_count); + bitmap_copy( + info->bits, + info->bits_bytes, + 0, + bitmap, + bitmap_size, + info->start_off, + info->pulses_count); } } free(bitmap); diff --git a/signal_file.c b/signal_file.c index 31c8726fb06..8f325ab529d 100644 --- a/signal_file.c +++ b/signal_file.c @@ -13,57 +13,66 @@ * but it's logical representation stored in the app->msg_info bitmap, where * each 1 or 0 means a puls or gap for the specified short pulse duration time * (te). */ -bool save_signal(ProtoViewApp *app, const char *filename) { +bool save_signal(ProtoViewApp* app, const char* filename) { /* We have a message at all? */ - if (app->msg_info == NULL || app->msg_info->pulses_count == 0) return false; - - Storage *storage = furi_record_open(RECORD_STORAGE); - FlipperFormat *file = flipper_format_file_alloc(storage); - Stream *stream = flipper_format_get_raw_stream(file); - FuriString *file_content = NULL; + if(app->msg_info == NULL || app->msg_info->pulses_count == 0) return false; + + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* file = flipper_format_file_alloc(storage); + Stream* stream = flipper_format_get_raw_stream(file); + FuriString* file_content = NULL; bool success = true; - if (flipper_format_file_open_always(file, filename)) { + if(flipper_format_file_open_always(file, filename)) { /* Write the file header. */ - FuriString *file_content = furi_string_alloc(); - const char *preset_id = ProtoViewModulations[app->modulation].id; - - furi_string_printf(file_content, - "Filetype: Flipper SubGhz RAW File\n" - "Version: 1\n" - "Frequency: %ld\n" - "Preset: %s\n", - app->frequency, - preset_id ? preset_id : "FuriHalSubGhzPresetCustom"); + FuriString* file_content = furi_string_alloc(); + const char* preset_id = ProtoViewModulations[app->modulation].id; + + furi_string_printf( + file_content, + "Filetype: Flipper SubGhz RAW File\n" + "Version: 1\n" + "Frequency: %ld\n" + "Preset: %s\n", + app->frequency, + preset_id ? preset_id : "FuriHalSubGhzPresetCustom"); /* For custom modulations, we need to emit a set of registers. */ - if (preset_id == NULL) { - FuriString *custom = furi_string_alloc(); - uint8_t *regs = ProtoViewModulations[app->modulation].custom; - furi_string_printf(custom, + if(preset_id == NULL) { + FuriString* custom = furi_string_alloc(); + uint8_t* regs = ProtoViewModulations[app->modulation].custom; + furi_string_printf( + custom, "Custom_preset_module: CC1101\n" - "Custom_preset_data: "); - for (int j = 0; regs[j]; j += 2) { - furi_string_cat_printf(custom, "%02X %02X ", - (int)regs[j], (int)regs[j+1]); + "Custom_preset_data: "); + + /* We will know the size of the preset data once we reach the end + * of the registers (null address). For now it's INT_MAX. */ + int preset_data_size = INT_MAX; + bool patable_reached = false; + for(int j = 0; j <= preset_data_size; j += 2) { + // End reached, set the size to write the remaining 8 bytes (PATABLE) + if(!patable_reached && regs[j] == 0) { + preset_data_size = j + 8; + patable_reached = true; + } + furi_string_cat_printf(custom, "%02X %02X ", (int)regs[j], (int)regs[j + 1]); } - size_t len = furi_string_size(file_content); - furi_string_set_char(custom,len-1,'\n'); - furi_string_cat(file_content,custom); + size_t len = furi_string_size(custom); + furi_string_set_char(custom, len - 1, '\n'); + furi_string_cat(file_content, custom); furi_string_free(custom); } /* We always save raw files. */ - furi_string_cat_printf(file_content, - "Protocol: RAW\n" - "RAW_Data: -10000\n"); // Start with 10 ms of gap + furi_string_cat_printf( + file_content, + "Protocol: RAW\n" + "RAW_Data: -10000\n"); // Start with 10 ms of gap /* Write header. */ size_t len = furi_string_size(file_content); - if (stream_write(stream, - (uint8_t*) furi_string_get_cstr(file_content), len) - != len) - { + if(stream_write(stream, (uint8_t*)furi_string_get_cstr(file_content), len) != len) { FURI_LOG_W(TAG, "Short write to file"); success = false; goto write_err; @@ -76,15 +85,13 @@ bool save_signal(ProtoViewApp *app, const char *filename) { uint32_t this_line_samples = 0; uint32_t max_line_samples = 100; uint32_t idx = 0; // Iindex in the signal bitmap. - ProtoViewMsgInfo *i = app->msg_info; + ProtoViewMsgInfo* i = app->msg_info; while(idx < i->pulses_count) { - bool level = bitmap_get(i->bits,i->bits_bytes,idx); + bool level = bitmap_get(i->bits, i->bits_bytes, idx); uint32_t te_times = 1; idx++; /* Count the duration of the current pulse/gap. */ - while(idx < i->pulses_count && - bitmap_get(i->bits,i->bits_bytes,idx) == level) - { + while(idx < i->pulses_count && bitmap_get(i->bits, i->bits_bytes, idx) == level) { te_times++; idx++; } @@ -92,32 +99,29 @@ bool save_signal(ProtoViewApp *app, const char *filename) { // next gap or pulse. int32_t dur = (int32_t)i->short_pulse_dur * te_times; - if (level == 0) dur = -dur; /* Negative is gap in raw files. */ + if(level == 0) dur = -dur; /* Negative is gap in raw files. */ /* Emit the sample. If this is the first sample of the line, * also emit the RAW_Data: field. */ - if (this_line_samples == 0) - furi_string_cat_printf(file_content,"RAW_Data: "); - furi_string_cat_printf(file_content,"%d ",(int)dur); + if(this_line_samples == 0) furi_string_cat_printf(file_content, "RAW_Data: "); + furi_string_cat_printf(file_content, "%d ", (int)dur); this_line_samples++; /* Store the current set of samples on disk, when we reach a * given number or the end of the signal. */ bool end_reached = (idx == i->pulses_count); - if (this_line_samples == max_line_samples || end_reached) { + if(this_line_samples == max_line_samples || end_reached) { /* If that's the end, terminate the signal with a long * gap. */ - if (end_reached) furi_string_cat_printf(file_content,"-10000 "); + if(end_reached) furi_string_cat_printf(file_content, "-10000 "); /* We always have a trailing space in the last sample. Make it * a newline. */ size_t len = furi_string_size(file_content); - furi_string_set_char(file_content,len-1,'\n'); + furi_string_set_char(file_content, len - 1, '\n'); - if (stream_write(stream, - (uint8_t*) furi_string_get_cstr(file_content), - len) != len) - { + if(stream_write(stream, (uint8_t*)furi_string_get_cstr(file_content), len) != + len) { FURI_LOG_W(TAG, "Short write to file"); success = false; goto write_err; @@ -136,6 +140,6 @@ bool save_signal(ProtoViewApp *app, const char *filename) { write_err: furi_record_close(RECORD_STORAGE); flipper_format_free(file); - if (file_content != NULL) furi_string_free(file_content); + if(file_content != NULL) furi_string_free(file_content); return success; } diff --git a/ui.c b/ui.c index 8badab5bf8a..b0251f09fbe 100644 --- a/ui.c +++ b/ui.c @@ -10,36 +10,31 @@ /* Return the ID of the currently selected subview, of the current * view. */ -int ui_get_current_subview(ProtoViewApp *app) { +int ui_get_current_subview(ProtoViewApp* app) { return app->current_subview[app->current_view]; } /* Called by view rendering callback that has subviews, to show small triangles * facing down/up if there are other subviews the user can access with up * and down. */ -void ui_show_available_subviews(Canvas *canvas, ProtoViewApp *app, - int last_subview) -{ +void ui_show_available_subviews(Canvas* canvas, ProtoViewApp* app, int last_subview) { int subview = ui_get_current_subview(app); - if (subview != 0) - canvas_draw_triangle(canvas,120,5,8,5,CanvasDirectionBottomToTop); - if (subview != last_subview-1) - canvas_draw_triangle(canvas,120,59,8,5,CanvasDirectionTopToBottom); + if(subview != 0) canvas_draw_triangle(canvas, 120, 5, 8, 5, CanvasDirectionBottomToTop); + if(subview != last_subview - 1) + canvas_draw_triangle(canvas, 120, 59, 8, 5, CanvasDirectionTopToBottom); } /* Handle up/down keys when we are in a subview. If the function catched * such keypress, it returns true, so that the actual view input callback * knows it can just return ASAP without doing anything. */ -bool ui_process_subview_updown(ProtoViewApp *app, InputEvent input, int last_subview) { +bool ui_process_subview_updown(ProtoViewApp* app, InputEvent input, int last_subview) { int subview = ui_get_current_subview(app); - if (input.type == InputTypePress) { - if (input.key == InputKeyUp) { - if (subview != 0) - app->current_subview[app->current_view]--; + if(input.type == InputTypePress) { + if(input.key == InputKeyUp) { + if(subview != 0) app->current_subview[app->current_view]--; return true; - } else if (input.key == InputKeyDown) { - if (subview != last_subview-1) - app->current_subview[app->current_view]++; + } else if(input.key == InputKeyDown) { + if(subview != last_subview - 1) app->current_subview[app->current_view]++; return true; } } @@ -62,16 +57,18 @@ bool ui_process_subview_updown(ProtoViewApp *app, InputEvent input, int last_sub * * Note: if the buffer is not a null-termined zero string, what it contains will * be used as initial input for the user. */ -void ui_show_keyboard(ProtoViewApp *app, char *buffer, uint32_t buflen, - void (*done_callback)(void*)) -{ +void ui_show_keyboard( + ProtoViewApp* app, + char* buffer, + uint32_t buflen, + void (*done_callback)(void*)) { app->show_text_input = true; app->text_input_buffer = buffer; app->text_input_buffer_len = buflen; app->text_input_done_callback = done_callback; } -void ui_dismiss_keyboard(ProtoViewApp *app) { +void ui_dismiss_keyboard(ProtoViewApp* app) { view_dispatcher_stop(app->view_dispatcher); } @@ -79,24 +76,24 @@ void ui_dismiss_keyboard(ProtoViewApp *app) { /* Set an alert message to be shown over any currently active view, for * the specified amount of time of 'ttl' milliseconds. */ -void ui_show_alert(ProtoViewApp *app, const char *text, uint32_t ttl) { +void ui_show_alert(ProtoViewApp* app, const char* text, uint32_t ttl) { app->alert_dismiss_time = furi_get_tick() + furi_ms_to_ticks(ttl); - snprintf(app->alert_text,ALERT_MAX_LEN,"%s",text); + snprintf(app->alert_text, ALERT_MAX_LEN, "%s", text); } /* Cancel the alert before its time has elapsed. */ -void ui_dismiss_alert(ProtoViewApp *app) { +void ui_dismiss_alert(ProtoViewApp* app) { app->alert_dismiss_time = 0; } /* Show the alert if an alert is set. This is called after the currently * active view displayed its stuff, so we overwrite the screen with the * alert message. */ -void ui_draw_alert_if_needed(Canvas *canvas, ProtoViewApp *app) { - if (app->alert_dismiss_time == 0) { +void ui_draw_alert_if_needed(Canvas* canvas, ProtoViewApp* app) { + if(app->alert_dismiss_time == 0) { /* No active alert. */ return; - } else if (app->alert_dismiss_time < furi_get_tick()) { + } else if(app->alert_dismiss_time < furi_get_tick()) { /* Alert just expired. */ ui_dismiss_alert(app); return; @@ -106,41 +103,43 @@ void ui_draw_alert_if_needed(Canvas *canvas, ProtoViewApp *app) { canvas_set_font(canvas, FontPrimary); uint8_t w = canvas_string_width(canvas, app->alert_text); uint8_t h = 8; // Font height. - uint8_t text_x = 64-(w/2); - uint8_t text_y = 32+4; + uint8_t text_x = 64 - (w / 2); + uint8_t text_y = 32 + 4; uint8_t padding = 3; - canvas_set_color(canvas,ColorBlack); - canvas_draw_box(canvas,text_x-padding,text_y-padding-h,w+padding*2,h+padding*2); - canvas_set_color(canvas,ColorWhite); - canvas_draw_box(canvas,text_x-padding+1,text_y-padding-h+1,w+padding*2-2,h+padding*2-2); - canvas_set_color(canvas,ColorBlack); - canvas_draw_str(canvas,text_x,text_y,app->alert_text); + canvas_set_color(canvas, ColorBlack); + canvas_draw_box( + canvas, text_x - padding, text_y - padding - h, w + padding * 2, h + padding * 2); + canvas_set_color(canvas, ColorWhite); + canvas_draw_box( + canvas, + text_x - padding + 1, + text_y - padding - h + 1, + w + padding * 2 - 2, + h + padding * 2 - 2); + canvas_set_color(canvas, ColorBlack); + canvas_draw_str(canvas, text_x, text_y, app->alert_text); } /* =========================== Canvas extensions ============================ */ -void canvas_draw_str_with_border(Canvas* canvas, uint8_t x, uint8_t y, const char* str, Color text_color, Color border_color) -{ +void canvas_draw_str_with_border( + Canvas* canvas, + uint8_t x, + uint8_t y, + const char* str, + Color text_color, + Color border_color) { struct { - uint8_t x; uint8_t y; - } dir[8] = { - {-1,-1}, - {0,-1}, - {1,-1}, - {1,0}, - {1,1}, - {0,1}, - {-1,1}, - {-1,0} - }; + uint8_t x; + uint8_t y; + } dir[8] = {{-1, -1}, {0, -1}, {1, -1}, {1, 0}, {1, 1}, {0, 1}, {-1, 1}, {-1, 0}}; /* Rotate in all the directions writing the same string to create a * border, then write the actual string in the other color in the * middle. */ canvas_set_color(canvas, border_color); - for (int j = 0; j < 8; j++) - canvas_draw_str(canvas,x+dir[j].x,y+dir[j].y,str); + for(int j = 0; j < 8; j++) canvas_draw_str(canvas, x + dir[j].x, y + dir[j].y, str); canvas_set_color(canvas, text_color); - canvas_draw_str(canvas,x,y,str); + canvas_draw_str(canvas, x, y, str); canvas_set_color(canvas, ColorBlack); } diff --git a/view_build.c b/view_build.c index b47c2c9c232..57e6e4fbcc8 100644 --- a/view_build.c +++ b/view_build.c @@ -3,39 +3,38 @@ #include "app.h" -extern ProtoViewDecoder *Decoders[]; // Defined in signal.c. +extern ProtoViewDecoder* Decoders[]; // Defined in signal.c. /* Our view private data. */ #define USER_VALUE_LEN 64 typedef struct { - ProtoViewDecoder *decoder; /* Decoder we are using to create a + ProtoViewDecoder* decoder; /* Decoder we are using to create a message. */ - uint32_t cur_decoder; /* Decoder index when we are yet selecting + uint32_t cur_decoder; /* Decoder index when we are yet selecting a decoder. Used when decoder is NULL. */ - ProtoViewFieldSet *fieldset; /* The fields to populate. */ - uint32_t cur_field; /* Field we are editing right now. This + ProtoViewFieldSet* fieldset; /* The fields to populate. */ + uint32_t cur_field; /* Field we are editing right now. This is the index inside the 'fieldset' fields. */ - char *user_value; /* Keyboard input to replace the current + char* user_value; /* Keyboard input to replace the current field value goes here. */ } BuildViewPrivData; /* Not all the decoders support message bulding, so we can't just * increment / decrement the cur_decoder index here. */ -static void select_next_decoder(ProtoViewApp *app) { - BuildViewPrivData *privdata = app->view_privdata; - do { +static void select_next_decoder(ProtoViewApp* app) { + BuildViewPrivData* privdata = app->view_privdata; + do { privdata->cur_decoder++; - if (Decoders[privdata->cur_decoder] == NULL) - privdata->cur_decoder = 0; + if(Decoders[privdata->cur_decoder] == NULL) privdata->cur_decoder = 0; } while(Decoders[privdata->cur_decoder]->get_fields == NULL); } /* Like select_next_decoder() but goes backward. */ -static void select_prev_decoder(ProtoViewApp *app) { - BuildViewPrivData *privdata = app->view_privdata; +static void select_prev_decoder(ProtoViewApp* app) { + BuildViewPrivData* privdata = app->view_privdata; do { - if (privdata->cur_decoder == 0) { + if(privdata->cur_decoder == 0) { /* Go one after the last one to wrap around. */ while(Decoders[privdata->cur_decoder]) privdata->cur_decoder++; } @@ -45,69 +44,73 @@ static void select_prev_decoder(ProtoViewApp *app) { /* Render the view to select the decoder, among the ones that * support message building. */ -static void render_view_select_decoder(Canvas *const canvas, ProtoViewApp *app) { - BuildViewPrivData *privdata = app->view_privdata; +static void render_view_select_decoder(Canvas* const canvas, ProtoViewApp* app) { + BuildViewPrivData* privdata = app->view_privdata; canvas_set_font(canvas, FontPrimary); canvas_draw_str(canvas, 0, 9, "Signal creator"); canvas_set_font(canvas, FontSecondary); canvas_draw_str(canvas, 0, 19, "up/down: select, ok: choose"); canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas,64,38,AlignCenter,AlignCenter, - Decoders[privdata->cur_decoder]->name); + canvas_draw_str_aligned( + canvas, 64, 38, AlignCenter, AlignCenter, Decoders[privdata->cur_decoder]->name); } /* Render the view that allows the user to populate the fields needed * for the selected decoder to build a message. */ -static void render_view_set_fields(Canvas *const canvas, ProtoViewApp *app) { - BuildViewPrivData *privdata = app->view_privdata; +static void render_view_set_fields(Canvas* const canvas, ProtoViewApp* app) { + BuildViewPrivData* privdata = app->view_privdata; char buf[32]; - snprintf(buf,sizeof(buf), "%s field %d/%d", - privdata->decoder->name, (int)privdata->cur_field+1, + snprintf( + buf, + sizeof(buf), + "%s field %d/%d", + privdata->decoder->name, + (int)privdata->cur_field + 1, (int)privdata->fieldset->numfields); - canvas_set_color(canvas,ColorBlack); - canvas_draw_box(canvas,0,0,128,21); - canvas_set_color(canvas,ColorWhite); + canvas_set_color(canvas, ColorBlack); + canvas_draw_box(canvas, 0, 0, 128, 21); + canvas_set_color(canvas, ColorWhite); canvas_set_font(canvas, FontPrimary); canvas_draw_str(canvas, 1, 9, buf); canvas_set_font(canvas, FontSecondary); canvas_draw_str(canvas, 1, 19, "up/down: next field, ok: edit"); /* Write the field name, type, current content. */ - canvas_set_color(canvas,ColorBlack); - ProtoViewField *field = privdata->fieldset->fields[privdata->cur_field]; - snprintf(buf,sizeof(buf), "%s %s:%d", field->name, - field_get_type_name(field), (int)field->len); + canvas_set_color(canvas, ColorBlack); + ProtoViewField* field = privdata->fieldset->fields[privdata->cur_field]; + snprintf( + buf, sizeof(buf), "%s %s:%d", field->name, field_get_type_name(field), (int)field->len); buf[0] = toupper(buf[0]); canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas,64,30,AlignCenter,AlignCenter,buf); + canvas_draw_str_aligned(canvas, 64, 30, AlignCenter, AlignCenter, buf); canvas_set_font(canvas, FontSecondary); /* Render the current value between "" */ - unsigned int written = (unsigned int) field_to_string(buf+1,sizeof(buf)-1,field); + unsigned int written = (unsigned int)field_to_string(buf + 1, sizeof(buf) - 1, field); buf[0] = '"'; - if (written+3 < sizeof(buf)) memcpy(buf+written+1,"\"\x00",2); - canvas_draw_str_aligned(canvas,63,45,AlignCenter,AlignCenter,buf); + if(written + 3 < sizeof(buf)) memcpy(buf + written + 1, "\"\x00", 2); + canvas_draw_str_aligned(canvas, 63, 45, AlignCenter, AlignCenter, buf); /* Footer instructions. */ canvas_draw_str(canvas, 0, 62, "Long ok: create, < > incr/decr"); } /* Render the build message view. */ -void render_view_build_message(Canvas *const canvas, ProtoViewApp *app) { - BuildViewPrivData *privdata = app->view_privdata; +void render_view_build_message(Canvas* const canvas, ProtoViewApp* app) { + BuildViewPrivData* privdata = app->view_privdata; - if (privdata->decoder) - render_view_set_fields(canvas,app); + if(privdata->decoder) + render_view_set_fields(canvas, app); else - render_view_select_decoder(canvas,app); + render_view_select_decoder(canvas, app); } /* Handle input for the decoder selection. */ -static void process_input_select_decoder(ProtoViewApp *app, InputEvent input) { - BuildViewPrivData *privdata = app->view_privdata; - if (input.type == InputTypeShort) { - if (input.key == InputKeyOk) { +static void process_input_select_decoder(ProtoViewApp* app, InputEvent input) { + BuildViewPrivData* privdata = app->view_privdata; + if(input.type == InputTypeShort) { + if(input.key == InputKeyOk) { privdata->decoder = Decoders[privdata->cur_decoder]; privdata->fieldset = fieldset_new(); privdata->decoder->get_fields(privdata->fieldset); @@ -116,11 +119,8 @@ static void process_input_select_decoder(ProtoViewApp *app, InputEvent input) { * same decoder the user selected, let's populate the * defaults with the current values. So the user will * actaully edit the current message. */ - if (app->signal_decoded && - app->msg_info->decoder == privdata->decoder) - { - fieldset_copy_matching_fields(privdata->fieldset, - app->msg_info->fieldset); + if(app->signal_decoded && app->msg_info->decoder == privdata->decoder) { + fieldset_copy_matching_fields(privdata->fieldset, app->msg_info->fieldset); } /* Now we use the subview system in order to protect the @@ -128,10 +128,10 @@ static void process_input_select_decoder(ProtoViewApp *app, InputEvent input) { Since we are technically into a subview now, we'll have control of < and >. */ InputEvent ii = {.type = InputTypePress, .key = InputKeyDown}; - ui_process_subview_updown(app,ii,2); - } else if (input.key == InputKeyDown) { + ui_process_subview_updown(app, ii, 2); + } else if(input.key == InputKeyDown) { select_next_decoder(app); - } else if (input.key == InputKeyUp) { + } else if(input.key == InputKeyUp) { select_prev_decoder(app); } } @@ -140,12 +140,13 @@ static void process_input_select_decoder(ProtoViewApp *app, InputEvent input) { /* Called after the user typed the new field value in the keyboard. * Let's save it and remove the keyboard view. */ static void text_input_done_callback(void* context) { - ProtoViewApp *app = context; - BuildViewPrivData *privdata = app->view_privdata; + ProtoViewApp* app = context; + BuildViewPrivData* privdata = app->view_privdata; - if (field_set_from_string(privdata->fieldset->fields[privdata->cur_field], - privdata->user_value, strlen(privdata->user_value)) == false) - { + if(field_set_from_string( + privdata->fieldset->fields[privdata->cur_field], + privdata->user_value, + strlen(privdata->user_value)) == false) { ui_show_alert(app, "Invalid value", 1500); } @@ -160,94 +161,88 @@ static void text_input_done_callback(void* context) { * decrement the current field in a much simpler way. * * The current filed is changed by 'incr' amount. */ -static bool increment_current_field(ProtoViewApp *app, int incr) { - BuildViewPrivData *privdata = app->view_privdata; - ProtoViewFieldSet *fs = privdata->fieldset; - ProtoViewField *f = fs->fields[privdata->cur_field]; - return field_incr_value(f,incr); +static bool increment_current_field(ProtoViewApp* app, int incr) { + BuildViewPrivData* privdata = app->view_privdata; + ProtoViewFieldSet* fs = privdata->fieldset; + ProtoViewField* f = fs->fields[privdata->cur_field]; + return field_incr_value(f, incr); } /* Handle input for fields editing mode. */ -static void process_input_set_fields(ProtoViewApp *app, InputEvent input) { - BuildViewPrivData *privdata = app->view_privdata; - ProtoViewFieldSet *fs = privdata->fieldset; +static void process_input_set_fields(ProtoViewApp* app, InputEvent input) { + BuildViewPrivData* privdata = app->view_privdata; + ProtoViewFieldSet* fs = privdata->fieldset; - if (input.type == InputTypeShort && input.key == InputKeyOk) { + if(input.type == InputTypeShort && input.key == InputKeyOk) { /* Show the keyboard to let the user type the new * value. */ - if (privdata->user_value == NULL) - privdata->user_value = malloc(USER_VALUE_LEN); - field_to_string(privdata->user_value, USER_VALUE_LEN, - fs->fields[privdata->cur_field]); - ui_show_keyboard(app, privdata->user_value, USER_VALUE_LEN, - text_input_done_callback); - } else if (input.type == InputTypeShort && input.key == InputKeyDown) { - privdata->cur_field = (privdata->cur_field+1) % fs->numfields; - } else if (input.type == InputTypeShort && input.key == InputKeyUp) { - if (privdata->cur_field == 0) - privdata->cur_field = fs->numfields-1; + if(privdata->user_value == NULL) privdata->user_value = malloc(USER_VALUE_LEN); + field_to_string(privdata->user_value, USER_VALUE_LEN, fs->fields[privdata->cur_field]); + ui_show_keyboard(app, privdata->user_value, USER_VALUE_LEN, text_input_done_callback); + } else if(input.type == InputTypeShort && input.key == InputKeyDown) { + privdata->cur_field = (privdata->cur_field + 1) % fs->numfields; + } else if(input.type == InputTypeShort && input.key == InputKeyUp) { + if(privdata->cur_field == 0) + privdata->cur_field = fs->numfields - 1; else privdata->cur_field--; - } else if (input.type == InputTypeShort && input.key == InputKeyRight) { - increment_current_field(app,1); - } else if (input.type == InputTypeShort && input.key == InputKeyLeft) { - increment_current_field(app,-1); - } else if (input.type == InputTypeRepeat && input.key == InputKeyRight) { + } else if(input.type == InputTypeShort && input.key == InputKeyRight) { + increment_current_field(app, 1); + } else if(input.type == InputTypeShort && input.key == InputKeyLeft) { + increment_current_field(app, -1); + } else if(input.type == InputTypeRepeat && input.key == InputKeyRight) { // The reason why we don't use a large increment directly // is that certain field types only support +1 -1 increments. int times = 10; - while(times--) increment_current_field(app,1); - } else if (input.type == InputTypeRepeat && input.key == InputKeyLeft) { + while(times--) increment_current_field(app, 1); + } else if(input.type == InputTypeRepeat && input.key == InputKeyLeft) { int times = 10; - while(times--) increment_current_field(app,-1); - } else if (input.type == InputTypeLong && input.key == InputKeyOk) { + while(times--) increment_current_field(app, -1); + } else if(input.type == InputTypeLong && input.key == InputKeyOk) { // Build the message in a fresh raw buffer. - if (privdata->decoder->build_message) { - RawSamplesBuffer *rs = raw_samples_alloc(); - privdata->decoder->build_message(rs,privdata->fieldset); + if(privdata->decoder->build_message) { + RawSamplesBuffer* rs = raw_samples_alloc(); + privdata->decoder->build_message(rs, privdata->fieldset); app->signal_decoded = false; // So that the new signal will be - // accepted as the current signal. - scan_for_signal(app,rs,5); + // accepted as the current signal. + scan_for_signal(app, rs, 5); raw_samples_free(rs); - ui_show_alert(app,"Done: press back key",3000); + ui_show_alert(app, "Done: press back key", 3000); } } } /* Handle input for the build message view. */ -void process_input_build_message(ProtoViewApp *app, InputEvent input) { - BuildViewPrivData *privdata = app->view_privdata; - if (privdata->decoder) - process_input_set_fields(app,input); +void process_input_build_message(ProtoViewApp* app, InputEvent input) { + BuildViewPrivData* privdata = app->view_privdata; + if(privdata->decoder) + process_input_set_fields(app, input); else - process_input_select_decoder(app,input); + process_input_select_decoder(app, input); } /* Enter view callback. */ -void view_enter_build_message(ProtoViewApp *app) { - BuildViewPrivData *privdata = app->view_privdata; +void view_enter_build_message(ProtoViewApp* app) { + BuildViewPrivData* privdata = app->view_privdata; // When we enter the view, the current decoder is just set to zero. // Seek the next valid if needed. - if (Decoders[privdata->cur_decoder]->get_fields == NULL) { + if(Decoders[privdata->cur_decoder]->get_fields == NULL) { select_next_decoder(app); } // However if there is currently a decoded message, and the // decoder of such message supports message building, let's // select it. - if (app->signal_decoded && - app->msg_info->decoder->get_fields && - app->msg_info->decoder->build_message) - { - while(Decoders[privdata->cur_decoder] != app->msg_info->decoder) - select_next_decoder(app); + if(app->signal_decoded && app->msg_info->decoder->get_fields && + app->msg_info->decoder->build_message) { + while(Decoders[privdata->cur_decoder] != app->msg_info->decoder) select_next_decoder(app); } } /* Called on exit for cleanup. */ -void view_exit_build_message(ProtoViewApp *app) { - BuildViewPrivData *privdata = app->view_privdata; - if (privdata->fieldset) fieldset_free(privdata->fieldset); - if (privdata->user_value) free(privdata->user_value); +void view_exit_build_message(ProtoViewApp* app) { + BuildViewPrivData* privdata = app->view_privdata; + if(privdata->fieldset) fieldset_free(privdata->fieldset); + if(privdata->user_value) free(privdata->user_value); } diff --git a/view_direct_sampling.c b/view_direct_sampling.c index 4dd7b6a05a6..f43c77042fd 100644 --- a/view_direct_sampling.c +++ b/view_direct_sampling.c @@ -2,78 +2,75 @@ * See the LICENSE file for information about the license. */ #include "app.h" -#include +#include -static void direct_sampling_timer_start(ProtoViewApp *app); -static void direct_sampling_timer_stop(ProtoViewApp *app); +static void direct_sampling_timer_start(ProtoViewApp* app); +static void direct_sampling_timer_stop(ProtoViewApp* app); -#define CAPTURED_BITMAP_BITS (128*64) -#define CAPTURED_BITMAP_BYTES (CAPTURED_BITMAP_BITS/8) +#define CAPTURED_BITMAP_BITS (128 * 64) +#define CAPTURED_BITMAP_BYTES (CAPTURED_BITMAP_BITS / 8) #define DEFAULT_USEC_PER_PIXEL 50 #define USEC_PER_PIXEL_SMALL_CHANGE 5 #define USEC_PER_PIXEL_LARGE_CHANGE 25 #define USEC_PER_PIXEL_MIN 5 #define USEC_PER_PIXEL_MAX 300 typedef struct { - uint8_t *captured; // Bitmap with the last captured screen. - uint32_t captured_idx; // Current index to write into the bitmap + uint8_t* captured; // Bitmap with the last captured screen. + uint32_t captured_idx; // Current index to write into the bitmap uint32_t usec_per_pixel; // Number of useconds a pixel should represent bool show_usage_info; } DirectSamplingViewPrivData; /* 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) { - DirectSamplingViewPrivData *privdata = app->view_privdata; +void render_view_direct_sampling(Canvas* const canvas, ProtoViewApp* app) { + DirectSamplingViewPrivData* privdata = app->view_privdata; - if (!app->direct_sampling_enabled && privdata->show_usage_info) { + if(!app->direct_sampling_enabled && privdata->show_usage_info) { canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas,2,9, "Direct sampling displays the"); - canvas_draw_str(canvas,2,18,"the captured signal in real"); - canvas_draw_str(canvas,2,27,"time, like in a CRT TV set."); - canvas_draw_str(canvas,2,36,"Use UP/DOWN to change the"); - canvas_draw_str(canvas,2,45,"resolution (usec/pixel)."); + canvas_draw_str(canvas, 2, 9, "Direct sampling displays the"); + canvas_draw_str(canvas, 2, 18, "the captured signal in real"); + canvas_draw_str(canvas, 2, 27, "time, like in a CRT TV set."); + canvas_draw_str(canvas, 2, 36, "Use UP/DOWN to change the"); + canvas_draw_str(canvas, 2, 45, "resolution (usec/pixel)."); canvas_set_font(canvas, FontPrimary); - canvas_draw_str(canvas,5,60,"To start/stop, press OK"); + canvas_draw_str(canvas, 5, 60, "To start/stop, press OK"); return; } privdata->show_usage_info = false; /* Draw on screen. */ int idx = 0; - for (int y = 0; y < 64; y++) { - for (int x = 0; x < 128; x++) { - bool level = bitmap_get(privdata->captured, - CAPTURED_BITMAP_BYTES,idx++); - if (level) canvas_draw_dot(canvas,x,y); + for(int y = 0; y < 64; y++) { + for(int x = 0; x < 128; x++) { + bool level = bitmap_get(privdata->captured, CAPTURED_BITMAP_BYTES, idx++); + if(level) canvas_draw_dot(canvas, x, y); } } char buf[32]; - snprintf(buf,sizeof(buf),"%lu usec/px", privdata->usec_per_pixel); + snprintf(buf, sizeof(buf), "%lu usec/px", privdata->usec_per_pixel); canvas_set_font(canvas, FontSecondary); - canvas_draw_str_with_border(canvas,1,60,buf,ColorWhite,ColorBlack); + canvas_draw_str_with_border(canvas, 1, 60, buf, ColorWhite, ColorBlack); } /* Handle input */ -void process_input_direct_sampling(ProtoViewApp *app, InputEvent input) { - DirectSamplingViewPrivData *privdata = app->view_privdata; +void process_input_direct_sampling(ProtoViewApp* app, InputEvent input) { + DirectSamplingViewPrivData* privdata = app->view_privdata; - if (input.type == InputTypePress && input.key == InputKeyOk) { + if(input.type == InputTypePress && input.key == InputKeyOk) { app->direct_sampling_enabled = !app->direct_sampling_enabled; } - if ((input.key == InputKeyUp || input.key == InputKeyDown) && - (input.type == InputTypePress || input.type == InputTypeRepeat)) - { - uint32_t change = input.type == InputTypePress ? - USEC_PER_PIXEL_SMALL_CHANGE : - USEC_PER_PIXEL_LARGE_CHANGE; - if (input.key == InputKeyUp) change = -change; + if((input.key == InputKeyUp || input.key == InputKeyDown) && + (input.type == InputTypePress || input.type == InputTypeRepeat)) { + uint32_t change = input.type == InputTypePress ? USEC_PER_PIXEL_SMALL_CHANGE : + USEC_PER_PIXEL_LARGE_CHANGE; + if(input.key == InputKeyUp) change = -change; privdata->usec_per_pixel += change; - if (privdata->usec_per_pixel < USEC_PER_PIXEL_MIN) + if(privdata->usec_per_pixel < USEC_PER_PIXEL_MIN) privdata->usec_per_pixel = USEC_PER_PIXEL_MIN; - else if (privdata->usec_per_pixel > USEC_PER_PIXEL_MAX) + else if(privdata->usec_per_pixel > USEC_PER_PIXEL_MAX) privdata->usec_per_pixel = USEC_PER_PIXEL_MAX; /* Update the timer frequency. */ direct_sampling_timer_stop(app); @@ -83,25 +80,26 @@ void process_input_direct_sampling(ProtoViewApp *app, InputEvent input) { /* Enter view. Stop the subghz thread to prevent access as we read * the CC1101 data directly. */ -void view_enter_direct_sampling(ProtoViewApp *app) { +void view_enter_direct_sampling(ProtoViewApp* app) { /* Set view defaults. */ - DirectSamplingViewPrivData *privdata = app->view_privdata; + DirectSamplingViewPrivData* privdata = app->view_privdata; privdata->usec_per_pixel = DEFAULT_USEC_PER_PIXEL; privdata->captured = malloc(CAPTURED_BITMAP_BYTES); privdata->show_usage_info = true; - if (app->txrx->txrx_state == TxRxStateRx && - !app->txrx->debug_timer_sampling) - { - furi_hal_subghz_stop_async_rx(); + if(app->txrx->txrx_state == TxRxStateRx && !app->txrx->debug_timer_sampling) { + subghz_devices_stop_async_rx(app->radio_device); /* To read data asynchronously directly from the view, we need * to put the CC1101 back into reception mode (the previous call * to stop the async RX will put it into idle) and configure the * G0 pin for reading. */ - furi_hal_subghz_rx(); - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, - GpioSpeedLow); + subghz_devices_set_rx(app->radio_device); + furi_hal_gpio_init( + subghz_devices_get_data_gpio(app->radio_device), + GpioModeInput, + GpioPullNo, + GpioSpeedLow); } else { raw_sampling_worker_stop(app); } @@ -111,47 +109,49 @@ void view_enter_direct_sampling(ProtoViewApp *app) { } /* Exit view. Restore the subghz thread. */ -void view_exit_direct_sampling(ProtoViewApp *app) { - DirectSamplingViewPrivData *privdata = app->view_privdata; - if (privdata->captured) free(privdata->captured); +void view_exit_direct_sampling(ProtoViewApp* app) { + DirectSamplingViewPrivData* privdata = app->view_privdata; + if(privdata->captured) free(privdata->captured); app->direct_sampling_enabled = false; direct_sampling_timer_stop(app); /* Restart normal data feeding. */ - if (app->txrx->txrx_state == TxRxStateRx && - !app->txrx->debug_timer_sampling) - { - furi_hal_subghz_start_async_rx(protoview_rx_callback, NULL); + if(app->txrx->txrx_state == TxRxStateRx && !app->txrx->debug_timer_sampling) { + subghz_devices_start_async_rx(app->radio_device, protoview_rx_callback, NULL); } else { + furi_hal_gpio_init( + subghz_devices_get_data_gpio(app->radio_device), + GpioModeInput, + GpioPullNo, + GpioSpeedLow); raw_sampling_worker_start(app); } } /* =========================== Timer implementation ========================= */ -static void ds_timer_isr(void *ctx) { - ProtoViewApp *app = ctx; - DirectSamplingViewPrivData *privdata = app->view_privdata; +static void ds_timer_isr(void* ctx) { + ProtoViewApp* app = ctx; + DirectSamplingViewPrivData* privdata = app->view_privdata; - if (app->direct_sampling_enabled) { - bool level = furi_hal_gpio_read(&gpio_cc1101_g0); - bitmap_set(privdata->captured,CAPTURED_BITMAP_BYTES, - privdata->captured_idx,level); - privdata->captured_idx = (privdata->captured_idx+1) % - CAPTURED_BITMAP_BITS; + if(app->direct_sampling_enabled) { + bool level = furi_hal_gpio_read(subghz_devices_get_data_gpio(app->radio_device)); + bitmap_set(privdata->captured, CAPTURED_BITMAP_BYTES, privdata->captured_idx, level); + privdata->captured_idx = (privdata->captured_idx + 1) % CAPTURED_BITMAP_BITS; } LL_TIM_ClearFlag_UPDATE(TIM2); } -static void direct_sampling_timer_start(ProtoViewApp *app) { - DirectSamplingViewPrivData *privdata = app->view_privdata; +static void direct_sampling_timer_start(ProtoViewApp* app) { + DirectSamplingViewPrivData* privdata = app->view_privdata; + + furi_hal_bus_enable(FuriHalBusTIM2); LL_TIM_InitTypeDef tim_init = { - .Prescaler = 63, /* CPU frequency is ~64Mhz. */ + .Prescaler = 63, /* CPU frequency is ~64Mhz. */ .CounterMode = LL_TIM_COUNTERMODE_UP, - .Autoreload = privdata->usec_per_pixel - }; + .Autoreload = privdata->usec_per_pixel}; LL_TIM_Init(TIM2, &tim_init); LL_TIM_SetClockSource(TIM2, LL_TIM_CLOCKSOURCE_INTERNAL); @@ -162,12 +162,12 @@ static void direct_sampling_timer_start(ProtoViewApp *app) { LL_TIM_EnableCounter(TIM2); } -static void direct_sampling_timer_stop(ProtoViewApp *app) { +static void direct_sampling_timer_stop(ProtoViewApp* app) { UNUSED(app); FURI_CRITICAL_ENTER(); LL_TIM_DisableCounter(TIM2); LL_TIM_DisableIT_UPDATE(TIM2); furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, NULL, NULL); - LL_TIM_DeInit(TIM2); + furi_hal_bus_disable(FuriHalBusTIM2); FURI_CRITICAL_EXIT(); } diff --git a/view_info.c b/view_info.c index 6aa69739c53..4148c27a67f 100644 --- a/view_info.c +++ b/view_info.c @@ -3,7 +3,7 @@ #include "app.h" #include -#include +#include /* This view has subviews accessible navigating up/down. This * enumaration is used to track the currently active subview. */ @@ -20,31 +20,29 @@ typedef struct { * so that the user can see what they are saving. With left/right * you can move to next rows. Here we store where we are. */ uint32_t signal_display_start_row; - char *filename; + char* filename; uint8_t cur_info_page; // Info page to display. Useful when there are - // too many fields populated by the decoder that - // a single page is not enough. + // too many fields populated by the decoder that + // a single page is not enough. } InfoViewPrivData; /* Draw the text label and value of the specified info field at x,y. */ -static void render_info_field(Canvas *const canvas, - ProtoViewField *f, uint8_t x, uint8_t y) -{ +static void render_info_field(Canvas* const canvas, ProtoViewField* f, uint8_t x, uint8_t y) { char buf[64]; char strval[32]; - field_to_string(strval,sizeof(strval),f); - snprintf(buf,sizeof(buf),"%s: %s", f->name, strval); + field_to_string(strval, sizeof(strval), f); + snprintf(buf, sizeof(buf), "%s: %s", f->name, strval); canvas_set_font(canvas, FontSecondary); canvas_draw_str(canvas, x, y, buf); } /* Render the view with the detected message information. */ #define INFO_LINES_PER_PAGE 5 -static void render_subview_main(Canvas *const canvas, ProtoViewApp *app) { - InfoViewPrivData *privdata = app->view_privdata; - uint8_t pages = (app->msg_info->fieldset->numfields - +(INFO_LINES_PER_PAGE-1)) / INFO_LINES_PER_PAGE; +static void render_subview_main(Canvas* const canvas, ProtoViewApp* app) { + InfoViewPrivData* privdata = app->view_privdata; + uint8_t pages = + (app->msg_info->fieldset->numfields + (INFO_LINES_PER_PAGE - 1)) / INFO_LINES_PER_PAGE; privdata->cur_info_page %= pages; uint8_t current_page = privdata->cur_info_page; char buf[32]; @@ -53,9 +51,9 @@ static void render_subview_main(Canvas *const canvas, ProtoViewApp *app) { canvas_set_font(canvas, FontPrimary); uint8_t y = 8, lineheight = 10; - if (pages > 1) { - snprintf(buf,sizeof(buf),"%s %u/%u", app->msg_info->decoder->name, - current_page+1, pages); + if(pages > 1) { + snprintf( + buf, sizeof(buf), "%s %u/%u", app->msg_info->decoder->name, current_page + 1, pages); canvas_draw_str(canvas, 0, y, buf); } else { canvas_draw_str(canvas, 0, y, app->msg_info->decoder->name); @@ -64,26 +62,30 @@ static void render_subview_main(Canvas *const canvas, ProtoViewApp *app) { /* Draw the info fields. */ uint8_t max_lines = INFO_LINES_PER_PAGE; - uint32_t j = current_page*max_lines; - while (j < app->msg_info->fieldset->numfields) { - render_info_field(canvas,app->msg_info->fieldset->fields[j++],0,y); + uint32_t j = current_page * max_lines; + while(j < app->msg_info->fieldset->numfields) { + render_info_field(canvas, app->msg_info->fieldset->fields[j++], 0, y); y += lineheight; - if (--max_lines == 0) break; + if(--max_lines == 0) break; } /* Draw a vertical "save" label. Temporary solution, to switch to * something better ASAP. */ y = 37; lineheight = 7; - canvas_draw_str(canvas, 119, y, "s"); y += lineheight; - canvas_draw_str(canvas, 119, y, "a"); y += lineheight; - canvas_draw_str(canvas, 119, y, "v"); y += lineheight; - canvas_draw_str(canvas, 119, y, "e"); y += lineheight; + canvas_draw_str(canvas, 119, y, "s"); + y += lineheight; + canvas_draw_str(canvas, 119, y, "a"); + y += lineheight; + canvas_draw_str(canvas, 119, y, "v"); + y += lineheight; + canvas_draw_str(canvas, 119, y, "e"); + y += lineheight; } /* Render view with save option. */ -static void render_subview_save(Canvas *const canvas, ProtoViewApp *app) { - InfoViewPrivData *privdata = app->view_privdata; +static void render_subview_save(Canvas* const canvas, ProtoViewApp* app) { + InfoViewPrivData* privdata = app->view_privdata; /* Display our signal in digital form: here we don't show the * signal with the exact timing of the received samples, but as it @@ -92,21 +94,20 @@ static void render_subview_save(Canvas *const canvas, ProtoViewApp *app) { uint8_t rowheight = 11; uint8_t bitwidth = 4; uint8_t bitheight = 5; - uint32_t idx = privdata->signal_display_start_row * (128/4); + uint32_t idx = privdata->signal_display_start_row * (128 / 4); bool prevbit = false; - for (uint8_t y = bitheight+12; y <= rows*rowheight; y += rowheight) { - for (uint8_t x = 0; x < 128; x += 4) { - bool bit = bitmap_get(app->msg_info->bits, - app->msg_info->bits_bytes,idx); - uint8_t prevy = y + prevbit*(bitheight*-1) - 1; - uint8_t thisy = y + bit*(bitheight*-1) - 1; - canvas_draw_line(canvas,x,prevy,x,thisy); - canvas_draw_line(canvas,x,thisy,x+bitwidth-1,thisy); + for(uint8_t y = bitheight + 12; y <= rows * rowheight; y += rowheight) { + for(uint8_t x = 0; x < 128; x += 4) { + bool bit = bitmap_get(app->msg_info->bits, app->msg_info->bits_bytes, idx); + uint8_t prevy = y + prevbit * (bitheight * -1) - 1; + uint8_t thisy = y + bit * (bitheight * -1) - 1; + canvas_draw_line(canvas, x, prevy, x, thisy); + canvas_draw_line(canvas, x, thisy, x + bitwidth - 1, thisy); prevbit = bit; - if (idx >= app->msg_info->pulses_count) { + if(idx >= app->msg_info->pulses_count) { canvas_set_color(canvas, ColorWhite); - canvas_draw_dot(canvas, x+1,thisy); - canvas_draw_dot(canvas, x+3,thisy); + canvas_draw_dot(canvas, x + 1, thisy); + canvas_draw_dot(canvas, x + 3, thisy); canvas_set_color(canvas, ColorBlack); } idx++; // Draw next bit @@ -118,28 +119,32 @@ static void render_subview_save(Canvas *const canvas, ProtoViewApp *app) { } /* Render the selected subview of this view. */ -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; } - ui_show_available_subviews(canvas,app,SubViewInfoLast); + ui_show_available_subviews(canvas, app, SubViewInfoLast); switch(app->current_subview[app->current_view]) { - case SubViewInfoMain: render_subview_main(canvas,app); break; - case SubViewInfoSave: render_subview_save(canvas,app); break; + case SubViewInfoMain: + render_subview_main(canvas, app); + break; + case SubViewInfoSave: + render_subview_save(canvas, app); + break; } } /* The user typed the file name. Let's save it and remove the keyboard * view. */ static void text_input_done_callback(void* context) { - ProtoViewApp *app = context; - InfoViewPrivData *privdata = app->view_privdata; + ProtoViewApp* app = context; + InfoViewPrivData* privdata = app->view_privdata; - FuriString *save_path = furi_string_alloc_printf( - "%s/%s.sub", EXT_PATH("subghz"), privdata->filename); + FuriString* save_path = + furi_string_alloc_printf("%s/%s.sub", EXT_PATH("subghz"), privdata->filename); save_signal(app, furi_string_get_cstr(save_path)); furi_string_free(save_path); @@ -151,22 +156,17 @@ static void text_input_done_callback(void* context) { /* Replace all the occurrences of character c1 with c2 in the specified * string. */ -void str_replace(char *buf, char c1, char c2) { - char *p = buf; +void str_replace(char* buf, char c1, char c2) { + char* p = buf; while(*p) { - if (*p == c1) *p = c2; + if(*p == c1) *p = c2; p++; } } /* Set a random filename the user can edit. */ -void set_signal_random_filename(ProtoViewApp *app, char *buf, size_t buflen) { - char suffix[6]; - set_random_name(suffix,sizeof(suffix)); - snprintf(buf,buflen,"%.10s-%s-%d",app->msg_info->decoder->name,suffix,rand()%1000); - str_replace(buf,' ','_'); - str_replace(buf,'-','_'); - str_replace(buf,'/','_'); +void set_signal_random_filename(ProtoViewApp* app, char* buf, size_t buflen) { + name_generator_make_auto(buf, buflen, app->msg_info->decoder->name); } /* ========================== Signal transmission =========================== */ @@ -180,20 +180,20 @@ typedef enum { SendSignalEndTransmission } SendSignalState; -#define PROTOVIEW_SENDSIGNAL_START_GAP 10000 /* microseconds. */ -#define PROTOVIEW_SENDSIGNAL_END_GAP 10000 /* microseconds. */ +#define PROTOVIEW_SENDSIGNAL_START_GAP 10000 /* microseconds. */ +#define PROTOVIEW_SENDSIGNAL_END_GAP 10000 /* microseconds. */ typedef struct { - SendSignalState state; // Current state. - uint32_t curpos; // Current bit position of data to send. - ProtoViewApp *app; // App reference. + SendSignalState state; // Current state. + uint32_t curpos; // Current bit position of data to send. + ProtoViewApp* app; // App reference. uint32_t start_gap_dur; // Gap to send at the start. - uint32_t end_gap_dur; // Gap to send at the end. + uint32_t end_gap_dur; // Gap to send at the end. } SendSignalCtx; /* Setup the state context for the callback responsible to feed data * to the subghz async tx system. */ -static void send_signal_init(SendSignalCtx *ss, ProtoViewApp *app) { +static void send_signal_init(SendSignalCtx* ss, ProtoViewApp* app) { ss->state = SendSignalSendStartGap; ss->curpos = 0; ss->app = app; @@ -214,27 +214,26 @@ static void send_signal_init(SendSignalCtx *ss, ProtoViewApp *app) { * message we are, in ss->curoff. We also send a start and end gap in order * to make sure the transmission is clear. */ -LevelDuration radio_tx_feed_data(void *ctx) { - SendSignalCtx *ss = ctx; +LevelDuration radio_tx_feed_data(void* ctx) { + SendSignalCtx* ss = ctx; /* Send start gap. */ - if (ss->state == SendSignalSendStartGap) { + if(ss->state == SendSignalSendStartGap) { ss->state = SendSignalSendBits; - return level_duration_make(0,ss->start_gap_dur); + return level_duration_make(0, ss->start_gap_dur); } /* Send data. */ - if (ss->state == SendSignalSendBits) { + if(ss->state == SendSignalSendBits) { uint32_t dur = 0, j; uint32_t level = 0; /* Let's see how many consecutive bits we have with the same * level. */ - for (j = 0; ss->curpos+j < ss->app->msg_info->pulses_count; j++) { - uint32_t l = bitmap_get(ss->app->msg_info->bits, - ss->app->msg_info->bits_bytes, - ss->curpos+j); - if (j == 0) { + for(j = 0; ss->curpos + j < ss->app->msg_info->pulses_count; j++) { + uint32_t l = + bitmap_get(ss->app->msg_info->bits, ss->app->msg_info->bits_bytes, ss->curpos + j); + if(j == 0) { /* At the first bit of this sequence, we store the * level of the sequence. */ level = l; @@ -244,22 +243,21 @@ LevelDuration radio_tx_feed_data(void *ctx) { /* As long as the level is the same, we update the duration. * Otherwise stop the loop and return this sample. */ - if (l != level) break; + if(l != level) break; dur += ss->app->msg_info->short_pulse_dur; } ss->curpos += j; /* If this was the last set of bits, change the state to * send the final gap. */ - if (ss->curpos >= ss->app->msg_info->pulses_count) - ss->state = SendSignalSendEndGap; + if(ss->curpos >= ss->app->msg_info->pulses_count) ss->state = SendSignalSendEndGap; return level_duration_make(level, dur); } /* Send end gap. */ - if (ss->state == SendSignalSendEndGap) { + if(ss->state == SendSignalSendEndGap) { ss->state = SendSignalEndTransmission; - return level_duration_make(0,ss->end_gap_dur); + return level_duration_make(0, ss->end_gap_dur); } /* End transmission. Here state is guaranteed @@ -268,7 +266,7 @@ LevelDuration radio_tx_feed_data(void *ctx) { } /* Vibrate and produce a click sound when a signal is sent. */ -void notify_signal_sent(ProtoViewApp *app) { +void notify_signal_sent(ProtoViewApp* app) { static const NotificationSequence sent_seq = { &message_blue_255, &message_vibro_on, @@ -277,59 +275,53 @@ void notify_signal_sent(ProtoViewApp *app) { &message_sound_off, &message_vibro_off, &message_blue_0, - NULL - }; + NULL}; notification_message(app->notification, &sent_seq); } /* Handle input for the info view. */ -void process_input_info(ProtoViewApp *app, InputEvent input) { +void process_input_info(ProtoViewApp* app, InputEvent input) { /* If we don't have a decoded signal, we don't allow to go up/down * in the subviews: they are only useful when a loaded signal. */ - if (app->signal_decoded && - ui_process_subview_updown(app,input,SubViewInfoLast)) return; + if(app->signal_decoded && ui_process_subview_updown(app, input, SubViewInfoLast)) return; - InfoViewPrivData *privdata = app->view_privdata; + InfoViewPrivData* privdata = app->view_privdata; int subview = ui_get_current_subview(app); /* Main subview. */ - if (subview == SubViewInfoMain) { - if (input.type == InputTypeLong && input.key == InputKeyOk) { + if(subview == SubViewInfoMain) { + if(input.type == InputTypeLong && input.key == InputKeyOk) { /* Reset the current sample to capture the next. */ reset_current_signal(app); - } else if (input.type == InputTypeShort && input.key == InputKeyOk) { + } else if(input.type == InputTypeShort && input.key == InputKeyOk) { /* Show next info page. */ privdata->cur_info_page++; } - } else if (subview == SubViewInfoSave) { - /* Save subview. */ - if (input.type == InputTypePress && input.key == InputKeyRight) { + } else if(subview == SubViewInfoSave) { + /* Save subview. */ + if(input.type == InputTypePress && input.key == InputKeyRight) { privdata->signal_display_start_row++; - } else if (input.type == InputTypePress && input.key == InputKeyLeft) { - if (privdata->signal_display_start_row != 0) - privdata->signal_display_start_row--; - } else if (input.type == InputTypeLong && input.key == InputKeyOk) - { + } else if(input.type == InputTypePress && input.key == InputKeyLeft) { + if(privdata->signal_display_start_row != 0) privdata->signal_display_start_row--; + } else if(input.type == InputTypeLong && input.key == InputKeyOk) { // We have have the buffer already allocated, in case the // user aborted with BACK a previous saving. - if (privdata->filename == NULL) - privdata->filename = malloc(SAVE_FILENAME_LEN); - set_signal_random_filename(app,privdata->filename,SAVE_FILENAME_LEN); - ui_show_keyboard(app, privdata->filename, SAVE_FILENAME_LEN, - text_input_done_callback); - } else if (input.type == InputTypeShort && input.key == InputKeyOk) { + if(privdata->filename == NULL) privdata->filename = malloc(SAVE_FILENAME_LEN); + set_signal_random_filename(app, privdata->filename, SAVE_FILENAME_LEN); + ui_show_keyboard(app, privdata->filename, SAVE_FILENAME_LEN, text_input_done_callback); + } else if(input.type == InputTypeShort && input.key == InputKeyOk) { SendSignalCtx send_state; - send_signal_init(&send_state,app); - radio_tx_signal(app,radio_tx_feed_data,&send_state); + send_signal_init(&send_state, app); + radio_tx_signal(app, radio_tx_feed_data, &send_state); notify_signal_sent(app); } } } /* Called on view exit. */ -void view_exit_info(ProtoViewApp *app) { - InfoViewPrivData *privdata = app->view_privdata; +void view_exit_info(ProtoViewApp* app) { + InfoViewPrivData* privdata = app->view_privdata; // When the user aborts the keyboard input, we are left with the // filename buffer allocated. - if (privdata->filename) free(privdata->filename); + if(privdata->filename) free(privdata->filename); } diff --git a/view_raw_signal.c b/view_raw_signal.c index 023e986f958..38354bef961 100644 --- a/view_raw_signal.c +++ b/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,31 +20,29 @@ 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; @@ -53,61 +51,63 @@ 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->msg_info->decoder->name, ColorWhite, ColorBlack); + canvas_draw_str_with_border( + canvas, 1, 61, app->msg_info->decoder->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.type == InputTypeLong) { - if (input.key == InputKeyOk) { + if(input.key == InputKeyRight) + app->signal_offset++; + else if(input.key == InputKeyLeft) + app->signal_offset--; + } else if(input.type == InputTypeLong) { + if(input.key == InputKeyOk) { /* Reset the current sample to capture the next. */ reset_current_signal(app); } - } else if (input.type == InputTypeShort) { - if (input.key == InputKeyOk) { + } else if(input.type == InputTypeShort) { + if(input.key == InputKeyOk) { app->signal_offset = 0; - adjust_raw_view_scale(app,DetectedSamples->short_pulse_dur); - } else if (input.key == InputKeyDown) { + adjust_raw_view_scale(app, DetectedSamples->short_pulse_dur); + } 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; } } } /* Adjust raw view scale depending on short pulse duration. */ -void adjust_raw_view_scale(ProtoViewApp *app, uint32_t short_pulse_dur) { - if (short_pulse_dur == 0) +void adjust_raw_view_scale(ProtoViewApp* app, uint32_t short_pulse_dur) { + if(short_pulse_dur == 0) app->us_scale = PROTOVIEW_RAW_VIEW_DEFAULT_SCALE; - else if (short_pulse_dur < 75) + else if(short_pulse_dur < 75) app->us_scale = 10; - else if (short_pulse_dur < 145) + else if(short_pulse_dur < 145) app->us_scale = 30; - else if (short_pulse_dur < 400) + else if(short_pulse_dur < 400) app->us_scale = 100; - else if (short_pulse_dur < 1000) + else if(short_pulse_dur < 1000) app->us_scale = 200; else app->us_scale = PROTOVIEW_RAW_VIEW_DEFAULT_SCALE; diff --git a/view_settings.c b/view_settings.c index 1e2dce226fa..09abf5a2aeb 100644 --- a/view_settings.c +++ b/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,42 +55,40 @@ 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; } @@ -106,9 +104,13 @@ 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);