From d93ed003fec62ec8b9dc8a6b5ffbe93a25a0ef40 Mon Sep 17 00:00:00 2001 From: Krzysztof Zdulski Date: Sun, 29 Jan 2023 06:53:35 +0100 Subject: [PATCH 1/5] Change camelCase to PascalCase in code style (#2329) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- CODING_STYLE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CODING_STYLE.md b/CODING_STYLE.md index 6c7d6d792..c62009eff 100644 --- a/CODING_STYLE.md +++ b/CODING_STYLE.md @@ -52,7 +52,7 @@ Almost everything in flipper firmware is built around this concept. ## Naming -### Type names are CamelCase +### Type names are PascalCase Examples: From f5fe0ff694fd5c51472d3430565a156a66a05c15 Mon Sep 17 00:00:00 2001 From: Petr Portnov | PROgrm_JARvis Date: Sun, 29 Jan 2023 13:12:24 +0300 Subject: [PATCH 2/5] Furi: make `furi_is_irq_context` public (#2276) * Furi: make `furi_is_irq_context` public * Furi: proper name and documentation for furi_kernel_is_irq_or_masked. * Target: bump symbol table version * Furi: proper doxygen context for warnings Co-authored-by: Aleksandr Kutuzov --- firmware/targets/f7/api_symbols.csv | 1 + furi/core/common_defines.h | 24 ------------------- furi/core/kernel.c | 36 ++++++++++++++++++++++++----- furi/core/kernel.h | 29 ++++++++++++++++++++++- furi/core/message_queue.c | 16 ++++++------- furi/core/timer.c | 11 ++++----- furi/furi.c | 4 ++-- 7 files changed, 74 insertions(+), 47 deletions(-) diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 0e027a6a2..a75e88bad 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1389,6 +1389,7 @@ Function,+,furi_hal_version_uid_size,size_t, Function,-,furi_hal_vibro_init,void, Function,+,furi_hal_vibro_on,void,_Bool Function,-,furi_init,void, +Function,+,furi_kernel_is_irq_or_masked,_Bool, Function,+,furi_kernel_get_tick_frequency,uint32_t, Function,+,furi_kernel_lock,int32_t, Function,+,furi_kernel_restore_lock,int32_t,int32_t diff --git a/furi/core/common_defines.h b/furi/core/common_defines.h index c7acf95b4..1ec847d45 100644 --- a/furi/core/common_defines.h +++ b/furi/core/common_defines.h @@ -52,30 +52,6 @@ extern "C" { } #endif -static inline bool furi_is_irq_context() { - bool irq = false; - BaseType_t state; - - if(FURI_IS_IRQ_MODE()) { - /* Called from interrupt context */ - irq = true; - } else { - /* Get FreeRTOS scheduler state */ - state = xTaskGetSchedulerState(); - - if(state != taskSCHEDULER_NOT_STARTED) { - /* Scheduler was started */ - if(FURI_IS_IRQ_MASKED()) { - /* Interrupts are masked */ - irq = true; - } - } - } - - /* Return context, 0: thread context, 1: IRQ context */ - return (irq); -} - #ifdef __cplusplus } #endif diff --git a/furi/core/kernel.c b/furi/core/kernel.c index 73d2012b4..7928ad11c 100644 --- a/furi/core/kernel.c +++ b/furi/core/kernel.c @@ -7,8 +7,32 @@ #include CMSIS_device_header +bool furi_kernel_is_irq_or_masked() { + bool irq = false; + BaseType_t state; + + if(FURI_IS_IRQ_MODE()) { + /* Called from interrupt context */ + irq = true; + } else { + /* Get FreeRTOS scheduler state */ + state = xTaskGetSchedulerState(); + + if(state != taskSCHEDULER_NOT_STARTED) { + /* Scheduler was started */ + if(FURI_IS_IRQ_MASKED()) { + /* Interrupts are masked */ + irq = true; + } + } + } + + /* Return context, 0: thread context, 1: IRQ context */ + return (irq); +} + int32_t furi_kernel_lock() { - furi_assert(!furi_is_irq_context()); + furi_assert(!furi_kernel_is_irq_or_masked()); int32_t lock; @@ -33,7 +57,7 @@ int32_t furi_kernel_lock() { } int32_t furi_kernel_unlock() { - furi_assert(!furi_is_irq_context()); + furi_assert(!furi_kernel_is_irq_or_masked()); int32_t lock; @@ -63,7 +87,7 @@ int32_t furi_kernel_unlock() { } int32_t furi_kernel_restore_lock(int32_t lock) { - furi_assert(!furi_is_irq_context()); + furi_assert(!furi_kernel_is_irq_or_masked()); switch(xTaskGetSchedulerState()) { case taskSCHEDULER_SUSPENDED: @@ -99,7 +123,7 @@ uint32_t furi_kernel_get_tick_frequency() { } void furi_delay_tick(uint32_t ticks) { - furi_assert(!furi_is_irq_context()); + furi_assert(!furi_kernel_is_irq_or_masked()); if(ticks == 0U) { taskYIELD(); } else { @@ -108,7 +132,7 @@ void furi_delay_tick(uint32_t ticks) { } FuriStatus furi_delay_until_tick(uint32_t tick) { - furi_assert(!furi_is_irq_context()); + furi_assert(!furi_kernel_is_irq_or_masked()); TickType_t tcnt, delay; FuriStatus stat; @@ -137,7 +161,7 @@ FuriStatus furi_delay_until_tick(uint32_t tick) { uint32_t furi_get_tick() { TickType_t ticks; - if(furi_is_irq_context() != 0U) { + if(furi_kernel_is_irq_or_masked() != 0U) { ticks = xTaskGetTickCountFromISR(); } else { ticks = xTaskGetTickCount(); diff --git a/furi/core/kernel.h b/furi/core/kernel.h index f30f109bb..371f76c1f 100644 --- a/furi/core/kernel.h +++ b/furi/core/kernel.h @@ -10,19 +10,42 @@ extern "C" { #endif +/** Check if CPU is in IRQ or kernel running and IRQ is masked + * + * Originally this primitive was born as a workaround for FreeRTOS kernel primitives shenanigans with PRIMASK. + * + * Meaningful use cases are: + * + * - When kernel is started and you want to ensure that you are not in IRQ or IRQ is not masked(like in critical section) + * - When kernel is not started and you want to make sure that you are not in IRQ mode, ignoring PRIMASK. + * + * As you can see there will be edge case when kernel is not started and PRIMASK is not 0 that may cause some funky behavior. + * Most likely it will happen after kernel primitives being used, but control not yet passed to kernel. + * It's up to you to figure out if it is safe for your code or not. + * + * @return true if CPU is in IRQ or kernel running and IRQ is masked + */ +bool furi_kernel_is_irq_or_masked(); + /** Lock kernel, pause process scheduling + * + * @warning This should never be called in interrupt request context. * * @return previous lock state(0 - unlocked, 1 - locked) */ int32_t furi_kernel_lock(); /** Unlock kernel, resume process scheduling + * + * @warning This should never be called in interrupt request context. * * @return previous lock state(0 - unlocked, 1 - locked) */ int32_t furi_kernel_unlock(); /** Restore kernel lock state + * + * @warning This should never be called in interrupt request context. * * @param[in] lock The lock state * @@ -37,7 +60,9 @@ int32_t furi_kernel_restore_lock(int32_t lock); uint32_t furi_kernel_get_tick_frequency(); /** Delay execution - * + * + * @warning This should never be called in interrupt request context. + * * Also keep in mind delay is aliased to scheduler timer intervals. * * @param[in] ticks The ticks count to pause @@ -45,6 +70,8 @@ uint32_t furi_kernel_get_tick_frequency(); void furi_delay_tick(uint32_t ticks); /** Delay until tick + * + * @warning This should never be called in interrupt request context. * * @param[in] ticks The tick until which kerel should delay task execution * diff --git a/furi/core/message_queue.c b/furi/core/message_queue.c index 9a41f8775..ddf56f006 100644 --- a/furi/core/message_queue.c +++ b/furi/core/message_queue.c @@ -1,11 +1,11 @@ +#include "kernel.h" #include "message_queue.h" -#include "core/common_defines.h" #include #include #include "check.h" FuriMessageQueue* furi_message_queue_alloc(uint32_t msg_count, uint32_t msg_size) { - furi_assert((furi_is_irq_context() == 0U) && (msg_count > 0U) && (msg_size > 0U)); + furi_assert((furi_kernel_is_irq_or_masked() == 0U) && (msg_count > 0U) && (msg_size > 0U)); QueueHandle_t handle = xQueueCreate(msg_count, msg_size); furi_check(handle); @@ -14,7 +14,7 @@ FuriMessageQueue* furi_message_queue_alloc(uint32_t msg_count, uint32_t msg_size } void furi_message_queue_free(FuriMessageQueue* instance) { - furi_assert(furi_is_irq_context() == 0U); + furi_assert(furi_kernel_is_irq_or_masked() == 0U); furi_assert(instance); vQueueDelete((QueueHandle_t)instance); @@ -28,7 +28,7 @@ FuriStatus stat = FuriStatusOk; - if(furi_is_irq_context() != 0U) { + if(furi_kernel_is_irq_or_masked() != 0U) { if((hQueue == NULL) || (msg_ptr == NULL) || (timeout != 0U)) { stat = FuriStatusErrorParameter; } else { @@ -65,7 +65,7 @@ FuriStatus furi_message_queue_get(FuriMessageQueue* instance, void* msg_ptr, uin stat = FuriStatusOk; - if(furi_is_irq_context() != 0U) { + if(furi_kernel_is_irq_or_masked() != 0U) { if((hQueue == NULL) || (msg_ptr == NULL) || (timeout != 0U)) { stat = FuriStatusErrorParameter; } else { @@ -131,7 +131,7 @@ uint32_t furi_message_queue_get_count(FuriMessageQueue* instance) { if(hQueue == NULL) { count = 0U; - } else if(furi_is_irq_context() != 0U) { + } else if(furi_kernel_is_irq_or_masked() != 0U) { count = uxQueueMessagesWaitingFromISR(hQueue); } else { count = uxQueueMessagesWaiting(hQueue); @@ -148,7 +148,7 @@ uint32_t furi_message_queue_get_space(FuriMessageQueue* instance) { if(mq == NULL) { space = 0U; - } else if(furi_is_irq_context() != 0U) { + } else if(furi_kernel_is_irq_or_masked() != 0U) { isrm = taskENTER_CRITICAL_FROM_ISR(); /* space = pxQueue->uxLength - pxQueue->uxMessagesWaiting; */ @@ -167,7 +167,7 @@ FuriStatus furi_message_queue_reset(FuriMessageQueue* instance) { QueueHandle_t hQueue = (QueueHandle_t)instance; FuriStatus stat; - if(furi_is_irq_context() != 0U) { + if(furi_kernel_is_irq_or_masked() != 0U) { stat = FuriStatusErrorISR; } else if(hQueue == NULL) { stat = FuriStatusErrorParameter; diff --git a/furi/core/timer.c b/furi/core/timer.c index be7efebe2..4b6ccecba 100644 --- a/furi/core/timer.c +++ b/furi/core/timer.c @@ -3,7 +3,6 @@ #include "memmgr.h" #include "kernel.h" -#include "core/common_defines.h" #include #include @@ -27,7 +26,7 @@ static void TimerCallback(TimerHandle_t hTimer) { } FuriTimer* furi_timer_alloc(FuriTimerCallback func, FuriTimerType type, void* context) { - furi_assert((furi_is_irq_context() == 0U) && (func != NULL)); + furi_assert((furi_kernel_is_irq_or_masked() == 0U) && (func != NULL)); TimerHandle_t hTimer; TimerCallback_t* callb; @@ -60,7 +59,7 @@ FuriTimer* furi_timer_alloc(FuriTimerCallback func, FuriTimerType type, void* co } void furi_timer_free(FuriTimer* instance) { - furi_assert(!furi_is_irq_context()); + furi_assert(!furi_kernel_is_irq_or_masked()); furi_assert(instance); TimerHandle_t hTimer = (TimerHandle_t)instance; @@ -82,7 +81,7 @@ void furi_timer_free(FuriTimer* instance) { } FuriStatus furi_timer_start(FuriTimer* instance, uint32_t ticks) { - furi_assert(!furi_is_irq_context()); + furi_assert(!furi_kernel_is_irq_or_masked()); furi_assert(instance); TimerHandle_t hTimer = (TimerHandle_t)instance; @@ -99,7 +98,7 @@ FuriStatus furi_timer_start(FuriTimer* instance, uint32_t ticks) { } FuriStatus furi_timer_stop(FuriTimer* instance) { - furi_assert(!furi_is_irq_context()); + furi_assert(!furi_kernel_is_irq_or_masked()); furi_assert(instance); TimerHandle_t hTimer = (TimerHandle_t)instance; @@ -117,7 +116,7 @@ FuriStatus furi_timer_stop(FuriTimer* instance) { } uint32_t furi_timer_is_running(FuriTimer* instance) { - furi_assert(!furi_is_irq_context()); + furi_assert(!furi_kernel_is_irq_or_masked()); furi_assert(instance); TimerHandle_t hTimer = (TimerHandle_t)instance; diff --git a/furi/furi.c b/furi/furi.c index a616bce63..cc0e3f4f1 100644 --- a/furi/furi.c +++ b/furi/furi.c @@ -3,7 +3,7 @@ #include "queue.h" void furi_init() { - furi_assert(!furi_is_irq_context()); + furi_assert(!furi_kernel_is_irq_or_masked()); furi_assert(xTaskGetSchedulerState() == taskSCHEDULER_NOT_STARTED); furi_log_init(); @@ -11,7 +11,7 @@ void furi_init() { } void furi_run() { - furi_assert(!furi_is_irq_context()); + furi_assert(!furi_kernel_is_irq_or_masked()); furi_assert(xTaskGetSchedulerState() == taskSCHEDULER_NOT_STARTED); #if(__ARM_ARCH_7A__ == 0U) From 55054fc1a7467aff87f7250178ecd8efd9e7a738 Mon Sep 17 00:00:00 2001 From: DEXV <89728480+DXVVAY@users.noreply.github.com> Date: Sun, 29 Jan 2023 11:55:04 +0100 Subject: [PATCH 3/5] Assets: correct MicroSD card pinout in service animations (#2323) * New Micro Sd-Card pinout: fixing the Micro Sd-Card pinout for the blocking an internal sd card animations * Updating the sd card pinout * Updating magnifying glass --- .../blocking/L0_NoDb_128x51/frame_0.png | Bin 1398 -> 1424 bytes .../blocking/L0_NoDb_128x51/frame_1.png | Bin 1403 -> 1425 bytes .../blocking/L0_NoDb_128x51/frame_2.png | Bin 1403 -> 1423 bytes .../blocking/L0_NoDb_128x51/frame_3.png | Bin 1401 -> 1420 bytes .../blocking/L0_SdBad_128x51/frame_0.png | Bin 1552 -> 1370 bytes .../blocking/L0_SdBad_128x51/frame_1.png | Bin 1586 -> 1387 bytes .../blocking/L0_SdOk_128x51/frame_0.png | Bin 1549 -> 1387 bytes .../blocking/L0_SdOk_128x51/frame_1.png | Bin 1559 -> 1395 bytes .../blocking/L0_SdOk_128x51/frame_2.png | Bin 1578 -> 1413 bytes .../blocking/L0_SdOk_128x51/frame_3.png | Bin 1567 -> 1403 bytes .../blocking/L0_Url_128x51/frame_0.png | Bin 1358 -> 1380 bytes .../blocking/L0_Url_128x51/frame_1.png | Bin 1964 -> 2046 bytes .../blocking/L0_Url_128x51/frame_2.png | Bin 1956 -> 2044 bytes .../blocking/L0_Url_128x51/frame_3.png | Bin 1955 -> 2045 bytes .../internal/L1_NoSd_128x49/frame_0.png | Bin 1382 -> 1411 bytes .../internal/L1_NoSd_128x49/frame_1.png | Bin 1376 -> 1410 bytes .../internal/L1_NoSd_128x49/frame_2.png | Bin 1381 -> 1416 bytes .../internal/L1_NoSd_128x49/frame_3.png | Bin 1378 -> 1415 bytes .../internal/L1_NoSd_128x49/frame_4.png | Bin 1372 -> 1401 bytes .../internal/L1_NoSd_128x49/frame_5.png | Bin 1377 -> 1412 bytes 20 files changed, 0 insertions(+), 0 deletions(-) diff --git a/assets/dolphin/blocking/L0_NoDb_128x51/frame_0.png b/assets/dolphin/blocking/L0_NoDb_128x51/frame_0.png index e82c6f2e9c6a2880b0b983e1a80497daac351704..759007623af4e16c1bbdd167750e2ee47152ff88 100644 GIT binary patch delta 602 zcmeyyHGzAAL?8<@0|UcaKUrlU#aJBV?!>U}oXkrghb7(7*O7r?V?XzwL{=c5Ex;$l z_5c6>KxXKn%XL7Cv%n*=n1O-s00=W?UF+mzU|^m((aWESmuupzJZ4@lg~=R@POLy$ zL1D8O;{rx@FjGNc@++o=^$PlS5U!1WQA(PXOKNd)QD#9&W_})693mRz;s)f|=vP=( zD9qCSiJ- zyW#1BPdSXM*p9d|D~2C5xiCr9gyl|;y#gb9`nMI)?=+sNMr(WO`!U+tTj@JUKXd43 z{Gi!Y|7GU6jt?Hb4EH3G9nM+%8b4F2)?(by@44lvs=}@Yjs1)%+zm__#v8t`=4hJ9 zc9Zq>j%)|lW|jln)juy-^<5+&DE+5E13$y-z#EJSoEb6Uu?I`}&M{_8KU?Bjaj>zN z_m_3M_?=#n*I#lSJ}P{gZNT_o%Z7!FOEkC{b@}3-$_-7%~V00001JzVIRd000?uMObuGZ)S9NVRB^vL1b@YWgtmyVP|Dh zWnpA_ami&o000Aik!eo>Ad$LmlPdv2vvL8T0h1X6p?@zvAUrQ}WM(=fFD zZ*D#yJTG!&W;#+tMm``sFL*k5ZE$U6bYVUqJU@7FVPk7$bRcDJWIZBsB0oM;Hv@wJ z000J1OjJbx00960|E1ZV-2eaqd`Uz>RCwC#n28R9APk28zW+;aS)`X$px}`Ow|GRb z-+KKr(0{`l7Qkx&aJpR&06avX0Dc$o_ay-EIfH{^TdLtk0MHzOa;2wyclM-QZUa%U z*?=0=nS|IrRQv%)YIbt+Grk{?0pKUq05-pwu~PtAJs>|`oG}Yvp9554D$(WetC@Vy zl=hn%(E?Zi*4PYyQYWBlD+Vc3`Y-|@?Vzd!+JC+-=6(R|N}K^;+JzZp?UQEd1aMuB zyA)9*6ao<1x2G8Z69Ut}&j2tD03M+30TOeLpeqM#2&Aq=415ka08T-=sSkuczY~BF zJj!zbgl7PTt6&-cjRQ2wo7V!{{qg|y8K7e=P_KbWfR+id7AQ5evAGr?h>m+N$N=DA zS#E|JK=fvk0zmAGomvy>1UV0sr5x0X(X4fA078%qK`4Pm?pgpa2tXFVER5R#FcqLX z2QbtJ*n+H*(N_h70O>yP9{^oiFdu-LpNO{Mm#Ojkvw8v0M}PqUpUe`n6H&y000000 LNkvXXu0mjfS)JjK diff --git a/assets/dolphin/blocking/L0_NoDb_128x51/frame_1.png b/assets/dolphin/blocking/L0_NoDb_128x51/frame_1.png index 58dab74d4a618d0ccc4674be0754b2f11a28e111..c9810b61e34bfc61f4b62a26225afaab6a05490d 100644 GIT binary patch delta 603 zcmey(HIaLQL?8<@0|UcaKUrlU#aJBV?!>U}oXkrghb7(7*O7r?V?XzwL{=c5Ex;$l z_5c6>KxXKn%XL7Cv%n*=n1O-s00=W?UF+mzU|^m((aWESmuupzJZ4@lg~=R@POLy$ zL1D8O;{rx@FjGNc@++o=^$PlS5U!1WQA(PXOKNd)QD#9&W_})693mRz;s)f|=vP=( zw#+_v4VT99ldTO;^Yd!VcHOU+&u6IPQh4!Rae=@sf5r)id;HRkUwNA| zYDhC^zKm_2#HKbe>{OA5@tck2OBoCgo|as@q*SYF^1Har;g*yxYf5 zObhI0*hDuhb6mKJQ9RhubiypFDNK2{dZ`~99 z7&mBk)t@k)DL7BLo%usHS4X9K+^-mw;-ie)W>v&TG1{gf=iXSmvmTNHLopF6}g0)F({~@jmx34pL>g6#1O7XGdSBqbDyZ&o% b*eUK(MH8nPTONl3(*uL2tDnm{r-UW|yvo^{ delta 578 zcmV-I0=@l_3;PO?7%~V00001JzVIRd000?uMObuGZ)S9NVRB^vL1b@YWgtmyVP|Dh zWnpA_ami&o000Aik!eo>Ad$LmlPdv2vvL8T0h1X6p?@zvAUrQ}WM(=fFD zZ*D#yJTG!&W;#+tMm``sFL*k5ZE$U6bYVUqJU@7FVPk7$bRcDJWIZBsB0oOC5CQ4{ z000J1OjJbx00960|E1ZV-2eaqfk{L`RCwC#m9uK7aBSSEh13Cm;RV)TJ2MmBnK-$s=sy^Qd zAP}f^+ze0@;x>TEDp&?U^8ikHE484a3ITxr4A7kxh!?>SfM-HW3xpa9S3V3-jgNXS zh;sm-u`EL$pnNk40if=SLahmXf|v)=QVwFpsMflD0JuY14M9}`lic|LFa&U>cv7h@-A}f&37T^=& z`v3obAT#vPO>_%)r2R0E8K{u61%UFfh-Y=;hDE%QbOU9y2eO!ekCcCsrV> zps?ACaRDPcn5m#J`4!W`dIfzu2-ilxC?(CxCAB!YD6^m>Gd~Y34iOD7gEebrY#pM&; z)Nh_Vy+fg+g4D?$Q&(m^P|J;PVZPhH(v9(An@T*T|+p7|IW>`T;td(YD#u8JhAX$_#oi=Y^Iyb z6A#q}m3n*j?+rcHZ-qCW&{Jiw(@K8JDG=;%MW5kEf(=6fgB7ce*nP#sC(U!3eDBmc zwDU75JQKUu)x1cyaJej#)6sN>8m*Qgr1$WJiwROa&G5npZC0?L% zgzZ6%x$rZF5NYXk4CjxnIKxnL%Ao%W!+*IQi_;lye>xJe-Sd4dgTe~DWM4fw#Vk% delta 578 zcmV-I0=@l@3;PO?7%~V00001JzVIRd000?uMObuGZ)S9NVRB^vL1b@YWgtmyVP|Dh zWnpA_ami&o000Aik!eo>Ad$LmlPdv2vvL8T0h1X6p?@zvAUrQ}WM(=fFD zZ*D#yJTG!&W;#+tMm``sFL*k5ZE$U6bYVUqJU@7FVPk7$bRcDJWIZBsB0oMwV421M z000J1OjJbx00960|E1ZV-2eaqfk{L`RCwC#n2QdBAPhxs|Np1A7@#kdg2-ILw)lu> z4{hnS1AqPagAMQ+09<~a2LOIVAOMb&`tKzGusMU1eJs_m5CAj>AfL%wz8igzPt!nT z>|#KS>P$kUFJ*tgkg5$I9LD{C5rFcf7C`cw8ao9b>H(fSJ7X4r&jHFY<>;31i#y9T zwY1+|YxxTRy0lh_w0q7&Z07-ZfvS0hb Qf&c&j07*qoM6N<$f^|vo?*IS* diff --git a/assets/dolphin/blocking/L0_NoDb_128x51/frame_3.png b/assets/dolphin/blocking/L0_NoDb_128x51/frame_3.png index 789dc8822e8442ddd49ea503b9ac8d109475e1ef..b48aef97839e4766b73c9630ae25c4fc2abb3d3a 100644 GIT binary patch delta 598 zcmey#)x$kOB9Midfq`MIpR6*FVk{1FcVbv~PUa<$!;&U>cv7h@-A}f&37T^=& z`v3obAT#vPO>_%)r2R0E8K{u61%UFfh-Y=;hDE%QbOU9y2eO!ekCcCsrV> zps?ACaRDPcn5m#J`4!W`dIfzu2-ilxC?(CxCAB!YD6^m>Gd~Y34iOD4`zo``_31b32qV@>rWQ9?@9NxS(A+JlrtPTc2?O zYs0iRdCiyD5=+-k`+6bm$K#AwOjktKhi}_-b>^F8cNaffU(&9?&yYJcV=v>a%#f-4 z43{6gi)0d!7m&9TDAJ$7za_0{SHtvg%zKzBPR+fl^U3JI|6qrS-W1%uJMFH1XI%QP7`_(^63JMb_Du4nvkZU$okLlmoyh3c1@3C8n+Z@cV{t0*s(kaZm5ZOoNS`1{exrSNu33E&5X58#IG@YS5MbV;d#e+ zYWqwB#s#--6fjnprBwozw^c4-`cWlNC&!p`TKK-&-_Wm86D}J`JKa<{xoH(^ON(Ly zlftJJ3My+De!OdTK2#%C)BdbXtgGd$#g0`!q!vu(KHXp@X}zDl+5YNN|G(PGznGtL WnK&{0-W3K+3JjjEelF{r5}E+=mf!XO delta 576 zcmV-G0>Ax?3;7C=7%~V00001JzVIRd000?uMObuGZ)S9NVRB^vL1b@YWgtmyVP|Dh zWnpA_ami&o000Aik!eo>Ad$LmlPdv2vvL8T0h1X6p?@zvAUrQ}WM(=fFD zZ*D#yJTG!&W;#+tMm``sFL*k5ZE$U6bYVUqJU@7FVPk7$bRcDJWIZBsB0oN5K?$w^ z000J1OjJbx00960|E1ZV-2eaqe@R3^RCwC#n2QdBAPhxs|Np1AEI=O=t@6+%xXnoh z_E1W%9e?QI0~_Eq060A60{{;Z2!LP1f4>9(HfGTHj)fW)0)WN<P3?TVU^&J8b^#D(voiPfajsePR%Bx$JUyLlj zsjdB{M6>}mz<7YXPe7-s7&HNZ)4{10X#1W^;(wF2Tf6{3r3=+b*-y3&LVVIP%atre zNJ}!Ebpog*Q2DzJK*BI}0>AJ3020R{j{A`-F)HA1og O0000U}oXkrghb7(7*O7r?V?XzwL{=c5Ex;$l z_5c6>KxXKn%XL7Cv%n*=n1O-s5C}7hYIrp;Ffh-Y=;hDE%QbOU9y2eO!ekCcCsrV> zps?ACaRDPcn5m#J`4!W`dIfzu2-ilxC?(CxCAB!YD6^m>Gd~Y34iODOmSOyv4Q%M`>aOn5*dHIgX+W?QkAwwy`O3H@L=4^)oo@i zzKS~6l>_CIVl`i!?wmJiOC^P?u>qNI^UZB z_TvrHHgG$v%Vlz4ePO4tWOtCCvx9krSD@q};RpLxJ`q-W(!~5ma>3c%THX2ywIc{w;!)foz+D=s%LwzPi+6`4nyAd$LmlPdv2vvL8T0h1X6p?@zvAUrQ}WM(=fFD zZ*D#yJTG!&W;#+tMm``sFL*k5ZE$U6bYVUqJU@7FVPk7$bRcDJWIZBsB0oMEGI*l^ z00JmUL_t(|+U#2Gj>I4cJR9Htm3y1poM}J-KWNnsKQ`Uex^+fiU=8Pa{=lBI0}y}! z5CD75Vt?sI2*K$mk#%$r@I&8w^4&tv`DX(!U<>2`?jmhYlb))5Jp3E^+V8u3hB)WG z_`GQHnEqu~IUo7y|>!DM4zFZj0QpJW%S`&z|Xm@w-`6)LFY#hn+m z91!%sUrXEYdY3;=23%*$a<39V-iJ@f{brdE^na{itqnLm?}E<;bG1f-Ask9JZNT@Wp1^M2ZR$(1-DI zOn>O`sBI$217y*rYG>oHr9+V2IYuU7l4s<8S^&>QfFSb+{#`Xy+2txdVy(au1ipM^VL9lB zuOf>~MK7qi*aP~)wmxdnm(ToM&m(=2zagi3NQd~FXriq;F=f!015yA LNkvXXu0mjfqA^8s diff --git a/assets/dolphin/blocking/L0_SdBad_128x51/frame_1.png b/assets/dolphin/blocking/L0_SdBad_128x51/frame_1.png index bd0f2b9338264ef39ffcf378f9e6017edc05286e..147561f0a40ab1c8ac7134844843d659783c2109 100644 GIT binary patch delta 565 zcmdnQ^O|ddL?8<@0|UcaKUrlU#aJBV?!>U}oXkrghb7(7*O7r?V?XzwL{=c5Ex;$l z_5c6>KxXKn%XL7Cv%n*=n1O-s5C}7hYIrp;Ffh-Y=;hDE%QbOU9y2eO!ekCcCsrV> zps?ACaRDPcn5m#J`4!W`dIfzu2-ilxC?(CxCAB!YD6^m>Gd~Y34iOD7K5f@kuUvl$d`dceBj?BVltoMHa~b5`YnL%L=JGReh8H(%VBLSrcGDk* zDh8IljMrOF)S5HQkYrL4lStTm?Zq_NE6?Wsy({qME`!`B%ibRQd*1h(UmW3g;5s-j zwBeYM8RI0z8O%!}WF4;8hz8XgvKKr3id86X=sUJw5tmEpqsDVK~`H#}*0;5XZpp|#)pPTYlQP6`fNS=RV0Viu5-JENbR%=aaL z{dmKy4crf&G9D1!z{{X*(B_oIb~;9&;^;f3d9JGO1rk`6@$@hR+3qf43~ZEQkJuQ) ze#5<1_E8QGV`Z@>qt+~Ap~V6_%oR3HTRMl)aN!pghG{0P=C&5In!jnSVc@D}yl|#< pYS$IHNJgvo^ZzgZ+bnj7-E@s%@+~b+J77#Rc)I$ztaD0e0sw!z%fSEu delta 763 zcmVAd$LmlPdv2vvL8T0h1X6p?@zvAUrQ}WM(=fFD zZ*D#yJTG!&W;#+tMm``sFL*k5ZE$U6bYVUqJU@7FVPk7$bRcDJWIZBsB0oO0n2LV@ z00K%$L_t(|+U#1{j>8}fED`_z%h{^cqR2M33AhPl{IE(D!ZCx-b=vp+1-VW(KmYOA6dF+Fn_yp6!bpi7k6@Caxsq&{Mz!rl7BzSD_O;&bERA51j7THWi=qk+FW}Z9! zP3kc8qR=tx_6Mw+Gb$kQR&6Ecuz7t(kx?FH6VW%pRbX`vTTCtZVZ}0$5d|seLwGu- zRDXEPGLfbMdeCOZ&dy;|n;^e(jC8`p&*=5YRXG^WR&LckT~VCFmVwwT{+MMVT5x|( z3W`Jw!U#a`!tdo|G0yy79gvM8KxtMeuO56Bl`m^5&hSp zeSxJOMQCdMCEXA9jO)^RdCfIJB9Midfq`MIpR6*FVk{1FcVbv~PUa<$!;&U>cv7h@-A}f&37T^=& z`v3obAT#vPO>_%)r2R2!t6$HM|-a7?@{H^zvuo<(fDvkC~TCVKN7!6DyEb zP}uCnxPXxz%v4aA{EBH|y@I|SglnT;l#*uUl3JWxlvz-cnV$z1hlmEbxB+=K`W03c zxdpkYC5a%OeMLcHa&~HoLTX-$tx}Paz1{yC;&wpydwRM!hIkx*I<0%%VFix1pZ6K= z{ZBo9G%EM%#xl>%#UA3~UGq=4de^%stc|H>DEZvFp6SH=YzDTg&u=quY|L`_$ekEk zD0CpNmf^y2hOf>MXH8twQnj{D=5c7wI-OV@%eZcp+h>zD_T+{;tR{K;m@9Y^m>XOD z41f5k{h*gK?#Oig delta 726 zcmV;{0xA9L3XKeq7%~U~0002c0+&Ys000?uMObuGZ)S9NVRB^vL1b@YWgtmyVP|Dh zWnpA_ami&o000Aik!eo>Ad$LmlPdv2vvL8T0h1X6p?@zvAUrQ}WM(=fFD zZ*D#yJTG!&W;#+tMm``sFL*k5ZE$U6bYVUqJU@7FVPk7$bRcDJWIZBsB0oNTQ!`Ki z00JdRL_t(|+U#1}vV$NDEDrzw%bYXq)JcKF5Qr#lo={sZ*<4pcO6dieCmU1%0001F zo<#0J2!Fw8No1X45Af5z_vEXEp!Al31IU6oAbf>-Sk=!s=QBGcU5ielpD-?<{oS|d z_#Ek9?kQCeb&@}6t6LKi9u`&&?{P`p4J?11SSB_E36|c}Slt&w zC+%}O*_z^vzBNaNFeYK;M@}3JpGT`|zkm7>syud~A_OO0*zkpqP|Lue57)70LkJ*( zu9XU}oXkrghb7(7*O7r?V?XzwL{=c5Ex;$l z_5c6>KxXKn%XL7Cv%n*=n1O-s5C}7hYIrp;Ffh-Y=;hDE%QbOU9y2eO!ekCcCsrV> zps?ACaRDPcn5m#J`4!W`dIfzu2-ilxC?(CxCAB!YD6^m>Gd~Y34iODB1mW<1j6)WVvJ9oPPxyQ!48jGOlskvre!-&%m?l{BlNtq^k{E}{ z?x>7senJa+~jX3G0qkQoWZYdcR2>Cs}-xt=ljs?3_^!8hfzIzx&1YFU5FJ^T-N zN?o)M)q5-R__NnM>H7YwdzSvx_g7t9tG^uLXoygq#8Akh|3d75X}c!dM$5~b2N)lS zHdL?&^g3ELovr54IKJTZ!A%FQUo>Nw&0u%#lZb*egUi2**Brxl)t>(A^lDvxxt;8b zuTJN$pN`RbT4}zHVcB;_W(%oTOcA>#9(d~aAi=aDI!E1x;YV~eb3*gY+Vjn8C3rS( y;PLpx>abm0Z~d{C(_eiGOBN5?{>$F!3-e7Dv!zz0+0TJ7&fw|l=d#Wzp$P!L_tz)@ delta 736 zcmV<60w4YJ3YQF!7%~U~0002c0+&Ys000?uMObuGZ)S9NVRB^vL1b@YWgtmyVP|Dh zWnpA_ami&o000Aik!eo>Ad$LmlPdv2vvL8T0h1X6p?@zvAUrQ}WM(=fFD zZ*D#yJTG!&W;#+tMm``sFL*k5ZE$U6bYVUqJU@7FVPk7$bRcDJWIZBsB0oNGb`Hz{ z00J*bL_t(|+U#1}vV$NDEDrzw%bYXq)JcKF5Qr#lo={sZ*<4pcO6dieCmU1%0001F zo<#0J2!Fw8No1X45Af5z_vEXEp!Al31IU6oAbf>-Sk=!s=QBGcU5ielpD-?<{oS|d z_#Ek9?kQCeb&cR(W=_7euOHIU8o4bNf$PJ;Um;C zFzCZ|?AZ_kh@fjF26!p&qJ6!M0)X*_ja>KxfRiwA&jC+Xbf(<$&7HoD004F_fzx&U>cv7h@-A}f&36W|l# z`v3obAoK6vzyJRI`{g134#?pw@Q5sCVBk9h!i=ICUJVQk%rhr?`7`lyO`Mg-%*&-P znS;@Z6-X;6Z1!SYz{n0}Dkw~T#k8Y^rpl>0b=AFvA)PXxA4=E<|EgDRR_$kXFpuBBrLg}RLqgW| z-HbXcTMdrtvt4QNWVln!zF<0|RJ+67lI}C7eO{>>Gx)C3J^rf z4qTtP9#|&q7Ic20$^L%jk@xHh_Iu23{9=sgyL!XtS)00bPLzqL9s3KnBK1xYhoApg z5@OG09p64Dmi+Y5p9;U(`@Eru??RgX)yDJP%WBvYHr``Yu+?Eava}&o zw;+%?o}q1*tJV^3hx$*S#143HFODdGAEY^dbs<~9Eq*Hp(JOog9#(x{Wi9gq*LHum0&Vv8-3GiKkC_`5ij}!PerbR8<iZ0h1pCp?@zvAUrQ}WM(=fFD zZ*D#yJTG!&W;#+tMm``sFL*k5ZE$U6bYVUqJU@7FVPk7$bRcDJWIZBsB0oM%Oi~C71l@rZ^CN`rWH(PVL%q>bkUycgPaP-qu>$(mxMhY~CNP0@yp6uo9o!5R!k$u$X4#%CD7S(J% z`hR87A$*qJ`BIl#-kgE;&9&sq$)Y z|G5W~8GX5!ph=A-a`6;g(D+Wcbar4mKmpbmnqCI_M|(h4$$^JBS^*{8K;O3ts`hB| z5%#SK2@jJ}HDXrmrZjxzp<@rr#D*Y&jejulQ1>-|C-gZ?3|x?@HAhNlOu~8wO6U(} zMZfwdRC(+|MF>thvEd7ULM;P>K3vD14IzLCSSxjaLwOhU^)?Cxj4y2D!XFAaNC)mY z;K@LjOmG&9^lbzb(9R`rh?|-?grJKxVi3O~yIL|K1hKLH7&dfiNZcO#xN@v_og_2h lG!Z!f3V=WXkCi_G1^|H}F1Di~quBre002ovPDHLkV1k$oN;v=k diff --git a/assets/dolphin/blocking/L0_SdOk_128x51/frame_3.png b/assets/dolphin/blocking/L0_SdOk_128x51/frame_3.png index 614ef9c5aeea94fc93a47ccf41b0ba8b4c8ee7ee..1e52f1513742e3b985d00385f5e153ccc3937edf 100644 GIT binary patch delta 581 zcmbQw^P6jeL?8<@0|UcaKUrlU#aJBV?!>U}oXkrghb7(7*O7r?V?XzwL{=c5Ex;$l z_5c6>KxXKn%XL7Cv%n*=n1O-s5C}7hYIrp;Ffh-Y=;hDE%QbOU9y2eO!ekCcCsrV> zps?ACaRDPcn5m#J`4!W`dIfzu2-ilxC?(CxCAB!YD6^m>Gd~Y34iOD9odm%?2E0G_Ydu}l%9{f|7{Pwvlh}5fR*!A_)YKG3&QVkQz_DVB|&SDFybIs7w zW0JVW-mr&fgT<7zH1{WA>x`^p86$%3%5J;PE3hif#QvDjL5T(BjGAZC8%{9(QCo1z zneSe3=YRPFe?H7Q@SJhs=e!&(^T*Be_O0O&ox}V_;o?>ot_APx858d9DSLE6u(lx~ zLLp2ruwLqD0l&h#oF6Op2Yuf9Y4NNjS=-}gFb0IJRCBoLBvQhkV10N>^P@SNm^Uz5 zFnX|Mlr(ISxuW~y_ld?QG8@7i#1C##V6dA~$o*jBJw^lD7^aLZOzXcfWtl(VOt`UK zM*LDi?fXjpge%;OBlzD3Y0fWy%=Mr|uB2h@LcWHjoh;W}W*#{7ib31BkXd2hEpvvz zl0PXd+lvfrTT>k-@iVmTE%;$F?_Y3inJItb)vND2PROSwnAsjIw2T7AJcFmJpUXO@ GgeCxpA=_&J delta 744 zcmVAd$LmlPdv2vvL8T0h1X6p?@zvAUrQ}WM(=fFD zZ*D#yJTG!&W;#+tMm``sFL*k5ZE$U6bYVUqJU@7FVPk7$bRcDJWIZBsB0oN1o5w={ z00K8jL_t(|+U#1{vV$NDEe`+x%icTflu3cautX3}p0HY$ll`bW&+`QtCkG&a0-ylM zIPu&K=YO2hlE^yO9$<&Q_2jFCpz|#q2apAGfcpydu&keP&R6Y}bS*lGenP(h`oXv8 z_#SCrZkH;Ex=7?Sk^M&g6j`*!&x93=J%C%ddN3rb2hRwo(aZla{e|9YcaG15>HO=2 zAQ}f|uCKVg`4?npw(y%Ao*H0uDLk3}P6ry34}VoS_pf}}VC~E33AU?(pa2LIFs0Nd z;UUQb3ixR;if#=jDk^IB^f-;>gQ^h=&0A5S7S&$Qb%iXsWJfpoj_4~VsW8UK5(V-= z?uxnDs>@|=QF?qiD){c`cho`F7&&A)?uC0MqHD6plofEfsAlti<}6B2RZX_E)ECOt zTYrMmQG~?}39x$I^DA9~p7md>xS+mOP?kT>+&rG&~IS@Ad#1WPttv1*C8TecLK1+gQoX(W40o3zN!*ci&ij zFKPJ7myRusJsW}q3Zba6dan5!q0eD*6n_yo`qmsNp)v_8KXT&K@VT`r`q__A<*^AB zAvo#6hA(`Cng#}axb`g@LI4r4R_XvRU}oXkrghb7(7*O7r?V?XzwL{=c5Ex;$l z_5c6>KxXKn%XL7Cv%n*=n1O+BKL|5gum-CzFfg}F^zvuo<(fDvpP83SVKOJ96DyEb zP}uCvxPXxz%v4aA{FCzM>#8IXksPAvG_>R;ftI-tPJ7X{JE;hkLp>hIkx*JB>T9#em1f*nGy$ z|Eby5iuPu^8MQ*QUb|*Ap6Pz}&n8*Ht_WcUcAM?y#sVlDj4O{@87Xw70?z zOtDLMGhA>x_Tskp494r1en=$vMe+FDP5PkQ`o)_u;=vE53l?{UU9V1RXHZ@cx65m( zcr}B9!b0DUzz!>)Ejy10AK1XSV3E_}Ka%ASjxKn{;M60TAZ~VNS^|^4gZ<|J)5Tx$ zwM=`iTL0vyl!JCd7(;Kv6+hbp&(pQCUu=G`J)1|uu;I1NE?qX(h9A1G8caV0J6Scv zF|69lDzh$l*G_G5ML7ns$Q>L9|T#nOn<}vA5HgA*J2Fp};AT{lWY2k31Lj5}rDK`NEeV z%KS1?%#87D9b3V>)@+8J$ocs%KTm)4Eu{Wv&@ZO<4Ti?5S8OVQ@y_7s>gTe~DWM4f DD0JIS delta 554 zcmV+_0@eNG3eF0U7%~V00001JzVIRd000?uMObuGZ)S9NVRB^vL1b@YWgtmyVP|Dh zWnpA_ami&o000ANk!eo>Ad$LnlPm#3vvUEU0h1a7i+?XZAUrQ}WM(=fFD zZ*D#yJTG!&W;#+tMm``sFL*k5ZE$U6bYVUqJU@7FVPk7$bRcDJWIZBsB0oOw-4GW5 z000J1OjJbx00960|E1ZV-2eaqX-PyuRCwC#*nzHtAPhj!+yDRRB@Ss}1wldH+cZnm zMClQs#eX4-Hy*&B0Z`rM17MNB0JanV_XU8Y3GN4wgk1#KqG&8`9fa?Qgn04EZUxA1 z1Hv#ugWfy_{zoQ!0NsF28O;DsR36x&89oEhavFCgl--p9+btn3?(3n>E2o`FcbVTpnNNCEilzEeK&If&fCe>1s4IfYUG_? zSz8Vn!8JhkSwJg*Gb^teAnDu|fC`2H%$#opFcfg22V!MjGzJ)z2Tsm{ae%%&@NIyG sS}+qJ2QAX{WjL=IKYvy~0C@>806$X`4`zs|dH?_b07*qoM6N<$f+3vSUH||9 diff --git a/assets/dolphin/blocking/L0_Url_128x51/frame_1.png b/assets/dolphin/blocking/L0_Url_128x51/frame_1.png index 69f1fb365988f248aa238fa6a2d672fa7d8bcee1..9975ca3f042623b1cefdd5eb913a804dd185d734 100644 GIT binary patch delta 682 zcmZ3(|BruyL?8<@0|UcaKUrlU#aJBV?!>U}oXkrghb7(7*O7r?V?XzwL{=c5Ex;$l z_5c6>KxXKn%XL7Cv%n*=n1O*?7=#%aX3dcRs#2KflA_ zpXF>ll4hVPeRO3u`q)f_sB=jzPAbocRnwN7ht`GX(lx-}lyu z|AzPDq#B&{BP!Dh5S#|Hi>aZs!4%{dD^NuZ+)Mju{Sg6x+O`tx~ zx3|>Li$7o$b4%R9xPOm~HzXx&Wju0&@r&cttE{FQn6?DIihsBwpP}$|Y{yrw24@C# zhHVWYZEOtIKK$~pbZ(UNHCFH>lu6wcYG!VbSohkY=*jMu5QaF0O*JfYqQ(0d=RY+# zB7C4!%#1ODIb?15v>z|`m>=P3SS5Tru47J)KjYc#lhPe|KJU(%Da=>CSWxU>!clcV zaH^$4?pnh~DeoE1%)iIUuq=#1%;Y|NnpK&G~mH28c7U-kg}}&!{kQSN>!j zMyJiij9-~1=d&!7(YI5usmLwx^|kWIEH23}ss!?jQqrt~Qw!}Td$69Z2Px2pi2#}C ziotR&sl~}fnFS@8`FSwqK`w3}W`z|{LvCtGB9LQWQIMFNom!%hnwMg$RHS5YckBCC zYX%0!bWaz@kch)?XWZV@tiamQiU zxS;ARdqX=1%Yie0YZx6;ntn5O+_7UxNb8<^RD8SI!$%XZ-nZKv&G=?J<24f{i@lcY z|J>DU7VDGYvW8teSGB_FhEI#%ayX={<85eeeZw?iRzCX!r-op`3(8l8Ef1L1J}425VZCsheING% sNA5JH8;!2B{MtTW`SEx2FV+qGb}NPXFA6B10HzKGPgg&ebxsLQ04xUSEdT%j diff --git a/assets/dolphin/blocking/L0_Url_128x51/frame_2.png b/assets/dolphin/blocking/L0_Url_128x51/frame_2.png index 855e7450e53b99c522d473e9e913271be4b36536..84241c3f16ab65f696964e17ab96c1deb8a7dc6c 100644 GIT binary patch delta 680 zcmZ3&|A&8qL?8<@0|UcaKUrlU#aJBV?!>U}oXkrghb7(7*O7r?V?XzwL{=c5Ex;$l z_5c6>KxXKn%XL7Cv%n*=n1O*?7=#%aX3dcRs#2KflA_ zpXF>ll4hVPeRO3u`q)f_sB=jzPARUho>;uzv_{Oz>tqQeS2EnCea?*892QNUkV$miC- zjMX12at_sT*cLpwe`t&Oz3a&gFEkrIycSS!*;|snPr9Fx=dC&eW9-u93>VUt-8>$> zgX!JUkI4;|nT7_X-3OL8TN_JU(V znKXk*Q~sk_toF&-JTsCQu1A(c^Dr&2I~FRt!R_Cq2}}SSw$=v*})r_JJ=3#c-)oHf6uV&>#>f> zdl?tqo!)+fwPA)WLs8r}hJUr8=NQ(?|99q}#BJS>Zm6L*CvD-bf(Z6z;rrY(svpWR nB%fIC{OjNRSKor_kGjaE>KGbtX4qr|Ok50}u6{1-oD!Mq=#1%;Y|NnpK&G~mH28c7U-kg}}&!{kQSN>!j zMyJiij9-~1=d&!7(YI5usmLwx^|kWIEH23}ss!?jQqrt~Qw!}Td$69Z2Px2pi2#}C ziotR&sl~}fnFS@8`FSwqK`w3}W`z|{LvCtGB9LQWQIMFNom!%hnwMg$RHS5YcjC&P zrwj~?@t!V@ArXh)&bZxoSb@jo{Gb2+-xXQTm~LRbRG%vx>ZP@HUbA5`$F84`7=K7G z%(`C1u%q(;gU8kL;tVc|H~1Y?s)ZYtD4$z+^qP{5NMTi7N;Kn_+YIY^I2O&$eo$}m zklBF8z~fjBL--9no}CP~@qcqA7!I!VF03&T7GPZRt1?LIT^Hj6lS&awJEI8|?i(1E zBz+5GRXEe=&02r#-J#+`3H47Hll~s4uw*TKBExWuF{P!H*Fo6f=yJ7&bD6!IS*r!4 zEw3>J{5+S!)e!l4FT<1e-+LuLu*}X@P`$B-tEg$Oox%5%FN_SbdlOU}^pq}@ j8bn|3oj?65e?#1h55oNRTYdxrQw4*ktDnm{r-UW|Ys2O5 diff --git a/assets/dolphin/blocking/L0_Url_128x51/frame_3.png b/assets/dolphin/blocking/L0_Url_128x51/frame_3.png index 9e9a79405817d4f1f3ec242de249beedac773c57..c44b171bfaae20b74657855c263a48cb0ca3ae85 100644 GIT binary patch delta 681 zcmZ3?|CfJ)L?8<@0|UcaKUrlU#aJBV?!>U}oXkrghb7(7*O7r?V?XzwL{=c5Ex;$l z_5c6>KxXKn%XL7Cv%n*=n1O*?7=#%aX3dcRs#2KflA_ zpXF>ll4hVPeRO3u`q)f_sB=jzPA_)(Vst@sWaSZV|{&re+(P0IimYdRR-qqiH)yR5d*})}m z`?vL|CtKh0ck=K$^}kE?@1tT{wuU!M9J|dJTTgh0y~!<>&a&}@i^PKDw=QKk_mO148k(ujE_RS<(PUJYUVI9Fmlau?r~Z&^K=30ixu^S z3`e!EoSy%WEsW<9Yrqn}X2u2lS?i($8bue>Zrb17`zlC$*TdPmE&JIfa5Cr}<8^Rz z$dtEjSanw5SMUbO*_I2QGc3FOW}A$~fjiBYtrF7yl_-b`Y;<1c{59BNB|}$^-&3t4 zyBU5imdKI;Vst04103%>V!Z delta 569 zcmey%znFi5gfs^;0|P_Gz7I-3iY>q=#1%;Y|NnpK&G~mH28c7U-kg}}&!{kQSN>!j zMyJiij9-~1=d&!7(YI5usmLwx^|kWIEH23}ss!?jQqrt~Qw!}Td$69Z2Px2pi2#}C ziotR&sl~}fnFS@8`FSwqK`w3}W`z|{LvCtGB9LQWQIMFNom!%hnwMg$RHS5YXE%4t z0R{%fI8PVHkch)?XLR!&Qs8m*|NnovubR_ouBJ`r>W_<^-FD=w+&eFBC3X2226<+O z?-p_leo6rhw|oEpWMmL-s$l4O!Okg=ld>|y*8Ty7=GqAOmP#r_(0}IpH~H= z53|ptjZzA#Wn72j8YbSZnZd6Md9nT+5Rr(SumCB;Q{Rp6TdSUa4UF4xG}t7>ZrcTD3IHzmT}gp z+Pcd zYZ-)h@jh7VGmT+|r9ImL?dU}oXkrghb7(7*O7r?V?XzwL{=c5Ex;$l z_5c6>KxXKn%XL7Cv%n*=n1O-s5C}7hYIrp;Ffh-b=;hDE%QbOU0W&X`!elN+CsrV> zps?A8aS0Gd~Y34iODy-7)iZ-yW^cKASs1s%!TYpTt@Q=^YUv z71E3s9z|MqZhqO+z!AgXaF0!9k{Cmc{DWD~b_;&8dr+eQS#MaGCx|y}NpR<3 zP(OIptDbd(nU`bt>72mw1I;@H7~7rAS!;IkH!NV>`*+nn;fhk{fOoRD7(tYR5DGNX3G}xo6A4`&apd}80Y#kUU=8jr}l|)!F2rx-0IKMfN{^@>FVdQ I&MBb@00G9?xc~qF delta 551 zcmV+?0@(e73+4)t7%~V00000bc2Ow+000?uMObuGZ)S9NVRB^vL1b@YWgtmyVP|Dh zWnpA_ami&o000Aok!eo>Ad$LolPv*4vvdKZ0h1pCp?@zvAUrQ}WM(=fFD zZ*D#yJTG!&W;#+tMm``sFL*k5ZE$U6bYVUqJU@7FVPk7$bRcDJWIZBsB0oL}eh47| z000J1OjJex|Nj6009C@%egFUgW=TXrRCwCtn2ip@AP9u--v6bSt<$Lkg{pKvW?7nQ zxvwaQ1%F~1i-z#lh(Yq%H(_gqvV83gECM*!&j&z5tqP0KouFdS##onB48q zUkUJZKc#Xg?L7HPf}Quk!5{z>)07x9@WLp_=6?kM;`x_H%>huPI_z};n3l!d6m$~- zbbw3tf*RmuU}6gB>aPF_FH}TJ&jZxYB~56a190kt3c=ZHlymlHutNY;m9%BapX#+?Pm2=7!0A+&c)!6Oo z=~z#K4?zC9GRm{d0Elv@P~)?H_fhPw02o6CH~_$P pEZ_$H6MOBI0I)7?qq&;^0|4Z21;)FNg7E+V002ovPDHLkV1nMUU}oXkrghb7(7*O7r?V?XzwL{=c5Ex;$l z_5c6>KxXKn%XL7Cv%n*=n1O-s5C}7hYIrp;Ffh-b=;hDE%QbOU0W&X`!elN+CsrV> zps?A8aS0Gd~Y34iOD+9 zf0HHJC8H14qjK5W)do3KJdj%!Q&RggW877sQGTozB4}j`kuj#_1p&zg}wr&gcZ{N zI0cwff@PQ{`26%}R1Mt4B*yqF^cl}(uSNN9`1h=F*dNUx)?qZ$bluU&0EPgD1}o_# zrX3v1>ZeIH*liYA@@m(jyaxSmPE0J#i5!0n`WjX=x<3!CR$JiX9Fiw&#bCm;hM|f< zV_n`&u>#$rKR9oS34CYL5I@1n7}MEU;Sjs>0_&B3D*m%iZfauEe7e|D){Q~qd7olu5+Z>X@>H4PuH#$#hsOG3V$Z7UH8dXogv*L zy5ZEmdd}Gldour*{FAycM_YoSO#emA?ef_SheRB{FcyUBXO=|lJ_n3?22WQ%mvv4F FO#pDe(sBR* delta 545 zcmV++0^a?C3*ZWn7%~V00000bc2Ow+000?uMObuGZ)S9NVRB^vL1b@YWgtmyVP|Dh zWnpA_ami&o000Aok!eo>Ad$LolPv*4vvdKZ0h1pCp?@zvAUrQ}WM(=fFD zZ*D#yJTG!&W;#+tMm``sFL*k5ZE$U6bYVUqJU@7FVPk7$bRcDJWIZBsB0oM(r97wr z000J1OjJex|Nj6009C@%egFUgU`a$lRCwCtn2QdBAPhx&|No~KryyWKN+0e)mcl00jdy=@o$z5*z`P!XBl2hhKlG@-c$Ak-&x2v)DMQvFLVtUGpu>T~_Fjeg%w z_5ZnUwbwz(JAsvkkPX|Ww%h>Rd2tyaJ>Vw9buIA(z~x9~)pAc^2tdF9fKvV_)osr} zl~Sn~0SuO!0r0PRF8-dWXe$7qvScywDJiIy0K7%>D40r=^X-HVz~FZLH~=jN!MMUW jzXCRNfP%mP;3dESp_c`?`EG1V00000NkvXXu0mjfVa42_ diff --git a/assets/dolphin/internal/L1_NoSd_128x49/frame_2.png b/assets/dolphin/internal/L1_NoSd_128x49/frame_2.png index 12dc13430908844d89e0c7a002030cdc4999124b..5b474fff262306ecccbdc2a0ace273bd26e55219 100644 GIT binary patch delta 588 zcmaFL)xkYMB9Midfq~&c+a3)d#aJBV?!>U}oXkrghb7(7*O7r?V?XzwL{=c5Ex;$l z_5c6>KxXKn%XL7Cv%n*=n1O-s5C}7hYIrp;Ffh-b=;hDE%QbOU0W&X`!elN+CsrV> zps?A8aS0Gd~Y34iODv^(Izmxfoo%Z3S(t+2?qoBLQV~aLI#7j;5jElsuK4x{$WbkrF~__S?SyQ z4Xv$nD`T=F?l_n+eD*4}o3*)*;cTVZ&nds`8h%zWE%E-i*H6c~{A-oBtp0-CE~X9k zT!Gfn`<94*+8YqlaIVk2q0mO>u*F<$8OCPK^Y318J6LGRIdI6=gzKB#_`MI5Y#2OU L{an^LB{Ts5>L%C3 delta 550 zcmV+>0@?kD3*`!s7%~V00000bc2Ow+000?uMObuGZ)S9NVRB^vL1b@YWgtmyVP|Dh zWnpA_ami&o000Aok!eo>Ad$LolPv*4vvdKZ0h1pCp?@zvAUrQ}WM(=fFD zZ*D#yJTG!&W;#+tMm``sFL*k5ZE$U6bYVUqJU@7FVPk7$bRcDJWIZBsB0oML+;Z^% z000J1OjJex|Nj6009C@%egFUgWl2OqRCwC#n2Qd>AP7ad|Nqm=Qf+;Jq5|2LnPus! z<(>+HlYc0tp=cD|8qsJ5PA(8Zl4|Z_(9ZOAU4Rn-l$~!8@dGFQvfBv&>H$6gppOT* zhqwTMRsbAU0Tu>%0D!&%fPNQX?WsRN9Dral3g9x<+XRIHWDG3m+W}n0I-@gvOMt8n zaB+b&BwcZ^=QDub1(nK$_a1;7*fa>!TM`2RN`GHB#2Ekv>zzgb$YF8j0xV{s3=okC z(RSAWkAVXh%=IAw6thqf+317QXa2fAKehl=`VdM%wPg1IR`t7FSnt?j>3=o~N_}p> zY*mP*g;2dew_CNVgFCws1Av*0Wq=75xB_GefV#G_5teT2W^fZAUqCMVN(UYS5TvqO zI#(aIF`xlxHW_5P3Of53#NpHrI{^GK7k~U@z6y#o$sV@#2M-hlAlM^6fMGt;=h4j& om?U^|JQ>#kcpg9-0=xtm0DO@JvS@mv#Q*>R07*qoM6N<$g7Hz*umAu6 diff --git a/assets/dolphin/internal/L1_NoSd_128x49/frame_3.png b/assets/dolphin/internal/L1_NoSd_128x49/frame_3.png index f17f8713d34b7702ad58def6cf332b1770412808..952f968fb673a5de24fefb834e6c8655cc29a6d4 100644 GIT binary patch delta 587 zcmaFF)y_RZB9Midfq~&c+a3)d#aJBV?!>U}oXkrghb7(7*O7r?V?XzwL{=c5Ex;$l z_5c6>KxXKn%XL7Cv%n*=n1O-s5C}7hYIrp;Ffh-b=;hDE%QbOU0W&X`!elN+CsrV> zps?A8aS0Gd~Y34iOD!g+4&{$9uXshIkymJ1uh3VFezSm(p9_ z)!&?WBpwptZfZ+(^0SUjtyk(cTKl47|Yk0XXAgSzR zb~yt#x3x!1_DR=AtTVz_eJ*`=GxU&o!?Qix-qkRC{>tFsCH3#o^V=Fv&fFDAmR(-$ zP~Q29^B{M^pDTYiU3-xXHjCsf*;S(Qsp`MB3A-}~ky{o^?wipBB OpTX1B&t;ucLK6VtnBzwP delta 547 zcmV+;0^I$F3*rip7%~V00000bc2Ow+000?uMObuGZ)S9NVRB^vL1b@YWgtmyVP|Dh zWnpA_ami&o000Aok!eo>Ad$LolPv*4vvdKZ0h1pCp?@zvAUrQ}WM(=fFD zZ*D#yJTG!&W;#+tMm``sFL*k5ZE$U6bYVUqJU@7FVPk7$bRcDJWIZBsB0oN4z+l$^ z000J1OjJex|Nj6009C@%egFUgVo5|nRCwC#m;nxhAPhzO?tkfJ5fr2dg+jJv#26>6 z|6xn1iht~BBu%!ro9J!^O>QGXP3m@^hc>6**9D6R=&0s#360Qp@2+rRz*`v58?cL5y6N}I?wfQW%X-w)t0Rv2yRdjdpt z(BgtHBwT5*^G|@%1)0i$_a49r>>7k&mZAV4;eT{O%mMJQ-sK5^7#4Fbz?g+1z>ZAF zy*mfE3{1G7txpXgc?ubkk3O2d^6Gm17y{_hr?M4fOLh#vs$b;7ykf`G|7sMZ`dYp0 zwGr1ALN7P^ lpO$=HP6xC7PXN3G7yx#e1*^`5DHH$z002ovPDHLkV1mgA;jjPz diff --git a/assets/dolphin/internal/L1_NoSd_128x49/frame_4.png b/assets/dolphin/internal/L1_NoSd_128x49/frame_4.png index 7a6992a2b04cdaf35abb8c23f269293e143a4ea0..2bb43b306f3bf9025ba68190fb4903cc5a132133 100644 GIT binary patch delta 573 zcmcb^^^;M1%fy~fDm+OEOXMsm#F#`kNArNL1)$nRyU|^m<(aWESmuupz0%l$=g~?ouPOLy$ zL1D8G;}S-8FjGNc@&~4c^$PlS5U!1WQA(PXOKNd)QD#9&W_})693mRz;s)f|=vP=( z3GX z?|;K45}z%!CFpnd%qu-752SNv`LMGECR^2S72>k&jo)(aQFL@wwWL&=wfvcc^9*_g zsw*$N=4}vt@1-87e^Vf!L7HL7dPcQO<^%5;S82w%EqTxQ?ZjdJTC;trFl$Ns3O1XL@Pk`6ubuwKq0WDbz=F?@ x#S-4|pEmM)FWT2|aB6k>7gmMbNR|ng_&I;+l{b0*l?BE)gQu&X%Q~loCIB#J)NKF& delta 541 zcmV+&0^Ad$LolPv*4vvdKZ0h1pCp?@zvAUrQ}WM(=fFD zZ*D#yJTG!&W;#+tMm``sFL*k5ZE$U6bYVUqJU@7FVPk7$bRcDJWIZBsB0oL=qvEvy z000J1OjJex|Nj6009C@%egFUgTuDShRCwC#n2Qd>AP7ad|Nqm=nrTr{QGx8`mZqyM z_e>C}M1L_2tWkKYM57rbxj=-TRCOJLcBZfE0-Ok-?0kxJzi`s$-A({d-{1oPdV7F; ziwgi~1;AkzVBsbY0MJJO(DwqYJ@p6pc>qu}KvkoKk3ea3rf&xTmiO8KNGnf1L+UaF z5dTzh1s4M7`n<-*&IR(I-UIMDWQa3>!5GjG0Dmbg&Rl@SB#;0iW&!H%65uv);DWh6 zdH_6Bu=^s1sW`^3uJ?~E0F^#EO98vI`zpJt-{iuwVu$TAAVdMzm+EC3{d)b8^&wTO zHaT#&7%%{s*;odcUs0ZWtOW?Q@QKv4i4d*lZ&%t!h>x)}nK1W%4T<2nG( f4QNAvhX4Zr1eOJu%LfM}00000NkvXXu0mjf9sAk! diff --git a/assets/dolphin/internal/L1_NoSd_128x49/frame_5.png b/assets/dolphin/internal/L1_NoSd_128x49/frame_5.png index 00d9843007e85a247de0c23257c310512b5f71f5..d7f8c6402a27c5a2eb565d12b595a3a438ed94ee 100644 GIT binary patch delta 584 zcmaFJ)xteNB9Midfq~&c+a3)d#aJBV?!>U}oXkrghb7(7*O7r?V?XzwL{=c5Ex;$l z_5c6>KxXKn%XL7Cv%n*=n1O-s5C}7hYIrp;Ffh-b=;hDE%QbOU0W&X`!elN+CsrV> zps?A8aS0Gd~Y34iODz$gZO)!`%nnn~{g8^b441&v9d6lX$2%~n|wpQUNDi>ERVTD;TVg~x&%=k2Q!AubA1(z zAx*2N)^jGDkLIu{+3kCmaeoB2xPqijTf^-4JSU_+l*FA)XNs2GRHXlcIYH=wbOQf^ zCpXvLW_lHJ#`(pzhCN~$sm(kb2Nc?FF`F<*{B>>aUYRR@`DFq7fs*hQn)CgqTQ^K^ zom)98JK~Om9K*b+vud`aS|uyuqh zX6oPe`*r@3B|}Ns9j2bgiIK-%#ysSZ@Yz}J9?y`n+Lb}cz9mQhq<7|ZMPTGJc)I$z JtaD0e0syY@+hza& delta 546 zcmV+-0^R+D3*ico7%~V00000bc2Ow+000?uMObuGZ)S9NVRB^vL1b@YWgtmyVP|Dh zWnpA_ami&o000Aok!eo>Ad$LolPv*4vvdKZ0h1pCp?@zvAUrQ}WM(=fFD zZ*D#yJTG!&W;#+tMm``sFL*k5ZE$U6bYVUqJU@7FVPk7$bRcDJWIZBsB0oNIez$f2 z000J1OjJex|Nj6009C@%egFUgVM#AP7ad|Nqm=)@jv(q5|E+G)*%N z_e=#5qJNl%qEUEjM57rvxj+O-s`(wAwx_S_0-Ok-?0kxd51jPNZYKb!2lxPhJ{}+s zaRC6$062I7CI)!`fW88Neir~8sT%=a?#7sm0yu0jHva_xCqR=8P%uCfUJ*D0NYg!` zZwc_8esJkfI(hOJ2fIH52Ll68EK_34z=mFs&3`ihp!X|}+5-Sfb=d0yAZ?4iCFlkM zk^ycx3u=JVz{V0_s=op#W}zZ7y$?XXmo%=q1~92l5C~SUva|Y^SXgK52-WBEWgC6j zPWAt}Y}GCkSV;&C07f>Z0R~u1nA=H-ne?eSTg8B{4dsgf=>y`iuU)`R0D>6s%VXYl z4^wCWnk0ivz2IUWoqaf5hZO+6%*B_V%)OuplWei=T6mx+0Kp#l0d(V$K8`MKfkA*L k$I7@4z_S2t2=EYK0A@u6zLP8OIRF3v07*qoM6N<$g6II$yZ`_I From e12958d408dfe0e1571b60451bf73b1d41914981 Mon Sep 17 00:00:00 2001 From: Skorpionm <85568270+Skorpionm@users.noreply.github.com> Date: Sun, 29 Jan 2023 15:08:26 +0400 Subject: [PATCH 4/5] [FL-3082] WS: add protocol LaCrosse-TX (TFA Dostmann) (#2292) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * WS: add protocol LaCrosse-TX (TFA Dostmann) * WS: fix syntax * WS: fix MSG_TYPE * WS: fix PVS Co-authored-by: あく --- .../helpers/weather_station_types.h | 2 +- .../weather_station/protocols/lacrosse_tx.c | 329 ++++++++++++++++++ .../weather_station/protocols/lacrosse_tx.h | 79 +++++ .../protocols/protocol_items.c | 1 + .../protocols/protocol_items.h | 1 + 5 files changed, 411 insertions(+), 1 deletion(-) create mode 100644 applications/plugins/weather_station/protocols/lacrosse_tx.c create mode 100644 applications/plugins/weather_station/protocols/lacrosse_tx.h diff --git a/applications/plugins/weather_station/helpers/weather_station_types.h b/applications/plugins/weather_station/helpers/weather_station_types.h index d512251f1..1f5612e2e 100644 --- a/applications/plugins/weather_station/helpers/weather_station_types.h +++ b/applications/plugins/weather_station/helpers/weather_station_types.h @@ -3,7 +3,7 @@ #include #include -#define WS_VERSION_APP "0.6.1" +#define WS_VERSION_APP "0.7" #define WS_DEVELOPED "SkorP" #define WS_GITHUB "https://github.com/flipperdevices/flipperzero-firmware" diff --git a/applications/plugins/weather_station/protocols/lacrosse_tx.c b/applications/plugins/weather_station/protocols/lacrosse_tx.c new file mode 100644 index 000000000..8d8a24e24 --- /dev/null +++ b/applications/plugins/weather_station/protocols/lacrosse_tx.c @@ -0,0 +1,329 @@ +#include "lacrosse_tx.h" + +#define TAG "WSProtocolLaCrosse_TX" + +/* + * Help + * https://github.com/merbanan/rtl_433/blob/master/src/devices/lacrosse.c + * + * + * LaCrosse TX 433 Mhz Temperature and Humidity Sensors. + * - Tested: TX-7U and TX-6U (Temperature only) + * - Not Tested but should work: TX-3, TX-4 + * - also TFA Dostmann 30.3120.90 sensor (for e.g. 35.1018.06 (WS-9015) station) + * - also TFA Dostmann 30.3121 sensor + * Protocol Documentation: http://www.f6fbb.org/domo/sensors/tx3_th.php + * Message is 44 bits, 11 x 4 bit nybbles: + * [00] [cnt = 10] [type] [addr] [addr + parity] [v1] [v2] [v3] [iv1] [iv2] [check] + * Notes: + * - Zero Pulses are longer (1,400 uS High, 1,000 uS Low) = 2,400 uS + * - One Pulses are shorter ( 550 uS High, 1,000 uS Low) = 1,600 uS + * - Sensor id changes when the battery is changed + * - Primary Value are BCD with one decimal place: vvv = 12.3 + * - Secondary value is integer only intval = 12, seems to be a repeat of primary + * This may actually be an additional data check because the 4 bit checksum + * and parity bit is pretty week at detecting errors. + * - Temperature is in Celsius with 50.0 added (to handle negative values) + * - Humidity values appear to be integer precision, decimal always 0. + * - There is a 4 bit checksum and a parity bit covering the three digit value + * - Parity check for TX-3 and TX-4 might be different. + * - Msg sent with one repeat after 30 mS + * - Temperature and humidity are sent as separate messages + * - Frequency for each sensor may be could be off by as much as 50-75 khz + * - LaCrosse Sensors in other frequency ranges (915 Mhz) use FSK not OOK + * so they can't be decoded by rtl_433 currently. + * - Temperature and Humidity are sent in different messages bursts. +*/ + +#define LACROSSE_TX_GAP 1000 +#define LACROSSE_TX_BIT_SIZE 44 +#define LACROSSE_TX_SUNC_PATTERN 0x0A000000000 +#define LACROSSE_TX_SUNC_MASK 0x0F000000000 +#define LACROSSE_TX_MSG_TYPE_TEMP 0x00 +#define LACROSSE_TX_MSG_TYPE_HUM 0x0E + +static const SubGhzBlockConst ws_protocol_lacrosse_tx_const = { + .te_short = 550, + .te_long = 1300, + .te_delta = 120, + .min_count_bit_for_found = 40, +}; + +struct WSProtocolDecoderLaCrosse_TX { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + WSBlockGeneric generic; + + uint16_t header_count; +}; + +struct WSProtocolEncoderLaCrosse_TX { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + WSBlockGeneric generic; +}; + +typedef enum { + LaCrosse_TXDecoderStepReset = 0, + LaCrosse_TXDecoderStepCheckPreambule, + LaCrosse_TXDecoderStepSaveDuration, + LaCrosse_TXDecoderStepCheckDuration, +} LaCrosse_TXDecoderStep; + +const SubGhzProtocolDecoder ws_protocol_lacrosse_tx_decoder = { + .alloc = ws_protocol_decoder_lacrosse_tx_alloc, + .free = ws_protocol_decoder_lacrosse_tx_free, + + .feed = ws_protocol_decoder_lacrosse_tx_feed, + .reset = ws_protocol_decoder_lacrosse_tx_reset, + + .get_hash_data = ws_protocol_decoder_lacrosse_tx_get_hash_data, + .serialize = ws_protocol_decoder_lacrosse_tx_serialize, + .deserialize = ws_protocol_decoder_lacrosse_tx_deserialize, + .get_string = ws_protocol_decoder_lacrosse_tx_get_string, +}; + +const SubGhzProtocolEncoder ws_protocol_lacrosse_tx_encoder = { + .alloc = NULL, + .free = NULL, + + .deserialize = NULL, + .stop = NULL, + .yield = NULL, +}; + +const SubGhzProtocol ws_protocol_lacrosse_tx = { + .name = WS_PROTOCOL_LACROSSE_TX_NAME, + .type = SubGhzProtocolWeatherStation, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, + + .decoder = &ws_protocol_lacrosse_tx_decoder, + .encoder = &ws_protocol_lacrosse_tx_encoder, +}; + +void* ws_protocol_decoder_lacrosse_tx_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + WSProtocolDecoderLaCrosse_TX* instance = malloc(sizeof(WSProtocolDecoderLaCrosse_TX)); + instance->base.protocol = &ws_protocol_lacrosse_tx; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void ws_protocol_decoder_lacrosse_tx_free(void* context) { + furi_assert(context); + WSProtocolDecoderLaCrosse_TX* instance = context; + free(instance); +} + +void ws_protocol_decoder_lacrosse_tx_reset(void* context) { + furi_assert(context); + WSProtocolDecoderLaCrosse_TX* instance = context; + instance->header_count = 0; + instance->decoder.parser_step = LaCrosse_TXDecoderStepReset; +} + +static bool ws_protocol_lacrosse_tx_check_crc(WSProtocolDecoderLaCrosse_TX* instance) { + if(!instance->decoder.decode_data) return false; + uint8_t msg[] = { + (instance->decoder.decode_data >> 36) & 0x0F, + (instance->decoder.decode_data >> 32) & 0x0F, + (instance->decoder.decode_data >> 28) & 0x0F, + (instance->decoder.decode_data >> 24) & 0x0F, + (instance->decoder.decode_data >> 20) & 0x0F, + (instance->decoder.decode_data >> 16) & 0x0F, + (instance->decoder.decode_data >> 12) & 0x0F, + (instance->decoder.decode_data >> 8) & 0x0F, + (instance->decoder.decode_data >> 4) & 0x0F}; + + uint8_t crc = subghz_protocol_blocks_add_bytes(msg, 9); + return ((crc & 0x0F) == ((instance->decoder.decode_data) & 0x0F)); +} + +/** + * Analysis of received data + * @param instance Pointer to a WSBlockGeneric* instance + */ +static void ws_protocol_lacrosse_tx_remote_controller(WSBlockGeneric* instance) { + uint8_t msg_type = (instance->data >> 32) & 0x0F; + instance->id = (((instance->data >> 28) & 0x0F) << 3) | (((instance->data >> 24) & 0x0F) >> 1); + + float msg_value = (float)((instance->data >> 20) & 0x0F) * 10.0f + + (float)((instance->data >> 16) & 0x0F) + + (float)((instance->data >> 12) & 0x0F) * 0.1f; + + if(msg_type == LACROSSE_TX_MSG_TYPE_TEMP) { //-V1051 + instance->temp = msg_value - 50.0f; + instance->humidity = WS_NO_HUMIDITY; + } else if(msg_type == LACROSSE_TX_MSG_TYPE_HUM) { + //ToDo for verification, records are needed with sensors maintaining temperature and temperature for this standard + instance->humidity = (uint8_t)msg_value; + } else { + furi_crash("WS: WSProtocolLaCrosse_TX incorrect msg_type."); + } + + instance->btn = WS_NO_BTN; + instance->battery_low = WS_NO_BATT; + instance->channel = WS_NO_CHANNEL; +} + +void ws_protocol_decoder_lacrosse_tx_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + WSProtocolDecoderLaCrosse_TX* instance = context; + + switch(instance->decoder.parser_step) { + case LaCrosse_TXDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, LACROSSE_TX_GAP) < + ws_protocol_lacrosse_tx_const.te_delta * 2)) { + instance->decoder.parser_step = LaCrosse_TXDecoderStepCheckPreambule; + instance->header_count = 0; + } + break; + + case LaCrosse_TXDecoderStepCheckPreambule: + + if(level) { + if((DURATION_DIFF(duration, ws_protocol_lacrosse_tx_const.te_short) < + ws_protocol_lacrosse_tx_const.te_delta) && + (instance->header_count > 1)) { + instance->decoder.parser_step = LaCrosse_TXDecoderStepCheckDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->decoder.te_last = duration; + } else if(duration > (ws_protocol_lacrosse_tx_const.te_long * 2)) { + instance->decoder.parser_step = LaCrosse_TXDecoderStepReset; + } + } else { + if(DURATION_DIFF(duration, LACROSSE_TX_GAP) < + ws_protocol_lacrosse_tx_const.te_delta * 2) { + instance->decoder.te_last = duration; + instance->header_count++; + } else { + instance->decoder.parser_step = LaCrosse_TXDecoderStepReset; + } + } + + break; + + case LaCrosse_TXDecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = LaCrosse_TXDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = LaCrosse_TXDecoderStepReset; + } + break; + + case LaCrosse_TXDecoderStepCheckDuration: + + if(!level) { + if(duration > LACROSSE_TX_GAP * 3) { + if(DURATION_DIFF( + instance->decoder.te_last, ws_protocol_lacrosse_tx_const.te_short) < + ws_protocol_lacrosse_tx_const.te_delta) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = LaCrosse_TXDecoderStepSaveDuration; + } else if( + DURATION_DIFF( + instance->decoder.te_last, ws_protocol_lacrosse_tx_const.te_long) < + ws_protocol_lacrosse_tx_const.te_delta) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = LaCrosse_TXDecoderStepSaveDuration; + } + if((instance->decoder.decode_data & LACROSSE_TX_SUNC_MASK) == + LACROSSE_TX_SUNC_PATTERN) { + if(ws_protocol_lacrosse_tx_check_crc(instance)) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = LACROSSE_TX_BIT_SIZE; + ws_protocol_lacrosse_tx_remote_controller(&instance->generic); + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + } + + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->header_count = 0; + instance->decoder.parser_step = LaCrosse_TXDecoderStepReset; + break; + } else if( + (DURATION_DIFF(instance->decoder.te_last, ws_protocol_lacrosse_tx_const.te_short) < + ws_protocol_lacrosse_tx_const.te_delta) && + (DURATION_DIFF(duration, LACROSSE_TX_GAP) < + ws_protocol_lacrosse_tx_const.te_delta * 2)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = LaCrosse_TXDecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, ws_protocol_lacrosse_tx_const.te_long) < + ws_protocol_lacrosse_tx_const.te_delta) && + (DURATION_DIFF(duration, LACROSSE_TX_GAP) < + ws_protocol_lacrosse_tx_const.te_delta * 2)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = LaCrosse_TXDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = LaCrosse_TXDecoderStepReset; + } + + } else { + instance->decoder.parser_step = LaCrosse_TXDecoderStepReset; + } + + break; + } +} + +uint8_t ws_protocol_decoder_lacrosse_tx_get_hash_data(void* context) { + furi_assert(context); + WSProtocolDecoderLaCrosse_TX* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool ws_protocol_decoder_lacrosse_tx_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + WSProtocolDecoderLaCrosse_TX* instance = context; + return ws_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool ws_protocol_decoder_lacrosse_tx_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + WSProtocolDecoderLaCrosse_TX* instance = context; + bool ret = false; + do { + if(!ws_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + ws_protocol_lacrosse_tx_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; +} + +void ws_protocol_decoder_lacrosse_tx_get_string(void* context, FuriString* output) { + furi_assert(context); + WSProtocolDecoderLaCrosse_TX* instance = context; + furi_string_printf( + output, + "%s %dbit\r\n" + "Key:0x%lX%08lX\r\n" + "Sn:0x%lX Ch:%d Bat:%d\r\n" + "Temp:%3.1f C Hum:%d%%", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data >> 32), + (uint32_t)(instance->generic.data), + instance->generic.id, + instance->generic.channel, + instance->generic.battery_low, + (double)instance->generic.temp, + instance->generic.humidity); +} diff --git a/applications/plugins/weather_station/protocols/lacrosse_tx.h b/applications/plugins/weather_station/protocols/lacrosse_tx.h new file mode 100644 index 000000000..e88455689 --- /dev/null +++ b/applications/plugins/weather_station/protocols/lacrosse_tx.h @@ -0,0 +1,79 @@ +#pragma once + +#include + +#include +#include +#include +#include "ws_generic.h" +#include + +#define WS_PROTOCOL_LACROSSE_TX_NAME "LaCrosse_TX" + +typedef struct WSProtocolDecoderLaCrosse_TX WSProtocolDecoderLaCrosse_TX; +typedef struct WSProtocolEncoderLaCrosse_TX WSProtocolEncoderLaCrosse_TX; + +extern const SubGhzProtocolDecoder ws_protocol_lacrosse_tx_decoder; +extern const SubGhzProtocolEncoder ws_protocol_lacrosse_tx_encoder; +extern const SubGhzProtocol ws_protocol_lacrosse_tx; + +/** + * Allocate WSProtocolDecoderLaCrosse_TX. + * @param environment Pointer to a SubGhzEnvironment instance + * @return WSProtocolDecoderLaCrosse_TX* pointer to a WSProtocolDecoderLaCrosse_TX instance + */ +void* ws_protocol_decoder_lacrosse_tx_alloc(SubGhzEnvironment* environment); + +/** + * Free WSProtocolDecoderLaCrosse_TX. + * @param context Pointer to a WSProtocolDecoderLaCrosse_TX instance + */ +void ws_protocol_decoder_lacrosse_tx_free(void* context); + +/** + * Reset decoder WSProtocolDecoderLaCrosse_TX. + * @param context Pointer to a WSProtocolDecoderLaCrosse_TX instance + */ +void ws_protocol_decoder_lacrosse_tx_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a WSProtocolDecoderLaCrosse_TX instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void ws_protocol_decoder_lacrosse_tx_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a WSProtocolDecoderLaCrosse_TX instance + * @return hash Hash sum + */ +uint8_t ws_protocol_decoder_lacrosse_tx_get_hash_data(void* context); + +/** + * Serialize data WSProtocolDecoderLaCrosse_TX. + * @param context Pointer to a WSProtocolDecoderLaCrosse_TX instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool ws_protocol_decoder_lacrosse_tx_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data WSProtocolDecoderLaCrosse_TX. + * @param context Pointer to a WSProtocolDecoderLaCrosse_TX instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool ws_protocol_decoder_lacrosse_tx_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a WSProtocolDecoderLaCrosse_TX instance + * @param output Resulting text + */ +void ws_protocol_decoder_lacrosse_tx_get_string(void* context, FuriString* output); diff --git a/applications/plugins/weather_station/protocols/protocol_items.c b/applications/plugins/weather_station/protocols/protocol_items.c index 99c8344f4..2c9d751c7 100644 --- a/applications/plugins/weather_station/protocols/protocol_items.c +++ b/applications/plugins/weather_station/protocols/protocol_items.c @@ -8,6 +8,7 @@ const SubGhzProtocol* weather_station_protocol_registry_items[] = { &ws_protocol_gt_wt_03, &ws_protocol_acurite_606tx, &ws_protocol_acurite_609txc, + &ws_protocol_lacrosse_tx, &ws_protocol_lacrosse_tx141thbv2, &ws_protocol_oregon2, &ws_protocol_acurite_592txr, diff --git a/applications/plugins/weather_station/protocols/protocol_items.h b/applications/plugins/weather_station/protocols/protocol_items.h index 9d5d096f8..f9e443abc 100644 --- a/applications/plugins/weather_station/protocols/protocol_items.h +++ b/applications/plugins/weather_station/protocols/protocol_items.h @@ -8,6 +8,7 @@ #include "gt_wt_03.h" #include "acurite_606tx.h" #include "acurite_609txc.h" +#include "lacrosse_tx.h" #include "lacrosse_tx141thbv2.h" #include "oregon2.h" #include "acurite_592txr.h" From a8e5f2250026c4260bb13fddf696acb7f9322b77 Mon Sep 17 00:00:00 2001 From: Angel Date: Sun, 29 Jan 2023 06:23:45 -0500 Subject: [PATCH 5/5] LF-RFID: add CRC calculation to paradox protocol (#2299) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Angel Co-authored-by: あく --- lib/lfrfid/protocols/protocol_paradox.c | 45 ++++++++++++++++++++++--- 1 file changed, 40 insertions(+), 5 deletions(-) diff --git a/lib/lfrfid/protocols/protocol_paradox.c b/lib/lfrfid/protocols/protocol_paradox.c index 7e029f1de..26c9b55dc 100644 --- a/lib/lfrfid/protocols/protocol_paradox.c +++ b/lib/lfrfid/protocols/protocol_paradox.c @@ -136,17 +136,45 @@ LevelDuration protocol_paradox_encoder_yield(ProtocolParadox* protocol) { return level_duration_make(level, duration); }; +static uint8_t protocol_paradox_calculate_checksum(uint8_t fc, uint16_t card_id) { + uint8_t card_hi = (card_id >> 8) & 0xff; + uint8_t card_lo = card_id & 0xff; + + uint8_t arr[5] = {0, 0, fc, card_hi, card_lo}; + + uint8_t manchester[9]; + + bit_lib_push_bit(manchester, 9, false); + bit_lib_push_bit(manchester, 9, false); + bit_lib_push_bit(manchester, 9, false); + bit_lib_push_bit(manchester, 9, false); + + for(uint8_t i = 6; i < 40; i += 1) { + if(bit_lib_get_bit(arr, i) == 0b1) { + bit_lib_push_bit(manchester, 9, true); + bit_lib_push_bit(manchester, 9, false); + } else { + bit_lib_push_bit(manchester, 9, false); + bit_lib_push_bit(manchester, 9, true); + } + } + + uint8_t output = bit_lib_crc8(manchester, 9, 0x31, 0x00, true, true, 0x06); + + return output; +} + void protocol_paradox_render_data(ProtocolParadox* protocol, FuriString* result) { uint8_t* decoded_data = protocol->data; uint8_t fc = bit_lib_get_bits(decoded_data, 10, 8); uint16_t card_id = bit_lib_get_bits_16(decoded_data, 18, 16); + uint8_t card_crc = bit_lib_get_bits_16(decoded_data, 34, 8); + uint8_t calc_crc = protocol_paradox_calculate_checksum(fc, card_id); furi_string_cat_printf(result, "Facility: %u\r\n", fc); furi_string_cat_printf(result, "Card: %u\r\n", card_id); - furi_string_cat_printf(result, "Data: "); - for(size_t i = 0; i < PARADOX_DECODED_DATA_SIZE; i++) { - furi_string_cat_printf(result, "%02X", decoded_data[i]); - } + furi_string_cat_printf(result, "CRC: %u Calc CRC: %u\r\n", card_crc, calc_crc); + if(card_crc != calc_crc) furi_string_cat_printf(result, "CRC Mismatch, Invalid Card!\r\n"); }; void protocol_paradox_render_brief_data(ProtocolParadox* protocol, FuriString* result) { @@ -154,8 +182,15 @@ void protocol_paradox_render_brief_data(ProtocolParadox* protocol, FuriString* r uint8_t fc = bit_lib_get_bits(decoded_data, 10, 8); uint16_t card_id = bit_lib_get_bits_16(decoded_data, 18, 16); + uint8_t card_crc = bit_lib_get_bits_16(decoded_data, 34, 8); + uint8_t calc_crc = protocol_paradox_calculate_checksum(fc, card_id); - furi_string_cat_printf(result, "FC: %03u, Card: %05u", fc, card_id); + furi_string_cat_printf(result, "FC: %03u, Card: %05u\r\n", fc, card_id); + if(calc_crc == card_crc) { + furi_string_cat_printf(result, "CRC : %03u", card_crc); + } else { + furi_string_cat_printf(result, "Card is Invalid!"); + } }; bool protocol_paradox_write_data(ProtocolParadox* protocol, void* data) {