From 80591cf1a7f48bf1e7bc2c1552d008dc3bd87e42 Mon Sep 17 00:00:00 2001 From: RocketGod <57732082+RocketGod-git@users.noreply.github.com> Date: Sun, 7 Dec 2025 14:45:19 -0800 Subject: [PATCH] Kia V4 Decode working now --- README.md | 2 +- dist/proto_pirate.fap | Bin 40564 -> 40124 bytes protocols/kia_v4.c | 214 +++++++++++++++++++++--------------------- 3 files changed, 106 insertions(+), 110 deletions(-) diff --git a/README.md b/README.md index 744f6ef..f645ea6 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # ProtoPirate ## for Flipper Zero -Work in progress by The Pirates' Plunder crew... currently only decoding Kia V0, V1, V2, and V5. +Work in progress by The Pirates' Plunder crew... currently only decoding Kia V0, V1, V2, V4, and V5. but will eventually be a drop-in app (fap) for decoding, storing, and replicating all automotive key fobs and any other protocols which utilize rolling code. If you contribute significantly you will be added to the app credits. diff --git a/dist/proto_pirate.fap b/dist/proto_pirate.fap index 6b6f78b3989408411be6f52222fb68debaa8f8e0..0320a1369507bb20e13ea2b91c2e798a6ef227c4 100644 GIT binary patch delta 14090 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 delta 14615 zcmZu%4OmuHwq9p{{0k_*{-Jz;{0OLMIAN)gnxd)MgoR3miH3>BC^c5*$8@PnqZQ4; zxu)1qYC~m3X_FNePFBo}7EU(dq-A9(mJOQT(CgTJ-+eaT^YPs85#ROhwfEX<|DLn= zS

#v(2iuU)ZU1(Z7WHSCY?$&dhmDQe=pvwsZtt*U}Mlg=axkmp7-Ot01SUYg$gu zZjq4JioAi>ZHihQ^D$$sy1>ziI`%zTXtPQ>EGWg@yTsb#&g)dvaSx z_!Eyd1xyZmDKF*S)84=>r@b=tg6E8NA@E%0#H0(-8FICn7;`$LcTks=b?ShmebH6k z6mdEyEHAZb@_;TYqJ7G?(_YVer@cxpcz*O=P-hzRL&n8Sj6<2pCnad&$&iqf2U3=W z*k6|i+bQKic3s!_oIszwWodxDzeAqb-=WF^rfzxsnL7if=S^;y64A8#MCar3&RTkG zess32BWS~2CqskoIvEn!(hw#(bl&^5MUE!+=zS-UTA-%5k~fpA<+Oub;84!Jr^ zyC1CG-`tg6Q7|>Qvr~LEQ~qP}=B_lUnc@kUc(T{R&0VPyY&)q#kQ%(nGZE=2TNdU` zu9+fH)m`Idb5{y9ESJn20h_y$fKyKPx`Sz6$oVIOJuLM_S9{CXrQ#hB_U@1Qmg-b0 zY~qvQR%d_}HgQgb)fwW0a46F1>;p`lnsh;3@^o72!IK}ac=t!S_Q8`@FX_C-+H<S@A+9r{0QJABC4`|rrOVtr=sLp#p3`m*2I za;|mdyZBq5m2pT=?8I>xf!@=;69%j+K0AKb+51*(?Dy)q^S&RL<_*j|d8n>yS|4lZ zw};TWq@go1r)BC!V9g}&?FdR?wcZ?+^67EpgN^3nQLijq;7Rd)bUgpe;a}Pl{saTw zoNKb)KQ!oi@8k_r7M4j?OWV_Bl$(#s&^He(T!I{4x)=zD>0aQWYcjuubFIHxKQy%c zU`No%&-zWh)*6KhN1{6K2giH%!?%}nP3*Kh9mgdUy*UG9ZeBa*EK`+HT(;^slf&w?eZW ze{b$MG+$b>(HMYcAOshvlTG2 z@0AnnvsbLLZn2l;j`d}PiS&}1a9I~Fb>VV2LXJjAK%`VfN`90SM9J1DX^fKXQBoT% zbR4G9D+O^fJx(^nNqwA*jhEWKQrB13 z^_Tko(%4^``b%?vY3naV1EqNSKq(n0&56>ID0>s7HBt7ZN?WQNOqGB%$xD;zX;P9V zm1)wDCROQDlP*WoESIuE1wI0b2mRVsk3F%P}4fHVmgpzuXOb?dgFqwt)jZZ%v!1NrC z9P~(aumtpy>PUHzi+&eeqj%FiqH=aU7=f{o3J<5hH&}Ls%8*DI8zri3)FY#N`JihK zlG<=-kB~`GvMyRC#mb~Osf&}nsZyOTtyzxMd~DyedNIi1#i)vWNSHiG+>OnM58IGl zvO3&Jis!&i+ZJs2ftK_88O^i$Fe(XtbM()cHtP9+V|qR!;TALH0;jh6Bl z**8G)2FZp*=}eNA6gikKdoz8~Y|0(Ta{S*OEzJWQQ=wkRR1-`+|HZPy+%b1X%R$V~ zz9Mk$vtho)AxLbFmckg>H9)Epr6@@XQzSo4_GO7$JM1@KbCYg~mWmje6)SuD%fSIs zn<$ORQlB9izHFze{qUM&w>MgfkulCm9i8DgP!!|v1Ey^yhOhTJ`mz{NU;kqN9Ck%z zjBLnovLA|a)Wxx)wm&f|ligDi>*)Rxt#v*rH3?R?EyE8X1MQW^%GfwjV{XjtWcdoC z-ez1|4cnv6K6@v#R>ewbreppH)J&?0l`@kQnCy6y4{LeS`e!z`&=)5=2TFaKQ)&S= z873FSNkg0zn}Saxl~os`VC`F;9Xr6xV|I#Y7vC7_=i?Jw%te?$-9z~{%yu6SA1 zR~nGm67u&inQc$du{vvbC=qLrRdH^axX$ z;$?b2*@u)wU%!M$*q-Kim^lCHHd%Cae?C&J4LlR3> z!}~GKzaGhKHGO4!e<{Tb{mY3bpJ#!FzA`CI)T!7DFOy0?S&iz4zwWEsPF~PYc7f0T zy`4Z?v(TV~YsUYJauwD-`shL8=d4rzA5(Q zJ6M0q06CHD)ITQMsei9o%jX9;d=@%o<4Z|vS+sDV_|!KW|8{|-42t(RKlB0Ry^ue; z=hE-OA%`c(*#yZQBwdNJ+AD2%K~IwJlO!QojwMSqw(=BFyT(@)GIO5<=}M5XgXChO ztn*5{S89{wY?64B<$JvCr^wzE$G!anoq_aSOpscys5|cIx{57WohVzAMLk&ba{y^o zqU;2%U-v>J=U`2uG-XLnwiIWJy0`J6cj)Z8L{af~Jh_B&9pv@Cqc^f_eWEm@>~G_D zZY6C`@$}Z^c8R6)fpgVh|BkzO!z^SO-+8eJlU{m`b z^5{6yYOibseQNgeIkZ@VX5_B@u$Yb>^h!>K49S)vI2zVk*UbFINm7KqD*S7bA}vXh z;uN2#e?9xYKBbi=Nr~y5YX>=lRhA^GbWhs^W-m{YGNX=5aMTr0Kl_JGWmH!tNrh4W z1$|40RzY33>p#DwdUcYlGHS~huY$VGclx=1P+y%SRq*ZF_udF3?MjkX(4~J{{46as znMpM+*|A(|Or;uLZul6(D-GXc_-fdCckTP*XzMWKB_9pBk@5+)f5%gU`jec6B5e26 zK+3JD*gq0xG*fPe{QCUeT0WfWEc#jKWa^GWmwd^SMfn(X#8WAb?gVtJ?$6lDwC|1X zOQY+AZibb3$l~+=tWkbqlsLnxf(E;s1usJO{=7@CK*0v`dFV&4Gd%?IvSo?4Q4WV( zb16BLav#VuU!VOx7-{pQYn zTnVew(uZ3Jyv+^wEI>>=#KjR(q3*zmz_|}^o@Ga80}sFc5z`wXuTKd5l=61Szuh@{l! zXR@tNa(ZB=;g6bO*@e8nKUScJq6PAc;pzIxvKR8H!Si3DjaHQLQFzFV(LN~8yq@*~ z)7l_^(0K0Ol-nWCd~-`9<-?E{Y(Z`&oN7eapOQb!-kMUbce^Onab*Ft`C-1~3qbc}P!(wKV6t&n{c*;h1>G-4n9 zIqJT?ly^Z+KJ$nFrksuK^R4yHLpe9w&YF-AhWBo~N8?4HVaJUVj$6KROkQR!^*Hmm z%J4F9rU!;OdZcSU%{>w)1i(b%u{I_BPm0PaYfHee%P^(ftz;VE=1!>W=6s_XSvA*_*FVv=3k3 z$Cr(1;QTB}f`HTSWlo=SU7UIxg#GjdE>1lT#D4lh7pEQvW4GQXC9VW!z%_(BgM_?cma~#Gg)-Qj(i__kA7e_H^a`7Z?>^My5q8mB0+R^tqhDf>h(;)Z<#>vYs3po%uOE7TbeoxH#=i|G)A+-Nm?EGC!xw zntSklF3t*Zt>Blx-NmOt!xMEUuzQZ+y2LMk>t&AiYlD{)90%H6d^rt*Hvqd0;u^vH zocya?oc3@zdL zBjGb%tWhCb#Jd{j=M-pmaq4jvHa|xn?&8$rZ0x5mbaCo&koMD`&>Zb&imC9>i6b`8gE?xH#?MOzo$y0C(y)J%B47zYNVTP6Ie^`{`SI@O>`MEt;q20+i@> z5NCA1^05is^&1Bs?vY`gi_-y|@cj%3&WO&>8A2TYFLTr{`7X}#IJW!g+Y(LtyXOEd z5BxHS*Pn;DxHjnGtgz0-sc&;}>JPg(^@St(zy3L6R_Edrzjtwq`I7CIZW`>vt%mtI z#fJ3Yc|G`87pFyBUig)7adGO8xj3usbaCi?yfpVSu+GJ40JklE`nDeYpo=%51|DkN za~a2Pzw$?2{2-s2U-l?)!j*v@fA8X5NI2CaKaTN!72s>{WzGiRlfY$8ds}fpaekO; zrI7Fy;&Otn(8Xy0ryM_h9~Y+{7cYMLTo3-64_**pzkTyyA5IPC=j`4kJvd(K{q#I-I6ssBvRQ{O?ZNNq z!5{GFKI!sLke_?-al`y8EFSL97xv)CdhqXh@ca?(@>oIjehE>hkp5579rj+`@Gjn1&g&sh`56S~*lGv|I48(@#0JJ{N9-#RzZ>aZV}`*IY&>-0fw z(RlLxh|}$Cd)&MmqIm=kH>r=~bz)qy{nWg{(fppo>Br3Yaw$&_wLhFU*qUmens?2} zP%mCZjPeP5ndarDGW;cQ-oSMi{t$e(eQR-|wcCE6_?l=QFe^;nH$y~90_+cq2U|z% zQ^kqVJkaljCDwW~+F~W!gKr(K)_d)lx6Y5_VVTXR_RcU7-cL-o_uM+r`ounRYoZE` zv@hK{Gn!}S?iMY_$!%MjJ%9fAD4qzsC>L zehg6TvEROJd^B&1yPGu^p5ZhbSu!5&m{BsqT4z6i|A*NxmL#ewkNsiEa8>THFO|HV z#A}gQ<7OUO%QJtsKShv_*hd!Jpk_zf!*5?6&978DP3FZIprMiWOSg|#zZz)&aQn%tJ(eR#}>_0fywsgi-xN) z*>>dO@z!~J#^M{4HOPKy@pyG_jQ#oIyYw%fnCW35`2 zX-AeVPcDF4-5EO2vAlfBFrEbC9jh|HeyVJ6G;d(KJF67QfdlOi%Lc25GVLGA)>x+B}jYQ;Xa)y}Yc9DN!$yp+Jm(gD&RwE<+`B)$Tf9ixKAw*P! zcmh!;Eyfavnr%rA@fk~UiTE2!9I1q#Q`9L-+K9N1Jxs*yB%%axkBIWH#r!!$TqGhe zLED>7{M?dR9=SqiC}IK(RuEe(!KI)!SWARK+~H}1h%ta6`~61}Lnm1}kVduGKRhzU z2PZFLQXqs3`#KO1LWE5(5jJy)a5A3=n+RgiHW9R-ovR_D93m5Re#B2`XAxK+3itYq zAkmE>!Y(h0Xpa{{J{siB4_nO(9%e)+gf@&w1W}kmyvQLU2u14=iJ}of52DZx*$(TI=<+#OqpmB7agdTwlqHeGl+@lR4a6wd=#pn#} z0D>g6Jp?h}8xeXCTN{T2kw7{>!htj*l0xV6Avj1UAVNqZLLKmB6b~F?gkp4qsL7U; z5&wpYh--8hgD8ANKvE7M zQK#WCkscE#B51BJvk^bGMg=o#WH^bBz(dWN_g4ii6v!^9()q(s<7)RVSX zuMy+V4W5R0@uET%V;MwcV%ZTt#3Uoa5yUTu`WOxpvlIgr@Hq#|k@y5QGa||(I6>6Y z7;_@*Am~AtLxh5;_b}!}d}2XV0w&=Vijfq$AVMQ_Mubu5f>p#Y#lQqnn=A$;h+1JW zTuVEIm@SP6pwJE>Y(hJPPz6yxVNw#|81HX5r+81J-oUaWqG5=-!1`xPnD8T7LWJjt zUJ$k4(ibx8ilJMI5DM)`v&o292ZSi$WrujD#ZU)P*@}@L+EK))X$KJUAj-ytF>x6- zAR?CaQ6gG;jEJ7#MUu+LV}UqCA@qUkAK&bF1H|Qqz$LwI5eI<}hZrN`SyW8K{(uN5 zz043>r7Ji{gk$YQY$&`K;{L%4A9gt}cGMqn)FC3;0ugoE;bMa&J|iI1M0W{-C`9#B zjNH-{@ZyKF9Z?~=YkAS5{%J9&0#P0o<0bImQ~Lge9KjQsBPs&R4KIRt@G~88TiS6% zR%ku%Thvrbd^mXi0Y#l6(I`Y z2ndJ3B*degxX}`XK!}RLla`1MX)(ChV5@^ZL3{+_1mHOlo?axPMXS9IuOi+J2Z{Ky zSwn1wgGA)3C8DKmM3g^B9ETw>`obje+30#A8dyvWR#HN&z?hX%D8z}5( zf?gsD@(PG!(?~?uBkDwVaTBo~8w>F@%n2g8x|xWf;MEZAoFHQIB49?_2P%oTpeKmvfz`yL=m{cv0I@vU zff^zj!mB1a)IvlLAa+NmBQiwyz`hi&fAj$Fl+;b|oS24XL_Cgyxay(j2&B||H=;W5 z5gN;oXkp9^A{bS3M0$t{hdp9{#pn)E2eJN%LzN(+1J48<&4Gql(wXW*Lc5OO5Y>T6 z$PAz8s0~C^qJ}i^-(kxnf+A`|)b&_?#Kl;CM8tU@QUl+aEkXMNFGOv|n+*|THi?Kmy?}^k#&ja`Au2;u zEZ!H0uVQS8m6+wkftck)KKwA?hWJEfE=45eH-W5r<-Ii5b}Y ziJxMY6VsKfBjTH5 zCE_2kr4xty@Gzl}tQatXmSO`TVzwe^LR2&+6R|HQ6A>*qW)Q&>qV`~JWB|MHI3)%t zL7)WQpzx3(jziB7Q4T?+`jre(t9n@@I$A$78116F9APfH%iD=jO5Y%=Ki~xfhhKF+ z<^mBzg1`z<+c6i2596^-`~i<`;;*sJi7#R<5HDaZ5a9pZ4fY8l<^W<;^}8JcQ}sI?ZyLEwd85d#-b+LeB0@vdml!%6 zIk|-+*iCQY2-ZNTAciiB>mLOXlcyg#2tn4b*NEUiq&$N zVZAzd)5sPgUQTy;1`#_aqCXHjf}tS3j#WlX!fGKRe^EBqKgI~*fI35=IErH8e6*Mt zpqzv3-?8J77h_cr7h=aFp2w;nPQ*ir_%0?haX7}Fcnp&fm9avcRS)_D5zHd$O1%H~q3{5BR+zi zk@z-N1MyF2IdM8x12GXVvBY~Yd5NE36%Yq0L6C{45)37A112xA2@N7HNB<*;MATt) z4RIB^hWKYZ<%o08GsJahIq_oan_)NW2X#Ct6ru#P6}Zh~J{+K}^Rv!yu-F zzLS6&52??waZtbjhZ7&iLxospUwZNxRg!H#voSfS-*55q-rN4`#=&7Fn5lL6^M=@G zHYTf_C++x6$w5`QxID?V$8SngwVUk4fYx05u}x`#-q9#&U%M&B-nXewU>?{s`^!x! y>cNS2P<@&@KgAwipB6EzKqNl}5ge#F)m~6PP}MHA*VLy4?%E`ByZywbnEwUrpVdkL diff --git a/protocols/kia_v4.c b/protocols/kia_v4.c index 490ca70..c561ea8 100644 --- a/protocols/kia_v4.c +++ b/protocols/kia_v4.c @@ -16,7 +16,10 @@ struct SubGhzProtocolDecoderKiaV4 { SubGhzBlockDecoder decoder; SubGhzBlockGeneric generic; uint16_t header_count; - uint8_t short_pulse_count; + + uint8_t raw_bits[32]; + uint16_t raw_bit_count; + uint32_t encrypted; uint32_t decrypted; }; @@ -30,8 +33,7 @@ struct SubGhzProtocolEncoderKiaV4 { typedef enum { KiaV4DecoderStepReset = 0, KiaV4DecoderStepCheckPreamble, - KiaV4DecoderStepSaveDuration, - KiaV4DecoderStepCheckDuration, + KiaV4DecoderStepCollectRawBits, } KiaV4DecoderStep; const SubGhzProtocolDecoder kia_protocol_v4_decoder = { @@ -61,66 +63,87 @@ const SubGhzProtocol kia_protocol_v4 = { .encoder = &kia_protocol_v4_encoder, }; -static const uint32_t keeloq_nlf = 0x3A5C742E; - -static uint32_t keeloq_decrypt(uint32_t data, uint64_t key) { +// KeeLoq decrypt +static uint32_t keeloq_common_decrypt(uint32_t data, uint64_t key) { + uint32_t block = data; + uint64_t tkey = key; for(int i = 0; i < 528; i++) { - uint8_t nlf_idx = ((data >> 1) & 1) | ((data >> 8) & 2) | ((data >> 18) & 4) | - ((data >> 21) & 8) | ((data >> 27) & 16); - uint8_t nlf_bit = (keeloq_nlf >> nlf_idx) & 1; - - uint8_t key_bit = (key >> (15 - (i % 64))) & 1; - uint8_t msb = (data >> 31) & 1; - uint8_t bit0 = (data >> 0) & 1; - uint8_t bit16 = (data >> 16) & 1; - - uint8_t new_bit = nlf_bit ^ msb ^ bit0 ^ bit16 ^ key_bit; - - data = (data >> 1) | (new_bit << 31); + int lutkey = ((block >> 0) & 1) | ((block >> 7) & 2) | + ((block >> 17) & 4) | ((block >> 22) & 8) | ((block >> 26) & 16); + int lsb = ((block >> 31) ^ ((block >> 15) & 1) ^ + ((0x3A5C742E >> lutkey) & 1) ^ ((tkey >> 15) & 1)); + block = ((block & 0x7FFFFFFF) << 1) | lsb; + tkey = ((tkey & 0x7FFFFFFFFFFFFFFFULL) << 1) | (tkey >> 63); } - return data; + return block; } -static uint8_t kia_v4_reverse8(uint8_t byte) { +static uint8_t reverse8(uint8_t byte) { byte = (byte & 0xF0) >> 4 | (byte & 0x0F) << 4; byte = (byte & 0xCC) >> 2 | (byte & 0x33) << 2; byte = (byte & 0xAA) >> 1 | (byte & 0x55) << 1; return byte; } -static void kia_v4_extract_fields(SubGhzProtocolDecoderKiaV4* instance) { - uint64_t inverted = ~instance->decoder.decode_data; - - instance->generic.data = inverted; - instance->generic.data_count_bit = instance->decoder.decode_count_bit; - - uint8_t b[8]; - for(int i = 0; i < 8; i++) { - b[i] = (instance->generic.data >> (56 - i * 8)) & 0xFF; +static void kia_v4_add_raw_bit(SubGhzProtocolDecoderKiaV4* instance, bool bit) { + if(instance->raw_bit_count < 256) { + uint16_t byte_idx = instance->raw_bit_count / 8; + uint8_t bit_idx = 7 - (instance->raw_bit_count % 8); + if(bit) { + instance->raw_bits[byte_idx] |= (1 << bit_idx); + } else { + instance->raw_bits[byte_idx] &= ~(1 << bit_idx); + } + instance->raw_bit_count++; + } +} + +static bool kia_v4_process_buffer(SubGhzProtocolDecoderKiaV4* instance) { + if(instance->raw_bit_count < 64) { + return false; } - instance->generic.serial = - ((uint32_t)kia_v4_reverse8(b[7] & 0xF0) << 24) | - ((uint32_t)kia_v4_reverse8(b[6]) << 16) | - ((uint32_t)kia_v4_reverse8(b[5]) << 8) | - (uint32_t)kia_v4_reverse8(b[4]); + uint8_t* b = instance->raw_bits; - instance->generic.btn = (kia_v4_reverse8(b[7]) & 0xF0) >> 4; + // Extract fields + uint32_t encrypted = ((uint32_t)reverse8(b[3]) << 24) | + ((uint32_t)reverse8(b[2]) << 16) | + ((uint32_t)reverse8(b[1]) << 8) | + (uint32_t)reverse8(b[0]); - instance->encrypted = - ((uint32_t)kia_v4_reverse8(b[3]) << 24) | - ((uint32_t)kia_v4_reverse8(b[2]) << 16) | - ((uint32_t)kia_v4_reverse8(b[1]) << 8) | - (uint32_t)kia_v4_reverse8(b[0]); + uint32_t serial = ((uint32_t)reverse8(b[7] & 0xF0) << 24) | + ((uint32_t)reverse8(b[6]) << 16) | + ((uint32_t)reverse8(b[5]) << 8) | + (uint32_t)reverse8(b[4]); - instance->decrypted = keeloq_decrypt(instance->encrypted, kia_v4_mf_key); - instance->generic.cnt = instance->decrypted & 0xFFFF; + uint8_t btn = (reverse8(b[7]) & 0xF0) >> 4; - FURI_LOG_D(TAG, "Raw: %016llX Bits: %u Inv: %016llX", - instance->decoder.decode_data, instance->decoder.decode_count_bit, inverted); - FURI_LOG_D(TAG, "Sn: %07lX Btn: %X Enc: %08lX Dec: %08lX Cnt: %04lX", - instance->generic.serial, instance->generic.btn, instance->encrypted, - instance->decrypted, instance->generic.cnt); + // Decrypt with master key + uint32_t decrypted = keeloq_common_decrypt(encrypted, kia_v4_mf_key); + uint8_t dec_btn = (decrypted >> 28) & 0x0F; + uint8_t dec_serial_lsb = (decrypted >> 16) & 0xFF; + uint8_t our_serial_lsb = serial & 0xFF; + + // Validate + if(dec_btn != btn || dec_serial_lsb != our_serial_lsb) { + return false; + } + + // Valid decode + instance->encrypted = encrypted; + instance->decrypted = decrypted; + instance->generic.serial = serial; + instance->generic.btn = btn; + instance->generic.cnt = decrypted & 0xFFFF; + + uint64_t key_data = ((uint64_t)b[0] << 56) | ((uint64_t)b[1] << 48) | + ((uint64_t)b[2] << 40) | ((uint64_t)b[3] << 32) | + ((uint64_t)b[4] << 24) | ((uint64_t)b[5] << 16) | + ((uint64_t)b[6] << 8) | (uint64_t)b[7]; + instance->generic.data = key_data; + instance->generic.data_count_bit = 64; + + return true; } void* kia_protocol_decoder_v4_alloc(SubGhzEnvironment* environment) { @@ -128,9 +151,6 @@ void* kia_protocol_decoder_v4_alloc(SubGhzEnvironment* environment) { SubGhzProtocolDecoderKiaV4* instance = malloc(sizeof(SubGhzProtocolDecoderKiaV4)); instance->base.protocol = &kia_protocol_v4; instance->generic.protocol_name = instance->base.protocol->name; - instance->encrypted = 0; - instance->decrypted = 0; - instance->short_pulse_count = 0; return instance; } @@ -144,12 +164,9 @@ void kia_protocol_decoder_v4_reset(void* context) { furi_assert(context); SubGhzProtocolDecoderKiaV4* instance = context; instance->decoder.parser_step = KiaV4DecoderStepReset; - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; instance->header_count = 0; - instance->short_pulse_count = 0; - instance->encrypted = 0; - instance->decrypted = 0; + instance->raw_bit_count = 0; + memset(instance->raw_bits, 0, sizeof(instance->raw_bits)); } void kia_protocol_decoder_v4_feed(void* context, bool level, uint32_t duration) { @@ -158,75 +175,65 @@ void kia_protocol_decoder_v4_feed(void* context, bool level, uint32_t duration) switch(instance->decoder.parser_step) { case KiaV4DecoderStepReset: - if(level) { - if(DURATION_DIFF(duration, kia_protocol_v4_const.te_long) < kia_protocol_v4_const.te_delta) { - instance->decoder.parser_step = KiaV4DecoderStepCheckPreamble; - instance->header_count = 1; - instance->short_pulse_count = 0; - } + if(level && DURATION_DIFF(duration, kia_protocol_v4_const.te_short) < kia_protocol_v4_const.te_delta) { + instance->decoder.parser_step = KiaV4DecoderStepCheckPreamble; + instance->decoder.te_last = duration; + instance->header_count = 1; } break; case KiaV4DecoderStepCheckPreamble: if(level) { - if(DURATION_DIFF(duration, kia_protocol_v4_const.te_long) < kia_protocol_v4_const.te_delta) { - instance->header_count++; - instance->short_pulse_count = 0; - } else if(DURATION_DIFF(duration, kia_protocol_v4_const.te_short) < kia_protocol_v4_const.te_delta) { - instance->short_pulse_count++; + if(DURATION_DIFF(duration, kia_protocol_v4_const.te_short) < kia_protocol_v4_const.te_delta) { + instance->decoder.te_last = duration; } else if(duration > 1000 && duration < 1500) { - if(instance->header_count >= 6 && instance->short_pulse_count >= 2) { - FURI_LOG_D(TAG, "Sync: %lu (hdr=%u, short=%u)", - duration, instance->header_count, instance->short_pulse_count); - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - instance->decoder.parser_step = KiaV4DecoderStepSaveDuration; + // Sync pulse + if(instance->header_count >= 8) { + instance->decoder.parser_step = KiaV4DecoderStepCollectRawBits; + instance->raw_bit_count = 0; + memset(instance->raw_bits, 0, sizeof(instance->raw_bits)); } else { instance->decoder.parser_step = KiaV4DecoderStepReset; } } else { instance->decoder.parser_step = KiaV4DecoderStepReset; } + } else { + if(DURATION_DIFF(duration, kia_protocol_v4_const.te_short) < kia_protocol_v4_const.te_delta && + DURATION_DIFF(instance->decoder.te_last, kia_protocol_v4_const.te_short) < kia_protocol_v4_const.te_delta) { + instance->header_count++; + } else if(duration > 1500) { + instance->decoder.parser_step = KiaV4DecoderStepReset; + } } break; - case KiaV4DecoderStepSaveDuration: + case KiaV4DecoderStepCollectRawBits: if(level) { - instance->decoder.te_last = duration; - instance->decoder.parser_step = KiaV4DecoderStepCheckDuration; - } else { - if(duration > 1500) { - if(instance->decoder.decode_count_bit >= kia_protocol_v4_const.min_count_bit_for_found) { - kia_v4_extract_fields(instance); + // HIGH pulse - PWM data bit (short=0, long=1) + if(duration > 1000 && duration < 1500) { + // Next sync pulse - end this packet + if(kia_v4_process_buffer(instance)) { if(instance->base.callback) instance->base.callback(&instance->base, instance->base.context); } instance->decoder.parser_step = KiaV4DecoderStepReset; - } - } - break; - - case KiaV4DecoderStepCheckDuration: - if(!level) { - if(DURATION_DIFF(instance->decoder.te_last, kia_protocol_v4_const.te_short) < kia_protocol_v4_const.te_delta) { - instance->decoder.decode_data <<= 1; - instance->decoder.decode_count_bit++; - instance->decoder.parser_step = KiaV4DecoderStepSaveDuration; - } else if(DURATION_DIFF(instance->decoder.te_last, kia_protocol_v4_const.te_long) < kia_protocol_v4_const.te_delta) { - instance->decoder.decode_data <<= 1; - instance->decoder.decode_data |= 1; - instance->decoder.decode_count_bit++; - instance->decoder.parser_step = KiaV4DecoderStepSaveDuration; + } else if(DURATION_DIFF(duration, kia_protocol_v4_const.te_short) < kia_protocol_v4_const.te_delta) { + kia_v4_add_raw_bit(instance, false); + } else if(DURATION_DIFF(duration, kia_protocol_v4_const.te_long) < kia_protocol_v4_const.te_delta) { + kia_v4_add_raw_bit(instance, true); } else { - if(instance->decoder.decode_count_bit >= kia_protocol_v4_const.min_count_bit_for_found) { - kia_v4_extract_fields(instance); + instance->decoder.parser_step = KiaV4DecoderStepReset; + } + } else { + // LOW pulse - check for end gap + if(duration > 1500) { + if(kia_v4_process_buffer(instance)) { if(instance->base.callback) instance->base.callback(&instance->base, instance->base.context); } instance->decoder.parser_step = KiaV4DecoderStepReset; } - } else { - instance->decoder.parser_step = KiaV4DecoderStepReset; } break; } @@ -259,26 +266,15 @@ void kia_protocol_decoder_v4_get_string(void* context, FuriString* output) { furi_assert(context); SubGhzProtocolDecoderKiaV4* instance = context; - uint8_t b[8]; - for(int i = 0; i < 8; i++) { - b[i] = (instance->generic.data >> (56 - i * 8)) & 0xFF; - } - uint64_t yek = 0; - for(int i = 0; i < 8; i++) { - yek |= ((uint64_t)kia_v4_reverse8(b[7-i]) << (56 - i * 8)); - } - furi_string_cat_printf( output, "%s %dbit\r\n" "Key:%016llX\r\n" - "Yek:%016llX\r\n" "Sn:%07lX Btn:%X Cnt:%04lX\r\n" "Enc:%08lX Dec:%08lX\r\n", instance->generic.protocol_name, instance->generic.data_count_bit, instance->generic.data, - yek, instance->generic.serial, instance->generic.btn, instance->generic.cnt,