diff --git a/code/Candle_receiver/Candle_receiver.arduino.avr.nano.elf b/code/Candle_receiver/Candle_receiver.arduino.avr.nano.elf new file mode 100644 index 0000000..e1cd65d Binary files /dev/null and b/code/Candle_receiver/Candle_receiver.arduino.avr.nano.elf differ diff --git a/code/Candle_receiver/Candle_receiver.arduino.avr.nano.hex b/code/Candle_receiver/Candle_receiver.arduino.avr.nano.hex new file mode 100644 index 0000000..b5f155a --- /dev/null +++ b/code/Candle_receiver/Candle_receiver.arduino.avr.nano.hex @@ -0,0 +1,945 @@ +:100000000C94E4010C9426100C94FF0F0C940C0239 +:100010000C940C020C940C020C944D100C940C02D9 +:100020000C940C020C940C020C940C020C940C0218 +:100030000C940C020C940C020C940C020C940C0208 +:100040000C94B50F0C940C020C94830F0C945D0F60 +:100050000C940C020C940C020C940C020C940C02E8 +:100060000C940C020C940C0200407A10F35A00A07D +:10007000724E18090010A5D4E80000E87648170071 +:1000800000E40B54020000CA9A3B000000E1F505B1 +:10009000000080969800000040420F000000A086FB +:1000A00001000000102700000000E803000000002D +:1000B0006400000000000A000000000001000000D1 +:1000C00000002C76D888DC674F0823DFC1DFAE59EB +:1000D000E1B1B796E5E3E453C63AE651997696E87E +:1000E000E6C28426EB898C9B62ED407C6FFCEFBC02 +:1000F0009C9F40F2BAA56FA5F490055A2AF75C932D +:100100006B6CF9676DC11BFCE0E40D47FEF520E662 +:10011000B500D0ED902E0300943577050080841E45 +:10012000080000204E0A000000C80C333333330FA0 +:10013000986E12831141EF8D2114893BE65516CF3D +:10014000FEE6DB18D1844B381BF77C1D901DA4BB49 +:10015000E424203284725E228100C9F124ECA1E5FE +:100160003D2725753B25753B25753B25753B25753D +:100170003B25730A00000000080002010000030490 +:10018000070000000000000000637C777BF26B6FCB +:10019000C53001672BFED7AB76CA82C97DFA5947B5 +:1001A000F0ADD4A2AF9CA472C0B7FD9326363FF742 +:1001B000CC34A5E5F171D8311504C723C3189605D1 +:1001C0009A071280E2EB27B27509832C1A1B6E5A2C +:1001D000A0523BD6B329E32F8453D100ED20FCB1CC +:1001E0005B6ACBBE394A4C58CFD0EFAAFB434D33A4 +:1001F0008545F9027F503C9FA851A3408F929D38BE +:10020000F5BCB6DA2110FFF3D2CD0C13EC5F9744A6 +:1002100017C4A77E3D645D197360814FDC222A906C +:100220008846EEB814DE5E0BDBE0323A0A4906245B +:100230005CC2D3AC629195E479E7C8376D8DD54E39 +:10024000A96C56F4EA657AAE08BA78252E1CA6B4D5 +:10025000C6E8DD741F4BBD8B8A703EB5664803F659 +:100260000E613557B986C11D9EE1F8981169D98E86 +:10027000949B1E87E9CE5528DF8CA1890DBFE642ED +:100280006841992D0FB054BB1652096AD53036A576 +:1002900038BF40A39E81F3D7FB7CE339829B2FFFBD +:1002A00087348E4344C4DEE9CB547B9432A6C22308 +:1002B0003DEE4C950B42FAC34E082EA16628D92478 +:1002C000B2765BA2496D8BD12572F8F66486689888 +:1002D00016D4A45CCC5D65B6926C704850FDEDB947 +:1002E000DA5E154657A78D9D8490D8AB008CBCD3A1 +:1002F0000AF7E45805B8B34506D02C1E8FCA3F0F45 +:1003000002C1AFBD0301138A6B3A9111414F67DC03 +:10031000EA97F2CFCEF0B4E67396AC7422E7AD352F +:1003200085E2F937E81C75DF6E47F11A711D29C5A2 +:10033000896FB7620EAA18BE1BFC563E4BC6D27917 +:10034000209ADBC0FE78CD5AF41FDDA8338807C79A +:1003500031B11210592780EC5F60517FA919B54A5D +:100360000D2DE57A9F93C99CEFA0E03B4DAE2AF599 +:10037000B0C8EBBB3C83539961172B047EBA77D688 +:1003800026E169146355210C7D000000002500283A +:10039000002B0000000000240027002A00040404B1 +:1003A000040404040402020202020203030303031E +:1003B00003010204081020408001020408102001FB +:1003C000020408102000B01311241FBECFEFD8E0A4 +:1003D000DEBFCDBF11E0A0E0B1E0E0EAFAE302C089 +:1003E00005900D92AA35B107D9F724E0AAE5B1E04E +:1003F00001C01D92A43DB207E1F711E0C4EED1E0C7 +:1004000004C02197FE010E94301DC33ED107C9F7E9 +:100410000E945D140C944E1D0C94000090E0FC01B1 +:10042000EF54FC4F249183569C4FFC018491882308 +:1004300099F090E0880F991FFC01ED56FC4FA591B3 +:10044000B491FC01E757FC4F859194918FB7F894D4 +:10045000EC91E22BEC938FBF0895DB01FC01242F7C +:100460003C91308311963C911197318312963C91C7 +:100470001297328313963C9113973383245014962A +:100480003496243068F72CEF240F2695269530E01B +:100490002F5F3F4F220F331F220F331F4370DB01AB +:1004A000A20FB31FFC01E20FF31F415018F08D9112 +:1004B0008193FBCF0895AF92BF92CF92DF92EF92DC +:1004C000FF920F931F93CF93DF936C017B018B01FE +:1004D000040F151FEB015E01AE18BF08C017D1074E +:1004E00059F06991D601ED91FC910190F081E02DD8 +:1004F000C6010995892B79F7C501DF91CF911F912D +:100500000F91FF90EF90DF90CF90BF90AF90089544 +:10051000FC01538D448D252F30E0842F90E0821B09 +:10052000930B541710F0CF96089501970895FC018E +:10053000918D828D981761F0A28DAE0FBF2FB11DE6 +:100540005D968C91928D9F5F9F73928F90E008953E +:100550008FEF9FEF0895FC01918D828D981731F0F8 +:10056000828DE80FF11D858D90E008958FEF9FEF4C +:100570000895FC01918D228D892F90E0805C9F4F22 +:10058000821B91098F739927089587EB92E00E944F +:10059000B90221E0892B09F420E0822F0895FC01A3 +:1005A000A48DA80FB92FB11DA35ABF4F2C91848DD4 +:1005B00090E001968F739927848FA689B7892C9331 +:1005C000A089B1898C91837080648C93938D848D84 +:1005D000981306C00288F389E02D80818F7D808387 +:1005E0000895EF92FF920F931F93CF93DF93EC0147 +:1005F00081E0888F9B8D8C8D98131AC0E889F989CA +:10060000808185FF15C09FB7F894EE89FF896083CC +:10061000E889F98980818370806480839FBF81E04D +:1006200090E0DF91CF911F910F91FF90EF9008958F +:10063000F62E0B8D10E00F5F1F4F0F731127E02E6A +:100640008C8D8E110CC00FB607FCFACFE889F989A2 +:10065000808185FFF5CFCE010E94CF02F1CFEB8DD7 +:10066000EC0FFD2FF11DE35AFF4FF0829FB7F89476 +:100670000B8FEA89FB8980818062CFCFCF93DF9394 +:10068000EC01888D8823B9F0AA89BB89E889F989AA +:100690008C9185FD03C0808186FD0DC00FB607FCDF +:1006A000F7CF8C9185FFF2CF808185FFEDCFCE0112 +:1006B0000E94CF02E9CFDF91CF91089580E090E0D2 +:1006C000892B29F00E94C50281110C940000089525 +:1006D0002FB7F8946091AF027091B0028091B1028F +:1006E0009091B2022FBF089508958091480281FD34 +:1006F00004C08160826080934802089588E19FE091 +:100700000FB6F894A895809360000FBE9093600098 +:1007100008950F931F930E94680300913202109175 +:1007200033022091340230913502601B710B820B31 +:10073000930B683E73408105910558F00E94680351 +:1007400060933202709333028093340290933502A7 +:10075000A8951F910F9108950895FC0183818770DA +:100760008860838384818F71806284836783CF01F3 +:100770000895E0913602F0913702309721F002801F +:10078000F381E02D099408959C01809136029091A7 +:10079000370282179307F1F0909140029F71909376 +:1007A00040023093370220933602E0913602F091F6 +:1007B0003702309721F00190F081E02D09950E94D9 +:1007C0006803609338027093390280933A029093E1 +:1007D0003B02089580914002805E80934002E5CF05 +:1007E0000F931F930E946803009138021091390201 +:1007F00020913A0230913B02601B710B820B930BEC +:10080000209141022077203779F000E117E220E0C3 +:1008100030E0061717072807390758F481E191E0FF +:100820001F910F910C94C40300E61AEE20E030E013 +:10083000F0CF1F910F9108958091400284608093C2 +:10084000400280914102807880934102E09145020C +:10085000F0914602309709F009940895CF93DF9301 +:10086000FC01EB01DA019C01205F3F4F88819C91E4 +:1008700089278083898111969C911197892781838B +:100880008A8112969C911297892782838B81139675 +:100890009C91139789278383349624961496E217A4 +:1008A000F30721F7DF91CF910895FC01DB019C0153 +:1008B000205F3F4F80819C918927808381811196A1 +:1008C0009C91119789278183828112969C9112971E +:1008D00089278283838113969C9113978927838329 +:1008E00034961496E217F30729F708958091790357 +:1008F000829586958770813019F480917C030895E4 +:10090000811105C08CE793E00E94EB16089580E00A +:100910000895E0E4F2E08081877F8F7E80831092EB +:100920003C0210923D0210923E0210923F020E9441 +:10093000680360932E0270932F028093300290938D +:1009400031021092060210920702109208021092D1 +:10095000090244E050E070E060E08AE292E00C942A +:10096000BF1C0F931F93CF93DF93EC01E0E0611561 +:10097000710551F0FB0101900020E9F73197E61B6A +:10098000F70BE931F10500F50E2F10E08B81877030 +:10099000980193E0220F331F9A95E1F7822B8B8306 +:1009A0008C818F718C83EE2329F0A801CE010796EC +:1009B0000E943F1DFE01E00FF11F1782CE01DF9163 +:1009C000CF911F910F910895E9E1DECF91E59CBD94 +:1009D00091E09DBD2998E3E2F0E03197F1F78EBDFB +:1009E00000000DB407FEFDCF8EB5FB019FEF415017 +:1009F000C8F0222361F09EBD00000DB407FEFDCFBC +:100A00008EB56115710599F380833196F0CFDF01C2 +:100A1000119680818EBD00000DB407FEFDCF8EB50E +:100A2000FD01E5CF299AE3E2F0E03197F1F708956F +:100A3000CF93DF931F92CDB7DEB7698320E041E00B +:100A4000BE016F5F7F4F0E94E6040F90DF91CF9150 +:100A5000089580914102982F9077903759F0982F00 +:100A600092959F709F5F97709295907F8F78892B5A +:100A700080934102809140028B7F8F7E8093400261 +:100A80002A986CE080E20C9418056FE080E20E94E6 +:100A90001805609108016F3F19F08AE20E9418055D +:100AA0002A9A089521E041E070E060E00C94E604A9 +:100AB00020E040E070E060E00C94E6042F923F926A +:100AC0004F925F926F927F928F929F92AF92BF925E +:100AD000CF92DF92EF92FF920F931F93CF93DF930A +:100AE000CDB7DEB7A2970FB6F894DEBF0FBECDBF6D +:100AF0007C01FC0102818091400280FFFAC080E00D +:100B00000F3F09F0D1C11FEF80912A02F7018083C6 +:100B1000038102FD01C1069506950695095FF091D6 +:100B20002D02F9A3F170FAA3402F013208F040E240 +:100B300050E0B7018AE092E00E94361D10929004C6 +:100B400010929104109292041092930410929404C3 +:100B500010929504109296041092970410929804A3 +:100B60001092990410929A0410929B0410929C0483 +:100B700010929D0410929E0410929F04109288047B +:100B80001092890410928A0410928B0410928C04A3 +:100B900010928D0410928E0410928F04B0E19B2E5F +:100BA000013110F000E2902EC92CC294FFE0CF2258 +:100BB000D12CAAE0AA2EA2E0BA2E7E0181E1E80E95 +:100BC000F11C0BE1B50180E994E00E94550480918D +:100BD000960390919703892B09F41EC148E953E0CD +:100BE00060E974E0C7010E942E0488248394482D94 +:100BF00050E08091960390919703481759070CF0A5 +:100C00008DC09701DE0111966FE0362E7AE0472EF7 +:100C1000E5E05E2EF701E50DF11D708084E0850DA5 +:100C20008F70582EF701E40DF11D808194E0940D32 +:100C30009F70492EF701E30DF11D908164E0630D73 +:100C40006F70362EF9016081E62FF0E0E757FE4F16 +:100C50007491E72DF0E0E757FE4F6491E82FF0E044 +:100C6000E757FE4F8491E92FF0E0E757FE4F94914C +:100C7000672E660C77FD6026E62FEE0F67FDE027F6 +:100C8000282E220C87FD2026792E770C97FD7026C2 +:100C9000F62FF827F927F625FE27FC93F72FF827DC +:100CA000F927FE27F2251196FC93119767279627BF +:100CB0002926272412962C921297862768267624B6 +:100CC00013967C9213972C5F3F4F1496EA16FB06FF +:100CD00009F0A0CF94E0440F551F9A95E1F74856CC +:100CE0005C4FBE016F5F7F4FC7010E942E0483944B +:100CF0007ECF10912B02002309F406CF0F3F09F499 +:100D000002CF802F90E003960E94CF1C182F8F3FB8 +:100D100009F0FACE102FF8CE00E201CFE989F0E019 +:100D2000E757FE4FE491E98BED89F0E0E757FE4F7E +:100D3000E491ED8BE98DF0E0E757FE4FE491E98F08 +:100D4000ED8DF0E0E757FE4FE491ED8F8A89EE8953 +:100D5000F0E0E757FE4FE491EA8BEA8DF0E0E757C9 +:100D6000FE4FE491EE8BEE8DF0E0E757FE4FE491FD +:100D7000EA8FE82FF0E0E757FE4FE491EE8F8B8982 +:100D8000EB8DF0E0E757FE4FE491EB8BE82FF0E0BE +:100D9000E757FE4FE491EB8F8F89EF8DF0E0E75737 +:100DA000FE4FE491EF8BE82FF0E0E757FE4FE49120 +:100DB000EF8F88A1EC8DF0E0E757FE4FE491E8A3B8 +:100DC000E88DF0E0E757FE4FE491EC8FEC89F0E01E +:100DD000E757FE4FE491E88FE82FF0E0E757FE4F2A +:100DE000E491EC8B84E0440F551F8A95E1F7485657 +:100DF0005C4FB70180E994E00E942E0440E160E975 +:100E000074E0C5010E942D02F0E1AF0EB11C81E03B +:100E1000C81AD10809F0D6CE2A9883E092E001974B +:100E2000F1F76EE080E20E9418058BE891E00197EF +:100E3000F1F7612F8AE20E941805612F80E30E947A +:100E4000180581EE0E9458051F3F19F0EAA180EABB +:100E5000E11180EB20E0492D6AE072E00E94E60497 +:100E60002A9AF12CE12C8FEF0E945805082F8073ED +:100E700021F4F1E0EF1AF108B1F72A9860E387E274 +:100E80000E94180504FF03C081EE0E9458050E94CD +:100E9000450505FF02C081E089A381E01F3F09F0FD +:100EA00080E099A19170892BA2960FB6F894DEBFCD +:100EB0000FBECDBFDF91CF911F910F91FF90EF90AB +:100EC000DF90CF90BF90AF909F908F907F906F906A +:100ED0005F904F903F902F9008952091400222FD07 +:100EE0000C945E0580E00895CF92DF92EF92FF921E +:100EF000C0900602D0900702E0900802F09009022C +:100F0000C114D104E104F10431F50E946803609337 +:100F100006027093070280930802909309021092D0 +:100F200055038FEF8093560380935A0384E1809397 +:100F3000590380915803807E83608093580367E54E +:100F400071E084E593E00E94B104FF90EF90DF90A0 +:100F5000CF900C945E050E9468036C197D098E0980 +:100F60009F0961387F448241910578F6FF90EF90A8 +:100F7000DF90CF9008958F929F92AF92BF92CF92C1 +:100F8000DF92EF92FF920F931F93CF93DF93CDB732 +:100F9000DEB764970FB6F894DEBF0FBECDBF8E01EB +:100FA0000F5F1F4F80E1F80111928A95E9F760E128 +:100FB00070E08BE291E00E943017AC016BE271E0CF +:100FC000C8010E94361D8AE090E0909397038093B9 +:100FD000960340E1B80188E993E00E942D0250E0B9 +:100FE00040E080E190E0FF24F39430E1E32EBE0185 +:100FF0006B5E7F4FEBE1BE2ED82EFC01EC56FC4F12 +:101000009E012F5E3F4FD901C190CD92A617B70721 +:10101000D9F7DE1020C0D988F0E1EF0EEA89F0E0C0 +:10102000E757FE4FE491EF25E98BEB89F0E0E757B6 +:10103000FE4FE491EA8BEC89F0E0E757FE4FE49134 +:10104000EB8BED2DF0E0E757FE4FE491EC8BF7FED4 +:1010500010C1FF0CFB24FC01E856FC4F4F01F901C5 +:10106000A1909F016401C81AD90AC40ED51EF601C9 +:10107000D080DA24F401D1924F012A173B0779F787 +:1010800004964C5F5F4F803B910509F0B5CF80E13E +:10109000F80111928A95E9F7229A219A2A98299AB9 +:1010A0001FB7F89480910502811146C087EA93E04A +:1010B000FC0124914BEB53E0FA013491E22FF0E074 +:1010C000EE0FFF1FED56FC4FA591B4912C913223EA +:1010D00021F5EFE7F1E02491FA014491FC019491AC +:1010E0009923D9F0222359F0233009F4CFC008F016 +:1010F000C2C0213009F4D0C0223009F4D1C0E92F98 +:10110000F0E0EE0FFF1FE757FC4FA591B4918FB7AA +:10111000F8949C91492B4C938FBF8AE00E940E0259 +:101120008CB580618CBD8CB580648CBD8DE00E94D7 +:101130000E028BE00E940E02809105028F5F809369 +:1011400005021FBF2A986EE080E20E9418058BE41A +:1011500096E40197F1F763E083E20E9418056FE5DA +:1011600084E20E9418056CE485E20E94180567E09D +:1011700086E20E94180564E08DE30E9418058DE167 +:101180000E945205843041F063E780E50E94180513 +:1011900064E08DE30E94180586E00E9452058730C6 +:1011A00009F08BC085E00E9452058C3409F085C09F +:1011B00062E082E20E94180560E081E20E94180568 +:1011C00063E08CE30E9418058FEF8093030120E019 +:1011D00045E063E071E08BE20E94E60420E045E038 +:1011E00063E071E08AE20E94E60420E045E063E00B +:1011F00071E080E30E94E60482EE0E94580581EED1 +:101200000E94580560E787E20E941805809140021D +:1012100080618093400280912D028E7F80932D0209 +:1012200010922B0210922C0210922A0210920801A6 +:1012300063E082E20E94180561E081E20E941805E5 +:101240000E94450589E091E00E94C40364960FB6B0 +:10125000F894DEBF0FBECDBFDF91CF911F910F91EC +:10126000FF90EF90DF90CF90BF90AF909F908F90C6 +:101270000895FF0CF0CE2730B1F02830D1F02430A3 +:1012800009F03DCF809180008F7D03C08091800068 +:101290008F778093800033CF84B58F7784BD2FCF35 +:1012A00084B58F7DFBCF8091B0008F778093B000A5 +:1012B00026CF8091B0008F7DF9CF8DE091E0C4CF33 +:1012C0000F931F93CF93DF93EC018C8182958695CA +:1012D0008770A9F44B8146954695469550E0BE012E +:1012E000695F7F4F82ED91E00E943F1DEB81E695A3 +:1012F000E695E695F0E0EE52FE4F10820AC081308E +:1013000009F044C08F814AE062ED71E090E00E94F4 +:1013100062178C8122ED31E03F932F939D811F92C4 +:101320009F9384FB992790F91F929F9387701F9238 +:101330008F938E811F928F9389811F928F9382E664 +:1013400091E09F938F931F9288E78F93CAE5D1E036 +:10135000DF93CF930E94D51709900020E9F7AE01E3 +:101360004B5551406AE571E087EB92E00E945B02C9 +:10137000EDB7FEB772960FB6F894FEBF0FBEEDBF85 +:1013800081E0DF91CF911F910F910895823031F468 +:101390004AE062ED71E08F819885B9CF833041F4E6 +:1013A0004AE062ED71E08F8198850E94BB17B1CF52 +:1013B000843051F46F81788589859A852AE042EDE1 +:1013C00051E00E946D17A5CF853051F46F8178856B +:1013D00089859A852AE042ED51E00E94991799CFBC +:1013E000873051F1A2EDB1E09E01295F3F4F40E00F +:1013F000EB81E695E695E6954E1780F5B901F90182 +:101400008081082E000C990B54E0959587955A958C +:10141000E1F78F708A30F0F4805D8C932F5F3F4F3F +:10142000FB0180818F708A30B8F4805D11968C93B7 +:1014300011974F5F1296DCCF2B85293008F028E0FA +:101440006F81788589859A8502ED11E042E00E94DE +:10145000CD165FCF895CE1CF895CE8CFF0E0EE0F7D +:10146000FF1F49CFFC012281211102C00C946009A9 +:101470000C946D07E4E5F3E0118212828FEF86830E +:1014800082E185838481807E848365E371E0CF017E +:101490000E94B1040C94320A0F931F93CF93DF93F1 +:1014A00090917A03C0917603C111B5C09D3059F473 +:1014B00088E198E00FB6F894A895809360000FBE7D +:1014C00090936000FFCF9B31A1F09630C1F49091D2 +:1014D0007C0381E0992319F09D3409F080E080932A +:1014E000470241E050E063E071E087E492E00E944F +:1014F000D71C81E0DF91CF911F910F910895933117 +:1015000019F40E943A0AF5CF923171F50E946803EE +:10151000009138021091390220913A0230913B0239 +:10152000AB01BC01401B510B620B730B10925503B6 +:10153000109256038FEF80935A0386E180935903EC +:1015400083EA809358038091570387708062809369 +:10155000570340935B0350935C0360935D03709368 +:101560005E0384E593E04EC0923019F2913011F59C +:1015700080E090E0892B09F4BCCF809179038295BB +:1015800086958770853059F460917C0370917D0356 +:1015900080917E0390917F030E940000AACF811169 +:1015A00005C08CE793E00E940917F6CF60E070E079 +:1015B000CB01F2CF9A3049F580917C03833409F056 +:1015C00098CFC3E0D0E06FEFCE010E94E71C2196D8 +:1015D000C33081E0D807B9F7109255031092560333 +:1015E0008FEF80935A038AE08093590380915803C8 +:1015F000807E8360809358036BE371E084E593E021 +:101600000E94B1040E94320A74CF81E09C3109F437 +:1016100071CF80E06FCF9A31E1F70E94760410928B +:101620005503C09356039FEF90935A039BE1909309 +:10163000590390915703977098609093570393E2E2 +:101640009093580391E0823008F490E090935B030C +:1016500088CF2F923F924F925F926F927F928F929C +:101660009F92AF92BF92CF92DF92EF92FF920F9331 +:101670001F93CF93DF93CDB7DEB7E9970FB6F894FA +:10168000DEBF0FBECDBF8091400284FFFDC30E942C +:10169000680300912E0210912F02209130023091A8 +:1016A0003102601B710B820B930B613A7B4B8D40B7 +:1016B0009105A0F00E94680360932E0270932F02A0 +:1016C000809330029093310286E00E945205873069 +:1016D00009F4C3C18DE091E00E94C40336E03BAB46 +:1016E0001BE187E10E94520580FDCEC34BA941500A +:1016F0004BAB442309F4C8C321E041E070E060E053 +:1017000080E60E94E604082F813220F082EE0E94DB +:10171000580500E021E0402F65E773E081E60E9474 +:10172000E60460E487E20E9418051092900410928B +:1017300091041092920410929304109294041092C7 +:1017400095041092960410929704109298041092A7 +:10175000990410929A0410929B0410929C04109287 +:101760009D0410929E0410929F041092880410927F +:10177000890410928A0410928B0410928C041092A7 +:101780008D0410928E0410928F04013108F46CC104 +:1017900082E090E09AAB89AB25E7222E23E0322E3F +:1017A00029A93AA9215031093AAB29AB2F3F3F4F24 +:1017B00009F478C140E1B101CE0181960E942D0269 +:1017C00040919603509197034115510509F46AC160 +:1017D00094E0440F551F9A95E1F748565C4FB101CC +:1017E000CE0141960E942E04E989F0E0E757FD4FB3 +:1017F000E491E98BED89F0E0E757FD4FE491ED8B43 +:10180000E98DF0E0E757FD4FE491E98FED8DF0E0D1 +:10181000E757FD4FE491ED8F8E8DEA8DF0E0E757AD +:10182000FD4FE491EE8FEE89F0E0E757FD4FE49134 +:10183000EA8FEA89F0E0E757FD4FE491EE8BE82F5D +:10184000F0E0E757FD4FE491EA8B8B89EB8DF0E0F8 +:10185000E757FD4FE491EB8BE82FF0E0E757FD4FA2 +:10186000E491EB8F8F89EF8DF0E0E757FD4FE49126 +:10187000EF8BE82FF0E0E757FD4FE491EF8F8C8975 +:10188000E88DF0E0E757FD4FE491EC8BEC8DF0E054 +:10189000E757FD4FE491E88FE8A1F0E0E757FD4FEF +:1018A000E491EC8FE82FF0E0E757FD4FE491E8A3D7 +:1018B000009196031FAA1EAA8FEF800FE82FF0E079 +:1018C000F9AFE8AF015009F4D4C048AD59AD2EA925 +:1018D0003FA9421B530B84E0440F551F8A95E1F743 +:1018E00048565C4FBE016F5E7F4FCE0101960E944D +:1018F0002E04CE0101967C019E012F5E3F4F3DAB31 +:101900002CABA5E0B0E0F701908181813281238189 +:10191000792F770F97FD7127B82EBB0C87FDB12665 +:10192000C32ECC0C37FDC126622F660F27FD612721 +:10193000472F440F77FD41274B2C440CB7FE04C0C2 +:101940005B2D550F452E41269C2C990CC7FC9126EA +:10195000A62EAA0C67FDA126D42EDD0C47FF04C0DD +:10196000542F550FD52ED126542D550F47FC5127F6 +:10197000F92DFF0F97FCF127EA2DEE0FA7FCE127C9 +:10198000792E7D24582E5526832E8F26622E6E2684 +:101990004D2554259F26AE26E32FE727EB25E42788 +:1019A000E525E925E625F0E0E757FD4F3491ECA960 +:1019B000FDA93083E22FEB25EC25E725E527E82577 +:1019C000EA25F0E0E757FD4FD490FD01EF70FF27C7 +:1019D00021E130E02C0F3D1FE20FF31FD0829C2548 +:1019E00096274927542695246924E62DF0E0E757E9 +:1019F000FD4F6490FD013596EF70FF2721E130E047 +:101A00002C0F3D1FE20FF31F6082E82FE727E62728 +:101A1000E7255E27E52FE825AE26EA2DF0E0E7571B +:101A2000FD4FA490FD013A96EF70FF27E20FF31FE0 +:101A3000A08294E0E90EF11C2CA93DA92C5F3F4F38 +:101A40003DAB2CAB1496A531B10509F05CCF8EA946 +:101A50009FA901969FAB8EAB35CF85E00E945205C2 +:101A60008C3409F037CE3ACEE1E0F0E0FAABE9ABE6 +:101A700093CE48E953E0BE016F5E7F4FC1010E94E3 +:101A80002E0460E974E0C1010E94550440E1BE01EA +:101A90006F5D7F4F80E994E00E942D0290E1290E56 +:101AA000311C7ECEF09078038F2D8370823009F048 +:101AB00018CEC090790300917A03E0907603A0904D +:101AC0007503B090770380912A02E81631F08E2DCD +:101AD00090E06A2D03960E94E71CEC2DE770CE2E55 +:101AE0008091410280688093410290912A02B9124C +:101AF000C9C080917803282F2695269526952A31EE +:101B000008F029E1F8E02F9F900111248770822BC3 +:101B100080937803EF2DE695E695E695EA3108F097 +:101B2000E9E1F0E0EB58FC4F17828091790383FFE5 +:101B300015C021E2E5E7F3E0A4E5B3E001900D92E2 +:101B40002A95E1F7877F806180935803909355032E +:101B5000E092560384E593E00E946D07809179033B +:101B600084FD82C023E0C2127FC090917A039F302F +:101B7000E9F018F0923108F4B4CD083109F03FC013 +:101B800010925503E09256038FEF80935A0389E138 +:101B90008093590380915803807E8360809358031B +:101BA00061E084E593E00E94AD0357C09091760315 +:101BB00010927603909377039FEF90937B03807E40 +:101BC00083608093790367E571E085E793E00E9485 +:101BD000B10480917803877080618093780380914D +:101BE00079038F71806C8093790381E080937C030B +:101BF00010927D0385E793E00E94320A72CD09318D +:101C000061F48091400283FF6CCD877F8093400216 +:101C10000E9476048093420264CD0E3109F461CDB6 +:101C20000D31F1F410925503109256038FEF80930B +:101C30005A038FE18093590383E480935803809182 +:101C40005703877080618093570380E09FEF9093E4 +:101C50005C0380935B0384E593E00E945E0541CDC5 +:101C60000E944C0A81113DCD85E793E00E946009F6 +:101C700080E090E0892B09F434CD85E793E00E9461 +:101C800000002FCD3FEFB312E7C043E0E413D2C012 +:101C90008091400282FFCBC0073009F0C8C080911C +:101CA0002B02E81609F41DCD0E94680340903C0207 +:101CB00050903D0260903E0270903F026419750999 +:101CC00086099709603177428105910530F180914D +:101CD000400283FB002700F983FD03CD90912A0287 +:101CE00091114AC01092420280914002877F8093F6 +:101CF0004002009142020F3F09F4F3CC0E946803B6 +:101D000060933C0270933D0280933E0290933F02A9 +:101D100080912C02801300932C020E946803609330 +:101D20003C0270933D0280933E0290933F020E94DA +:101D300068034B015C0123E09222AA24BB240E9489 +:101D400076132B013C0181149104A104B10409F024 +:101D500062C010925503E09256038FEF80935A03AE +:101D600088E08093590380915803807E836080933C +:101D7000580360912C0215CF886080934002B09286 +:101D800042021092550310925603B0925A0388E112 +:101D90008093590380915803807E83608093580319 +:101DA00061E084E593E00E94AD030E945E050E941D +:101DB00068034B015C0110927A030E946803681962 +:101DC00079098A099B09603D77408105910508F0F2 +:101DD0008BCF011189CF0E94290BA895809179039F +:101DE0008770833051F701E080917A03893129F3BC +:101DF00000E0E3CF31E0831A9108A108B10848EE72 +:101E0000440E43E0541E611C711C81149104A10412 +:101E1000B10409F498CF0E947613641975098609F4 +:101E20009709683E73408105910520F78CCF0830F3 +:101E300009F457CC80912B02A8120AC0E91641F090 +:101E40008091400282FF04C085E793E00E945E0516 +:101E5000F3E0CF1209CF45CC8091400282FF41CC04 +:101E600023E0E2130EC088EE800F823050F40E940F +:101E700076048E3F31F061E0680F85E793E00E94C1 +:101E8000AD0385E793E0E9CEE9960FB6F894DEBF9F +:101E90000FBECDBFDF91CF911F910F91FF90EF90BB +:101EA000DF90CF90BF90AF909F908F907F906F907A +:101EB0005F904F903F902F9008951F920F920FB612 +:101EC0000F9211242F933F934F935F936F937F93C0 +:101ED0008F939F93AF93BF93EF93FF9387EB92E022 +:101EE0000E94CF02FF91EF91BF91AF919F918F918F +:101EF0007F916F915F914F913F912F910F900FBE06 +:101F00000F901F9018951F920F920FB60F921124E9 +:101F10002F938F939F93EF93FF93E091C702F091DC +:101F2000C8028081E091CD02F091CE0282FD1BC0FB +:101F300090818091D0028F5F8F732091D1028217A0 +:101F400041F0E091D002F0E0E954FD4F958F80938D +:101F5000D002FF91EF919F918F912F910F900FBE23 +:101F60000F901F9018958081F4CF1F920F920FB69B +:101F70000F9211242F933F938F939F93AF93BF930F +:101F80008091AF029091B002A091B102B091B202E3 +:101F90003091AE0223E0230F2D3758F50196A11D95 +:101FA000B11D2093AE028093AF029093B002A09334 +:101FB000B102B093B2028091B3029091B402A091A9 +:101FC000B502B091B6020196A11DB11D8093B30276 +:101FD0009093B402A093B502B093B602BF91AF91B3 +:101FE0009F918F913F912F910F900FBE0F901F9057 +:101FF000189526E8230F0296A11DB11DD2CF1F927E +:102000000F920FB60F9211242F933F934F935F932C +:102010006F937F938F939F93AF93BF93EF93FF93B0 +:10202000E0911701F09118010995FF91EF91BF918F +:10203000AF919F918F917F916F915F914F913F9160 +:102040002F910F900FBE0F901F9018951F920F9217 +:102050000FB60F9211242F933F934F935F936F937B +:102060007F938F939F93AF93BF93EF93FF93E091F1 +:102070001501F09116010995FF91EF91BF91AF9174 +:102080009F918F917F916F915F914F913F912F9190 +:102090000F900FBE0F901F9018951F920F920FB6C2 +:1020A0000F9211240F900FBE0F901F901895DB0117 +:1020B000FC0158E0149744E018960E9001924A955E +:1020C000E1F75A95C1F708958F929F92AF92BF9210 +:1020D000CF92DF920F931F93EDB7FEB7B8970FB66D +:1020E000F894EDBF0FBEFEBF9F938F9331968F0183 +:1020F00049015A016B01C801AAD2BB2011F4AA20E0 +:1021000059F0C801B601A1D030E4C30ED11C32E0B1 +:10211000931AA108B108F1CFC801B601A40115D0E6 +:102120008F919F91B801C3DFEDB7FEB7B8960FB698 +:10213000F894EDBF0FBEFEBF1F910F91DF90CF90BF +:10214000BF90AF909F908F900895523090F09F93E2 +:102150008F937F936F935F934F9377D04F915F915E +:102160006F917F918F919F91525030E4630F711D59 +:10217000ECCFEDB7FEB70FB6E054F040F894EDBFEA +:102180000FBEFEBF3196242F26952695269550FB2F +:1021900025F9DB01222329F0122E0D9001921A94C9 +:1021A000E1F730E8032E37E0342329F006943A951E +:1021B000E9F73C91032A01922395293308F421C0C1 +:1021C00030E4321B19F011923A95E9F7FF973197F5 +:1021D000BF01FF93EF939F938F935F934F9335D0FE +:1021E0004F915F918F919F91EF91FF91DC019196BB +:1021F0003C9132503D9326E03C9130403D932A95EE +:10220000D9F738E3321B21F0112411923A95E9F7FE +:10221000DC01909638960D90040E02920D90051EEA +:10222000029236E00D90011C02923A95D9F7F89788 +:10223000BF010BD0EDB7FEB70FB6FF963196F894FD +:10224000EDBF0FBEFEBF112408954F925F926F92B3 +:102250007F928F929F92AF92BF92CF92DF92EF9236 +:10226000FF920F931F93CF93DF934DB75EB79A0101 +:10227000FB0140525140DA010FB6F8944DBF0FBE3A +:102280005EBF2F933F938F939F93119640E171917F +:102290006191319121912D933D936D937D934A9559 +:1022A000B1F740E3142EFD01FF9731970191119191 +:1022B00021913191C190D190E190F1906D2D7E2DC1 +:1022C0008F2D9C2D41E0E9D14B015C01B701C60186 +:1022D00042E0F9D186269726A826B92673E0F6941F +:1022E000E794D794C7947A95D1F78C249D24AE2493 +:1022F000BF24080D191D2A1D3B1D648D758D868D0B +:10230000978D060F171F281F391FC0A8D1A8E2A854 +:10231000F3A8B701C60141E0D6D15C014B0142E010 +:10232000D2D186269726A826B92672E0F694E7949D +:10233000D7947A95D9F78D249E24AF24080D191DC2 +:102340002A1D3B1D0D931D932D933D931A9409F067 +:10235000AACFFF91EF91EF93FF9390E211901D921E +:102360009A95E1F79097ED01EEE4F5E2BA9590E4E5 +:10237000192E4C885D886E887F88688979898A895A +:102380009B89C88CD98CEA8CFB8C46225722682208 +:1023900079226095709580959095C622D722E82283 +:1023A000F9224C245D246E247F24F888C988DA88B9 +:1023B000EB88B601C70142E070D14B015C01B60168 +:1023C000C70143E080D186269726A826B926B70103 +:1023D000C60141E078D186269726A826B926480C62 +:1023E000591C6A1C7B1C8C8C9D8CAE8CBF8C480C41 +:1023F000591C6A1C7B1C8D909D90AD90BD90480C23 +:10240000591C6A1C7B1C85909590A590B590480C32 +:10241000591C6A1C7B1C688179818A819B818C8014 +:102420009D80AE80BF8086229722A822B922C884D0 +:10243000D984EA84FB846C217D218E219F2186260C +:102440009726A826B9266C817D818E819F816C217B +:102450007D218E219F2186269726A826B926C88011 +:10246000D980EA80FB80B601C70142E02CD18B0104 +:102470009C01B701C60143E010D106271727282782 +:1024800039276F2D7C2D8D2D9E2D42E006D10627FC +:10249000172728273927080D191D2A1D3B1D5CE128 +:1024A0006C969A919C835A95E1F7C888D988EA88F6 +:1024B000FB88C40CD51CE61CF71CC88AD98AEA8A9A +:1024C000FB8A040D151D261D371D088319832A83D9 +:1024D0003B831A9409F04DCFFF91EF9158E00081B2 +:1024E0001181228133816991799189919991060FA6 +:1024F000171F281F391F01931193219331935A9568 +:1025000071F7319652E066E04081450F41935527BF +:1025100028F44081451F41936A95D1F75F914F910F +:102520000FB6F8944DBF0FBE5EBF1124DF91CF915F +:102530001F910F91FF90EF90DF90CF90BF90AF90E1 +:102540009F908F907F906F905F904F900895982FFD +:102550008A4291443771CFFBC0B5A5DBB5E95BC2B8 +:102560005639F111F159A4823F92D55E1CAB98AA5D +:1025700007D8015B8312BE853124C37D0C55745D81 +:10258000BE72FEB1DE80A706DC9B74F19BC1C169FF +:102590009BE48647BEEFC69DC10FCCA10C246F2CD7 +:1025A000E92DAA84744ADCA9B05CDA88F976525124 +:1025B0003E986DC631A8C82703B0C77F59BFF30B3B +:1025C000E0C64791A7D55163CA0667292914850A31 +:1025D000B72738211B2EFC6D2C4D130D3853547327 +:1025E0000A65BB0A6A762EC9C281852C7292A1E85F +:1025F000BFA24B661AA8708B4BC2A3516CC719E8D7 +:1026000092D1240699D685350EF470A06A1016C1B1 +:10261000A419086C371E4C774827B5BCB034B30CEE +:102620001C394AAAD84E4FCA9C5BF36F2E68EE82C3 +:102630008F746F63A5781478C8840802C78CFAFF7A +:10264000BE90EB6C50A4F7A3F9BEF27871C6DC0122 +:10265000E0E6F6E268E275917D936A95E1F7089508 +:1026600067E6096A85AE67BB72F36E3C3AF54FA523 +:102670007F520E518C68059BABD9831F19CDE05B4F +:102680000000000000000000483038F0592F982F5B +:10269000872F762F652F4850F7CF552788944423EE +:1026A00039F0661F771F881F991F551F4A95C9F774 +:1026B000652B0895483038F0562F672F782F892FD3 +:1026C000952F4850F7CF55278894442339F0979594 +:1026D00087957795679557954A95C9F7952B0895EE +:1026E000AB01692F782F852F942F08953FB7F89469 +:1026F0008091B3029091B402A091B502B091B6025C +:1027000026B5A89B05C02F3F19F00196A11DB11D4C +:102710003FBFBA2FA92F982F8827BC01CD01620F88 +:10272000711D811D911D42E0660F771F881F991F43 +:102730004A95D1F70895CF93DF93FC01108211825F +:102740001282148215821682DC0117968AE1ED014D +:1027500019928A95E9F782E08383DF91CF910895FA +:10276000CF93DF93CDB7DEB728970FB6F894DEBFCF +:102770000FBECDBFE7EBF2E01382128288EE93E04A +:10278000A0E0B0E084839583A683B7838DE191E0D8 +:102790009183808385EC90E09587848784EC90E03A +:1027A0009787868780EC90E0918B808B81EC90E01E +:1027B000938B828B82EC90E0958B848B86EC90E0FF +:1027C000978B868B118E128E138E148E83EB94E072 +:1027D0000E949B1398E09E012F5F3F4FD901E92F84 +:1027E0001D92EA95E9F7E8E8F4E081E08083118240 +:1027F0001282138214821582168217821092970316 +:10280000109296031092A1041092A0041092A304B7 +:102810001092A204E8E9F3E040EFDF011D924A952F +:10282000E9F7888748E0F901A0E9B4E001900D924A +:102830004A95E1F748E0F901A8E9B4E001900D926A +:102840004A95E1F78093A40482E08093A50483E095 +:102850008093A60484E08093A70485E08093A80475 +:1028600086E08093A90487E08093AA049093AB0448 +:1028700089E08093AC048AE08093AD048BE0809380 +:10288000AE048CE08093AF048DE08093B0048EE0C2 +:102890008093B1048FE08093B20485E793E00E94B7 +:1028A0009B1384E593E028960FB6F894DEBF0FBE25 +:1028B000CDBFDF91CF910C949B13CF93DF93CDB716 +:1028C000DEB7CA55D1090FB6F894DEBF0FBECDBF33 +:1028D000789484B5826084BD84B5816084BD85B5FB +:1028E000826085BD85B5816085BD80916E00816007 +:1028F00080936E00109281008091810082608093AD +:1029000081008091810081608093810080918000AE +:102910008160809380008091B10084608093B100D9 +:102920008091B00081608093B00080917A008460D3 +:1029300080937A0080917A00826080937A008091FF +:102940007A00816080937A0080917A008068809319 +:102950007A001092C100A89580E090E0892B11F0D8 +:102960000E940000E091C702F091C80282E08083DB +:10297000E091C302F091C4021082E091C502F0918F +:10298000C60280E180831092CF02E091CB02F091E9 +:10299000CC0286E08083E091C902F091CA02808176 +:1029A00080618083E091C902F091CA0280818860D1 +:1029B0008083E091C902F091CA028081806880839F +:1029C000E091C902F091CA0280818F7D808380915D +:1029D00048028D7F8093480280E090E0892B11F0BF +:1029E0000E94000040E250E063E671E0CE010196F3 +:1029F0000E94BF1C40E150E06CE871E0CE0181967E +:102A00000E94BF1C49E050E063E871E0CE01C1962E +:102A10000E94BF1C41E050E06AE171E0CE01865A9D +:102A20009F4F0E94BF1C28EC31E040E050E0BE0107 +:102A30006F5F7F4FCE01CA960E94641041E050E064 +:102A400063E071E087E492E00E94BF1C6FEF81E0D9 +:102A500090E00E94E71C809141028F7880934102B0 +:102A6000109237021092360281E191E00E94C40375 +:102A700085E793E090934602809345020E946803A5 +:102A80000E9468030E94B9030E94290B80914002B2 +:102A900082FB882780F9A895882391F3109255032B +:102AA000109256038FEF80935A038EE08093590360 +:102AB00080915803807E8360809358036EE371E0B9 +:102AC00084E593E00E94B1040E9460090E943A0AE2 +:102AD0000E947E03A89570E0E72E70E0F72EE0E0FC +:102AE000CE2EE0E0DE2E77247A94A89587EB92E054 +:102AF0000E94B902892B09F4ADC087EB92E00E94D5 +:102B000097029091AD02933608F033C1E92FF0E0BF +:102B1000E75BFD4F8A3009F027C11082AE01465CA9 +:102B20005F4F68E571E089E492E00E943B17A12CB9 +:102B300010E000E0B12C812C912C009709F485C0A5 +:102B40000630110509F498C00330110509F44AC094 +:102B5000B4F401301105A9F102301105B9F10E9458 +:102B6000EB168093B5040F5F1F4FAE01465C5F4FBD +:102B700068E571E090E080E00E943B17DECF043012 +:102B8000110509F43FC00530110549F724E0FC01A7 +:102B9000A21249C08081882331F38A33C4F580535F +:102BA0008295807F91819A33C4F59053A1E0B0E083 +:102BB000AC0FBD1FAB0DB11D890F8C93B394329632 +:102BC000E9CF0E94EB168093B904CDCF0E94EB169B +:102BD000A82E8091B704887F9A2D9770892B8093B7 +:102BE000B704C1CF2091B704622EE89463F80E9425 +:102BF000EB16892B29F088E0682A6092B704B3CFDE +:102C000080E0FACF0E94EB168093B804ACCF8136F7 +:102C100014F08755C5CF8753C3CF913614F097551D +:102C2000C5CF9753C3CF01900020E9F7E81BE250CE +:102C3000E80FF92FF11D20812D3009F41082208139 +:102C40002A3009F410824C018ECF05301105A4F40E +:102C50001092AD020E94B9030E94290B0E948903C1 +:102C6000C114D10409F441CF0E94C502882309F49C +:102C70003CCF0E94000039CF1092B4041092B304EC +:102C80008091B7048F7E8093B70494E0A91266C048 +:102C90004B2D4A3108F049E19091B604977050E00D +:102CA0009A0163E0220F331F6A95E1F7922B90930C +:102CB000B6048F71806C8093B704BE016F5F7F4F45 +:102CC0008AEB94E00E94361D1092AD0281E2E3EBA4 +:102CD000F4E0A5E7B3E001900D928A95E1F78091C9 +:102CE0007703811155C08091790383FF17C091E26A +:102CF000E5E7F3E0A4E5B3E001900D929A95E1F7E2 +:102D0000877F8061809358031092550380917603EA +:102D10008093560384E593E00E94600980917903D3 +:102D20008770833061F580917A03823029F51092A3 +:102D300055031092560370925A0380935903809161 +:102D40005803807E83608093580365E371E084E5D7 +:102D500093E00E94B1040E9460097CCFB40183EB30 +:102D600094E00E94B104B0CF80839F5F9093AD0246 +:102D7000BDCE1092AD02BACE0E944C0A6BCFE114C8 +:102D8000F10409F467CF85E793E00E94000062CF69 +:102D900085E793E00E946D075DCFEF920F931F933D +:102DA000CF93DF93E80134E047FD34E1042E000CBB +:102DB000550B57FF03C0519541955109E32E022F42 +:102DC000242FAE010E94DF19CE01DF91CF911F9118 +:102DD0000F91EF900895FC0188279927E89421919D +:102DE0002032E9F3293010F02E30C8F32B3241F0B5 +:102DF0002D3239F4689404C00E949117820F911DFE +:102E0000219120532A30C0F31EF4909581959F4F55 +:102E100008951F93FC0199278827BC01E89411911C +:102E20001032E9F3193010F01E30C8F31B3251F0A4 +:102E30001D3249F4689406C00E947C17610F711D11 +:102E4000811D911D119110531A30B0F33EF49095ED +:102E50008095709561957F4F8F4F9F4F1F9108957B +:102E6000FC016150704001900110D8F78095909559 +:102E70008E0F9F1F0895FA01A191B081009719F458 +:102E80001097E1F0CD01DC01CD010D90002011F48F +:102E9000C00113C0FB012191222319F02015D9F79D +:102EA000F3CFFB012191201519F41E92119606C053 +:102EB0002223C1F70D900020A1F7D001FA01A193C0 +:102EC000B0830895BB274A3031F4992322F4BDE240 +:102ED000909581959F4F0C94BC17BB272A3051F4D5 +:102EE000992342F4BDE290958095709561957F4F4E +:102EF0008F4F9F4F0C949A17592F482F372F262FFB +:102F0000660F771F881F991F660F771F881F991FED +:102F1000620F731F841F951F660F771F881F991FED +:102F200008957AE0979F902D879F802D910D112411 +:102F30000895BB27FA01A62F621771058105910537 +:102F4000330B30FB66F0AA27660F771F881F991F87 +:102F5000AA1FA21710F0A21B63953850A9F7A05D15 +:102F6000AA3308F0A95DA19336F7B111B19310828D +:102F7000CA010C94211CBB27FB015527AA27880FE7 +:102F8000991FAA1FA41710F0A41B83955051B9F7DD +:102F9000A05DAA3308F0A95DA193009779F7B1115C +:102FA000B1931192CB010C94211CAEE0B0E0EBED9B +:102FB000F7E10C94071D0D891E898F89988D2EE0ED +:102FC0002C831A83098397FF02C080E090E8019761 +:102FD0009E838D83AE01455E5F4F698D7A8DCE01F4 +:102FE00001960E9405184D815E8157FD0AC02F8110 +:102FF0003885421753070CF49A01020F131FF8018A +:1030000010822E96E4E00C94231DABE0B0E0EBE0E0 +:10301000F8E10C94F91C7C013B018A01FC01178248 +:103020001682838181FFC8C1CE0101965C01F70140 +:103030009381F30193FD859193FF81913F01882353 +:1030400009F453C1853239F493FD859193FF819141 +:103050003F01853229F4B70190E00E94311CE7CF8F +:10306000912C212C312CFFE1F31538F08B3211F12A +:1030700090F4803209F1833229F137FC3CC020ED15 +:10308000280F2A3050F536FE20C08AE0989E200D89 +:103090001124922E06C08D3291F0803371F768941E +:1030A00030F8F30193FD859193FF81913F018111E8 +:1030B000DACF21C0689431F8689432F8F2CF68947E +:1030C00033F8EFCF689434F8ECCFEAE02E9E200D71 +:1030D0001124222E689435F8E4CF8E3229F436FC80 +:1030E00004C1689436F8DDCF8C3619F4689437F84B +:1030F000D8CF8836B1F2982F9F7D95549330E0F069 +:103100008336A1F18337C1F1833509F063C028010B +:10311000F2E04F0E511CF801C080D180692D70E0A3 +:1031200036FC02C06FEF7FEFC6010E94161C4C01F7 +:10313000689437F882010AC00C5F1F4FFFE3F983E0 +:1031400088248394912C6501E89437F833FE2DC0D0 +:10315000522C8114910471F5552009F468CFB70100 +:1031600080E290E00E94311C5A94F6CFF8018081F1 +:1031700089830E5F1F4FE4CF2801F2E04F0E511CF0 +:10318000F801C080D180692D70E036FC02C06FEF7D +:103190007FEFC6010E9430174C018201D5CFB701E5 +:1031A00080E290E00E94311C2A942814190409F04E +:1031B000B0F7CECFF60137FC859137FE81916F01D4 +:1031C000B70190E00E94311C51105A94F1E08F1A1F +:1031D0009108BFCF843619F0893609F077C0F8011D +:1031E00037FE6BC060817181828193810C5F1F4FBC +:1031F000F32DFF763F2E97FF09C09095809570952F +:1032000061957F4F8F4F9F4F689437F82AE030E0E9 +:10321000A5010E94611CC82ECA188C2C432C36FEB6 +:103220000CC0E89440F8C91440F434FE05C032FCE8 +:1032300003C0F32DFE7E4F2E892C44FE95C0FE0167 +:10324000EC0DF11D8081803309F087C0242D297E8B +:10325000422E842D8870582E43FC95C040FE8FC0AE +:103260009C2C821418F42C0C922C981844FE91C0BB +:10327000B70180E390E00E94311C42FE09C088E75C +:1032800090E041FE02C088E590E0B7010E94311C49 +:10329000C91408F48AC0CA94D12C9FEFC91AD90A5C +:1032A000CA0CDB1CF60182916F01B70190E00E940D +:1032B000311CAC14BD04B1F74FCF60817181072E72 +:1032C000000C880B990B0E5F1F4F92CFD32CE89404 +:1032D000D4F82AE030E0853739F1232D297FD22E2A +:1032E0008F3601F148F48835A9F0F701868197817E +:1032F0002B96E2E10C94151D803749F08837A9F729 +:10330000D4FE02C06894D2F820E130E00DC0689489 +:10331000D4F8F6CF34FE03C0822F8660D82E20E189 +:1033200032E002C028E030E0F801D7FE0FC0608133 +:103330007181828193810C5F1F4FA5010E94611CE6 +:10334000C82ECA183D2CE89437F867CF6081718188 +:1033500090E080E00E5F1F4FF0CF42FC02C08394EC +:1033600078CF8394839475CF842D867809F471CFB8 +:10337000F6CFB70180E290E00E94311C8394821462 +:10338000C0F3512C73CF522C5818821408F46ECF0E +:10339000F8CF842D867809F47BCF8BE241FE80E262 +:1033A00047FC8DE2B70190E071CFB70180E390E078 +:1033B0000E94311C9A946CCF8FEF9FEF99CFA9E0B8 +:1033C000B0E0E5EEF9E10C94FF1C6A01F22EB02E9C +:1033D000102FFBE3AF2EA01608F41BE31F5F012F95 +:1033E00027E0AE014F5F5F4F0E943E1BBC01898109 +:1033F000982F9970913009F43FC09BE2E1FC04C022 +:103400009E2D9170E0FC3AC05E2D5071E82FE8705F +:1034100083FF46C084E0911101C083E0EF2DE81BDB +:103420008F1508F0E0E0D6018E2F20E2552351F1F0 +:10343000992329F0D6019C93C60101966C0196014F +:103440002D5F3F4FE2FE24C08EE4D6018C9391E4C1 +:1034500011969C93119712968C93D9018E2F90E21E +:1034600081111BC0E20FF32FF11D10828EEF9FEF31 +:103470002996ECE00C941B1D9DE2C6CF90E2C4CFD0 +:103480002D9381508111FCCFCE0ED11CE0E0D0CF26 +:103490008EE6D6018C9391E6DBCF9D938150E0CFF1 +:1034A000482F447082FF3DC084E0911101C083E049 +:1034B0008F1510F4EF2DE81BD6018E2F20E2552337 +:1034C00009F1992329F0D6019C93C60101966C015C +:1034D00096012D5F3F4FE2FE1BC089E4D6018C931D +:1034E0008EE411968C93119786E412968C93D901F1 +:1034F0008E2F90E2882309F4B5CF9D938150FACFA7 +:103500002D9381508111FCCFCE0ED11CE0E0D9CF9C +:1035100089E6D6018C938EE611968C93119786E6F8 +:10352000E4CF9B0177FF02C030E020E0A1E0B0E0F3 +:10353000911102C0B0E0A0E0F9013196AE0FBF1FBB +:103540008B2C912CBB2009F461C05401EFEFAE1A13 +:10355000BE0AAA0DBB1DFA161B0619F014F04F2D5A +:103560004A1B511188C0E3FC59C07601A42FB0E278 +:10357000A1114FC0C40ED11C992309F45BC040E0D7 +:10358000D6011196F60190836D01552309F448C0C8 +:10359000012F060F9A81582F507184FF03C0913379 +:1035A00009F4015010160CF047C0093008F008E08B +:1035B000EEE2FE2E5B01A01AB10885019194819480 +:1035C00091082F3F320729F4D601FC92F60131967B +:1035D0006F016217730794F1021713077CF5DB0183 +:1035E000A21BB30BAA24A394B12CAC0EBD1EAA0D32 +:1035F000BB1D11968C9121503109D60111962815C9 +:103600003905F4F0F60180836D01DBCFB12CA12CDC +:10361000A0CFF701B1937F01A150AACF9111B0CFF4 +:10362000D601942F50E302C05D9391509111FCCFCD +:10363000C40ED11C40E0ACCF01E0BACF80E3DBCFB9 +:103640006217730731F4963388F4953311F45523D8 +:1036500069F0F6018083FD01842F90E2811108C09A +:10366000A40FB11D1C9290E080E002CF81E3F1CF66 +:1036700091938150F3CF911183CF8ACF283008F0F6 +:1036800027E03327DA01990F311D87FD91600096FD +:103690006105710539F432602E5F3D9330E32A9560 +:1036A000E1F708959F3F30F080387105610509F01A +:1036B0003C5F3C5F3D93913008F08068911DDF9343 +:1036C000CF931F930F93FF92EF92192F987F9695A8 +:1036D000E92F96959695E90FFF27EE53FF4F99270F +:1036E0003327EE24FF24A701E701059008940794EF +:1036F00028F4360FE71EF81E491F511D660F771F6D +:10370000881F991F0694A1F70590079428F4E70EE7 +:10371000F81E491F561FC11D770F881F991F661F6E +:103720000694A1F70590079428F4F80E491F561F38 +:10373000C71FD11D880F991F661F771F0694A1F719 +:103740000590079420F4490F561FC71FD81F990FE3 +:10375000661F771F881F0694A9F78491109517702C +:1037600041F0D695C79557954795F794E7941A95E4 +:10377000C1F7E8E6F0E068941590159135916591F0 +:10378000959105907FE27395E118F10A430B560B72 +:10379000C90BD009C0F7E10CF11E431F561FC91F0A +:1037A000D01D7EF4703311F48A95E6CFE894015071 +:1037B00030F0080F0AF40027021708F4202F239591 +:1037C000022F7A3328F079E37D932A95E9F710C028 +:1037D0007D932A9589F60694979567953795179561 +:1037E0001794E118F10A430B560BC90BD00998F056 +:1037F00023957E9173957A3308F070E37C932013C0 +:10380000B8F77E9170617D9330F0839571E37D937D +:1038100070E32A95E1F71124EF90FF900F911F912B +:10382000CF91DF91992787FD90950895FC01059030 +:10383000615070400110D8F7809590958E0F9F1FB2 +:103840000895DC01FC01672F71917723E1F732972E +:1038500004C07C916D9370836291AE17BF07C8F36B +:1038600008950F931F93CF93DF93182F092FEB0128 +:103870008B8181FD09C01FEF0FEF812F902FDF910A +:10388000CF911F910F91089582FF14C02E813F8127 +:103890008C819D81281739073CF4E881F981CF019B +:1038A00001969983888310838E819F8101969F83DF +:1038B0008E83E3CFE885F985812F0995892BA1F3C4 +:1038C000DACFFA01AA27283051F1203181F1E894AA +:1038D0006F936E7F6E5F7F4F8F4F9F4FAF4FB1E003 +:1038E0003ED0B4E03CD0670F781F891F9A1FA11DFE +:1038F000680F791F8A1F911DA11D6A0F711D811DFF +:10390000911DA11D20D009F468943F912AE0269FC3 +:1039100011243019305D3193DEF6CF010895462F22 +:103920004770405D4193B3E00FD0C9F7F6CF462F03 +:103930004F70405D4A3318F0495D31FD405241936C +:1039400002D0A9F7EACFB4E0A69597958795779529 +:103950006795BA95C9F700976105710508959B01B0 +:10396000AC010A2E06945795479537952795BA9539 +:10397000C9F7620F731F841F951FA01D0895DC01F6 +:10398000CB01FC01F999FECF06C0F2BDE1BDF89A6A +:10399000319600B40D9241505040B8F70895F9990E +:1039A000FECF92BD81BDF89A992780B50895DC01BC +:1039B000A40FB51F4150504048F0CB01840F951F14 +:1039C0002E910E94E81C41505040D0F70895262FB8 +:1039D000F999FECF92BD81BDF89A019700B4021605 +:1039E00039F01FBA20BD0FB6F894FA9AF99A0FBEB3 +:1039F00008952F923F924F925F926F927F928F9293 +:103A00009F92AF92BF92CF92DF92EF92FF920F936D +:103A10001F93CF93DF93CDB7DEB7CA1BDB0B0FB677 +:103A2000F894DEBF0FBECDBF09942A883988488834 +:103A30005F846E847D848C849B84AA84B984C884CA +:103A4000DF80EE80FD800C811B81AA81B981CE0FC1 +:103A5000D11D0FB6F894DEBF0FBECDBFED010895A6 +:103A6000EE0FFF1F0590F491E02D0994FB01DC019E +:103A700002C001900D9241505040D8F70895FB01CB +:103A8000DC014150504048F001900D920020C9F7F0 +:103A900001C01D9241505040E0F70895F894FFCFC7 +:103AA000FFFFFF00FCE1A8A8FF1C0474072905F034 +:103AB000038904BB077403740300000000F1025B78 +:103AC0000288023E03B9029702AB02736D61727401 +:103AD000686F6D6500322E332E31004F4B00476109 +:103AE0007465776179207374617274757020636F87 +:0A3AF0006D706C6574652E003B00DC +:00000001FF diff --git a/code/Candle_receiver/Candle_receiver.ino b/code/Candle_receiver/Candle_receiver.ino new file mode 100644 index 0000000..2637185 --- /dev/null +++ b/code/Candle_receiver/Candle_receiver.ino @@ -0,0 +1,105 @@ +/* +* +* The Candle receiver acts as the bridge between the Candle devices and the Candle Controller. +* +* It only allows communication with other Candle devices that use the same encryption password as it uses itself. +* When you install the Candle Manager, a random password is generated for you. If you ever want to change the encryption password used by your network, this can be done in the Candle Manager settings. +* Be warned that you will have to re-create this receiver as well as all your devices, since they will all need to have new code with the new password in it. +* +* If you have already installed the MySensors add-on, please temporarily disable it before creating this receiver. Otherwise the MySensors add-on may try to connect to it during the creation process, and thus disrupt it. +* +* +* SETTINGS */ + + +#define RF_NANO // RF-Nano. Enable this if you are using the RF-Nano Arduino, which has a built in radio. The Candle project uses the RF-Nano. + +/* END OF SETTINGS +* +* +* +*/ + + +// Enable MySensors debug output to the serial monitor, so you can check if the radio is working ok. +//#define MY_DEBUG + +#ifdef RF_NANO +// If you are using an RF-Nano, you have to switch CE and CS pins. +#define MY_RF24_CS_PIN 9 // Used by the MySensors library. +#define MY_RF24_CE_PIN 10 // Used by the MySensors library. +#endif + +// Enable and select radio type attached +#define MY_RADIO_RF24 +//#define MY_RADIO_NRF5_ESB +//#define MY_RADIO_RFM69 +//#define MY_RADIO_RFM95 + +// Set LOW transmit power level as default, if you have an amplified NRF-module and +// power your radio separately with a good regulator you can turn up PA level. +//#define MY_RF24_PA_LEVEL RF24_PA_MIN +//#define MY_RF24_PA_LEVEL RF24_PA_LOW +//#define MY_RF24_PA_LEVEL RF24_PA_HIGH +#define MY_RF24_PA_LEVEL RF24_PA_MAX + +// Mysensors advanced security +#define MY_ENCRYPTION_SIMPLE_PASSWD "smarthome" // The Candle Manager add-on will change this into the actual password your network uses. +//#define MY_SIGNING_SOFT_RANDOMSEED_PIN A7 // Setting a pin to pickup random electromagnetic noise helps make encryption more secure. + +// Mysensors advanced settings +//#define MY_RF24_CHANNEL 100 // In EU the default channel 76 overlaps with wifi, so you could try using channel 100. But you will have to set this up on every device, and also on the controller. You can even try 115. +//#define MY_RF24_DATARATE RF24_250KBPS // Slower datarate increases the range, but the RF-Nano does not support this slow speed. +#define MY_RF24_DATARATE RF24_1MBPS // This datarate is supported by pretty much all NRF24 radios, including the RF-Nano. +#define MY_SPLASH_SCREEN_DISABLED // Saves a little memory. + +// Enable serial gateway +#define MY_GATEWAY_SERIAL // This is the main function of this code. It tells the MySensors library to turn this device into a gateway for MySensors network. + +#include // The MySensors library, which takes care of creating the wireless network. +#include // The watchdog timer - if the device becomes unresponsive and doesn't periodically reset the timer, then it will automatically reset once the timer reaches 0. + +// Clock for the watchdog +#define INTERVAL 1000 // Every second we reset the watchdog timer. If the device freezes, the watchdog will not re reset, and the device will reboot. +unsigned long previousMillis = 0; // Used to run the internal clock + + +void setup() +{ + wdt_enable(WDTO_2S); // Starts the watchdog timer. If it is not reset at least once every 2 seconds, then the entire device will automatically restart. +} + + +void presentation() +{ + // The receiver does not have any extra children. +} + + +void loop() +{ + if(millis() - previousMillis >= INTERVAL){ // Main loop, runs every second. + previousMillis = millis(); // Store the current time as the previous measurement start time. + wdt_reset(); // Reset the watchdog timer + } +} + + +/** +* The MySensors Arduino library handles the wireless radio link and protocol +* between your home built sensors/actuators and HA controller of choice. +* The sensors forms a self healing radio network with optional repeaters. Each +* repeater and gateway builds a routing tables in EEPROM which keeps track of the +* network topology allowing messages to be routed to nodes. +* +* Created by Henrik Ekblad +* Copyright (C) 2013-2018 Sensnology AB +* Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors +* +* Documentation: http://www.mysensors.org +* Support Forum: http://forum.mysensors.org +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* version 2 as published by the Free Software Foundation. +*/ diff --git a/code/Carbon_sensor/Carbon_sensor.arduino.avr.nano.elf b/code/Carbon_sensor/Carbon_sensor.arduino.avr.nano.elf new file mode 100644 index 0000000..bfdeed7 Binary files /dev/null and b/code/Carbon_sensor/Carbon_sensor.arduino.avr.nano.elf differ diff --git a/code/Carbon_sensor/Carbon_sensor.arduino.avr.nano.hex b/code/Carbon_sensor/Carbon_sensor.arduino.avr.nano.hex new file mode 100644 index 0000000..169b827 --- /dev/null +++ b/code/Carbon_sensor/Carbon_sensor.arduino.avr.nano.hex @@ -0,0 +1,1419 @@ +:100000000C94E4040C9440210C9419210C94672165 +:100010000C9467210C9467210C94DF210C940C053F +:100020000C940C050C940C050C940C050C940C050C +:100030000C940C050C940C050C940C050C940C05FC +:100040000C94CF200C940C050C949D200C947720DC +:100050000C940C050C940C050C940C050C940C05DC +:100060000C940C050C940C05AED580A83FD3004031 +:100070008D142002A1C8DA12817FD9F1DB40A4A639 +:10008000AF2D4E6577206465736972656420747264 +:10009000616E736D697373696F6E207374617465DB +:1000A0003A20002D476F74206563686F003E3E2044 +:1000B000726563656976696E67206D6573736167E4 +:1000C0006500637C777BF26B6FC53001672BFED7D1 +:1000D000AB76CA82C97DFA5947F0ADD4A2AF9CA4D1 +:1000E00072C0B7FD9326363FF7CC34A5E5F171D841 +:1000F000311504C723C31896059A071280E2EB272F +:10010000B27509832C1A1B6E5AA0523BD6B329E351 +:100110002F8453D100ED20FCB15B6ACBBE394A4C31 +:1001200058CFD0EFAAFB434D338545F9027F503CB1 +:100130009FA851A3408F929D38F5BCB6DA2110FFDD +:10014000F3D2CD0C13EC5F974417C4A77E3D645DDA +:10015000197360814FDC222A908846EEB814DE5E67 +:100160000BDBE0323A0A4906245CC2D3AC629195BB +:10017000E479E7C8376D8DD54EA96C56F4EA657AF7 +:10018000AE08BA78252E1CA6B4C6E8DD741F4BBD98 +:100190008B8A703EB5664803F60E613557B986C145 +:1001A0001D9EE1F8981169D98E949B1E87E9CE5562 +:1001B00028DF8CA1890DBFE6426841992D0FB0540C +:1001C000BB16434F322050504D3A0021204E4F2055 +:1001D000434F4E4E454354494F4E00436F6E6E653C +:1001E0006374656420746F20676174657761792139 +:1001F00000434152424F4E000000050720600000BE +:1002000000000000005F00000007000700147F14DA +:100210007F14242A7F2A1223130864623649562049 +:10022000500008070300001C2241000041221C006E +:100230002A1C7F1C2A08083E08080080703000082D +:1002400008080808000060600020100804023E5101 +:1002500049453E00427F4000724949494621414993 +:100260004D331814127F1027454545393C4A4949FA +:100270003141211109073649494936464949291E64 +:100280000000140000004034000000081422411453 +:1002900014141414004122140802015909063E41A5 +:1002A0005D594E7C1211127C7F494949363E4141CD +:1002B00041227F4141413E7F494949417F09090926 +:1002C000013E414151737F0808087F00417F410092 +:1002D0002040413F017F081422417F404040407F41 +:1002E000021C027F7F0408107F3E4141413E7F098E +:1002F0000909063E4151215E7F09192946264949CF +:10030000493203017F01033F4040403F1F2040200E +:100310001F3F4038403F631408146303047804030C +:100320006159494D43007F414141020408102000BA +:100330004141417F0402010204404040404000032B +:1003400007080020545478407F28444438384444F7 +:100350004428384444287F385454541800087E09EF +:100360000218A4A49C787F0804047800447D40000F +:100370002040403D007F1028440000417F40007C29 +:10038000047804787C080404783844444438FC1821 +:1003900024241818242418FC7C0804040848545405 +:1003A000542404043F44243C4040207C1C20402032 +:1003B0001C3C4030403C44281028444C9090907C99 +:1003C0004464544C440008364100000077000000AB +:1003D0004136080002010204023C2623263C680044 +:1003E0001980400048656C6C6F2C204920616D209D +:1003F0006120636172626F6E2073656E736F72004D +:100400004F70696E696F6E00436172626F6E206437 +:10041000696F786964650044617461207472616E0B +:10042000736D697373696F6E00312E3000436172B2 +:10043000626F6E2073656E736F720070706D002155 +:1004400020436F727275707465642064617461001A +:1004500021204E6F6E2D73656E736F722064617410 +:100460006100212049732074686520434F32207356 +:10047000656E736F7220636F6E6E65637465643F43 +:100480000052657175657374696E6720646174618B +:100490002066726F6D20434F322073656E736F72EA +:1004A000206D6F64756C65002E0054002E00660090 +:1004B00046002E0057004E6F7420616C6C6F77659C +:1004C0006420746F2073656E642074686520434FE8 +:1004D0003220646174610042616400506F6F720089 +:1004E0004F4B00476F6F640047726561740053653E +:1004F0006E64696E6720434F322076616C75653A91 +:100500002000576169742E2E0042414400504F4F25 +:1005100052004F4B00474F4F440047524541540053 +:1005200044415441204552524F5200434845434BA9 +:1005300020574952450020207B7D7B7D7B7D2046D6 +:10054000616B6520434F322076616C75653A2000FF +:1005500066616B6520434F32206164646974696F22 +:100560006E20616674657220626F756E64732063BD +:100570006865636B3A20006C6173745F6D61783AF3 +:1005800020006C6173745F6D696E3A200066616B68 +:100590006520434F32206A69747465722061667405 +:1005A0006572206D6F76656D656E7420666163742B +:1005B0006F723A200061637475616C206D6F7665AF +:1005C0006D656E7420666163746F723A2000636FAC +:1005D000325F66616B655F646174615F6D6F7665E4 +:1005E0006D656E745F666163746F723A2000666C4D +:1005F000697070656420636F696E3A200066616B94 +:100600006520434F32206164646974696F6E3A20DB +:100610000046616B696E672068617320656E646572 +:10062000640073746F7070696E672066616B696EC9 +:10063000672C206D6F76656D656E742066616374DE +:100640006F722073657420746F202D302C393A201E +:100650000073746F7070696E672066616B696E6796 +:100660002C206D6F76656D656E7420666163746FA6 +:10067000722073657420746F20302C393A20006921 +:100680006E6974696174696E672066616B65206468 +:1006900061746100557365722077616E74732074A4 +:1006A0006F2067656E65726174652066616B652099 +:1006B000646174612E0061637475616C206D696E94 +:1006C0002D6D61782072616E67653A2000706F74DD +:1006D000656E7469616C206D696E2D6D6178207234 +:1006E000616E67653A20006E657720636F32207611 +:1006F000616C75657320776173206E6F7420626919 +:1007000067676572206F7220736D616C6C65722013 +:100710007468616E20726563656E74206D656173C7 +:100720007572656D656E74732E006E657720636FEC +:10073000322076616C756520776173206269676726 +:100740006572207468616E206D6178696D756D20C9 +:10075000666F756E642E006E657720636F3220764B +:10076000616C75652077617320736D616C6C657267 +:10077000207468616E206D696E696D756D20666F9D +:10078000756E642E0052657374617274696E6720B1 +:100790006D696E2D6D617820616E616C797369731E +:1007A0002061726F756E6420636F322076616C75A4 +:1007B00065206F6620006D6561737572656D656E8D +:1007C00074735F66616B656E6573735F72616E678C +:1007D000655F636F756E746572203D20006672659B +:1007E000736820636F322076616C75653A20005F14 +:1007F0005F7374617274696E675F5F0053656E64E6 +:10080000696E67206E65772064617461207472611F +:100810006E736D697373696F6E2073746174653A7A +:10082000200044617461207472616E736D6973732A +:10083000696F6E20627574746F6E20707265737369 +:100840006564200046414B45204441544120544FAB +:1008500047474C454420544F200052096AD5303652 +:10086000A538BF40A39E81F3D7FB7CE339829B2F41 +:10087000FF87348E4344C4DEE9CB547B9432A6C256 +:10088000233DEE4C950B42FAC34E082EA16628D9A3 +:1008900024B2765BA2496D8BD12572F8F664866826 +:1008A0009816D4A45CCC5D65B6926C704850FDED92 +:1008B000B9DA5E154657A78D9D8490D8AB008CBCE5 +:1008C000D30AF7E45805B8B34506D02C1E8FCA3FAB +:1008D0000F02C1AFBD0301138A6B3A9111414F67FB +:1008E000DCEA97F2CFCEF0B4E67396AC7422E7ADB3 +:1008F0003585E2F937E81C75DF6E47F11A711D295D +:10090000C5896FB7620EAA18BE1BFC563E4BC6D2F5 +:1009100079209ADBC0FE78CD5AF41FDDA833880712 +:10092000C731B11210592780EC5F60517FA919B50A +:100930004A0D2DE57A9F93C99CEFA0E03B4DAE2A6E +:10094000F5B0C8EBBB3C83539961172B047EBA7793 +:10095000D626E169146355210C7D00030C0F30335A +:100960003C3FC0C3CCCFF0F3FCFF000000002400EC +:1009700027002A000000000800020100000304070D +:10098000000000000000000000000000230026001E +:10099000290000000000250028002B0004040404A6 +:1009A0000404040402020202020203030303030319 +:1009B00001020408102040800102040810200102F6 +:1009C000040810201D25782711241FBECFEFD8E082 +:1009D000DEBFCDBF11E0A0E0B1E0EAEEF7E502C076 +:1009E00005900D92A43AB107D9F724E0A4EAB1E04A +:1009F00001C01D92A43FB207E1F714E0C3EED4E0BA +:100A000004C02197FE010E94C52BC23ED107C9F741 +:100A10000E9495260C94E82B0C940000E0E4F2E090 +:100A2000A089B18982E08C93A485B5851C92A685A6 +:100A3000B78580E18C93108EA489B58986E08C936C +:100A4000A289B3898C9180618C93A289B3898C919E +:100A500088608C93A289B3898C9180688C9302887A +:100A6000F389E02D80818F7D80830895DB01FC0177 +:100A7000242F3C91308311963C911197318312962B +:100A80003C911297328313963C91139733832450F1 +:100A900014963496243068F72CEF240F269526956B +:100AA00030E02F5F3F4F220F331F220F331F437061 +:100AB000DB01A20FB31FFC01E20FF31F415018F03E +:100AC0008D918193FBCF0895EF92FF920F931F9327 +:100AD000CF93DF938C017B01D0E0C0E0F701EC0FF6 +:100AE000FD1F6491662361F0D801ED91FC910190A6 +:100AF000F081E02DC8010995892B11F02196EECFE8 +:100B0000CE01DF91CF911F910F91FF90EF9008954B +:100B1000AF92BF92CF92DF92EF92FF920F931F930B +:100B2000CF93DF936C017B018B01040F151FEB0149 +:100B30005E01AE18BF08C017D10759F06991D60100 +:100B4000ED91FC910190F081E02DC6010995892B72 +:100B500079F7C501DF91CF911F910F91FF90EF9031 +:100B6000DF90CF90BF90AF900895FC01538D448DDE +:100B7000252F30E0842F90E0821B930B541710F048 +:100B8000CF96089501970895FC01918D828D981755 +:100B900061F0A28DAE0FBF2FB11D5D968C91928D2D +:100BA0009F5F9F73928F90E008958FEF9FEF08955E +:100BB000FC01918D828D981731F0828DE80FF11D27 +:100BC000858D90E008958FEF9FEF0895FC01918D42 +:100BD000228D892F90E0805C9F4F821B91098F733B +:100BE0009927089580E492E00E94E60521E0892B90 +:100BF00009F420E0822F0895FC01A48DA80FB92FDD +:100C0000B11DA35ABF4F2C91848D90E001968F7334 +:100C10009927848FA689B7892C93A089B1898C9153 +:100C2000837080648C93938D848D981306C00288A2 +:100C3000F389E02D80818F7D80830895EF92FF926C +:100C40000F931F93CF93DF93EC0181E0888F9B8DEF +:100C50008C8D98131AC0E889F989808185FF15C0A9 +:100C60009FB7F894EE89FF896083E889F9898081CC +:100C70008370806480839FBF81E090E0DF91CF919B +:100C80001F910F91FF90EF900895F62E0B8D10E0BD +:100C90000F5F1F4F0F731127E02E8C8D8E110CC02C +:100CA0000FB607FCFACFE889F989808185FFF5CF77 +:100CB000CE010E94FC05F1CFEB8DEC0FFD2FF11D55 +:100CC000E35AFF4FF0829FB7F8940B8FEA89FB89B4 +:100CD00080818062CFCFCF93DF93EC01888D882312 +:100CE000B9F0AA89BB89E889F9898C9185FD03C08F +:100CF000808186FD0DC00FB607FCF7CF8C9185FF74 +:100D0000F2CF808185FFEDCFCE010E94FC05E9CFB7 +:100D1000DF91CF91089580E090E0892B29F00E9427 +:100D2000F20581110C9400000895833081F028F4BD +:100D3000813099F08230A9F008958730A9F0883089 +:100D4000C9F08430B1F4809180008F7D03C0809120 +:100D500080008F7780938000089584B58F7784BD5D +:100D6000089584B58F7DFBCF8091B0008F778093FD +:100D7000B00008958091B0008F7DF9CFCF93DF93BD +:100D8000282F30E0F901EC58F64F8491F901E05535 +:100D9000F64FD491F901E456F64FC491CC23A1F05B +:100DA00081110E949506EC2FF0E0EE0FFF1FE8572F +:100DB000F64FA591B491EC91ED2381E090E009F418 +:100DC00080E0DF91CF91089580E090E0FACF1F930B +:100DD000CF93DF93282F30E0F901EC58F64F849140 +:100DE000F901E055F64FD491F901E456F64FC4915C +:100DF000CC23A9F0162F81110E949506EC2FF0E06C +:100E0000EE0FFF1FEE56F64FA591B4918FB7F894F1 +:100E1000EC91111108C0D095DE23DC938FBFDF91D8 +:100E2000CF911F910895DE2BF8CFCF93DF9390E001 +:100E3000FC01E055F64F24918456964FFC018491B5 +:100E40008823C9F090E0880F991FFC01E659F64FFE +:100E5000A591B491FC01EE56F64FC591D491611164 +:100E60000DC09FB7F8948C91209582238C93888134 +:100E7000282328839FBFDF91CF910895623051F4DA +:100E80009FB7F8943C91822F809583238C93E881BF +:100E90002E2BEFCF8FB7F894EC912E2B2C938FBF86 +:100EA000EACF3FB7F8948091380290913902A0912F +:100EB0003A02B0913B0226B5A89B05C02F3F19F01E +:100EC0000196A11DB11D3FBFBA2FA92F982F8827CA +:100ED000BC01CD01620F711D811D911D42E0660FA5 +:100EE000771F881F991F4A95D1F708958F929F9277 +:100EF000AF92BF92CF92DF92EF92FF926B017C0193 +:100F00000E9451074B015C01C114D104E104F104BA +:100F1000B9F00E945107681979098A099B09683E4E +:100F200073408105910580F321E0C21AD108E108E0 +:100F3000F10888EE880E83E0981EA11CB11CE4CF56 +:100F4000FF90EF90DF90CF90BF90AF909F908F90E9 +:100F500008952FB7F8946091340270913502809112 +:100F60003602909137022FBF08950895CF93DF93F3 +:100F7000EC018D85882329F081508D87DF91CF91F9 +:100F800008959C818E819817C8F788896827E88121 +:100F9000F9810084F185E02D42E0CE0109958C8134 +:100FA0008F5F8C83EBCF2091310230913202281772 +:100FB000390771F49091300280912F02981741F017 +:100FC000E0913002F0E0E151FE4F808190E0089521 +:100FD0008FEF9FEF08950895EF92FF920F931F9365 +:100FE000CF93DF93DC015C96ED90FC905D97E1146C +:100FF000F10479F481E090E013969C938E9312971C +:1010000090E080E0DF91CF911F910F91FF90EF90E2 +:1010100008955196ED91FC91529750968C9150976E +:10102000982F90950FB75E962C915E97122F1270A5 +:1010300021FD6095F8942081112319F1282B20833C +:10104000E7012197F1F728E0462F50E0308160FF5B +:101050001AC0382B3083E7012197F1F7BA01759553 +:101060006795215089F7112381F08081892380833E +:101070000FBF5C968D919C910197F1F781E090E014 +:10108000C1CF2923DCCF3923E5CF9081892BEFCF46 +:1010900020913102309132022817390771F4809182 +:1010A0002F022091300290E0805C9F4F821B9109BB +:1010B00060E470E00E949D2B089590E080E0089528 +:1010C000209131023091320228173907B9F49091FA +:1010D000300280912F02981789F0E0913002F0E001 +:1010E000E151FE4F80812091300230E02F5F3F4F71 +:1010F0002F7333272093300290E008958FEF9FEFF6 +:101100000895FC016093BC008091BC0087FFFCCF78 +:101110009091B900987F90830895DF92EF92FF92AB +:101120000F931F93CF93DF93EC01D62EF42E8C896F +:10113000882351F0411108C084E98093BC0080915C +:10114000BC0084FDFCCF1C8A8E010E5E1F4F8C8973 +:1011500081111AC0EB8864EAC8010E9481088A895B +:10116000883011F0803139F4EE0CE092BB0064E875 +:10117000C8010E94810880E0F11080E48093BB00E8 +:1011800064E8C8010E948108D092BB0064E8C801ED +:101190000E94810882E0F8120BC08C898F5F8C8BD3 +:1011A000DF91CF911F910F91FF90EF90DF90089505 +:1011B00084E98093BC008091BC0084FDFCCF1C8A34 +:1011C000EFCFDC01ED91FC910084F185E02D40E052 +:1011D00009940F931F93CF938C01FC016483C08506 +:1011E000C60F6C2F6F700E94E1086C2F62956F70B4 +:1011F0006061C801CF911F910F910C94E108FC012F +:101200002781269526952695621738F465832385D0 +:10121000620F6770606B0C94E10808951F93CF9381 +:10122000DF93EC01142F8E81681718F4CE010E9411 +:10123000E908612FCE01DF91CF911F910C94FF0837 +:10124000CF92DF92EF92FF920F931F93CF93DF9392 +:10125000EC01F62EC42ED22E1D868F8186958695A2 +:101260008695081710F00FEF080F1D2D011780F05D +:10127000412F6F2DCE010E940E09EF2CCE1430F0BD +:101280006889CE010E94B607E394F8CF1F5FEECFC6 +:101290004D2D6F2DCE01DF91CF911F910F91FF90BA +:1012A000EF90DF90CF900C940E090F93DC01159610 +:1012B0002C9115971E96ED91FC911F9700E03097A9 +:1012C00079F03396E491F0E0379683E0F595E79571 +:1012D0008A95E1F751960C9151970E9F002D11249C +:1012E000EFEFE20F0E0F16964C91169741501496A1 +:1012F0006C911497CD010E9420090F9108952F92AF +:101300003F924F925F926F927F928F929F92AF9295 +:10131000BF92CF92DF92EF92FF920F931F93CF93E2 +:10132000DF9300D000D000D0CDB7DEB7FC0126851A +:1013300037852115310509F44AC19E838D83F90152 +:101340003296649031964491E42EF12CF7E0EF0E42 +:10135000F11C53E0F594E7945A95E1F7E982F9011D +:10136000349654913196E491651748F08E2F90E0B1 +:10137000FC01E50FF11D6E171F060CF469C06D30FE +:1013800031F5ED81FE81868181111BC081E090E005 +:1013900026960FB6F894DEBF0FBECDBFDF91CF917A +:1013A0001F910F91FF90EF90DF90CF90BF90AF9083 +:1013B0009F908F907F906F905F904F903F902F9075 +:1013C000089560E08D819E810E94E908DFCF6A3038 +:1013D00009F0FDC0ED81FE818681882321F060E067 +:1013E000CF010E94E908ED81FE8181898E9DE02C6C +:1013F000112465816E0D8485882349F09781969527 +:10140000969596958E2D891B860F18162CF08D813A +:101410009E810E94FF08BACFED81FE812385820F55 +:10142000877083879E199583CF010E945509ED81AE +:10143000FE818485813009F0A9CF6385660F660F30 +:10144000660F6F7362876064CF010E94E1089ECFD0 +:1014500089010A5F1F4F651BED81FE8171855189EE +:10146000759F502D11245C83F90154912F5F3F4FDC +:10147000F9012491352F2230310508F06AC06E9CA5 +:10148000C0016F9C900D1124689FF001699FF00DC1 +:1014900011240E0F1F1F512CED81FE81F481FA8360 +:1014A000ED81FE812580F585FB83862C912CAA2475 +:1014B000A394B12C312C8981381608F086C0412CB8 +:1014C000ED81FE818189481608F078C08B8185877F +:1014D000311002C0442041F0ED81FE8145814F5F13 +:1014E0006A81CF010E940E09D12CC12CC61408F0CC +:1014F00058C0F801EC0DFD1D7490552061F0EA1400 +:10150000FB0449F4E72DF0E0052C02C0F595E795C2 +:101510000A94E2F77E2EED81FE818189823081F48A +:10152000442009F43AC072948FE07822E72DF0E06D +:10153000E65AF64F7490672D8D819E810E94B60702 +:10154000672D8D819E810E94B6078FEFC81AD80A39 +:10155000CDCF477028E0522E541A411101C0512CB2 +:10156000F801612C30E020E0661631F04491240F40 +:10157000311D63943196F8CF600E712E711CF3010A +:101580006490E29EF001E39EF00DF29EF00D1124B6 +:101590008E0F9F1F080F191F7FCFF72DFF707F2E13 +:1015A000C5CFD12CEC81DE1639F060E08D819E81B3 +:1015B0000E94B607D394F6CF439482CF3394080D9C +:1015C000191DFFEFAF1ABF0A76CF622D20CF90E032 +:1015D00080E0DECEFC018381877088608383848114 +:1015E0008F71806284836783CF010895E091E00169 +:1015F000F091E101309721F00280F381E02D099410 +:1016000008950F931F930E94A9070091E201109182 +:10161000E3012091E4013091E501601B710B820B25 +:10162000930B1F910F9108959C018091E00190917F +:10163000E10182179307F1F09091EA019F71909375 +:10164000EA013093E1012093E001E091E001F091A3 +:10165000E101309721F00190F081E02D09950E9481 +:10166000A9076093E2017093E3018093E4019093F2 +:10167000E50108958091EA01805E8093EA01E5CF5B +:101680000F931F930E94010B2091EB0120772037CD +:1016900079F000E117E220E030E00617170728078D +:1016A000390758F485E391E01F910F910C94140BC6 +:1016B00000E61AEE20E030E0F0CF1F910F91089580 +:1016C0008091EB018F70863020F088E191E00C94DE +:1016D000140B08958091EA0184608093EA0180915F +:1016E000EB0180788093EB01E091DC01F091DD016A +:1016F000309709F009940895CF93C091EC01CF3F42 +:10170000A1F00E94A9076093E6017093E70180931E +:10171000E8019093E90180913E02C813C0933E0214 +:1017200084E191E0CF910C94140B0E94010B613D78 +:1017300077408105910568F08091EA01803618F4C0 +:1017400080E191E0EFCF877F8093EA0188E191E02B +:10175000E9CFCF91089580913C028F3F21F080E145 +:1017600091E00C94140B0E94010B613D77408105C0 +:10177000910550F08091EA01803618F48CE091E0F8 +:10178000F0CF88E091E0EDCF08950E94010B613D1C +:1017900077408105910520F48091EA0181FF17C00F +:1017A00080913D028F3F49F08091EA018E7F8093C6 +:1017B000EA018CE091E00C94140B8091EA018036F0 +:1017C00018F488E191E0F7CF88E091E0F4CF089534 +:1017D000CF93DF93FC01EB01DA019C01205F3F4FC7 +:1017E00088819C9189278083898111969C9111978A +:1017F000892781838A8112969C91129789278283F7 +:101800008B8113969C911397892783833496249612 +:101810001496E217F30721F7DF91CF910895FC01A9 +:10182000DB019C01205F3F4F80819C918927808351 +:10183000818111969C9111978927818382811296CB +:101840009C91129789278283838113969C91139789 +:101850008927838334961496E217F30729F70895AE +:10186000089590E080E008950E94CB2BFB01019049 +:101870000020E9F73197AF01461B570BDC01ED91D2 +:10188000FC910280F381E02D0994CF93DF93BC019A +:1018900080E492E00E946405EC016EE771E080E470 +:1018A00092E00E94360C8C0F9D1FDF91CF9108951E +:1018B0008F929F92AF92BF92EF92FF920F931F93DE +:1018C000CF93DF93CDB7DEB7A1970FB6F894DEBF05 +:1018D0000FBECDBF7C01FA01CB0119A2223008F462 +:1018E0002AE08E010F5D1F4F822E912CB12CA12C6E +:1018F000BF01A50194010E944D2AF901CA016A3075 +:101900000CF5605DD8016E938D01232B242B252BC4 +:1019100079F790E080E0109721F0BD01C7010E94A7 +:10192000360CA1960FB6F894DEBF0FBECDBFDF9187 +:10193000CF911F910F91FF90EF90BF90AF909F902C +:101940008F900895695CDECFCF92DF92EF92FF9285 +:101950000F931F93CF93DF93EC016A017B01E88122 +:10196000F9810190F081E02D6DE209958C01442709 +:101970005527BA014C195D096E097F092AE0CE018D +:101980000E94580C800F911FDF91CF911F910F91F2 +:10199000FF90EF90DF90CF900895CF93DF93AC014D +:1019A000990F660B770B77FF10C080E492E00E94DE +:1019B000A40CEC016EE771E080E492E00E94360C2A +:1019C0008C0F9D1FDF91CF9108952AE080E492E073 +:1019D0000E94580CEECFAB01770F660B770B77FDAB +:1019E0000C94A40C2AE00C94580C4F925F926F92C6 +:1019F0007F928F929F92AF92BF92CF92DF92EF929F +:101A0000FF926B017C01AC019B010E94982B61E865 +:101A100071E081111AC046015701E894B7F82FEF21 +:101A20003FEF4FE75FE7C501B4010E94982B81119A +:101A300026C02FEF3FEF4FE75FE7C501B4010E94DB +:101A4000BD281816E4F465E871E080E492E00E9495 +:101A5000360C7C016EE771E080E492E00E94360C67 +:101A60008E0D9F1DFF90EF90DF90CF90BF90AF90B5 +:101A70009F908F907F906F905F904F9008952FEF81 +:101A80003FEF4FE75FE4C701B6010E94932B1816A2 +:101A90001CF469E871E0D9CF2FEF3FEF4FE75FEC1F +:101AA000C701B6010E94BD2887FDF3CF20E030E0DA +:101AB000A901C701B6010E94BD28B12CA12C87FF46 +:101AC0000AC06DE280E492E00E941E065C01F7FA13 +:101AD000F094F7F8F0942AE037ED43EA5BE3C701AE +:101AE000B6010E947F2A2B013C010E94642B6B01EE +:101AF0007C010E94C2289B01AC01C301B2010E947B +:101B00007E2A2B013C012AE0B701A60180E492E085 +:101B10000E94580C7C01EA0CFB1C6EE280E492E00F +:101B20000E941E06E80EF91E20E030E040E251E47B +:101B3000C301B2010E9423292B013C010E94642BA6 +:101B40004B01B12CA12C2AE0B501A40180E492E064 +:101B50000E94580CE80EF91EC501B4010E94C2286B +:101B60009B01AC01C301B2010E947E2A20E030E05B +:101B700040E251E40E9423290E94642BAB0170E0F3 +:101B800060E02AE080E492E00E94580CE80EF91E22 +:101B900061CFCF93DF93BC0180E492E00E94360CCA +:101BA000EC016EE771E080E492E00E94360C8C0F4D +:101BB0009D1FDF91CF910895EAEEF1E08081877F4C +:101BC0008F7E80831092E6011092E7011092E80167 +:101BD0001092E90144E050E070E060E08CE392E0B4 +:101BE0000C94E129EF92FF920F931F93CF93DF9311 +:101BF000EC017B0180E06115710531F0CB010E94A1 +:101C00007D288931910518F5082F10E09B81977088 +:101C1000980143E0220F331F4A95E1F7922B9B83F3 +:101C20009C819F719C83882331F0A801B701CE016C +:101C300007960E948628FE01E00FF11F1782CE0151 +:101C4000DF91CF911F910F91FF90EF90089589E15F +:101C5000DBCF91E59CBD91E09DBD2998E3E2F0E0EA +:101C60003197F1F78EBD00000DB407FEFDCF8EB5A4 +:101C7000FB019FEF4150C8F0222361F09EBD0000A0 +:101C80000DB407FEFDCF8EB56115710599F3808304 +:101C90003196F0CFDF01119680818EBD00000DB42A +:101CA00007FEFDCF8EB5FD01E5CF299AE3E2F0E016 +:101CB0003197F1F70895CF93DF931F92CDB7DEB739 +:101CC000698320E041E0BE016F5F7F4F0E94290ED3 +:101CD0000F90DF91CF9108958091EB01982F90772D +:101CE000903759F0982F92959F709F5F97709295BB +:101CF000907F8F78892B8093EB018091EA018B7F15 +:101D00008F7E8093EA012A986CE080E20C945B0E4F +:101D10006FE080E20E945B0E609139016F3F19F025 +:101D20008AE20E945B0E2A9A0895CF93C82F80936F +:101D30003C028F3F51F08093390163E082E20E94C0 +:101D40005B0E61E081E20E945B0E0E94880E6C2FA8 +:101D500090E080E00E94092A81E0CF91089521E07F +:101D600041E070E060E00C94290E20E040E070E07B +:101D700060E00C94290E0F931F93CF93DF93EC0137 +:101D8000E0E06115710551F0FB0101900020E9F7D9 +:101D90003197E61BF70BE931F10500F50E2F10E046 +:101DA0008B818770980193E0220F331F9A95E1F79A +:101DB000822B8B838C818F718C83EE2329F0A80179 +:101DC000CE0107960E94D92BFE01E00FF11F17826A +:101DD000CE01DF91CF911F910F910895E9E1DECF00 +:101DE000FC012481229526952770213011F48781EA +:101DF0000895211104C007960E943828089580E0B4 +:101E00000895CF93DF93EC018DEA90E00E94450C9A +:101E100081E08093C5018C8184FF06C083EA90E055 +:101E2000DF91CF910C94450C8D818230C9F48E8165 +:101E30008330B1F4CE010E94F00E91E0811101C017 +:101E400090E09093330161E870E080E492E00E94BA +:101E500064058091330190E0DF91CF910C94CD0C1B +:101E6000DF91CF9108952F923F924F925F926F92A0 +:101E70007F928F929F92AF92BF92CF92DF92EF921A +:101E8000FF920F931F93CF93DF93CDB7DEB7A29747 +:101E90000FB6F894DEBF0FBECDBFB82E80913C02C6 +:101EA000FB018083138112FDEDC0169516951695E2 +:101EB000195FF0913F02F9A3F170FAA3412F11329B +:101EC00008F040E250E085EA91E00E94D02B1092A9 +:101ED000D1041092D2041092D3041092D404109220 +:101EE000D5041092D6041092D7041092D804109200 +:101EF000D9041092DA041092DB041092DC041092E0 +:101F0000DD041092DE041092DF041092E0041092BF +:101F1000C9041092CA041092CB041092CC041092FF +:101F2000CD041092CE041092CF041092D00400E29F +:101F3000902E113110F410E1912EE92CE294FFE083 +:101F4000EF22F12CA5EACA2EA1E0DA2E8E010F5E57 +:101F50001F4FBBE1AB2EB60181ED94E00E940F0C48 +:101F60008091D7039091D803892B09F40BC149EDD7 +:101F700053E061ED74E0C8010E94E80B882483946B +:101F8000482D50E08091D7039091D8034817590706 +:101F90000CF07AC09801DE0111966FE0362E7AE0DF +:101FA000572EE5E04E2EF801E40DF11D708084E01F +:101FB000840D8F70482EF801E50DF11D808194E0AD +:101FC000950D9F70592EF801E30DF11D908164E08D +:101FD000630D6F70362EF9016081E62FF0E0EE534D +:101FE000FF4F7491E72DF0E0EE53FF4F6491E82F1F +:101FF000F0E0EE53FF4F8491E92FF0E0EE53FF4FF6 +:102000009491672E660C77FD6A24E62FEE0F67FD2C +:10201000EA25282E220C87FD2A24792E770C97FD9D +:102020007A24F62FF827F927F625FE27FC93F72FB9 +:10203000F827F927FE27F2251196FC9311976727B9 +:1020400096272926272412962C92129786276826EF +:10205000762413967C9213972C5F3F4F14960A17A1 +:102060001B0709F0A0CF94E0440F551F9A95E1F7A4 +:1020700047525C4FBE016F5F7F4FC8010E94E80B63 +:1020800083947ECF10E215CFE989F0E0EE53FF4F45 +:10209000E491E98BED89F0E0EE53FF4FE491ED8B95 +:1020A000E98DF0E0EE53FF4FE491E98FED8DF0E024 +:1020B000EE53FF4FE491ED8F8A89EE89F0E0EE5305 +:1020C000FF4FE491EA8BEA8DF0E0EE53FF4FE4918D +:1020D000EE8BEE8DF0E0EE53FF4FE491EA8FE82FA8 +:1020E000F0E0EE53FF4FE491EE8F8B89EB8DF0E043 +:1020F000EE53FF4FE491EB8BE82FF0E0EE53FF4FF0 +:10210000E491EB8F8F89EF8DF0E0EE53FF4FE49178 +:10211000EF8BE82FF0E0EE53FF4FE491EF8F88A1B3 +:10212000EC8DF0E0EE53FF4FE491E8A3E88DF0E092 +:10213000EE53FF4FE491EC8FEC89F0E0EE53FF4F4C +:10214000E491E88FE82FF0E0EE53FF4FE491EC8B41 +:1021500084E0440F551F8A95E1F747525C4FB80160 +:1021600081ED94E00E94E80B40E161ED74E0C6016E +:102170000E943605F0E1CF0ED11C81E0E81AF1088B +:1021800009F0E9CE2A9883E092E00197F1F76EE03A +:1021900080E20E945B0E8BE891E00197F1F76B2DD6 +:1021A0008AE20E945B0E6B2D80E30E945B0E81EE43 +:1021B0000E94B50EEFEFBE1619F0FAA180EAF111F8 +:1021C00080EB20E0492D65EA71E00E94290E2A9AF1 +:1021D000F12CE12C8FEF0E94B50E182F807321F4A3 +:1021E00081E0E81AF108B1F72A9860E387E20E94DB +:1021F0005B0E14FF03C081EE0E94B50E0E94880E94 +:1022000015FF02C0E1E0E9A391E0FFEFBF1290E00B +:1022100089A18170892BA2960FB6F894DEBF0FBEFC +:10222000CDBFDF91CF911F910F91FF90EF90DF9085 +:10223000CF90BF90AF909F908F907F906F905F9066 +:102240004F903F902F9008950F931F93CF938C0141 +:10225000FC0182819091EA0190FF04C08F3F51F50B +:10226000CFEF04C0811116C0C0913D02B8018C2F80 +:102270000E94330F90913D029C1313C09091EB018B +:10228000811113C021E0290F2F70907F922B909322 +:10229000EB0107C08F3F21F3B8010E94330F882361 +:1022A00019F3CF911F910F910895907F9093EB01B7 +:1022B00081E0F7CF80E0F5CF2091EA0122FD0C9478 +:1022C000241180E00895E5E9F3E090913C029183C8 +:1022D0001282868365838481807E8483BA01CF01E4 +:1022E0000E94F20D0C945C1120913C02FC012183B0 +:1022F000F8E06F9FB00111246160FC012481207F10 +:10230000622B64832091DF0120FD0C945C1180E03E +:102310000895EF92FF921F93CF93DF93C5E9D3E027 +:102320008CE3E82E82E0F82EF701808189831A82FF +:102330001FEF1E838BE08D838C81807E83608C8376 +:102340006DE274E0CE010E94F20D0E945C11F70173 +:10235000808189831A821E838CE08D838C81807EAC +:1023600083608C8369E274E0CE010E94F20D0E94CA +:102370005C1164E670E080E090E00E94531647E153 +:1023800054E063E083E00E94631164E670E080E063 +:1023900090E00E94531648E054E066E182E00E941B +:1023A000631164E670E080E090E00E94531640E024 +:1023B00054E064E285E00E94631164E670E080E02E +:1023C00090E00E94531681E080933401DF91CF9119 +:1023D0001F91FF90EF900895FF920F931F93CF935B +:1023E000DF93C5E9D3E00CE312E0F8018081898333 +:1023F0001A82FF24FA94FE828FE08D838C81807E86 +:1024000083608C8360E871E0CE010E94BB0E8B81FB +:10241000877080618B838C818F71806C8C8381E06D +:102420008F831886CE010E945C118FE00E942016D7 +:10243000F801808189831A82FE8281E18D838C81FB +:10244000807E8C836DE871E0CE010E94BB0E0E94FD +:102450005C11F801808189831A82FE8286E08D8377 +:102460008C81807E83608C836181CE010E94EA0A28 +:102470000E945C1186E00E942016DF91CF911F918F +:102480000F91FF900C9489112F923F924F925F927F +:102490006F927F928F929F92AF92BF92CF92DF9274 +:1024A000EF92FF920F931F93CF93DF93CDB7DEB7D9 +:1024B000E9970FB6F894DEBF0FBECDBF26E02BAB79 +:1024C0001BE187E10E94AF0E80FD9BC33BA9315009 +:1024D0003BAB332309F495C321E041E070E060E0B9 +:1024E00080E60E94290E082F813220F082EE0E94A1 +:1024F000B50E00E021E0402F66EB73E081E60E941C +:10250000290E60E487E20E945B0E1092D1041092C3 +:10251000D2041092D3041092D4041092D5041092D5 +:10252000D6041092D7041092D8041092D9041092B5 +:10253000DA041092DB041092DC041092DD04109295 +:10254000DE041092DF041092E0041092C90410928D +:10255000CA041092CB041092CC041092CD041092B5 +:10256000CE041092CF041092D004013108F463C15C +:1025700082E090E09AAB89ABE6EB2E2EE3E03E2EB4 +:10258000E9A9FAA93197FAABE9AB319609F471C11F +:1025900040E1B101CE0181960E9436054091D703FA +:1025A0005091D8034115510509F463C174E0440FFB +:1025B000551F7A95E1F747525C4FB101CE01419624 +:1025C0000E94E80BE989F0E0E65AF74FE491E98BC5 +:1025D000ED89F0E0E65AF74FE491ED8BE98DF0E0FC +:1025E000E65AF74FE491E98FED8DF0E0E65AF74FA8 +:1025F000E491ED8F8E8DEA8DF0E0E65AF74FE4918D +:10260000EE8FEE89F0E0E65AF74FE491EA8FEA891F +:10261000F0E0E65AF74FE491EE8BE82FF0E0E65A4F +:10262000F74FE491EA8B8B89EB8DF0E0E65AF74F98 +:10263000E491EB8BE82FF0E0E65AF74FE491EB8F53 +:102640008F89EF8DF0E0E65AF74FE491EF8BE82F9A +:10265000F0E0E65AF74FE491EF8F8C89E88DF0E0D7 +:10266000E65AF74FE491EC8BEC8DF0E0E65AF74F29 +:10267000E491E88FE8A1F0E0E65AF74FE491EC8F9F +:10268000E82FF0E0E65AF74FE491E8A30091D70372 +:102690001FAA1EAA8FEF800FA82FB0E0B9AFA8AF76 +:1026A000015009F4CDC048AD59ADEEA9FFA94E1BAC +:1026B0005F0B64E0440F551F6A95E1F747525C4F8A +:1026C000BE016F5E7F4FCE0101960E94E80B9E0116 +:1026D0002F5F3F4F7901CE0141969DAB8CABA5E0BA +:1026E000B0E0F7019081818132812381792F770FCA +:1026F00097FD7127B82EBB0C87FDB126C32ECC0CDD +:1027000037FDC126622F660F27FD6127472F440F33 +:1027100077FD41274B2C440CB7FE04C05B2D550FB1 +:10272000452E41269C2C990CC7FC9126A62EAA0C5E +:1027300067FDA126D42EDD0C47FF04C0542F550F92 +:10274000D52ED126542D550F47FC5127F92DFF0FBB +:1027500097FCF127EA2DEE0FA7FCE127792E7D24C7 +:10276000582E5526832E8F26622E6E264D255425F3 +:102770009F26AE26E32FE727EB25E427E525E9256D +:10278000E625F0E0E65AF74F3491ECA9FDA9308335 +:10279000E22FEB25EC25E725E527E825EA25F0E003 +:1027A000E65AF74FD490FD01EF70FF2721E130E0AA +:1027B0002C0F3D1FE20FF31FD0829C25962749273F +:1027C000542695246924E62DF0E0E65AF74F6490EC +:1027D000FD013596EF70FF2721E130E02C0F3D1F02 +:1027E000E20FF31F6082E82FE727E627E7255E2741 +:1027F000E52FE825AE26EA2DF0E0E65AF74FA49043 +:10280000FD013A96EF70FF27E20FF31FA08294E0DC +:10281000E90EF11C2CA93DA92C5F3F4F3DAB2CAB21 +:102820001496A531B10509F05CCF8EA99FA9019638 +:102830009FAB8EAB35CFA1E0B0E0BAABA9AB9CCEDD +:1028400049ED53E0BE016F5E7F4FC1010E94E80B6E +:1028500061ED74E0C1010E940F0C40E1BE016F5DAB +:102860007F4F81ED94E00E94360590E1290E311CE6 +:1028700087CE3091B903832F8370823009F021CE47 +:102880002091BA03A22FA770EA2E0091BB03F0900B +:10289000B7038091B6034091B8039091EB01906823 +:1028A0009093EB0190913C02491376C1832F86955A +:1028B000869586958A3108F089E190E03770BC01F1 +:1028C00053E0660F771F5A95E1F7362B3093B90323 +:1028D0008A549C4FFC01178223FF15C081E2E6EB6E +:1028E000F3E0A5E9B3E001900D928A95E1F7277F27 +:1028F00020612093990340939603F092970385E912 +:1029000093E00E945C119091BA0394FD3DC1F3E005 +:10291000EF123AC18091BB0321EF280F233008F456 +:10292000D0CD0430C1F48091BC0390913A01891755 +:1029300019F08F3F09F0C5CD86EB93E00E94F00EB1 +:102940009FEF980F9E3F18F40E94950EBACD8FEF1F +:1029500080933C02B6CD0830D9F40091EA0100FF23 +:10296000B0CD86EB93E00E94F00E8F3F09F4A9CD25 +:102970008F5F8F3F09F4A5CD90913E02891708F033 +:10298000A0CD01FD9ECD80933E02F0923D0299CDF7 +:102990000831C1F480913C0280939603F092970332 +:1029A0008FEF80939B0389E180939A038091990331 +:1029B000807E83608093990361E085E993E00E94C3 +:1029C000EA0A32C0093171F48091EA0183FF79CDBE +:1029D000877F8093EA0186EB93E00E94F00E80935C +:1029E000EC016FCD0E3109F46CCD0D3101F5809104 +:1029F0003C0280939603109297038FEF80939B0382 +:102A00008FE180939A0383E48093990380919803E4 +:102A1000877080618093980380E09FEF90939D037F +:102A200080939C0385E993E00E9424114ACD209174 +:102A3000B7032111AEC08D3059F488E198E00FB68C +:102A4000F894A895809360000FBE90936000FFCF2C +:102A50008B3179F486EB93E00E94F00E91E08111C6 +:102A600001C090E08091DF0190FB80F98093DF014D +:102A700028CD863099F49091BD0381E0992319F017 +:102A80009D3409F080E08093DE0141E050E063E096 +:102A900071E08EED91E00E94F92913CD833119F494 +:102AA0000E94EC110ECD823121F50E94010B209184 +:102AB0003C0220939603109297032FEF20939B03E1 +:102AC00026E120939A0323EA209399032091980307 +:102AD000277020622093980360939C0370939D035A +:102AE00080939E0390939F0385E993E00E945C117D +:102AF000E8CC823009F590913C02909396031092B5 +:102B000097039FEF90939B0380939A0383EA8093AC +:102B100099038091980387708062809398038FEF68 +:102B200091E0A3E0B2E080939C0390939D03A09377 +:102B30009E03B0939F03D8CF813001F520E030E0B1 +:102B4000232B09F4BECC929596959770953059F445 +:102B50006091BD037091BE038091BF039091C0034B +:102B60000E940000AECC911105C08DEB93E00E9455 +:102B70005628F6CF60E070E0CB01F2CF8A3009F43E +:102B8000A0CC8C3109F49DCC86EB93E00E94010F20 +:102B900098CC8A31C9F795CC4F3F09F092CC93E09D +:102BA000A9132AC09091EA0192FF03C0073009F4EB +:102BB00088CC043109F085CC90913D02891381CCF9 +:102BC0000E94A9077370882799270E947607809131 +:102BD0003C0280939603F09297038FEF80939B03C0 +:102BE00085E180939A0380919903807E836080932E +:102BF000990360913D02E1CE90913D02891361CC31 +:102C0000C3CFE9960FB6F894DEBF0FBECDBFDF91FC +:102C1000CF911F910F91FF90EF90DF90CF90BF90D9 +:102C2000AF909F908F907F906F905F904F903F906C +:102C30002F9008958091EA0184FD0C944412089528 +:102C4000CF92DF92EF92FF92CF93DF93D82F0E9423 +:102C5000A9076B017C011092BB03C0E00E94A90789 +:102C60006C197D098E099F09603D774081059105AA +:102C700090F4C11110C0A8950E94F60A0E941A167D +:102C80008091BA038770833049F7C1E08091BB031C +:102C90008D13E3CFE3CF8C2FDF91CF91FF90EF9097 +:102CA000DF90CF9008958F929F92AF92BF92CF9274 +:102CB000DF92EF92FF924B015C010E94A9076B012A +:102CC0007C010E94A9076C197D098E099F0968156E +:102CD00079058A059B0530F4A8950E94F60A0E94A2 +:102CE0001A16EFCFFF90EF90DF90CF90BF90AF908C +:102CF0009F908F900895EF92FF920F931F93CF9321 +:102D00000E940E0568EC70E080E090E00E9453168F +:102D100084EE93E00E94450C80EA91E00E94F12944 +:102D200091E0811101C090E09093330160E085E073 +:102D30000E94150761E086E00E94150762E088E0C6 +:102D40000E94150762E087E00E9415071092940325 +:102D50008CE3809393031092B9008CE08093B800C9 +:102D60001092840310928503EEEDF3E0E590F49069 +:102D7000E0EEF3E0C491E1EEF3E0E491E09386034A +:102D8000E2EEF3E0E491E0938703E3EEF3E0E49115 +:102D9000E093880310E000E00C1758F4F701E00F0F +:102DA000F11F649180E893E00E94E1080F5F1F4FDC +:102DB000F3CF10928B0310928A0360E480E893E0D3 +:102DC0000E94E1080091870306950695069501503B +:102DD00040918603415020E060E080E893E00E944B +:102DE0002009E8EFF1E0F0938F03E0938E038491E4 +:102DF000E9EFF1E0E491F82F319709F0D1C010929A +:102E000089036FEA80E893E00E94E10810928C0346 +:102E100040E060E080E893E00E940E0961EF71E01D +:102E200080E893E00E9464058091EA0182FFBCC0C3 +:102E30008BED91E00E94450C81E08093C50160ED2F +:102E400077E080E090E00E94531610927C0310928D +:102E50007B0310927A0310927903109278031092F8 +:102E600077038DE991E090937E0380937D039091A9 +:102E70006D03963108F047C02DEB30E030937803B6 +:102E8000209377032BE931E030937A032093790381 +:102E90002CE231E030937C0320937B032091680087 +:102EA00084E0983048F031E09E3008F430E081E072 +:102EB00001C0880F3A95EAF7822B809368002DE6CF +:102EC00030E0983050F02BE630E09E3030F02CE6C9 +:102ED00030E0963110F030E020E03093750320931D +:102EE0007403292F30E0983008F063C081E001C0FE +:102EF000880F9A95EAF78093760380917D039091ED +:102F00007E030197F1F780917B0390917C03892BDD +:102F100049F1E0913102F091320283E0E136F807A5 +:102F200009F1309739F0A389B4899C9185898095FE +:102F300089238C9380917F038E7F80937F031092EF +:102F40002F021092300281E693E090933202809338 +:102F50003102E0917403F0917503808190917603C2 +:102F6000892B808340913101415060E080E893E0FB +:102F70000E940E0962EC71E080E893E00E94640513 +:102F800088E199E20FB6F894A895809360000FBE8F +:102F900090936000CF911F910F91FF90EF90089553 +:102FA00081E0809389032DCF8BEC91E00E94450C4A +:102FB00046CF9E3040F42850310981E001C0880F8F +:102FC0002A95EAF798CF2E503109F7CF4F925F92AA +:102FD0006F927F928F929F92AF92BF92CF92DF9229 +:102FE000EF92FF920F931F93CF93DF93CDB7DEB78E +:102FF00062970FB6F894DEBF0FBECDBF809134014B +:10300000882309F455C01092340183E0809346036D +:1030100090913301809143038770886080934303CC +:10302000809144038F71806280934403909347039F +:1030300060E080E493E00E94741164E670E080E058 +:1030400090E00E94531680913301882389F112E0A9 +:10305000109325036BE374E08FE193E00E94F20D7F +:1030600060E00E94741164E670E080E090E00E94ED +:1030700076072091D3013091D4012F3541E03407F8 +:10308000BCF010930403809102038F7180648093DD +:103090000203809101038770806180930103309364 +:1030A00006032093050361E08EEF92E00E94741105 +:1030B00087E00E94BE06892BB9F48091DB0191E084 +:1030C00089278093DB0164E478E080E492E00E9449 +:1030D00064058091DB0190E00E94CD0C64EF71E00B +:1030E00080E090E00E94531688E00E94BE06892B83 +:1030F00061F56091330181E068276093330180EAD4 +:1031000091E00E94092A82E298E00E94450C64EF57 +:1031100071E080E090E00E94531683E080934603C4 +:1031200090913301809143038770886080934303BB +:10313000809144038F71806280934403909347038E +:1031400060E080E493E00E9474116091330180910B +:103150003201681749F16093320180EA91E00E94E0 +:10316000092A6CEF77E080E492E00E946405809188 +:10317000330190E00E94CD0C83E080934603909150 +:10318000330180914303877088608093430380916B +:1031900044038F718062809344039093470360E0FF +:1031A00080E493E00E9474110E94A9070091D70166 +:1031B0001091D8012091D9013091DA016B017C0185 +:1031C000C01AD10AE20AF30A97018601093E1340A8 +:1031D0002105310508F468C36093D7017093D801C5 +:1031E0008093D9019093DA018091D5019091D60115 +:1031F00001969093D6018093D501CC9774F06EE739 +:1032000071E080E492E00E94360C8FEE97E00E941D +:10321000450C1092D6011092D501A89511E010939B +:10322000910340E064E680E893E00E940E098091FB +:10323000D5019091D6016CE370E0681B790B80E8B2 +:1032400093E00E94EB0C80E893E00E94550983E034 +:10325000809331018091D5019091D6018130910503 +:1032600009F09FC481E693E00E9460088C010F3F43 +:103270001007C1F789E0E3E4F1E0DE011196019067 +:103280000D928A95E1F781E894E00E94450C49E0AF +:1032900050E0BE016F5F7F4F81E693E00E9488059A +:1032A0006E0193E1C90ED11CFE013A967F010E9486 +:1032B000A9076093690370936A0380936B039093EB +:1032C0006C0381E693E00E94600897FF09C50E94A5 +:1032D000A9078090690390906A03A0906B03B09057 +:1032E0006C03681979098A099B098090650390909D +:1032F0006603A0906703B0906803681579058A0596 +:103300009B05F8F28A858F3F09F4E5C282E694E0D6 +:103310000E94450C1093D4010093D3016DED77E02A +:1033200080E492E00E9464058091D3019091D401E1 +:103330000E94CD0C8091D3019091D4018F359140A2 +:103340000CF4DBC08091D2018111D7C08091300193 +:103350008F5F8093300166EB77E080E492E00E941B +:1033600064054091300150E070E060E02AE080E4C4 +:1033700092E00E94580C6EE771E080E492E00E94B7 +:10338000360C80913001853008F469C065E877E03B +:1033900080E492E00E9464058091D3019091D40171 +:1033A0000E94CD0C1092300180902C0190902D0144 +:1033B000A0902E01B0902F0140902801509029013B +:1033C00060902A0170902B01A3019201C501B40104 +:1033D0000E947E2A6B017C0120E030E0A9010E945E +:1033E000BD28882311F120E030E040EF51E4C7010F +:1033F000B6010E94BD2887FF18C0C092CE01D092AE +:10340000CF01E092D001F092D1014092240150927C +:1034100025016092260170922701809220019092EE +:103420002101A0922201B09223016091D3017091F9 +:10343000D401072E000C880B990B0E94C4286093BE +:1034400028017093290180932A0190932B016093A6 +:103450002C0170932D0180932E0190932F01609188 +:10346000D3017091D401072E000C880B990B0E9498 +:10347000C4286B017C0120912801309129014091E1 +:103480002A0150912B010E94BD2887FF4FC2C09294 +:103490002801D0922901E0922A01F0922B0187E5C0 +:1034A00097E00E94450C6DEC76E080E492E00E948B +:1034B0006405209128013091290140912A01509101 +:1034C0002B0160912C0170912D0180912E01909122 +:1034D0002F010E947E2A0E94F50C66EB76E080E4C4 +:1034E00092E00E9464056091CE017091CF018091BD +:1034F000D0019091D1010E94F50C8091DB018823CD +:1035000009F430C284E996E00E94450C8091D20112 +:10351000811145C08FE796E00E94450C81E08093C1 +:10352000D20120E030E0A9016091CE017091CF017D +:103530008091D0019091D1010E94BD2881110CC0D1 +:1035400080E090E0A0E8BFE38093CE019093CF01AC +:10355000A093D001B093D10180E090E0A0E0BFEB58 +:1035600080931C0190931D01A0931E01B0931F0135 +:103570006091D3017091D401072E000C880B990B38 +:103580000E94C4286093CA017093CB018093CC0140 +:103590009093CD018091D201882309F43AC120E0B3 +:1035A00030E44CE156E46091CE017091CF018091FE +:1035B000D0019091D1010E9423290E945D2B6B01C3 +:1035C0007C01C114D104E104F10441F00E94E02720 +:1035D000A70196010E941B2A6B017C01C701B6015D +:1035E0000E94C42820E030E44CE156E40E94EB2A1B +:1035F0006093C6017093C7018093C8019093C9017D +:103600006DEF75E080E492E00E9464056091C60170 +:103610007091C7018091C8019091C9010E94F50C79 +:103620000E94E02722E030E040E050E00E941B2AA8 +:103630000E94C4286B017C016EEE75E080E492E08C +:103640000E946405C701B6010E94F50C6EEC75E09E +:1036500080E492E00E94640560911C0170911D015C +:1036600080911E0190911F010E94F50C20911C0178 +:1036700030911D0140911E0150911F01C701B601FB +:103680000E947F2A6B017C0165EB75E080E492E08B +:103690000E946405C701B6010E94F50CA7019601BE +:1036A0006091C6017091C7018091C8019091C901D4 +:1036B0000E9423296093C6017093C7018093C801BB +:1036C0009093C9016DE875E080E492E00E94640582 +:1036D0006091C6017091C7018091C8019091C901A4 +:1036E0000E94F50C62E875E080E492E00E946405B7 +:1036F000609124017091250180912601909127010C +:103700000E94F50C67E775E080E492E00E94640592 +:1037100060912001709121018091220190912301FB +:103720000E94F50CC090C601D090C701E090C8017E +:10373000F090C90120E030E0A901C701B6010E9464 +:10374000932B18160CF0B4C12091CA013091CB0113 +:103750004091CC015091CD01C701B6010E947F2A52 +:1037600020912001309121014091220150912301AB +:103770000E94932B18160CF09BC1F7FAF094F7F8FF +:10378000F094C092C601D092C701E092C801F092B5 +:10379000C90183E991E00E94C90D60E575E080E40C +:1037A00092E00E9464056091C6017091C70180910A +:1037B000C8019091C9010E94F50C2091C601309179 +:1037C000C7014091C8015091C9016091CA0170912F +:1037D000CB018091CC019091CD010E947F2A609312 +:1037E000CA017093CB018093CC019093CD010E94CC +:1037F0005D2B7093D4016093D30166E375E080E4A0 +:1038000092E00E9464058091D3019091D4010E94BE +:10381000CD0C82E0809391034091310160E080E81B +:1038200093E00E940E096091D3017091D4016F3F23 +:10383000760709F070C16BE275E080E893E00E94C2 +:10384000640580E893E00E94550981E0809391032C +:1038500040E062E480E893E00E940E099091DB0171 +:103860008091D201992321F060EB74E0811106C0B0 +:103870006EEA74E0981302C06CEA74E080E893E0AA +:103880000E94640540E068E480E893E00E940E092D +:10389000809133016AEA74E0811102C068EA74E041 +:1038A00080E893E00E94640562960FB6F894DEBF4C +:1038B0000FBECDBFDF91CF911F910F91FF90EF9081 +:1038C000DF90CF90BF90AF909F908F907F906F9040 +:1038D0005F904F9008958B85863839F080E594E0AD +:1038E0000E94450C0EEF1FEF15CDFE013B962AE01E +:1038F00030E02C0F3D1F285F3F4F80E09191890FF2 +:103900002E173F07D9F781959A89891719F08FE308 +:1039100094E0E6CF0C85002E000C110B102F002731 +:103920008D85080F111D87FD1A95F4CC20912C016F +:1039300030912D0140912E0150912F01C701B60108 +:103940000E94932B18165CF4C0922C01D0922D018A +:10395000E0922E01F0922F018AE297E0A2CD87EE4D +:1039600096E09FCD8091D201882309F452CF6091D7 +:10397000D3017091D401072E000C880B990B0E9483 +:10398000C4286B017C018090CE019090CF01A09063 +:10399000D001B090D101A50194010E947E2A6093CC +:1039A0002401709325018093260190932701A5019E +:1039B0009401C701B6010E947F2A60932001709391 +:1039C000210180932201909323018090CA0190905D +:1039D000CB01A090CC01B090CD01A5019401C7010D +:1039E000B6010E94932B18160CF049C08DEC9CEC8C +:1039F000ACECBDEB80931C0190931D01A0931E01C4 +:103A0000B0931F0181E596E00E94450C6091D301BF +:103A10007091D401072E000C880B990B0E94C428CA +:103A20009B01AC016091CA017091CB018091CC01E6 +:103A30009091CD010E947E2A6B017C0120E030E054 +:103A4000A9010E94932B181624F0F7FAF094F7F8C6 +:103A5000F094A70196016091CE017091CF01809101 +:103A6000D0019091D1010E94932B18160CF092CDA9 +:103A700081E196E00E94450C1092D2018BCDA50108 +:103A80009401C701B6010E94BD2887FFBFCF82E223 +:103A900096E00E94450C86E696E6A6E6BFEB80938C +:103AA0001C0190931D01A0931E01B0931F01AECF86 +:103AB00020E030E0A901C701B6010E94BD2887FFC0 +:103AC00027C02091CA013091CB014091CC01509187 +:103AD000CD01C701B6010E947F2A209124013091B7 +:103AE000250140912601509127010E94BD2887FFA2 +:103AF0000FC0F7FAF094F7F8F094C092C601D09294 +:103B0000C701E092C801F092C90185E991E043CE76 +:103B100087E991E040CE6E3F4FEF740719F460E201 +:103B200075E08BCECB018F5591408A32924108F0DF +:103B30008CCE80E893E00E94EB0C80E893E00E943A +:103B40005509409131016CE380E893E00E940E0931 +:103B50008091D3019091D4016AE175E0843FF1E056 +:103B60009F070CF46ACE65E175E08C3B22E092077A +:103B70000CF463CE62E175E0883E43E094070CF4F8 +:103B80005CCE6DE075E0803DE7E09E070CF455CE1D +:103B900069E075E0893893410CF44FCE62E075E03E +:103BA0004CCE8230910509F083C08091D301909171 +:103BB000D4018D5291408736904108F046CE8091D5 +:103BC0003301882309F46FC01092C5016EEE74E0D2 +:103BD00080E492E00E9464058091D3019091D40129 +:103BE0000E94CD0C1092C50182E0809304032091C5 +:103BF000D3013091D401809102038F71806480934E +:103C000002038091010387708061809301033093E8 +:103C100006032093050361E08EEF92E00E94741189 +:103C200064E670E080E090E00E9453168091D3013A +:103C30009091D401823C41E094076CF485E080933C +:103C4000E30268EE74E08DED92E00E94F20D60E018 +:103C50000E947411FACD8C3BE2E09E0734F485E0BB +:103C60008093E30263EE74E0EECF883EF3E09F07BB +:103C700034F485E08093E30260EE74E0E4CF803DAD +:103C800027E0920734F485E08093E3026BED74E063 +:103C9000DACF883893410CF0D8CD85E08093E302E9 +:103CA00067ED74E0D0CF86EB94E00E94450CCDCD5B +:103CB000039709F0CACD1093910340E060E580E8D6 +:103CC00093E00E940E098091C50164EB74E08111BC +:103CD00002C062EB74E080E893E00E946405B5CD19 +:103CE000F70181937F01CE16DF0609F0E0CA0ACB07 +:103CF0001F93CF93DF938091DF0181FD2DC00E9440 +:103D0000EC118091DF0181608093DF01C4E0DFEF7F +:103D10001AE180913C028093960310929703D0930E +:103D20009B0310939A0380919903807E8360809314 +:103D3000990362E085E993E00E94EA0A0E945C111F +:103D40008BE10E942016811102C0C15011F78091B1 +:103D5000DF0182608093DF01DF91CF911F91089591 +:103D60008FEF8093EC01EAEEF1E090819860908310 +:103D7000E5E9F3E090913C0291831282868388E129 +:103D800085838481807E8360848361E0CF010E948B +:103D9000EA0A0C94241180913C028F3F21F50E9485 +:103DA000A9076F3FE9F060933A0180913A01909141 +:103DB0003C02909396031092970380939B0383E0B9 +:103DC00080939A0380919903807E83608093990306 +:103DD00060E871E085E993E00E94BB0E0C94241129 +:103DE00010923A01E2CF0895EAEEF1E08081897FF6 +:103DF00081608083ACE3B2E08FEF12968C931297D0 +:103E000011968C931197E5E9F3E09C91918382835D +:103E1000868387E085838481807E8360848360E8F5 +:103E200071E0CF010E94BB0E0C9424118F929F92DF +:103E3000AF92BF92CF92DF92EF92FF920F931F93B8 +:103E4000CF93DF93CDB7DEB764970FB6F894DEBF9C +:103E50000FBECDBF8E010F5F1F4F80E1F8011192A1 +:103E60008A95E9F760E170E08AE991E00E9495287F +:103E7000AC016AE971E0C8010E94D02B8AE090E0B1 +:103E80009093D8038093D70340E1B80189ED93E084 +:103E90000E94360550E040E080E190E0FF24F3947A +:103EA00020E1E22EBE016B5E7F4F3BE1B32ED82EA8 +:103EB000FC01EB52FC4F9E012F5E3F4FD901C19098 +:103EC000CD92A617B707D9F7DE1020C0D988F0E148 +:103ED000EF0EEA89F0E0EE53FF4FE491EF25E98B16 +:103EE000EB89F0E0EE53FF4FE491EA8BEC89F0E0D0 +:103EF000EE53FF4FE491EB8BED2DF0E0EE53FF4FCF +:103F0000E491EC8BF7FECFC0FF0CFB24FC01E752E1 +:103F1000FC4F4F01F901A1909F016401C81AD90A11 +:103F2000C40ED51EF601D080DA24F401D1924F01DF +:103F30002A173B0779F704964C5F5F4F803B91054A +:103F400009F0B5CF80E1F80111928A95E9F7229A3C +:103F5000219A2A98299A1FB7F8948091A401811177 +:103F600027C0E6EAF9E08491EAEBF9E09491E82FC2 +:103F7000F0E0EE0FFF1FE659F64FA591B491EC91DA +:103F8000E92321F461E08AE00E94E70661E08AE02B +:103F90000E9415078CB580618CBD8CB580648CBD8A +:103FA00061E08DE00E94150761E08BE00E9415073B +:103FB0008091A4018F5F8093A4011FBF2A986EE0B7 +:103FC00080E20E945B0E8BE496E40197F1F763E0D8 +:103FD00083E20E945B0E6FE584E20E945B0E6CE45C +:103FE00085E20E945B0E67E086E20E945B0E64E061 +:103FF0008DE30E945B0E8DE10E94AF0E843041F094 +:1040000063E780E50E945B0E64E08DE30E945B0E37 +:1040100086E00E94AF0E873009F04FC085E00E9415 +:10402000AF0E8C3409F049C062E082E20E945B0E60 +:1040300060E081E20E945B0E63E08CE30E945B0E15 +:104040001FEF1093030120E045E063E071E08BE295 +:104050000E94290E20E045E063E071E08AE20E94C0 +:10406000290E20E045E063E071E080E30E94290E24 +:1040700082EE0E94B50E81EE0E94B50E60E787E2E7 +:104080000E945B0E8091EA0180618093EA01809139 +:104090003F028E7F80933F0280913C028F3F29F444 +:1040A00088E191E00CC0FF0C31CF9FEF980F9E3F4D +:1040B000D8F40E94950E8111F3CF88E091E00E9420 +:1040C000140B64960FB6F894DEBF0FBECDBFDF9120 +:1040D000CF911F910F91FF90EF90DF90CF90BF9005 +:1040E000AF909F908F90089510933C02E6CF1F925F +:1040F0000F920FB60F9211242F933F934F935F931C +:104100006F937F938F939F93AF93BF93EF93FF939F +:1041100080E492E00E94FC05FF91EF91BF91AF9186 +:104120009F918F917F916F915F914F913F912F91CF +:104130000F900FBE0F901F9018951F920F920FB601 +:104140000F9211242F938F939F93EF93FF93E091FE +:104150005002F09151028081E0915602F091570295 +:1041600082FD1BC09081809159028F5F8F732091D7 +:104170005A02821741F0E0915902F0E0E05CFD4FF5 +:10418000958F80935902FF91EF919F918F912F917D +:104190000F900FBE0F901F9018958081F4CF1F9243 +:1041A0000F920FB60F9211242F933F938F939F93EB +:1041B000AF93BF938091340290913502A091360263 +:1041C000B09137023091330223E0230F2D3758F599 +:1041D0000196A11DB11D2093330280933402909368 +:1041E0003502A0933602B093370280913802909145 +:1041F0003902A0913A02B0913B020196A11DB11D76 +:104200008093380290933902A0933A02B0933B0214 +:10421000BF91AF919F918F913F912F910F900FBEC2 +:104220000F901F90189526E8230F0296A11DB11D2F +:10423000D2CF1F920F920FB60F9211242F933F935C +:104240004F935F936F937F938F939F93AF93BF939E +:10425000EF93FF93E0913D01F0913E010995FF91AD +:10426000EF91BF91AF919F918F917F916F915F91EE +:104270004F913F912F910F900FBE0F901F90189567 +:104280001F920F920FB60F9211242F933F934F93CB +:104290005F936F937F938F939F93AF93BF93EF93AE +:1042A000FF93E0913B01F0913C010995FF91EF9163 +:1042B000BF91AF919F918F917F916F915F914F913E +:1042C0003F912F910F900FBE0F901F9018951F9246 +:1042D0000F920FB60F9211242F933F934F935F933A +:1042E0006F937F938F939F93AF93BF93EF93FF93BE +:1042F000E0913102F0913202309749F0A685B785FE +:104300008585968D91FF14C09C918923A1F4FF911E +:10431000EF91BF91AF919F918F917F916F915F913D +:104320004F913F912F910F900FBE0F901F901895B6 +:104330009C91892361F7A389B4899C918589809593 +:1043400089238C93868997890197F1F7608D718D08 +:10435000A685B785558538E020E0CB010197F1F7B8 +:10436000822F90E095958795282F4C91452309F051 +:104370002068315091F7868D81FD209580912F0224 +:1043800090E001968F73992730913002381799F099 +:10439000A0912F02B0E0A151BE4F2C9380932F0229 +:1043A000828D938D0197F1F7A389B4898C919589BA +:1043B000892B8C93ACCF868D8160868FF1CF1F9235 +:1043C0000F920FB60F9211240F900FBE0F901F90F7 +:1043D0001895DB01FC0158E0149744E018960E9004 +:1043E00001924A95E1F75A95C1F708958F929F92ED +:1043F000AF92BF92CF92DF920F931F93EDB7FEB7AC +:10440000B8970FB6F894EDBF0FBEFEBF9F938F9382 +:1044100031968F0149015A016B01C801AAD2BB2014 +:1044200011F4AA2059F0C801B601A1D030E4C30E9E +:10443000D11C32E0931AA108B108F1CFC801B6012E +:10444000A40115D08F919F91B801C3DFEDB7FEB7DE +:10445000B8960FB6F894EDBF0FBEFEBF1F910F9137 +:10446000DF90CF90BF90AF909F908F900895523083 +:1044700090F09F938F937F936F935F934F9377D039 +:104480004F915F916F917F918F919F91525030E446 +:10449000630F711DECCFEDB7FEB70FB6E054F040DF +:1044A000F894EDBF0FBEFEBF3196242F26952695BA +:1044B000269550FB25F9DB01222329F0122E0D90C1 +:1044C00001921A94E1F730E8032E37E0342329F003 +:1044D00006943A95E9F73C91032A019223952933F2 +:1044E00008F421C030E4321B19F011923A95E9F733 +:1044F000FF973197BF01FF93EF939F938F935F9344 +:104500004F9335D04F915F918F919F91EF91FF9194 +:10451000DC0191963C9132503D9326E03C91304035 +:104520003D932A95D9F738E3321B21F011241192DB +:104530003A95E9F7DC01909638960D90040E0292B8 +:104540000D90051E029236E00D90011C02923A95E4 +:10455000D9F7F897BF010BD0EDB7FEB70FB6FF96AE +:104560003196F894EDBF0FBEFEBF112408954F920F +:104570005F926F927F928F929F92AF92BF92CF92F3 +:10458000DF92EF92FF920F931F93CF93DF934DB77C +:104590005EB79A01FB0140525140DA010FB6F89420 +:1045A0004DBF0FBE5EBF2F933F938F939F93119686 +:1045B00040E171916191319121912D933D936D93E2 +:1045C0007D934A95B1F740E3142EFD01FF97319793 +:1045D0000191119121913191C190D190E190F1908F +:1045E0006D2D7E2D8F2D9C2D41E0E9D14B015C017D +:1045F000B701C60142E0F9D186269726A826B9263A +:1046000073E0F694E794D794C7947A95D1F78C2405 +:104610009D24AE24BF24080D191D2A1D3B1D648D49 +:10462000758D868D978D060F171F281F391FC0A8FF +:10463000D1A8E2A8F3A8B701C60141E0D6D15C0138 +:104640004B0142E0D2D186269726A826B92672E0F1 +:10465000F694E794D7947A95D9F78D249E24AF24C5 +:10466000080D191D2A1D3B1D0D931D932D933D9380 +:104670001A9409F0AACFFF91EF91EF93FF9390E284 +:1046800011901D929A95E1F79097ED01E2E7F8E419 +:10469000BA9590E4192E4C885D886E887F88688969 +:1046A00079898A899B89C88CD98CEA8CFB8C4622B3 +:1046B0005722682279226095709580959095C62240 +:1046C000D722E822F9224C245D246E247F24F88826 +:1046D000C988DA88EB88B601C70142E070D14B0186 +:1046E0005C01B601C70143E080D186269726A82643 +:1046F000B926B701C60141E078D186269726A826BB +:10470000B926480C591C6A1C7B1C8C8C9D8CAE8C69 +:10471000BF8C480C591C6A1C7B1C8D909D90AD90E1 +:10472000BD90480C591C6A1C7B1C85909590A590E7 +:10473000B590480C591C6A1C7B1C688179818A8160 +:104740009B818C809D80AE80BF8086229722A8228C +:10475000B922C884D984EA84FB846C217D218E210E +:104760009F2186269726A826B9266C817D818E8179 +:104770009F816C217D218E219F2186269726A82648 +:10478000B926C880D980EA80FB80B601C70142E023 +:104790002CD18B019C01B701C60143E010D1062743 +:1047A0001727282739276F2D7C2D8D2D9E2D42E030 +:1047B00006D10627172728273927080D191D2A1D76 +:1047C0003B1D5CE16C969A919C835A95E1F7C888F1 +:1047D000D988EA88FB88C40CD51CE61CF71CC88A5B +:1047E000D98AEA8AFB8A040D151D261D371D088308 +:1047F00019832A833B831A9409F04DCFFF91EF91DF +:1048000058E00081118122813381699179918991E8 +:104810009991060F171F281F391F01931193219398 +:1048200031935A9571F7319652E066E04081450F19 +:104830004193552728F44081451F41936A95D1F74C +:104840005F914F910FB6F8944DBF0FBE5EBF11241C +:10485000DF91CF911F910F91FF90EF90DF90CF905C +:10486000BF90AF909F908F907F906F905F904F9090 +:104870000895982F8A4291443771CFFBC0B5A5DBCC +:10488000B5E95BC25639F111F159A4823F92D55E68 +:104890001CAB98AA07D8015B8312BE853124C37D67 +:1048A0000C55745DBE72FEB1DE80A706DC9B74F110 +:1048B0009BC1C1699BE48647BEEFC69DC10FCCA1D9 +:1048C0000C246F2CE92DAA84744ADCA9B05CDA8828 +:1048D000F97652513E986DC631A8C82703B0C77FFC +:1048E00059BFF30BE0C64791A7D55163CA066729A4 +:1048F0002914850AB72738211B2EFC6D2C4D130D6A +:10490000385354730A65BB0A6A762EC9C281852C56 +:104910007292A1E8BFA24B661AA8708B4BC2A3513A +:104920006CC719E892D1240699D685350EF470A08B +:104930006A1016C1A419086C371E4C774827B5BCFD +:10494000B034B30C1C394AAAD84E4FCA9C5BF36FE3 +:104950002E68EE828F746F63A5781478C88408027D +:10496000C78CFAFFBE90EB6C50A4F7A3F9BEF278A7 +:1049700071C6DC01E4E8F9E468E275917D936A951B +:10498000E1F7089567E6096A85AE67BB72F36E3C8E +:104990003AF54FA57F520E518C68059BABD9831F0A +:1049A00019CDE05B0000000000000000483038F046 +:1049B000592F982F872F762F652F4850F7CF5527DF +:1049C0008894442339F0661F771F881F991F551F4D +:1049D0004A95C9F7652B0895483038F0562F672F50 +:1049E000782F892F952F4850F7CF55278894442347 +:1049F00039F0979587957795679557954A95C9F7B3 +:104A0000952B0895AB01692F782F852F942F08954A +:104A1000CF93DF93FC0110821182128214821582DF +:104A20001682DC0117968AE1ED0119928A95E9F761 +:104A300082E08383DF91CF910895CF92DF92EF924E +:104A4000FF920F931F93CF93DF93CDB7DEB72897D5 +:104A50000FB6F894DEBF0FBECDBF10924302109286 +:104A6000420298EEC92E93E0D92EE12CF12CC0928F +:104A70004402D0924502E0924602F092470280E55D +:104A800091E0909341028093400285EC90E09093F6 +:104A90004D0280934C0284EC90E090934F028093FF +:104AA0004E0280EC90E0909351028093500281EC92 +:104AB00090E0909353028093520282EC90E09093A6 +:104AC00055028093540286EC90E0909357028093B5 +:104AD00056021092590210925A0210925B021092E2 +:104AE0005C0288E09E012F5F3F4FD901E82F1D92A5 +:104AF000EA95E9F711E01093C9041092CA041092E4 +:104B0000CB041092CC041092CD041092CE041092DB +:104B1000CF041092D0041092D8031092D7031092B1 +:104B2000E2041092E1041092E4041092E304E9ED2F +:104B3000F3E090EFDF011D929A95E9F71887F901EC +:104B4000A1EDB4E0982F01900D929A95E1F7F9014B +:104B5000A9EDB4E0982F01900D929A95E1F710938A +:104B6000E50492E09093E60493E09093E70494E0E8 +:104B70009093E80495E09093E90496E09093EA041A +:104B800097E09093EB048093EC0489E08093ED042C +:104B90008AE08093EE048BE08093EF048CE08093B6 +:104BA000F0048DE08093F1048EE08093F2048FE0B6 +:104BB0008093F30486EB93E00E94082585E993E057 +:104BC0000E940825109283031092820310928C0396 +:104BD00010928D0310928F0310928E031092900307 +:104BE0001093910384E791E0909381038093800375 +:104BF0001092640310926303C0926503D09266031F +:104C0000E0926703F092680382E691E0909362037A +:104C100080936103109278031092770310927A03C5 +:104C20001092790310927C0310927B0310927E0302 +:104C300010927D0380917F038E7F8D7F80937F0311 +:104C400081FB662760F9612786E00E94E70661E044 +:104C500086E00E941507E6EBF9E0E491E09371032A +:104C6000E2EAF9E0E491F0E0EE0FFF1FEE56F64FB6 +:104C700085919491909373038093720360E085E033 +:104C80000E94150780917F0381FD04C061E085E0EB +:104C90000E94E70615E010936D03E5EBF9E0E4915F +:104CA000E0936E03E1EAF9E0E491F0E0EE0FFF1F1C +:104CB000E857F64F859194919093700380936F031A +:104CC00080E493E00E94082583E08093460302E09D +:104CD000009345038FE193E00E94082581E08093D3 +:104CE00025038BE2809324038EEF92E00E94082537 +:104CF0000093040385E2809303038DED92E00E940C +:104D000008251093E3028FE28093E20228960FB603 +:104D1000F894DEBF0FBECDBFDF91CF911F910F91F1 +:104D2000FF90EF90DF90CF900895CF93DF93CDB7B2 +:104D3000DEB7CA55D1090FB6F894DEBF0FBECDBF9E +:104D4000789484B5826084BD84B5816084BD85B566 +:104D5000826085BD85B5816085BD80916E00816072 +:104D600080936E0010928100809181008260809318 +:104D7000810080918100816080938100809180001A +:104D80008160809380008091B10084608093B10045 +:104D90008091B00081608093B00080917A0084603F +:104DA00080937A0080917A00826080937A0080916B +:104DB0007A00816080937A0080917A008068809385 +:104DC0007A001092C100A89580E090E0892B11F044 +:104DD0000E9400000E940E058091DF018D7F80936C +:104DE000DF0180E090E0892B11F00E94000040E29A +:104DF00050E063E671E0CE0101960E94E12940E1B6 +:104E000050E06CE871E0CE0181960E94E12949E012 +:104E100050E063E871E0CE01C1960E94E12941E0D3 +:104E200050E06AE171E0CE01865A9F4F0E94E1296D +:104E300028EC31E040E050E0BE016F5F7F4FCE01D3 +:104E4000CA960E94F62141E050E063E071E08EEDE9 +:104E500091E00E94E1296FEF81E090E00E94092A31 +:104E60008091EB018F788093EB011092E101109219 +:104E7000E00185E391E00E94140B88E79EE19093A6 +:104E8000DD018093DC010E94A9076B017C010E9477 +:104E9000A9076C197D098E099F0960317742810548 +:104EA0009105C8F00E947B16A89500E010E0A89537 +:104EB0000E94F60A8091EA0184FD0E9444120E9439 +:104EC000E6170115110599F30E94F205882379F37D +:104ED0000E940000ECCF0E94F60A0E941A168091F0 +:104EE000EA0182FB882780F9A8958111DBCFCFCF1B +:104EF00082E691E090936203809361038091310296 +:104F0000909132028156934071F4E0917403F091D4 +:104F10007503908180917603809589238083109218 +:104F200032021092310208958F929F92AF92BF92F7 +:104F3000CF92DF92EF92FF92CF93DF93EC016881E3 +:104F400079818A819B81611571058105910521F423 +:104F500064E279ED8BE597E02DE133EF41E050E03D +:104F60000E941B2A49015A019B01AC01A7EAB1E446 +:104F70000E943A2A6B017C01ACEEB4EFA5019401CA +:104F80000E94482AC60ED71EE81EF91EF7FE06C06C +:104F900081E0C81AD108E10880E8F80AC882D982FD +:104FA000EA82FB82C701B6019F77DF91CF91FF9024 +:104FB000EF90DF90CF90BF90AF909F908F9008952B +:104FC0008F929F92AF92BF92CF92DF92EF92FF9219 +:104FD00060913F01709140018091410190914201A7 +:104FE000611571058105910521F464E279ED8BE588 +:104FF00097E02DE133EF41E050E00E941B2A490188 +:105000005A019B01AC01A7EAB1E40E943A2A6B0164 +:105010007C01ACEEB4EFA50194010E94482AC60EB3 +:10502000D71EE81EF91EF7FE06C081E0C81AD10897 +:10503000E10880E8F80AC0923F01D0924001E09276 +:105040004101F0924201C701B6019F77FF90EF90B6 +:10505000DF90CF90BF90AF909F908F900895609316 +:105060003F01709340018093410190934201089564 +:10507000FC0188279927E89421912032E9F329300F +:1050800010F02E30C8F32B3241F02D3239F46894F1 +:1050900004C00E94B528820F911D219120532A300F +:1050A000C0F31EF4909581959F4F08951F93FC01C6 +:1050B00099278827BC01E89411911032E9F319303F +:1050C00010F01E30C8F31B3251F01D3249F46894C1 +:1050D00006C00E94A028610F711D811D911D1191B4 +:1050E00010531A30B0F33EF4909580957095619509 +:1050F0007F4F8F4F9F4F1F910895FC010590002017 +:10510000E9F7809590958E0F9F1F0895FB01DC01B4 +:105110004150504048F005900D920020C9F701C061 +:105120001D9241505040E0F70895FC0161507040DD +:1051300001900110D8F7809590958E0F9F1F0895CC +:10514000592F482F372F262F660F771F881F991F3B +:10515000660F771F881F991F620F731F841F951F8B +:10516000660F771F881F991F08957AE0979F902DEB +:10517000879F802D910D112408950E94FF2808F427 +:1051800081E00895E89409C097FB3EF490958095DE +:10519000709561957F4F8F4F9F4F9923A9F0F92FFD +:1051A00096E9BB279395F695879577956795B7957B +:1051B000F111F8CFFAF4BB0F11F460FF1BC06F5F61 +:1051C0007F4F8F4F9F4F16C0882311F096E911C073 +:1051D000772321F09EE8872F762F05C0662371F094 +:1051E00096E8862F70E060E02AF09A95660F771FA8 +:1051F000881FDAF7880F9695879597F90895990F84 +:105200000008550FAA0BE0E8FEEF16161706E80790 +:10521000F907C0F012161306E407F50798F0621BB1 +:10522000730B840B950B39F40A2661F0232B242B86 +:10523000252B21F408950A2609F4A140A6958FEFA5 +:10524000811D811D08950E9436290C94A7290E9472 +:10525000992938F00E94A02920F0952311F00C9490 +:1052600090290C94962911240C94DB290E94B829CA +:1052700070F3959FC1F3950F50E0551F629FF001A9 +:10528000729FBB27F00DB11D639FAA27F00DB11DC2 +:10529000AA1F649F6627B00DA11D661F829F22274B +:1052A000B00DA11D621F739FB00DA11D621F839FD2 +:1052B000A00D611D221F749F3327A00D611D231FA8 +:1052C000849F600D211D822F762F6A2F11249F57F6 +:1052D00050409AF0F1F088234AF0EE0FFF1FBB1FF9 +:1052E000661F771F881F91505040A9F79E3F5105B8 +:1052F00080F00C9490290C94DB295F3FE4F3983EF6 +:10530000D4F3869577956795B795F795E7959F5F61 +:10531000C1F7FE2B880F911D9695879597F90895F3 +:1053200097F99F6780E870E060E008959FEF80EC58 +:10533000089500240A9416161706180609060895FB +:1053400000240A9412161306140605060895092E61 +:105350000394000C11F4882352F0BB0F40F4BF2BD0 +:1053600011F460FF04C06F5F7F4F8F4F9F4F089510 +:1053700057FD9058440F551F59F05F3F71F0479506 +:10538000880F97FB991F61F09F3F79F087950895EB +:10539000121613061406551FF2CF4695F1DF08C00A +:1053A000161617061806991FF1CF86957105610527 +:1053B00008940895E894BB2766277727CB0197F9CF +:1053C0000895DC01CB01FC01F999FECF06C0F2BDC6 +:1053D000E1BDF89A319600B40D9241505040B8F7B3 +:1053E0000895F999FECF92BD81BDF89A992780B5AD +:1053F0000895DC01A40FB51F4150504048F0CB0187 +:10540000840F951F2E910E940A2A41505040D0F7D8 +:105410000895262FF999FECF92BD81BDF89A019784 +:1054200000B4021639F01FBA20BD0FB6F894FA9AEC +:10543000F99A0FBE0895052E97FB1EF400940E9462 +:10544000322A57FD07D00E944D2A07FC03D04EF4A4 +:105450000C94322A50954095309521953F4F4F4FEF +:105460005F4F089590958095709561957F4F8F4F10 +:105470009F4F08950E946F2AA59F900DB49F900D95 +:10548000A49F800D911D11240895B7FF0C943A2A12 +:105490000E943A2A821B930B0895A1E21A2EAA1B9E +:1054A000BB1BFD010DC0AA1FBB1FEE1FFF1FA217D4 +:1054B000B307E407F50720F0A21BB30BE40BF50BD1 +:1054C000661F771F881F991F1A9469F7609570955A +:1054D000809590959B01AC01BD01CF010895A29FDD +:1054E000B001B39FC001A39F700D811D1124911DB8 +:1054F000B29F700D811D1124911D08955058BB2736 +:10550000AA270E94962A0C94A7290E94992938F06C +:105510000E94A02920F039F49F3F19F426F40C943E +:1055200096290EF4E095E7FB0C949029E92F0E9450 +:10553000B82958F3BA17620773078407950720F054 +:1055400079F4A6F50C94DA290EF4E0950B2EBA2F17 +:10555000A02D0B01B90190010C01CA01A001112479 +:10556000FF27591B99F0593F50F4503E68F11A1625 +:10557000F040A22F232F342F4427585FF3CF4695B6 +:1055800037952795A795F0405395C9F77EF41F16D8 +:10559000BA0B620B730B840BBAF09150A1F0FF0FA2 +:1055A000BB1F661F771F881FC2F70EC0BA0F621F8E +:1055B000731F841F48F4879577956795B795F7957E +:1055C0009E3F08F0B0CF9395880F08F09927EE0F13 +:1055D0009795879508950E94FF2A0C94A7290E9409 +:1055E000A02958F00E94992940F029F45F3F29F042 +:1055F0000C94902951110C94DB290C9496290E944B +:10560000B82968F39923B1F3552391F3951B550BF2 +:10561000BB27AA2762177307840738F09F5F5F4F85 +:10562000220F331F441FAA1FA9F335D00E2E3AF0C4 +:10563000E0E832D091505040E695001CCAF72BD0DC +:10564000FE2F29D0660F771F881FBB1F261737072D +:105650004807AB07B0E809F0BB0B802DBF01FF275F +:1056600093585F4F3AF09E3F510578F00C94902983 +:105670000C94DB295F3FE4F3983ED4F3869577954D +:105680006795B795F7959F5FC9F7880F911D969518 +:10569000879597F90895E1E0660F771F881FBB1F74 +:1056A000621773078407BA0720F0621B730B840B21 +:1056B000BA0BEE1F88F7E09508950E94642B68945A +:1056C000B1110C94DB2908950E94C02988F09F57DE +:1056D00098F0B92F9927B751B0F0E1F0660F771F16 +:1056E000881F991F1AF0BA95C9F714C0B13091F00C +:1056F0000E94DA29B1E008950C94DA29672F782FF7 +:105700008827B85F39F0B93FCCF3869577956795D0 +:10571000B395D9F73EF490958095709561957F4F3C +:105720008F4F9F4F08950E94FF2808F48FEF089530 +:105730000E94FF28880B990B089597FB072E16F4FB +:10574000009407D077FD09D00E94B12B07FC05D04B +:105750003EF4909581959F4F0895709561957F4F88 +:105760000895AA1BBB1B51E107C0AA1FBB1FA617A8 +:10577000B70710F0A61BB70B881F991F5A95A9F7FA +:1057800080959095BC01CD010895EE0FFF1F059007 +:10579000F491E02D099481E090E0F8940C94E82BCA +:1057A000FB01DC0102C001900D9241505040D8F73E +:1057B0000895FB01DC014150504048F001900D92EA +:1057C0000020C9F701C01D9241505040E0F70895F4 +:1057D00014E0C3EED4E004C0FE010E94C52B219664 +:0A57E000C43ED107C9F7F894FFCFCB +:1057EA00FFFFFF00FCE1A8A86C0E400BCB1EAB0B21 +:1057FA00B01E7C0B6A0B600BF41EC50B000000BFC9 +:10580A000000CD430000C8430000C8430000CD4358 +:10581A000503010101DC0D161FFFFFB507B50701DE +:10582A00000000FF0186000000000079000000006F +:10583A001E068805B5056B06E605C405D8050000F1 +:10584A000000EC078805310CEB0748086008D3070D +:10585A00000000007F098805310C300C8D080D0A04 +:10586A00006E616E00696E66006F766600322E33D6 +:10587A002E3100410042004343003051386A5373CD +:04588A006E45000067 +:00000001FF diff --git a/code/Carbon_sensor/Carbon_sensor.ino b/code/Carbon_sensor/Carbon_sensor.ino new file mode 100644 index 0000000..24931a3 --- /dev/null +++ b/code/Carbon_sensor/Carbon_sensor.ino @@ -0,0 +1,812 @@ +/* + * + * This is a CO2 sensor from the Candle project. + * + * You can attach both a carbon monoxide and a carbon dioxide sensor. + * + * Carbon monoxide is a dangerous, poisonous gas which is completely odourless. It is often formed when something is burning, but doesn't burn with enough oxygen. + * + * Carbon dioxide is what we breathe out. Plants consume carbon dioxide to grow. High levels of carbon dioxide can influence how you feel. + * + * Do not use this device as your sole carbon monoxide sensor! Use it as a support to your main carbon monoxide sensor only. + * + * SETTINGS */ + + + +//#define HAS_CO_SENSOR // Have you attached a CO sensor? + +#define HAS_CO2_SENSOR // Have you attached a CO2 sensor? + +#define MEASUREMENT_INTERVAL 60 // How many seconds do you want between each measurement? The minimum is 10 seconds. + +#define HAS_DISPLAY // Does the sensor have a little OLED display attached? + +#define ALLOW_CONNECTING_TO_NETWORK // Connect wirelessly. Is this device allowed to connect to the local Candle network? For privacy or security reasons you may prefer a stand-alone device. + +#define ALLOW_FAKE_DATA // Allow fake data? This feature is designed to make the sensor less intrusive in some social situations. If enabled, you can toggle this ability by pressing a button that you will need to connect as well. When fake data is being generated, a small F icon is visible on the display. + +//#define MY_REPEATER_FEATURE // Act as a repeater? The devices can pass along messages to each other to increase the range of your network. + +#define RF_NANO // RF-Nano. Check this box if you are using the RF-Nano Arduino, which has a built in radio. The Candle project uses the RF-Nano. + +/* END OF SETTINGS + * + */ + +//#define DEBUG +//#define MY_DEBUG // MySensors debugging. Enable MySensors debug output to the serial monitor, so you can check if the radio is working ok. + +// Enable and select the attached radio type +#define MY_RADIO_RF24 // This is a common and simple radio used with MySensors. Downside is that it uses the same frequency space as WiFi. +//#define MY_RADIO_NRF5_ESB // This is a new type of device that is arduino and radio all in one. Currently not suitable for beginners yet. +//#define MY_RADIO_RFM69 // This is an open source radio on the 433mhz frequency. Great range and built-in encryption, but more expensive and little more difficult to connect. +//#define MY_RADIO_RFM95 // This is a LoRaWan radio, which can have a range of 10km. + +// MySensors: Choose your desired radio power level. High power can cause issues on cheap Chinese NRF24 radio's. +//#define MY_RF24_PA_LEVEL RF24_PA_MIN +//#define MY_RF24_PA_LEVEL RF24_PA_LOW +//#define MY_RF24_PA_LEVEL RF24_PA_HIGH +#define MY_RF24_PA_LEVEL RF24_PA_MAX + +// Mysensors security +#define MY_ENCRYPTION_SIMPLE_PASSWD "0Q8jSsnE" // Be aware, the length of the password has an effect on memory use. +//#define MY_SIGNING_SOFT_RANDOMSEED_PIN A7 // Setting a pin to pickup random electromagnetic noise helps make encryption more secure. + +// Mysensors advanced settings +#define MY_TRANSPORT_WAIT_READY_MS 10000 // Try connecting for 10 seconds. Otherwise just continue. +//#define MY_RF24_CHANNEL 100 // In EU the default channel 76 overlaps with wifi, so you could try using channel 100. But you will have to set this up on every device, and also on the controller. +#define MY_RF24_DATARATE RF24_1MBPS // Slower datarate makes the network more stable? +#define MY_SPLASH_SCREEN_DISABLED // Saves a little memory. +//#define MY_DISABLE_RAM_ROUTING_TABLE_FEATURE // Saves a little memory. + + + +// PINS +// Be aware, on the RF-Nano pins 9 through 13 are used by the radio. +#define CO_RX_PIN 3 // The RX (receive) pin for the CO sensor. This should be connected to the TX (transmit) pin on the sensor module. +#define CO_TX_PIN 4 // The TX (transmit) pin for the CO sensor. This should be connected to the RX (receive) pin on the sensor module. +#define CO2_RX_PIN 5 // The RX (receive) pin for the CO2 sensor. This should be connected to the TX (transmit) pin on the sensor module. +#define CO2_TX_PIN 6 // The TX (transmit) pin for the CO2 sensor. This should be connected to the RX (receive) pin on the sensor module. + + +#define HORIZONTAL_START_POSITION 0 // Pixels from the left of the screen +#define DATA_TRANSMISSION_BUTTON_PIN 8 +#define TOGGLE_FAKE_DATA_PIN 7 + + +#ifdef RF_NANO +// If you are using an RF-Nano, you have to switch CE and CS pins. +#define MY_RF24_CS_PIN 9 // Used by the MySensors library. +#define MY_RF24_CE_PIN 10 // Used by the MySensors library. +#endif + + + + +// LIBRARIES (in the Arduino IDE go to Sketch -> Include Library -> Manage Libraries to add these if you don't have them installed yet.) +#include // MySensors library +#include // Serial data connection to the sensor + + +#ifdef HAS_DISPLAY + #define OLED_I2C_ADDRESS 0x3C + #include // Simple drivers for the screen. + #include // "SSD1306Ascii". + SSD1306AsciiAvrI2c oled; + byte screen_vertical_position = 3; // Used to always show both CO and CO2 levels at the top of the screen. + #define F_POSITION 66 // Horizontal position of the "F" icon, indicating it is allowed to generate fake data. + #define T_POSITION 72 // Horizontal position of the "T" icon, indicating it is allowed to transmit data. + #define W_POSITION 80 // Horizontal position of the "W" icon, indicating a wireless connection. +#endif + + +#ifdef HAS_CO_SENSOR +// CO sensor variables +SoftwareSerial co_sensor(CO_RX_PIN, CO_TX_PIN); // Receive (RX) and transmit (TX) pins. RX should always connect to TX on the opposite device. +int COValue = 0; +#endif + + +#ifdef HAS_CO2_SENSOR +// CO2 sensor variables +SoftwareSerial co2_sensor(CO2_RX_PIN, CO2_TX_PIN); // Receive (RX) and transmit (TX) pins. RX should always connect to TX on the opposite device. +int co2_value = 0; +float average_co2_value = 400; +#endif + + +// Mysensors settings +#define CO_CHILD_ID 1 // The child ID of the sensor that will be presented to the controller. +#define CO2_CHILD_ID 2 // The child ID of the sensor that will be presented to the controller. +#define DATA_TRANSMISSION_CHILD_ID 3 // The child ID of the data transmission switch. +#define CO_OPINION_CHILD_ID 4 // The child ID of the human readable opinion about the carbon monoxide level. +#define CO2_OPINION_CHILD_ID 5 // The child ID of the human readable opinion about the carbon dioxide level. + +const byte RADIO_DELAY = 100; // A few milliseconds delay between sending makes the radio happy. + +MyMessage relaymsg(DATA_TRANSMISSION_CHILD_ID, V_STATUS); // To toggle data transmission on or off remotely. + +MyMessage prefix_message(CO_CHILD_ID, V_UNIT_PREFIX); // Tell the controller what to display along with the value. + + +#ifdef HAS_CO_SENSOR +MyMessage CO_message(CO_CHILD_ID, V_LEVEL); // Sets up the message format that we'll be sending to the MySensors gateway later. +#endif + +#ifdef HAS_CO2_SENSOR +MyMessage CO2_message(CO2_CHILD_ID, V_LEVEL); // Sets up the message format that we'll be sending to the controller. +MyMessage info_message(CO2_OPINION_CHILD_ID,V_TEXT); // Sets up the message format that we'll be sending to the controller. The first part is the ID of the specific sensor module on this node. The second part describes what kind of data to expect. + +#endif + + + +#ifdef ALLOW_FAKE_DATA +// Fake data feature +#define AMOUNT_OF_MEASUREMENTS_TO_AVERAGE 5 // How many old measurements to remember. This is used to determine a good fake data range. +boolean sending_fake_data = false; // Experimental. Will allow a user to send fake data for a while. Useful in some social situations. +boolean desired_sending_fake_data = false; // If the user wants to change the state of sending fake data. +float fake_co2_value = 0; // Holds the meandering fake value +float co2_minimum_found = 400; +float co2_maximum_found = 410; +float last_co2_minimum_found = 400; +float last_co2_maximum_found = 410; + + +float co2_fakeness_range = 0; // How far of the last average the value can meander. +float fake_co2_jitter = 0; // Holds how much will actually be deviated from the average when generating a fake value. +float co2_fake_data_movement_factor = -0.5; // The odds that a new fake value will be above or below the current fake value. +float past_measurements[AMOUNT_OF_MEASUREMENTS_TO_AVERAGE]; // An array that holds previous measurements. Used to generate fake data. +byte measurements_fakeness_range_counter = AMOUNT_OF_MEASUREMENTS_TO_AVERAGE; // Current position in the array that holds a few of the last real measurements. +#endif + +// Connection toggle feature +boolean connected_to_network = false; +boolean transmission_state = true; +boolean previous_transmission_state = true; +// Other +#define LOOPDURATION 1000 // The main loop runs every x milliseconds. Normally this sensor has a 'heartbeat' of once every second. +boolean send_all_values = true; // If the controller asks the devive to re-present itself, then this is used to also resend all the current sensor values. + + + +void presentation() +{ +#ifdef ALLOW_CONNECTING_TO_NETWORK + // Send the sketch version information to the gateway and Controller + sendSketchInfo(F("Carbon sensor"), F("1.0")); wait(RADIO_DELAY); + + // Register all sensors to gateway: + present(DATA_TRANSMISSION_CHILD_ID, S_BINARY, F("Data transmission")); wait(RADIO_DELAY); + +#ifdef HAS_CO_SENSOR + present(CO_CHILD_ID, S_AIR_QUALITY, F("Carbon monoxide")); wait(RADIO_DELAY); +#endif + +#ifdef HAS_CO2_SENSOR + present(CO2_CHILD_ID, S_AIR_QUALITY, F("Carbon dioxide")); wait(RADIO_DELAY); +#ifdef HAS_CO_SENSOR + present(CO2_OPINION_CHILD_ID, S_INFO, F("Carbon dioxide opinion")); wait(RADIO_DELAY); // The opinion about the CO2 level. If a CO sensor is also present, the name of this property helps distinguish the two opinions. +#else + present(CO2_OPINION_CHILD_ID, S_INFO, F("Opinion")); wait(RADIO_DELAY); // The opinion about the CO2 level. +#endif +#endif + + send_all_values = true; +#endif +} + + +void setup() +{ + Serial.begin(115200); + wait(200); + + Serial.println(F("Hello, I am a carbon sensor")); + + transmission_state = loadState(DATA_TRANSMISSION_CHILD_ID); + + pinMode(CO2_RX_PIN, INPUT); + pinMode(CO2_TX_PIN, OUTPUT); + + pinMode(DATA_TRANSMISSION_BUTTON_PIN, INPUT_PULLUP); // Attach a push button to this pin to toggle whether or not the device is allowed to transmit data to the controller. + + +#ifdef ALLOW_FAKE_DATA + pinMode(TOGGLE_FAKE_DATA_PIN, INPUT_PULLUP); + //Serial.print(F("Toggle fake-data-mode using a button on pin ")); Serial.println(TOGGLE_FAKE_DATA_PIN); +#endif + + +#ifdef HAS_DISPLAY + // Initiate the display + oled.begin(&Adafruit128x64, OLED_I2C_ADDRESS); + oled.setFont(Adafruit5x7); + oled.ssd1306WriteCmd(SSD1306_DISPLAYON); + oled.setScroll(false); + oled.setCursor(HORIZONTAL_START_POSITION,0); + oled.print(F("CARBON")); +#endif + + +#ifdef ALLOW_CONNECTING_TO_NETWORK + if( isTransportReady() ){ // Check if a network connection has been established + Serial.println(F("Connected to gateway!")); + connected_to_network = true; + } + else { + Serial.println(F("! NO CONNECTION")); + } +#endif + + +#ifdef HAS_CO_SENSOR + co_sensor.begin(9600); +#ifdef HAS_DISPLAY + oled.setCursor(0,screen_vertical_position - 1); // The labels are shown slightly above the values. + oled.print(F("CO PPM:")); + screen_vertical_position = screen_vertical_position + 3; +#endif +#endif + + +#ifdef HAS_CO2_SENSOR + wait(2000); // Give the sensor some time to boot up + co2_sensor.begin(9600); + //wait(4000); // Give the sensor some time to boot up +#ifdef HAS_DISPLAY + oled.setCursor(0,screen_vertical_position - 1); // The labels are shown slightly above the values. + oled.print(F("CO2 PPM:")); +#endif +#endif + +#if not defined(HAS_CO_SENSOR) && not defined(HAS_CO2_SENSOR) + Serial.println(F("Please enable at least one sensor!")); +#ifdef HAS_DISPLAY + oled.setCursor(0,3); + oled.print(F("NO SENSORS ENABLED")); +#endif + while(1); +#endif + + wdt_enable(WDTO_8S); // Starts the watchdog timer. If it is not reset once every few seconds, then the entire device will automatically restart. +} + + +#ifdef ALLOW_CONNECTING_TO_NETWORK +void send_values() +{ + send(relaymsg.setSensor(DATA_TRANSMISSION_CHILD_ID).set(transmission_state)); wait(RADIO_DELAY); +#ifdef HAS_CO_SENSOR + send(prefix_message.setSensor(CO_CHILD_ID).set( F("ppm") )); delay(RADIO_DELAY); // Carbon values are always transmitted. They are not a large privacy risk, while being very important to safety. + if( COValue != 0 ){ + send(CO_message.setSensor(CO_CHILD_ID).set(COValue),1); + } +#endif + if(transmission_state){ +#ifdef HAS_CO2_SENSOR + send(prefix_message.setSensor(CO2_CHILD_ID).set( F("ppm") )); delay(RADIO_DELAY); + if(co2_value > 350){ + send(CO2_message.setSensor(CO2_CHILD_ID).set(co2_value),1); + } +#endif + } +} +#endif + + +void loop() +{ +#ifdef ALLOW_CONNECTING_TO_NETWORK + if( send_all_values ){ +#ifdef DEBUG + Serial.println(F("RESENDING VALUES")); +#endif + send_all_values = 0; + send_values(); + } +#endif + + +#ifdef ALLOW_FAKE_DATA + boolean fake_data_pin_state = digitalRead(TOGGLE_FAKE_DATA_PIN); + if( fake_data_pin_state == 0 ){ // If the button is being pressed + desired_sending_fake_data = !desired_sending_fake_data; // Switch the setting to its opposive value. + Serial.print(F("FAKE DATA TOGGLED TO ")); Serial.println(desired_sending_fake_data); + wait(500); // Wait a while to allow the button to be released + } +#endif + + + boolean transmission_button_state = digitalRead(DATA_TRANSMISSION_BUTTON_PIN); + if( transmission_button_state == 0 ){ // If the button is being pressed + transmission_state = !transmission_state; // Switch the setting to its opposive value. + saveState(DATA_TRANSMISSION_CHILD_ID, transmission_state); // Store the new preference, so that is the device is rebooted, it will still be correct. + Serial.println(F("Data transmission button pressed ")); + wait(500); // Wait a while to allow the button to be released +#ifdef ALLOW_CONNECTING_TO_NETWORK + + send(relaymsg.setSensor(DATA_TRANSMISSION_CHILD_ID).set(transmission_state)); +#endif + } + +if ( transmission_state != previous_transmission_state ){ + previous_transmission_state = transmission_state; + saveState(DATA_TRANSMISSION_CHILD_ID, transmission_state); + Serial.print(F("Sending new data transmission state: ")); Serial.println(transmission_state); + send(relaymsg.setSensor(DATA_TRANSMISSION_CHILD_ID).set(transmission_state)); +} + + + // + // HEARTBEAT LOOP + // runs every second (or as long as you want). By counting how often this loop has run (and resetting that counter back to zero after a number of loops), it becomes possible to schedule all kinds of things without using a lot of memory. + // The maximum time that can be scheduled is 255 * the time that one loop takes. So usually 255 seconds. + // + + static unsigned long lastLoopTime = 0; // Holds the last time the main loop ran. + static int loopCounter = 0; // Count how many loops have passed (reset to 0 after at most 254 loops). + unsigned long currentMillis = millis(); + + if( currentMillis - lastLoopTime > LOOPDURATION ){ + lastLoopTime = currentMillis; + loopCounter++; +#ifdef DEBUG + Serial.print("loopcounter:"); Serial.println(loopCounter); +#endif + if(loopCounter >= MEASUREMENT_INTERVAL){ + Serial.println(); Serial.println(F("__starting__")); + loopCounter = 0; + } + + wdt_reset(); // Reset the watchdog timer + + /* + // Used during development + if(measurements_counter == 10 && desired_sending_fake_data == false){ + Serial.println(); Serial.println(F("INITIATING FAKENESS----------------------------------------")); + desired_sending_fake_data = true; + } + */ + +#ifdef HAS_DISPLAY + // Show second counter + oled.set1X(); + oled.setCursor(100,0); + oled.print(MEASUREMENT_INTERVAL - loopCounter); + oled.clearToEOL(); + + screen_vertical_position = 3; // If there is one sensor attached, then new data should be shown at line 3 of the screen. If there are two, then data is shown on line 3 and line 6. + +#endif + + + // schedule + if( loopCounter == 1 ){ + + // CARBON MONIXODE +#ifdef HAS_CO_SENSOR + COValue = readCOValue(); // Get carbon monoxide level from sensor module +#ifdef HAS_DISPLAY + // Show CO level on the screen + //oled.set1X(); + oled.setCursor(HORIZONTAL_START_POSITION,screen_vertical_position); + + if (COValue == -1){ // -1 value means sensor probably not connected + oled.print(F("CHECK WIRE")); + oled.clearToEOL(); + break; + } + else if (COValue == -2){ + oled.print(F("DATA ERROR")); // -2 value means we got data form the sensor module was was not a CO2 level. For example, a response to some other command. + oled.clearToEOL(); + } + else{ + // Display CO value. + oled.print(COValue); + oled.clearToEOL(); + + // Show quality opinion the screen. + oled.setCursor(60,screen_vertical_position); + if (COValue > 0 && COValue < 450){ oled.print(F("GREAT"));} + else if (COValue < 700){ oled.print(F("GOOD"));oled.clearToEOL();} + else if (COValue < 1000){ oled.print(F("OK")); oled.clearToEOL();} + else if (COValue < 2000){ oled.print(F("POOR"));oled.clearToEOL();} + else if (COValue < 4500){ oled.print(F("BAD")); oled.clearToEOL();} + else { + oled.print(F("Wait..")); + oled.clearToEOL(); + } + } + screen_vertical_position = screen_vertical_position + 3; // If a CO sensor is attached, it's value will be displayed on top. The Co2 value will then be shown 3 lines below it. +#endif +#endif + + // CARBON DIOXIDE +#ifdef HAS_CO2_SENSOR + //int new_co2_value = readco2_value(); // Get carbon dioxide level from sensor module + co2_value = read_co2_value(); // Get carbon dioxide level from sensor module + Serial.print(F("fresh co2 value: ")); Serial.println(co2_value); +#ifdef DEBUG + if( co2_value == -1 || co2_value == -2 ){ + Serial.println(F("SENSOR ERROR -> GENERATING RANDOM DATA")); // Used during development to test even though no actual sensor is attached. + co2_value = random(500,600); + } +#endif + +#ifdef ALLOW_FAKE_DATA + if( co2_value > 350 && sending_fake_data == false){ // While fake data is not being created, we analyse the real data to look for the range it displays. + measurements_fakeness_range_counter++; + Serial.print(F("measurements_fakeness_range_counter = ")); Serial.println(measurements_fakeness_range_counter); + if( measurements_fakeness_range_counter >= AMOUNT_OF_MEASUREMENTS_TO_AVERAGE){ + Serial.print(F("Restarting min-max analysis around co2 value of ")); Serial.println(co2_value); + measurements_fakeness_range_counter = 0; + if( co2_maximum_found - co2_minimum_found != 0 && co2_maximum_found - co2_minimum_found < 30 ){ + co2_fakeness_range = co2_maximum_found - co2_minimum_found; // What is the difference between the highest and lowest co2 value we spotted recently. + last_co2_minimum_found = co2_minimum_found; + last_co2_maximum_found = co2_maximum_found; + } + co2_minimum_found = co2_value; + co2_maximum_found = co2_value; + } + + if(co2_value < co2_minimum_found){ + co2_minimum_found = co2_value; + Serial.println(F("new co2 value was smaller than minimum found.")); + } + else if(co2_value > co2_maximum_found){ + co2_maximum_found = co2_value; + Serial.println(F("new co2 value was bigger than maximum found.")); + } + else{ + Serial.println(F("new co2 values was not bigger or smaller than recent measurements.")); + } + Serial.print(F("potential min-max range: ")); Serial.println(co2_maximum_found - co2_minimum_found); + Serial.print(F("actual min-max range: ")); Serial.println(co2_fakeness_range); + } + + + if( desired_sending_fake_data == true ){ + Serial.println(F("User wants to generate fake data.")); + if( sending_fake_data == false ){ // On the first run of fake data, we try and figure out what the range to fake in is. + Serial.println(F("initiating fake data")); + sending_fake_data = true; + if(co2_fakeness_range == 0){ + co2_fakeness_range = 1; // The minimum to actually make some fake jitter + } + co2_fake_data_movement_factor = -0.5; + fake_co2_value = co2_value; // The initial fake value; + } + } + else{ + // The user no longer wants to generate fake data. + if( sending_fake_data == true ){ // If we were generating fake data, we should slowly move the fake value towards the real value. Only then can we stop generating the fake value. + last_co2_minimum_found = co2_value - co2_fakeness_range; + last_co2_maximum_found = co2_value + co2_fakeness_range; + + if(co2_value > fake_co2_value){ + co2_fake_data_movement_factor = -0.1; // By modifiying this factor to favour one direction, the fake data will move towards the real co2 value. + Serial.println(F("stopping faking, movement factor set to 0,9: ")); + } + else if(co2_value < fake_co2_value){ + Serial.println(F("stopping faking, movement factor set to -0,9: ")); + co2_fake_data_movement_factor = -0.9; + } + if( abs(fake_co2_value - co2_value) < co2_fakeness_range ){ // When the fake value is very close to the real value, the real value can take over again. + Serial.println(F("Faking has ended")); + sending_fake_data = false; + } + } + } + + if( sending_fake_data == true ){ + // We are now sending fake data. + fake_co2_jitter = (float)random( (co2_fakeness_range) * 10000) / 10000; + Serial.print(F("fake CO2 addition: ")); Serial.println(fake_co2_jitter); + + float flipped_coin = random(2); // This will be 0 or 1. + Serial.print(F("flipped coin: ")); Serial.println(flipped_coin); + Serial.print(F("co2_fake_data_movement_factor: ")); Serial.println(co2_fake_data_movement_factor); + float factor = flipped_coin + co2_fake_data_movement_factor; + Serial.print(F("actual movement factor: ")); Serial.println(factor); + fake_co2_jitter = fake_co2_jitter * factor; // The addition is now multiplied by -0,5 or +0,5. + Serial.print(F("fake CO2 jitter after movement factor: ")); Serial.println(fake_co2_jitter); + + Serial.print(F("last_min: ")); Serial.println(last_co2_minimum_found); + Serial.print(F("last_max: ")); Serial.println(last_co2_maximum_found); + if( fake_co2_jitter > 0 && fake_co2_value + fake_co2_jitter > last_co2_maximum_found){ // If the new addition (which can be negative) moves the fake data value ourside of the allowed range, then adjust it. + fake_co2_jitter = -fake_co2_jitter; + Serial.println("A"); + } + else if( fake_co2_jitter < 0 && fake_co2_value + fake_co2_jitter < last_co2_minimum_found){ // If the new addition (which can be negative) moves the fake data value ourside of the allowed range, then adjust it. + fake_co2_jitter = -fake_co2_jitter; + Serial.println("B"); + } + else{ + Serial.println("CC"); + } + Serial.print(F("fake CO2 addition after bounds check: ")); Serial.println(fake_co2_jitter); + + fake_co2_value = fake_co2_value + fake_co2_jitter; + co2_value = int(fake_co2_value); + /* + // Create meandering data effect + if( flipped_coin == 0 && fake_co2_value + fake_co2_jitter > average_co2_value + co2_fakeness_range ){ // Check if there is head room to make the fake data value change in the random direction. + co2_value = fake_co2_value - fake_co2_jitter; // There is no room to go up, the fake value should go down. + } + else if( flipped_coin == 1 && fake_co2_value - fake_co2_jitter < average_co2_value - co2_fakeness_range ){ + co2_value = fake_co2_value + fake_co2_jitter; // There is no room to go down, the fake value should go up. + } + else{ + if( flipped_coin ){ fake_co2_jitter = -fake_co2_jitter; } // If we have not reached the maximum high or low fake data value, then randomly add or subtract the addition. + fake_co2_value = fake_co2_value + fake_co2_jitter; + } + */ + Serial.print(F(" {}{}{} Fake CO2 value: ")); Serial.println(co2_value); + + } +#endif // End of allow fake data + + + +#ifdef HAS_DISPLAY + // Show CO2 level on the screen + oled.set2X(); + oled.setCursor(HORIZONTAL_START_POSITION,screen_vertical_position); + + if (co2_value == -1){ // -1 value means sensor probably not connected + oled.print(F("CHECK WIRE")); + oled.clearToEOL(); + } + else if (co2_value == -2){ + oled.print(F("DATA ERROR")); // -2 value means we got data form the sensor module was was not a CO2 level. For example, a response to some other command. + oled.clearToEOL(); + } + else if( co2_value > 350 && co2_value < 5001){ + // Display CO2 value. + oled.print(co2_value); + oled.clearToEOL(); + + // Show quality opinion the screen. + oled.setCursor(60,screen_vertical_position); + + if (co2_value < 500){ oled.print(F("GREAT"));} + else if (co2_value < 700){ oled.print(F("GOOD"));} + else if (co2_value < 1000){ oled.print(F("OK"));} + else if (co2_value < 2000){ oled.print(F("POOR"));} + else if (co2_value < 5001){ oled.print(F("BAD"));} + else { + oled.print(F("Wait..")); + } + oled.clearToEOL(); + } +#ifdef DEBUG + else{ + Serial.println(F("CO2 value was out of bounds")); + } +#endif + + +#endif // End of has_display +#endif // End of hasCO2senor + } + + + else if( loopCounter == 2 ){ // Send the data + +#ifdef HAS_CO_SENSOR + if( COValue > 0 && COValue < 4500 ){ // Avoid sending erroneous values +#ifdef ALLOW_CONNECTING_TO_NETWORK + connected_to_network = false; + Serial.println(F("Sending CO to controller")); + send(CO_message.setSensor(CO_CHILD_ID).set(COValue),1); // We ask the controller to acknowledge that it has received the data. +#endif // end of allow connecting to network + } +#endif // end of has CO sensor + + +#ifdef HAS_CO2_SENSOR + if( co2_value > 300 && co2_value < 4500 ){ // Avoid sending erroneous values +#ifdef ALLOW_CONNECTING_TO_NETWORK + if( transmission_state ){ + connected_to_network = false; // If the network connection is ok, then this will be immediately set back to true. + Serial.print(F("Sending CO2 value: ")); Serial.println(co2_value); + connected_to_network = false; // If the network connection is ok, then this will be immediately set back to true: + send(CO2_message.setSensor(CO2_CHILD_ID).set(co2_value),1); // We send the data, and ask the controller to acknowledge that it has received the data. + wait(RADIO_DELAY); + + // Also send the human readable opinion + if (co2_value < 450){ send(info_message.setSensor(CO2_OPINION_CHILD_ID).set( F("Great") )); } + else if (co2_value < 700){ send(info_message.setSensor(CO2_OPINION_CHILD_ID).set( F("Good") )); } + else if (co2_value < 1000){ send(info_message.setSensor(CO2_OPINION_CHILD_ID).set( F("OK") )); } + else if (co2_value < 2000){ send(info_message.setSensor(CO2_OPINION_CHILD_ID).set( F("Poor") )); } + else if (co2_value < 5000){ send(info_message.setSensor(CO2_OPINION_CHILD_ID).set( F("Bad") )); } + + } + else{ + Serial.println(F("Not allowed to send the CO2 data")); + } +#endif // end of allow connecting to network + } +#endif // end of has CO2 sensor + } + + +#ifdef HAS_DISPLAY + else if( loopCounter == 3 ){ // Show the various states on the display + oled.set1X(); + oled.setCursor(W_POSITION,0); + if( connected_to_network ){ // Add W icon to the top right corner of the screen, indicating a wireless connection. + oled.print(F("W")); + }else { + oled.print(F(".")); // Remove W icon + } + } + + + // The following two are updated every second. + oled.set1X(); + + #ifdef ALLOW_FAKE_DATA + oled.setCursor(F_POSITION,0); + if( desired_sending_fake_data && sending_fake_data){ // We are sending fake data + oled.print(F("F")); + } + else if(desired_sending_fake_data != sending_fake_data){ // In the transition between real and fake data + oled.print(F("f")); + } + else{ // No fake data is being generated + oled.print(F(".")); + } +#endif + +#ifdef ALLOW_CONNECTING_TO_NETWORK + oled.setCursor(T_POSITION,0); + if( transmission_state ){ + oled.print(F("T")); + } + else{ + oled.print(F(".")); + } +#endif + +#endif // end of has display + } +} + + + +#ifdef HAS_CO_SENSOR +int readCOValue() +{ + + while (co_sensor.read()!=-1) {}; // Clear serial buffer + + char response[9]; // Holds response from sensor + byte requestReading[9] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79}; + + Serial.println(F("Requesting data from CO sensor module")); + co_sensor.write(requestReading, 9); // Request PPM CO + co_sensor.readBytes(response, 9); + + // Do some checks on the response: + if (byte(response[0]) != 0xFF){ + Serial.println(F("! Sensor not connected?")); + while (co_sensor.read()!=-1) {}; // Empty the serial buffer, for a fresh start, just in case. + return -1; + } + if (byte(response[1]) != 0x86){ + Serial.println(F("! Sensor did not send CO data")); + return -2; + } + // Did the data get damaged along the way? + char check = getCheckSum(response); + if (response[8] != check) { + Serial.println(F("ERROR: checksum did not match")); + return -2; + } + + int high = response[2]; + int low = response[3]; + return high * 256 + low; +} +#endif + + +#ifdef HAS_CO2_SENSOR +int read_co2_value() +{ + + while (co2_sensor.read()!=-1) {}; // Clear serial buffer + + char response[9]; // Holds response from sensor + byte requestReading[9] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79}; + + Serial.println(F("Requesting data from CO2 sensor module")); + co2_sensor.write(requestReading, 9); // Request data from sensor module. + co2_sensor.readBytes(response, 9); + + // Do some checks on the response: + if (byte(response[0]) != 0xFF){ + Serial.println(F("! Is the CO2 sensor connected?")); + return -1; + } + if (byte(response[1]) != 0x86){ + Serial.println(F("! Non-sensor data")); +#ifdef DEBUG + Serial.println(response[1]); +#endif + return -2; + } + // Did the data get damaged along the way? + char check = getCheckSum(response); + if (response[8] != check) { + Serial.println(F("! Corrupted data")); + return -2; + } + + int high = response[2]; + int low = response[3]; + return high * 256 + low; +} +#endif + + +#ifdef ALLOW_CONNECTING_TO_NETWORK +void receive(const MyMessage &message) +{ + Serial.println(F(">> receiving message")); + connected_to_network = true; + + if( message.isAck() ){ + Serial.println(F("-Got echo")); + return; + } + + if (message.type == V_STATUS && message.sensor == DATA_TRANSMISSION_CHILD_ID ){ + transmission_state = message.getBool(); //?RELAY_ON:RELAY_OFF; + Serial.print(F("-New desired transmission state: ")); Serial.println(transmission_state); + } +} +#endif + +// A helper function to check the integrity of a received sensor message. +byte getCheckSum(byte* packet) +{ + byte i; + unsigned char checksum = 0; + for (i = 1; i < 8; i++) { + checksum += packet[i]; + } + checksum = 0xff - checksum; + checksum += 1; + return checksum; +} + + + +/* + * + * This code makes use of the MySensors library: + * + * The MySensors Arduino library handles the wireless radio link and protocol + * between your home built sensors/actuators and HA controller of choice. + * The sensors forms a self healing radio network with optional repeaters. Each + * repeater and gateway builds a routing tables in EEPROM which keeps track of the + * network topology allowing messages to be routed to nodes. + * + * Created by Henrik Ekblad + * Copyright (C) 2013b-2015 Sensnology AB + * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors + * + * Documentation: http://www.mysensors.org + * Support Forum: http://forum.mysensors.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + ******************************* + * + */ diff --git a/code/Dust_sensor/Dust_sensor.arduino.avr.nano.elf b/code/Dust_sensor/Dust_sensor.arduino.avr.nano.elf new file mode 100644 index 0000000..705c228 Binary files /dev/null and b/code/Dust_sensor/Dust_sensor.arduino.avr.nano.elf differ diff --git a/code/Dust_sensor/Dust_sensor.arduino.avr.nano.hex b/code/Dust_sensor/Dust_sensor.arduino.avr.nano.hex new file mode 100644 index 0000000..65c66ab --- /dev/null +++ b/code/Dust_sensor/Dust_sensor.arduino.avr.nano.hex @@ -0,0 +1,1404 @@ +:100000000C94E0030C9475200C944E200C949C20CE +:100010000C949C200C949C200C9414210C940804A7 +:100020000C9408040C9408040C9408040C94080420 +:100030000C9408040C9408040C9408040C94080410 +:100040000C9404200C9408040C94D21F0C94AC1F44 +:100050000C9408040C9408040C9408040C940804F0 +:100060000C9408040C940804AED580A83FD300403B +:100070008D142002A1C8DA12817FD9F1DB40A4A639 +:10008000AF200054002D4E6577206D61795F736558 +:100090006E645F646174612073746174653A2000FA +:1000A0003E3E2052656365697665642061636B6ED0 +:1000B0006F776C656467656D656E7400637C777BD4 +:1000C000F26B6FC53001672BFED7AB76CA82C97D54 +:1000D000FA5947F0ADD4A2AF9CA472C0B7FD9326E5 +:1000E000363FF7CC34A5E5F171D8311504C723C3E9 +:1000F0001896059A071280E2EB27B27509832C1A2D +:100100001B6E5AA0523BD6B329E32F8453D100ED86 +:1001100020FCB15B6ACBBE394A4C58CFD0EFAAFB6A +:10012000434D338545F9027F503C9FA851A3408F32 +:10013000929D38F5BCB6DA2110FFF3D2CD0C13EC4A +:100140005F974417C4A77E3D645D197360814FDCDF +:10015000222A908846EEB814DE5E0BDBE0323A0AC3 +:100160004906245CC2D3AC629195E479E7C8376D47 +:100170008DD54EA96C56F4EA657AAE08BA78252E6C +:100180001CA6B4C6E8DD741F4BBD8B8A703EB566F5 +:100190004803F60E613557B986C11D9EE1F89811E6 +:1001A00069D98E949B1E87E9CE5528DF8CA1890DD5 +:1001B000BFE6426841992D0FB054BB1631302E3046 +:1001C0003A00322E353A002000540021204E4F20B4 +:1001D000434F4E4E454354494F4E005700436F6EB8 +:1001E0006E656374656420746F2067617465776100 +:1001F00079210046494E45204455535400000005DE +:10020000072060000000000000005F000000070001 +:100210000700147F147F14242A7F2A1223130864F2 +:100220006236495620500008070300001C22410096 +:100230000041221C002A1C7F1C2A08083E080800D6 +:100240008070300008080808080000606000201076 +:100250000804023E5149453E00427F400072494930 +:1002600049462141494D331814127F102745454511 +:10027000393C4A4949314121110907364949493632 +:10028000464949291E0000140000004034000000C7 +:1002900008142241141414141400412214080201F9 +:1002A0005909063E415D594E7C1211127C7F494925 +:1002B00049363E414141227F4141413E7F494949C2 +:1002C000417F090909013E414151737F0808087FB8 +:1002D00000417F41002040413F017F081422417FBF +:1002E000404040407F021C027F7F0408107F3E4157 +:1002F00041413E7F090909063E4151215E7F0919AE +:100300002946264949493203017F01033F404040C5 +:100310003F1F2040201F3F4038403F6314081463B4 +:1003200003047804036159494D43007F4141410270 +:1003300004081020004141417F04020102044040B2 +:10034000404040000307080020545478407F284470 +:1003500044383844444428384444287F385454545A +:100360001800087E090218A4A49C787F0804047869 +:1003700000447D40002040403D007F1028440000A4 +:10038000417F40007C047804787C08040478384479 +:10039000444438FC1824241818242418FC7C08042D +:1003A0000408485454542404043F44243C4040204E +:1003B0007C1C2040201C3C4030403C4428102844F9 +:1003C0004C9090907C4464544C44000836410000AA +:1003D00000770000004136080002010204023C26BA +:1003E00023263C68001980400048656C6C6F2C2007 +:1003F0004920616D206120647573742073656E738C +:100400006F720064617461207472616E736D6973E0 +:1004100073696F6E00322E35206D6963726F6D6582 +:1004200074657273003130206D6963726F6D65742D +:10043000657273202620736D616C6C657200312EBD +:10044000310046696E6520647573742073656E7340 +:100450006F720075672F6D330075672F6D33004124 +:10046000766572616765207031303A2000417665AB +:10047000726167652070322E353A200053656E64D4 +:10048000696E672066616B6520646174612C2073FE +:100490006F2061766572616765206973206E6F7485 +:1004A000206368616E67696E672E0020004E6F20C2 +:1004B000636F6E6E656374696F6E20746F20636F17 +:1004C0006E74726F6C6C657221005700436F6E6EB4 +:1004D000656374696F6E20746F20636F6E74726FE2 +:1004E0006C6C6572206973206F6B2E002D3E20733B +:1004F000656E64696E672031302E303A20003C3CD6 +:100500002073656E64696E672046414B452031302B +:100510002E303A20007031305F6164646974696F15 +:100520006E203D20003C3C2073656E64696E672040 +:10053000322E353A20003C3C2073656E64696E674C +:100540002046414B4520322E353A2000322E3520B0 +:100550004164646974696F6E3A200057696C6C205D +:1005600073656E64207265616C20646174612061E2 +:100570006761696E2E0053656E736F7220676F69D5 +:100580006E6720746F20736C6565702E0042414465 +:10059000202000504F4F5220004F4B20202000477A +:1005A0004F4F44200047524541540046414B45447B +:1005B00000424144202000504F4F5220004F4B201A +:1005C000202000474F4F44200047524541540046E9 +:1005D000414B454400202020002020200057616925 +:1005E00074696E6720666F72206461746100417384 +:1005F0006B696E6720666F7220667265736820642F +:1006000061746120616761696E0041736B696E6737 +:1006100020666F7220667265736820646174610081 +:1006200053656E736F722077616B696E67207570AA +:10063000005F5F7374617274696E675F5F002000B2 +:100640004E4554574F524B20544F47474C4544203A +:10065000544F200052096AD53036A538BF40A39EBA +:1006600081F3D7FB7CE339829B2FFF87348E434491 +:10067000C4DEE9CB547B9432A6C2233DEE4C950BED +:1006800042FAC34E082EA16628D924B2765BA2494D +:100690006D8BD12572F8F66486689816D4A45CCC6C +:1006A0005D65B6926C704850FDEDB9DA5E1546573F +:1006B000A78D9D8490D8AB008CBCD30AF7E4580575 +:1006C000B8B34506D02C1E8FCA3F0F02C1AFBD0381 +:1006D00001138A6B3A9111414F67DCEA97F2CFCE52 +:1006E000F0B4E67396AC7422E7AD3585E2F937E8ED +:1006F0001C75DF6E47F11A711D29C5896FB7620E2F +:10070000AA18BE1BFC563E4BC6D279209ADBC0FE0F +:1007100078CD5AF41FDDA8338807C731B1121059BC +:100720002780EC5F60517FA919B54A0D2DE57A9FAE +:1007300093C99CEFA0E03B4DAE2AF5B0C8EBBB3CA3 +:1007400083539961172B047EBA77D626E169146327 +:1007500055210C7D00030C0F30333C3FC0C3CCCF80 +:10076000F0F3FCFF00000000240027002A00000036 +:100770000000250028002B000000000023002600B8 +:100780002900040404040404040402020202020214 +:100790000303030303030102040810204080010245 +:1007A00004081020010204081020000000080002C4 +:1007B00001000003040700000000000000005224B4 +:1007C00011241FBECFEFD8E0DEBFCDBF11E0A0E007 +:1007D000B1E0EAEFF6E502C005900D92A63AB10746 +:1007E000D9F724E0A6EAB1E001C01D92AF3CB20700 +:1007F000E1F713E0C0EED3E004C02197FE010E94B0 +:10080000442BCF3DD107C9F70E9427250C947B2BA1 +:100810000C940000E3E5F2E0A089B18982E08C93BA +:10082000A485B5851C92A685B78580E18C93108E32 +:10083000A489B58986E08C93A289B3898C91806163 +:100840008C93A289B3898C9188608C93A289B38997 +:100850008C9180688C930288F389E02D80818F7D54 +:1008600080830895DB01FC01242F3C913083119695 +:100870003C911197318312963C91129732831396D3 +:100880003C9113973383245014963496243068F7A0 +:100890002CEF240F2695269530E02F5F3F4F220F37 +:1008A000331F220F331F4370DB01A20FB31FFC0164 +:1008B000E20FF31F415018F08D918193FBCF089503 +:1008C0004F925F926F927F928F929F92AF92BF9260 +:1008D000CF92DF92EF92FF920F931F93CF93DF930C +:1008E00088248394912CB12CA12CD12CC12CF12CD7 +:1008F000E12CD0E0C0E06AE0462E512C612C772438 +:1009000073948091550390915603DC01ED91FC9115 +:100910000084F185E02D0995181619060CF0A3C086 +:100920008091550390915603DC01ED91FC91008478 +:10093000F185E02D099592012C1B3D0B8217930741 +:100940000CF491C08091550390915603DC01ED9118 +:10095000FC910284F385E02D09959C013327CA3070 +:10096000D10588F4FE01E954FB4F0C94442B020599 +:100970000D05C10414051B051F05180518052405E0 +:100980002F05890169012196CA30D10509F0B9CF37 +:10099000B1E0AB16B10409F0B4CFB701FF0C880B7E +:1009A000990B0E945F2820E030E040E251E40E9471 +:1009B0007E2A609347027093480280934902909385 +:1009C0004A02B601DD0C880B990B0E945F2820E0DB +:1009D00030E040E251E40E947E2A60934B02709323 +:1009E0004C0280934D0290934E02912C812CB12C9D +:1009F000A12C10E000E0D12CC12CF12CE12CD0E096 +:100A0000C0E07FCFC1E0D0E02A3A310511F4D0E058 +:100A1000C0E0D195C195D109B6CF203C310509F48C +:100A2000B2CFCFEFDFEFAFCF922F8827C80ED91EFE +:100A3000020F131FA8CF020F131F7901A4CF922F0B +:100A40008827E80EF91EF4CFC801B3010E94302BAD +:100A50002817390731F7AA24A394B12C94CF2B3A45 +:100A60003105F9F690CFC401DF91CF911F910F911D +:100A7000FF90EF90DF90CF90BF90AF909F908F90BE +:100A80007F906F905F904F900895DC01ED91FC9105 +:100A90000190F081E02D0994EF92FF920F931F9344 +:100AA000CF93DF938C017B01D0E0C0E0F701EC0F26 +:100AB000FD1F6491662361F0D801ED91FC910190D6 +:100AC000F081E02DC8010995892B11F02196EECF18 +:100AD000CE01DF91CF911F910F91FF90EF9008957C +:100AE000AF92BF92CF92DF92EF92FF920F931F933C +:100AF000CF93DF936C017B018B01040F151FEB017A +:100B00005E01AE18BF08C017D10759F06991D60130 +:100B1000ED91FC910190F081E02DC6010995892BA2 +:100B200079F7C501DF91CF911F910F91FF90EF9061 +:100B3000DF90CF90BF90AF900895FC01538D448D0E +:100B4000252F30E0842F90E0821B930B541710F078 +:100B5000CF96089501970895FC01918D828D981785 +:100B600061F0A28DAE0FBF2FB11D5D968C91928D5D +:100B70009F5F9F73928F90E008958FEF9FEF08958E +:100B8000FC01918D828D981731F0828DE80FF11D57 +:100B9000858D90E008958FEF9FEF0895FC01918D72 +:100BA000228D892F90E0805C9F4F821B91098F736B +:100BB0009927089583E592E00E94CE0521E0892BD4 +:100BC00009F420E0822F0895FC01A48DA80FB92F0D +:100BD000B11DA35ABF4F2C91848D90E001968F7365 +:100BE0009927848FA689B7892C93A089B1898C9184 +:100BF000837080648C93938D848D981306C00288D3 +:100C0000F389E02D80818F7D80830895EF92FF929C +:100C10000F931F93CF93DF93EC0181E0888F9B8D1F +:100C20008C8D98131AC0E889F989808185FF15C0D9 +:100C30009FB7F894EE89FF896083E889F9898081FC +:100C40008370806480839FBF81E090E0DF91CF91CB +:100C50001F910F91FF90EF900895F62E0B8D10E0ED +:100C60000F5F1F4F0F731127E02E8C8D8E110CC05C +:100C70000FB607FCFACFE889F989808185FFF5CFA7 +:100C8000CE010E94E405F1CFEB8DEC0FFD2FF11D9D +:100C9000E35AFF4FF0829FB7F8940B8FEA89FB89E4 +:100CA00080818062CFCFCF93DF93EC01888D882342 +:100CB000B9F0AA89BB89E889F9898C9185FD03C0BF +:100CC000808186FD0DC00FB607FCF7CF8C9185FFA4 +:100CD000F2CF808185FFEDCFCE010E94E405E9CF00 +:100CE000DF91CF91089580E090E0892B29F00E9458 +:100CF000DA0581110C9400000895833081F028F406 +:100D0000813099F08230A9F008958730A9F08830B9 +:100D1000C9F08430B1F4809180008F7D03C0809150 +:100D200080008F7780938000089584B58F7784BD8D +:100D3000089584B58F7DFBCF8091B0008F7780932D +:100D4000B00008958091B0008F7DF9CFCF93DF93ED +:100D5000E0EBF7E08491ECE9F7E0D491E8E8F7E024 +:100D6000C491CC23A1F081110E947D06EC2FF0E00C +:100D7000EE0FFF1FE858F84FA591B491EC91ED23C9 +:100D800081E090E009F480E0DF91CF91089580E068 +:100D900090E0FACF1F93CF93DF93282F30E0F90133 +:100DA000E655F84F8491F901EA56F84FD491F901CC +:100DB000EE57F84FC491CC23A9F0162F81110E9451 +:100DC0007D06EC2FF0E0EE0FFF1FE259F84FA591E2 +:100DD000B4918FB7F894EC91111108C0D095DE232F +:100DE000DC938FBFDF91CF911F910895DE2BF8CF59 +:100DF000CF93DF9390E0FC01EA56F84F24918E5791 +:100E0000984FFC0184918823D1F090E0880F991FBE +:100E1000FC01EC59F84FA591B491FC01E259F84F4F +:100E2000C591D49161110EC09FB7F8948C91E22FB7 +:100E3000E0958E238C932881E223E8839FBFDF9186 +:100E4000CF9108958FB7F894EC91E22BEC938FBF7C +:100E5000F6CF3FB7F8948091430290914402A0915D +:100E60004502B091460226B5A89B05C02F3F19F058 +:100E70000196A11DB11D3FBFBA2FA92F982F88271A +:100E8000BC01CD01620F711D811D911D42E0660FF5 +:100E9000771F881F991F4A95D1F708958F929F92C7 +:100EA000AF92BF92CF92DF92EF92FF926B017C01E3 +:100EB0000E9429074B015C01C114D104E104F10433 +:100EC000B9F00E942907681979098A099B09683EC7 +:100ED00073408105910580F321E0C21AD108E10831 +:100EE000F10888EE880E83E0981EA11CB11CE4CFA7 +:100EF000FF90EF90DF90CF90BF90AF909F908F903A +:100F000008952FB7F89460913F027091400280914C +:100F10004102909142022FBF08950895CF93DF932D +:100F2000EC018D85882329F081508D87DF91CF9149 +:100F300008959C818E819817C8F788896827E88171 +:100F4000F9810084F185E02D42E0CE0109958C8184 +:100F50008F5F8C83EBCF20913C0230913D022817AC +:100F6000390771F490913B0280913A02981741F051 +:100F7000E0913B02F0E0E650FE4F808190E0089562 +:100F80008FEF9FEF08950895EF92FF920F931F93B5 +:100F9000CF93DF93DC015C96ED90FC905D97E114BC +:100FA000F10479F481E090E013969C938E9312976C +:100FB00090E080E0DF91CF911F910F91FF90EF9033 +:100FC00008955196ED91FC91529750968C915097BF +:100FD000982F90950FB75E962C915E97122F1270F6 +:100FE00021FD6095F8942081112319F1282B20838D +:100FF000E7012197F1F728E0462F50E0308160FFAC +:101000001AC0382B3083E7012197F1F7BA017595A3 +:101010006795215089F7112381F08081892380838E +:101020000FBF5C968D919C910197F1F781E090E064 +:10103000C1CF2923DCCF3923E5CF9081892BEFCF96 +:1010400020913C0230913D022817390771F48091BC +:101050003A0220913B0290E0805C9F4F821B9109F5 +:1010600060E470E00E94302B089590E080E00895E5 +:1010700020913C0230913D0228173907B9F4909134 +:101080003B0280913A02981789F0E0913B02F0E030 +:10109000E650FE4F808120913B0230E02F5F3F4FB2 +:1010A0002F73332720933B0290E008958FEF9FEF3B +:1010B0000895FC016093BC008091BC0087FFFCCFC9 +:1010C0009091B900987F90830895DF92EF92FF92FC +:1010D0000F931F93CF93DF93EC01D62EF42E8C89C0 +:1010E000882351F0411108C084E98093BC008091AD +:1010F000BC0084FDFCCF1C8A8E010E5E1F4F8C89C4 +:1011000081111AC0EB8864EAC8010E9459088A89D3 +:10111000883011F0803139F4EE0CE092BB0064E8C5 +:10112000C8010E94590880E0F11080E48093BB0060 +:1011300064E8C8010E945908D092BB0064E8C80165 +:101140000E94590882E0F8120BC08C898F5F8C8B4B +:10115000DF91CF911F910F91FF90EF90DF90089555 +:1011600084E98093BC008091BC0084FDFCCF1C8A84 +:10117000EFCFDC01ED91FC910084F185E02D40E0A2 +:1011800009940F931F93CF938C01FC016483C08556 +:10119000C60F6C2F6F700E94B9086C2F62956F702C +:1011A0006061C801CF911F910F910C94B908FC01A7 +:1011B0002781269526952695621738F46583238521 +:1011C000620F6770606B0C94B90808951F93CF93FA +:1011D000DF93EC01142F8E81681718F4CE010E9462 +:1011E000C108612FCE01DF91CF911F910C94D708D8 +:1011F000CF92DF92EF92FF920F931F93CF93DF93E3 +:10120000EC01F62EC42ED22E1D868F8186958695F2 +:101210008695081710F00FEF080F1D2D011780F0AD +:10122000412F6F2DCE010E94E608EF2CCE1430F036 +:101230006889CE010E948E07E394F8CF1F5FEECF3E +:101240004D2D6F2DCE01DF91CF911F910F91FF900A +:10125000EF90DF90CF900C94E6080F93DC01159689 +:101260002C9115971E96ED91FC911F9700E03097F9 +:1012700079F03396E491F0E0379683E0F595E795C1 +:101280008A95E1F751960C9151970E9F002D1124EC +:10129000EFEFE20F0E0F16964C91169741501496F1 +:1012A0006C911497CD010E94F8080F9108952F9228 +:1012B0003F924F925F926F927F928F929F92AF92E6 +:1012C000BF92CF92DF92EF92FF920F931F93CF9333 +:1012D000DF9300D000D000D0CDB7DEB7FC0126856B +:1012E00037852115310509F44AC19E838D83F901A3 +:1012F0003296649031964491E42EF12CF7E0EF0E93 +:10130000F11C53E0F594E7945A95E1F7E982F9016D +:10131000349654913196E491651748F08E2F90E001 +:10132000FC01E50FF11D6E171F060CF469C06D304E +:1013300031F5ED81FE81868181111BC081E090E055 +:1013400026960FB6F894DEBF0FBECDBFDF91CF91CA +:101350001F910F91FF90EF90DF90CF90BF90AF90D3 +:101360009F908F907F906F905F904F903F902F90C5 +:10137000089560E08D819E810E94C108DFCF6A30B0 +:1013800009F0FDC0ED81FE818681882321F060E0B7 +:10139000CF010E94C108ED81FE8181898E9DE02CE4 +:1013A000112465816E0D8485882349F09781969577 +:1013B000969596958E2D891B860F18162CF08D818B +:1013C0009E810E94D708BACFED81FE812385820FCE +:1013D000877083879E199583CF010E942D09ED8127 +:1013E000FE818485813009F0A9CF6385660F660F81 +:1013F000660F6F7362876064CF010E94B9089ECF49 +:1014000089010A5F1F4F651BED81FE81718551893E +:10141000759F502D11245C83F90154912F5F3F4F2C +:10142000F9012491352F2230310508F06AC06E9CF5 +:10143000C0016F9C900D1124689FF001699FF00D11 +:1014400011240E0F1F1F512CED81FE81F481FA83B0 +:10145000ED81FE812580F585FB83862C912CAA24C5 +:10146000A394B12C312C8981381608F086C0412C08 +:10147000ED81FE818189481608F078C08B818587CF +:10148000311002C0442041F0ED81FE8145814F5F63 +:101490006A81CF010E94E608D12CC12CC61408F045 +:1014A00058C0F801EC0DFD1D7490552061F0EA1450 +:1014B000FB0449F4E72DF0E0052C02C0F595E79513 +:1014C0000A94E2F77E2EED81FE818189823081F4DB +:1014D000442009F43AC072948FE07822E72DF0E0BE +:1014E000EC5AF84F7490672D8D819E810E948E0773 +:1014F000672D8D819E810E948E078FEFC81AD80AB2 +:10150000CDCF477028E0522E541A411101C0512C02 +:10151000F801612C30E020E0661631F04491240F90 +:10152000311D63943196F8CF600E712E711CF3015A +:101530006490E29EF001E39EF00DF29EF00D112406 +:101540008E0F9F1F080F191F7FCFF72DFF707F2E63 +:10155000C5CFD12CEC81DE1639F060E08D819E8103 +:101560000E948E07D394F6CF439482CF3394080D14 +:10157000191DFFEFAF1ABF0A76CF622D20CF90E082 +:1015800080E0DECEFC018381877088608383848164 +:101590008F71806284836783CF010895E091EB01AE +:1015A000F091EC01309721F00280F381E02D099455 +:1015B00008950F931F930E9481070091ED011091F0 +:1015C000EE012091EF013091F001601B710B820B55 +:1015D000930B1F910F9108959C018091EB019091C5 +:1015E000EC0182179307F1F09091F5019F719093B0 +:1015F000F5013093EC012093EB01E091EB01F091C8 +:10160000EC01309721F00190F081E02D09950E94C6 +:1016100081076093ED017093EE018093EF01909349 +:10162000F00108958091F501805E8093F501E5CF8A +:101630000F931F930E94D90A2091F601207720373B +:1016400079F000E117E220E030E0061717072807DD +:10165000390758F481E291E01F910F910C94EC0A44 +:1016600000E61AEE20E030E0F0CF1F910F910895D0 +:101670008091F6018F70863020F088E191E00C9423 +:10168000EC0A08958091F50184608093F5018091C2 +:10169000F60180788093F601E091E701F091E8018E +:1016A000309709F009940895CF93C091F701CF3F87 +:1016B000A1F00E9481076093F1017093F201809381 +:1016C000F3019093F40180915102C813C093510229 +:1016D00084E191E0CF910C94EC0A0E94D90A613D1B +:1016E00077408105910568F08091F501803618F406 +:1016F00080E191E0EFCF877F8093F50188E191E071 +:10170000E9CFCF91089580914F028F3F21F080E182 +:1017100091E00C94EC0A0E94D90A613D7740810562 +:10172000910550F08091F501803618F48CE091E03D +:10173000F0CF88E091E0EDCF08950E94D90A613D95 +:1017400077408105910520F48091F50181FF17C054 +:10175000809150028F3F49F08091F5018E7F8093F8 +:10176000F5018CE091E00C94EC0A8091F501803653 +:1017700018F488E191E0F7CF88E091E0F4CF089584 +:10178000CF93DF93FC01EB01DA019C01205F3F4F17 +:1017900088819C9189278083898111969C911197DA +:1017A000892781838A8112969C9112978927828347 +:1017B0008B8113969C911397892783833496249663 +:1017C0001496E217F30721F7DF91CF910895FC01FA +:1017D000DB019C01205F3F4F80819C9189278083A2 +:1017E000818111969C91119789278183828112961C +:1017F0009C91129789278283838113969C911397DA +:101800008927838334961496E217F30729F70895FE +:10181000089590E080E008950E945E2BFB01019006 +:101820000020E9F73197AF01461B570BDC01ED9122 +:10183000FC910280F381E02D09948F929F92AF92E8 +:10184000BF92EF92FF920F931F93CF93DF93CDB789 +:10185000DEB7A1970FB6F894DEBF0FBECDBF7C01F7 +:10186000FA01CB0119A2223008F42AE08E010F5DA3 +:101870001F4F822E912CB12CA12CBF01A5019401E8 +:101880000E94E029F901CA016A300CF5605DD801B7 +:101890006E938D01232B242B252B79F790E080E08C +:1018A000109721F0BD01C7010E940E0CA1960FB642 +:1018B000F894DEBF0FBECDBFDF91CF911F910F9186 +:1018C000FF90EF90BF90AF909F908F900895695CCC +:1018D000DECFCF92DF92EF92FF92CF93DF936C0136 +:1018E000990FEE08FF08F7FE23C06DE283E592E052 +:1018F0000E944505EC0144275527BA014C195D09A2 +:101900006E097F092AE083E592E00E941D0CC80F52 +:10191000D91F6AE771E083E592E00E940E0C8C0FFC +:101920009D1FDF91CF91FF90EF90DF90CF900895B2 +:101930002AE0B701A60183E592E00E941D0CEC01AC +:10194000E8CF2F923F924F925F926F927F928F9249 +:101950009F92AF92BF92CF92DF92EF92FF921F932E +:10196000CF93DF93EC016A017B01722EAB019601EC +:10197000CB01B6010E942B2B6DE771E0811126C0CF +:1019800046015701E894B7F82FEF3FEF4FE75FE7C5 +:10199000C501B4010E942B2B81110CC02FEF3FEF2A +:1019A0004FE75FE7C501B4010E94582861E871E084 +:1019B000181664F02FEF3FEF4FE75FE4C701B60161 +:1019C0000E94262B1816B4F465E871E0CE01DF9171 +:1019D000CF911F91FF90EF90DF90CF90BF90AF908D +:1019E0009F908F907F906F905F904F903F902F903F +:1019F0000C940E0C2FEF3FEF4FE75FECC701B601E1 +:101A00000E94582887FDE0CF20E030E0A901C701FF +:101A1000B6010E945828312C212C87FF09C06DE2A5 +:101A2000CE010E9445051C01F7FAF094F7F8F094F6 +:101A300010E060E070E080E09FE3711641F020E08C +:101A400030E040E251E40E947E2A1F5FF6CFA701FA +:101A500096010E94122A2B013C010E94F72A4B0199 +:101A60005C012AE0BC01A401CE010E941D0C7C0196 +:101A7000E20CF31C1123C1F1C501B4010E945D28E1 +:101A80009B01AC01C301B2010E94112A4B015C0110 +:101A90006EE2CE010E944505E80EF91E115020F1BC +:101AA00020E030E040E251E4C501B4010E94BE28CC +:101AB0002B013C010E94F72A4B01B12CA12C2AE0FA +:101AC000B501A401CE010E941D0CE80EF91EC5014E +:101AD000B4010E945D289B01AC01C301B2010E94C8 +:101AE000112A4B015C01DACFC701DF91CF911F9121 +:101AF000FF90EF90DF90CF90BF90AF909F908F902E +:101B00007F906F905F904F903F902F900895CF936C +:101B1000DF93AB01BC0122E083E592E00E94A10CBF +:101B2000EC016AE771E083E592E00E940E0C8C0FF5 +:101B30009D1FDF91CF9108950F931F93CF93DF9354 +:101B4000EC010E944C058C016AE771E0CE010E9415 +:101B50000E0C800F911FDF91CF911F910F9108956F +:101B6000E5EFF1E08081877F8F7E80831092F10125 +:101B70001092F2011092F3011092F40144E050E04F +:101B800070E060E08FE492E00C947C29EF92FF9289 +:101B90000F93CF93DF9383E08093530384E080938C +:101BA00054038FE190E00E940A26EC011B821A8206 +:101BB00088EE93E0A0E0B0E08C839D83AE83BF838A +:101BC00089E391E0998388831F8A1E8A198E188E73 +:101BD0001B8E1A8E1D8E1C8E6E8D6E7F6D7F6E8F8E +:101BE0006695617081E0682784E00E94CA0661E022 +:101BF00084E00E94F806EAE9F7E0E491E88BE6E881 +:101C0000F7E0E491F0E0EE0FFF1FE259F84F859105 +:101C100094919A8B898B60E083E00E94F8068E8D08 +:101C200081FD04C061E083E00E94CA0683E08C87E6 +:101C3000E9E9F7E0E491ED87E5E8F7E0E491F0E029 +:101C4000EE0FFF1FE858F84F859194919F878E877C +:101C50008DE991E09D8F8C8F8DEB90E09F8B8E8B2B +:101C60008BE991E0998F888F8CE291E09B8F8A8F2E +:101C7000809168008460809368009C852DE630E048 +:101C8000983050F02BE630E09E3030F02CE630E01B +:101C9000963110F030E020E03C8B2B8B292F30E088 +:101CA000983008F06DC081E001C0880F9A95EAF77E +:101CB0008D8B8C8D9D8D0197F1F78A8D9B8D892B56 +:101CC00009F1E0913C02F0913D02CE17DF07D1F01F +:101CD000309739F0A389B4899C91858980958923AF +:101CE0008C938E8D8E7F8E8F10923A0210923B02D3 +:101CF000D0933D02C0933C02EB89FC8980819D8991 +:101D0000892B8083D0935603C09355030E940A0405 +:101D100069EE73E083E592E00E949C0D10926B03E4 +:101D20008CE380936A031092B9008CE08093B80032 +:101D300010925B0310925C03E3EEF3E0E590F49005 +:101D4000E5EEF3E00491E6EEF3E0E491E0935D0369 +:101D5000E7EEF3E0E491E0935E03E8EEF3E0E49174 +:101D6000E0935F03D0E0C0E0C017B8F4FE01EE0DD1 +:101D7000FF1D649187E593E00E94B9082196F4CF96 +:101D80009E3040F42850310981E001C0880F2A9527 +:101D9000EAF78ECF2E503109F7CF109262031092DE +:101DA000610360E487E593E00E94B90800915E0357 +:101DB000069506950695015040915D03415020E03F +:101DC00060E087E593E00E94F808EDEFF1E0F09322 +:101DD0006603E09365038491EEEFF1E0E491F82F60 +:101DE000319709F065C0109260036FEA87E593E0D0 +:101DF0000E94B9081092630340E060E087E593E039 +:101E00000E94E60863EF71E087E593E00E944C05CD +:101E10008091F50182FF50C06DED71E083E592E0A5 +:101E20000E949C0D40E060E587E593E00E94E60893 +:101E30006BED71E087E593E00E944C0540E066E4BD +:101E400087E593E00E94E60880911C0169EC71E04F +:101E5000811102C067EC71E087E593E00E944C05B8 +:101E600042E060E087E593E00E94E60862EC71E002 +:101E700087E593E00E944C0545E060E087E593E04C +:101E80000E94E6086CEB71E087E593E00E944C0548 +:101E900088E199E20FB6F894A895809360000FBE90 +:101EA00090936000DF91CF910F91FF90EF90089594 +:101EB00081E08093600399CF6BEC71E083E592E061 +:101EC0000E949C0DBBCFEF92FF920F931F93CF9375 +:101ED000DF93EC017B0180E06115710531F0CB01EE +:101EE0000E9418288931910518F5082F10E09B8170 +:101EF0009770980143E0220F331F4A95E1F7922B28 +:101F00009B839C819F719C83882331F0A801B7013A +:101F1000CE0107960E942128FE01E00FF11F1782D3 +:101F2000CE01DF91CF911F910F91FF90EF90089517 +:101F300089E1DBCF91E59CBD91E09DBD2998E3E26D +:101F4000F0E03197F1F78EBD00000DB407FEFDCF34 +:101F50008EB5FB019FEF4150C8F0222361F09EBD7A +:101F600000000DB407FEFDCF8EB56115710599F324 +:101F700080833196F0CFDF01119680818EBD000005 +:101F80000DB407FEFDCF8EB5FD01E5CF299AE3E242 +:101F9000F0E03197F1F70895CF93DF931F92CDB71B +:101FA000DEB7698320E041E0BE016F5F7F4F0E9492 +:101FB0009A0F0F90DF91CF9108958091F601982F9D +:101FC0009077903759F0982F92959F709F5F9770F8 +:101FD0009295907F8F78892B8093F6018091F501FF +:101FE0008B7F8F7E8093F5012A986CE080E20C94C1 +:101FF000CC0F6FE080E20E94CC0F609125016F3F13 +:1020000019F08AE20E94CC0F2A9A0895CF93C82F24 +:1020100080934F028F3F51F08093250163E082E26D +:102020000E94CC0F61E081E20E94CC0F0E94F90F68 +:102030006C2F90E080E00E949C2981E0CF91089570 +:1020400021E041E070E060E00C949A0F20E040E075 +:1020500070E060E00C949A0F0F931F93CF93DF937F +:10206000EC01E0E06115710551F0FB0101900020E9 +:10207000E9F73197E61BF70BE931F10500F50E2F73 +:1020800010E08B818770980193E0220F331F9A959F +:10209000E1F7822B8B838C818F718C83EE2329F067 +:1020A000A801CE0107960E946C2BFE01E00FF11FE4 +:1020B0001782CE01DF91CF911F910F910895E9E131 +:1020C000DECFFC012481229526952770213011F462 +:1020D00087810895211104C007960E94D32708958F +:1020E00080E008952F923F924F925F926F927F927D +:1020F0008F929F92AF92BF92CF92DF92EF92FF9218 +:102100000F931F93CF93DF93CDB7DEB7A2970FB690 +:10211000F894DEBF0FBECDBFB82E80914F02FB01F9 +:102120008083138112FDEDC0169516951695195FE3 +:10213000F0915202F9A3F170FAA3412F113208F085 +:1021400040E250E087EA91E00E94632B1092A804DD +:102150001092A9041092AA041092AB041092AC043D +:102160001092AD041092AE041092AF041092B0041D +:102170001092B1041092B2041092B3041092B404FD +:102180001092B5041092B6041092B7041092A004F5 +:102190001092A1041092A2041092A3041092A4041D +:1021A0001092A5041092A6041092A70400E2902EAB +:1021B000113110F410E1912EE92CE294FFE0EF22AE +:1021C000F12CA7EACA2EA1E0DA2E8E010F5E1F4F76 +:1021D000BBE1AB2EB60188EA94E00E94E70B809148 +:1021E000AE039091AF03892B09F40BC140EB53E090 +:1021F00068EA74E0C8010E94C00B88248394482DCB +:1022000050E08091AE039091AF03481759070CF04E +:102210007AC09801DE0111966FE0362E7AE0572ED3 +:10222000E5E04E2EF801E40DF11D708084E0840D90 +:102230008F70482EF801E50DF11D808194E0950D19 +:102240009F70592EF801E30DF11D908164E0630D3C +:102250006F70362EF9016081E62FF0E0E454FF4FF5 +:102260007491E72DF0E0E454FF4F6491E82FF0E023 +:10227000E454FF4F8491E92FF0E0E454FF4F949130 +:10228000672E660C77FD6A24E62FEE0F67FDEA25C0 +:10229000282E220C87FD2A24792E770C97FD7A248C +:1022A000F62FF827F927F625FE27FC93F72FF827B6 +:1022B000F927FE27F2251196FC9311976727962799 +:1022C0002926272412962C92129786276826762490 +:1022D00013967C9213972C5F3F4F14960A171B0797 +:1022E00009F0A0CF94E0440F551F9A95E1F74055AF +:1022F0005C4FBE016F5F7F4FC8010E94C00B83948B +:102300007ECF10E215CFE989F0E0E454FF4FE4916D +:10231000E98BED89F0E0E454FF4FE491ED8BE98D1A +:10232000F0E0E454FF4FE491E98FED8DF0E0E454E8 +:10233000FF4FE491ED8F8A89EE89F0E0E454FF4F7E +:10234000E491EA8BEA8DF0E0E454FF4FE491EE8BE8 +:10235000EE8DF0E0E454FF4FE491EA8FE82FF0E0D7 +:10236000E454FF4FE491EE8F8B89EB8DF0E0E45461 +:10237000FF4FE491EB8BE82FF0E0E454FF4FE49142 +:10238000EB8F8F89EF8DF0E0E454FF4FE491EF8BFA +:10239000E82FF0E0E454FF4FE491EF8F88A1EC8D3B +:1023A000F0E0E454FF4FE491E8A3E88DF0E0E4545A +:1023B000FF4FE491EC8FEC89F0E0E454FF4FE4919F +:1023C000E88FE82FF0E0E454FF4FE491EC8B84E0D9 +:1023D000440F551F8A95E1F740555C4FB80188EAD4 +:1023E00094E00E94C00B40E168EA74E0C6010E94DC +:1023F0003204F0E1CF0ED11C81E0E81AF10809F0B7 +:10240000E9CE2A9883E092E00197F1F76EE080E24E +:102410000E94CC0F8BE891E00197F1F76B2D8AE2D7 +:102420000E94CC0F6B2D80E30E94CC0F81EE0E94A6 +:102430002610EFEFBE1619F0FAA180EAF11180EB39 +:1024400020E0492D67EA71E00E949A0F2A9AF12C48 +:10245000E12C8FEF0E942610182F807321F481E069 +:10246000E81AF108B1F72A9860E387E20E94CC0FDE +:1024700014FF03C081EE0E9426100E94F90F15FF81 +:1024800002C0E1E0E9A391E0FFEFBF1290E089A173 +:102490008170892BA2960FB6F894DEBF0FBECDBF18 +:1024A000DF91CF911F910F91FF90EF90DF90CF9030 +:1024B000BF90AF909F908F907F906F905F904F9064 +:1024C0003F902F9008950F931F93CF938C01FC01A1 +:1024D00082819091F50190FF04C08F3F51F5CFEFBD +:1024E00004C0811116C0C0915002B8018C2F0E9407 +:1024F0007210909150029C1313C09091F6018111BB +:1025000013C021E0290F2F70907F922B9093F6013A +:1025100007C08F3F21F3B8010E947210882319F37E +:10252000CF911F910F910895907F9093F60181E0D4 +:10253000F7CF80E0F5CF2091F50122FD0C946312D6 +:1025400080E00895ECE6F3E090914F0291831282CF +:10255000868365838481807E8483BA01CF010E9453 +:10256000630F0C949B1220914F02FC012183F8E031 +:102570006F9FB00111246160FC012481207F622BD8 +:1025800064832091EA0120FD0C949B1280E0089561 +:10259000CF93DF93EC018C8184FF09C060EA70E087 +:1025A00083E592E00E949C0D81E08093E1018D81A2 +:1025B000823009F041C08E818230F1F5CE010E9457 +:1025C000611091E0811101C090E090931C0182E0C4 +:1025D0008093F6028091F302877088608093F30203 +:1025E0008091F4028F7180628093F4029093F702DD +:1025F00060E080EF92E00E94B31265E870E083E54E +:1026000092E00E944C0580911C0190E00E94690CB0 +:1026100040E066E487E593E00E94E60880911C01B3 +:1026200063E870E0811102C061E870E087E593E043 +:10263000DF91CF910C944C05DF91CF910895EF92EB +:10264000FF921F93CF93DF93CCE6D3E08FE4E82E85 +:1026500082E0F82EF701808189831A821FEF1E83A2 +:102660008BE08D838C81807E83608C8362E474E058 +:10267000CE010E94630F0E949B12F7018081898323 +:102680001A821E838CE08D838C81807E83608C8394 +:102690006EE374E0CE010E94630F0E949B1245E23C +:1026A00054E068E180E00E94A21266E970E080E0F8 +:1026B00090E00E944E0745E154E068E181E00E940D +:1026C000A21266E970E080E090E00E944E0743E0CD +:1026D00054E063E082E00E94A21281E08093200136 +:1026E000DF91CF911F91FF90EF900895FF920F938C +:1026F0001F93CF93DF93CCE6D3E00FE412E0F80111 +:10270000808189831A82FF24FA94FE828FE08D8370 +:102710008C81807E83608C836CE771E0CE010E94A7 +:102720002C108B81877080618B838C818F71806C82 +:102730008C8381E08F831886CE010E949B128FE0EC +:102740000E94AA17F801808189831A82FE8281E1A2 +:102750008D838C81807E8C8369E871E0CE010E943C +:102760002C100E949B12F801808189831A82FE82BC +:1027700086E08D838C81807E83608C836181CE0135 +:102780000E94C20A0E949B1286E00E94AA17DF9153 +:10279000CF911F910F91FF900C941F132F923F9296 +:1027A0004F925F926F927F928F929F92AF92BF9261 +:1027B000CF92DF92EF92FF920F931F93CF93DF930D +:1027C000CDB7DEB7E9970FB6F894DEBF0FBECDBF29 +:1027D00026E02BAB1BE187E10E94201080FD9BC30C +:1027E0003BA931503BAB332309F495C321E041E0D1 +:1027F00070E060E080E60E949A0F082F813220F09E +:1028000082EE0E94261000E021E0402F6DE873E088 +:1028100081E60E949A0F60E487E20E94CC0F10923A +:10282000A8041092A9041092AA041092AB0410926A +:10283000AC041092AD041092AE041092AF0410924A +:10284000B0041092B1041092B2041092B30410922A +:10285000B4041092B5041092B6041092B70410920A +:10286000A0041092A1041092A2041092A30410924A +:10287000A4041092A5041092A6041092A70401319A +:1028800008F463C182E090E09AAB89ABEDE82E2EAC +:10289000E3E03E2EE9A9FAA93197FAABE9AB31960C +:1028A00009F471C140E1B101CE0181960E94320468 +:1028B0004091AE035091AF034115510509F463C136 +:1028C00074E0440F551F7A95E1F740555C4FB10114 +:1028D000CE0141960E94C00BE989F0E0EC5AF94F15 +:1028E000E491E98BED89F0E0EC5AF94FE491ED8B3E +:1028F000E98DF0E0EC5AF94FE491E98FED8DF0E0CD +:10290000EC5AF94FE491ED8F8E8DEA8DF0E0EC5AA0 +:10291000F94FE491EE8FEE89F0E0EC5AF94FE49133 +:10292000EA8FEA89F0E0EC5AF94FE491EE8BE82F58 +:10293000F0E0EC5AF94FE491EA8B8B89EB8DF0E0F3 +:10294000EC5AF94FE491EB8BE82FF0E0EC5AF94F99 +:10295000E491EB8F8F89EF8DF0E0EC5AF94FE49121 +:10296000EF8BE82FF0E0EC5AF94FE491EF8F8C8970 +:10297000E88DF0E0EC5AF94FE491EC8BEC8DF0E04F +:10298000EC5AF94FE491E88FE8A1F0E0EC5AF94FE6 +:10299000E491EC8FE82FF0E0EC5AF94FE491E8A3D2 +:1029A0000091AE031FAA1EAA8FEF800FA82FB0E0E0 +:1029B000B9AFA8AF015009F4CDC048AD59ADEEA9EB +:1029C000FFA94E1B5F0B64E0440F551F6A95E1F7AA +:1029D00040555C4FBE016F5E7F4FCE0101960E9455 +:1029E000C00B9E012F5F3F4F7901CE0141969DABF9 +:1029F0008CABA5E0B0E0F701908181813281238129 +:102A0000792F770F97FD7127B82EBB0C87FDB12664 +:102A1000C32ECC0C37FDC126622F660F27FD612720 +:102A2000472F440F77FD41274B2C440CB7FE04C0C1 +:102A30005B2D550F452E41269C2C990CC7FC9126E9 +:102A4000A62EAA0C67FDA126D42EDD0C47FF04C0DC +:102A5000542F550FD52ED126542D550F47FC5127F5 +:102A6000F92DFF0F97FCF127EA2DEE0FA7FCE127C8 +:102A7000792E7D24582E5526832E8F26622E6E2683 +:102A80004D2554259F26AE26E32FE727EB25E42787 +:102A9000E525E925E625F0E0EC5AF94F3491ECA95B +:102AA000FDA93083E22FEB25EC25E725E527E82576 +:102AB000EA25F0E0EC5AF94FD490FD01EF70FF27C2 +:102AC00021E130E02C0F3D1FE20FF31FD0829C2547 +:102AD00096274927542695246924E62DF0E0EC5AE0 +:102AE000F94F6490FD013596EF70FF2721E130E04A +:102AF0002C0F3D1FE20FF31F6082E82FE727E62728 +:102B0000E7255E27E52FE825AE26EA2DF0E0EC5A12 +:102B1000F94FA490FD013A96EF70FF27E20FF31FE3 +:102B2000A08294E0E90EF11C2CA93DA92C5F3F4F37 +:102B30003DAB2CAB1496A531B10509F05CCF8EA945 +:102B40009FA901969FAB8EAB35CFA1E0B0E0BAABA9 +:102B5000A9AB9CCE40EB53E0BE016F5E7F4FC1013D +:102B60000E94C00B68EA74E0C1010E94E70B40E1DB +:102B7000BE016F5D7F4F88EA94E00E94320490E1CD +:102B8000290E311C87CE30919003832F83708230C1 +:102B900009F021CE20919103A22FA770EA2E009177 +:102BA0009203F0908E0380918D0340918F0390915A +:102BB000F60190689093F60190914F02491376C107 +:102BC000832F8695869586958A3108F089E190E075 +:102BD0003770BC0153E0660F771F5A95E1F7362B2B +:102BE0003093900383579C4FFC01178223FF15C03D +:102BF00081E2EDE8F3E0ACE6B3E001900D928A9556 +:102C0000E1F7277F20612093700340936D03F092DA +:102C10006E038CE693E00E949B129091910394FDC9 +:102C20003DC1F3E0EF123AC18091920321EF280FEA +:102C3000233008F4D0CD0430C1F4809193039091F7 +:102C40002601891719F08F3F09F0C5CD8DE893E073 +:102C50000E9461109FEF980F9E3F18F40E9406108B +:102C6000BACD8FEF80934F02B6CD0830D9F40091E2 +:102C7000F50100FFB0CD8DE893E00E9461108F3F19 +:102C800009F4A9CD8F5F8F3F09F4A5CD9091510232 +:102C9000891708F0A0CD01FD9ECD80935102F092DE +:102CA000500299CD0831C1F480914F0280936D0399 +:102CB000F0926E038FEF8093720389E1809371032A +:102CC00080917003807E83608093700361E08CE666 +:102CD00093E00E94C20A32C0093171F48091F5017B +:102CE00083FF79CD877F8093F5018DE893E00E9483 +:102CF00061108093F7016FCD0E3109F46CCD0D3169 +:102D000001F580914F0280936D0310926E038FEF57 +:102D1000809372038FE18093710383E48093700347 +:102D200080916F038770806180936F0380E09FEFD5 +:102D300090937403809373038CE693E00E94631274 +:102D40004ACD20918E032111AEC08D3059F488E117 +:102D500098E00FB6F894A895809360000FBE90930A +:102D60006000FFCF8B3179F48DE893E00E94611011 +:102D700091E0811101C090E08091EA0190FB80F91F +:102D80008093EA0128CD863099F49091940381E0F4 +:102D9000992319F09D3409F080E08093E90141E026 +:102DA00050E063E071E089EE91E00E948C2913CD40 +:102DB000833119F40E9476130ECD823121F50E94E1 +:102DC000D90A20914F0220936D0310926E032FEFCA +:102DD0002093720326E12093710323EA209370036A +:102DE00020916F032770206220936F036093730319 +:102DF0007093740380937503909376038CE693E04D +:102E00000E949B12E8CC823009F590914F0290937A +:102E10006D0310926E039FEF909372038093710382 +:102E200083EA8093700380916F0387708062809340 +:102E30006F038FEF91E0A3E0B2E080937303909370 +:102E40007403A0937503B0937603D8CF813001F556 +:102E500020E030E0232B09F4BECC92959695977034 +:102E6000953059F460919403709195038091960385 +:102E7000909197030E940000AECC911105C084E9A7 +:102E800093E00E94F127F6CF60E070E0CB01F2CF33 +:102E90008A3009F4A0CC8C3109F49DCC8DE893E004 +:102EA0000E94C81298CC8A31C9F795CC4F3F09F0DF +:102EB00092CC93E0A9132AC09091F50192FF03C030 +:102EC000073009F488CC043109F085CC9091500288 +:102ED000891381CC0E9481077370882799270E94EB +:102EE0004E0780914F0280936D03F0926E038FEF37 +:102EF0008093720385E18093710380917003807EDB +:102F000083608093700360915002E1CE90915002F3 +:102F1000891361CCC3CFE9960FB6F894DEBF0FBE1C +:102F2000CDBFDF91CF911F910F91FF90EF90DF9078 +:102F3000CF90BF90AF909F908F907F906F905F9059 +:102F40004F903F902F9008958091F50184FD0C944F +:102F5000CE130895CF92DF92EF92FF92CF93DF933B +:102F6000D82F0E9481076B017C0110929203C0E070 +:102F70000E9481076C197D098E099F09603D774089 +:102F80008105910590F4C11110C0A8950E94CE0A48 +:102F90000E94A417809191038770833049F7C1E0A4 +:102FA000809192038D13E3CFE3CF8C2FDF91CF91EC +:102FB000FF90EF90DF90CF9008958F929F92AF9205 +:102FC000BF92CF92DF92EF92FF924B015C010E9481 +:102FD00081076B017C010E9481076C197D098E09B4 +:102FE0009F09681579058A059B0530F4A8950E940C +:102FF000CE0A0E94A417EFCFFF90EF90DF90CF9002 +:10300000BF90AF909F908F9008958F929F92AF92B4 +:10301000BF92CF92DF92EF92FF920F931F93CF93C5 +:10302000DF9380912001882309F49EC01092200133 +:103030001092170369E574E081E193E00E94630F49 +:1030400060E00E94B31266E970E080E090E00E94C8 +:103050004E0780911C01882359F11092380340914A +:103060004702509148026091490270914A02809152 +:103070003503877088628093350380913603806EB4 +:10308000809336034093390350933A0360933B0394 +:1030900070933C0381E080933D0360E082E393E022 +:1030A0000E94B31266E970E080E090E00E944E0753 +:1030B000C1E0C093170363E574E081E193E00E94EF +:1030C000630F60E00E94B31266E970E080E090E078 +:1030D0000E944E07D0911C01DD2351F1C0933803AB +:1030E00040914B0250914C0260914D0270914E0202 +:1030F0008091350387708862809335038091360311 +:10310000806E809336034093390350933A03609363 +:103110003B0370933C03C0933D0360E082E393E084 +:103120000E94B31266E970E080E090E00E944E07D2 +:1031300082E08093F6028091F3028770886080932A +:10314000F3028091F4028F7180628093F402D09335 +:10315000F70260E080EF92E00E94B31266E970E04F +:1031600080E090E00E94DD170E94A60620911F01DA +:103170002817190691F16AE070E080E090E00E9463 +:10318000DD170E94A60621E0892B09F420E0209398 +:103190001F0160E476E083E592E00E944C05809197 +:1031A0001F0190E00E94690C80911F0181110FC0E6 +:1031B00081E08093680340E060E587E593E00E944A +:1031C000E6086EE376E087E593E00E944C056AE04E +:1031D00070E080E090E00E94DD170E94810700917E +:1031E000E3011091E4012091E5013091E6016B01CA +:1031F0007C01C01AD10AE20AF30A97018601093E4E +:1032000013402105310508F474C06093E301709305 +:10321000E4018093E5019093E60180911D01909176 +:103220001E01019690931E0180931D016FE871E0CD +:1032300083E592E00E940E0C80911D0190911E0189 +:103240000E94690C80911D0190911E01CD9784F020 +:103250006AE771E083E592E00E940E0C61E376E09C +:1032600083E592E00E949C0D10921E0110921D01B8 +:10327000A89581E08093680340E064E687E593E0E9 +:103280000E94E60880911D0190911E012CE3C22E40 +:10329000D12CC81AD90A0D2C000CEE08FF082AE020 +:1032A000B701A601F7FE0DC06DE287E593E00E942D +:1032B000450544275527BA014C195D096E097F0958 +:1032C0002AE087E593E00E941D0C87E593E00E94C9 +:1032D0002D0980911D0190911E018B30910509F4FB +:1032E000FAC0A4F48130910509F4B8C00A9709F432 +:1032F000DEC0DF91CF911F910F91FF90EF90DF9093 +:10330000CF90BF90AF909F908F9008958D30910592 +:1033100009F41AC30CF430C20E9759F78091C70113 +:103320009091C8014CE3489F9001499F300D1124B2 +:1033300020313E402CF401969093C8018093C70140 +:103340008091E201811102C4C0904B02D0904C02E6 +:10335000E0904D02F0904E028090DD019090DE01F1 +:10336000A090DF01B090E001C091C701D091C801E9 +:10337000C230D10594F0C430D1050CF0A6C3A5012C +:103380009401C701B6010E94122A20E030E040E01B +:103390005FE30E94BE286B017C01C092DD01D092E8 +:1033A000DE01E092DF01F092E001C0904702D09090 +:1033B0004802E0904902F0904A028090D901909032 +:1033C000DA01A090DB01B090DC01C230D10594F0AD +:1033D000C430D1050CF09AC3A5019401C701B60110 +:1033E0000E94122A20E030E040E05FE30E94BE2805 +:1033F0006B017C01C092D901D092DA01E092DB012D +:10340000F092DC016DE674E083E592E00E944C05E9 +:103410006091DD017091DE018091DF019091E0010A +:103420000E94870D6FE574E083E592E00E944C05F1 +:103430006091D9017091DA018091DB019091DC01FA +:10344000DF91CF911F910F91FF90EF90DF90CF9080 +:10345000BF90AF909F908F900C94870D60E276E0C4 +:1034600083E592E00E949C0D809155039091560354 +:10347000DC01ED91FC910190F081E02D61E0099576 +:103480008091550390915603DC01ED91FC910680EB +:10349000F781E02DDF91CF911F910F91FF90EF9079 +:1034A000DF90CF90BF90AF909F908F9009946AE08B +:1034B00076E083E592E00E949C0DDF91CF911F9111 +:1034C0000F91FF90EF90DF90CF90BF90AF909F90C3 +:1034D0008F900C9460046EEE75E083E592E00E949C +:1034E0009C0D0E946004892B69F46DED75E083E505 +:1034F00092E00E949C0D6AE070E080E090E00E9403 +:103500004E07EFCF82E08093680343E060E087E5F9 +:1035100093E00E94E60840914B0250914C0260916A +:103520004D0270914E0222E087E593E00E94A10CCB +:1035300069ED75E087E593E00E949C0D46E060E050 +:1035400087E593E00E94E6084091470250914802C7 +:103550006091490270914A0222E087E593E00E945F +:10356000A10C65ED75E087E593E00E949C0D43E0BA +:1035700066E487E593E00E94E6088091E2016FEC43 +:1035800075E08111B2C0C0904B02D0904C02E09027 +:103590004D02F0904E0220E030E0A901C701B601D3 +:1035A0000E945828811164C087E593E00E942D098C +:1035B00046E066E487E593E00E94E6088091E20138 +:1035C0006BEA75E08111D3C0C0904702D0904802E9 +:1035D000E0904902F0904A0220E030E0A901C701E2 +:1035E000B6010E945828811185C087E593E00E94AA +:1035F0002D0966E775E083E592E00E949C0DC7E423 +:10360000D1E00AE511E0809155039091560369914C +:10361000DC01ED91FC910190F081E02D09950C17F2 +:103620001D0789F78091550390915603DC01ED91B8 +:10363000FC910680F781E02D0995809155039091CA +:103640005603DC01ED91FC910084F185E02D099594 +:10365000181619060CF04DCE809155039091560323 +:10366000DC01ED91FC910284F385E02D0995E5CF15 +:1036700020E030E040E450E4C701B6010E94582841 +:1036800069EC75E018168CF520E030E040EA50E473 +:10369000C701B6010E94582863EC75E018162CF596 +:1036A00020E030E040E051E4C701B6010E94582814 +:1036B0006DEB75E01816CCF420E030E040E651E404 +:1036C000C701B6010E94582867EB75E018166CF424 +:1036D00020E030E040EA51E4C701B6010E94262B09 +:1036E00018160CF065CF61EB75E087E593E00E945A +:1036F0004C055ECF20E030E040EA50E4C701B6015F +:103700000E94582865EA75E018168CF520E030E034 +:1037100040E251E4C701B6010E9458286FE975E004 +:1037200018162CF520E030E040EA51E4C701B6015C +:103730000E94582869E975E01816CCF420E030E0C2 +:1037400040EF51E4C701B6010E94582863E975E0D3 +:1037500018166CF420E030E040EF51E4C701B601E8 +:103760000E94262B18160CF044CF6DE875E087E513 +:1037700093E00E944C053DCF1092E1018091E2015F +:10378000882341F06BE575E083E592E00E944C05EB +:103790001092E2018091E201882309F4B9C020E08F +:1037A00030E543EC57E46091D5017091D6018091EA +:1037B000D7019091D8010E94BE280E94F02A6B0187 +:1037C0007C01C114D104E104F10441F00E947B2783 +:1037D000A70196010E94AE296B017C01C701B601C9 +:1037E0000E945F2820E030E543EC57E40E947E2AE7 +:1037F0006093C9017093CA018093CB019093CC016F +:103800000E947B2722E030E040E050E00E94AE2999 +:10381000672B682B692B89F08091C9019091CA01AF +:10382000A091CB01B091CC01B0588093C901909385 +:10383000CA01A093CB01B093CC016CE475E083E5A1 +:1038400092E00E944C056091C9017091CA0180917B +:10385000CB019091CC010E94870D2091DD01309128 +:10386000DE014091DF015091E0016091C90170914A +:10387000CA018091CB019091CC010E94122A6093E1 +:10388000C9017093CA018093CB019093CC0166E388 +:1038900075E083E592E00E944C056091C90170914A +:1038A000CA018091CB019091CC010E94870D91E0DB +:1038B000909338034091C9015091CA016091CB01A6 +:1038C0007091CC01809135038770886280933503B5 +:1038D00080913603806E8093360340933903509372 +:1038E0003A0360933B0370933C0390933D0361E084 +:1038F00082E393E0DF91CF911F910F91FF90EF90C2 +:10390000DF90CF90BF90AF909F908F900C94B312A8 +:1039100065E275E083E592E00E944C0560914B0200 +:1039200070914C0280914D0290914E020E94870D41 +:1039300091E09093380340914B0250914C0260917A +:103940004D0270914E02BECF8091E201882309F4AE +:103950007DC02091CD013091CE014091CF01509199 +:10396000D0016091D1017091D2018091D3019091E9 +:10397000D4010E94BE286B017C0165E175E083E5FE +:1039800092E00E944C05C701B6010E94870D20916C +:10399000D9013091DA014091DB015091DC01C7017E +:1039A000B6010E94122A6B017C016EEF74E083E580 +:1039B00092E00E944C05C701B6010E94870D8091DC +:1039C0001C018823E9F091E090933803809135033E +:1039D000877088628093350380913603806E809370 +:1039E0003603C0923903D0923A03E0923B03F0923F +:1039F0003C0390933D0361E082E393E00E94B312A5 +:103A000081E08093680340E060E587E593E00E94F1 +:103A1000E6088091E101882309F44EC06CEC74E063 +:103A200083E592E00E949C0D6AEC74E087E593E0E8 +:103A3000DF91CF911F910F91FF90EF90DF90CF908A +:103A4000BF90AF909F908F900C944C056CEE74E0FB +:103A500083E592E00E944C05609147027091480214 +:103A60008091490290914A020E94870D80911C0129 +:103A7000882331F210923803409147025091480256 +:103A80006091490270914A02809135038770886283 +:103A90008093350380913603806E80933603409384 +:103AA000390350933A0360933B0370933C0381E0E6 +:103AB00080933D0360E0A0CF6DEA74E083E592E07F +:103AC0000E949C0D6BEA74E0B1CFBE016150710998 +:103AD000072E000C880B990B0E945F28A50194010A +:103AE0000E94BE28A70196010E94122A6B017C0148 +:103AF000BE010D2E000C880B990B0E945F289B01C4 +:103B0000AC01C701B6010E947E2A45CCBE016150BE +:103B10007109072E000C880B990B0E945F28A501E4 +:103B200094010E94BE28A70196010E94122A6B01EF +:103B30007C01BE01DD0F880B990B0E945F289B0161 +:103B4000AC01C701B6010E947E2A52CC6CE774E03A +:103B500083E592E00E944C0555CC1F93CF93DF93F1 +:103B60008091EA0181FD2DC00E9476138091EA01C7 +:103B700081608093EA01C4E0DFEF1AE180914F0297 +:103B800080936D0310926E03D093720310937103B0 +:103B900080917003807E83608093700362E08CE686 +:103BA00093E00E94C20A0E949B128BE10E94AA1716 +:103BB000811102C0C15011F78091EA0182608093A7 +:103BC000EA01DF91CF911F9108958FEF8093F70164 +:103BD000E5EFF1E0908198609083ECE6F3E090915E +:103BE0004F0291831282868388E185838481807E5F +:103BF0008360848361E0CF010E94C20A0C94631247 +:103C000080914F028F3F21F50E9481076F3FE9F0BD +:103C1000609326018091260190914F0290936D034D +:103C200010926E038093720383E0809371038091FE +:103C30007003807E8360809370036CE771E08CE694 +:103C400093E00E942C100C94631210922601E2CF94 +:103C50000895E5EFF1E08081897F81608083AFE4A2 +:103C6000B2E08FEF12968C93129711968C93119766 +:103C7000ECE6F3E09C9191838283868387E08583E1 +:103C80008481807E836084836CE771E0CF010E9431 +:103C90002C100C9463128F929F92AF92BF92CF928E +:103CA000DF92EF92FF920F931F93CF93DF93CDB7E5 +:103CB000DEB764970FB6F894DEBF0FBECDBF8E019E +:103CC0000F5F1F4F80E1F80111928A95E9F760E1DB +:103CD00070E08CE991E00E943028AC016CE971E061 +:103CE000C8010E94632B8AE090E09093AF03809319 +:103CF000AE0340E1B80180EB93E00E94320450E053 +:103D000040E080E190E0FF24F39420E1E22EBE0148 +:103D10006B5E7F4F3BE1B32ED82EFC01E455FC4F88 +:103D20009E012F5E3F4FD901C190CD92A617B707D4 +:103D3000D9F7DE1020C0D988F0E1EF0EEA89F0E073 +:103D4000E454FF4FE491EF25E98BEB89F0E0E45474 +:103D5000FF4FE491EA8BEC89F0E0E454FF4FE491EB +:103D6000EB8BED2DF0E0E454FF4FE491EC8BF7FE8C +:103D7000CFC0FF0CFB24FC01E055FC4F4F01F901C3 +:103D8000A1909F016401C81AD90AC40ED51EF6017C +:103D9000D080DA24F401D1924F012A173B0779F73A +:103DA00004964C5F5F4F803B910509F0B5CF80E1F1 +:103DB000F80111928A95E9F7229A219A2A98299A6C +:103DC0001FB7F8948091A601811127C0ECE8F7E0B5 +:103DD0008491E0EAF7E09491E82FF0E0EE0FFF1F06 +:103DE000EC59F84FA591B491EC91E92321F461E0ED +:103DF0008AE00E94CA0661E08AE00E94F8068CB55B +:103E000080618CBD8CB580648CBD61E08DE00E94CA +:103E1000F80661E08BE00E94F8068091A6018F5FB2 +:103E20008093A6011FBF2A986EE080E20E94CC0F0B +:103E30008BE496E40197F1F763E083E20E94CC0FF4 +:103E40006FE584E20E94CC0F6CE485E20E94CC0F07 +:103E500067E086E20E94CC0F64E08DE30E94CC0F05 +:103E60008DE10E942010843041F063E780E50E94DC +:103E7000CC0F64E08DE30E94CC0F86E00E942010FE +:103E8000873009F04FC085E00E9420108C3409F083 +:103E900049C062E082E20E94CC0F60E081E20E94B1 +:103EA000CC0F63E08CE30E94CC0F1FEF1093030153 +:103EB00020E045E063E071E08BE20E949A0F20E091 +:103EC00045E063E071E08AE20E949A0F20E045E05D +:103ED00063E071E080E30E949A0F82EE0E94261058 +:103EE00081EE0E94261060E787E20E94CC0F80914D +:103EF000F50180618093F501809152028E7F80935D +:103F0000520280914F028F3F29F488E191E00CC06A +:103F1000FF0C31CF9FEF980F9E3FD8F40E94061000 +:103F20008111F3CF88E091E00E94EC0A64960FB60D +:103F3000F894DEBF0FBECDBFDF91CF911F910F91DF +:103F4000FF90EF90DF90CF90BF90AF909F908F90B9 +:103F5000089510934F02E6CF1F920F920FB60F9263 +:103F600011242F933F934F935F936F937F938F937E +:103F70009F93AF93BF93EF93FF9383E592E00E94EB +:103F8000E405FF91EF91BF91AF919F918F917F9148 +:103F90006F915F914F913F912F910F900FBE0F90B6 +:103FA0001F9018951F920F920FB60F9211242F9306 +:103FB0008F939F93EF93FF93E0916302F0916402DC +:103FC0008081E0916902F0916A0282FD1BC09081BC +:103FD00080916C028F5F8F7320916D02821741F088 +:103FE000E0916C02F0E0ED5AFD4F958F80936C02EA +:103FF000FF91EF919F918F912F910F900FBE0F9096 +:104000001F9018958081F4CF1F920F920FB60F92D8 +:1040100011242F933F938F939F93AF93BF938091DE +:104020003F0290914002A0914102B0914202309132 +:104030003E0223E0230F2D3758F50196A11DB11D37 +:1040400020933E0280933F0290934002A09341024E +:10405000B09342028091430290914402A0914502A4 +:10406000B09146020196A11DB11D80934302909329 +:104070004402A0934502B0934602BF91AF919F9135 +:104080008F913F912F910F900FBE0F901F90189519 +:1040900026E8230F0296A11DB11DD2CF1F920F92C9 +:1040A0000FB60F9211242F933F934F935F936F930B +:1040B0007F938F939F93AF93BF93EF93FF93E09181 +:1040C0002901F0912A010995FF91EF91BF91AF91DC +:1040D0009F918F917F916F915F914F913F912F9120 +:1040E0000F900FBE0F901F9018951F920F920FB652 +:1040F0000F9211242F933F934F935F936F937F936E +:104100008F939F93AF93BF93EF93FF93E09127011A +:10411000F09128010995FF91EF91BF91AF919F9187 +:104120008F917F916F915F914F913F912F910F9060 +:104130000FBE0F901F9018951F920F920FB60F92FF +:1041400011242F933F934F935F936F937F938F939C +:104150009F93AF93BF93EF93FF93E0913C02F09155 +:104160003D02309749F0A685B7858585968D91FFEC +:1041700014C09C918923A1F4FF91EF91BF91AF915D +:104180009F918F917F916F915F914F913F912F916F +:104190000F900FBE0F901F9018959C91892361F787 +:1041A000A389B4899C918589809589238C9386897C +:1041B00097890197F1F7608D718DA685B785558533 +:1041C00038E020E0CB010197F1F7822F90E0959540 +:1041D0008795282F4C91452309F02068315091F79D +:1041E000868D81FD209580913A0290E001968F7333 +:1041F000992730913B02381799F0A0913A02B0E02C +:10420000A650BE4F2C9380933A02828D938D0197D6 +:10421000F1F7A389B4898C919589892B8C93ACCFC4 +:10422000868D8160868FF1CF1F920F920FB60F920D +:1042300011240F900FBE0F901F901895DB01FC0109 +:1042400058E0149744E018960E9001924A95E1F7D1 +:104250005A95C1F708958F929F92AF92BF92CF92D5 +:10426000DF920F931F93EDB7FEB7B8970FB6F89490 +:10427000EDBF0FBEFEBF9F938F9331968F01490113 +:104280005A016B01C801AAD2BB2011F4AA2059F02F +:10429000C801B601A1D030E4C30ED11C32E0931A9C +:1042A000A108B108F1CFC801B601A40115D08F91C2 +:1042B0009F91B801C3DFEDB7FEB7B8960FB6F8947B +:1042C000EDBF0FBEFEBF1F910F91DF90CF90BF904B +:1042D000AF909F908F900895523090F09F938F935E +:1042E0007F936F935F934F9377D04F915F916F91CF +:1042F0007F918F919F91525030E4630F711DECCFED +:10430000EDB7FEB70FB6E054F040F894EDBF0FBE26 +:10431000FEBF3196242F26952695269550FB25F92C +:10432000DB01222329F0122E0D9001921A94E1F75D +:1043300030E8032E37E0342329F006943A95E9F764 +:104340003C91032A01922395293308F421C030E4DB +:10435000321B19F011923A95E9F7FF973197BF0197 +:10436000FF93EF939F938F935F934F9335D04F912C +:104370005F918F919F91EF91FF91DC0191963C911C +:1043800032503D9326E03C9130403D932A95D9F739 +:1043900038E3321B21F0112411923A95E9F7DC0140 +:1043A000909638960D90040E02920D90051E029282 +:1043B00036E00D90011C02923A95D9F7F897BF01AB +:1043C0000BD0EDB7FEB70FB6FF963196F894EDBF60 +:1043D0000FBEFEBF112408954F925F926F927F929D +:1043E0008F929F92AF92BF92CF92DF92EF92FF9205 +:1043F0000F931F93CF93DF934DB75EB79A01FB01E5 +:1044000040525140DA010FB6F8944DBF0FBE5EBF67 +:104410002F933F938F939F93119640E171916191F8 +:10442000319121912D933D936D937D934A95B1F7F1 +:1044300040E3142EFD01FF973197019111912191D5 +:104440003191C190D190E190F1906D2D7E2D8F2D05 +:104450009C2D41E0E9D14B015C01B701C60142E06E +:10446000F9D186269726A826B92673E0F694E79414 +:10447000D794C7947A95D1F78C249D24AE24BF2479 +:10448000080D191D2A1D3B1D648D758D868D978D18 +:10449000060F171F281F391FC0A8D1A8E2A8F3A82C +:1044A000B701C60141E0D6D15C014B0142E0D2D157 +:1044B00086269726A826B92672E0F694E794D79424 +:1044C0007A95D9F78D249E24AF24080D191D2A1D35 +:1044D0003B1D0D931D932D933D931A9409F0AACF84 +:1044E000FF91EF91EF93FF9390E211901D929A95B7 +:1044F000E1F79097ED01ECEDF6E4BA9590E4192E12 +:104500004C885D886E887F88688979898A899B89CB +:10451000C88CD98CEA8CFB8C4622572268227922DF +:104520006095709580959095C622D722E822F92251 +:104530004C245D246E247F24F888C988DA88EB88AF +:10454000B601C70142E070D14B015C01B601C70161 +:1045500043E080D186269726A826B926B701C60152 +:1045600041E078D186269726A826B926480C591C02 +:104570006A1C7B1C8C8C9D8CAE8CBF8C480C591C8F +:104580006A1C7B1C8D909D90AD90BD90480C591C71 +:104590006A1C7B1C85909590A590B590480C591C81 +:1045A0006A1C7B1C688179818A819B818C809D80BB +:1045B000AE80BF8086229722A822B922C884D984DF +:1045C000EA84FB846C217D218E219F2186269726FB +:1045D000A826B9266C817D818E819F816C217D21E9 +:1045E0008E219F2186269726A826B926C880D980A5 +:1045F000EA80FB80B601C70142E02CD18B019C010F +:10460000B701C60143E010D106271727282739270D +:104610006F2D7C2D8D2D9E2D42E006D1062717276C +:1046200028273927080D191D2A1D3B1D5CE16C96B2 +:104630009A919C835A95E1F7C888D988EA88FB88C3 +:10464000C40CD51CE61CF71CC88AD98AEA8AFB8AE6 +:10465000040D151D261D371D088319832A833B83EE +:104660001A9409F04DCFFF91EF9158E0008111812C +:10467000228133816991799189919991060F171F50 +:10468000281F391F01931193219331935A9571F784 +:10469000319652E066E04081450F4193552728F45A +:1046A0004081451F41936A95D1F75F914F910FB6B5 +:1046B000F8944DBF0FBE5EBF1124DF91CF911F91C3 +:1046C0000F91FF90EF90DF90CF90BF90AF909F90B1 +:1046D0008F907F906F905F904F900895982F8A42AF +:1046E00091443771CFFBC0B5A5DBB5E95BC2563944 +:1046F000F111F159A4823F92D55E1CAB98AA07D85C +:10470000015B8312BE853124C37D0C55745DBE727E +:10471000FEB1DE80A706DC9B74F19BC1C1699BE4FE +:104720008647BEEFC69DC10FCCA10C246F2CE92D8E +:10473000AA84744ADCA9B05CDA88F97652513E98B2 +:104740006DC631A8C82703B0C77F59BFF30BE0C6B9 +:104750004791A7D55163CA0667292914850AB72747 +:1047600038211B2EFC6D2C4D130D385354730A65E4 +:10477000BB0A6A762EC9C281852C7292A1E8BFA2BB +:104780004B661AA8708B4BC2A3516CC719E892D123 +:10479000240699D685350EF470A06A1016C1A419A6 +:1047A000086C371E4C774827B5BCB034B30C1C39A5 +:1047B0004AAAD84E4FCA9C5BF36F2E68EE828F7464 +:1047C0006F63A5781478C8840802C78CFAFFBE907E +:1047D000EB6C50A4F7A3F9BEF27871C6DC01EEEEE3 +:1047E000F7E468E275917D936A95E1F7089567E6CD +:1047F000096A85AE67BB72F36E3C3AF54FA57F52EE +:104800000E518C68059BABD9831F19CDE05B00006E +:10481000000000000000483038F0592F982F872FF3 +:10482000762F652F4850F7CF55278894442339F0C9 +:10483000661F771F881F991F551F4A95C9F7652B5B +:104840000895483038F0562F672F782F892F952FED +:104850004850F7CF55278894442339F0979587958A +:104860007795679557954A95C9F7952B0895AB01AC +:10487000692F782F852F942F0895CF93DF93FC0114 +:10488000108211821282148215821682DC01179620 +:104890008AE1ED0119928A95E9F782E08383DF913D +:1048A000CF9108950F931F93CF93DF93CDB7DEB7CA +:1048B00028970FB6F894DEBF0FBECDBFE3E5F2E058 +:1048C0001382128288EE93E0A0E0B0E084839583A7 +:1048D000A683B7838EE591E09183808385EC90E099 +:1048E0009587848784EC90E09787868780EC90E0BA +:1048F000918B808B81EC90E0938B828B82EC90E0AB +:10490000958B848B86EC90E0978B868B118E128E24 +:10491000138E148E28E0CE010196DC01E22F1D9249 +:10492000EA95E9F7E0EAF4E011E0108311821282DF +:10493000138214821582168217821092AF0310928E +:10494000AE031092B9041092B8041092BB041092F6 +:10495000BA04E0EBF3E030EFDF011D923A95E9F79E +:10496000188738E0FC01A8EAB4E001900D923A956E +:10497000E1F738E0FC01A0EBB4E001900D923A952C +:10498000E1F71093BC0402E00093BD0483E0809340 +:10499000BE0484E08093BF0485E08093C00486E079 +:1049A0008093C10487E08093C2042093C30489E00C +:1049B0008093C4048AE08093C5048BE08093C6048E +:1049C0008CE08093C7048DE08093C8048EE08093D0 +:1049D000C9048FE08093CA048DE893E00E943D24CF +:1049E0008CE693E00E943D24E7E5F3E01382128217 +:1049F0001486158617861686108A118B80E791E03B +:104A00009183808382E393E00E943D24E2E3F3E01C +:104A1000168285E2858381E193E00E943D24E1E1F5 +:104A2000F3E016828BE2858380EF92E00E943D24C2 +:104A3000E0EFF2E00683058328960FB6F894DEBF18 +:104A40000FBECDBFDF91CF911F910F910895CF93EE +:104A5000DF93CDB7DEB7CA55D1090FB6F894DEBFE4 +:104A60000FBECDBF789484B5826084BD84B581606B +:104A700084BD85B5826085BD85B5816085BD809129 +:104A80006E00816080936E001092810080918100A1 +:104A90008260809381008091810081608093810099 +:104AA000809180008160809380008091B10084605B +:104AB0008093B1008091B00081608093B0008091BC +:104AC0007A00846080937A0080917A00826080937B +:104AD0007A0080917A00816080937A0080917A00D8 +:104AE000806880937A001092C100A89580E090E0E1 +:104AF000892B11F00E9400000E940A048091EA01B3 +:104B00008D7F8093EA0180E090E0892B11F00E9474 +:104B1000000040E250E063E671E0CE0101960E94A1 +:104B20007C2940E150E06CE871E0CE0181960E9462 +:104B30007C2949E050E063E871E0CE01C1960E9413 +:104B40007C2941E050E06AE171E0CE01865A9F4F36 +:104B50000E947C2928EC31E040E050E0BE016F5F0C +:104B60007F4FCE01CA960E942B2141E050E063E0C6 +:104B700071E089EE91E00E947C296FEF81E090E086 +:104B80000E949C298091F6018F788093F601109203 +:104B9000EC011092EB0181E291E00E94EC0A8DEAB7 +:104BA0009DE19093E8018093E7010E9481076B01EA +:104BB0007C010E9481076C197D098E099F09603173 +:104BC000774281059105C8F00E94C60DA89500E0C6 +:104BD00010E0A8950E94CE0A8091F50184FD0E9404 +:104BE000CE130E9405180115110599F30E94DA05EC +:104BF000882379F30E940000ECCF0E94CE0A0E9425 +:104C0000A4178091F50182FB882780F9A89581116E +:104C1000DBCFCFCF0F931F93CF93DF9382309105DC +:104C200010F482E090E0E091CD04F091CE0430E009 +:104C300020E0B0E0A0E0309799F42115310509F4A7 +:104C40004AC0281B390B24303105D8F58A819B8155 +:104C50006115710589F1FB0193838283FE0111C007 +:104C6000408151810281138148175907E0F04817AC +:104C7000590799F4109761F012960C9312971396B6 +:104C80001C933296CF01DF91CF911F910F91089520 +:104C90000093CD041093CE04F4CF2115310551F0CB +:104CA0004217530738F0A901DB019A01BD01DF016A +:104CB000F801C1CFEF01F9CF9093CE048093CD04DA +:104CC000CDCFFE01E20FF31F819391932250310962 +:104CD00039832883D7CF2091CB043091CC04232B68 +:104CE00041F420912D0130912E013093CC0420937A +:104CF000CB0420912B0130912C012115310541F479 +:104D00002DB73EB740912F0150913001241B350B38 +:104D1000E091CB04F091CC04E217F307A0F42E1B32 +:104D20003F0B2817390778F0AC014E5F5F4F24170F +:104D3000350748F04E0F5F1F5093CC044093CB04CF +:104D4000819391939FCFF0E0E0E09CCFCF93DF93EE +:104D50000097E9F0FC01329713821282A091CD04F2 +:104D6000B091CE04ED0130E020E01097A1F4208155 +:104D70003181820F931F2091CB043091CC042817EE +:104D8000390709F061C0F093CC04E093CB04DF91C4 +:104D9000CF910895EA01CE17DF07E8F54A815B81DC +:104DA0009E0141155105B1F7E901FB83EA83499161 +:104DB0005991C40FD51FEC17FD0761F480819181D3 +:104DC0000296840F951FE9019983888382819381DC +:104DD0009B838A83F0E0E0E012968D919C9113977B +:104DE0000097B9F52D913C911197CD010296820F54 +:104DF000931F2091CB043091CC042817390739F642 +:104E0000309751F51092CE041092CD04B093CC049B +:104E1000A093CB04BCCFD383C28340815181840F44 +:104E2000951FC817D90761F44E5F5F4F888199813C +:104E3000480F591F518340838A819B8193838283CA +:104E40002115310509F0B0CFF093CE04E093CD04E5 +:104E50009ECFFD01DC01C0CF13821282D7CF8F928B +:104E60009F92AF92BF92CF92DF92EF92FF92CF9339 +:104E7000DF93EC01688179818A819B8161157105DD +:104E80008105910521F464E279ED8BE597E02DE150 +:104E900033EF41E050E00E94AE2949015A019B01E5 +:104EA000AC01A7EAB1E40E94CD296B017C01ACEE14 +:104EB000B4EFA50194010E94DB29C60ED71EE81E9F +:104EC000F91EF7FE06C081E0C81AD108E10880E8A3 +:104ED000F80AC882D982EA82FB82C701B6019F77AD +:104EE000DF91CF91FF90EF90DF90CF90BF90AF9088 +:104EF0009F908F9008958F929F92AF92BF92CF9282 +:104F0000DF92EF92FF9260913101709132018091B6 +:104F1000330190913401611571058105910521F4EA +:104F200064E279ED8BE597E02DE133EF41E050E06D +:104F30000E94AE2949015A019B01AC01A7EAB1E4E4 +:104F40000E94CD296B017C01ACEEB4EFA501940168 +:104F50000E94DB29C60ED71EE81EF91EF7FE06C00A +:104F600081E0C81AD108E10880E8F80AC09231014E +:104F7000D0923201E0923301F0923401C701B601C0 +:104F80009F77FF90EF90DF90CF90BF90AF909F9072 +:104F90008F900895609331017093320180933301B3 +:104FA000909334010895FC0188279927E894219172 +:104FB0002032E9F3293010F02E30C8F32B3241F0C3 +:104FC0002D3239F4689404C00E945028820F911D3C +:104FD000219120532A30C0F31EF4909581959F4F64 +:104FE00008951F93FC0199278827BC01E89411912B +:104FF0001032E9F3193010F01E30C8F31B3251F0B3 +:105000001D3249F4689406C00E943B28610F711D4F +:10501000811D911D119110531A30B0F33EF49095FB +:105020008095709561957F4F8F4F9F4F1F91089589 +:10503000FC0105900020E9F7809590958E0F9F1F49 +:105040000895FB01DC014150504048F005900D925D +:105050000020C9F701C01D9241505040E0F708956B +:10506000FC016150704001900110D8F78095909537 +:105070008E0F9F1F0895592F482F372F262F660F09 +:10508000771F881F991F660F771F881F991F620F50 +:10509000731F841F951F660F771F881F991F089520 +:1050A0007AE0979F902D879F802D910D1124089570 +:1050B0000E949A2808F481E00895E89409C097FBBB +:1050C0003EF490958095709561957F4F8F4F9F4FDF +:1050D0009923A9F0F92F96E9BB279395F695879523 +:1050E00077956795B795F111F8CFFAF4BB0F11F4E6 +:1050F00060FF1BC06F5F7F4F8F4F9F4F16C088238D +:1051000011F096E911C0772321F09EE8872F762FC2 +:1051100005C0662371F096E8862F70E060E02AF003 +:105120009A95660F771F881FDAF7880F96958795EF +:1051300097F90895990F0008550FAA0BE0E8FEEFC4 +:1051400016161706E807F907C0F012161306E4074B +:10515000F50798F0621B730B840B950B39F40A2644 +:1051600061F0232B242B252B21F408950A2609F422 +:10517000A140A6958FEF811D811D08950E94D12821 +:105180000C9442290E94342938F00E943B2920F0D7 +:10519000952311F00C942B290C94312911240C9493 +:1051A00076290E94532970F3959FC1F3950F50E023 +:1051B000551F629FF001729FBB27F00DB11D639FC9 +:1051C000AA27F00DB11DAA1F649F6627B00DA11D6F +:1051D000661F829F2227B00DA11D621F739FB00D15 +:1051E000A11D621F839FA00D611D221F749F332785 +:1051F000A00D611D231F849F600D211D822F762F1E +:105200006A2F11249F5750409AF0F1F088234AF0FA +:10521000EE0FFF1FBB1F661F771F881F9150504066 +:10522000A9F79E3F510580F00C942B290C94762908 +:105230005F3FE4F3983ED4F3869577956795B795ED +:10524000F795E7959F5FC1F7FE2B880F911D969507 +:10525000879597F9089597F99F6780E870E060E077 +:1052600008959FEF80EC089500240A9416161706FF +:1052700018060906089500240A9412161306140647 +:1052800005060895092E0394000C11F4882352F0AA +:10529000BB0F40F4BF2B11F460FF04C06F5F7F4F62 +:1052A0008F4F9F4F089557FD9058440F551F59F049 +:1052B0005F3F71F04795880F97FB991F61F09F3F03 +:1052C00079F087950895121613061406551FF2CF2C +:1052D0004695F1DF08C0161617061806991FF1CF7C +:1052E00086957105610508940895E894BB276627A3 +:1052F0007727CB0197F90895DC01CB01FC01F999DF +:10530000FECF06C0F2BDE1BDF89A319600B40D9211 +:1053100041505040B8F70895DC01A40FB51F41502B +:10532000504048F0CB01840F951F2E910E949D297B +:1053300041505040D0F70895262FF999FECF92BDE5 +:1053400081BDF89A019700B4021639F01FBA20BD4A +:105350000FB6F894FA9AF99A0FBE0895052E97FBA6 +:105360001EF400940E94C52957FD07D00E94E02931 +:1053700007FC03D04EF40C94C52950954095309508 +:1053800021953F4F4F4F5F4F0895909580957095B1 +:1053900061957F4F8F4F9F4F08950E94022AA59FCE +:1053A000900DB49F900DA49F800D911D1124089520 +:1053B000B7FF0C94CD290E94CD29821B930B089531 +:1053C000A1E21A2EAA1BBB1BFD010DC0AA1FBB1F09 +:1053D000EE1FFF1FA217B307E407F50720F0A21B7B +:1053E000B30BE40BF50B661F771F881F991F1A94E8 +:1053F00069F760957095809590959B01AC01BD0112 +:10540000CF010895A29FB001B39FC001A39F700D6B +:10541000811D1124911DB29F700D811D1124911DBC +:1054200008955058BB27AA270E94292A0C94422984 +:105430000E94342938F00E943B2920F039F49F3F24 +:1054400019F426F40C9431290EF4E095E7FB0C9442 +:105450002B29E92F0E94532958F3BA1762077307C3 +:105460008407950720F079F4A6F50C9475290EF4BD +:10547000E0950B2EBA2FA02D0B01B90190010C0164 +:10548000CA01A0011124FF27591B99F0593F50F47C +:10549000503E68F11A16F040A22F232F342F4427D4 +:1054A000585FF3CF469537952795A795F0405395CC +:1054B000C9F77EF41F16BA0B620B730B840BBAF09C +:1054C0009150A1F0FF0FBB1F661F771F881FC2F707 +:1054D0000EC0BA0F621F731F841F48F4879577951B +:1054E0006795B795F7959E3F08F0B0CF9395880FD5 +:1054F00008F09927EE0F9795879508950E94922AB4 +:105500000C9442290E943B2958F00E94342940F013 +:1055100029F45F3F29F00C942B2951110C94762922 +:105520000C9431290E94532968F39923B1F3552330 +:1055300091F3951B550BBB27AA27621773078407A6 +:1055400038F09F5F5F4F220F331F441FAA1FA9F33C +:1055500035D00E2E3AF0E0E832D091505040E6952A +:10556000001CCAF72BD0FE2F29D0660F771F881F8B +:10557000BB1F261737074807AB07B0E809F0BB0B7E +:10558000802DBF01FF2793585F4F3AF09E3F510592 +:1055900078F00C942B290C9476295F3FE4F3983E25 +:1055A000D4F3869577956795B795F7959F5FC9F77B +:1055B000880F911D9695879597F90895E1E0660FFC +:1055C000771F881FBB1F621773078407BA0720F075 +:1055D000621B730B840BBA0BEE1F88F7E0950895DE +:1055E0000E94F72A6894B1110C94762908950E94BC +:1055F0005B2988F09F5798F0B92F9927B751B0F0E1 +:10560000E1F0660F771F881F991F1AF0BA95C9F746 +:1056100014C0B13091F00E947529B1E008950C9446 +:105620007529672F782F8827B85F39F0B93FCCF3F9 +:10563000869577956795B395D9F73EF490958095C3 +:10564000709561957F4F8F4F9F4F08950E949A28C4 +:1056500008F48FEF08950E949A28880B990B0895FB +:1056600097FB072E16F4009407D077FD09D00E940F +:105670004A2B07FC05D03EF4909581959F4F0895E5 +:10568000709561957F4F0895EE0FFF1F0590F4917F +:10569000E02D0994AA1BBB1B51E107C0AA1FBB1F29 +:1056A000A617B70710F0A61BB70B881F991F5A95AE +:1056B000A9F780959095BC01CD01089581E090E017 +:1056C000F8940C947B2BFB01DC0102C001900D923D +:1056D00041505040D8F70895FB01DC014150504043 +:1056E00048F001900D920020C9F701C01D92415071 +:0A56F0005040E0F70895F894FFCF52 +:1056FA00FFFFFF00FCE1A8A8DD0F180B001E830BBB +:10570A00E51D540B420B380B291E9D0B013C000171 +:10571A0001B00D4B1EFFFF8D078D070000CF0480DF +:10572A00000100000000000000C4077005090CC356 +:10573A000720083808AB07AAB406010000000000D9 +:10574A00000000000000FFFF05AB00000000060695 +:10575A0070059D055306CE05AC05C0050000000086 +:10576A0057097005090C080C65080D0A006E616E70 +:10577A0000696E66006F766600322E332E31006C39 +:10578A006F6F70636F756E7465723A00736D6172D4 +:06579A0074686F6D6500EC +:00000001FF diff --git a/code/Dust_sensor/Dust_sensor.ino b/code/Dust_sensor/Dust_sensor.ino new file mode 100644 index 0000000..fa180fb --- /dev/null +++ b/code/Dust_sensor/Dust_sensor.ino @@ -0,0 +1,607 @@ +/* +* Candle fine dust sensor +* +* Often the air in our homes is much dirtier than the air outside. This fine dust sensor can help you gain insight into the quality of he air inside your home. +* +* It is built around the SDS011 fine dust sensor. It's used here because we can directly connect to it without needing to solder anything onto it. +* +* Sensors like can output 'particle count per cubic meter' or 'micrograms per cubic meter. This sensor does the latter. +* +* The most popular particle size to measure is 2,5 micrometers. These particles can enter deep inside your lungs. Some dust sensors can also mearure other 'bins', such as 1, 5 or 10 micrometers. This sensors can also give us the weight of the 10 micrometer particles. This weight includes the weight of the smaller sizes particles.. +* +* +* SETTINGS */ + + +#define MEASUREMENT_INTERVAL 60 // Seconds between measurements. How many seconds between measurements? Recommended is at least 120 seconds. The minimum is 15 seconds. + +#define HAS_DISPLAY // Did you connect an OLED display? If you have connected a small OLED dislay it will show the latest measurements. + +//#define SHOW_DATAVIZ // Show a basic datavizualisation on the display? This vizualisation is experimental and far from perfect, but might be fun to try. + +#define ALLOW_CONNECTING_TO_NETWORK // Connect wirelessly. Is this device allowed to connect to the network? For privacy or security reasons you may prefer a stand-alone device. If you do allow the device to connect, you can connect a toggle switch to pin 6 to change the connection state at any time. + +//#define ALLOW_FAKE_DATA // Allow fake data to be sent? This is an experimental feature, and requires you to attach a switch to pin 5. It's designed to make the sensor less intrusive in some social situations, allowing you to pretend you are not smoking/cooking. + +//#define MY_REPEATER_FEATURE // Act as signal repeater. Should this sensor act as a repeater for your other devices? This can help the signal spread further. + +#define RF_NANO // RF-Nano. Check this box if you are using the RF-Nano Arduino, which has a built in radio. The Candle project uses the RF-Nano. + + +/* END OF SETTINGS +* +* +* +*/ + +//#define MY_DEBUG // Enable MySensors debug output to the serial monitor? This will help you check if the radio is working ok. + + +#define AVERAGE_HOURS 1 // Averaging period. Over how many hours should the average be calculated? If the device has been on shorter than this period, it will show the average until then. + + +// PINS +#define DUST_SENSOR_RX_PIN 3 // Dust sensor RX pin. Connect this to the TX pin on the sensor. +#define DUST_SENSOR_TX_PIN 4 // Dust sensor TX pin. Connect this to the RX pin on the sensor. +#define TOGGLE_FAKE_DATA_PIN 5 // Pin where the toggle switch to send fake data is connected. +#define CONNECT_TO_NETWORK_PIN 6 // Pin where the toggle switch to allow connecting to the network is connected. +#define RANDOM_SEED_PIN A7 // Pin to use to create more random variables. + +#ifdef RF_NANO +// If you are using an RF-Nano, you have to switch CE and CS pins. +#define MY_RF24_CS_PIN 9 // Used by the MySensors library. +#define MY_RF24_CE_PIN 10 // Used by the MySensors library. +#endif + + +// Enable and select the attached radio type +#define MY_RADIO_RF24 // This is a common and simple radio used with MySensors. Downside is that it uses the same frequency space as WiFi. +//#define MY_RADIO_NRF5_ESB // This is a new type of device that is arduino and radio all in one. Currently not suitable for beginners yet. +//#define MY_RADIO_RFM69 // This is an open source radio on the 433mhz frequency. Great range and built-in encryption, but more expensive and little more difficult to connect. +//#define MY_RADIO_RFM95 // This is a LoRaWan radio, which can have a range of 10km. + +// MySensors: Choose your desired radio power level. High power can cause issues on cheap Chinese NRF24 radio's. +//#define MY_RF24_PA_LEVEL RF24_PA_MIN +//#define MY_RF24_PA_LEVEL RF24_PA_LOW +//#define MY_RF24_PA_LEVEL RF24_PA_HIGH +#define MY_RF24_PA_LEVEL RF24_PA_MAX + +// Mysensors advanced security +#define MY_ENCRYPTION_SIMPLE_PASSWD "smarthome" // Be aware, the length of the password has an effect on memory use. +//#define MY_SIGNING_SOFT_RANDOMSEED_PIN A7 // Setting a pin to pickup random electromagnetic noise helps make encryption more secure. + +// Mysensors advanced settings +#define MY_TRANSPORT_WAIT_READY_MS 10000 // Try connecting for 10 seconds. Otherwise just continue. +//#define MY_RF24_CHANNEL 100 // In EU the default channel 76 overlaps with wifi, so you could try using channel 100. But you will have to set this up on every device, and also on the controller. +#define MY_RF24_DATARATE RF24_1MBPS // Slower datarate makes the network more stable? +//#define MY_NODE_ID 10 // Giving a node a manual ID can in rare cases fix connection issues. +//#define MY_PARENT_NODE_ID 0 // Fixating the ID of the gatewaynode can in rare cases fix connection issues. +//#define MY_PARENT_NODE_IS_STATIC // Used together with setting the parent node ID. Daking the controller ID static can in rare cases fix connection issues. +#define MY_SPLASH_SCREEN_DISABLED // Saves a little memory. +//#define MY_DISABLE_RAM_ROUTING_TABLE_FEATURE // Saves a little memory. + + + +#define RADIO_DELAY 150 // Milliseconds between sensing radio signals during the presentation phase. Gives the radio some time to breathe in between working, and time to listen to a response. +#define LOOPDURATION 1000 // The main loop runs every x milliseconds. It's like a second counter on a clock. + + +// LIBRARIES (in the Arduino IDE go to Sketch -> Include Library -> Manage Libraries to add these if you don't have them installed yet.) +#include // MySensors library +#include // "SDS011 sensor Library". Makes it easy to talk to the fine dust sensor. +#include // Watchdog library. Resets the device if it becomes unresponsive. + + +#ifdef HAS_DISPLAY + #define INCLUDE_SCROLLING 0 + #define OLED_I2C_ADDRESS 0x3C + #include // Simple drivers for the screen. + #include // "SSD1306Ascii". Simple drivers for the screen. + SSD1306AsciiAvrI2c oled; +#endif + + +// SDS011 dust sensor details +float p10 = 0; +float p25 = 0; +float average_p10 = 0; +float average_p25 = 0; + +SDS011 my_sds; + + +// Mysensors settings. +#define CHILD_ID_DUST_PM10 0 +#define CHILD_ID_DUST_PM25 1 +#define ACTIVATED_CHILD_ID 2 +MyMessage message_dust(CHILD_ID_DUST_PM10, V_LEVEL); // Sets up the message format for actual dust messages. +MyMessage message_prefix(CHILD_ID_DUST_PM10, V_UNIT_PREFIX); // Sets up the MySensors prefix message +MyMessage relaymsg(ACTIVATED_CHILD_ID, V_STATUS); // Toggle message + + + +// Other +unsigned long lastLoopTime = 0; // Holds the last time the main loop ran. +int loopCounter = MEASUREMENT_INTERVAL; // Count how many loops have passed. +int measurements_counter = 0; // Used by averaging function. +byte vizPosition = 30; // Used by the experimenal data vizualisation option. +boolean send_all_values = true; // If the controller asks the devive to re-present itself, then this is used to also resend all the current sensor values. +boolean received_echo = false; // If we get a response from the controller, then this is set to true. + +// Fake data feature +boolean sending_fake_data = false; // Experimental. Will allow a user to send fake data for a while. Useful in some social situations. +boolean desired_sending_fake_data = false; // If the user wants to change the state of sending fake data. +float p25_fakeness_range = 0; // How far of the last average the value can meander. +float p10_fakeness_range = 0; // How far of the last average the value can meander. +float p25_addition = 0; // Holds how much will actually be deviated from the average when generating a fake value. +float fakeness_proportion = 0; // How these two values relate. if one goes up, the other should also go up, but in proportion to it's own fakeness range. + +// Connection toggle feature +boolean desired_connecting_to_network = false; +boolean connecting_to_network = true; +boolean may_send_data = true; + +void presentation() +{ +#ifdef ALLOW_CONNECTING_TO_NETWORK + // Send the sketch version information to the gateway and Controller + sendSketchInfo(F("Fine dust sensor"), F("1.1")); + + // Register all child sensors with the gateway + present(CHILD_ID_DUST_PM10, S_DUST, F("10 micrometers & smaller")); delay(RADIO_DELAY); + present(CHILD_ID_DUST_PM25, S_DUST, F("2.5 micrometers")); delay(RADIO_DELAY); + present(ACTIVATED_CHILD_ID, S_BINARY, F("data transmission")); + + send_all_values = true; +#endif +} + + +void setup() +{ + my_sds.begin(DUST_SENSOR_RX_PIN, DUST_SENSOR_TX_PIN); + Serial.begin(115200); + Serial.println(F("Hello, I am a dust sensor")); + +#ifdef ALLOW_FAKE_DATA + pinMode(TOGGLE_FAKE_DATA_PIN, INPUT_PULLUP); + Serial.print(F("Toggle fake-data-mode using a switch on pin ")); Serial.println(TOGGLE_FAKE_DATA_PIN); +#endif + + +#ifdef HAS_DISPLAY + // Start the display (if there is one) + oled.begin(&Adafruit128x64, OLED_I2C_ADDRESS); + oled.setFont(Adafruit5x7); + + oled.ssd1306WriteCmd(SSD1306_DISPLAYON); + oled.setScroll(false); + oled.setCursor(0,0); + oled.print(F("FINE DUST")); + //delay(1000); +#endif + + +#ifdef ALLOW_CONNECTING_TO_NETWORK + // Check if there is a network connection + if(isTransportReady()){ + Serial.println(F("Connected to gateway!")); + +#ifdef HAS_DISPLAY + // Show connection icon on the display + oled.setCursor(80,0); + oled.print(F("W")); +#endif + + }else{ + Serial.println(F("! NO CONNECTION")); + } + +#ifdef HAS_DISPLAY + // Show data transmission icon on the display + oled.setCursor(70,0); + if(may_send_data){ // A small "T" icon on the screen reflects that data transmission is currently allowed. + oled.print(F("T")); + } + else{ + oled.print(F(" ")); + } +#endif + + +#else + Serial.println("This device will not connect to the network.") +#endif + + // Place labels on the screen +#ifdef HAS_DISPLAY + oled.setCursor(0,2); + oled.print(F("2.5:")); + oled.setCursor(0,5); + oled.print(F("10.0:")); +#endif + + wdt_enable(WDTO_8S); // Starts the watchdog timer. If it is not reset once every few seconds, then the entire device will automatically restart. + + //my_sds.wakeup(); +} + + +void send_values() +{ +#ifdef ALLOW_CONNECTING_TO_NETWORK + + send(message_prefix.setSensor(CHILD_ID_DUST_PM10).set( F("ug/m3") ),0); delay(RADIO_DELAY); + if(may_send_data){ + send(message_dust.setSensor(CHILD_ID_DUST_PM10).set(p10,1),0); delay(RADIO_DELAY); + } + send(message_prefix.setSensor(CHILD_ID_DUST_PM25).set( F("ug/m3") ),0); delay(RADIO_DELAY); + if(may_send_data){ + send(message_dust.setSensor(CHILD_ID_DUST_PM25).set(p25,1),0); delay(RADIO_DELAY); + } + send(relaymsg.setSensor(ACTIVATED_CHILD_ID).set(may_send_data)); wait(RADIO_DELAY); + +#endif +} + + +void loop() { + // Send all the child states to the controller. This will initialise things there. + if( send_all_values ){ +#ifdef DEBUG + Serial.println(F("Sending all values")); +#endif + send_all_values = false; + send_values(); + } + + + + +#ifdef ALLOW_FAKE_DATA + boolean fake_data_pin_state = digitalRead(TOGGLE_FAKE_DATA_PIN); + if( fake_data_pin_state != desired_sending_fake_data ){ + desired_sending_fake_data = fake_data_pin_state; + Serial.print(F("FAKE DATA TOGGLED TO ")); Serial.println(desired_sending_fake_data); +#ifdef HAS_DISPLAY + oled.set1X(); + oled.setCursor(72,0); + if( desired_sending_fake_data ){ + oled.print(F("F")); + } + else{ + oled.print(F(" ")); + } +#endif + wait(20); + } +#endif + + +#ifdef ALLOW_CONNECTING_TO_NETWORK + if( digitalRead(CONNECT_TO_NETWORK_PIN) != connecting_to_network ){ + + //connecting_to_network = !desired_connecting_to_network; + wait(10); + connecting_to_network = digitalRead(CONNECT_TO_NETWORK_PIN); + Serial.print(F("NETWORK TOGGLED TO ")); Serial.println(connecting_to_network); + +#ifdef HAS_DISPLAY + if(!connecting_to_network){ // If we should not connect to the network, remove the W icon. + oled.set1X(); + oled.setCursor(80,0); + oled.print(F(" ")); + } +#endif + wait(10); // Avoid bounce + } +#endif + + + // + // HEARTBEAT LOOP + // runs every second (or as long as you want). By counting how often this loop has run (and resetting that counter back to zero after a number of loops), it becomes possible to schedule all kinds of things without using a lot of memory. + // The maximum time that can be scheduled is 255 * the time that one loop takes. So usually 255 seconds. + // + unsigned long currentMillis = millis(); + + if (currentMillis - lastLoopTime > LOOPDURATION) { + lastLoopTime = currentMillis; + loopCounter++; + Serial.print("loopcounter:"); Serial.println(loopCounter); + if(loopCounter > MEASUREMENT_INTERVAL){ + Serial.println(); Serial.println(F("__starting__")); + loopCounter = 0; + } + wdt_reset(); + +#ifdef HAS_DISPLAY + // Update the second countdown on the display. + oled.set1X(); + oled.setCursor(100,0); + oled.print(MEASUREMENT_INTERVAL - loopCounter); + oled.clearToEOL(); +#endif + + + // schedule + switch (loopCounter) { + + case 1: // On the first second + //if(MEASUREMENT_INTERVAL > 29){ // Only uses the sleep-and-wake functionality if there is at least 30 seconds between measurements. + Serial.println(F("Sensor waking up")); + my_sds.wakeup(); + //} + break; + + case 10: + Serial.println(F("Asking for fresh data")); + my_sds.read(&p25, &p10); + break; + + case 11: // On the 11th second (after the fan has been spinning for 10 seconds) + Serial.println(F("Asking for fresh data again")); + while (!my_sds.read(&p25, &p10)) + { + Serial.println(F("Waiting for data")); + delay(10); + } + +#ifdef HAS_DISPLAY + // update the display + + oled.set2X(); + oled.setCursor(0,3); + oled.print(p25); oled.println(F(" ")); + oled.setCursor(0,6); + oled.print(p10); oled.println(F(" ")); + + + + // PM2.5 levels based on opinions of Dutch scientists and the World Health Organization. Keep your yearly average below 10. + oled.setCursor(70,3); + if(sending_fake_data){oled.print(F("FAKED"));} + else if (p25 == 0){ oled.clearToEOL(); } + else if (p25 <= 3){ oled.print(F("GREAT"));} + else if (p25 <= 5){ oled.print(F("GOOD "));} + else if (p25 <= 8){ oled.print(F("OK "));} + else if (p25 <= 14){ oled.print(F("POOR "));} + else if (p25 > 20){ oled.print(F("BAD "));} + + // PM10 levels based on opinions of Dutch scientists and the World Health Organization. Keep your yearly average below 20. + oled.setCursor(70,6); + if(sending_fake_data){oled.print(F("FAKED"));} + else if (p10 == 0){ oled.clearToEOL(); } + else if (p10 <= 5){ oled.print(F("GREAT"));} + else if (p10 <= 10){ oled.print(F("GOOD "));} + else if (p10 <= 20){ oled.print(F("OK "));} + else if (p10 <= 30){ oled.print(F("POOR "));} + else if (p10 > 30){ oled.print(F("BAD "));} +#endif + + if( MEASUREMENT_INTERVAL > 29 ){ // Only goes to sleep if there is a long enough interval between desired measurements. + Serial.println(F("Sensor going to sleep.")); + my_sds.sleep(); + } + break; + + case 12: // On the 12th second we send the first bit of data + received_echo = false; // If all goes well this will be reset to 'true' when the controller acknowledges that it has received the first message. +#ifdef ALLOW_CONNECTING_TO_NETWORK + + if( desired_sending_fake_data == true && sending_fake_data == false ){ + + if( average_p25 != 0 && p25 != average_p25 && average_p10 != 0 && p10 != average_p10 ){ + // We have good enough measurements to create fake data. + + // Determine the fakeness range: how far from the last average newly generated values may meander. + /*if( p25 > average_p25){ + p25_fakeness_range = p25 - average_p25; + } + else if ( p25 < average_p25){ + p25_fakeness_range = average_p25 - p25; + } + + if( p10 > average_p10){ + p10_fakeness_range = p10 - average_p10; + } + else if ( p10 < average_p10){ + p10_fakeness_range = average_p25 - p25; + } + */ + p25_fakeness_range = p25 - average_p25; + p10_fakeness_range = p10 - average_p10; + + if( p25_fakeness_range < 0 ){ + p25_fakeness_range = -p25_fakeness_range; + } + if( p10_fakeness_range < 0 ){ + p10_fakeness_range = -p10_fakeness_range; + } + //fakeness_proportion = (float) p10_fakeness_range / p25_fakeness_range; // Usually the p10 values will be slightly bigger. + fakeness_proportion = (float) p10 / p25; // Usually the p10 values will be slightly bigger. + + randomSeed(analogRead(RANDOM_SEED_PIN)); // Creates better random values. + Serial.print(F("Starting sending fake data. \n-P25 range: ")); Serial.println(p25_fakeness_range); + Serial.print(F("-p10 range: ")); Serial.println(p10_fakeness_range); + Serial.print(F("-proportion: ")); Serial.println(fakeness_proportion); + if(p25_fakeness_range != 0){ + sending_fake_data = true; + } + } +#ifdef DEBUG + else{ + Serial.println(F("Measurement not useful for intiating fake data.")); + } +#endif + } + if( desired_sending_fake_data == false && sending_fake_data == true ){ + Serial.print(F("Will send real data again.")); + sending_fake_data = false; + } + + if(sending_fake_data){ + p25_addition = (float)random( p25_fakeness_range * 100000) / 100000; + if( random(2) ){ p25_addition = -p25_addition;} + Serial.print(F("2.5 Addition: ")); Serial.println(p25_addition); + p25_addition = average_p25 + p25_addition; + Serial.print(F("<< sending FAKE 2.5: ")); Serial.println(p25_addition); + send(message_dust.setSensor(CHILD_ID_DUST_PM25).set(p25_addition,1),1); // This message asks the controller to send and acknowledgement it was received. + } + else{ + Serial.print(F("<< sending 2.5: ")); Serial.println(p25); + send(message_dust.setSensor(CHILD_ID_DUST_PM25).set(p25,1),1); // This message asks the controller to send and acknowledgement it was received. + } +#endif + break; + + case 13: // On the 13th second we send the second bit of data, and check the network connection. +#ifdef ALLOW_CONNECTING_TO_NETWORK + if(sending_fake_data){ // Now creating fake data for P10. It should generally move in the same direction as the p25. + float p10_addition = (float) (p10_fakeness_range * fakeness_proportion); // + (p10_fakeness_range / random(4,8)); + //float addition = (float)random( p25_fakeness_range * 100000) / 100000; + //if( random(2) ){ p10_addition = p10_addition ; } + Serial.print(F("p10_addition = ")); Serial.println(p10_addition); + p10_addition = p10_addition + average_p10; + Serial.print(F("<< sending FAKE 10.0: ")); Serial.println(p10_addition); + if(may_send_data){ + send(message_dust.setSensor(CHILD_ID_DUST_PM25).set(p10_addition,1),1); // This message asks the controller to send and acknowledgement it was received. + } + } + else{ + Serial.print(F("-> sending 10.0: ")); Serial.println(p10); + if(may_send_data){ + send(message_dust.setSensor(CHILD_ID_DUST_PM10).set(p10,1),0); + } + } +#endif + +#ifdef HAS_DISPLAY + oled.set1X(); + oled.setCursor(80,0); +#endif + + if( received_echo == true ){ + Serial.println(F("Connection to controller is ok.")); +#ifdef HAS_DISPLAY + oled.print(F("W")); // Add W icon +#endif + } + else { + Serial.println(F("No connection to controller!")); +#ifdef HAS_DISPLAY + oled.print(F(" ")); // Remove W icon +#endif + } + + + break; + + case 14: // Calculating averages + // Still thinking how to optimally deal with outliers and/or early mis-measurements: + if(measurements_counter * MEASUREMENT_INTERVAL < AVERAGE_HOURS * 3600){ // This limits how strongly the old values influence the new average. + measurements_counter++; + } + + if(!sending_fake_data){ + average_p25 = average(measurements_counter, average_p25, p25); + average_p10 = average(measurements_counter, average_p10, p10); + } + else{ + Serial.print(F("Sending fake data, so average is not changing.")); + } + + Serial.print(F("Average p2.5: ")); Serial.println(average_p25); + Serial.print(F("Average p10: ")); Serial.println(average_p10); + + // Data vizualisation experiment +#if defined(HAS_DISPLAY) && defined(SHOW_DATAVIZ) + // This is an experimental way to show a basic datavizualisation using only the characters available in this display library. + if(vizPosition > 60 ){vizPosition = 30;} + //Serial.print(F("Dataviz x position: ")); Serial.println(vizPosition); + oled.set1X(); + oled.setCursor(vizPosition,1); + if(p25 < 1 || vizPosition == 1){oled.print(F(" "));} + else if(p25 < 2){oled.print(F("_"));} + else if(p25 < 3){oled.print(F("/"));} + else if(p25 < 4){oled.print(F("4"));} // This one isn't perfect.. + else if(p25 < 5){oled.print(F("+"));} + else if(p25 < 6){oled.print(F("t"));} + else if(p25 < 7){oled.print(F("~"));} + else {oled.print(F("'"));} + vizPosition++; + oled.setCursor(vizPosition,1); + oled.print(F(" ")); +#endif + break; + } + } +} + + +float average(int measurements, float old_average, float new_value){ + if(measurements < 2){ + return new_value; + } + else if(measurements < 4){ + return (old_average + new_value) / 2; + } + else if(measurements >= 4){ + float totally = (measurements - 1) * old_average; + return (totally + new_value) / measurements; + } +} + +void receive(const MyMessage &message) +{ +#ifdef ALLOW_CONNECTING_TO_NETWORK + + + if (message.isAck()) { + Serial.println(F(">> Received acknowledgement")); + received_echo = true; + } + + if (message.type == V_STATUS && message.sensor == ACTIVATED_CHILD_ID ){ + may_send_data = message.getBool(); //?RELAY_ON:RELAY_OFF; + send(relaymsg.setSensor(ACTIVATED_CHILD_ID).set(may_send_data)); // We echo the new state to the controller, to say "we got the message". + Serial.print(F("-New may_send_data state: ")); Serial.println(may_send_data); +#ifdef HAS_DISPLAY + // Show connection icon on the display + oled.setCursor(70,0); + if(may_send_data){ // A small "T" icon on the screen reflects that data transmission is currently allowed. + oled.print(F("T")); + } + else{ + oled.print(F(" ")); + } +#endif + + } + +#endif +} + + + +/** This device uses the MySensors library: + * + * The MySensors Arduino library handles the wireless radio link and protocol + * between your home built sensors/actuators and HA controller of choice. + * The sensors forms a self healing radio network with optional repeaters. Each + * repeater and gateway builds a routing tables in EEPROM which keeps track of the + * network topology allowing messages to be routed to nodes. + * + * Created by Henrik Ekblad + * Copyright (C) 2013-2015 Sensnology AB + * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors + * + * Documentation: http://www.mysensors.org + * Support Forum: http://forum.mysensors.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + ******************************* + */ diff --git a/code/Signal-hub/Signal-hub.arduino.avr.nano.elf b/code/Signal-hub/Signal-hub.arduino.avr.nano.elf new file mode 100644 index 0000000..1abeccb Binary files /dev/null and b/code/Signal-hub/Signal-hub.arduino.avr.nano.elf differ diff --git a/code/Signal-hub/Signal-hub.arduino.avr.nano.hex b/code/Signal-hub/Signal-hub.arduino.avr.nano.hex new file mode 100644 index 0000000..ffd3825 --- /dev/null +++ b/code/Signal-hub/Signal-hub.arduino.avr.nano.hex @@ -0,0 +1,1547 @@ +:100000000C941C040C945E260C9437260C948526C4 +:100010000C9485260C9485260C94FD260C9444049F +:100020000C9444040C9444040C9444040C94440430 +:100030000C9444040C9444040C9444040C94440420 +:100040000C94ED250C9444040C94BB250C9495253C +:100050000C9444040C9444040C9444040C94440400 +:100060000C9444040C9444042D416464696E67202C +:10007000746F20706C61796C697374002D526571B6 +:10008000756573746564207374617475733A2000C8 +:100090002D41636B00494E434F4D494E47204D451E +:1000A000535341474520666F72206368696C642032 +:1000B0002300637C777BF26B6FC53001672BFED723 +:1000C000AB76CA82C97DFA5947F0ADD4A2AF9CA4E1 +:1000D00072C0B7FD9326363FF7CC34A5E5F171D851 +:1000E000311504C723C31896059A071280E2EB273F +:1000F000B27509832C1A1B6E5AA0523BD6B329E362 +:100100002F8453D100ED20FCB15B6ACBBE394A4C41 +:1001100058CFD0EFAAFB434D338545F9027F503CC1 +:100120009FA851A3408F929D38F5BCB6DA2110FFED +:10013000F3D2CD0C13EC5F974417C4A77E3D645DEA +:10014000197360814FDC222A908846EEB814DE5E77 +:100150000BDBE0323A0A4906245CC2D3AC629195CB +:10016000E479E7C8376D8DD54EA96C56F4EA657A07 +:10017000AE08BA78252E1CA6B4C6E8DD741F4BBDA8 +:100180008B8A703EB5664803F60E613557B986C155 +:100190001D9EE1F8981169D98E949B1E87E9CE5572 +:1001A00028DF8CA1890DBFE6426841992D0FB0541C +:1001B000BB1648656C6C6F2C204920616D20612056 +:1001C0005369676E616C204875622E0021204E4F86 +:1001D000434F4E4E454354494F4E00486900436FCC +:1001E0006E6E656374656420746F206761746577F3 +:1001F000617921000402FFFFEF030402EF446574FC +:1002000065637465642020005265706C61792020FC +:10021000202000446576696365207374617475738A +:1002200000312E31005369676E616C2048756200A1 +:100230002C005061747465726E3A20002020444591 +:10024000433A2000203E204845583A2000580031CB +:1002500000300043616E6E6F742068616E646C657F +:100260002074686973207369676E616C002C005399 +:1002700069676E616C2073746F726564204F4B0008 +:10028000207E7E3E2000456E6F756768207370612A +:100290006365206C656674005F5F53746F72696E8E +:1002A00067003E00656E746572696E672064656CF8 +:1002B00065746520414C4C206D656E7500656E74EB +:1002C0006572696E672064656C657465206C617326 +:1002D00074206D656E7500656E746572696E672059 +:1002E0006D656E755F6E657700042000FFEF5F5FE0 +:1002F0004D454E55004D4F52453E002C204F6666F1 +:10030000002C204F6E002D416464696E6720746F6D +:1003100020706C61796C6973743A0045524153499D +:100320004E472053544F524544205349474E414C69 +:10033000204441544100030600EF2D6E65772023D1 +:1003400000506C6179696E67207369676E616C201B +:1003500066726F6D20706C61796C69737420230014 +:100360004C6F77207175616C697479207369676E61 +:10037000616C004E6F20726570656174696E6720F4 +:100380007061727420666F756E64004572726F7270 +:100390002073746F72696E67207369676E616C0099 +:1003A0004572726F722073746F72696E6720736921 +:1003B000676E616C004F4B0046696E6973686564D7 +:1003C00020636F7079696E67004E6F7720706C6183 +:1003D0007920746865204F4646207369676E616CAA +:1003E00000204E6F7720706C6179204F4646207355 +:1003F00069676E616C0053746174652000446574B4 +:1004000065637465642061206B6E6F776E2073691D +:10041000676E616C004D4154434800436F6D7061DD +:1004200072696E670020746F200053454E443A2075 +:10043000546F67676C6520004D6174636800071135 +:10044000204F4E2020EF0711204F464620EF071186 +:1004500020504C4159EF44656C6574696E67206C9F +:100460006173742073746F726564207369676E6161 +:100470006C004F4B005245504C4159494E4700418A +:100480006E6F6D616C7920696E7369646500506C84 +:100490006179696E672E2E005F5F7265706C61799D +:1004A000696E675F0007117700200000EF07114DAC +:1004B000454E5520EF04200000EF07114D4F5245E7 +:1004C0003EEFFF43414E43454C000000000000005A +:1004D000000000000000001E4E6577207369676E03 +:1004E000616C000000000000000000002844656C02 +:1004F000657465206C6173740000000000000000EA +:10050000003244656C65746520616C6C000000000D +:100510000000000000001F44657465637420736967 +:100520006E676C6500000000000000204465746583 +:100530006374206F6E202B206F6666000000000041 +:10054000215265706C61792073696E676C6500007B +:100550000000000000225265706C6179206F6E20EF +:100560002B206F666600000000001F5265616C6CF6 +:10057000792064656C657465206C61737400001F7C +:100580005265616C6C792064656C65746520414CC2 +:100590004C0000000150726F63657373696E6700F1 +:1005A000000000000000000000025369676E616CEB +:1005B000206D6174636865642100000000000352CF +:1005C00065706C6179696E672000000000000000B2 +:1005D000000000045369676E616C207761732073BB +:1005E000746F72656400000005426164207369677E +:1005F0006E616C2074727920616761696E064F7557 +:1006000074206F662073746F726167652073706108 +:10061000636507506C617920746865207369676E43 +:10062000616C000000000008506C6179204F4E2082 +:100630007369676E616C00000000000009506C6116 +:1006400079204F4646207369676E616C0000000098 +:10065000000A44656C65746564206C617374000005 +:100660000000000000000B44656C65746564206147 +:100670006C6C0000000000000000007468726F776E +:10068000696E672061776179206C656674206F768A +:10069000657220746F7563685F73637265656E5F02 +:1006A00073657269616C20627974653A00546F7584 +:1006B00063682073637265656E20646964206E6F81 +:1006C0007420726573706F6E6420746F20636F6D39 +:1006D0006D616E64000306FFEF52096AD53036A5DE +:1006E00038BF40A39E81F3D7FB7CE339829B2FFF69 +:1006F00087348E4344C4DEE9CB547B9432A6C223B4 +:100700003DEE4C950B42FAC34E082EA16628D92423 +:10071000B2765BA2496D8BD12572F8F66486689833 +:1007200016D4A45CCC5D65B6926C704850FDEDB9F2 +:10073000DA5E154657A78D9D8490D8AB008CBCD34C +:100740000AF7E45805B8B34506D02C1E8FCA3F0FF0 +:1007500002C1AFBD0301138A6B3A9111414F67DCAF +:10076000EA97F2CFCEF0B4E67396AC7422E7AD35DB +:1007700085E2F937E81C75DF6E47F11A711D29C54E +:10078000896FB7620EAA18BE1BFC563E4BC6D279C3 +:10079000209ADBC0FE78CD5AF41FDDA8338807C746 +:1007A00031B11210592780EC5F60517FA919B54A09 +:1007B0000D2DE57A9F93C99CEFA0E03B4DAE2AF545 +:1007C000B0C8EBBB3C83539961172B047EBA77D634 +:1007D00026E169146355210C7D00000000240027E8 +:1007E000002A0000000008000201000003040700C6 +:1007F0000000000000000000000000230026002987 +:100800000000000000250028002B0004040404045C +:1008100004040402020202020203030303030301AD +:100820000204081020408001020408102001020484 +:10083000081020003B2A722C11241FBECFEFD8E0F5 +:10084000DEBFCDBF11E0A0E0B1E0EAE2F0E602C019 +:1008500005900D92AA36B107D9F725E0AAE6B1E0D6 +:1008600001C01D92A63CB207E1F714E0CBE1D4E051 +:1008700004C02197FE010E94EA2FCA31D107C9F7AF +:100880000E94932B0C9408300C940000E8E4F3E0F1 +:10089000A089B18982E08C93A485B5851C92A68538 +:1008A000B78580E18C93108EA489B58986E08C93FE +:1008B000A289B3898C9180618C93A289B3898C9130 +:1008C00088608C93A289B3898C9180688C9302880C +:1008D000F389E02D80818F7D80830895DB01FC0109 +:1008E000242F3C91308311963C91119731831296BD +:1008F0003C911297328313963C9113973383245083 +:1009000014963496243068F72CEF240F26952695FC +:1009100030E02F5F3F4F220F331F220F331F4370F2 +:10092000DB01A20FB31FFC01E20FF31F415018F0CF +:100930008D918193FBCF0895AF92BF92CF92DF92BA +:10094000EF92FF920F931F93CF93DF936C017B0184 +:100950008B01040F151FEB015E01AE18BF08C01715 +:10096000D10759F06991D601ED91FC910190F08188 +:10097000E02DC6010995892B79F7C501DF91CF914B +:100980001F910F91FF90EF90DF90CF90BF90AF90AD +:100990000895FC01538D448D252F30E0842F90E085 +:1009A000821B930B541710F0CF960895019708956A +:1009B000FC01918D828D981761F0A28DAE0FBF2F33 +:1009C000B11D5D968C91928D9F5F9F73928F90E089 +:1009D00008958FEF9FEF0895FC01918D828D9817F8 +:1009E00031F0828DE80FF11D858D90E008958FEF35 +:1009F0009FEF0895FC01918D228D892F90E0805CFE +:100A00009F4F821B91098F739927089588E493E083 +:100A10000E94FA0421E0892B09F420E0822F089536 +:100A2000FC01A48DA80FB92FB11DA35ABF4F2C9163 +:100A3000848D90E001968F739927848FA689B7895A +:100A40002C93A089B1898C91837080648C93938D51 +:100A5000848D981306C00288F389E02D80818F7DF4 +:100A600080830895EF92FF920F931F93CF93DF93AC +:100A7000EC0181E0888F9B8D8C8D98131AC0E889DA +:100A8000F989808185FF15C09FB7F894EE89FF89A9 +:100A90006083E889F98980818370806480839FBF47 +:100AA00081E090E0DF91CF911F910F91FF90EF9047 +:100AB0000895F62E0B8D10E00F5F1F4F0F73112757 +:100AC000E02E8C8D8E110CC00FB607FCFACFE88992 +:100AD000F989808185FFF5CFCE010E941005F1CF05 +:100AE000EB8DEC0FFD2FF11DE35AFF4FF0829FB706 +:100AF000F8940B8FEA89FB8980818062CFCF0F93B6 +:100B00001F93CF93DF938C01D0E0C0E0F801EC0F8E +:100B1000FD1F6491662341F088E493E00E94320552 +:100B2000892B11F02196F2CFCE01DF91CF911F9149 +:100B30000F910895CF93DF93EC01888D8823B9F04E +:100B4000AA89BB89E889F9898C9185FD03C08081D8 +:100B500086FD0DC00FB607FCF7CF8C9185FFF2CF55 +:100B6000808185FFEDCFCE010E941005E9CFDF9196 +:100B7000CF91089580E090E0892B29F00E9406052E +:100B800081110C940000089590E0FC01ED51F84FA4 +:100B90002491FC01E15EF74F3491FC01E55FF74FD2 +:100BA000E491EE23C9F0222339F0233001F1A8F4B7 +:100BB000213019F1223029F1F0E0EE0FFF1FEF5F35 +:100BC000F74FA591B4918FB7F894EC91611126C0BD +:100BD00030953E233C938FBF08952730A9F02830ED +:100BE000C9F0243049F7809180008F7D03C0809147 +:100BF00080008F7780938000DFCF84B58F7784BDAE +:100C0000DBCF84B58F7DFBCF8091B0008F77809351 +:100C1000B000D2CF8091B0008F7DF9CF3E2BDACFDC +:100C2000CF93DF9390E0FC01E15EF74F2491855F65 +:100C3000974FFC0184918823C9F090E0880F991F99 +:100C4000FC01E752F84FA591B491FC01EF5FF74F1B +:100C5000C591D49161110DC09FB7F8948C912095E6 +:100C600082238C938881282328839FBFDF91CF9193 +:100C70000895623051F49FB7F8943C91822F80958B +:100C800083238C93E8812E2BEFCF8FB7F894EC91D0 +:100C90002E2B2C938FBFEACF3FB7F894809140035F +:100CA00090914103A0914203B091430326B5A89BC4 +:100CB00005C02F3F19F00196A11DB11D3FBFBA2FEE +:100CC000A92F982F8827BC01CD01620F711D811DAE +:100CD000911D42E0660F771F881F991F4A95D1F733 +:100CE00008952FB7F89460913C0370913D03809173 +:100CF0003E0390913F032FBF08950895209139033B +:100D000030913A032817390771F490913803809194 +:100D10003703981741F0E0913803F0E0E950FD4FB8 +:100D2000808190E008958FEF9FEF08950895EF92EE +:100D3000FF920F931F93CF93DF93DC015C96ED90AE +:100D4000FC905D97E114F10479F481E090E0139652 +:100D50009C938E93129790E080E0DF91CF911F914A +:100D60000F91FF90EF9008955196ED91FC9152975D +:100D700050968C915097982F90950FB75E962C9126 +:100D80005E97122F127021FD6095F8942081112337 +:100D900019F1282B2083E7012197F1F728E0462F4E +:100DA00050E0308160FF1AC0382B3083E701219773 +:100DB000F1F7BA0175956795215089F7112381F0F4 +:100DC0008081892380830FBF5C968D919C910197D0 +:100DD000F1F781E090E0C1CF2923DCCF3923E5CFC3 +:100DE0009081892BEFCF2091390330913A03281756 +:100DF000390771F4809137032091380390E0805CCB +:100E00009F4F821B910960E470E00E94B42F089507 +:100E100090E080E008952091390330913A0328173B +:100E20003907B9F49091380380913703981789F006 +:100E3000E0913803F0E0E950FD4F808120913803C4 +:100E400030E02F5F3F4F2F7333272093380390E01C +:100E500008958FEF9FEF089590E080E00895FC01E2 +:100E6000838187708860838384818F7180628483AB +:100E70006783CF010895E091E802F091E90230978D +:100E800021F00280F381E02D099408950F931F93C0 +:100E90000E9471060091EA021091EB022091EC028F +:100EA0003091ED02601B710B820B930B1F910F9120 +:100EB00008959C018091E8029091E90282179307BE +:100EC000F1F09091F2029F719093F2023093E90257 +:100ED0002093E802E091E802F091E902309721F0D6 +:100EE0000190F081E02D09950E9471066093EA025D +:100EF0007093EB028093EC029093ED020895809141 +:100F0000F202805E8093F202E5CF0F931F930E945E +:100F100046072091F3022077203779F000E117E2AD +:100F200020E030E0061717072807390758F48FE14B +:100F300091E01F910F910C94590700E61AEE20E002 +:100F400030E0F0CF1F910F9108958091F3028F70E0 +:100F5000863020F088E191E00C9459070895809143 +:100F6000F20284608093F2028091F3028078809391 +:100F7000F302E0919A01F0919B01309709F00994F6 +:100F80000895CF93C091F402CF3FA1F00E94710663 +:100F90006093EE027093EF028093F0029093F1025F +:100FA00080914603C813C093460384E191E0CF913A +:100FB0000C9459070E944607613D774081059105D1 +:100FC00068F08091F202803618F480E191E0EFCF72 +:100FD000877F8093F20288E191E0E9CFCF91089575 +:100FE000809144038F3F21F080E191E00C945907F8 +:100FF0000E944607613D77408105910550F0809140 +:10100000F202803618F48CE091E0F0CF88E091E0B5 +:10101000EDCF08950E944607613D77408105910517 +:1010200020F48091F20281FF17C0809145038F3F29 +:1010300049F08091F2028E7F8093F2028CE091E081 +:101040000C9459078091F202803618F488E191E0FF +:10105000F7CF88E091E0F4CF0895CF93DF93FC01C0 +:10106000EB01DA019C01205F3F4F88819C91892729 +:101070008083898111969C911197892781838A8128 +:1010800012969C911297892782838B8113969C914B +:10109000139789278383349624961496E217F307CF +:1010A00021F7DF91CF910895FC01DB019C01205FC6 +:1010B0003F4F80819C9189278083818111969C91EB +:1010C000119789278183828112969C911297892793 +:1010D0008283838113969C91139789278383349607 +:1010E0001496E217F30729F70895ECEAF1E08CE291 +:1010F00091E0DF019C011D9221503040E1F71092F8 +:101100002D0110922C018BE291E090932B018093A2 +:101110002A018FEF809329011092AB011092AA014E +:101120001092A50181E0809328011092A4010E94F1 +:101130004C066093A6017093A7018093A801909339 +:10114000A90108958F929F92AF92BF920F931F9320 +:10115000CF93DF93CDB7DEB7A1970FB6F894DEBF7C +:101160000FBECDBF19A2423008F44AE08E010F5DD8 +:101170001F4F842E912CB12CA12CA50194010E940B +:10118000C82FE62FB901CA01EA3044F5E05DD80165 +:10119000EE938D01232B242B252B79F790E080E013 +:1011A000109769F0FD0101900020E9F73197AF0138 +:1011B0004A1B5B0BBD0188E493E00E949C04A1964E +:1011C0000FB6F894DEBF0FBECDBFDF91CF911F9158 +:1011D0000F91BF90AF909F908F900895E95CD7CF0B +:1011E000CF92DF92EF92FF92CF93DF936B017C015E +:1011F0006DE288E493E00E943205EC0166277727D0 +:10120000CB016C197D098E099F094AE00E94A20852 +:101210008C0F9D1FDF91CF91FF90EF90DF90CF90CB +:101220000895BC01990F880B990B97FD0C94F00859 +:101230004AE00C94A20842E050E068E571E088E4DE +:1012400093E00C949C04CF93DF930E947F05EC0104 +:101250000E941B098C0F9D1FDF91CF9108950E9462 +:10126000460482EB91E00C942309CF93DF93682F1F +:1012700070E090E080E04AE00E94A208EC010E9449 +:101280001B098C0F9D1FDF91CF91089588E494E096 +:101290000E94F306892B51F088E494E00E940B072A +:1012A000682F88E493E00E943205F0CF0C941B096C +:1012B0001F93CF93DF93EC01162F0E944E2E181729 +:1012C00039F0612FCE01DF91CF911F910C94782ED0 +:1012D000DF91CF911F9108950C944E2E91E59CBD06 +:1012E00091E09DBD2A98E3E2F0E03197F1F78EBDE1 +:1012F00000000DB407FEFDCF8EB5FB019FEF4150FE +:10130000C8F0222361F09EBD00000DB407FEFDCFA2 +:101310008EB56115710599F380833196F0CFDF01A9 +:10132000119680818EBD00000DB407FEFDCF8EB5F5 +:10133000FD01E5CF2A9AE3E2F0E03197F1F7089555 +:10134000CF93DF931F92CDB7DEB7698320E041E0F2 +:10135000BE016F5F7F4F0E946E090F90DF91CF91AA +:1013600008958091F302982F9077903759F0982F35 +:1013700092959F709F5F97709295907F8F78892B41 +:101380008093F3028091F2028B7F8F7E8093F20232 +:1013900029986CE080E20C94A0096FE080E20E9442 +:1013A000A00960912E016F3F19F08AE20E94A00906 +:1013B000299A0895CF93C82F809344038F3F51F00B +:1013C00080932E0163E082E20E94A00961E081E245 +:1013D0000E94A0090E94CD096C2F90E080E00E943D +:1013E000662E81E0CF91089521E041E070E060E059 +:1013F0000C946E0920E040E070E060E00C946E090F +:10140000E2EFF2E08081877F8F7E80831092EE0290 +:101410001092EF021092F0021092F10244E050E0BC +:1014200070E060E084E493E00C943E2EEF92FF9233 +:101430000F931F93CF93DF93EC017B0180E0611545 +:10144000710531F0CB010E94DA2C8931910518F534 +:10145000082F10E09B819770980143E0220F331F03 +:101460004A95E1F7922B9B839C819F719C838823F3 +:1014700031F0A801B701CE0107960E94E32CFE01CE +:10148000E00FF11F1782CE01DF91CF911F910F91D5 +:10149000FF90EF90089589E1DBCF0F931F93CF93D7 +:1014A000DF93EC01E0E06115710551F0FB01019063 +:1014B0000020E9F73197E61BF70BE931F10500F55C +:1014C0000E2F10E08B818770980193E0220F331F5D +:1014D0009A95E1F7822B8B838C818F718C83EE231D +:1014E00029F0A801CE0107960E94F92FFE01E00F16 +:1014F000F11F1782CE01DF91CF911F910F910895B7 +:10150000E9E1DECFFC012481229526952770213068 +:1015100011F487810895211104C007960E948E2C32 +:10152000089580E00895CF92DF92EF92FF921F938B +:10153000CF93DF93EC0185E990E00E947F058E81D7 +:101540000E9435098C8184FF0BC080E990E0DF9117 +:10155000CF911F91FF90EF90DF90CF900C942309D3 +:101560008D818230B1F58CE790E00E947F05CE013D +:101570000E94820AC12CD12C7601C394811103C030 +:10158000C12CD12C76014AE0C701B6010E94A20805 +:101590000E941B098E818A508A35D8F4809191016E +:1015A0008630B8F488E690E00E94230920919101EA +:1015B00011E0120F10939101CE010E94820A9E81C8 +:1015C000212F30E025573E4F81110BC09950F90172 +:1015D0009083DF91CF911F91FF90EF90DF90CF909C +:1015E0000895955AF4CF2F923F924F925F926F9247 +:1015F0007F928F929F92AF92BF92CF92DF92EF92A3 +:10160000FF920F931F93CF93DF93CDB7DEB7A297CF +:101610000FB6F894DEBF0FBECDBFB82E8091440345 +:10162000FB018083138112FDEDC01695169516956A +:10163000195FF0914703F9A3F170FAA3412F11321A +:1016400008F040E250E08BE691E00E94F02F10920B +:10165000A3051092A4051092A5051092A60510925C +:10166000A7051092A8051092A9051092AA0510923C +:10167000AB051092AC051092AD051092AE0510921C +:10168000AF051092B0051092B1051092B2051092FC +:101690009B0510929C0510929D0510929E0510923C +:1016A0009F051092A0051092A1051092A20500E2DC +:1016B000902E113110F410E1912EE92CE294FFE00C +:1016C000EF22F12CABE6CA2EA1E0DA2E8E010F5EDE +:1016D0001F4FBBE1AB2EB60183EA95E00E94540890 +:1016E0008091A9049091AA04892B09F40BC14BEABB +:1016F00054E063EA75E0C8010E942D0888248394B1 +:10170000482D50E08091A9049091AA0448175907E8 +:101710000CF07AC09801DE0111966FE0362E7AE067 +:10172000572EE5E04E2EF801E40DF11D708084E0A7 +:10173000840D8F70482EF801E50DF11D808194E035 +:10174000950D9F70592EF801E30DF11D908164E015 +:10175000630D6F70362EF9016081E62FF0E0EE54D4 +:10176000FF4F7491E72DF0E0EE54FF4F6491E82FA6 +:10177000F0E0EE54FF4F8491E92FF0E0EE54FF4F7C +:101780009491672E660C77FD6A24E62FEE0F67FDB5 +:10179000EA25282E220C87FD2A24792E770C97FD26 +:1017A0007A24F62FF827F927F625FE27FC93F72F42 +:1017B000F827F927FE27F2251196FC931197672742 +:1017C00096272926272412962C9212978627682678 +:1017D000762413967C9213972C5F3F4F14960A172A +:1017E0001B0709F0A0CF94E0440F551F9A95E1F72D +:1017F00045555B4FBE016F5F7F4FC8010E942D08AA +:1018000083947ECF10E215CFE989F0E0EE54FF4FCC +:10181000E491E98BED89F0E0EE54FF4FE491ED8B1C +:10182000E98DF0E0EE54FF4FE491E98FED8DF0E0AB +:10183000EE54FF4FE491ED8F8A89EE89F0E0EE548B +:10184000FF4FE491EA8BEA8DF0E0EE54FF4FE49114 +:10185000EE8BEE8DF0E0EE54FF4FE491EA8FE82F2F +:10186000F0E0EE54FF4FE491EE8F8B89EB8DF0E0CA +:10187000EE54FF4FE491EB8BE82FF0E0EE54FF4F76 +:10188000E491EB8F8F89EF8DF0E0EE54FF4FE49100 +:10189000EF8BE82FF0E0EE54FF4FE491EF8F88A13B +:1018A000EC8DF0E0EE54FF4FE491E8A3E88DF0E01A +:1018B000EE54FF4FE491EC8FEC89F0E0EE54FF4FD3 +:1018C000E491E88FE82FF0E0EE54FF4FE491EC8BC9 +:1018D00084E0440F551F8A95E1F745555B4FB801E9 +:1018E00083EA95E00E942D0840E163EA75E0C601B5 +:1018F0000E946E04F0E1CF0ED11C81E0E81AF108DD +:1019000009F0E9CE299883E092E00197F1F76EE0C3 +:1019100080E20E94A0098BE891E00197F1F76B2D1E +:101920008AE20E94A0096B2D80E30E94A00981EE4B +:101930000E94FA09EFEFBE1619F0FAA180EAF11140 +:1019400080EB20E0492D6BE671E00E946E09299A38 +:10195000F12CE12C8FEF0E94FA09182F807321F4EB +:1019600081E0E81AF108B1F7299860E387E20E9464 +:10197000A00914FF03C081EE0E94FA090E94CD095C +:1019800015FF02C0E1E0E9A391E0FFEFBF1290E094 +:1019900089A18170892BA2960FB6F894DEBF0FBE85 +:1019A000CDBFDF91CF911F910F91FF90EF90DF900E +:1019B000CF90BF90AF909F908F907F906F905F90EF +:1019C0004F903F902F9008950F931F93CF938C01CA +:1019D000FC0182819091F20290FF04C08F3F51F58B +:1019E000CFEF04C0811116C0C0914503B8018C2F00 +:1019F0000E94F30A909145039C1313C09091F30247 +:101A0000811113C021E0290F2F70907F922B9093AA +:101A1000F30207C08F3F21F3B8010E94F30A882325 +:101A200019F3CF911F910F910895907F9093F30236 +:101A300081E0F7CF80E0F5CF2091F20222FD0C94F7 +:101A4000E40C80E008959091440390936804109210 +:101A5000690480936D0460936C0490916B04907E94 +:101A600090936B048F3F11F44BE551E0BA0187E688 +:101A700094E00E944D0A0C941C0D20914403FC013B +:101A80002183F8E06F9FB00111246160FC01248183 +:101A9000207F622B64832091E70220FD0C941C0DB3 +:101AA00080E008951F93CF93DF93CDB7DEB72B97D8 +:101AB0000FB6F894DEBF0FBECDBF0E944B158091CC +:101AC000440380936804109269041FEF10936D041F +:101AD0008BE080936C0480916B04807E83608093A4 +:101AE0006B0465E272E087E694E00E94160A0E94A9 +:101AF0001C0D809144038093680410926904109334 +:101B00006D048CE080936C0480916B04807E836014 +:101B100080936B0461E272E087E694E00E94160A0B +:101B20000E941C0D60E971E080E090E00E948C1240 +:101B300080914403809368041092690481E080934B +:101B40006D0484E280936C0480916B04807E8093AA +:101B50006B0463E172E087E694E00E94160A0E943B +:101B60001C0D60E971E080E090E00E948C1268E05A +:101B700072E0CE0101960E94D32C1AE08091A3015D +:101B800090E009961817190609F094F487E2810F7E +:101B90008887AE014F5F5F4F63E0812F0E94230D66 +:101BA00060E971E080E090E00E948C121F5FE6CF58 +:101BB0006DEF71E0CE0101960E94D32C14E6809166 +:101BC0009D019091A301891B990B8D599F4F181767 +:101BD000190609F094F48DEC810F8A87AE014F5FEE +:101BE0005F4F60E0812F0E94230D60E971E080E08B +:101BF00090E00E948C121F5FE2CF81E080931C0175 +:101C00002B960FB6F894DEBF0FBECDBFDF91CF91FC +:101C10001F910895FF920F931F93CF93DF93C7E611 +:101C2000D4E004E413E0F801808189831A82FF2460 +:101C3000FA94FE828FE08D838C81807E83608C831A +:101C40006AE571E0CE010E944D0A8B818770806148 +:101C50008B838C818F71806C8C8381E08F8318865D +:101C6000CE010E941C0D8FE00E9459124AE551E0FE +:101C700061E18FEF0E94230DF801808189831A8230 +:101C8000FE8286E08D838C81807E83608C8361817F +:101C9000CE010E942F070E941C0D86E00E9459125F +:101CA000DF91CF911F910F91FF900C94520D2F92C5 +:101CB0003F924F925F926F927F928F929F92AF92DC +:101CC000BF92CF92DF92EF92FF920F931F93CF9329 +:101CD000DF93CDB7DEB7E9970FB6F894DEBF0FBE3E +:101CE000CDBF26E02BAB1BE187E10E94F40980FD0C +:101CF000C1C33BA931503BAB332309F4BBC321E043 +:101D000041E070E060E080E60E946E09082F8132B9 +:101D100020F082EE0E94FA0900E021E0402F68E8FE +:101D200074E081E60E946E0960E487E20E94A009E7 +:101D30001092A3051092A4051092A5051092A60575 +:101D40001092A7051092A8051092A9051092AA0555 +:101D50001092AB051092AC051092AD051092AE0535 +:101D60001092AF051092B0051092B1051092B20515 +:101D700010929B0510929C0510929D0510929E0555 +:101D800010929F051092A0051092A1051092A20535 +:101D9000013108F463C182E090E09AAB89ABE8E8D6 +:101DA0002E2EE4E03E2EE9A9FAA93197FAABE9AB71 +:101DB000319609F471C140E1B101CE0181960E94D2 +:101DC0006E044091A9045091AA044115510509F4EB +:101DD00063C174E0440F551F7A95E1F745555B4F99 +:101DE000B101CE0141960E942D08E989F0E0E75249 +:101DF000F94FE491E98BED89F0E0E752F94FE49176 +:101E0000ED8BE98DF0E0E752F94FE491E98FED8D2C +:101E1000F0E0E752F94FE491ED8F8E8DEA8DF0E01E +:101E2000E752F94FE491EE8FEE89F0E0E752F94F77 +:101E3000E491EA8FEA89F0E0E752F94FE491EE8B02 +:101E4000E82FF0E0E752F94FE491EA8B8B89EB8DB4 +:101E5000F0E0E752F94FE491EB8BE82FF0E0E75226 +:101E6000F94FE491EB8F8F89EF8DF0E0E752F94F56 +:101E7000E491EF8BE82FF0E0E752F94FE491EF8F18 +:101E80008C89E88DF0E0E752F94FE491EC8BEC8D12 +:101E9000F0E0E752F94FE491E88FE8A1F0E0E75273 +:101EA000F94FE491EC8FE82FF0E0E752F94FE4911D +:101EB000E8A30091A9041FAA1EAA8FEF800FA82FE4 +:101EC000B0E0B9AFA8AF015009F4CDC048AD59ADED +:101ED000EEA9FFA94E1B5F0B64E0440F551F6A95E6 +:101EE000E1F745555B4FBE016F5E7F4FCE01019616 +:101EF0000E942D089E012F5F3F4F7901CE01419630 +:101F00009DAB8CABA5E0B0E0F7019081818132817F +:101F10002381792F770F97FD7127B82EBB0C87FD92 +:101F2000B126C32ECC0C37FDC126622F660F27FDCC +:101F30006127472F440F77FD41274B2C440CB7FEF8 +:101F400004C05B2D550F452E41269C2C990CC7FCD7 +:101F50009126A62EAA0C67FDA126D42EDD0C47FFE4 +:101F600004C0542F550FD52ED126542D550F47FCA4 +:101F70005127F92DFF0F97FCF127EA2DEE0FA7FC53 +:101F8000E127792E7D24582E5526832E8F26622E0A +:101F90006E264D2554259F26AE26E32FE727EB25F9 +:101FA000E427E525E925E625F0E0E752F94F3491ED +:101FB000ECA9FDA93083E22FEB25EC25E725E527E9 +:101FC000E825EA25F0E0E752F94FD490FD01EF70E3 +:101FD000FF2721E130E02C0F3D1FE20FF31FD082DD +:101FE0009C2596274927542695246924E62DF0E060 +:101FF000E752F94F6490FD013596EF70FF2721E11C +:1020000030E02C0F3D1FE20FF31F6082E82FE7271F +:10201000E627E7255E27E52FE825AE26EA2DF0E046 +:10202000E752F94FA490FD013A96EF70FF27E20FB7 +:10203000F31FA08294E0E90EF11C2CA93DA92C5FAE +:102040003F4F3DAB2CAB1496A531B10509F05CCFE9 +:102050008EA99FA901969FAB8EAB35CFA1E0B0E0D2 +:10206000BAABA9AB9CCE4BEA54E0BE016F5E7F4F8A +:10207000C1010E942D0863EA75E0C1010E94540865 +:1020800040E1BE016F5D7F4F83EA95E00E946E04E0 +:1020900090E1290E311C87CE30918B04832F837001 +:1020A000823009F021CE20918C04A22FA770EA2E55 +:1020B00000918D04F09089048091880440918A04F5 +:1020C0009091F30290689093F30290914403491326 +:1020D00076C1832F8695869586958A3108F089E1A9 +:1020E00090E03770BC0153E0660F771F5A95E1F717 +:1020F000362B30938B0488579B4FFC01178223FFAC +:1021000015C081E2E8E8F4E0A7E6B4E001900D92A2 +:102110008A95E1F7277F206120936B044093680440 +:10212000F092690487E694E00E941C0D90918C0463 +:1021300094FD3DC1F3E0EF123AC180918D0421EF8F +:10214000280F233008F4D0CD0430C1F480918E04E0 +:1021500090912F01891719F08F3F09F0C5CD88E8BC +:1021600094E00E94820A9FEF980F9E3F18F40E940D +:10217000DA09BACD8FEF80934403B6CD0830D9F495 +:102180000091F20200FFB0CD88E894E00E94820A3C +:102190008F3F09F4A9CD8F5F8F3F09F4A5CD9091B2 +:1021A0004603891708F0A0CD01FD9ECD809346031C +:1021B000F092450399CD0831C1F480914403809396 +:1021C0006804F09269048FEF80936D0489E1809335 +:1021D0006C0480916B04807E836080936B0461E06B +:1021E00087E694E00E942F0732C0093171F4809194 +:1021F000F20283FF79CD877F8093F20288E894E032 +:102200000E94820A8093F4026FCD0E3109F46CCDE6 +:102210000D3101F5809144038093680410926904A4 +:102220008FEF80936D048FE180936C0483E480933F +:102230006B0480916A048770806180936A0480E0F7 +:102240009FEF90936F0480936E0487E694E00E9462 +:10225000E40C4ACD209189042111AEC08D3059F48F +:1022600088E198E00FB6F894A895809360000FBEBF +:1022700090936000FFCF8B3179F488E894E00E945E +:10228000820A91E0811101C090E08091E70290FB09 +:1022900080F98093E70228CD863099F490918F04DD +:1022A00081E0992319F09D3409F080E08093E602E3 +:1022B00041E050E063E071E086EE92E00E94562E2D +:1022C00013CD833119F40E940A0E0ECD823121F50F +:1022D0000E944607209144032093680410926904E9 +:1022E0002FEF20936D0426E120936C0423EA2093C2 +:1022F0006B0420916A042770206220936A04609323 +:102300006E0470936F04809370049093710487E659 +:1023100094E00E941C0DE8CC823009F590914403B2 +:1023200090936804109269049FEF90936D048093DA +:102330006C0483EA80936B0480916A0487708062E6 +:1023400080936A048FEF91E0A3E0B2E080936E0483 +:1023500090936F04A0937004B0937104D8CF813030 +:1023600001F520E030E0232B09F4BECC9295969540 +:102370009770953059F460918F047091900480911A +:102380009104909192040E940000AECC911105C07E +:102390008FE894E00E94AC2CF6CF60E070E0CB01B7 +:1023A000F2CF8A3009F4A0CC8C3109F49DCC88E8B6 +:1023B00094E00E94930A98CC8A31C9F795CC4F3F9C +:1023C00009F092CC93E0A91350C09091F20292FFD1 +:1023D00003C0073009F488CC043109F085CC909112 +:1023E0004503891381CC0E9471064B015C01A3E077 +:1023F0009A22AA24BB240E944C062B013C01811482 +:102400009104A104B10429F58091440380936804E8 +:10241000F09269048FEF80936D0485E180936C04E2 +:1024200080916B04807E836080936B046091450390 +:10243000D7CEB1E08B1A9108A108B108E8EE4E0E94 +:10244000E3E05E1E611C711C81149104A104B104BF +:10245000B1F20E944C066419750986099709683E15 +:1024600073408105910528F7CACF909145038913E0 +:102470003BCC9DCFE9960FB6F894DEBF0FBECDBF23 +:10248000DF91CF911F910F91FF90EF90DF90CF9050 +:10249000BF90AF909F908F907F906F905F904F9084 +:1024A0003F902F9008958091F20284FD0C94570E76 +:1024B0000895CF92DF92EF92FF92CF93DF93D82FC0 +:1024C0000E9471066B017C0110928D04C0E00E9495 +:1024D00071066C197D098E099F09603D7740810561 +:1024E000910590F4C11110C0A8950E943B070E946D +:1024F000531280918C048770833049F7C1E080913A +:102500008D048D13E3CFE3CF8C2FDF91CF91FF901C +:10251000EF90DF90CF9008958F929F92AF92BF92ED +:10252000CF92DF92EF92FF924B015C010E94710605 +:102530006B017C010E9471066C197D098E099F094F +:10254000681579058A059B0530F4A8950E943B071C +:102550000E945312EFCFFF90EF90DF90CF90BF908B +:10256000AF909F908F900895CF93C0E088E494E05F +:102570000E94F306892B51F4CA3F41F0CF5F61E01E +:1025800070E080E090E00E948C12F0CF88E494E04C +:102590000E94F3061816190694F46AE070E080E0D1 +:1025A00090E00E948C1288E494E00E94F3061816D2 +:1025B000190664F488E494E00E940B07F4CFCA3F44 +:1025C00029F48DEA96E0CF910C942309CF910895D8 +:1025D000EF92FF920F931F93CF937C0188E494E0D6 +:1025E0000E94F306892B11F00E9446096EE788E4E9 +:1025F00094E00E949706C0E087010C0F111DF801BE +:10260000649188E494E00E949706F80104910F3EDB +:1026100009F4C0E1CF5FC03178F3CF911F910F91E2 +:10262000FF90EF900C94B4128091E502811104C0E8 +:1026300085ED96E00E94E8128FEF8093E502089501 +:102640008F929F92AF92BF92EF92FF920F931F9340 +:10265000CF93DF9300D0CDB7DEB788E494E00E943B +:10266000F3069A83898389819A8105970CF4D0C0F7 +:1026700088E494E00E947E068E3709F0B1C010E035 +:10268000E12C01E0FF24FA9488E494E00E94F30630 +:10269000892B09F4BDC088E494E00E940B07EE206A +:1026A00069F0E12FF0E0E352FD4F8F3E61F080834F +:1026B0001F5F183008F017E0EE24E3948E3721F7FF +:1026C000EE24E394E1CF10828091DD02863009F0A0 +:1026D00081C08091DE02873009F07CC06091DF020A +:1026E000862F90E0982F88276091E002680F792F5D +:1026F000711D60557109072E000C880B990B0E9403 +:10270000212D2DEC3CEC4CE650E40E94F32E4B01C5 +:102710005C0120E030E0A9010E941A2D70E060E029 +:1027200087FD10C020E030E040E753E4C501B4016C +:102730000E949B2F60EF70E0181624F0C501B401D1 +:102740000E94652F7093DC026093DB026091E102CE +:10275000862F90E0982F88276091E202680F792FEA +:10276000711D64567109072E000C880B990B0E948D +:10277000212D23E333E343E350E40E94F32E4B0186 +:102780005C0120E030E0A9010E941A2D70E060E0B9 +:1027900087FD10C020E030E040EA53E4C501B401F9 +:1027A0000E949B2F60E471E0181624F0C501B4016B +:1027B0000E94652F7093DA026093D9028091E5023E +:1027C00081110BC085ED96E00E94E812F092E502BF +:1027D0000E94460910E0E12C57CF0093D802F6CFB3 +:1027E00088E494E00E940B07182F8BE796E00E9484 +:1027F0007F05812F0F900F90DF91CF911F910F9147 +:10280000FF90EF90BF90AF909F908F900C94350900 +:102810000F900F90DF91CF911F910F91FF90EF904C +:10282000BF90AF909F908F900895EF92FF920F937B +:102830001F93CF93DF93CDB7DEB728970FB6F894E9 +:10284000DEBF0FBECDBF2EE7298326E02A8323E11A +:102850002B8321E02C832AE02D832FEE28879E8373 +:102860008F838E010F5F1F4F7E0189E0E80EF11C00 +:102870000E151F0541F0F80161918F0188E494E085 +:102880000E949706F5CF28960FB6F894DEBF0FBECC +:10289000CDBFDF91CF911F910F91FF90EF900C94DE +:1028A000B412EF92FF920F931F93CF93DF93CDB7A4 +:1028B000DEB728970FB6F894DEBF0FBECDBF9EE7F8 +:1028C000998396E09A8391E09B839FEE98871C8280 +:1028D0008D837E836F838E010F5F1F4F7E0189E0A2 +:1028E000E80EF11C0E151F0541F0F80161918F01F2 +:1028F00088E494E00E949706F5CF28960FB6F894E6 +:10290000DEBF0FBECDBFDF91CF911F910F91FF9022 +:10291000EF900C94B412FF920F931F93CF93DF9319 +:1029200095E1899F800111240D531B4FC8010E941E +:10293000DA2CF82E6EE788E494E00E94970662E0B5 +:102940006F0D88E494E00E94970661E188E494E0CA +:102950000E949706D0E0C0E0CF1550F4F801EC0FCC +:10296000FD1F649188E494E00E9497062196F4CFBD +:102970006FEE88E494E00E949706DF91CF911F915B +:102980000F91FF900C94B412EF92FF920F931F934C +:10299000CF93DF93CDB7DEB760970FB6F894DEBF65 +:1029A0000FBECDBF9EE799839EE09A839CE29B83F6 +:1029B0009FEE988B1C828D837E836F831886498758 +:1029C0001A8622502B871C860D87FE86EF868E0185 +:1029D0000F5F1F4F7E0181E1E80EF11C0E151F05F0 +:1029E00041F0F80161918F0188E494E00E9497061C +:1029F000F5CF60960FB6F894DEBF0FBECDBFDF9166 +:102A0000CF911F910F91FF90EF900C94B412EF9221 +:102A1000FF920F931F93CF93DF93CDB7DEB72C9721 +:102A20000FB6F894DEBF0FBECDBFFE01339626E091 +:102A3000DF011D922A95E9F72EE729832AE02A83F0 +:102A400023E22B8320EF29872FEF2A872B872FEE76 +:102A50002C879E838F838E010F5F1F4F7E01BDE009 +:102A6000EB0EF11C0E151F0541F0F80161918F016D +:102A700088E494E00E949706F5CF2C960FB6F89460 +:102A8000DEBF0FBECDBFDF91CF911F910F91FF90A1 +:102A9000EF900C94B4122F923F924F925F926F92EC +:102AA0007F928F929F92AF92BF92CF92DF92EF92DE +:102AB000FF920F931F93CF93DF9310929D018FEF9F +:102AC00091E09093250180932401C0E0D0E0C09074 +:102AD0002401D090250186010F5F1F4FC8010E947D +:102AE0006C09882E8FEF880D8E3F08F02CC19090D6 +:102AF0002601A82CB12C29E2921220C0C8018A0D0F +:102B00009B1D0E946C098F3FC9F486E594E00E94EA +:102B10002309C0912401D09125012196809124019F +:102B20009091250101968A0D9B1DC817D9070CF0BD +:102B300020C16FEFCE010E945809EFCF80919D0117 +:102B40008F5F80939D01C60102960E946C09582EEA +:102B50008071482E54FE13C0CF5F93E099124DC090 +:102B6000809127018C130BC010932D0100932C0131 +:102B7000CA0CDB1CD0922B01C0922A01D82D809167 +:102B8000240190912501A80EB91EB0922501A092B2 +:102B90002401809126018F3F09F099CFD11197CF61 +:102BA000411095CF53FA222420F8312C6090230154 +:102BB000712C062D10E004551E4F7214130411F0F1 +:102BC0000CF085CF98016501769CC018D1081124BE +:102BD000C01AD10A8CEA91E08217930709F4CBC09E +:102BE000F90142904F01C601820F931F0E946C09A8 +:102BF0009401481679F37394E0CFEDEF9E12BFCFA6 +:102C000020919C01220F220F2C1708F0B8CFCC2E58 +:102C1000D12C30E02C5F3F4F2C153D050CF4AFCF8D +:102C2000F1E0CF1AD10823E0C222DD24C6010196CB +:102C3000E5E3E89F4001E99F900C11243401F3E1A2 +:102C40006F0E711C8FEEE82E8BE7F82E01E110E07D +:102C500025E330E053FC1CC040EF50E0B40190E0AD +:102C600080E00E94C414B30183E190E00E945114FB +:102C700080919C0124E0829FC00111248C0D9D1D38 +:102C800001960E9415148EE494E00E94E81277CF1A +:102C900048E750E0B40190E080E00E94C414B30122 +:102CA00083E190E00E94511480919C01E4E08E9FAA +:102CB000C00111248C0D9D1D01960E94151486E4FF +:102CC00094E00E94E81225E330E048E750E0B401C8 +:102CD00088E790E00E94C414B3018BE890E00E9462 +:102CE000511480919C01F4E08F9FC00111248C0D40 +:102CF0009D1D01960E9415148EE394E0C6CF2D59B8 +:102D00002C1B2093EB03977098609093E8038F71CE +:102D100080628093E90381E0711080E08093EC038E +:102D200061E085EE93E00E943D0D8093A20160E991 +:102D300071E080E090E00E948C1282E00E941B17FC +:102D4000D0919D01C4CEC093A3018D2FDF91CF916F +:102D50001F910F91FF90EF90DF90CF90BF90AF90B9 +:102D60009F908F907F906F905F904F903F902F90AB +:102D70000895D1E0EACF88E394E00E9423098AE233 +:102D800094E00E947F0580919D018C1B990B8D59C9 +:102D90009F4F0E94110985E294E00E947F05872DD4 +:102DA0000E94350920919D019091E8038091E903EB +:102DB00053FCA5CF2D592C1B2093EB038F718064FE +:102DC0008093E903977090619093E80381E090E02D +:102DD0009093ED038093EC0361E085EE93E00E9415 +:102DE0003D0D8093A20160E971E080E090E00E94D7 +:102DF0008C1260ED77E080E090E00E948C12809170 +:102E00009D018D598C1B8093EB038091E9038F7199 +:102E100080648093E9038091E803877080618093E8 +:102E2000E8031092ED031092EC0361E085EE93E06D +:102E30000E943D0D7CCFEF92FF920F931F93CF9393 +:102E4000C82F0E9446090E941413CF3F09F046C0C4 +:102E500085EB94E00E94E8122FE1E22EF12C0AE1CA +:102E600010E025E330E048E750E070E060E090E0FB +:102E700080E00E94C41463E170E083E190E00E946E +:102E800051148DEA94E00E94E8128DEF80932601A0 +:102E90000E944B15C09326018091A301853050F00C +:102EA0006CE171E083E190E00E9451148AEB94E0C0 +:102EB0000E94E8128091A201882309F4C7C063E14F +:102EC00070E082ED90E00E94511485EA94E0CF9189 +:102ED0001F910F91FF90EF900C94E8128EEF8C0FE2 +:102EE000823008F058C062E370E080E090E00E9419 +:102EF0008C1290E8E92E9CEFF92E10E000E027E319 +:102F000030E040EF50E069E071E090E080E00E9446 +:102F1000C41462E370E080E090E00E948C126CE1E7 +:102F200071E083E190E00E94511462E370E080E080 +:102F300090E00E948C12C23029F58BE00E948B1425 +:102F400060ED77E080E090E00E948C12F12CE12CA3 +:102F500010E000E027E330E040EF50E069E071E08E +:102F600090E080E00E94C4148091A301853008F4B1 +:102F70006DC06CE171E083E190E00E9451148AEB36 +:102F800094E0A5CF8CE00E948B148091270190E003 +:102F90000E941514D5CF809126018F518831E0F41D +:102FA00085EB94E00E94E8128FE1E82EF12C0AE014 +:102FB00010E025E330E048E750E070E060E090E0AA +:102FC00080E00E94C41463E170E083E190E00E941D +:102FD000511480E00E948B1468E470E083E190E07B +:102FE0000E945114C13211F0CF3141F480E1CF91F0 +:102FF0001F910F91FF90EF900C948B148C2F8D7F6D +:10300000803211F481E1F3CF8AED8C0F823010F41D +:1030100082E1EDCF8DE0C43061F08EE0C53039F350 +:103020008FE0C63031F083E1C93219F0CB3071F452 +:1030300084E10E948B1468EB7BE080E090E0CF910C +:103040001F910F91FF90EF900C948C12CF911F91D4 +:103050000F91FF90EF9008958F929F92AF92BF9241 +:10306000CF92DF92EF92FF920F931F93CF93DF9354 +:10307000C82FE62E88E994E00E947F058C2F0E94DD +:10308000350983E080932601C0932701109229011E +:10309000CC2309F48EC10E944B15D82F882309F444 +:1030A00079C18091A301882309F474C18C2F90E029 +:1030B0006E2D83569E4F0E94662EC75FC0930C04F0 +:1030C00080910904877088608093090480910A04C4 +:1030D0008F71806280930A04E0920D0460E086E0C4 +:1030E00094E00E943D0D60E971E080E090E00E9474 +:1030F0008C1281E080932D046EE874E087E294E006 +:103100000E94160A60E00E943D0D83E00E941B179A +:103110001092DD021092DE021092DF021092E002A5 +:103120001092E1021092E2021092E3021092E40285 +:1031300000912C0110912D01C80101960E946C098B +:10314000F82E8170A82E8093A10181E09F2D9470AC +:10315000892EF2FE80E08093A0018CEF8D0F80938A +:103160002901C80102960E946C09C82F8093DD02D4 +:10317000C80103960E946C098093DE02F5FCAAC088 +:103180008093DF02C093E002D4E0EF2DE0749E2E26 +:10319000C0E0F6FE17C0809129018250809329017A +:1031A000C8018D0F911D0E946C098093AC0181E0D4 +:1031B0008D0F800F912F911D0E946C098093AD019E +:1031C000DE5FC2E0BF2CBB1CBB24BB1CF1E0BF1206 +:1031D0001DC080912901825080932901C8018D0F63 +:1031E000911D0E946C09EC2FF0E0E455FE4F8083A6 +:1031F00081E08D0F800F912F911D0E946C09E1E0FD +:10320000EC0FF0E0E455FE4F8083CE5FF3FE05C087 +:1032100080912901869580932901C0902A01D09040 +:103220002B012091290130E06E2D70E06F5F7F4F00 +:10323000629FC001639F900D729F900D11248601C3 +:10324000081B190B0F5F1F4F821B930BC81AD90A5B +:10325000C016D1060CF44FC0DC2FF12CE12CC801B4 +:103260000E946C09AD2FB0E0A455BE4FE82FF0E0EE +:103270000E2C02C0F595E7950A94E2F7E170FF275E +:10328000EE0FFF1FE352FD4F80818C93C8010E9417 +:103290006C09A1E0AD0FB0E0A455BE4FE82FF0E0FF +:1032A0000E2C02C0F595E7950A94E2F7E170FF272E +:1032B000EE0FFF1FE352FD4F81818C93DE5F8FEF96 +:1032C000E81AF80A98E0E916F10449F6C05F0F5FC2 +:1032D0001F4FBECFC80104960E946C098093DF0285 +:1032E000C80105960E946C098093E002D650D093E5 +:1032F0002901D6E04ACFA110C850882051F08FE7AD +:1033000094E00E9423099110C250E1E0BE1201C076 +:10331000C25085E794E00E94230948E0F42ED0E0F3 +:1033200052E3E52E0CEA11E020E0C8018C5A9140EE +:103330008C179D0714F52223D1F060E084E00E94F1 +:10334000C40520E0F80181918F018E9DC0011124F8 +:1033500090939F0180939E018230910534F3880FF2 +:10336000991F880F991F05970197F1F7DECF61E04C +:1033700084E00E94C40521E0E5CFFA94F110D2CF99 +:1033800060E084E00E94C4056CE271E080E090E0BF +:103390000E948C1281E080932D0462E774E087E242 +:1033A00094E00E94160A60E00E943D0D8FEF80932A +:1033B0002601DF91CF911F910F91FF90EF90DF9049 +:1033C000CF90BF90AF909F908F90089562E083E080 +:1033D0000E94100661E084E00E94100660E084E034 +:1033E0000E94C40560ED77E080E090E00E948C12BE +:1033F00010926304109262041092610410926004AF +:1034000010925F0410925E048DE991E09093650440 +:103410008093640490915404963108F047C02DEBDA +:1034200030E030935F0420935E042BE931E0309369 +:103430006104209360042CE231E030936304209314 +:1034400062042091680084E0983048F031E09E30BA +:1034500008F430E081E001C0880F3A95EAF7822B4A +:10346000809368002DE630E0983050F02BE630E095 +:103470009E3030F02CE630E0963110F030E020E065 +:1034800030935C0420935B04292F30E0983008F0DF +:103490006BC081E001C0880F9A95EAF780935D04C4 +:1034A00080916404909165040197F1F78091620422 +:1034B00090916304892B49F1E0913903F0913A032B +:1034C00084E0E834F80709F1309739F0A389B4892A +:1034D0009C918589809589238C93809166048E7F49 +:1034E00080936604109237031092380388E494E0C6 +:1034F00090933A0380933903E0915B04F0915C046C +:10350000808190915D04892B808360ED77E080E07D +:1035100090E00E948C1285ED96E00E94E81289EFFF +:1035200091E00E94E81284EF91E00E94E8128091FD +:10353000F20282FF26C08EED91E00E94230981E015 +:103540008093A20180932D046BED71E087E294E0FB +:103550000E94160A60E00E943D0D8FEF80932601C5 +:103560000E941B170C9475089E3040F428503109B6 +:1035700081E001C0880F2A95EAF790CF2E503109DB +:10358000F7CF8CEC91E00E9423091092A201E5CFC5 +:103590002F923F924F925F926F927F928F929F9263 +:1035A000AF92BF92CF92DF92EF92FF920F931F9351 +:1035B000CF93DF93CDB7DEB72C970FB6F894DEBF6D +:1035C0000FBECDBF8091A5018111EFC08091A601F2 +:1035D0009091A701A091A801B091A901892B8A2BF4 +:1035E0008B2B51F40E944C066093A6017093A701A7 +:1035F0008093A8019093A9014B99FECF0E944C069D +:10360000609396017093970180939801909399012C +:103610002091A6013091A701621B730B70939F014B +:1036200060939E01CB01855691098B3B9D4408F028 +:1036300080C02091AA013091AB0137FD0AC0F90189 +:10364000E455FE4F633A40E3740708F46BC089EF1A +:1036500080832F5F3F4F3093AB012093AA0180916D +:1036600028018823E1F180919E0190919F014B995F +:1036700004C0803355E79507D0F30E944C06609351 +:10368000A6017093A7018093A8019093A9012091AE +:10369000960130919701621B730B70939F016093A9 +:1036A0009E01CB01855691098B3B9D4408F05CC07F +:1036B0002091AA013091AB011216130654F4F901BE +:1036C000E455FE4F633AB0E37B0708F446C089EF48 +:1036D00080832F5F3F4F3093AB012093AA018091ED +:1036E000AA019091AB018A3291401CF081E0809355 +:1036F000A5012C960FB6F894DEBF0FBECDBFDF91AB +:10370000CF911F910F91FF90EF90DF90CF90BF90DE +:10371000AF909F908F907F906F905F904F903F9071 +:103720002F900895CB0162E370E00E94A02F608388 +:1037300090CF1092AB011092AA011092280181E063 +:103740008093A5010E944C066093A6017093A70187 +:103750008093A8019093A90182CFCB0162E370E02E +:103760000E94A02F6083B5CF60337547B0F081E031 +:103770008093A5018091AA019091AB0197FF04C0AD +:103780001092AB011092AA011092A6011092A7010B +:103790001092A8011092A901A2CF1092AB01109231 +:1037A000AA0181E08093A5019ACF1092A5010E9401 +:1037B000201300912801002309F496C58091AA01E5 +:1037C0009091AB01813691050CF48EC580912A0150 +:1037D00090912B01FC01E455FE4F2081211104C082 +:1037E000019780339105B4F790932B0180932A01C0 +:1037F00020902C0130902D0180902A0190902B0177 +:103800005401A218B308C5016AE070E00E94B42F09 +:1038100019861A8610E09B01770F440B550B2983FC +:103820003A834B835C83A5010B2C000C660B770B52 +:103830004D835E836F837887F101E455FE4FD12C71 +:10384000C12CCF018C5A9140881699063CF0819189 +:103850008113F7CF5FEFC51AD50AF3CF11110BC053 +:1038600062E0C616D1040CF452C0F984F092A401AF +:1038700080E693E029C372E0C716D1041CF0898565 +:103880008F5F8987113009F442C00D2C000CEE08BF +:10389000FF0869817A818B819C810E94212D20E023 +:1038A00030E040E05FE3A7EFAA16AFEFBA060CF4F2 +:1038B0009BC00E94872E0E94652F6C157D058E058A +:1038C0009F052CF56D817E818F8198850E94212D29 +:1038D0002DEC3CEC4CE45FE30E94802D2B013C017D +:1038E00020E030E0A9010E949B2F20E030E040E082 +:1038F0005FE387FD7CC0C301B2010E94872E0E9456 +:10390000652FC616D706E806F9060CF40A871F5F6E +:103910001F3F09F091CFF984F092A401FA84FF20AF +:1039200009F4A6CF29852250223108F0A1CF1092A8 +:10393000DD021092DE021092DF021092E00210927D +:10394000E1021092E2021092E3021092E402662475 +:103950006A944AEFA42EB12CF0E0E0E0512C7724D9 +:10396000739490E080E000E0C72CD12C482F50E009 +:103970005E834D832A2D30E02C5F3F4F241735079F +:103980000CF478C09401A40170E060E022153305C6 +:103990009CF1D901A455BE4FFC908F1122C06F5FDE +:1039A0007F4FB1E07B16E1F09230D0F06A307105C4 +:1039B000BCF47401A3E0EA1AF1084E155F0584F423 +:1039C0007A01E21AF30AB1E3EB16F1044CF06E143B +:1039D0001F0431F02CF0642E621AA82E4A0119013E +:1039E000A90121503109D2CF0E94862E64CFC30194 +:1039F000B2010E94862E83CF61157105A9F121E0E5 +:103A0000290F309771F1002361F1081750F5AD814E +:103A1000BE81A01BB109CA16DB061CF19101245519 +:103A20003E4F6E177F0774F4F9019F012C5A314005 +:103A300082169306ACF021918213F7CFDF01119724 +:103A40000C93F3CFF9019F012C5A31408216930653 +:103A50003CF021910213F7CFDF0111978C93F3CF44 +:103A6000292F082FB82E02C0292FBF018F5F922F58 +:103A7000FB017CCF951509F440C03AEFF32EAF144B +:103A800011F0953038F029E02B1520F4592E35E04F +:103A9000731267CF30922D0120922C0190922B014E +:103AA00080922A019093A40160922901A0921E01A4 +:103AB0003AEFA31267C0809126018F51893008F038 +:103AC00010C4C401821993098E3391050CF409C402 +:103AD000811542E094071CF495958795182F80E294 +:103AE000482E212F30E0842D90E003968217930713 +:103AF0000CF084C054E0450EF6CF7394C4CF2F5F12 +:103B00003F4FBD81B23008F474C090E080E09F0167 +:103B10002C5A3140821693063CF03191D6012C91FB +:103B20003213F5CF0196F3CF40970CF0DAC382E35E +:103B300092E00E947F05F801E455FE4F6F01D60127 +:103B40006D916D0170E090E080E04AE00E94A20873 +:103B500080E392E00E947F05C601801B910B8C5A86 +:103B600091408E159F055CF30E941B09409229012C +:103B700010932D0100932C010E0D1F1D10932B018E +:103B800000932A0160912901362F36953250809199 +:103B90002C0190912D0170E08C01060F171FB0E0F1 +:103BA000A0E019821D826C01E4E0CE0ED11CC81683 +:103BB000D9060CF4E3C0AC01612C512C412CF12C42 +:103BC000912CB12C812CA12C19821D8240175107F8 +:103BD0000CF084C0FA01E455FE4F20812A1145C043 +:103BE000E180E81042C0F981FF5FF9834E5F5F4FCB +:103BF000EDCF0F5F1F4F15C0BCEF4B0E60E3641598 +:103C000008F06FC3F101E455FE4F9F0181016F0180 +:103C1000E42CF12CA4014E195F09570171E0A71A99 +:103C2000B1084017510744F3C1011D8248175907D5 +:103C30000CF465CFDC01A455BE4FBC87AB87B9013E +:103C4000712CDB015D90BD01AB85BC856D90BC879F +:103C5000AB87561008C073944710F3CF6D816F5F28 +:103C60006D838A0D9B1D0196E1CFB21207C0E180E2 +:103C70009E1004C029812F5F2983B8CFA11006C0F0 +:103C80008180E981EF5FE983A22EB0CFB11006C039 +:103C90009180F981FF5FF983B22EA8CFF11018C08F +:103CA00041807D8073942980231680F0270C332473 +:103CB000331C7101EE0CFF1CE616F70664F452800B +:103CC0006380ED81EE5FED83F22E90CFF22E7D8248 +:103CD00048EE53E08BCFF22E7D8288CFA092DD029A +:103CE0008092DE02B092DF029092E002F092E10256 +:103CF0004092E2025092E3026092E402298023168D +:103D000008F4C6C020912A0130912B01A20FB31FE5 +:103D1000B0932B01A0932A0190932D0180932C0145 +:103D20000E941B090E941B0900912C0110912D017A +:103D3000980124553E4F790180912A0190912B01E1 +:103D400080179107BCF0D7016D917D0170E090E084 +:103D500080E04AE00E94A2088DE692E00E947F0582 +:103D6000C80187709927892B11F40E941B090F5FE6 +:103D70001F4FE2CF0E941B090E941B098091DD02A8 +:103D80002091DE0241E050E0821711F050E040E067 +:103D90009091DF029417150641F49091E0022917E3 +:103DA00020F49093DE028093E00299819695969597 +:103DB000969590932301898187701092A1011092AA +:103DC000A001882349F09F5F90932301843009F07C +:103DD00068C081E08093A1011D82812C912C540147 +:103DE0008394809123013D80381608F08BC019829E +:103DF000F12CE12C00912C0110912D01F801E455DA +:103E0000FE4F8081240135010E2C04C0440C551C4A +:103E1000661C771C0A94D2F79091DD02891357C073 +:103E200021819091DE02291352C081E592E00E9427 +:103E30007F05842D80952980282229820E5F1F4FBF +:103E400010932D0100932C012FEFE21AF20A38E0B3 +:103E5000E316F10479F62D80E22DF0E0E455FE4FF3 +:103E60003980308284E492E00E947F05632D70E007 +:103E700090E080E040E10E94A2088CE392E00E9482 +:103E80007F0589810E9435092D812F5F2D83A9CF60 +:103E900011960F5F1F4FA530B10509F456C001966A +:103EA00086CE2D80820D9927991F8430910531F49B +:103EB00081E08093A1018093A0018ECF089711F437 +:103EC00081E0F9CF83E592E00E9423093EC0909102 +:103ED000DF0289130DC091818091E002981308C020 +:103EE0008FE492E00E947F05398034283982A6CF82 +:103EF0008DE492E00E947F05842D80952980282200 +:103F00002982A2CF0E941B090E941B092090260132 +:103F10002982222D2F3F09F062C08BE194E00E949C +:103F200023090E944B15882381F085E194E00E94CB +:103F3000230981E080932D046DEF73E087E294E024 +:103F40000E94160A60E00E943D0D0E947508809153 +:103F50001C01882309F404C210921C011AE080910C +:103F6000A30190E009961817190611F00CF0C2C1D0 +:103F700027EF210F422F50E043565E4F5A8349836B +:103F8000CA010E944E2E823028F060E089819A8119 +:103F90000E94662E89819A810E944E2E10930C04F5 +:103FA00090910904977098609093090490910A0485 +:103FB0009F71906290930A0491E0811101C090E09A +:103FC00090930D0460E086E094E00E943D0D60E96E +:103FD00071E080E090E00E948C121F5FC0CF898169 +:103FE0008F51893008F0B1CF86EF93E00E947F05B2 +:103FF00089810E943509809126018652823030F4F1 +:104000009091230180911D0198134EC188E992E09F +:104010000E942309809126019AED980F923008F4AE +:1040200075C02090A1012D829091A001992319F0D3 +:10403000222D24602D83309123013983982F9D7F79 +:10404000903209F045C04D8148604D83330F3E5F8B +:1040500039838152823060F58D8180614091DF0229 +:104060002091E0023091DD029091DE02491334C0CC +:10407000231332C069816E5F69838D838091E10271 +:104080008823B1F08091E202882391F08D818064D1 +:1040900099819E5F2091E3022223A9F12091E402FD +:1040A000222389F1BD81B06CBD83E981EC5FE98396 +:1040B000298021102EC080EA93E00E94230986E027 +:1040C0000E941B1781E080932D046BE873E000C110 +:1040D00059815E5F5983BDCF81E090E0431711F0B5 +:1040E00090E080E02817190639F47D8172617D83A4 +:1040F00089818E5F8983C2CF9D8190639D83A981D1 +:10410000AC5FA983BBCF99838D83D2CF22E0298373 +:104110001D8280912401909125012980820D911D9D +:104120008115944040F686E892E00E942309809130 +:1041300026018652823008F451C08091240190916A +:1041400025010196909325018093240169810E94A5 +:1041500058098091240190912501019690932501A1 +:10416000809324016D810E9458092D80E22CF12C4E +:1041700024FE34C00DED12E024E030E03A832983C0 +:10418000C701098002C0959587950A94E2F780FFE0 +:104190001AC08091240190912501019690932501E8 +:1041A00080932401D8016C910E94580980912401C8 +:1041B0009091250101969093250180932401F801A7 +:1041C00061810E94580929813A812F5F3F4F3A83CC +:1041D00029830E5F1F4F2830310591F61982809197 +:1041E00023012980281618F580912401909125013A +:1041F000019690932501809324010E94110980E883 +:1042000092E00E947F05022D10E004551E4FD80158 +:104210008C910E943509F801608180912401909170 +:1042200025010E945809F981FF5FF983D8CF809159 +:1042300026018652823028F080919D018F5F809305 +:104240009D012224239420922D046FE672E087E2E0 +:1042500094E00E94160A60E00E943D0D80912601C4 +:104260008D7F803231F581EE93E00E9423098091A9 +:10427000230180931D0120922D0469EC73E087E2F5 +:1042800094E00E94160A60E00E943D0D8091260194 +:10429000823219F487E280932601809126018032D0 +:1042A00019F486E280932601809126010E941B1753 +:1042B0004CCE84E00E941B170E94520D88EB93E0C5 +:1042C0000E9423093324339430922D0465EB73E06C +:1042D00087E294E00E94160A60E00E943D0D8FEF95 +:1042E000E1CF83E793E0F0CD1092A50181E08093C8 +:1042F00028012DCE60E971E080E090E00E948C12F0 +:1043000064E6698380919D019091A301891B990BBB +:104310008D599F4F29802816190609F00CF5209217 +:10432000EB038091E9038F7180648093E9038091AE +:10433000E803877080618093E8031092ED03109288 +:10434000EC0360E085EE93E00E943D0D60E971E0D2 +:1043500080E090E00E948C1229812F5F2983D2CFC8 +:104360000E94710600919201109193012091940195 +:1043700030919501601B710B820B930B65367105B3 +:104380008105910508F4B5C90E94710660939201F8 +:1043900070939301809394019093950120909101E3 +:1043A0002982222011F181E493E00E947F05898116 +:1043B0000E943509E0919101F0E0E557FE4F8081C0 +:1043C00060E0853610F061E084560E942C18309031 +:1043D0009101232D21502983209391018AE393E0B9 +:1043E0000E947F0589810E9435098091E502823013 +:1043F00008F483C081508093E5028091D80288231D +:1044000009F477C90E9414130E9446098091D902C9 +:104410009091DA0265E370E00E94B42F6093270167 +:104420001092D8026111FFC08EEE92E00E94230923 +:1044300088EC8093260164E670E080E090E00E94C2 +:104440008C120E94460980E491E09093DA028093F6 +:10445000D90281E08093D80225E3C22ED12C3EE11F +:10446000E32E48E2B42E52E3A52E62E370E080E032 +:1044700090E00E948C120E9420138091D802882321 +:10448000A1F31092D8020E9446098091D90290911E +:10449000DA02B6010E94B42F60932701611137C080 +:1044A0008FEF8093260180912601833309F0B0C0FD +:1044B0008BE193E00E94230910929C018BE00E9403 +:1044C0001B1720E032E03A8329836FEF89819A81BC +:1044D0000E94580949815A814F5F5F4F5A8349832F +:1044E0004130544091F70E944B158FEF8093260185 +:1044F000809126010E941B17D8C0813009F07DCF22 +:104500001092E50286E393E00E94E81276CF89EEEE +:1045100092E00E94E81263E170E083E190E00E9483 +:10452000511480E00E948B1485E390E00E940715EF +:104530009091260180912701983C09F067C09091E5 +:104540009D01992309F43FC033E03D83FF24F39498 +:10455000813051F487ED92E00E942309E092260118 +:1045600054E05D8394E0F92E80912701823051F46C +:104570008DEB92E00E942309B092260161E06D83E9 +:1045800088E0F82E80912701833001F584EA92E0DB +:104590000E942309A092260182EA92E00E947F05F0 +:1045A00080E00E94350968E470E083E190E00E94B9 +:1045B000511489E00E948B148AE690E00E9407154E +:1045C0000E94460952CF41E04D83C0CF198208E4D2 +:1045D00010E082EA92E00E947F0589810E943509FD +:1045E000B80183E190E00E94511489818F0D0E94EF +:1045F0008B14C80182960E94071579817F5F7983A9 +:104600000B5C1F4F2D807211E4CFDACF890F49CF99 +:10461000893209F06DCF10929C010E944B1589E2FE +:104620000E941B1762CF653008F069C0CB01992743 +:104630009A83898300919C0194E0099F8001112451 +:104640008091A30190E0801B910B29803A80821514 +:1046500093055CF180919101863038F586E093E016 +:104660000E947F05C101800F911F0E941109809156 +:1046700091018F5F809391018091DB029091DC0228 +:104680008937910564F181E093E00E942309E0916C +:104690009101F0E0E557FE4F80919C01880F880F53 +:1046A000909127019C59890F80832C960FB6F8941E +:1046B000DEBF0FBECDBFDF91CF911F910F91FF9055 +:1046C000EF90DF90CF90BF90AF909F908F907F90B2 +:1046D0006F905F904F903F902F900C9475088BEFE8 +:1046E00092E00E942309E0919101F0E0E557FE4F2E +:1046F00080919C01880F880F90912701D4CF85EF7E +:1047000092E00E94230980919C018F5F80939C011D +:1047100024E0829FC00111242091A30128171906CB +:1047200011F00CF0E5CE10929C01E2CE1F93CF93D6 +:10473000DF938091E70281FD2DC00E940A0E8091D7 +:10474000E70281608093E702C4E0DFEF1AE1809125 +:1047500044038093680410926904D0936D0410930D +:104760006C0480916B04807E836080936B0462E0B4 +:1047700087E694E00E942F070E941C0D8BE10E94A7 +:104780005912811102C0C15011F78091E702826075 +:104790008093E702DF91CF911F9108958FEF80936F +:1047A000F402E2EFF2E0908198609083E7E6F4E0B3 +:1047B0009091440391831282868388E1858384816A +:1047C000807E8360848361E0CF010E942F070C9478 +:1047D000E40C809144038F3F21F50E9471066F3FE6 +:1047E000E9F060932F0180912F0190914403909301 +:1047F00068041092690480936D0483E080936C04D4 +:1048000080916B04807E836080936B046AE571E025 +:1048100087E694E00E944D0A0C94E40C10922F015C +:10482000E2CF0895E2EFF2E08081897F81608083AA +:10483000A4E4B3E08FEF12968C93129711968C93A9 +:104840001197E7E6F4E09C9191838283868387E069 +:1048500085838481807E836084836AE571E0CF01F3 +:104860000E944D0A0C94E40C8F929F92AF92BF92DB +:10487000CF92DF92EF92FF920F931F93CF93DF932C +:10488000CDB7DEB764970FB6F894DEBF0FBECDBFCD +:104890008E010F5F1F4F80E1F80111928A95E9F7B1 +:1048A00060E170E081E691E00E94F22CAC0161E6EB +:1048B00071E0C8010E94F02F8AE090E09093AA0472 +:1048C0008093A90440E1B8018BEA94E00E946E0451 +:1048D00050E040E080E190E0FF24F39420E1E22EFC +:1048E000BE016B5E7F4F3BE1B32ED82EFC01E95534 +:1048F000FB4F9E012F5E3F4FD901C190CD92A6176D +:10490000B707D9F7DE1020C0D988F0E1EF0EEA89A9 +:10491000F0E0EE54FF4FE491EF25E98BEB89F0E0F6 +:10492000EE54FF4FE491EA8BEC89F0E0EE54FF4F38 +:10493000E491EB8BED2DF0E0EE54FF4FE491EC8B26 +:10494000F7FECFC0FF0CFB24FC01E555FB4F4F01E8 +:10495000F901A1909F016401C81AD90AC40ED51E9D +:10496000F601D080DA24F401D1924F012A173B07D7 +:1049700079F704964C5F5F4F803B910509F0B5CF06 +:1049800080E1F80111928A95E9F7219A229A2998F3 +:104990002A9A1FB7F89480916A01811127C0E5E136 +:1049A000F8E08491E9E2F8E09491E82FF0E0EE0F6E +:1049B000FF1FE752F84FA591B491EC91E92321F440 +:1049C00061E08AE00E94C40561E08AE00E9410066E +:1049D0008CB580618CBD8CB580648CBD61E08DE050 +:1049E0000E94100661E08BE00E94100680916A012F +:1049F0008F5F80936A011FBF29986EE080E20E945A +:104A0000A0098BE496E40197F1F763E083E20E944A +:104A1000A0096FE584E20E94A0096CE485E20E948F +:104A2000A00967E086E20E94A00964E08DE30E948D +:104A3000A0098DE10E94F409843041F063E780E52C +:104A40000E94A00964E08DE30E94A00986E00E9414 +:104A5000F409873009F04FC085E00E94F4098C34D6 +:104A600009F049C062E082E20E94A00960E081E2B0 +:104A70000E94A00963E08CE30E94A0091FEF10933D +:104A8000030120E045E063E071E08BE20E946E09E3 +:104A900020E045E063E071E08AE20E946E0920E0D8 +:104AA00045E063E071E080E30E946E0982EE0E94BF +:104AB000FA0981EE0E94FA0960E787E20E94A009E4 +:104AC0008091F20280618093F202809147038E7F91 +:104AD00080934703809144038F3F29F488E191E05C +:104AE0000CC0FF0C31CF9FEF980F9E3FD8F40E946F +:104AF000DA098111F3CF88E091E00E9459076496AA +:104B00000FB6F894DEBF0FBECDBFDF91CF911F91DE +:104B10000F91FF90EF90DF90CF90BF90AF909F905C +:104B20008F90089510934403E6CF1F920F920FB613 +:104B30000F9211242F933F934F935F936F937F9323 +:104B40008F939F93AF93BF93EF93FF9388E493E08A +:104B50000E941005FF91EF91BF91AF919F918F91AE +:104B60007F916F915F914F913F912F910F900FBE69 +:104B70000F901F9018951F920F920FB60F9211244D +:104B80002F938F939F93EF93FF93E0915803F091AE +:104B900059038081E0915E03F0915F0382FD1BC0A9 +:104BA0009081809161038F5F8F73209162038217E0 +:104BB00041F0E0916103F0E0E85BFC4F958F80935A +:104BC0006103FF91EF919F918F912F910F900FBEF5 +:104BD0000F901F9018958081F4CF1F920F920FB6FF +:104BE0000F9211242F933F938F939F93AF93BF9373 +:104BF00080913C0390913D03A0913E03B0913F030F +:104C000030913B0323E0230F2D3758F50196A11D6A +:104C1000B11D20933B0380933C0390933D03A093ED +:104C20003E03B0933F038091400390914103A091D4 +:104C30004203B09143030196A11DB11D809340032F +:104C400090934103A0934203B0934303BF91AF916C +:104C50009F918F913F912F910F900FBE0F901F90BA +:104C6000189526E8230F0296A11DB11DD2CF1F92E1 +:104C70000F920FB60F9211242F933F934F935F9390 +:104C80006F937F938F939F93AF93BF93EF93FF9314 +:104C9000E0913201F09133010995FF91EF91BF91BD +:104CA000AF919F918F917F916F915F914F913F91C4 +:104CB0002F910F900FBE0F901F9018951F920F927B +:104CC0000FB60F9211242F933F934F935F936F93DF +:104CD0007F938F939F93AF93BF93EF93FF93E09155 +:104CE0003001F09131010995FF91EF91BF91AF91A2 +:104CF0009F918F917F916F915F914F913F912F91F4 +:104D00000F900FBE0F901F9018951F920F920FB625 +:104D10000F9211242F933F934F935F936F937F9341 +:104D20008F939F93AF93BF93EF93FF93E0913903DA +:104D3000F0913A03309749F0A685B7858585968D21 +:104D400091FF14C09C918923A1F4FF91EF91BF9131 +:104D5000AF919F918F917F916F915F914F913F9113 +:104D60002F910F900FBE0F901F9018959C91892343 +:104D700061F7A389B4899C918589809589238C9357 +:104D8000868997890197F1F7608D718DA685B78522 +:104D9000558538E020E0CB010197F1F7822F90E0B4 +:104DA00095958795282F4C91452309F0206831501F +:104DB00091F7868D81FD20958091370390E00196D3 +:104DC0008F73992730913803381799F0A0913703E2 +:104DD000B0E0A950BD4F2C9380933703828D938D03 +:104DE0000197F1F7A389B4898C919589892B8C93CC +:104DF000ACCF868D8160868FF1CF1F920F920FB658 +:104E00000F9211240F900FBE0F901F901895DB0189 +:104E1000FC0158E0149744E018960E9001924A95D0 +:104E2000E1F75A95C1F708958F929F92AF92BF9282 +:104E3000CF92DF920F931F93EDB7FEB7B8970FB6DF +:104E4000F894EDBF0FBEFEBF9F938F9331968F01F5 +:104E500049015A016B01C801AAD2BB2011F4AA2052 +:104E600059F0C801B601A1D030E4C30ED11C32E024 +:104E7000931AA108B108F1CFC801B601A40115D059 +:104E80008F919F91B801C3DFEDB7FEB7B8960FB60B +:104E9000F894EDBF0FBEFEBF1F910F91DF90CF9032 +:104EA000BF90AF909F908F900895523090F09F9355 +:104EB0008F937F936F935F934F9377D04F915F91D1 +:104EC0006F917F918F919F91525030E4630F711DCC +:104ED000ECCFEDB7FEB70FB6E054F040F894EDBF5D +:104EE0000FBEFEBF3196242F26952695269550FBA2 +:104EF00025F9DB01222329F0122E0D9001921A943C +:104F0000E1F730E8032E37E0342329F006943A9590 +:104F1000E9F73C91032A01922395293308F421C033 +:104F200030E4321B19F011923A95E9F7FF97319767 +:104F3000BF01FF93EF939F938F935F934F9335D070 +:104F40004F915F918F919F91EF91FF91DC0191962D +:104F50003C9132503D9326E03C9130403D932A9560 +:104F6000D9F738E3321B21F0112411923A95E9F771 +:104F7000DC01909638960D90040E02920D90051E5D +:104F8000029236E00D90011C02923A95D9F7F897FB +:104F9000BF010BD0EDB7FEB70FB6FF963196F89470 +:104FA000EDBF0FBEFEBF112408954F925F926F9226 +:104FB0007F928F929F92AF92BF92CF92DF92EF92A9 +:104FC000FF920F931F93CF93DF934DB75EB79A0174 +:104FD000FB0140525140DA010FB6F8944DBF0FBEAD +:104FE0005EBF2F933F938F939F93119640E17191F2 +:104FF0006191319121912D933D936D937D934A95CC +:10500000B1F740E3142EFD01FF9731970191119103 +:1050100021913191C190D190E190F1906D2D7E2D33 +:105020008F2D9C2D41E0E9D14B015C01B701C601F8 +:1050300042E0F9D186269726A826B92673E0F69491 +:10504000E794D794C7947A95D1F78C249D24AE2405 +:10505000BF24080D191D2A1D3B1D648D758D868D7D +:10506000978D060F171F281F391FC0A8D1A8E2A8C7 +:10507000F3A8B701C60141E0D6D15C014B0142E083 +:10508000D2D186269726A826B92672E0F694E79410 +:10509000D7947A95D9F78D249E24AF24080D191D35 +:1050A0002A1D3B1D0D931D932D933D931A9409F0DA +:1050B000AACFFF91EF91EF93FF9390E211901D9291 +:1050C0009A95E1F79097ED01EEEAF2E5BA9590E452 +:1050D000192E4C885D886E887F88688979898A89CD +:1050E0009B89C88CD98CEA8CFB8C4622572268227B +:1050F00079226095709580959095C622D722E822F6 +:10510000F9224C245D246E247F24F888C988DA882B +:10511000EB88B601C70142E070D14B015C01B601DA +:10512000C70143E080D186269726A826B926B70175 +:10513000C60141E078D186269726A826B926480CD4 +:10514000591C6A1C7B1C8C8C9D8CAE8CBF8C480CB3 +:10515000591C6A1C7B1C8D909D90AD90BD90480C95 +:10516000591C6A1C7B1C85909590A590B590480CA5 +:10517000591C6A1C7B1C688179818A819B818C8087 +:105180009D80AE80BF8086229722A822B922C88443 +:10519000D984EA84FB846C217D218E219F2186267F +:1051A0009726A826B9266C817D818E819F816C21EE +:1051B0007D218E219F2186269726A826B926C88084 +:1051C000D980EA80FB80B601C70142E02CD18B0177 +:1051D0009C01B701C60143E010D1062717272827F5 +:1051E00039276F2D7C2D8D2D9E2D42E006D106276F +:1051F000172728273927080D191D2A1D3B1D5CE19B +:105200006C969A919C835A95E1F7C888D988EA8868 +:10521000FB88C40CD51CE61CF71CC88AD98AEA8A0C +:10522000FB8A040D151D261D371D088319832A834B +:105230003B831A9409F04DCFFF91EF9158E0008124 +:105240001181228133816991799189919991060F18 +:10525000171F281F391F01931193219331935A95DA +:1052600071F7319652E066E04081450F4193552732 +:1052700028F44081451F41936A95D1F75F914F9182 +:105280000FB6F8944DBF0FBE5EBF1124DF91CF91D2 +:105290001F910F91FF90EF90DF90CF90BF90AF9054 +:1052A0009F908F907F906F905F904F900895982F70 +:1052B0008A4291443771CFFBC0B5A5DBB5E95BC22B +:1052C0005639F111F159A4823F92D55E1CAB98AAD0 +:1052D00007D8015B8312BE853124C37D0C55745DF4 +:1052E000BE72FEB1DE80A706DC9B74F19BC1C16972 +:1052F0009BE48647BEEFC69DC10FCCA10C246F2C4A +:10530000E92DAA84744ADCA9B05CDA88F976525196 +:105310003E986DC631A8C82703B0C77F59BFF30BAD +:10532000E0C64791A7D55163CA0667292914850AA3 +:10533000B72738211B2EFC6D2C4D130D3853547399 +:105340000A65BB0A6A762EC9C281852C7292A1E8D1 +:10535000BFA24B661AA8708B4BC2A3516CC719E849 +:1053600092D1240699D685350EF470A06A1016C124 +:10537000A419086C371E4C774827B5BCB034B30C61 +:105380001C394AAAD84E4FCA9C5BF36F2E68EE8236 +:105390008F746F63A5781478C8840802C78CFAFFED +:1053A000BE90EB6C50A4F7A3F9BEF27871C6DC0195 +:1053B000E0ECF3E568E275917D936A95E1F7089575 +:1053C00067E6096A85AE67BB72F36E3C3AF54FA596 +:1053D0007F520E518C68059BABD9831F19CDE05BC2 +:1053E0000000000000000000483038F0592F982FCE +:1053F000872F762F652F4850F7CF55278894442361 +:1054000039F0661F771F881F991F551F4A95C9F7E6 +:10541000652B0895483038F0562F672F782F892F45 +:10542000952F4850F7CF55278894442339F0979506 +:1054300087957795679557954A95C9F7952B089560 +:10544000AB01692F782F852F942F0895CF93DF9389 +:10545000FC01108211821282148215821682DC01F4 +:1054600017968AE1ED0119928A95E9F782E0838324 +:10547000DF91CF910895CF92DF92EF92FF921F9329 +:10548000CF93DF93CDB7DEB728970FB6F894DEBF82 +:105490000FBECDBF10924B0310924A0398EEC92E57 +:1054A00093E0D92EE12CF12CC0924C03D0924D0305 +:1054B000E0924E03F0924F0388E391E0909349030A +:1054C0008093480385EC90E09093550380935403B8 +:1054D00084EC90E0909357038093560380EC90E027 +:1054E000909359038093580381EC90E090935B0371 +:1054F00080935A0382EC90E090935D0380935C0369 +:1055000086EC90E090935F0380935E0310926103BA +:1055100010926203109263031092640388E09E016C +:105520002F5F3F4FD901E82F1D92EA95E9F711E06F +:1055300010939B0510929C0510929D0510929E055C +:1055400010929F051092A0051092A1051092A2053D +:105550001092AA041092A9041092B4051092B305F7 +:105560001092B6051092B505EBEAF4E090EFDF017A +:105570001D929A95E9F71887F901A3EAB5E0982FEB +:1055800001900D929A95E1F7F901ABEAB5E0982FF9 +:1055900001900D929A95E1F71093B70592E09093E0 +:1055A000B80593E09093B90594E09093BA0595E01F +:1055B0009093BB0596E09093BC0597E09093BD0552 +:1055C0008093BE0589E08093BF058AE08093C00583 +:1055D0008BE08093C1058CE08093C2058DE08093C1 +:1055E000C3058EE08093C4058FE08093C50588E8ED +:1055F00094E00E94262A87E694E00E94262A1092D0 +:105600004B0410924A04C0924C04D0924D04E09294 +:105610004E04F0924F048AE491E090934904809301 +:10562000480410925F0410925E041092610410927C +:105630006004109263041092620410926504109248 +:105640006404809166048E7F8D7F8093660481FB65 +:10565000662760F9612786E00E94C40561E086E064 +:105660000E941006E5E2F8E0E491E0935804E1E1DD +:10567000F8E0E491F0E0EE0FFF1FEF5FF74F859148 +:10568000949190935A048093590460E085E00E94BD +:1056900010068091660481FD04C061E085E00E94EF +:1056A000C40585E080935404E4E2F8E0E491E093DB +:1056B0005504E0E1F8E0E491F0E0EE0FFF1FE9505F +:1056C000F84F85919491909357048093560487E204 +:1056D00094E00E94262A81E080932D048FE280933B +:1056E0002C0486E094E00E94262A83E080930C0438 +:1056F00082E080930B0485EE93E00E94262A8AE0E4 +:105700008093EB0380E18093EA0328960FB6F89428 +:10571000DEBF0FBECDBFDF91CF911F91FF90EF9005 +:10572000DF90CF900895CF93DF93CDB7DEB7CA5502 +:10573000D1090FB6F894DEBF0FBECDBF789484B503 +:10574000826084BD84B5816084BD85B5826085BD7D +:1057500085B5816085BD80916E00816080936E000B +:1057600010928100809181008260809381008091FD +:1057700081008160809381008091800081608093AE +:1057800080008091B10084608093B1008091B0006E +:1057900081608093B00080917A00846080937A0069 +:1057A00080917A00826080937A0080917A00816093 +:1057B00080937A0080917A00806880937A001092BA +:1057C000C100A89580E090E0892B11F00E940000B4 +:1057D0000E9446048091E7028D7F8093E7020E9439 +:1057E0002F0940E250E063E671E0CE0101960E948D +:1057F0003E2E40E150E06CE871E0CE0181960E94BF +:105800003E2E49E050E063E871E0CE01C1960E946F +:105810003E2E41E050E06AE171E0CE01865A9F4F92 +:105820000E943E2E28EC31E040E050E0BE016F5F68 +:105830007F4FCE01CA960E94142741E050E063E0FA +:1058400071E086EE92E00E943E2E6FEF81E090E0E4 +:105850000E94662E8091F3028F788093F30210925B +:10586000E9021092E8028FE191E00E94590786E96F +:1058700093E290939B0180939A010E9471066B01C1 +:105880007C010E9471066C197D098E099F096031A7 +:10589000774281059105C8F00E94E619A89500E0BD +:1058A00010E0A8950E943B078091F20284FD0E94BF +:1058B000570E0E94C81A0115110599F30E9406059A +:1058C000882379F30E940000ECCF0E943B070E94DE +:1058D00053128091F20282FB882780F9A8958111EA +:1058E000DBCFCFCF8AE491E09093490480934804C2 +:1058F0008091390390913A038854944071F4E09177 +:105900005B04F0915C04908180915D048095892313 +:10591000808310923A03109239030895FC0188277E +:105920009927E89421912032E9F3293010F02E30A4 +:10593000C8F32B3241F02D3239F4689404C00E9430 +:10594000122D820F911D219120532A30C0F31EF495 +:10595000909581959F4F08951F93FC019927882763 +:10596000BC01E89411911032E9F3193010F01E30A7 +:10597000C8F31B3251F01D3249F4689406C00E94EE +:10598000FD2C610F711D811D911D119110531A3055 +:10599000B0F33EF490958095709561957F4F8F4F51 +:1059A0009F4F1F910895FB01DC0105900D9200208F +:1059B000E1F70895FC0105900020E9F780959095A6 +:1059C0008E0F9F1F0895FB01DC014150504048F0AD +:1059D00005900D920020C9F701C01D924150504022 +:1059E000E0F70895FC016150704001900110D8F774 +:1059F000809590958E0F9F1F0895592F482F372F10 +:105A0000262F660F771F881F991F660F771F881F25 +:105A1000991F620F731F841F951F660F771F881FC2 +:105A2000991F08957AE0979F902D879F802D910D63 +:105A3000112408950E945C2D08F481E00895E894F3 +:105A400009C097FB3EF490958095709561957F4FC6 +:105A50008F4F9F4F9923A9F0F92F96E9BB27939574 +:105A6000F695879577956795B795F111F8CFFAF484 +:105A7000BB0F11F460FF1BC06F5F7F4F8F4F9F4FB5 +:105A800016C0882311F096E911C0772321F09EE813 +:105A9000872F762F05C0662371F096E8862F70E079 +:105AA00060E02AF09A95660F771F881FDAF7880F53 +:105AB0009695879597F90895990F0008550FAA0BA9 +:105AC000E0E8FEEF16161706E807F907C0F0121611 +:105AD0001306E407F50798F0621B730B840B950B14 +:105AE00039F40A2661F0232B242B252B21F4089569 +:105AF0000A2609F4A140A6958FEF811D811D089506 +:105B00000E94932D0C94042E0E94F62D38F00E94D2 +:105B1000FD2D20F0952311F00C94ED2D0C94F32D18 +:105B200011240C94382E0E94152E70F3959FC1F30A +:105B3000950F50E0551F629FF001729FBB27F00D3B +:105B4000B11D639FAA27F00DB11DAA1F649F662790 +:105B5000B00DA11D661F829F2227B00DA11D621FDF +:105B6000739FB00DA11D621F839FA00D611D221F99 +:105B7000749F3327A00D611D231F849F600D211D7D +:105B8000822F762F6A2F11249F5750409AF0F1F000 +:105B900088234AF0EE0FFF1FBB1F661F771F881F69 +:105BA00091505040A9F79E3F510580F00C94ED2D87 +:105BB0000C94382E5F3FE4F3983ED4F386957795A6 +:105BC0006795B795F795E7959F5FC1F7FE2B880F0F +:105BD000911D9695879597F9089597F99F6780E8A5 +:105BE00070E060E008959FEF80EC089500240A942F +:105BF0001616170618060906089500240A941216A8 +:105C00001306140605060895092E0394000C11F4DA +:105C1000882352F0BB0F40F4BF2B11F460FF04C087 +:105C20006F5F7F4F8F4F9F4F089557FD9058440FE0 +:105C3000551F59F05F3F71F04795880F97FB991FEB +:105C400061F09F3F79F087950895121613061406A8 +:105C5000551FF2CF4695F1DF08C016161706180635 +:105C6000991FF1CF86957105610508940895E89410 +:105C7000BB2766277727CB0197F90895DC01CB0175 +:105C8000FC01F999FECF06C0F2BDE1BDF89A31964C +:105C900000B40D9241505040B8F70895F999FECFE5 +:105CA00092BD81BDF89A992780B50895DC01A40FB3 +:105CB000B51F4150504048F0CB01840F951F2E91E5 +:105CC0000E94672E41505040D0F70895262FF99931 +:105CD000FECF92BD81BDF89A019700B4021639F04B +:105CE0001FBA20BD0FB6F894FA9AF99A0FBE08951C +:105CF000262FF999FECF1FBA92BD81BD20BD0FB6E8 +:105D0000F894FA9AF99A0FBE019608955058BB2755 +:105D1000AA270E949E2E0C94042E0E94F62D38F085 +:105D20000E94FD2D20F039F49F3F19F426F40C94C5 +:105D3000F32D0EF4E095E7FB0C94ED2DE92F0E9476 +:105D4000152E58F3BA17620773078407950720F0DA +:105D500079F4A6F50C94372E0EF4E0950B2EBA2F9D +:105D6000A02D0B01B90190010C01CA01A001112461 +:105D7000FF27591B99F0593F50F4503E68F11A160D +:105D8000F040A22F232F342F4427585FF3CF46959E +:105D900037952795A795F0405395C9F77EF41F16C0 +:105DA000BA0B620B730B840BBAF09150A1F0FF0F8A +:105DB000BB1F661F771F881FC2F70EC0BA0F621F76 +:105DC000731F841F48F4879577956795B795F79566 +:105DD0009E3F08F0B0CF9395880F08F09927EE0FFB +:105DE0009795879508950E94072F0C94042E0E9482 +:105DF000FD2D58F00E94F62D40F029F45F3F29F068 +:105E00000C94ED2D51110C94382E0C94F32D0E940E +:105E1000152E68F39923B1F3552391F3951B550B78 +:105E2000BB27AA2762177307840738F09F5F5F4F6D +:105E3000220F331F441FAA1FA9F335D00E2E3AF0AC +:105E4000E0E832D091505040E695001CCAF72BD0C4 +:105E5000FE2F29D0660F771F881FBB1F2617370715 +:105E60004807AB07B0E809F0BB0B802DBF01FF2747 +:105E700093585F4F3AF09E3F510578F00C94ED2D0A +:105E80000C94382E5F3FE4F3983ED4F386957795D3 +:105E90006795B795F7959F5FC9F7880F911D969500 +:105EA000879597F90895E1E0660F771F881FBB1F5C +:105EB000621773078407BA0720F0621B730B840B09 +:105EC000BA0BEE1F88F7E09508950E946C2F689436 +:105ED000B1110C94382E08950E941D2E88F09F5702 +:105EE00098F0B92F9927B751B0F0E1F0660F771FFE +:105EF000881F991F1AF0BA95C9F714C0B13091F0F4 +:105F00000E94372EB1E008950C94372E672F782F1A +:105F10008827B85F39F0B93FCCF3869577956795B8 +:105F2000B395D9F73EF490958095709561957F4F24 +:105F30008F4F9F4F08950E945C2D08F48FEF0895B6 +:105F4000AA1BBB1B51E107C0AA1FBB1FA617B7079F +:105F500010F0A61BB70B881F991F5A95A9F78095BB +:105F60009095BC01CD01089597FB072E16F400947F +:105F700007D077FD09D00E94A02F07FC05D03EF482 +:105F8000909581959F4F0895709561957F4F0895E5 +:105F9000A1E21A2EAA1BBB1BFD010DC0AA1FBB1F2D +:105FA000EE1FFF1FA217B307E407F50720F0A21B9F +:105FB000B30BE40BF50B661F771F881F991F1A940C +:105FC00069F760957095809590959B01AC01BD0136 +:105FD000CF010895EE0FFF1F0590F491E02D099475 +:105FE000FB01DC0102C001900D9241505040D8F7F6 +:105FF0000895FB01DC014150504048F001900D92A2 +:106000000020C9F701C01D9241505040E0F70895AB +:1060100014E0CBE1D4E004C0FE010E94EA2F2196F7 +:0A602000CC31D107C9F7F894FFCF87 +:10602A00FFFFFF00FCE1A8A8B1098507E923F007F3 +:10603A00CE23C107AF07A50712240A080103FA00F5 +:10604A000A3424030002FE6401FF2B010100FFFF52 +:10605A007D067D060000000032059C04C9049A05ED +:10606A00FA04D804EC040000000097069C042C07EC +:10607A009606F3060B077E060D0A00322E332E31E2 +:0A608A000079533168596C68360044 +:00000001FF diff --git a/code/Signal-hub/Signal-hub.ino b/code/Signal-hub/Signal-hub.ino new file mode 100644 index 0000000..a46f7aa --- /dev/null +++ b/code/Signal-hub/Signal-hub.ino @@ -0,0 +1,2830 @@ +/* + * + * Signal Hub + * + * This device can copy signals from wireless remote controls that use the 433 frequency, and then rebroadcast them. It can optionally also copy Infra red (IR) signals. + * + * It can do this in three ways: + * - Copy and replay ON and OFF signals. For example, from cheap wireless power switches. It basically copies remote controls. + * - Copy and then replay a single signal. For example, to emulate a window sensor. + * - Recognise signals without replaying them. For example, After learning the signal once, it can detect when a window sensor is triggered again. Or when a button on a remote control is pressed. + * + * This allows you to: + * - Create a smart home security solution using cheap window and movement sensors. + * - Automatically turn on lights and other devices when you get home, or when the sun goes down etc, using wireless power sockets. + * - Control automations using wireless buttons or remote controls. + * + * An Arduino Nano can store 50 "recognise only" signals, or about 20 on/off signals. You can store any combination of these. If you need to store more signals you could look into using an Arduino Mega. + * + * Are there any limits? + * - This does not work on things like garage door openers or keyless entry systems for cars. + * These devices have a very basic protection: the code changes everytime you use it, so replaying signals will not open the door again. + * + * Security? + * - Many cheap 433Mhz devices do not use encryption. This allows us to copy the signal in the first place. + * This also means that your neighbour can in theory do the same thing you can: copy and replay signals picked up through the walls. + * + * + * + * SETTINGS */ + +#define HAS_TOUCH_SCREEN // Have you connected a touch screen? Connecting a touch screen is recommended. + +//#define VERTICALLY_FLIP_TOUCH_SCREEN // Vertical flip. Select this if you would like to vertically flip the touch screen. + +//#define RF_NANO // RF-Nano. Check this box if you are using the RF-Nano Arduino, which has a built in radio. The Candle project uses the RF-Nano. + + /* END OF SETTINGS + * + * + * ABOUT THE CODE + * + * The code has a number of states it can be in. + * LISTENING MODE. Here The main loop continuously listens for signals. If it detects it calls three successive funtions: + * 1. Check if signal is a signal (SignalViabilityCheck function) + * 2. Clean up the signal (signalCleaner function) + * 3. Analyse the signal to find the binary code it represents (signalAnalysis function). + * + * If a valid binary code is found, the next action depends on which 'state' the system is in. + * - If in LISTENING_SIMPLE state, then the signal is compared to all the stored signals. It lets you know if there is a match. + * - If in LISTENING_ON state, then the signal is compared to all the stored signals. It lets you know if there is a match. + * - If in COPYING_SIMPLE state, the code is stored as a 'simple' signal. This can then be replayed later. + * - If in COPYING_ON state, the code is stored, after which the system asks for the OFF code (COPYING_OFF state), and then stores it with the same data. + * - If in LEARNING_SIMPLE state, only the binary code is stored, and not the meta-data required to fully recreate the signal. + * + * The final states the system can be in are: + * - IN_MENU. This is when the system is displaying a menu on the screen. + * - REPLAYING. This is the state while a signal is being replayed. + * + * Depending on the current state the various functions can work in slightly different ways. + * take for example the scanEeprom function: + * - When in LISTENING state it compares the latest found signal to existing signals stored in the EEPROM memory. + * - When in REPLAYING state it returns data required to rebuild the original signal. + * - If called with a 0, then it does not try to recognise or rebuild anything. This is used during setup, when we only need to know how many signals are stored. + * + * __SIGNAL ANALYSIS DETAILS__ + * When it detects a signal, the code tries to find the part of the signal that repeats. + * In normal operation the signalCleaner function cleans up the signal and simultaneously tries to find the 'betweenSpace' variable. + * Often, 433Mhz signals have a repeating binary code that is interrupted by a short burst of different signals (called the 'anomaly' in this code). + * If no anomaly can be detected, then the repeating part is probably 'back to back', without a separator signal. + * In this case there is a 'backup' function, the 'pattern finder'. This uses brute force to find the signal. + * If both methods fail, then the signal cannot be copied. + * + * + * + * TODO + * - Check if signal is already stored before storing it. Then again, there can be good reasons to store a signal twice. Perhaps only check for doubles with recognise-only signals? + * - Another bit could be used to store if an on/off signal should also be recognisable. That way the remote could be used twice somehow. + * - Allow multiple quick succession touch screen events to add play commands to the playlist. + */ + + +//#define DEBUG // Do you want to see extra debugging information in the serial output? +//#define DEBUG_SCREEN // Do you want to see extra debugging information about the touch screen in the serial output? +//#define MY_DEBUG // Enable MySensors debug output to the serial monitor, so you can check if the radio is working ok. + +// Receiver and transmitter pins +#define RECEIVER 3 // The pin where the receiver is connected. +#define TRANSMITTER 4 // The pin where the transmitter is connected. + +#define TOUCH_SCREEN_RX_PIN 5 // The receive (RX) pin for the touchscreen. This connects to the transmit (TX) pin of the touchscreen. +#define TOUCH_SCREEN_TX_PIN 6 // The receive (TX) pin for the touchscreen. This connects to the transmit (RX) pin of the touchscreen. + +#ifdef RF_NANO +// If you are using an RF-Nano, you have to switch CE and CS pins. +#define MY_RF24_CS_PIN 9 // Used by the MySensors library. +#define MY_RF24_CE_PIN 10 // Used by the MySensors library. +#endif + + +// This code has an extra pattern finding trick. Using brute force it will try to find a pattern in the data. The downside is it takes a lot of time to analyse signals this way. +// This means the system might not detect a signal because it is busy analysing a bad signal. It's up to you if you want to use it. +//#define PATTERN_FINDER + +// Enable and select the attached radio type +#define MY_RADIO_RF24 // This is a common and simple radio used with MySensors. Downside is that it uses the same frequency space as WiFi. +//#define MY_RADIO_NRF5_ESB // This is a new type of device that is arduino and radio all in one. Currently not suitable for beginners yet. +//#define MY_RADIO_RFM69 // This is an open source radio on the 433mhz frequency. Great range and built-in encryption, but more expensive and little more difficult to connect. +//#define MY_RADIO_RFM95 // This is a LoRaWan radio, which can have a range of 10km. + +// MySensors: Choose your desired radio power level. High power can cause issues on cheap Chinese NRF24 radio's. +//#define MY_RF24_PA_LEVEL RF24_PA_MIN +//#define MY_RF24_PA_LEVEL RF24_PA_LOW +//#define MY_RF24_PA_LEVEL RF24_PA_HIGH +#define MY_RF24_PA_LEVEL RF24_PA_MAX + +// Mysensors advanced security +#define MY_ENCRYPTION_SIMPLE_PASSWD "yS1hYlh6" // If you are using the Candle Manager, the password will be changed to what you chose in the interface automatically. Be aware, the length of the password has an effect on memory use. +//#define MY_SIGNING_SOFT_RANDOMSEED_PIN A7 // Setting a pin to pickup random electromagnetic noise helps make encryption more secure. + +// Mysensors advanced settings +#define MY_TRANSPORT_WAIT_READY_MS 10000 // Try connecting for 10 seconds. Otherwise just continue. +//#define MY_RF24_CHANNEL 100 // In EU the default channel 76 overlaps with wifi, so you could try using channel 100. But you will have to set this up on every device, and also on the controller. +#define MY_RF24_DATARATE RF24_1MBPS // Slower datarate makes the network more stable? +//#define MY_NODE_ID 10 // Giving a node a manual ID can in rare cases fix connection issues. +//#define MY_PARENT_NODE_ID 0 // Fixating the ID of the gatewaynode can in rare cases fix connection issues. +//#define MY_PARENT_NODE_IS_STATIC // Used together with setting the parent node ID. Daking the controller ID static can in rare cases fix connection issues. +#define MY_SPLASH_SCREEN_DISABLED // Saves a little memory. +//#define MY_DISABLE_RAM_ROUTING_TABLE_FEATURE // Saves a little memory. + + + +// REQUIRED LIBRARIES + +#include // The library that helps form the wireless network. +#include // Allows for storing data on the Arduino itself, like a mini hard-drive. + + +//#define HAS_BASIC_OLED_SCREEN // Have you connected a simple OLED screen? Connecting a screen is recommend. + +// Basic OLED screen +#ifdef HAS_BASIC_OLED_SCREEN +#define INCLUDE_SCROLLING 0 +#define OLED_I2C_ADDRESS 0x3C +#include // Driver for the simple OLED screen. +#include // "SSD1306Ascii" +SSD1306AsciiAvrI2c oled; +#endif + + +// Touch screen +#ifdef HAS_TOUCH_SCREEN + +#include +SoftwareSerial touch_screen_serial(TOUCH_SCREEN_RX_PIN,TOUCH_SCREEN_TX_PIN); // RX (receive) pin, TX (transmit) pin + +#define MAX_BASIC_COMMAND_LENGTH 16 // How many bytes are in the longest basic command? +#define TOUCHSCREEN_WIDTH 240 +#define TOUCHSCREEN_HEIGHT 320 +#define BUTTON_HEIGHT 53 // How many pixels tall are the touch screen buttons? +#define BUTTON_PADDING (BUTTON_HEIGHT/2) - 7 // The font is 14 pixels high, so this calculation places it in the middle of the buttons. + +const byte howManyReplayButtonsWillFitOnScreen = (byte)floor( (TOUCHSCREEN_HEIGHT / BUTTON_HEIGHT) - 2 ); // Minus one for the menu button, and one for the 'more' button. + +boolean touched = false; // Was the touchscreen just touched? +byte visibleReplayButtonsPage = 0; // The user can scroll through all the replay buttons. + +signed int touchX = 0; // Touch screen position X +signed int touchY = 0; // Touch screen position Y + +// Basic commands for the touch screen. All commands for the TFT should start with 0x7E, but to save storage space this is taken care of in the basicCommand function. +PROGMEM const byte play[] = {0x07, 0x11, ' ', 'P', 'L', 'A', 'Y', 0xEF,}; // Places the word ' play' on the screen. +PROGMEM const byte on[] = {0x07, 0x11, ' ', 'O','N', ' ', ' ', 0xEF,}; // Places the word ' on ' on the screen. +PROGMEM const byte off[] = {0x07, 0x11, ' ', 'O', 'F', 'F', ' ', 0xEF,}; // Places the word ' off ' on the screen. +PROGMEM const byte w[] = {0x07, 0x11, 'w', 0x00, ' ', 0x00, 0x00, 0xEF,}; // Places the word 'w ' on the screen. +PROGMEM const byte menu[] = {0x07, 0x11, 'M', 'E', 'N', 'U', ' ', 0xEF,}; // Places the word 'menu ' on the screen. +PROGMEM const byte more[] = {0x07, 0x11, 'M', 'O', 'R', 'E', '>', 0xEF,}; // Places the word 'menu ' on the screen. + +#ifdef VERTICALLY_FLIP_TOUCH_SCREEN +PROGMEM const byte set_vertical[] = {0x03, 0x04, 0x00, 0xEF,}; // To set rotation of the screen to vertical. Instead of the 0x02 you can try 0x00 (vertical B), 0x01 (horizontal A) or 0x03 (horizontal B). +#else +PROGMEM const byte set_vertical[] = {0x03, 0x04, 0x02, 0xEF,}; // To set rotation of the screen to vertical. Instead of the 0x02 you can try 0x00 (vertical B), 0x01 (horizontal A) or 0x03 (horizontal B). +#endif + +PROGMEM const byte fill_black[] = {0x04, 0x20, 0x00, 0x00, 0xEF,}; // Fill screen with one color +PROGMEM const byte fill_blue[] = {0x04, 0x20, 0x00, 0xFF, 0xEF,}; // Fill screen with one color + +PROGMEM const byte text_color_white[] = {0x04, 0x02, 0xFF, 0xFF, 0xEF,}; // white text color. fill screen with one color +//PROGMEM const byte text_color_black[] = {0x04, 0x02, 0x00, 0x00, 0xEF,}; // Dark text color. fill screen with one color +//PROGMEM const byte text_color_red[] = {0x04, 0x02, 0xF8, 0x00, 0xEF,}; // .. text color. fill screen with one color + +//PROGMEM const byte resetTFT[] = {0x02, 0x05, 0xEF,}; // Resets the TFT. But has no real effect. +#ifdef DEBUG_SCREEN +PROGMEM const byte testTFT[] = {0x02, 0x00, 0xEF,}; // Test the TFT, should respond with "OK". +#endif +PROGMEM const byte backlight_on[] = {0x03, 0x06, 0xFF, 0xEF,}; // Backlight intensity to half-full +PROGMEM const byte backlight_off[] = {0x03, 0x06, 0x00, 0xEF,}; // Backlight intensity to zero +//PROGMEM const byte serialSpeedUp[] = {0x03, 0x40, 0x03, 0xEF,}; // Sets communication speed to 57600 (from 9600) +//PROGMEM const byte serialSlowDown[] = {0x03, 0x40, 0x00, 0xEF,}; // Sets communication speed to 9600 again. Oddly enough, it seems it works fastest at this speed.. + +PROGMEM const byte test[] = {0x02, 0x00, 0xEF,}; // Test message + +#endif + + +PROGMEM const char detectedMessage[] = { "Detected " }; // This construction saves some memory. +PROGMEM const char replayMessage[] = { "Replay " }; // This construction saves some memory. + +/* +The colors use this RGB565 format: +http://www.barth-dev.de/online/rgb565-color-picker/ + +white: FF FF = 65535; +red: F8 00 = 63488 +purple: F0 1F = 61471 +blue: 00 1F = 31 +light blue: 07 FF = 2047 +bright green: 07 E0 = 2016 +yellow: FF 20 = 65312 +orange: FC 80 = 64640 +half grey: 7BEF = 31727 +*/ + + + + +// Keypad +#define KEYPAD_PIN A0 // The pin where the analog keypad is connected. These keypads vary their resistance according to which button is pressed. +byte buttonPressed = 100; // The last button that was pressed by the user. +byte prevButtonState = 100; +#define KEYPAD_BUTTON_COUNT 0 // How many buttons does your keypad have? Leave at 0 if you are using a touch screen. + +#if KEYPAD_BUTTON_COUNT > 0 +boolean buttonsToggleStatus[KEYPAD_BUTTON_COUNT + 1]; // Array to hold the buttons' toggle status (if the button has an on/off signal). For simple signals buttonsToggleStatus[x] is always 0. For on/off signals this actually switches between 0 and 1. For simplicity, the zero position of the array is ignored. +#endif + + +// SIGNAL CLONING + +/* What length of signal are we looking for? This determines how many edges we need to be able to store. Here's a chart: + * + * 1 byte = 8 bits = 16 timings + * 2 bytes = 16 bits = 32 timings + * 3 bytes = 24 bits = 48 timings <- default for MINIMAL_SIGNAL_LENGTH. Cheap window and door sensors use this. + * 4 bytes = 32 bits = 64 timings + * 5 bytes = 40 bits = 80 timings + * 6 bytes = 48 bits = 96 timings + * 8 bytes = 64 bits = 128 timings <- Most wireless sockets use 8 byte signals. I have never come across a longer signal. + * 10 bytes = 80 bits = 160 timings + * 12 bytes = 96 bits = 192 timings + * + * 16 bytes = 128bits = 256 timings + * + * As you can see, getting more than 16 bytes in practice requires more memory than the Arduino Nano has. + * Ideally, your MAXEDGES setting should be 3x the size of the repeating signal you are looking for. + * + */ +#define MAXEDGES 300 // Maximum samples, limited by RAM. The findPattern() function requires more signals to work than the default analyser. +#define GRANULARITY 50 // Sensitivity. Lower can grab faster signals, but might be less dependable. Set somewhere between 50 and 100. +#define EDGES_TO_SKIP 0 // How much of the beginning of the incoming signal to skip. This is a memory saving measure. Sometimes the first part of the signal is the 'pre-amble': simple timings to help the receiver synchronise its clock. Set somewhere between 0 and 100. Default is 60. +#define MINIMAL_SIGNAL_LENGTH 48 // How many edges should a signal have at the least? ( 1 byte = 2 nibbles = 8 bits = 16 edges). + +#define MINIMUMDURATION 100 // Minimum low or high signal duration for a valid signal in microseconds. Set somewhere between 50 and 100. Helps to avoid noise. +#define MAXIMUMDURATION 20000 // Maximum low or high signal duration for a valid signal. +#define MINIMUMSILENCE 30000 // Minimum low period after a complete signal in microseconds. If there is no input for this long, we can conclude that the transmission must have finished. +#define EEPROM_STORAGE_START 512 // From which position the eeprom can safely be used as storage without overwriting MySensors encryption data. Another option woudl be to use EEPROM_LOCAL_CONFIG_ADDRESS. +#define MAXI 249 * GRANULARITY // The maximum length in microseconds that any timing may have to be precisely stored. If a timing is longer, it will be truncated. + +byte timings[MAXEDGES]; // Creates the array we use to store the timings, but that is also re-used for other things in order to save memory. +const byte metadataArraySize = 8; // The size of the array to store the serial data we're decoding in. +byte metaData[metadataArraySize]; // Holds metadata for a replayable signal. +long signalstart = 0; // Used to calculate the length of each received timing. +long signalend; // Used to calculate the length of each received timing. +unsigned int interval; // The length of a timing in milliseconds. +signed int edges = 0 - EDGES_TO_SKIP; // By starting below zero we can try to avoid the beginning of signals, which often contains a 'pre-amble': a boring pattern to help the receiver get up to speed. +byte bucketsTotal = 0; // This describes how many different timings there are in the signal. To clean up the signal it's wise to lump neighbouring timings together. +byte repeatingPatternLength = 255; // This describes how many timings the repeating part of the signal is made of. +byte amountOfStoredSignals = 0; // How many signals are curently stored on the EEPROM storage. This counts detect-only as well as replayable signals. +byte amountOfStoredReplayableSignals = 0; // How many replayable signals are stored in the EEPROM? +byte repeatingSignalByteLength = 3; // How many bytes does the repeating part of the signal take up in EEPROM? +boolean lastByteIsSplit = false; // Is the last byte of a new signal only half-filled? Signals are multiples of 4 bits, but we can only store them in multiples of 8. So a signal with 20 bits (5 'nibbles'), would still be stored in 3*8 = 24 bits. We need to know that when reconstructing the signal. +boolean anomalyInside = false; // Is the anomaly part of the byte/nibble, or is it outside of it? +byte betweenSpace = 250; // Which timing denotes the space ("the anomaly" as it's called in this code) between repeated signals? If it's still set at 250 after the analysis, the the analysis must have failed to find it. +int startPosition = 1; // The start position of a clean signal (this cuts of the first one) +int endPosition = MAXEDGES - 1; // The end position of a clean signal (this cuts of the last one, which is often truncated). +int positionOfLastSignalEnd = EEPROM_STORAGE_START; // Denotes the EEPROM position right before the position where a new signal can safely be stored. +boolean validSignal = true; // Used by the signal detection loop If the 'high' part of a timing was bad, then this is set to false. Looking for a valid 'low' is then skipped. +boolean captureFinished = false; // If the entire edges array is full of freshly received timings, this is set to true. Analysis the signal is then set in motion. +byte bucketCount = 0; // How many different types of timings are there in the signal. This is used to tighten up the signal. +boolean connectedToNetwork = false; // Are we connected to the local MySensors network? Used to display the 'w' connection icon. +byte lengthOfSignalWeAreWaitingFor = 3; // Used when recording on-off signals. The off-signal should have the same byte length as the on signal. +byte brightnessTimer = 0; // When this reaches 0 the screen is turned off. + +#define PLAYLIST_SIZE 6 +byte playlist[PLAYLIST_SIZE]; // Sometimes multiple demands to play a signal come in. This holds all the signals we should replay one after the other +byte playlist_position = 0; // Signals that should be replayed are placed in a playlist, so they can be played one after the other of multiple should be played. + +byte response_position = 0; // Used in dealing with incoming serial messages from the touch screen. + + +// DESCRIPTION BIT STATES +#define DESCRIPTION_HALFBYTE 0 +#define DESCRIPTION_DUO_RECONSTRUCTION_TYPE 1 +#define DESCRIPTION_ANOMALY_INSIDE 2 +#define DESCRIPTION_ON_OFF 3 +#define DESCRIPTION_REPLAYABLE 4 +#define DESCRIPTION_UNRECONSTRUCTABLE_DUO 5 +#define DESCRIPTION_ANOMALY 6 +#define DESCRIPTION_LARGE_ANOMALY 7 + + +// DISPLAY & STATES +// The list is shared between the display function as well as to desribe what the device is currently doing. + +// Menu states +#define MENU_MAIN 200 +#define MENU_NEW 30 + #define LEARNING_SIMPLE 31 + #define LEARNING_ON 32 + #define COPYING_SIMPLE 33 + #define COPYING_ON 34 + +#define MENU_DELETE_LAST 40 + #define DELETE_LAST 41 + +#define MENU_DELETE_ALL 50 + #define DELETE_ALL 51 + +#define LEARNING_OFF 38 +#define COPYING_OFF 39 + + + +// General display codes +#define PROCESSING 1 // Not a state, only used for display. +#define MATCH 2 // Not a state, only used for display. +#define REPLAYING 3 +#define SIGNAL_STORED 4 // Not a state, only used for display. + +// Display codes for errors +#define BAD_SIGNAL 5 // Not a state, only used for display. +#define OUT_OF_SPACE 6 // Not a state, only used for display. +#define PLAY_SIMPLE_SIGNAL 7 +#define PLAY_ON_SIGNAL 8 // Not a state, only used for display. +#define PLAY_OFF_SIGNAL 9 // Not a state, only used for display. +#define DELETED_LAST 10 // Not a state, only used for display. +#define DELETED_ALL 11 // Not a state, only used for display. + +#define NO_MORE_FREE_BUTTONS 100 // Not a state, only used for display. +#define NO_SIGNAL_STORED_YET 101 // Not a state, only used for display. + +#define STARTUP 254 // This state is only used while booting up the device. +#define LISTENING 255 // This is the default state which it always returns to. + +byte state = STARTUP; // This variable stores what the device is currently doing. It shares some states with the display output. + +#ifdef HAS_TOUCH_SCREEN +#define DISPLAY_REPLAY_BUTTONS 253 // State used with touchscreen only. + + +// The MessageDef code below comes from https://arduino.stackexchange.com/questions/49236/printing-char-array-from-array-of-structs-in-progmem-to-serial + +typedef byte MessageID; // TODO Is this used? Not really. Simplifying the message table could save some storage space. + +struct MessageDef { + MessageID ID; + char Description[20]; +}; + +const MessageDef MessageTable[] PROGMEM = { + {LISTENING, "CANCEL"}, + {MENU_NEW, "New signal"}, + {MENU_DELETE_LAST,"Delete last"}, + {MENU_DELETE_ALL, "Delete all"}, + + {LEARNING_SIMPLE, "Detect single"}, + {LEARNING_ON, "Detect on + off"}, + {COPYING_SIMPLE, "Replay single"}, + {COPYING_ON, "Replay on + off"}, + + {LEARNING_SIMPLE, "Really delete last"}, + {LEARNING_SIMPLE, "Really delete ALL"}, + + {PROCESSING, "Processing"}, // 1 + {MATCH, "Signal matched!"}, // 2 + {REPLAYING, "Replaying "}, // 3 + {SIGNAL_STORED, "Signal was stored"}, // 4 + {BAD_SIGNAL, "Bad signal try again"}, // 5 + {OUT_OF_SPACE, "Out of storage space"}, // 6 + {PLAY_SIMPLE_SIGNAL,"Play the signal"}, // 7 + {PLAY_ON_SIGNAL, "Play ON signal"}, // 8 + {PLAY_OFF_SIGNAL, "Play OFF signal"}, // 9 + {DELETED_LAST, "Deleted last"}, // 10 + {DELETED_ALL, "Deleted all"} // 11 + }; + +#define START_OF_MENU_NEW 4 +#define START_OF_MENU_DELETE_LAST 8 +#define START_OF_MENU_DELETE_ALL 9 +#define START_OF_MENU_STATUS_MESSAGES 10 +#endif + + +// MYSENSORS + +#define RADIO_DELAY 400 // Milliseconds between sending radio signals. This keeps the radio happy. +#define DEVICE_STATUS_ID 1 // The first 'child' of this device is a text field that contains status updates. +#define LISTENER_OUTPUT_ID 2 // The first 'child' of this device is a text field that contains status updates. + +// The device creates 4 virtual buttons that allow signals to be recorded even without an attached screen and keypad. +#define LEARN_SIMPLE_BTN_ID 3 // Learn to detect a single signal. +#define LEARN_ON_OFF_BTN_ID 4 // Learn to detect an ON and an OFF signal that belong together. +#define COPYING_SIMPLE_BTN_ID 5 // Learn to replay a single signal. +#define COPYING_ON_OFF_BTN_ID 6 // Learn to replay an ON and an OFF signal that belong together. + +MyMessage textmsg(DEVICE_STATUS_ID, V_TEXT); // Sets up the message format that we'll be sending to the MySensors gateway later. In this case it's a text variable. The first part is the ID of the specific sensor module on this node. The second part tells the gateway what kind of data to expect. +MyMessage buttonmsg(LEARN_SIMPLE_BTN_ID, V_STATUS); // The message for replayable signals' buttons. This is an on/off message. +MyMessage detectmsg(10, V_TRIPPED); // The message for detect-only signals. This is an on/off message. + + +static unsigned long lastLoopTime = 0; // Holds the last time the main loop ran. +boolean resend_button_states = 1; + +void before() +{ + Serial.begin(115200); + Serial.println(F("Hello, I am a Signal Hub.")); +} + + +void presentation() +{ + scanEeprom(); // Find out how many signals are stored in memory. + + sendSketchInfo(F("Signal Hub"), F("1.1")); wait(RADIO_DELAY); // Child 0. Sends the sketch version information to the gateway and Controller + present(DEVICE_STATUS_ID, S_INFO, F("Device status")); wait(RADIO_DELAY); // Child 1. This outputs general status details. + + //present(LISTENER_OUTPUT_ID, S_INFO, F("Detected codes")); wait(RADIO_DELAY); // Child 2. This outputs the ID of detected signals that were matched to signals in eeprom. +#if !(defined(HAS_TOUCH_SCREEN)) + Serial.println(F("NO TOUCHSCREEN")); + present(LEARN_SIMPLE_BTN_ID, S_BINARY, F("Recognize a single code")); wait(RADIO_DELAY);// Child 3 + present(LEARN_ON_OFF_BTN_ID, S_BINARY, F("Recognize an ON+OFF code")); wait(RADIO_DELAY); // Child 4 + present(COPYING_SIMPLE_BTN_ID, S_BINARY, F("Copy a single code")); wait(RADIO_DELAY); // Child 5 + present(COPYING_ON_OFF_BTN_ID, S_BINARY, F("Copy an ON/OFF code")); wait(RADIO_DELAY); // Child 6 +#endif + + char childNameMessage[11]; + + strcpy_P(childNameMessage, replayMessage); + + // We loop over all the replayable signals, and present them to the controller. + for( byte replayableID=10; replayableID < 10 + amountOfStoredReplayableSignals; replayableID++ ){ +#ifdef DEBUG + Serial.print(F("Replayable child ID ")); Serial.println(replayableID); +#endif + childNameMessage[7] = (replayableID - 10) + 49; + present(replayableID, S_BINARY, childNameMessage); wait(RADIO_DELAY); + } + + strcpy_P(childNameMessage, detectedMessage); + + // We loop over all the detect-only signals, and present them to the controller. + for( byte recognisedID=100; recognisedID < 100 + (amountOfStoredSignals - amountOfStoredReplayableSignals); recognisedID++ ){ +#ifdef DEBUG + Serial.print(F("Detectable child ID ")); Serial.println(recognisedID); +#endif + childNameMessage[9] = (recognisedID - 100) + 49; // ASCII character '1' has number 49. + present(recognisedID, S_DOOR, childNameMessage); wait(RADIO_DELAY); + } + + resend_button_states = 1; +} + + + +void send_values(){ +#ifdef DEBUG + Serial.println(F("Sending button states")); +#endif + +#if !(defined(HAS_TOUCH_SCREEN)) + send(buttonmsg.setSensor(LEARN_SIMPLE_BTN_ID).set(0)); + send(buttonmsg.setSensor(LEARN_ON_OFF_BTN_ID).set(0)); + send(buttonmsg.setSensor(COPYING_SIMPLE_BTN_ID).set(0)); + send(buttonmsg.setSensor(COPYING_ON_OFF_BTN_ID).set(0)); +#endif + + // We loop over all the replayable signals, and send their values. + for( byte replayableID=10; replayableID < 10 + amountOfStoredReplayableSignals; replayableID++ ){ + //Serial.print(F("replay loadState at presentation: ")); Serial.println(loadState(replayableID)); + if( loadState(replayableID - 9) > 1 ){ +#ifdef DEBUG + Serial.println(F("LoadState had big value, setting to 0.")); +#endif + saveState(replayableID - 9, 0); // The -9 is to offset the ID back the the savestates in the eeprom. So child 10 has savestate 1, etc. + } + boolean saved_toggle_state = loadState(replayableID - 9); + send(buttonmsg.setSensor(replayableID).set( saved_toggle_state)); wait(RADIO_DELAY); // Tell the controller in what state the child is. + } + + wait(RADIO_DELAY); + + // We loop over all the detect-only signals, and send their values. + for( byte recognisedID=100; recognisedID < 100 + (amountOfStoredSignals - amountOfStoredReplayableSignals); recognisedID++ ){ + send(detectmsg.setSensor(recognisedID).set( 0 )); wait(RADIO_DELAY); // Tell the controller in what state the child is. + } + +} + +void setup() +{ + pinMode(RECEIVER, INPUT_PULLUP); // 433 receiver + //pinMode(RECEIVER, INPUT); // 433 receiver + pinMode(TRANSMITTER, OUTPUT); // 433 transmitter + digitalWrite(TRANSMITTER, LOW); // 433 transmitter set to off +#if KEYPAD_BUTTON_COUNT > 0 + pinMode(KEYPAD_PIN, INPUT); // Set keypad pin as input +#endif + +#ifdef HAS_BASIC_OLED_SCREEN + oled.begin(&Adafruit128x64, OLED_I2C_ADDRESS); // Start the display (if there is one) + oled.setFont(Adafruit5x7); + oled.ssd1306WriteCmd(SSD1306_DISPLAYON); + oled.setScroll(false); +#endif + +#ifdef HAS_TOUCH_SCREEN + wait(2000); + touch_screen_serial.begin(9600); + wait(2000); + +#ifdef DEBUG + for( int t = EEPROM_STORAGE_START - 1; t < EEPROM.length(); t++ ){ + Serial.print(t); Serial.print(F(" > ")); Serial.println( EEPROM.read(t) ); + } + Serial.print(F("replay button slots: ")); Serial.println( howManyReplayButtonsWillFitOnScreen ); +#endif + + +#ifdef DEBUG_SCREEN + Serial.println(F("BC: test")); + basicCommand(testTFT); +#endif + +#ifdef DEBUG_SCREEN + //Serial.println(F("BC: serial_slow")); +#endif + //basicCommand(serialSlowDown); + +#ifdef DEBUG_SCREEN + //Serial.println(F("BC: reset")); +#endif + //basicCommand(resetTFT); // Reset the TFT. + +#ifdef DEBUG_SCREEN + Serial.println(F("BC: backlight_on")); +#endif + basicCommand(backlight_on); + +#ifdef DEBUG_SCREEN + Serial.println(F("BC: vertical")); +#endif + basicCommand(set_vertical); // Set the screen to vertical mode. + +#ifdef DEBUG_SCREEN + Serial.println(F("BC: white text")); +#endif + basicCommand(text_color_white); // Set text color to white. + +#endif + + // Check if there is a network connection + if(isTransportReady()){ + Serial.println(F("Connected to gateway!")); + connectedToNetwork = true; + //send(relaymsg.setSensor(RELAY1_CHILD_ID).set( actualDoorStates[0] )); wait(RADIO_DELAY); // Tell the controller in what state the lock is. + + send(textmsg.setSensor(DEVICE_STATUS_ID).set( F("Hi") )); //wait(RADIO_DELAY); + +#ifdef DEBUG + Serial.print(F("Stored signal count: ")); + Serial.println(amountOfStoredSignals); + Serial.print(F("Replayable signal count: ")); + Serial.println(amountOfStoredReplayableSignals); +#endif + + + } + else{ + Serial.println(F("! NOCONNECTION")); + connectedToNetwork = false; + } + + state = LISTENING; + updateDisplay(LISTENING); // Show "Listening" on the display. + clearTimingsArray(); // Reset everything, so that we are ready to listen for a signal. + +#ifdef DEBUG + Serial.print(F("Free RAM after setup = ")); Serial.println(freeRam()); +#endif + +} + + +void loop() +{ + //readResponse(); + //wait(200); + //Serial.print(F(".")); + //readResponse(); + + // This part of the code checks the radio receiver pins. + if( !captureFinished ){ + //Serial.print(F("|")); + if( signalstart == 0 ){ + signalstart = micros(); + } + + // Part 1 - HIGH + while( bitRead(PIND, RECEIVER) == HIGH ){} + signalend = micros(); + interval = signalend - signalstart; + if (interval > MINIMUMDURATION && interval < MAXIMUMDURATION ){ + if( edges >= 0 ){ // The edges variable can start at a negative number. This helps cut off the pre-amble that is often transmitted, and that we don't really need. + if( interval > MAXI ){ // Very long edges will be truncated. + timings[edges] = 249; + } + else if( interval != 0 ){ + timings[edges] = (byte) constrain((interval / GRANULARITY), 0, 255); // this rounds the number to something simpler that should fit in a byte array. + } + } + edges++; + } + else { + edges = 0 - EDGES_TO_SKIP; + validSignal = false; + captureFinished = true; + //Serial.println(F("bad timing HIGH")); + signalstart = micros(); + } + + // Part 2 - LOW + if( validSignal ){ // We only try to find a low part if the high part of the signal before it was valid. + while( bitRead(PIND, RECEIVER) == LOW && interval < MINIMUMSILENCE ){} + signalstart = micros(); + interval = signalstart - signalend; + + if( interval > MINIMUMDURATION && interval < MAXIMUMDURATION ){ // Check if the timing falls within reasonable boundaries. + if( edges >= 1 ){ + if(interval > MAXI){ // MAXI = 254 * granularity + timings[edges] = 249; // A timing longer than this will be truncated. + } + else if( interval != 0 ){ + timings[edges] = (byte) constrain((interval / GRANULARITY), 0, 255); // This rounds the number to something simpler that should fit in a byte array. + } + } + edges++; + } + else if( interval >= MINIMUMSILENCE ){ // If the low part of the signal takes a very long time, it could be that the transmission is over. + captureFinished = true; + //Serial.println(F("minimum LOW silence reached")); + if( edges < 0 ){ edges = 0 - EDGES_TO_SKIP; } + signalstart = 0; + } + else { + edges = 0 - EDGES_TO_SKIP; + //validSignal = false; + captureFinished = true; + //Serial.println(F("bad timing LOW")); + } + } + if( edges >= MAXEDGES - 2 ){ // Our timings array is full of fresh data. + captureFinished = true; + //Serial.print(F("edges full ")); + } + } + + else { // SignalCapture is finished. + captureFinished = false; // Reset for next round. + boolean signalIsOk = false; + //Serial.println(F("DONE CAPTURING")); +#ifdef HAS_TOUCH_SCREEN + readResponse(); +#endif + + if( validSignal && edges > MINIMAL_SIGNAL_LENGTH * 2 ){ // The timings data is long enough. + if( signalViabilityCheck() ){ // A quick quality check on the signal. +#ifdef DEBUG + Serial.println(F("PROCESSING")); +#endif +#ifdef HAS_BASIC_OLED_SCREEN + updateDisplay(PROCESSING); +#endif + betweenSpace = 250; // Reset the betweenSpace. Might be superfluous. + if( signalCleaner() ){ // Cleaning and tightening the received signal. + if( signalAnalysis() ){ + Serial.println(); + signalIsOk = true; + + if( state == LISTENING ){ // System received a good signal while in the LISTENING state. + Serial.println(F("Comparing")); + //byte detectedSignalNumber = scanEeprom(); + //Serial.print(F("DetectedSignalNumber = ")); Serial.println(detectedSignalNumber); + if( scanEeprom() ){ // If the signal matches a signal in eeprom, then the scanEeprom function returns its number in eeprom. If there is no match, it returns 0. + Serial.println(F("MATCH")); + send(textmsg.setSensor(DEVICE_STATUS_ID).set( F("Detected a known signal") )); //wait(RADIO_DELAY); + } + } + else if( state > MENU_NEW && state < MENU_DELETE_LAST ){ // States in which a signal is supposed to be copied to the EEPROM memory. + + Serial.print(F("State ")); Serial.println(state); + + // If we are looking for an OFF signal, then we should check if this signal is just as long as the ON signal we heard earlier. + if( (state == LEARNING_OFF || state == COPYING_OFF) && repeatingSignalByteLength != lengthOfSignalWeAreWaitingFor ){ + // Poor signal. Try again. + } + else { + + // Store the signal + if( writeSignalToEeprom() ){ // We try to store the signal in the EEPROM (internal storage) + + // If we are doing an on/off copy, then we must move to the next step in the process. + if( state == COPYING_ON || state == LEARNING_ON ){ + Serial.println(F(" Now play OFF signal")); + lengthOfSignalWeAreWaitingFor = repeatingSignalByteLength; // When we record the 'off' signal later, we want it to be the same length. + send(textmsg.setSensor(DEVICE_STATUS_ID).set( F("Now play the OFF signal") )); //wait(RADIO_DELAY); + if( state == COPYING_ON ){ state = COPYING_OFF; } // switch to the next part of the process. + if( state == LEARNING_ON ){ state = LEARNING_OFF; } // switch to the next part of the process. + } + else{ + updateDisplay(SIGNAL_STORED); + presentation(); // Re-present the node, which now has a new child. + Serial.println(F("Finished copying")); + send(textmsg.setSensor(DEVICE_STATUS_ID).set( F("OK") ));// wait(RADIO_DELAY); + state = LISTENING; + } + } + else{ // Storing the signal in EEPROM failed. + Serial.println(F("Error storing signal")); + updateDisplay(OUT_OF_SPACE); + send(textmsg.setSensor(DEVICE_STATUS_ID).set( F("Error storing signal") )); //wait(RADIO_DELAY); + state = LISTENING; + } + } + updateDisplay(state); + } + } // End of 'signal is ok'. + else { +#ifdef DEBUG + Serial.println(F("signalAnalysis failed")); +#endif + } + } + else{ + Serial.println(F("No repeating part found")); // The signal cleaner couldn't find a repeating part + } + } + else { + Serial.println(F("Low quality signal")); + } + clearTimingsArray(); + } + else{ // The captured signal didn't have enough edges. + captureFinished = false; + validSignal = true; + } + + + // If a presentation has been requested, we send the new button states. + if( resend_button_states ){ + resend_button_states = 0; + send_values(); + } + +#if defined (HAS_BASIC_OLED_SCREEN) || defined (HAS_TOUCH_SCREEN) + + // + // MAIN MENU + // + + // Once every 100 milliseconds check if the menu button is being pressed. + if( millis() - lastLoopTime > 100 ){ + lastLoopTime = millis(); + + // If there is a signal to be played in the playlist, play it. + if( playlist_position > 0 ){ + Serial.print(F("Playing signal from playlist #")); Serial.println(playlist_position); + if( playlist[playlist_position] > 100 ){ // Bigger than 100 means it should be turned on. + replay( playlist[playlist_position] - 100, 1); + } + else{ + replay( playlist[playlist_position], 0); + } + playlist_position--; + Serial.print(F("-new #")); Serial.println(playlist_position); + } + + + // Turn screen off after a while + if( brightnessTimer > 1){ + brightnessTimer--; + //Serial.println(brightnessTimer); + } + else if( brightnessTimer == 1 ){ + brightnessTimer = 0; +#ifdef DEBUG_SCREEN + Serial.println(F("BC: backlight_off")); +#endif + basicCommand(backlight_off); // Set the screen to vertical mode. + } + + +#ifdef HAS_TOUCH_SCREEN + if( touched ){ + turnOnScreen(); + //Serial.println(F("TOUCH")); + buttonPressed = touchScreenButtonPress(); // Check what part of the touchscreen was pressed. +#ifdef DEBUG_SCREEN + drawPix(touchX,touchY, 0); // Draw a pixel where the user touched the screen. +#endif + touched = false; + +#elif KEYPAD_BUTTON_COUNT == 4 + buttonPressed = keypad4(); + if( buttonPressed != prevButtonState ){ +#elif KEYPAD_BUTTON_COUNT == 12 + buttonPressed = keypad12(); + if( buttonPressed != prevButtonState ){ +#endif + + if( buttonPressed == KEYPAD_BUTTON_COUNT ){ // Menu button is being pressed, which works as cancel if the user is already recording. + showMenu(); +#ifdef DEBUG + Serial.print(F("New state: ")); Serial.println(state); +#endif + + // Sub menu for deleting the eeprom. + if( state == DELETE_ALL ){ + Serial.println(F("ERASING STORED SIGNAL DATA")); + visibleReplayButtonsPage = 0; + updateDisplay(DELETED_ALL); + for( int g = EEPROM_STORAGE_START; g <= EEPROM.length(); g++ ){ // Deleting the data stored in the eeprom. + EEPROM.update(g, 255); + } + scanEeprom(); // Re-index the EEPROM, which in this case sets all stored signal counters back to 0. + state = LISTENING; + } + else if( state == DELETE_LAST ){ + visibleReplayButtonsPage = 0; + scanEeprom(); + updateDisplay(DELETE_LAST); + state = LISTENING; + } + updateDisplay(state); + } + +#ifdef HAS_TOUCH_SCREEN + else if( buttonPressed <= howManyReplayButtonsWillFitOnScreen ){ // A send-signal button is being pressed, and we're not inside the menu, so we should replay a signal. + if( buttonPressed <= amountOfStoredReplayableSignals - (visibleReplayButtonsPage * howManyReplayButtonsWillFitOnScreen) ){ + //boolean onOrOff = 0; + //if( touchX > TOUCHSCREEN_WIDTH / 2 ){ // If the user pressed the right side of the virtual button. + // onOrOff = 1; + //} + //replay( buttonPressed + (visibleReplayButtonsPage * howManyReplayButtonsWillFitOnScreen), onOrOff ); // The replay function manages the required state change internally. + + if( playlist_position < PLAYLIST_SIZE){ // We only add to the playlist if there is space left in the playlist. + + Serial.print(F("-Adding to playlist:")); Serial.print(buttonPressed + (visibleReplayButtonsPage * howManyReplayButtonsWillFitOnScreen)); + playlist_position++; + if( touchX > TOUCHSCREEN_WIDTH / 2 ){ // Right side of the screen was clicked, so send signal to turn on. + Serial.println(F(", On")); + playlist[playlist_position] = (buttonPressed + (visibleReplayButtonsPage * howManyReplayButtonsWillFitOnScreen)) + 100; + } + else { + Serial.println(F(", Off")); + playlist[playlist_position] = (buttonPressed + (visibleReplayButtonsPage * howManyReplayButtonsWillFitOnScreen)); + } + } + + + + } + } + else { + Serial.println(F("MORE>")); + visibleReplayButtonsPage++; + if( visibleReplayButtonsPage * howManyReplayButtonsWillFitOnScreen >= amountOfStoredReplayableSignals ){ + visibleReplayButtonsPage = 0; + } + updateDisplay(state); + } +#else // Basic oled with analog buttons, and a signal replay button has just been pressed. + else{ + boolean onOrOff = buttonsToggleStatus[signalNumber]; + buttonsToggleStatus[signalNumber] = !buttonsToggleStatus[signalNumber]; // Remember that the button has been toggled. + replay( buttonPressed, onOrOff ); + } + prevButtonState = buttonPressed; +#endif + clearTimingsArray(); + } + } // End of checking if the menu button is being pressed. +#endif + } // End of checking the received signal. +} // End of main loop. + + + +// +// VIABILITY CHECK +// +// The signal should have a minimal level of diversity. This function checks how many different types of timings there are, and how often they exist. + +boolean signalViabilityCheck() +{ +#ifdef DEBUG + Serial.println(F("__Viability check")); + printRawSignal(); +#endif + + while( timings[endPosition] == 0 ){ + endPosition--; // If the signal has a lot of zero's at the end, remove them. + if( endPosition < MINIMAL_SIGNAL_LENGTH ){ break; } + } + + boolean diverseEnough = false; // At the beginning of this function we assume the signal is not diverse enough, until proven otherwise. + int uniqueCounter = 0; // How often a certain timing exists in the timings data. + bucketCount = 0; + + for( byte s = 0; s < 255; s++ ){ // Check how often the different timings exist. + uniqueCounter = 0; + byte samenumber = 0; + for( int j = startPosition; j <= endPosition; j++ ){ + if( timings[j] == s ){ + uniqueCounter++; + } + } + + if( s == 0 && uniqueCounter > 1 ){ + return false; + } + + // If there was at least one occurence of this timing, then increase the bucketCount. + if( s != 0 && uniqueCounter > 1 ){ + //Serial.print(F("- bucket: ")); Serial.print(s); Serial.print(F(" has ")); Serial.println(uniqueCounter); + bucketCount++; + } + + // If we find a timing that takes up more than 10% of all timings and less than 90%, then this indicates the signal is varied enough. + if( s > 1 && uniqueCounter > round((endPosition-startPosition) / 10) && uniqueCounter < round((endPosition-startPosition) * .8) && diverseEnough == false ){ + diverseEnough = true; +#ifdef DEBUG + //Serial.print(F("signal is diverse enough (")); Serial.println(s); +#endif + } + } + + + if( diverseEnough && bucketCount >=2 && bucketCount < 20 ){ // If both indicators are positive, then the signal may be usable. +#ifdef DEBUG + printRawSignal(); + Serial.print(F("Buckets @ quality check: ")); Serial.println(bucketCount); +#endif + return true; + } + else{ + return false; // Indicates that the signal was not useable. + } +} + + +// +// SIGNAL CLEANER +// +// This merges timings that are very similar into one. To do this the variable P is used to combine increasingly distant neighbouringing timings together. +// In the first loop, when P is 1, only direct neighbours are merged (for example: 5 and 6). When P is 2 it could, for example, merge 4 and 6 together. And so forth. +// When P is 2, this is also the first round that we look for the repeating part of the timings. Most signals have some special timings in between the repeating bits. Here we look for it, and point to it with the 'betweenSpace' variable. + +boolean signalCleaner() +{ +#ifdef DEBUG + Serial.println(F("__signalCleaner")); +#endif + + for( byte i = 0; i < 8; i++ ){ + metaData[i] = 0; // Set all metadata values to 0. Don't want old data messing things up. + } + repeatingPatternLength = 255; // This is only allowed to become smaller. After all, we want the smallest repeating code. + + // Sorting timings into the most popular buckets. + int previousCounter = 0; + betweenSpace = 250; + byte prevBucketCount = 0; + byte highestFoundTiming = 0; + + byte p = 1; + while( p <= 4 ){ +#ifdef DEBUG + Serial.print(F("P")); Serial.println(p); +#endif + bucketCount = 0; // Reset bucket count. + byte lastOftenFoundTiming = 0; // Reset last found neighbouring timing. + boolean foundBetweenSpaceThisRound = false; + + for( byte i = 0; i < betweenSpace + 5; i++ ){ // The "+5" is a failsafe. The idea is it will scan a bit more of the original signal, just in case we find a betweenSpacing that's even better nearby. In practise it's not that useful.. + int currentCounter = 0; + int lastFoundJ = endPosition; // The position in the array of the last timing of this type that we found. We use this to check if there is enough distance between these timings, as the betweenSpaces we're looking for act like spacers between the repeating signals. + + for ( int j = endPosition; j >= startPosition; j-- ){ // count all occurrences of this timing in the data + if( timings[j] == i ){ + currentCounter++; + + if( p > 1 && bucketCount > 1 && currentCounter < 10 && foundBetweenSpaceThisRound == false ){ // If this timing was found more than 10 times, it's probably not the between space. J has to be bigger than 5 to allow the code to grab the spacer data later. + if ( lastFoundJ < endPosition - 3 && lastFoundJ - j > MINIMAL_SIGNAL_LENGTH && lastFoundJ - j < repeatingPatternLength ){ // endPosition - 3 is done so that we are sure we have enough space at the end of the array left over to copy the anomaly data from later. + // We found it! Determining the betweenSpace is important. It means we're certain we've found the part of the timings array that is the repeating part. + foundBetweenSpaceThisRound == true; // No need to keep looking while processing the higher timings. + betweenSpace = i; + endPosition = lastFoundJ; // We remember where the signal we found starter and ended. From now on we'll only be focussing on this small part of the timings array. + startPosition = j; + repeatingPatternLength = endPosition - startPosition; +#ifdef DEBUG + Serial.print(F("repeatingPatternLength found = ")); Serial.println(repeatingPatternLength); +#endif + } + } + lastFoundJ = j; + } + } + if( currentCounter > 0 ){ // We found at least one occurence of the timing we're currently looking at. +#ifdef DEBUG + //Serial.print(F("Found ")); Serial.print(i); Serial.print(F(" this often: ")); Serial.println(currentCounter); +#endif + bucketCount++; + highestFoundTiming = i; + if( previousCounter != 0 ){ // If we also already found another timing on a previous loop though, then we can compare the two. + if( lastOftenFoundTiming != 0 && lastOftenFoundTiming < i && i - lastOftenFoundTiming <= p && lastOftenFoundTiming != i ){ // If the neighbouring big timing isn't too far away from this one, then we may be able to gobble it up (or let it gobble up the current timing). + if( currentCounter >= previousCounter ){ // The current timing is found more often in the timings array. Whichever is bigger wil 'win', and the other timing will be set to be the same as the most often occuring of the two. +#ifdef DEBUG + //Serial.print(F("Shifting timing ")); Serial.print(lastOftenFoundTiming); Serial.print(F(" (found ")); Serial.print(previousCounter); Serial.print(F(" times) UP to ")); Serial.print(i); Serial.print(F(" (found ")); Serial.print(currentCounter); Serial.println(F(" times).")); +#endif + for( int e = startPosition; e <= endPosition; e++ ){ // Loop over the dataset and change all occurences of the other timing into the current timing. + if( timings[e] == lastOftenFoundTiming ){ + timings[e] = i; + } + } + //currentCounter = previousCounter + currentCounter; // Reflect that this is now a bigger bucket. + } + else{ // Previous counter was bigger +#ifdef DEBUG + //Serial.print(F("Shifting timing ")); Serial.print(i); Serial.print(F(" (found ")); Serial.print(currentCounter); Serial.print(F(" times) DOWN to ")); Serial.print(lastOftenFoundTiming); Serial.print(F(" (found ")); Serial.print(previousCounter); Serial.println(F(" times).")); +#endif + for( int e = startPosition; e <= endPosition; e++ ){ // loop over the timings and lift the previous ones up. + if( timings[e] == i ){ + timings[e] = lastOftenFoundTiming; + } + } + } + bucketCount--; // We just merged two timings together into a single bucket. + } + } + lastOftenFoundTiming = i; // Remember what the last timing was.. + previousCounter = currentCounter; // ..and how often we found it. + } + } // End of loop that goes over all timings to count how often each timing exists. + if( bucketCount != prevBucketCount ){ // If the bucketCount changed, then we may want to comb through the timings array again with the same P setting. + prevBucketCount = bucketCount; + if(betweenSpace != 250 && bucketCount < 5){ // We found the betweenSpace and have a cleaned up signal. So we have all that we need. + p = 5; // Breaks out of the while loop. + } + } + else{ // If the bucketcount is unchanged, the current P setting is no longer having any effect, and we should try increasing it. + //Serial.println(F("Unchanged bucketcount")); + p++; // By increasing P the next iteration will be allowed to merge timings that are further apart from each other. + } + lastOftenFoundTiming = 0; + if( highestFoundTiming < 10 ){ // After one round of cleaning the signal we can exit the process here in case the signal is very fast. Otherwise we may acutally damage the timings data by merging them too much. +#ifdef DEBUG + Serial.println(F("Fast signal")); // This is a very fast signal (probably without a betweenSpace anyway). Some more expensive modern devices use this. Wireless carkeys, for example. +#endif + break; + } + } // End of while loop. +#ifdef DEBUG + printRawSignal(); +#endif + if( betweenSpace == 250 ){ // 250 is the default value. This means the previous function did not find a between-space indicator. Time to bring in the pattern finder. + if( state > MENU_NEW && state < MENU_DELETE_LAST ){ + if( findPattern() ){ // The pattern finder is our last hope. It uses the 'brute force method' to search for a repeating pattern in the signal. It can find repeating patterns when the signal does not have spaces between the repeating parts. + return true; + } + } + return false; // Signal analysis is impossible. + } + else{ + return true; // We found a betweenSpace. + } +} + + +// +// SIGNAL ANALYSIS +// +// Now that the signal is clean we can try to find the byte code it contains. We try to fill the metaData array, which describes the signal, and will be stored to eeprom if necessary. +// +// The medaData array is as follows; +// metaData[0] -> The first timing of the first repeating duo +// metaData[1] -> The second timing of the first repeating duo +// metaData[2] -> The first timing of the second repeating duo +// metaData[3] -> The second timing of the second repeating duo +// metaData[4] -> The first timing of the first anomaly +// metaData[5] -> The second timing of the first anomaly +// metaData[6] -> The first timing of the second anomaly +// metaData[7] -> The second timing of the second anomaly +// +// The code can only store two anomalous bits (which are sent in between the main repeating part of the code) + +boolean signalAnalysis() +{ +#ifdef DEBUG + Serial.println(F("__signalAnalysis")); +#endif + + //byte jump = 1; // Used by the function that tries to determine the timing duo's that the repeating signal consists of. + byte anomalyCount = 0; + byte minimumDuosNeeded = int(repeatingPatternLength / 2) - 2; +#ifdef DEBUG + Serial.print(F("Duos needed: ")); Serial.print(minimumDuosNeeded); + Serial.print(F(" (repeatingPatternLength: ")); Serial.println(repeatingPatternLength); +#endif + byte totalDuosPossible = 0; + for( int i = startPosition; i < startPosition + 5; i++ ){ +#ifdef DEBUG + Serial.print(F("_____i_")); Serial.println(i - startPosition); +#endif + memset(metaData, 0, sizeof(metaData)); // Clear the metadata array. + totalDuosPossible = 0; // Will check how many timing-duo's we can find in the signal if using the current base duos + anomalyCount = 0; + for ( int j = i; j < i + repeatingPatternLength; j = j + 2 ){ + if( timings[j] == metaData[0] && timings[j+1] == metaData[1] ){ + totalDuosPossible++; // The current duo was in the metadata, so increase the possible duos counter. +#ifdef DEBUG + Serial.print( timings[j] ); Serial.print(F(",")); Serial.print( timings[j+1] ); Serial.println(F(" 0")); +#endif + } + else if( timings[j] == metaData[2] && timings[j+1] == metaData[3] ){ + totalDuosPossible++; // The current duo was in the metadata, so increase the possible duos counter. +#ifdef DEBUG + Serial.print( timings[j] ); Serial.print(F(",")); Serial.print( timings[j+1] ); Serial.println(F(" 1")); +#endif + } + else if( metaData[0] == 0 ){ // If it wasn't in the metadata, add it to the metadata, and register that we did in fact find a possible duo. + metaData[0] = timings[j]; + metaData[1] = timings[j+1]; + totalDuosPossible++; +#ifdef DEBUG + Serial.print( timings[j] ); Serial.print(F(",")); Serial.print( timings[j+1] ); Serial.println(F(" 0 <- Duo A")); +#endif + } + else if( metaData[2] == 0 ){ + metaData[2] = timings[j]; + metaData[3] = timings[j+1]; + totalDuosPossible++; +#ifdef DEBUG + Serial.print( timings[j] ); Serial.print(F(",")); Serial.print( timings[j+1] ); Serial.println(F(" 1 <- Duo B")); +#endif + } + else if( metaData[4] == 0 ){ + metaData[4] = timings[j]; + metaData[5] = timings[j+1]; + anomalyCount++; +#ifdef DEBUG + Serial.print( metaData[4] ); Serial.print(F(",")); Serial.print( metaData[5] ); Serial.println(F(" <- Anomaly A")); +#endif + + if( totalDuosPossible < minimumDuosNeeded){ + j = 1000; // Anomaly found too soon, should be at the end of the signal, after all the duos are found. So we can skip checking this. + } + else if( (totalDuosPossible * 2) + (anomalyCount * 2) < repeatingPatternLength ){ + metaData[6] = timings[j+2]; + metaData[7] = timings[j+3]; + anomalyCount++; +#ifdef DEBUG + Serial.print( metaData[6] ); Serial.print(F(",")); Serial.print( metaData[7] ); Serial.println(F(" <- Anomaly B")); +#endif + } + } + else{ + //Serial.println(F("bad round")); + j = 1000; // Too many anomalies (the code can handle 2). Skip to the next round, maybe we will have better luck. + } +#ifdef DEBUG + if( totalDuosPossible % 8 == 0 ){ Serial.println(); } +#endif + + } + if( totalDuosPossible >= minimumDuosNeeded ){ +#ifdef DEBUG + Serial.print(F("Enough duos found (")); Serial.println(totalDuosPossible); + Serial.println(metaData[0]); + Serial.println(metaData[1]); + Serial.println(metaData[2]); + Serial.println(metaData[3]); +#endif + endPosition = endPosition + (i - startPosition); // Shifting the window a little bit, if necessary. + startPosition = startPosition + (i - startPosition); // Shifting the window a little bit, if necessary. + + + Serial.println(); + Serial.println(); + for ( int s = startPosition; s <= endPosition; s++ ){ + + Serial.print(timings[s]); Serial.print(F(",")); + if(s % 8 == 0){ Serial.println(); } + } + Serial.println(); Serial.println(); + + break; + } + else { +#ifdef DEBUG + Serial.println(F("Not enough possible duos")); Serial.println(); +#endif + if( i - startPosition == 4 ){ + return false; // What's this all about? + } + } + } + + // Switch the duo's around in necessary, so that the one the has a difference in timings is the first one. This will save a byte in storage later. + if( metaData[0] == metaData[1] == metaData[2] && metaData[1] < metaData[3]){ + metaData[1] = metaData[3]; + metaData[3] = metaData[0]; + } + + repeatingSignalByteLength = (byte)totalDuosPossible / 8; + + byte leftovers = totalDuosPossible % 8; + +#ifdef DEBUG + Serial.print(F("leftover:")); Serial.println( leftovers ); + Serial.print(F("anomalyCount:")); Serial.println( anomalyCount ); +#endif + + lastByteIsSplit = false; + anomalyInside = false; + if( leftovers > 0 ){ + repeatingSignalByteLength++; // If there is a nibble (half a byte) left over, then increase the required array length by one. + + if( leftovers == 4 ){ + lastByteIsSplit = true; + //Serial.println(F("Last byte is split")); + } + else if( leftovers + anomalyCount == 4 ){ + lastByteIsSplit = true; + anomalyInside = true; + //Serial.println(F("anomaly is inside nibble")); + } + else if( leftovers + anomalyCount == 8 ){ + anomalyInside = true; + //Serial.println(F("anomaly is inside byte")); + } + else { + Serial.println(F("Cannot handle this signal")); + return false; // The system cannot currently deal with this signal, sorry. + } + } + + // Take 8 bits and encode them into a byte. + for( byte i = 0; i < repeatingSignalByteLength; i++ ){ + byte eepromByte = 0; + for( int j = 0; j < 8; j++ ){ + if( timings[startPosition] == metaData[0] && timings[startPosition + 1] == metaData[1] ){ + Serial.print(F("0")); + bitWrite(eepromByte,j,0); + startPosition = startPosition + 2; + } + else if( timings[startPosition] == metaData[2] && timings[startPosition + 1] == metaData[3] ){ + Serial.print(F("1")); + bitWrite(eepromByte,j,1); + startPosition = startPosition + 2; + } + else{ + Serial.print(F("X")); + bitWrite(eepromByte,j,0); + } // End of bit loop + } // End of byte loop + timings[i] = eepromByte; +//#ifdef DEBUG + Serial.print(F(" > HEX: ")); +//#endif + Serial.print(eepromByte, HEX); +//#ifdef DEBUG + Serial.print(F(" DEC: ")); Serial.println(eepromByte); +//#endif + } + Serial.println(); + return true; // return if the signal analysis went ok. +} + + +// +// PATTERN FINDER +// +// Finds repeating patterns in the signal data. This is a backup function. Some signals don't have a betweenSpace. In those cases we use brute force to look for the longest repeating pattern we can find. This function takes longer. + +boolean findPattern() +{ +#ifdef DEBUG + Serial.println(F("___pattern_finder___")); +#endif + byte maxPatternLength = (byte)constrain((endPosition-startPosition)/2, 31, 255); // what length is the pattern we're looking for allowed to be? It's most likely 8 bytes = 64 bits = 128 timings, but we can't be sure in this case. To find a repeating pattern, we need to be able to have it in the data twice, so the maximum length is the timings array length divided by two. + byte searchLength = 32; + while( searchLength + 4 <= maxPatternLength ){ // Check the maximum pattern length we can search for in the current memory. + searchLength = searchLength + 4; + } + //Serial.print(F(" searchLength that we begin pattern matching with: ")); Serial.println(searchLength); + for( searchLength; searchLength > MINIMAL_SIGNAL_LENGTH; searchLength = searchLength - 4 ){ // Find repeating patterns of a minimal length, starting with a long as possible signal, and then working down. + int patternFirstPosition = 0; // Where we found an often occuring pattern for the first time. To keep things simple(low memory) we only search the first 255 positions of the array. + for ( int i = startPosition; i <= endPosition-searchLength; i++ ){ + byte patternCount = 0; + for ( int y = startPosition; y <= endPosition-searchLength; y++ ) { + boolean oksofar = true; // Starts out assuming the pattern is found, and then starts comparing. As soon as one of the timings is not the same, it sets this to false. + for (byte r = 0; r < searchLength; r++) { // We compare the selected patterns + if( timings[i+r] != timings[y+r] ){ + oksofar = false; // If the pattern is not found at this position. + break; // No need continuing the comparison. + } + } + if( oksofar == true ){ + patternCount++; // We scanned over the position in the array, and nothing tripped up the recogniser, meaning it actuallly found the pattern. + y = y + (searchLength-1); // Minus one, because the for-loop will also add one. We skip ahead and see if we can find the same pattern again straigth after. + } + } + if(patternCount > 1){ // We found a repeating pattern! + // Quick quality check. It makes sure the found code isn't just the same number in a row a lot. + int sameNumber = 0; + for( int j = startPosition; j <= endPosition; j++ ){ + if( timings[j] == timings[startPosition] ){ + sameNumber++; + } + } + if(sameNumber > 15){ // This many of the same timings in a row is a bad sign. + return false; + } + Serial.print(F("Pattern: ")); + for (int t = 0; t < searchLength; t++) { + Serial.print(timings[i + t]); Serial.print(F(",")); + } + Serial.println(); + // Update the global variables: + repeatingPatternLength = searchLength; // Store the length of the repeating part. + startPosition = i; // Set the start position of the repeating part of the signal. + endPosition = i + searchLength; // Set the end position of the repeating oart of the signal. + return true; + } + } + } + //Serial.println(F("Pattern finder failed")); + return false; // We reached the end without finding any repeating pattern. +} + + +// +// SCAN EEPROM +// +// This function has multiple uses, depending on the state. +// When signalNumber is given as 0, then it acts as a simple scan of the eeprom, and can compare found signals to those in eeprom. +// If state is listening, then it returns the number of a signal mathed in eeprom. +byte scanEeprom() +{ + //Serial.println(F("SCANNING EEPROM")); + + byte whatWeCameHereFor = 0; + amountOfStoredSignals = 0; // Reset these. Everytime this function runs they are updated. + byte amountOfStoredReplayableSignalsScan = 0; + positionOfLastSignalEnd = EEPROM_STORAGE_START - 1; // MySensors has another value we could grab to get even more space. + boolean finishedScanningEeprom = false; + while( finishedScanningEeprom == false ){ + //Serial.println(positionOfLastSignalEnd); + byte storedSignalLength = EEPROM.read(positionOfLastSignalEnd + 1); // This get the first byte of the next stored signal (if it exists), and indicates how much data it takes on the eeprom. +#ifdef DEBUG + Serial.print(F("#")); Serial.println(amountOfStoredSignals + 1); + Serial.print(F("-storedlength:"));Serial.println(storedSignalLength); +#endif + if( storedSignalLength == 0xff || storedSignalLength == 0x00 ){ // No more signals found in the EEPROM. + finishedScanningEeprom = true; + } + + // For each signal in eeprom: + else { + //if( positionOfLastSignalEnd + storedSignalLength > EEPROM.length() ){ break; } // This should theoretically never happen. But if it did it would lock up the device, so it can't hurt. + // DELETE THE LAST RECORDED SIGNAL? + if( state == DELETE_LAST && EEPROM.read(positionOfLastSignalEnd + 1 + storedSignalLength) == 0xFF ){ // If the next storage slot empty? + Serial.println(F("Deleting last stored signal")); + for( int j = positionOfLastSignalEnd + 1; j < positionOfLastSignalEnd + 1 + storedSignalLength; j++ ){ // Overwrite the last signal with 255's (the default memory state). + EEPROM.update(j, 0xFF); + } + return 1; + } + if( storedSignalLength > 0 ){ + amountOfStoredSignals++; // Update how many signals are stored in eeprom. + } + byte descriptionData = EEPROM.read(positionOfLastSignalEnd + 2); + if( bitRead(descriptionData, DESCRIPTION_REPLAYABLE) == 1 ){ // Is it a replayable signal? + amountOfStoredReplayableSignalsScan++; + if( state == REPLAYING && amountOfStoredReplayableSignalsScan == buttonPressed ){ +#ifdef DEBUG + Serial.println(F("Eeprom scan found the signal to replay")); +#endif + whatWeCameHereFor = storedSignalLength; + startPosition = positionOfLastSignalEnd + 1; // Recycling variables to save memory. + endPosition = positionOfLastSignalEnd + storedSignalLength; + } + +#ifdef HAS_TOUCH_SCREEN + if( state == DISPLAY_REPLAY_BUTTONS ){ + // The signal is replayable. If a touchscreen is attached, we show the button for it on the screen. + byte pageStart = visibleReplayButtonsPage * howManyReplayButtonsWillFitOnScreen; + byte itemNumber = (amountOfStoredReplayableSignalsScan - 1 ) % howManyReplayButtonsWillFitOnScreen; + + //Serial.print(F("howManyReplayButtonsWillFitOnScreen: ")); Serial.println(howManyReplayButtonsWillFitOnScreen); + //Serial.print(F("amountOfStoredReplayableSignals so far: ")); Serial.println(amountOfStoredReplayableSignalsScan); + //Serial.print(F("page start: "));Serial.println( pageStart ); + //Serial.print(F("replayable. itemNumber: ")); Serial.println( itemNumber ); + + if( amountOfStoredReplayableSignalsScan > pageStart && amountOfStoredReplayableSignalsScan <= pageStart + howManyReplayButtonsWillFitOnScreen ){ + if( bitRead(descriptionData, DESCRIPTION_ON_OFF) == 0 ){ +#ifdef DEBUG_SCREEN + Serial.println(F("BC: 4 commands for next page button:")); +#endif + roundedRectangle( 0, BUTTON_HEIGHT + (itemNumber * BUTTON_HEIGHT), TOUCHSCREEN_WIDTH, BUTTON_HEIGHT, (int)BUTTON_HEIGHT/3, 31727 ); // dark grey rounded rectangle. + setCur( BUTTON_PADDING, BUTTON_PADDING + BUTTON_HEIGHT + (itemNumber * BUTTON_HEIGHT) ); + displayNumber(itemNumber + (visibleReplayButtonsPage * howManyReplayButtonsWillFitOnScreen) + 1 ); + basicCommand(play); + } + else { +#ifdef DEBUG_SCREEN + Serial.println(F("BC: roundedRect")); +#endif + roundedRectangle( 0, BUTTON_HEIGHT + (itemNumber * BUTTON_HEIGHT), (int)TOUCHSCREEN_WIDTH/2, BUTTON_HEIGHT, (int)BUTTON_HEIGHT/3, 31727 ); // dark grey rounded rectangle. +#ifdef DEBUG_SCREEN + Serial.println(F("BC: setCur")); +#endif + setCur( BUTTON_PADDING, BUTTON_PADDING + BUTTON_HEIGHT + (itemNumber * BUTTON_HEIGHT) ); +#ifdef DEBUG_SCREEN + Serial.println(F("BC: display_number")); +#endif + displayNumber(itemNumber + (visibleReplayButtonsPage * howManyReplayButtonsWillFitOnScreen) + 1 ); +#ifdef DEBUG_SCREEN + Serial.println(F("BC: off")); +#endif + basicCommand(off); +#ifdef DEBUG_SCREEN + Serial.println(F("BC: roundedRect")); +#endif + roundedRectangle( (int)TOUCHSCREEN_WIDTH/2, BUTTON_HEIGHT + (itemNumber * BUTTON_HEIGHT), (int)TOUCHSCREEN_WIDTH/2, BUTTON_HEIGHT, (int)BUTTON_HEIGHT/3, 31727 ); // dark grey rounded rectangle. +#ifdef DEBUG_SCREEN + Serial.println(F("BC: setcur")); +#endif + setCur( BUTTON_PADDING + (TOUCHSCREEN_WIDTH / 2), BUTTON_PADDING + BUTTON_HEIGHT + (itemNumber * BUTTON_HEIGHT) ); +#ifdef DEBUG_SCREEN + Serial.println(F("BC: display_number")); +#endif + displayNumber(itemNumber + (visibleReplayButtonsPage * howManyReplayButtonsWillFitOnScreen) + 1 ); +#ifdef DEBUG_SCREEN + Serial.println(F("BC: on")); +#endif + basicCommand(on); + } + } + //else { + //Serial.println(F("(skip)")); + //} + } +#endif + + } + else { +#ifdef DEBUG + Serial.println(F("-detectable")); +#endif + } + // LISTENING + positionOfLastSignalEnd = positionOfLastSignalEnd + storedSignalLength; + if( state == LISTENING && whatWeCameHereFor == 0 && bitRead(descriptionData, DESCRIPTION_REPLAYABLE) == 0 ){ // We only check detect-only signals for now. This could be changed to include all signals by removing the last check in this if statement. + // We should scan the stored binary data against the last signal we received. + boolean areTheyTheSame = true; // The code below continously tries to disprove that they are the same, and skips ahead at the first evidence that this is the case. + for( byte i = 0; i < 1 + bitRead(descriptionData, DESCRIPTION_ON_OFF); i++ ){ // Check both the on and off signals. + areTheyTheSame = true; + for( byte j = 0; j < repeatingSignalByteLength; j++ ){ + if( timings[(repeatingSignalByteLength - j) - 1] != EEPROM.read((positionOfLastSignalEnd - (repeatingSignalByteLength*i)) - j ) ){ + areTheyTheSame = false; + break; + } + } + if( areTheyTheSame == true ){ // We've compared the entire signal, and.. they are the same! + Serial.println(F("Match")); + Serial.print(F("SEND: Toggle ")); Serial.print(99 + (amountOfStoredSignals - amountOfStoredReplayableSignalsScan)); Serial.print(F(" to ")); Serial.println(i); + + if( bitRead(descriptionData, DESCRIPTION_ON_OFF) ){ // This is an on-off type detection, so we just toggle it to the correct position. + //Serial.println(F("-simple-on-off-toggle")); + connectedToNetwork = send(detectmsg.setSensor(99 + (amountOfStoredSignals - amountOfStoredReplayableSignalsScan)).set(!i),1); wait(RADIO_DELAY); // This sends the found value to the server. If the signal is an on-off version, it sends the correct value (which needs to be inversed, hence the !i). It also asks for a receipt (the 1 at the end), so that it acts as a network status detection at the same time. + + } + else{ // This is a simple single trigger-type detection. + //Serial.println(F("-simple single trigger, will go back to off by itself.")); + connectedToNetwork = send(detectmsg.setSensor(99 + (amountOfStoredSignals - amountOfStoredReplayableSignalsScan)).set(1),1); wait(RADIO_DELAY); // This sends the found value to the server. If the signal is an on-off version, it sends the correct value (which needs to be inversed, hence the !i). It also asks for a receipt (the 1 at the end), so that it acts as a network status detection at the same time. + wait(2000); + send(detectmsg.setSensor(99 + (amountOfStoredSignals - amountOfStoredReplayableSignalsScan)).set(0),1); wait(RADIO_DELAY); // ... and turn if back off again at the controller. + } + updateDisplay(MATCH); + whatWeCameHereFor = amountOfStoredSignals; // Sending back the index of this signal. + + break; // Just in case that this is a double signal, then we don't want a second loop to overrule this result. + } + } + } // end of detectable + } // end of 'is a signal' // end of checking a stored signal. + } // end of while loop, so the entire EEPROM has now been scanned. + amountOfStoredReplayableSignals = amountOfStoredReplayableSignalsScan; + return whatWeCameHereFor; +} + + +// +// STORE SIGNAL +// + +boolean writeSignalToEeprom() +{ + Serial.println(F("__Storing")); + byte signalDescriber = 0; // This will hold all the setings for this signal. The 'Description byte' is basically a settings switchboard that describes the signal. It helps to keep the required eeprom storage low. +#if KEYPAD_BUTTON_COUNT > 0 + if( (state == COPYING_ON || state == COPYING_SIMPLE) && amountOfStoredReplayableSignals == KEYPAD_BUTTON_COUNT - 1 ){ + updateDisplay(NO_MORE_FREE_BUTTONS); // Warn the user: there are no more physical buttons left. + } +#endif + // TODO?: If this is an on-off situation, maybe get the previous signal from storage and make sure that the meta data is similar? By doing a check we could make sure no rogue signals accidentally interfere with the recording proces. The odds of that happening are pretty slim though.. + byte spaceNeeded = 2; // We calculate how much space we need to store everything, and then check if it will still fit in memory. The minimal metadata length we can possibly start with is 2. One byte for signal length, and another for the signal describer + +/* + * THE COMPLETE DATA TO STORE + * This is what a stored signal in the EEPROM looks like: + * + * byte 0. How many of the eeprom's bytes are used to store the signal. + * byte 1. Signal describer. Stores settings. See below for details. + * + * The next part stores the metaData array. For 'recognise only' signals this is skipped. + * byte 3. Most popular bit-duo part A. Together they are binary 0. (only stored if the signal should be replayed) + * byte 4. Most popular bit-duo part B. Together they are binary 0. (only stored if the signal should be replayed) + * byte 5. Second most popular bit-duo part A. Together they are binary 1. (optional, depending on signal and state) + * byte 6. Second most popular bit-duo part B. Together they are binary 1. (optional, depending on signal and state) + * byte 7. First anomaly duo. (optional, depending on signal and state) + * byte 8. First anomaly duo. (optional, depending on signal and state) + * byte 9. Second anomaly duo. (optional, depending on signal and state) + * byte 10. Second anomaly duo. (optional, depending on signal and state) + * + * byte 11 and onwards: stores the actual repeating part of the signal. Each byte has 8 bits, and these 8 bits can be expanded into the original signal. + * + * If this is an on+off signal, then it will be appended, but only it's repeating signal data. + * + * + * Signal describer byte: + * bit 7. - Since there is a spacer, is it 2 bit (0) or 4 bit (1) spacer? + * bit 6. - Is there a spacer anomaly? Usually there is. No (0) or Yes (1). + * bit 5. - Can one of the duo's be reconstructed from the other one? No (1) or yes (0). If yes, then we can save 2 bytes, and must also store what type of reconstruction can be done (there are two options fo this). + * bit 4. - Simple read-only signal (0), or are we planning to transmit it (1)? If it's read-only, then we only store the signal spacer, signal description (so we can now it's a simple signal), and the repeating signal array. A normal, simple 8 byte signal would thus take 10 bytes of storage on the EEPROM. + * bit 3. - Is this a singular signal (0), or combined on/off signal (1). (The repeating signal payload is then doubled, and should later be split in half.) + * bit 2. - Currently not used. Perhaps in the future this could be used to store if a replayable signal should also operate as a recognise-only signal + * bit 1. - What type of duo reconstruction can be done? This depends on bit 5 being set to 0, indicating a duo reconstruction should be performed. If they are mirrored, then (0) is stored. Is the first duo just twice the first timing of the second duo (e.g. if it is 2&2 + 2&7), then a 1 is stored. + * bit 0. - Set to 1 if the last byte of the repeating pattern is only half used. + */ +#ifdef DEBUG + Serial.print(metaData[0]); Serial.println(F(" most popular bit-duo part A (0)")); + Serial.print(metaData[1]); Serial.println(F(" most popular bit-duo part A (0)")); + Serial.print(metaData[2]); Serial.println(F(" most popular bit-duo part B (1)")); + Serial.print(metaData[3]); Serial.println(F(" most popular bit-duo part B (1)")); + Serial.print(metaData[4]); Serial.println(F(" spacer anomaly 1")); + Serial.print(metaData[5]); Serial.println(F(" spacer anomaly 2")); + Serial.print(metaData[6]); Serial.println(F(" spacer anomaly 3")); + Serial.print(metaData[7]); Serial.println(F(" spacer anomaly 4")); +#endif + if( state != COPYING_OFF && state != LEARNING_OFF ){ // We only create the description bit on the ON part of an ON/OFF signal. The OFF part is just directly appended to the eeprom later. + // IS THE LAST BYTE SPLIT? (Uneven number of tri-bits) + if( lastByteIsSplit ){ + bitWrite(signalDescriber, DESCRIPTION_HALFBYTE , 1); // We use this position to remember if there is an even or uneven amount of 'nibbles' (a nibble is half a byte). + } + // IS THE LAST ANOMALY "inside" the last nibble, or does it follow it? + if( anomalyInside ){ + bitWrite(signalDescriber, DESCRIPTION_ANOMALY_INSIDE , 1); // We use this position to remember if there is an even or uneven amount of 'nibbles' (a nibble is half a byte). + } + // ARE WE STORING AN ON/OFF SIGNAL? + if( state == COPYING_ON || state == LEARNING_ON ){ + bitWrite(signalDescriber, DESCRIPTION_ON_OFF , 1); // We use this position to remember if this is an ON/OFF signal. + spaceNeeded = spaceNeeded + (repeatingSignalByteLength * 2); // If this is an on-off signal, then we will need to store the repeating part twice. + } else { + spaceNeeded = spaceNeeded + repeatingSignalByteLength; + } + // SIMPLE OR REPLAY? If it's simple, then all additional description bits can stay at 0. + if( state == COPYING_ON || state == COPYING_SIMPLE ){ + bitWrite(signalDescriber, DESCRIPTION_REPLAYABLE , 1); // We use this position to remember if this is a recognise-only signal(0), or if it's a signal that can be replayed(1). + if( metaData[2] == metaData[1] && metaData[3] == metaData[0] ){ + //Serial.println(F("A duo can be reconstructed. Duo 2 is the mirror image of duo 1.")); + spaceNeeded = spaceNeeded + 2; + } + else if( metaData[0] == metaData[2] == metaData[3] ){ + //Serial.println(F("A duo can be reconstructed. Duo 2 is just twice the first timing of duo 1")); + bitWrite(signalDescriber, DESCRIPTION_DUO_RECONSTRUCTION_TYPE , 1); + spaceNeeded = spaceNeeded + 2; + } + else{ + // There is no clever way to store the duo's using less storage space. + bitWrite(signalDescriber, DESCRIPTION_UNRECONSTRUCTABLE_DUO , 1); // We use this position to remember if a duo can be reconstructed (0) or not (1). + spaceNeeded = spaceNeeded + 4; + } + // ANOMALY? + if( metaData[4] != 0 && metaData[5] != 0 ){ + bitWrite(signalDescriber, DESCRIPTION_ANOMALY , 1); // We use this position to remember that there is at least one part anomaly. + spaceNeeded = spaceNeeded + 2; + if( metaData[6] != 0 && metaData[7] != 0 ){ + bitWrite(signalDescriber, DESCRIPTION_LARGE_ANOMALY , 1); // We use this position to remember that this is a double size anomaly. + spaceNeeded = spaceNeeded + 2; + } + } + +#ifdef DEBUG + Serial.print(F("Signal description byte (")); Serial.println(signalDescriber); + for (byte i = 0; i < 8; i++) { + Serial.print(i); Serial.print(F(" = ")); Serial.println(bitRead(signalDescriber,i)); + } + Serial.print(F("Storage space needed: ")); Serial.println(spaceNeeded); + Serial.print(F("Storage space left: ")); Serial.println( EEPROM.length() - positionOfLastSignalEnd ); +#endif + } + } // End of check if we're storing a single or double signal. + if( spaceNeeded > 0 && positionOfLastSignalEnd + spaceNeeded < EEPROM.length() ){ // If there is enough space to store the new signal. E2END+1 would also work instead of EEPROM.length(); + Serial.println(F("Enough space left")); + if( state != COPYING_OFF && state != LEARNING_OFF ){ + // Storing a new signal. Moving pointer to the next free space in the EEPROM. + positionOfLastSignalEnd++; + EEPROM.update(positionOfLastSignalEnd, spaceNeeded); // Here we store the length of all the data associated with this recording session. + //Serial.print(positionOfLastSignalEnd); Serial.print(F(" ~~> "));Serial.print(spaceNeeded); Serial.println(F(" (length)")); + positionOfLastSignalEnd++; + EEPROM.update(positionOfLastSignalEnd, signalDescriber); // Here we store the signal describer which holds all the settings needed to recreate this signal. + //Serial.print(positionOfLastSignalEnd); Serial.print(F(" ~~> "));Serial.print(signalDescriber); Serial.println(F(" (describer)")); + /* + Here we store the metaData array. This is how this loop works: + 0 -> bit4 (if replaying) -> metadata 1&2 must be stored + 1 -> bit5 (if a duo can not be cleverly reconstructed from the other) -> metadata 3&4 must be stored + 2 -> bit6 (if there is at least one part anomaly -> metadata 5&6 must be stored + 3 -> bit7 (if second part anomaly exists) -> metadata 7&8 must be stored. + */ + if( bitRead(signalDescriber, DESCRIPTION_REPLAYABLE) == 1 ){ // If we want to be able to replay this signal, then we should store the meta data. + for( byte i = 0; i < 4; i++ ){ // Looping over the signalDescriber and the metaData array at the same time. + if( bitRead(signalDescriber, i+4) == 1 ){ // 4, 5, 6, 7 + positionOfLastSignalEnd++; + EEPROM.update(positionOfLastSignalEnd, metaData[i*2]); + //Serial.print(positionOfLastSignalEnd); Serial.print(F(" ~> ")); Serial.print(metaData[i*2]); Serial.println(F(" (meta)")); + positionOfLastSignalEnd++; + EEPROM.update(positionOfLastSignalEnd, metaData[i*2 + 1]); + //Serial.print(positionOfLastSignalEnd); Serial.print(F(" ~> ")); Serial.print(metaData[i*2 + 1]); Serial.println(F(" (meta)")); + } + } + } + } // End of check if this is part two of an on/off signal. + // Add the repeating part of the signal to the storage. + for( byte i = 0; i < repeatingSignalByteLength; i++ ){ + positionOfLastSignalEnd++; + Serial.print(positionOfLastSignalEnd); Serial.print(F(" ~~> "));Serial.println(timings[i]); + EEPROM.update(positionOfLastSignalEnd, timings[i]); + } + if( state != COPYING_OFF && state != LEARNING_OFF ){ + amountOfStoredSignals++; // On/off signals only count as one because they would be attached to one button. + } + } + else{ + return false; // Unable to store the signal. It wasn't long enough or there was not enough space for it. + } + send(textmsg.setSensor(DEVICE_STATUS_ID).set( F("Signal stored OK") )); + return true; // Succesfully stored the signal +} + + + +// +// REPLAY +// + +void replay(byte signalNumber, boolean onOrOff) +{ + Serial.print(F("__replaying_")); Serial.println(signalNumber); + state = REPLAYING; + //boolean onOrOff = 0; + buttonPressed = signalNumber; + repeatingPatternLength = 0; + if( signalNumber == 0 ){ return; } // We cannot play signal 0. Just a (superfluous) safeguard. + byte storedSignalLength = scanEeprom(); + //Serial.print(F("storedSignalLength received from scanEeprom function: ")); Serial.println(storedSignalLength); + if( storedSignalLength && amountOfStoredReplayableSignals > 0 ){ + + saveState(signalNumber, onOrOff); // We save the new current toggle state in the eeprom too. + send(buttonmsg.setSensor(signalNumber + 9).set(onOrOff)); wait(RADIO_DELAY); // Tell the controller in what state the child is. + send(textmsg.setSensor(DEVICE_STATUS_ID).set( F("Playing..") )); + updateDisplay(REPLAYING); + for( byte i = 0; i < 8; i++ ){ + metaData[i] = 0; // Reset all metadata values. + } + byte eepromReadPosition = 4; // Where in the eeprom signal we are reading back the data. + byte restoredSignalLength = 0; // Pointer to where in the timings array the signal has been reconstructed. + byte signalDescription = EEPROM.read(startPosition+1); + lastByteIsSplit = bitRead(signalDescription, DESCRIPTION_HALFBYTE); + anomalyInside = bitRead(signalDescription, DESCRIPTION_ANOMALY_INSIDE); + +#ifdef DEBUG + Serial.print(F("Data from EEPROM (length: ")); Serial.println(storedSignalLength); + for( int i = startPosition; i <= endPosition; i++ ){ + Serial.println( EEPROM.read(i) ); + } +#endif + +#if defined HAS_BASIC_OLED_SCREEN && KEYPAD_BUTTON_COUNT > 0 && defined DEBUG + // Show on the screen if the ON or OFF signal is being played. + if( bitRead(signalDescription, DESCRIPTION_ON_OFF) ){ + oled.set1X(); + oled.setCursor(0,6); + oled.print(F("part ")); + oled.print(buttonsToggleStatus[signalNumber] + 1); + } +#endif + + repeatingPatternLength = storedSignalLength - 4;// This indicates how many of the bytes in the eeprom describe the actual repeating signal. + metaData[0] = EEPROM.read( startPosition + 2 ); // This duo is always reconstructed from the eeprom data. + metaData[1] = EEPROM.read( startPosition + 3 ); + if( bitRead(signalDescription,DESCRIPTION_UNRECONSTRUCTABLE_DUO) == 0 ){ // The second duo must be reconstructed because an eeprom space saving measure was used. + if( bitRead(signalDescription,DESCRIPTION_UNRECONSTRUCTABLE_DUO) == 0 ){ // The second duo is a mirror image of the first duo. To reconstruct the second duo we just take the first duo and flip it. + metaData[2] = metaData[1]; + metaData[3] = metaData[0]; + } + else{ // The second duo is just twice the first timing of the first duo. + metaData[2] = metaData[0]; + metaData[3] = metaData[0]; + } + } + else { + metaData[2] = EEPROM.read( startPosition + eepromReadPosition ); // If the second duo cannot be reconstructed from the first, then the first timing of the second duo can always be found at position 4 in the eeprom. + eepromReadPosition++; + metaData[3] = EEPROM.read( startPosition + eepromReadPosition ); // If the second duo cannot be reconstructed from the first, then the second timing of the second duo can always be found at position 5 in the eeprom. + eepromReadPosition++; + repeatingPatternLength = repeatingPatternLength - 2; // Since two of the bytes in eeprom were there to reconstruct the meta data, the actual repeating part must be a little shorter. + } + +#ifdef DEBUG + Serial.println(F("Reconstructed duo metaData:")); + for( byte w = 0; w < 4; w++ ){ + Serial.println(metaData[w]); + } +#endif + + // The anomaly can be placed directly into the timings array. + if( bitRead(signalDescription,DESCRIPTION_ANOMALY) == 1 ){ // Reconstruct part 1 of anomaly. + repeatingPatternLength = repeatingPatternLength - 2; // How many bytes in the eeprom actually describe the repeating signal is now reduced, since some of those bytes described the anomaly. + timings[restoredSignalLength] = EEPROM.read( startPosition + eepromReadPosition ); // Eeprom position here is 4 or 6, depending on whether the second repeating duo was a reconstructable or not. + restoredSignalLength++; + eepromReadPosition++; + timings[restoredSignalLength] = EEPROM.read( startPosition + eepromReadPosition ); // Eeprom position here is 5 or 7, depending on whether the second repeating duo was a reconstructable or not. + restoredSignalLength++; + eepromReadPosition++; +#ifdef DEBUG + Serial.println(F("Reconstructed betweenSpace anomaly: ")); + Serial.print(F("0 => ")); Serial.println(timings[0]); + Serial.print(F("1 => ")); Serial.println(timings[1]); +#endif + } + if( bitRead(signalDescription,DESCRIPTION_LARGE_ANOMALY) == 1 ){ // Reconstruct part 2 of anomaly. + repeatingPatternLength = repeatingPatternLength - 2; // Since two of the bytes in eeprom were there to reconstruct the meta data, the actual repeating part must be a little shorter. + timings[restoredSignalLength] = EEPROM.read( startPosition + eepromReadPosition ); + eepromReadPosition++; + restoredSignalLength++; + timings[restoredSignalLength] = EEPROM.read( startPosition + eepromReadPosition ); + eepromReadPosition++; + restoredSignalLength++; +#ifdef DEBUG + Serial.print(F("2 => ")); Serial.println(timings[2]); + Serial.print(F("3 => ")); Serial.println(timings[3]); +#endif + } +#ifdef DEBUG + Serial.println(); + Serial.print(F(">> restoredSignalLength ")); Serial.println(restoredSignalLength); Serial.println(); +#endif + + if( bitRead(signalDescription, DESCRIPTION_ON_OFF) ){ // If it's an on+off signal then the repeating part should be cut in half to get the actual length, since they are stored back to back in the eeprom. + repeatingPatternLength = repeatingPatternLength / 2; + } + // Looping over the EEPROM, with some special construction that switches which signal should be reconstructed based on the toggle state of the button. + for( int i = (endPosition - repeatingPatternLength * (1+onOrOff)) + 1; i <= endPosition - (repeatingPatternLength * onOrOff); i++ ){ + // Here we can reconstruct the original signal. +#ifdef DEBUG + Serial.print(F(" byte ")); Serial.print(i); Serial.print(F(" = ")); Serial.println( EEPROM.read(i) ); +#endif + for( byte j = 0; j < 8; j++ ){ +#ifdef DEBUG + Serial.print(restoredSignalLength); Serial.print(F(" => ")); Serial.println( metaData[ bitRead(EEPROM.read(i),j) * 2 ] ); +#endif + timings[restoredSignalLength] = metaData[ bitRead(EEPROM.read(i),j) * 2 ]; + restoredSignalLength++; +#ifdef DEBUG + Serial.print(restoredSignalLength); Serial.print(F(" => ")); Serial.println( metaData[ bitRead(EEPROM.read(i),j) * 2 + 1] ); +#endif + timings[restoredSignalLength] = metaData[ bitRead(EEPROM.read(i),j) * 2 +1]; + restoredSignalLength++; + } + //Serial.println(); + } + // If the last byte is only half used, then we should not transmit the last half byte. + if( lastByteIsSplit ){ +#ifdef DEBUG + Serial.println(F("Last byte is split")); +#endif + restoredSignalLength = restoredSignalLength - 8; // Since the last 4 bits of the byte didn't actually contain valid data, we 'cut it off' from the part that we will transmit. + } + + // If the anomaly is 'inside' the byte or nibble, then the anomaly length must be subtracted from the total restored length. + if( anomalyInside ){ + Serial.println(F("Anomaly inside")); + if( bitRead(signalDescription,DESCRIPTION_ANOMALY) == 1 ){ + restoredSignalLength = restoredSignalLength - 2; + } + if( bitRead(signalDescription,DESCRIPTION_LARGE_ANOMALY) == 1 ){ + restoredSignalLength = restoredSignalLength - 2; + } + } +#ifdef DEBUG + Serial.print(F(">> restoredSignalLength ")); Serial.println(restoredSignalLength); Serial.println(); + for( int j = 0; j < restoredSignalLength; j++ ){ + Serial.print( timings[j] ); Serial.print(F(",")); + if( j % 4 == 3 ){ Serial.println(); } + if( j % 32 == 31 ){ Serial.println(); } + } +#endif + + // Finally, play the new timing a few times. + Serial.println(F("REPLAYING")); + for( byte i = 0; i < 8; i++ ){ // Sending the pattern a few times. + boolean high = false; + for( int j = 0; j < restoredSignalLength; j++ ){ + if( high ){ + digitalWrite(TRANSMITTER, LOW); + high = false; + } + else{ + digitalWrite(TRANSMITTER, HIGH); + high = true; + } + interval = timings[j] * GRANULARITY; + delayMicroseconds(interval); + } + } + + digitalWrite(TRANSMITTER, LOW); // Just to be safe + wait(300); + +#ifdef DEBUG + wait(300); + Serial.println(F("NOW REPLAYING REVERSED")); +#endif + +#ifdef DEBUG + for( byte i = 0; i < 8; i++ ){ // Sending the pattern a few times. + boolean high = false; + for( int j = 0; j < restoredSignalLength; j++ ){ + if( high ){ + digitalWrite(TRANSMITTER, HIGH); + high = false; + } + else{ + digitalWrite(TRANSMITTER, LOW); + high = true; + } + interval = timings[j] * GRANULARITY; + delayMicroseconds(interval); + } + } + + digitalWrite(TRANSMITTER, LOW); // Just to be safe +#endif + +#ifdef DEBUG + wait(100); + Serial.println(F("DONE REPLAYING")); +#endif + } // End of check if at least one replayable signal is stored in the EEPROM. +#if KEYPAD_BUTTON_COUNT > 0 + else{ + //Serial.println(F("No signal to replay stored yet!")); + updateDisplay(NO_SIGNAL_STORED_YET); // Only useful to show this if there are physical buttons that don't have a replayable signal attached to them yet. + } +#endif + send(textmsg.setSensor(DEVICE_STATUS_ID).set( F("OK") )); + state = LISTENING; + //if( playlist_position > 0){ + // playlist_position--; + //} +} // End of replay function + + +// +// UPDATE DISPLAY +// + +void updateDisplay(byte currentStatus) // Show info on the display +{ +#ifdef HAS_TOUCH_SCREEN +#ifdef DEBUG + Serial.println(); Serial.print(F("UPDATE DISPLAY (state: ")); Serial.println(state); +#endif + clearReceivedBuffer(); + turnOnScreen(); // If the screen is turned off, it will be turned on again. + + if( currentStatus == LISTENING ){ + showTouchButtons(); + } + else { + if( currentStatus == MATCH || currentStatus == REPLAYING ){ + wait(50); + roundedRectangle( 0, ((howManyReplayButtonsWillFitOnScreen + 1) * BUTTON_HEIGHT), TOUCHSCREEN_WIDTH, TOUCHSCREEN_HEIGHT - ((howManyReplayButtonsWillFitOnScreen + 1) * BUTTON_HEIGHT), 0, 64640 ); // Orange rectangle. + wait(50); + setCur(BUTTON_PADDING,BUTTON_PADDING+((howManyReplayButtonsWillFitOnScreen + 1) * BUTTON_HEIGHT) ); + wait(50); + if( currentStatus == MATCH ){ + writeText(START_OF_MENU_DELETE_ALL + MATCH); + wait(2000); + } + else if( currentStatus == REPLAYING ){ + writeText(START_OF_MENU_DELETE_ALL + REPLAYING); + displayNumber( buttonPressed ); + wait(2000); + } + roundedRectangle( 0, ((howManyReplayButtonsWillFitOnScreen + 1) * BUTTON_HEIGHT), TOUCHSCREEN_WIDTH, TOUCHSCREEN_HEIGHT - ((howManyReplayButtonsWillFitOnScreen + 1) * BUTTON_HEIGHT), 0, 0 ); // Black rectangle + //Serial.print(F("amountOfStoredReplayableSignals:")); Serial.println(amountOfStoredReplayableSignals); + //Serial.print(F("howManyReplayButtonsWillFitOnScreen:")); Serial.println(howManyReplayButtonsWillFitOnScreen); + if( amountOfStoredReplayableSignals > howManyReplayButtonsWillFitOnScreen ){ + setCur(BUTTON_PADDING,BUTTON_PADDING+((howManyReplayButtonsWillFitOnScreen + 1) * BUTTON_HEIGHT)); + basicCommand(more); + } + return; + } + + // If we are copying a signal, show the cancel button. + if( state > MENU_NEW && state < MENU_DELETE_ALL + 5 ){ +#ifdef DEBUG_SCREEN + Serial.println(F("BC: fill_black")); +#endif + basicCommand(fill_black); + roundedRectangle( 0, 0, (int)TOUCHSCREEN_WIDTH / 2, BUTTON_HEIGHT, 10, 31 ); // Draw a blue (31) rounded rectangle, with a 10px radius. + setCur(BUTTON_PADDING,BUTTON_PADDING); + writeText(0); // 0 is 'cancel'. + } + + setCur(BUTTON_PADDING,BUTTON_PADDING+BUTTON_HEIGHT); + + // Copying + if( currentStatus == COPYING_SIMPLE || currentStatus == LEARNING_SIMPLE ){ + writeText(START_OF_MENU_DELETE_ALL + PLAY_SIMPLE_SIGNAL); + } + else if( currentStatus == COPYING_ON || currentStatus == LEARNING_ON ){ + writeText(START_OF_MENU_DELETE_ALL + PLAY_ON_SIGNAL); + } + else if( currentStatus == COPYING_OFF || currentStatus == LEARNING_OFF){ + writeText(START_OF_MENU_DELETE_ALL + PLAY_OFF_SIGNAL); + } + + else if( currentStatus == SIGNAL_STORED ){ + writeText(START_OF_MENU_DELETE_ALL + SIGNAL_STORED); + wait(3000); + } + + // Errors + else if( currentStatus == BAD_SIGNAL ){ + writeText(START_OF_MENU_DELETE_ALL + BAD_SIGNAL); + } + else if( currentStatus == OUT_OF_SPACE ){ + writeText(START_OF_MENU_DELETE_ALL + OUT_OF_SPACE); + wait(3000); + } + + // Other + else if( currentStatus == DELETE_LAST ){ + writeText(START_OF_MENU_DELETE_ALL + DELETED_LAST); + wait(3000); + } + else if( currentStatus == DELETED_ALL ){ + writeText(START_OF_MENU_DELETE_ALL + DELETED_ALL); + wait(3000); + } + } + +#endif + +#ifdef HAS_BASIC_OLED_SCREEN + oled.clear(); // Clear the display + displayNetworkStatus(); // Update the network connection icon. + oled.set2X(); // Switch font size + oled.setCursor(0,2); + + // Often used + if( currentStatus == LISTENING ){ + oled.print(F("LISTENING..")); // Call this "Scanning" instead? + oled.set1X(); + oled.setCursor(0,5); + oled.print(F("Detect signals: ")); oled.print(amountOfStoredSignals - amountOfStoredReplayableSignals); + oled.setCursor(0,6); + oled.print(F("Replay signals: ")); oled.print(amountOfStoredReplayableSignals); + + } + else if( currentStatus == PROCESSING ){ + oled.print(F("Processing")); + } + else if( currentStatus == MATCH ){ + oled.print(F("Found")); + oled.setCursor(0,5); + oled.print(F("match!")); + wait(1500); + } + else if( currentStatus == REPLAYING ){ + oled.print(F("Playing ")); oled.print(buttonPressed); + } + + // Copying + else if( currentStatus == COPYING_ON || currentStatus == LEARNING_ON ){ + oled.print(F("Play ON")); + oled.set1X(); + oled.setCursor(0,6); + oled.print(F(">cancel")); + } + else if( currentStatus == COPYING_OFF || currentStatus == LEARNING_OFF){ + oled.print(F("Play OFF")); + oled.set1X(); + oled.setCursor(0,6); + oled.print(F(">cancel")); + } + else if( currentStatus == COPYING_SIMPLE || currentStatus == LEARNING_SIMPLE ){ + oled.print(F("READY!")); + oled.set1X(); + oled.setCursor(0,4); + oled.print(F("Start your signal")); + oled.set1X(); + oled.setCursor(0,6); + oled.print(F(">cancel")); + } + else if( currentStatus == SIGNAL_STORED ){ + oled.print(F("SIGNAL")); + oled.setCursor(0,4); + oled.print(F("STORED")); + if( state == COPYING_OFF || state == LEARNING_OFF ){ + oled.set1X(); + oled.setCursor(0,7); + oled.print(F("Stored signals: ")); oled.print(amountOfStoredSignals); + } + wait(2000); + } + + // Errors + else if( currentStatus == BAD_SIGNAL ){ + oled.print(F("BAD SIGNAL")); + oled.set1X(); + oled.setCursor(0,5); + oled.print(F("TRY AGAIN")); + } + else if( currentStatus == OUT_OF_SPACE ){ + oled.print(F("Out of space!")); + wait(3000); + } + else if( currentStatus == NO_SIGNAL_STORED_YET ){ + oled.print(F("Empty")); + wait(2000); + } + else if( currentStatus == NO_MORE_FREE_BUTTONS ){ + oled.print(F("No buttons left")); + oled.set1X(); + oled.setCursor(0,0); + oled.print(F("NOTICE")); + oled.setCursor(0,5); + oled.print(F("You can only replay")); + oled.setCursor(0,6); + oled.print(F("it via the app.")); + wait(3000); + } + + // Other + else if( currentStatus == DELETE_LAST ){ + oled.print(F("DELETED")); + oled.setCursor(0,5); + oled.print(F("ONE SIGNAL")); + wait(3000); + } + else if( currentStatus == DELETED_ALL ){ + oled.print(F("MEMORY")); + oled.setCursor(0,5); + oled.print(F("CLEAR")); + wait(3000); + } +#ifdef DEBUG + else{ + oled.print(F("??? ")); oled.print(currentStatus); + } +#endif +#endif +} // End of update display function + + + +// +// MENUS +// + + +byte showMenu() // Lets the user select which state to enter. +{ + Serial.println(F("__MENU")); + //if( state == LISTENING ){ + state = MENU_MAIN; + //} + boolean stayInMenu = true; // We stay in the menu while this is true + +#ifdef HAS_TOUCH_SCREEN + byte menuOffset = 0; + byte menuItems = 4; + wait(100); + clearReceivedBuffer(); + touchY = TOUCHSCREEN_HEIGHT; + touched = true; +#ifdef DEBUG_SCREEN + Serial.print(F("touch_screen_serial.available:")); Serial.println( touch_screen_serial.available() ); +#endif + + while( stayInMenu ){ + menuOffset = 0; + wait(50); + readResponse(); // check for new touchscreen input. + if( touched == true ){ + touched = false; + buttonPressed = touchScreenButtonPress(); +#ifdef DEBUG_SCREEN + Serial.print(F("touched (")); Serial.println(buttonPressed); +#endif + if( buttonPressed == 0 ){ // No matter where in the menu we are, when the cancel button is pressed the menu is closed. +#ifdef DEBUG_SCREEN + Serial.println(F("Cancel button")); +#endif + state = LISTENING; + stayInMenu = false; // Exit menu + break; + } + else { +#ifdef DEBUG_SCREEN + Serial.println(F("BC: fill_blue")); +#endif + basicCommand(fill_blue); // Set the background color to blue + setCur(BUTTON_PADDING,BUTTON_PADDING); // Set the text cursor at the top left of the screen + writeText(0); // Display the Cancel item. All menu's have it. + simpleHorizontal(BUTTON_HEIGHT); + + if( state == MENU_MAIN ){ + menuOffset = 1; + if( amountOfStoredSignals > 0 ){ + menuItems = START_OF_MENU_NEW - 1; // 3 items + } + else { + menuItems = START_OF_MENU_NEW - 3; // 1 item. No need to show the delete buttons if there are no stored signals. + } + + if( buttonPressed == 1 ){ + Serial.println(F("entering menu_new")); + state = MENU_NEW; + menuOffset = START_OF_MENU_NEW; + menuItems = START_OF_MENU_DELETE_LAST - START_OF_MENU_NEW; // 4 items + } + if( buttonPressed == 2 ){ + Serial.println(F("entering delete last menu")); + state = MENU_DELETE_LAST; + menuOffset = START_OF_MENU_DELETE_LAST; + menuItems = START_OF_MENU_DELETE_ALL - START_OF_MENU_DELETE_LAST; // 1 item + } + if( buttonPressed == 3 ){ + Serial.println(F("entering delete ALL menu")); + state = MENU_DELETE_ALL; + menuOffset = START_OF_MENU_DELETE_ALL; + menuItems = START_OF_MENU_STATUS_MESSAGES - START_OF_MENU_DELETE_ALL; // 1 item + } + + for( byte i = 0; i < menuItems; i++ ){ + Serial.print(F(">")); Serial.println(i); + setCur(BUTTON_PADDING,BUTTON_PADDING + BUTTON_HEIGHT + (i * BUTTON_HEIGHT) ); + writeText(i + menuOffset); // The offset helps to select the right number which corresponds to a menu item's string in the messages array. + simpleHorizontal(BUTTON_HEIGHT + BUTTON_HEIGHT + (i * BUTTON_HEIGHT)); + } + } + else { // When the user presses a button while in a sub menu. + state = state + buttonPressed; + stayInMenu = false; + break; + } + } + clearReceivedBuffer(); + } + } + +#elif defined (HAS_BASIC_OLED_SCREEN) + byte menuItems = 4; // How many items are shown? Don't forget 'cancel' is always shown. + byte menuPosition = 2; // Current position of the cursor. + wait(100); + + while( stayInMenu ){ +#if KEYPAD_BUTTON_COUNT == 4 + buttonPressed = keypad4(); +#else if KEYPAD_BUTTON_COUNT == 12 + buttonPressed = keypad12(); +#endif + wait(100); + if(buttonPressed != prevButtonState){ + Serial.print(F("[] button pressed: ")); Serial.println(buttonPressed); + if( buttonPressed == KEYPAD_BUTTON_COUNT - 1 ){ // User has pressed the change button. + menuPosition++; + if( menuPosition > menuItems + 1 ){ menuPosition = 2; } // If necessary, loop around back to the top item in the menu. + Serial.print(F("changed to ")); Serial.println(menuPosition); + } + else if( buttonPressed == KEYPAD_BUTTON_COUNT ){ // User has pressed the menu/ok button. + Serial.print(F("select!")); Serial.println(menuPosition); + if( menuPosition == 2 ){ // User pressed cancel. This could move the user back to the main menu, but for simplicity, this currently just exits the menu. + state = 255; + stayInMenu = false; // We can now exit the menu. + } + else if( state == MENU_MAIN ){ // Going into sub-menu. + state = menuPosition * 10; // So the first submenu state is 30, the second submenu is 40, etc. + menuPosition = 2; // Reset the cursor position to the top position. + } + else{ // User is in submenu, so they are making their final selection for a new state. + state = state + menuPosition; // If the submenu state was 30, the final states are 32, 33, etc. + stayInMenu = false; // We can now exit the menu. + } + } + else if( buttonPressed == 0 && prevButtonState == KEYPAD_BUTTON_COUNT ){ + // Here we display the actual menu items. + oled.clear(); + displayNetworkStatus(); + oled.set2X(); + oled.setCursor(0,0); + oled.print(F("MENU")); + oled.set1X(); + oled.setCursor(8,2); + oled.print(F("CANCEL")); + oled.setCursor(8,3); + if( state == MENU_MAIN ){ + oled.print(F("NEW")); + oled.setCursor(8,4); + oled.print(F("DELETE LAST")); + oled.setCursor(8,5); + oled.print(F("DELETE ALL")); + } + else if( state == MENU_NEW ){ + menuItems = 5; // Cancel + the items below. + oled.print(F("Detect single")); + oled.setCursor(8,4); + oled.print(F("Detect On+Off")); + oled.setCursor(8,5); + oled.print(F("Replay single")); + oled.setCursor(8,6); + oled.print(F("Replay On+Off")); + } + else if( state == MENU_DELETE_LAST || state == MENU_DELETE_ALL){ + menuItems = 2; // Cancel + the item below. + oled.print(F("I am sure")); + oled.setCursor(8,4); + } + } + // Add the pointer. + for( byte w = 2; w < 7; w++ ){ // Update the position of the selection cursor on the screen. + oled.setCursor(0,w); + if( menuPosition == w ){ + oled.print(F(">")); + } + else{ + oled.print(F(" ")); + } + } + wait(1); + prevButtonState = buttonPressed; + } + } +#endif +} + + + +// +// RECEIVING DATA FROM THE NETWORK +// + + + +void printRawSignal() // Prints the entire timings array to the serial output. Useful for debugging. +{ + Serial.print(F("__printing_signal_")); Serial.println(edges+1); + for( int j = startPosition; j <= endPosition; j++ ){ + Serial.print(F(",")); Serial.print(timings[j]); + if( j % 16 == 15 ){ Serial.println(); } + } + Serial.println(); +} + + +void clearTimingsArray() +{ +#ifdef DEBUG + Serial.print(F("__Clearing_timings\n-state:")); Serial.println(state); +#endif + memset(timings,0,sizeof(timings)); // Write zeros to timings array. + startPosition = 0; + endPosition = MAXEDGES - 1; + repeatingPatternLength = 255; + edges = 0 - EDGES_TO_SKIP; + signalstart = 0; + bucketsTotal = 0; + captureFinished = false; + validSignal = true; + bucketCount = 0; + signalstart = micros(); +} + + +void displayNetworkStatus() // Show connection icon on the display +{ +#ifdef HAS_TOUCH_SCREEN + if( connectedToNetwork ){ + setCur(TOUCHSCREEN_WIDTH - 30, BUTTON_PADDING ); +#ifdef DEBUG_SCREEN + Serial.println(F("BC: show W icon")); +#endif + basicCommand(w); + } +#endif + +#ifdef HAS_BASIC_OLED_SCREEN + oled.set1X(); + oled.setCursor(122,0); + if( connectedToNetwork ){ + oled.print(F("w")); + } + else{ + oled.print(F(" ")); + } +#endif +} + + +#ifdef DEBUG +int freeRam() { + extern int __heap_start, *__brkval; + int v; + return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); +} +// via https://playground.arduino.cc/Code/AvailableMemory +#endif + +#ifdef HAS_BASIC_OLED_SCREEN && KEYPAD_BUTTON_COUNT != 0 +byte keypad4() +{ + signed int buttonNumber = (analogRead(KEYPAD_PIN) - 200) * (4 - 0 + 1) / (1024 - 200 + 1) + 0; + buttonNumber = constrain(buttonNumber, 0, 4); + return (byte)buttonNumber; +} + +byte keypad12() +{ + int analogValue = analogRead(KEYPAD_PIN); + + if (analogValue < 450) { + return 0; + } else if (analogValue < 500) { + return 1; + } else if (analogValue < 525) { + return 2; + } else if (analogValue < 555) { + return 3; + } else if (analogValue < 585) { + return 4; + } else if (analogValue < 620) { + return 5; + } else if (analogValue < 660) { + return 6; + } else if (analogValue < 705) { + return 7; + } else if (analogValue < 760) { + return 8; + } else if (analogValue < 820) { + return 9; + } else if (analogValue < 890) { + return 10; + } else if (analogValue < 976) { + return 11; + } else { + return 12; + } +} +#endif + + + + + + +// +// BELOW HERE ARE THE TOUCHSCREEN FUNCTIONS +// + +#ifdef HAS_TOUCH_SCREEN + +byte touchScreenButtonPress() +{ + clearReceivedBuffer(); + return ceil(touchY / BUTTON_HEIGHT); +} + +// Show the default interface, with numbered buttons that play signals when pressed. +void showTouchButtons() +{ +#ifdef DEBUG_SCREEN + Serial.println(F("__showTouchButtons ")); + + Serial.println(F("BC: fill_black")); +#endif + basicCommand(fill_black); +#ifdef DEBUG_SCREEN + Serial.println(F("BC: roundedRect")); +#endif + roundedRectangle( 0, 0, (int)TOUCHSCREEN_WIDTH / 2, BUTTON_HEIGHT, (int)BUTTON_HEIGHT / 2, 31 ); // blue rounded rectangle. +#ifdef DEBUG_SCREEN + Serial.println(F("BC: set_cur")); +#endif + setCur(BUTTON_PADDING,BUTTON_PADDING); +#ifdef DEBUG_SCREEN + Serial.println(F("BC: menu")); +#endif + basicCommand(menu); + + state = DISPLAY_REPLAY_BUTTONS; // This state is used to focus the scanEeprom function on displaying the replay buttons. + scanEeprom(); + state = LISTENING; + + // Show 'MORE' button? + if( amountOfStoredReplayableSignals > howManyReplayButtonsWillFitOnScreen ){ + setCur( BUTTON_PADDING, BUTTON_PADDING + BUTTON_HEIGHT + howManyReplayButtonsWillFitOnScreen * BUTTON_HEIGHT ); + basicCommand(more); + } + displayNetworkStatus(); +} + + + +void simpleHorizontal(int y) // Draw a horizontal line on the screen. +{ + +#ifdef DEBUG_SCREEN + Serial.println(F("SIMPLEHORIZONTAL:")); + Serial.print(F("y: ")); Serial.println(y); +#endif + byte command[12] = {0x7E, 0x0A, 0x23, 0,0, highByte(y), lowByte(y), 0,240, 255, 255, 0xEF,}; + for( int i=0; i < sizeof(command); i++ ){ + touch_screen_serial.write( command[i] ); + //Serial.print(command[i],HEX); Serial.print(F(" ")); + } + waitForResponse(); +} + + +void roundedRectangle(int x, int y, int w, int h, int r, int c) // Draw a pixel on the screen. +{ + +#ifdef DEBUG_SCREEN + Serial.println(F("ROUNDEDRECTANGLE")); + //Serial.print(F("x:")); Serial.println(x); // top-right x-position + //Serial.print(F("y:")); Serial.println(y); // top-left y-position + //Serial.print(F("w:")); Serial.println(w); // width + //Serial.print(F("h:")); Serial.println(h); // height + //Serial.print(F("r:")); Serial.println(r); // radius + //Serial.print(F("c:")); Serial.println(c); // color +#endif + byte command[16] = {0x7E, 0x0E, 0x2C, highByte(x), lowByte(x), highByte(y), lowByte(y), highByte(w), lowByte(w), highByte(h), lowByte(h) - 2, highByte(r), lowByte(r), highByte(c), lowByte(c), 0xEF,}; + + for( int i=0; i < sizeof(command); i++ ){ + touch_screen_serial.write( command[i] ); + //Serial.print(command[i],HEX); Serial.print(F(" ")); + } + waitForResponse(); +} + + + +// This function writes text to the screen. You can use the setCur function to place the cursor in the desired position first. +void writeText(byte textID) +{ +#ifdef DEBUG_SCREEN + Serial.println(F("WRITETEXT")); +#endif + byte string_length = strlen_P(MessageTable[textID].Description); + + touch_screen_serial.write( 0x7E ); + touch_screen_serial.write( string_length + 2); + touch_screen_serial.write( 0x11 ); + for( byte i=0; i < string_length; i++ ){ + char char_byte = pgm_read_byte(MessageTable[textID].Description + i); + //Serial.print("-"); Serial.print(char_byte); + touch_screen_serial.write( char_byte ); + } + touch_screen_serial.write( 0xEF ); + + waitForResponse(); +} + +#ifdef DEBUG_SCREEN +void drawPix(int x, int y, int c) // Draw a pixel on the screen. +{ + Serial.println(F("DRAWPIX:")); + Serial.print(F("x: ")); Serial.println(x); + Serial.print(F("y: ")); Serial.println(y); + Serial.print(F("c: ")); Serial.println(c); + byte command[10] = {0x7E, 0x08, 0x21, highByte(x), lowByte(x), highByte(y), lowByte(y), highByte(c), lowByte(c), 0xEF,}; + for( int i=0; i < sizeof(command); i++ ){ + touch_screen_serial.write( command[i] ); + //Serial.print(command[i],HEX); Serial.print(F(" ")); + } + waitForResponse(); +} +#endif + + +// This function places the text cursor anywhere on the screen. +void setCur(int x, int y) +{ +#ifdef DEBUG_SCREEN + Serial.println(F("SETCUR")); + Serial.print(F("x: ")); Serial.println(x); + Serial.print(F("y: ")); Serial.println(y); +#endif + byte command[8] = {0x7E, 0x06, 0x01, highByte(x), lowByte(x), highByte(y), lowByte(y), 0xEF,}; + for( int i=0; i < sizeof(command); i++ ){ + touch_screen_serial.write( command[i] ); + //Serial.print(command[i],HEX); Serial.print(F(" ")); + } + waitForResponse(); +} + + +// This function outputs a variable number to the screen. It can show negative and positive numbers. It cannot show floats. +void displayNumber(signed int number) +{ +#ifdef DEBUG_SCREEN + Serial.print(F("DISPLAYNUMBER (")); Serial.println(number); +#endif + byte command[8] = {0x7E, 0x06, 0x13, 0x01, 0x0A, highByte(number), lowByte(number), 0xEF,}; + for( int i=0; i < sizeof(command); i++ ){ + touch_screen_serial.write( command[i] ); + //Serial.print(command[i],HEX); Serial.print(F(" ")); + } + waitForResponse(); +} + + +// This function reads the serial data (if available) from the screen. +void readResponse() +{ + volatile int availableSerialCount = touch_screen_serial.available(); + //Serial.println(F("READRESPONSE")); + if( availableSerialCount < 5 ){ + return; + } + + boolean savingMessage = false; // When touch message, this starts recording data into an array for analysis. + //byte metaData[metadataArraySize]; // An array to store the received serial data // TODO: use the metadata array instead. It's just as long, and their uses don't overlap. + byte metaDataPosition = 0; // The metaData array is recycled: here is holds incoming serial data. + byte startMarker = 0x7E; // Any response from the screen will start with this. + byte endMarker = 0xEF; // Any response from the screen will end with this. + byte rc; // Hold the byte form the serial stream that we're examining. + + + + byte c = touch_screen_serial.peek(); + //Serial.print(F("SOME SERIAL DATA. Available:")); Serial.println( availableSerialCount ); + //Serial.print(F("Peek:")); Serial.println( c ); + if( c != startMarker ){ + rc = touch_screen_serial.read(); + Serial.print(F("throwing away left over touch_screen_serial byte:")); Serial.println(rc); + return; + } + +#ifdef DEBUG_SCREEN + Serial.println(F("GOT RESPONSE")); +#endif + + while( touch_screen_serial.available() ){ // By not checking for this the entire buffer is always cleared. + rc = touch_screen_serial.read(); +#ifdef DEBUG_SCREEN + Serial.print(rc); Serial.print(F("-")); +#endif + if( savingMessage == true ){ // We are now in the part of the response that is the message we were looking for. + if(rc != endMarker){ + metaData[metaDataPosition] = rc; + metaDataPosition++; + if( metaDataPosition >= metadataArraySize ){ + metaDataPosition = metadataArraySize - 1; + } + } + else { // We've arrived at the end marker. + metaData[metaDataPosition] = '\0'; // Terminate the string. + savingMessage = false; + metaDataPosition = 0; + if(metaData[metaDataPosition] == 0x06 && metaData[metaDataPosition + 1] == 0x07){ + touchX = touchToX( (metaData[metaDataPosition + 2] * 256) + metaData[metaDataPosition + 3] ); + touchY = touchToY( (metaData[metaDataPosition + 4] * 256) + metaData[metaDataPosition + 5] ); +#ifdef DEBUG_SCREEN + Serial.print(F("touchX=")); Serial.println(touchX); + Serial.print(F("touchY=")); Serial.println(touchY); + Serial.print(F("brightnessTimer=")); Serial.println(brightnessTimer); +#endif + + if( brightnessTimer == 0 ){ +#ifdef DEBUG_SCREEN + Serial.println(F("Turning on backlight")); +#endif + basicCommand(backlight_on); // If the screen was touched but the backlight was off, turn the backlight on. + } + else{ + touched = true; // To indicate that a touch event has just occured. + } + brightnessTimer = 255; + clearReceivedBuffer(); + } +#ifdef DEBUG_SCREEN + // OK message + else if( metaData[metaDataPosition] == 0x03 && metaData[metaDataPosition + 1] == 0x6F && metaData[metaDataPosition + 2] == 0x6B ){ + Serial.println(F("-OK")); + } + else{ // Unimplemented response form the touch screen. + Serial.println(F("-X-")); + metaDataPosition++; + if( metaDataPosition == metadataArraySize ){ return; } + } +#endif + } + } + if( rc == startMarker ){ // Once a valid startMarker is found, we start saving the message into the array. + savingMessage = true; +#ifdef DEBUG_SCREEN + Serial.print(F("(startMarker) ")); +#endif + } + } +} + +// This function can send basic string command that don't have any variable parts in them. +void basicCommand(const char* cmd) +{ +#ifdef DEBUG_SCREEN + Serial.println(F("BASIC COMMAND")); +#endif + if( touch_screen_serial.available() ){ // If necessary, clear old messages from the serial buffer. + clearReceivedBuffer(); + } + + touch_screen_serial.write(0x7E); // Starting byte, is always the same. + byte b = 0; + while( b < MAX_BASIC_COMMAND_LENGTH ){ // How many bytes are the basic commands at most? + touch_screen_serial.write( pgm_read_byte(&cmd[b]) ); + //Serial.print( pgm_read_byte(&cmd[b]),HEX ); Serial.print(F(" ")); + if( pgm_read_byte(&cmd[b]) == 0xEF ){ // This breaks out of the loop. + //waitForResponse(b); + b = MAX_BASIC_COMMAND_LENGTH; + } + b++; + } + waitForResponse(); +} + + +// This function can be activated after sending a command. It will wait until a response has arrived (or 100 milliseconds have passed), and then allow the Arduino to continue. +//void waitForResponse(byte expectedBytes) // From the touch screen + +void waitForResponse() // From the touch screen +{ +#ifdef DEBUG_SCREEN + Serial.println(); Serial.println(F("WAITING FOR RESPONSE FROM SCREEN")); + Serial.print(F("-available now: ")); Serial.println( touch_screen_serial.available() ); +#endif + byte b = 0; + while( touch_screen_serial.available() == 0 && b < 250){ + b++; + wait(1); + } +#ifdef DEBUG_SCREEN + Serial.print(F("wait time: ")); Serial.println(b); +#endif + if( touch_screen_serial.available() > 0 ){ + wait(10); // Perhaps some more bytes will show up. + while( touch_screen_serial.available() > 0 ){ // Throwing away the response. All we care about is touch messages, and they are handled in the readResponse function. + byte x = touch_screen_serial.read(); + //Serial.print(x); Serial.print(F("-")); + } + //Serial.println(); + } + else if( b == 250 ){ + Serial.println(F("Touch screen did not respond to command")); + } + +} + + + +void turnOnScreen() +{ +#ifdef DEBUG_SCREEN + Serial.print(F("__turnonscreen() (")); Serial.println(brightnessTimer); +#endif + if( brightnessTimer == 0 ){ +#ifdef DEBUG_SCREEN + Serial.println(F("BC:backlight_on")); +#endif + basicCommand(backlight_on); // Turn on the screen backlight again. + } + brightnessTimer = 255; +} + + +int touchToX(int x) +{ + return int constrain(((x - 80) / 3.7), 0, TOUCHSCREEN_WIDTH); +} +int touchToY(int y) +{ + return int constrain(((y - 100) / 2.8), 0, TOUCHSCREEN_HEIGHT); +} + + +void clearReceivedBuffer() +{ +//Serial.print(F("cleaning:")); + while( touch_screen_serial.available() ){ + char x = touch_screen_serial.read(); + Serial.print(x); + } + Serial.println(); +} + +#endif // End of touch screen check. + + + +void receive(const MyMessage &message) +{ + Serial.print(F("INCOMING MESSAGE for child #")); Serial.println(message.sensor); + if (message.isAck()) { + Serial.println(F("-Ack")); + } + else if( message.type==V_STATUS ){ + Serial.print(F("-Requested status: ")); Serial.println(message.getBool()); + + // turnOnScreen(); // Handled by display function now. + +#if !(defined(HAS_TOUCH_SCREEN)) + if( message.sensor < 10 && message.getBool() ){ + if( message.sensor == LEARN_SIMPLE_BTN_ID ){ // The user wants to the system to learn a new simple signal. This only starts when the button is toggled to on. + send(textmsg.setSensor(DEVICE_STATUS_ID).set( F("Play the signal") )); //wait(RADIO_DELAY); + state = LEARNING_SIMPLE; + } + else if( message.sensor == LEARN_ON_OFF_BTN_ID ){ // The user wants the system to learn a new on+off signal. This only starts when the button is toggled to on. + send(textmsg.setSensor(DEVICE_STATUS_ID).set( F("Play the ON signal") )); //wait(RADIO_DELAY); + state = LEARNING_ON; + } + if( message.sensor == COPYING_SIMPLE_BTN_ID ){ // The user wants to the system to learn a new simple signal. This only starts when the button is toggled to on. + send(textmsg.setSensor(DEVICE_STATUS_ID).set( F("Play the signal") )); //wait(RADIO_DELAY); + state = COPYING_SIMPLE; + } + else if( message.sensor == COPYING_ON_OFF_BTN_ID ){ // The user wants the system to learn a new on+off signal. This only starts when the button is toggled to on. + send(textmsg.setSensor(DEVICE_STATUS_ID).set( F("Play the ON signal") )); //wait(RADIO_DELAY); + state = COPYING_ON; + } + //send(buttonmsg.setSensor(message.sensor).set(0)); // The button will already be set back to 'off' after the entire signal is recorded and everything is re-presented. + } + else +#endif + + if( message.sensor >= 10 && message.sensor < 100 ){ // If the user toggled a signal replay button. + if( playlist_position < PLAYLIST_SIZE){ // We only add to the playlist if there is space left in the playlist. + + Serial.println(F("-Adding to playlist")); + playlist_position++; + if( message.getBool() == 0 ){ + playlist[playlist_position] = (message.sensor - 9); + } + else { + playlist[playlist_position] = (message.sensor - 9) + 100; + } + //replay(message.sensor - 9, message.getBool()); // The old way of directly starting a replay. Now it uses a playlist. + } + } + } +} + + + + +/** + * The MySensors Arduino library handles the wireless radio link and protocol + * between your home built sensors/actuators and HA controller of choice. + * The sensors forms a self healing radio network with optional repeaters. Each + * repeater and gateway builds a routing tables in EEPROM which keeps track of the + * network topology allowing messages to be routed to nodes. + * + * Created by Henrik Ekblad + * Copyright (C) 2013-2015 Sensnology AB + * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors + * + * Documentation: http://www.mysensors.org + * Support Forum: http://forum.mysensors.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * + */ diff --git a/code/Temperature_and_more/Temperature_and_more.arduino.avr.nano.elf b/code/Temperature_and_more/Temperature_and_more.arduino.avr.nano.elf new file mode 100644 index 0000000..b61dc7c Binary files /dev/null and b/code/Temperature_and_more/Temperature_and_more.arduino.avr.nano.elf differ diff --git a/code/Temperature_and_more/Temperature_and_more.arduino.avr.nano.hex b/code/Temperature_and_more/Temperature_and_more.arduino.avr.nano.hex new file mode 100644 index 0000000..fe8081d --- /dev/null +++ b/code/Temperature_and_more/Temperature_and_more.arduino.avr.nano.hex @@ -0,0 +1,1610 @@ +:100000000C944F030C94A6220C947F220C94CD22C6 +:100010000C94CD220C94CD220C9464240C94770380 +:100020000C9477030C9477030C9477030C94770368 +:100030000C9477030C9477030C9477030C94770358 +:100040000C9435220C9477030C9403220C94DD213C +:100050000C9477030C9477030C9477030C94770338 +:100060000C9445230C94770300407A10F35A00A0B7 +:10007000724E18090010A5D4E80000E87648170071 +:1000800000E40B54020000CA9A3B000000E1F505B1 +:10009000000080969800000040420F000000A086FB +:1000A00001000000102700000000E803000000002D +:1000B0006400000000000A000000000001000000D1 +:1000C00000002C76D888DC674F0823DFC1DFAE59EB +:1000D000E1B1B796E5E3E453C63AE651997696E87E +:1000E000E6C28426EB898C9B62ED407C6FFCEFBC02 +:1000F0009C9F40F2BAA56FA5F490055A2AF75C932D +:100100006B6CF9676DC11BFCE0E40D47FEF520E662 +:10011000B500D0ED902E0300943577050080841E45 +:10012000080000204E0A000000C80C333333330FA0 +:10013000986E12831141EF8D2114893BE65516CF3D +:10014000FEE6DB18D1844B381BF77C1D901DA4BB49 +:10015000E424203284725E228100C9F124ECA1E5FE +:100160003D2752096AD53036A538BF40A39E81F39A +:10017000D7FB7CE339829B2FFF87348E4344C4DE58 +:10018000E9CB547B9432A6C2233DEE4C950B42FA48 +:10019000C34E082EA16628D924B2765BA2496D8B86 +:1001A000D12572F8F66486689816D4A45CCC5D6597 +:1001B000B6926C704850FDEDB9DA5E154657A78DC2 +:1001C0009D8490D8AB008CBCD30AF7E45805B8B333 +:1001D0004506D02C1E8FCA3F0F02C1AFBD030113CD +:1001E0008A6B3A9111414F67DCEA97F2CFCEF0B4B7 +:1001F000E67396AC7422E7AD3585E2F937E81C75F5 +:10020000DF6E47F11A711D29C5896FB7620EAA18F2 +:10021000BE1BFC563E4BC6D279209ADBC0FE78CD81 +:100220005AF41FDDA8338807C731B112105927804F +:10023000EC5F60517FA919B54A0D2DE57A9F93C9EE +:100240009CEFA0E03B4DAE2AF5B0C8EBBB3C83531E +:100250009961172B047EBA77D626E169146355217C +:100260000C7D8D01020408102040801B36637C77D2 +:100270007BF26B6FC53001672BFED7AB76CA82C9A4 +:100280007DFA5947F0ADD4A2AF9CA472C0B7FD93DC +:1002900026363FF7CC34A5E5F171D8311504C723D4 +:1002A000C31896059A071280E2EB27B27509832CD2 +:1002B0001A1B6E5AA0523BD6B329E32F8453D100A8 +:1002C000ED20FCB15B6ACBBE394A4C58CFD0EFAAC7 +:1002D000FB434D338545F9027F503C9FA851A34015 +:1002E0008F929D38F5BCB6DA2110FFF3D2CD0C13F6 +:1002F000EC5F974417C4A77E3D645D197360814F1E +:10030000DC222A908846EEB814DE5E0BDBE0323A3F +:100310000A4906245CC2D3AC629195E479E7C837F8 +:100320006D8DD54EA96C56F4EA657AAE08BA78257B +:100330002E1CA6B4C6E8DD741F4BBD8B8A703EB57B +:10034000664803F60E613557B986C11D9EE1F898DF +:100350001169D98E949B1E87E9CE5528DF8CA1891F +:100360000DBFE6426841992D0FB054BB162D526562 +:1003700071756573746564207374617475733A2064 +:10038000002D41636B00494E434F4D494E47204D70 +:1003900045535341474520666F72206368696C641A +:1003A000202300212053656E736F72206572726F77 +:1003B000720021204E4F5420434F4E4E454354452A +:1003C0004420544F204741544557415900436F6ED4 +:1003D0006E656374656420746F206761746577610E +:1003E00079210004200000EF0402FFFFEF03040066 +:1003F000EF48656C6C6F2C204920616D2061206393 +:100400006C696D6174652073656E736F7200536300 +:100410007265656E00466F72656361737400416951 +:10042000722070726573737572650048756D6964CA +:100430006974790054656D706572617475726500D8 +:10044000312E300057656174686572207374617471 +:10045000696F6E007468726F77696E67206177618B +:1004600079206C656674206F76657220746F756391 +:10047000685F73637265656E5F73657269616C2036 +:10048000627974653A00444953504C41594E554D78 +:10049000424552202800436C6F75647900434C4FED +:1004A000554459005468756E64657273746F726D4B +:1004B000005448554E44455253544F524D005375C5 +:1004C0006E6E790053554E4E5900556E73746162CD +:1004D0006C6500554E535441424C45005374616263 +:1004E0006C6500535441424C4500556E6B6E6F77FE +:1004F0006E00554E4B4E4F574E0053656E64696EFD +:1005000067207072657373757265200053656E6441 +:10051000696E672068756D69646974792000536538 +:100520006E64696E672074656D703A2000071177FC +:1005300000200000EF0054656D7065726174757283 +:10054000650000000000000000000148756D69644E +:100550006974790000000000000000000000000243 +:100560004261726F6D6574657200000000000000EA +:100570000000000003535441424C450000000000BD +:100580000000000000000000000453554E4E5900CA +:100590000000000000000000000000000000054313 +:1005A0006C6F75647900000000000000000000001E +:1005B00000000006556E737461626C6500000000F7 +:1005C0000000000000000000075468756E6465724A +:1005D00073746F726D000000000000000008556E1B +:1005E0006B6E6F776E0000000000000000000000DE +:1005F0000000546F7563682073637265656E2064D4 +:100600006964206E6F7420726573706F6E642074FD +:100610006F20636F6D6D616E64000306FFEF03066C +:1006200000EF53657474696E672073637265656E5D +:10063000206261636B6C6963687420746F3A200098 +:1006400000000000240027002A000000000800022B +:10065000010000030407000000000000000000008B +:1006600000002300260029000000000025002800CB +:100670002B00040404040404040402020202020223 +:100680000303030303030102040810204080010256 +:1006900004081020010204081020A1278429112435 +:1006A0001FBECFEFD8E0DEBFCDBF11E0A0E0B1E0CC +:1006B000E0EDF3E602C005900D92A23BB107D9F739 +:1006C00025E0A2EBB1E001C01D92A939B207E1F724 +:1006D00013E0CEE4D3E004C02197FE010E94BD31B7 +:1006E000CD34D107C9F70E94A1280C94DB310C94BA +:1006F0000000EAEEF3E0A089B18982E08C93A48542 +:10070000B5851C92A685B78580E18C93108EA4894F +:10071000B58986E08C93A289B3898C9180618C9392 +:10072000A289B3898C9188608C93A289B3898C91BA +:1007300080688C930288F389E02D80818F7D80838F +:100740000895942FE7E5F5E03091E6033183828345 +:10075000668325834481487F492B477E4483CF01AC +:10076000089520919C0381E0213208F044C08091DB +:10077000C3038111FCCF80919B0392E09093C3034C +:1007800091E09093C2039FEF9093C1031092C00336 +:100790002093BF03ABE7B3E0EDE9F3E090E029136A +:1007A00031C01092BE039091BE03880F892B8093B5 +:1007B000BE038091BD03813049F51092BD03809145 +:1007C000BE038093BB008091BC0083FDF8CF85EC15 +:1007D0008093BC008091C3038230E1F38091C10318 +:1007E0008F3FB1F08091C1038032A1F08091C103AD +:1007F000803391F084E010927A0310929C0310925F +:10080000790308953D9131939F5FC9CF85EEE0CF85 +:1008100080E0F1CF82E0EFCF83E0EDCF8091C403A1 +:100820009091C503891B990B0895AF92BF92CF9207 +:10083000DF92EF92FF920F931F93CF93DF936C01A0 +:100840007B018B01040F151FEB015E01AE18BF0881 +:10085000C017D10759F06991D601ED91FC91019033 +:10086000F081E02DC6010995892B79F7C501DF914B +:10087000CF911F910F91FF90EF90DF90CF90BF909D +:10088000AF900895FC01538D448D252F30E0842FC7 +:1008900090E0821B930B541710F0CF9608950197A8 +:1008A0000895FC01918D828D981761F0A28DAE0F95 +:1008B000BF2FB11D5D968C91928D9F5F9F73928F1C +:1008C00090E008958FEF9FEF0895FC01918D828D48 +:1008D000981731F0828DE80FF11D858D90E0089515 +:1008E0008FEF9FEF0895FC01918D228D892F90E06D +:1008F000805C9F4F821B91098F73992708958AEE20 +:1009000093E00E94730421E0892B09F420E0822FF8 +:100910000895FC01A48DA80FB92FB11DA35ABF4F94 +:100920002C91848D90E001968F739927848FA689EE +:10093000B7892C93A089B1898C91837080648C9342 +:10094000938D848D981306C00288F389E02D8081F1 +:100950008F7D80830895EF92FF920F931F93CF9323 +:10096000DF93EC0181E0888F9B8D8C8D98131AC0EA +:10097000E889F989808185FF15C09FB7F894EE89D1 +:10098000FF896083E889F98980818370806480832E +:100990009FBF81E090E0DF91CF911F910F91FF9079 +:1009A000EF900895F62E0B8D10E00F5F1F4F0F7321 +:1009B0001127E02E8C8D8E110CC00FB607FCFACFDC +:1009C000E889F989808185FFF5CFCE010E948904ED +:1009D000F1CFEB8DEC0FFD2FF11DE35AFF4FF082AD +:1009E0009FB7F8940B8FEA89FB8980818062CFCF13 +:1009F0000F931F93CF93DF938C01D0E0C0E0F801F9 +:100A0000EC0FFD1F6491662341F08AEE93E00E9493 +:100A1000AB04892B11F02196F2CFCE01DF91CF915B +:100A20001F910F910895CF93DF93EC01888D882358 +:100A3000B9F0AA89BB89E889F9898C9185FD03C041 +:100A4000808186FD0DC00FB607FCF7CF8C9185FF26 +:100A5000F2CF808185FFEDCFCE010E948904E9CFDE +:100A6000DF91CF91089580E090E0892B29F00E94DA +:100A70007F0481110C940000089590E0FC01E65B76 +:100A8000F94F2491FC01EA57F94F3491FC01EE58DB +:100A9000F94FE491EE23C9F0222339F0233001F11C +:100AA000A8F4213019F1223029F1F0E0EE0FFF1FF8 +:100AB000E859F94FA591B4918FB7F894EC91611171 +:100AC00026C030953E233C938FBF08952730A9F070 +:100AD0002830C9F0243049F7809180008F7D03C011 +:100AE000809180008F7780938000DFCF84B58F77EF +:100AF00084BDDBCF84B58F7DFBCF8091B0008F7735 +:100B00008093B000D2CF8091B0008F7DF9CF3E2B83 +:100B1000DACFCF93DF9390E0FC01EA57F94F2491AD +:100B20008E58994FFC0184918823D1F090E0880F72 +:100B3000991FFC01E05CF94FA591B491FC01E859C3 +:100B4000F94FC591D49161110EC09FB7F8948C9163 +:100B5000E22FE0958E238C932881E223E8839FBFC8 +:100B6000DF91CF9108958FB7F894EC91E22BEC933D +:100B70008FBFF6CF3FB7F894809150039091510307 +:100B8000A0915203B091530326B5A89B05C02F3FF7 +:100B900019F00196A11DB11D3FBFBA2FA92F982FA3 +:100BA0008827BC01CD01620F711D811D911D42E09E +:100BB000660F771F881F991F4A95D1F708952FB7A1 +:100BC000F89460914C0370914D0380914E03909185 +:100BD0004F032FBF089508952091490330914A0390 +:100BE0002817390771F490914803809147039817AB +:100BF00041F0E0914803F0E0E95FFC4F808190E034 +:100C000008958FEF9FEF08950895EF92FF920F934D +:100C10001F93CF93DF93DC015C96ED90FC905D9782 +:100C2000E114F10479F481E090E013969C938E93A3 +:100C3000129790E080E0DF91CF911F910F91FF908C +:100C4000EF9008955196ED91FC91529750968C91AA +:100C50005097982F90950FB75E962C915E97122F14 +:100C6000127021FD6095F8942081112319F1282B31 +:100C70002083E7012197F1F728E0462F50E03081EB +:100C800060FF1AC0382B3083E7012197F1F7BA01D2 +:100C900075956795215089F7112381F0808189230B +:100CA00080830FBF5C968D919C910197F1F781E055 +:100CB00090E0C1CF2923DCCF3923E5CF9081892B68 +:100CC000EFCF2091490330914A032817390771F477 +:100CD000809147032091480390E0805C9F4F821BE6 +:100CE000910960E470E00E945D30089590E080E03A +:100CF00008952091490330914A0328173907B9F420 +:100D00009091480380914703981789F0E091480338 +:100D1000F0E0E95FFC4F80812091480330E02F5FD5 +:100D20003F4F2F7333272093480390E008958FEFB0 +:100D30009FEF089585ED8093BC008091BC0084FDF9 +:100D4000FCCF1092C30308952091E202260F3327AF +:100D5000331F21323105ECF42091C303FC0190E0F4 +:100D600080E0243069F082E00895A091E2022191B0 +:100D7000AC0140545D4FA40FB52FB11D2C930196CB +:100D8000861798F38091E202680F6093E20280E098 +:100D9000089581E0089508950895E091C503809134 +:100DA000C403E81730F4F0E0EA53FC4F808190E090 +:100DB00008958FEF9FEF08959091C5038091C4032C +:100DC0002FEF3FEF981748F4E92FF0E0EA53FC4F7C +:100DD000208130E09F5F9093C503C9010895CF92B1 +:100DE000DF92EF92FF920F931F93CF93DF937C01DB +:100DF000CB018A0120917903222389F0EB016B0159 +:100E0000C40ED51ECC15DD0569F06991D701ED91B1 +:100E1000FC910190F081E02DC7010995F3CF642F7B +:100E20000E94A406C801DF91CF911F910F91FF90FE +:100E3000EF90DF90CF900895CF93DF931F92CDB7BF +:100E4000DEB76983209179032223F9F020919C0376 +:100E5000203258F021E030E0FC013383228390E01F +:100E600080E00F90DF91CF91089580917A03E82F71 +:100E7000F0E0E558FC4F998190838F5F80937A036F +:100E800080939C0381E090E0ECCF61E0CE0101967D +:100E90000E94A406F7CFCF93C62F91E090937903D9 +:100EA00096E790939B0310927A0310929C03682F0D +:100EB00087E894E00E941C076C2F87E894E00E946A +:100EC0001C07CF910C94B10390E080E00895CF937C +:100ED000DF93CCEDD1E060E070E0CB012991399156 +:100EE000499159910E940F2F21E0C03FD207B1F7DD +:100EF00020E030E040EA50E40E947B2FDF91CF9168 +:100F00000895FC01938197706A3108F069E188E0E7 +:100F1000689FB0011124692B6383CF0108950F935B +:100F20001F93CF93DF93EC018B0162E00E94810756 +:100F30008C818F7180648C8318870F83CE01DF9141 +:100F4000CF911F910F9108951F93CF93DF93EC01E1 +:100F5000162F61E00E9481078C818F7180628C83E3 +:100F60001F83CE01DF91CF911F910895E091B302CD +:100F7000F091B402309721F00280F381E02D0994C2 +:100F800008950F931F930E94DF050091B502109101 +:100F9000B6022091B7023091B802601B710B820B30 +:100FA000930B1F910F9108959C018091B302909132 +:100FB000B40282179307F1F09091BD029F71909354 +:100FC000BD023093B4022093B302E091B302F091DA +:100FD000B402309721F00190F081E02D09950E9434 +:100FE000DF056093B5027093B6028093B7029093C9 +:100FF000B80208958091BD02805E8093BD02E5CF66 +:101000000F931F930E94C1072091BE0220772037C3 +:1010100079F000E117E220E030E006171707280713 +:10102000390758F482E291E01F910F910C94D40794 +:1010300000E61AEE20E030E0F0CF1F910F91089506 +:101040008091BE028F70863020F088E191E00C9490 +:10105000D40708958091BD0284608093BD02809181 +:10106000BE0280788093BE02E091DA01F091DB014C +:10107000309709F009940895CF93C091BF02CF3FF4 +:10108000A1F00E94DF056093B9027093BA028093C9 +:10109000BB029093BC028091E803C813C093E8039D +:1010A00084E191E0CF910C94D4070E94C107613D87 +:1010B00077408105910568F08091BD02803618F473 +:1010C00080E191E0EFCF877F8093BD0288E191E0DE +:1010D000E9CFCF9108958091E6038F3F21F080E121 +:1010E00091E00C94D4070E94C107613D77408105CF +:1010F000910550F08091BD02803618F48CE091E0AB +:10110000F0CF88E091E0EDCF08950E94C107613DE6 +:1011100077408105910520F48091BD0281FF17C0C1 +:101120008091E7038F3F49F08091BD028E7F8093CD +:10113000BD028CE091E00C94D4078091BD02803612 +:1011400018F488E191E0F7CF88E091E0F4CF0895BA +:101150009C0194E0D90180E0EC91F0E0EE59FE4F63 +:10116000E491EC938F5F14968430B1F791502F5F28 +:101170003F4F9111EFCF0895982F991F9927991FED +:101180002BE1929F902D1124880F892708959C01AF +:1011900094E0D90180E0EC91F0E0E359FD4FE49157 +:1011A000EC938F5F14968430B1F791502F5F3F4FCF +:1011B0009111EFCF08951F93CF93DF9320E1829F8A +:1011C000C0011124480F591F30E020E0FB01E20F5D +:1011D000F31FD901A40FB51F80E091911D91EF017C +:1011E0002197912798838F5F8430B9F72C5F3F4F09 +:1011F0002031310559F7DF91CF911F9108958EBDB0 +:1012000000000DB407FEFDCF8EB508959091C30385 +:101210009111FCCF282F91E09093C3039093C203C8 +:101220003FEF3093C1031092C003380F3093BF03D8 +:101230009093BE039091BE039C6E9093BE039091D9 +:10124000BD03913019F51092BD039091BE039093A8 +:10125000BB009091BC0093FDF8CF95EC9093BC003F +:101260009091C3039130E1F39091C003981710F46B +:101270002091C003ADE9B3E0E6ECF3E080E0821337 +:1012800007C01092C5038093C403089595EEE6CF7E +:101290009D9191938F5FF3CFCF9391E090937903DA +:1012A00096E790939B0310927A0310929C03682F09 +:1012B00087E894E00E941C070E94B10382E090E05E +:1012C0000E9406098091C4039091C503891B990B64 +:1012D00082309105ECF387E894E00E94DC06C82F89 +:1012E00087E894E00E94DC0620E03C2FA901482B0F +:1012F000CA01CF9108950E944C0998278927982701 +:10130000089591E09093790396E790939B03109250 +:101310007A0310929C03682F87E894E00E941C07D0 +:101320000E94B10381E090E00E9406099091C403FD +:101330008091C5039817F1F387E894E00E94DC06DA +:1013400008958F929F92AF92BF92CF92DF92EF92C9 +:10135000FF9291E09093790396E790939B0310920C +:101360007A0310929C03682F87E894E00E941C0780 +:101370000E94B10383E090E00E9406098091C403BB +:101380009091C503891B990B83309105ECF387E895 +:1013900094E00E94DC06092E000CAA0BBB0B8824EB +:1013A000982EA92EBA2E87E894E00E94DC06092E1A +:1013B000000CAA0BBB0B6C017D01C828D928EA28B8 +:1013C000FB28FE2CED2CDC2CCC2487E894E00E943A +:1013D000DC06BC01990F880B990B6C297D298E299D +:1013E0009F29FF90EF90DF90CF90BF90AF909F909C +:1013F0008F9008958F929F92AF92BF92CF92DF927B +:10140000EF92FF928AEF0E94A109E4E5F3E02081C8 +:101410003181C72ED82EE92EFF24E7FCFA94C21A98 +:10142000D30AE108F10857E0959587957795679578 +:101430005A95D1F7220F331F4B015C01821A930A90 +:10144000A108B108A5019401A281B3810E949E2E3A +:101450004B015C01BBE0B594A79497948794BA952F +:10146000D1F7A7019601C701B6010E942D309B015B +:10147000AC018CE055954795379527958A95D1F71E +:10148000A481B5810E949E2E9B01AC019EE05595E2 +:101490004795379527959A95D1F7280D391D4A1DFF +:1014A0005B1D21A332A343A354A3A5E0B0E00E9497 +:1014B000932E60587F4F8F4F9F4F672F782F892F24 +:1014C000992787FD9A950E94252D20E030E048EC71 +:1014D00052E40E947B2FFF90EF90DF90CF90BF905F +:1014E000AF909F908F9008952F923F924F925F926E +:1014F0006F927F928F929F92AF92BF92CF92DF9224 +:10150000EF92FF920F931F93CF93DF93CDB7DEB788 +:101510006B970FB6F894DEBF0FBECDBF0E94FA09DD +:1015200087EF0E94A1096E8B7F8B888F998F8091A6 +:10153000750390917603A0917703B09178036C01C5 +:101540007D01FF0CCC08DC2C76019C01AD016C2DDB +:101550007C2D8C2D9C2D345F41405109610971090E +:101560008109910929873A874B87652E762E872E28 +:10157000982E9B8F5901C42ED52EE62EF72E082FBC +:10158000192F0E947130322E232E488B598B6A8B73 +:101590007B8B8C8B9D8B30915D0320915C03932F13 +:1015A000990F990B492F592F692F792F892F0E9455 +:1015B00071300CE00E94693129833C874A8F5D8736 +:1015C0006E877F87582E492E30915F0320915E03EE +:1015D000932F990F990BA32CB22CC888D988EA882D +:1015E000FB880C891D89492F592F692F792F892F45 +:1015F0000E94713008E00E948231A980BC84CA8CAC +:10160000DD84EE84FF84052D142D0E949F317058D7 +:101610008F4F9F4FE0915A03F0915B035F01D12CF4 +:10162000C12CE12CF12C8701E983BA82CB82DC82C8 +:10163000ED82FE820F831887C12CD12CE12CF12C76 +:1016400000E010E00E94713001E20E9482312A8F96 +:10165000532E49835C876D877E878F87492EA0E054 +:101660000E94B13109F434C13091650320916403C3 +:10167000932F990F990BA32CB22CC888D988EA888C +:10168000FB880C891D89492F592F692F792F892FA4 +:101690000E9471301901488B598B6A8B7B8B8C8B24 +:1016A0009D8B3091630320916203932F990F990BC7 +:1016B000A984BA84CB84D62CE72CF82C092D1B8D59 +:1016C000492F592F692F792F892F0E94713001E1FD +:1016D0000E9469315101C888D988EA88FB880C8941 +:1016E0001D890E949F3159016A017B01382E192FF3 +:1016F0004E895F89688D798D84E0759567955795DA +:1017000047958A95D1F780E090E0A0E1B0E0841B96 +:10171000950BA60BB70B3C014D01990C6608762C76 +:1017200043019C01AD01662D762D862D962D0FE18E +:101730000E946931722E832E942E5987B62FA72FBF +:10174000F82FE92F3091610320916003932F990FB7 +:10175000990B492F592F692F792F892F03E20E9466 +:101760006931032D0E949F3159016A017B018C016F +:10177000272D382D492D59856B2F7A2F8F2F9E2F8E +:101780000E94A831E5E3AE2EFCE0BF2EC12CD12C87 +:10179000E12CF12C00E010E00E947130AA8CB52CF5 +:1017A000C980DC84ED84FE840F85142D0E94CB302B +:1017B0002F873E8749875A876B8779838C879D87D8 +:1017C0000DE00E948231922E832E742E652E562EAD +:1017D000472E382E292E30916B0320916A03932FC8 +:1017E000990F990BA92CB82CC72CD62CE52CF42CCE +:1017F000032D122D492F592F692F792F892F0E94E0 +:1018000071300E94713009E10E948231AF84BE8440 +:10181000C984DA84EB84F9800C851D850E949F3190 +:1018200019012A013B014C013091690320916803A1 +:10183000932F990F990B492F592F692F792F892FA2 +:101840000E94713003E10E94823159016A017B01DB +:101850008C019101A201B301C4010E949F3108E0F3 +:101860000E94823159016A017B01E82F192F3091C2 +:10187000670320916603932F990F990B492F592FD6 +:10188000692F792F892F04E00E9469310E2F0E9461 +:101890009F3149015A0199278B2D7A2D692D6B961D +:1018A0000FB6F894DEBF0FBECDBFDF91CF911F9171 +:1018B0000F91FF90EF90DF90CF90BF90AF909F90EF +:1018C0008F907F906F905F904F903F902F900895F2 +:1018D00060E070E0CB01E3CFFC0101900020E9F76C +:1018E0003197AF01481B590BBC018AEE93E00C9471 +:1018F000150488E395E00E946106892B51F088E386 +:1019000095E00E947906682F8AEE93E00E94AB046E +:10191000F0CF87E691E00C946C0CCF93DF930E949C +:10192000F804EC0187E691E00E946C0C8C0F9D1F7F +:10193000DF91CF9108958F929F92AF92BF920F93B4 +:101940001F93CF93DF93CDB7DEB7A1970FB6F8946F +:10195000DEBF0FBECDBF19A2423008F44AE08E01AF +:101960000F5D1F4F842E912CB12CA12CA501940149 +:101970000E94DD2EE62FB901CA01EA30F4F4E05DE1 +:10198000D801EE938D01232B242B252B79F790E0A2 +:1019900080E0109719F0CD010E946C0CA1960FB653 +:1019A000F894DEBF0FBECDBFDF91CF911F910F9195 +:1019B000BF90AF909F908F900895E95CE1CFCF9258 +:1019C000DF92EF92FF92CF93DF936C01990FEE08B5 +:1019D000FF08F7FE1FC06DE28AEE93E00E94AB04A1 +:1019E000EC0166277727CB016C197D098E099F09C9 +:1019F0004AE00E949B0CC80FD91F87E691E00E9425 +:101A00006C0C8C0F9D1FDF91CF91FF90EF90DF90BA +:101A1000CF9008954AE0C701B6010E949B0CEC01EB +:101A2000ECCFCF93DF93682F70E090E080E04AE046 +:101A30000E949B0CEC0187E691E00E946C0C8C0FDD +:101A40009D1FDF91CF910895BF92CF92DF92EF92C9 +:101A5000FF920F931F93CF93DF937B01042FB22E3E +:101A600091E59CBD91E09DBD2998E3E2F0E03197BE +:101A7000F1F70E94FF08182FE7010150A8F0BB20E2 +:101A800051F08FEF0E94FF08182FE114F104A9F321 +:101A900088832196F2CF6E018FEFC81AD80A888109 +:101AA0000E94FF08E601E9CF299A83E290E00197BE +:101AB000F1F7812FDF91CF911F910F91FF90EF9060 +:101AC000DF90CF90BF900895CF93DF931F92CDB753 +:101AD000DEB7698320E041E0BE016F5F7F4F0E9467 +:101AE000240D0F90DF91CF9108958091BE02982F21 +:101AF0009077903759F0982F92959F709F5F9770CD +:101B00009295907F8F78892B8093BE028091BD0241 +:101B10008B7F8F7E8093BD022A986CE080E20C94CC +:101B2000640D6FE080E20E94640D609127016F3FB9 +:101B300019F08AE20E94640D2A9A0895CF93C82F63 +:101B40008093E6038F3F51F08093270163E082E2A8 +:101B50000E94640D61E081E20E94640D0E94910D7B +:101B60006C2F90E080E00E94622E81E0CF9108957A +:101B700021E041E070E060E00C94240D20E040E0C2 +:101B800070E060E00C94240DEDEBF2E08081877F43 +:101B90008F7E80831092B9021092BA021092BB021B +:101BA0001092BC0244E050E070E060E086EE93E00A +:101BB0000C94422E0F931F93CF93DF93EC018B0174 +:101BC00080E06115710519F0CB010E94A72A682FEA +:101BD000CE010E9481078C818F718C834B81469549 +:101BE000469546954A3108F049E150E0B801CE01EA +:101BF00007960E94B02AEB81E695E695E695EA31D4 +:101C000008F0E9E1EC0FFD2FF11D1782CE01DF9105 +:101C1000CF911F910F910895EF92FF920F931F9311 +:101C2000CF93DF938C017B0160E0E114F10439F084 +:101C3000F70101900020E9F731976E2F6E19C80166 +:101C40000E948107F80184818F718483C381C695C6 +:101C5000C695C695CA3108F0C9E1D0E0AE01B7011A +:101C6000C80107960E94CC31C00FD11F1F82C80146 +:101C7000DF91CF911F910F91FF90EF900895FC019C +:101C80002481229526952770213011F487810895AB +:101C9000211104C007960E94622A089580E00895E9 +:101CA0002F923F924F925F926F927F928F929F926C +:101CB000AF92BF92CF92DF92EF92FF920F931F935A +:101CC000CF93DF93CDB7DEB7C855D1090FB6F894DF +:101CD000DEBF0FBECDBF9091E603DB019C93139650 +:101CE0001C9112FD44C01695169516951A3108F0F0 +:101CF00019E1195F9091E9033324339490FD03C0F7 +:101D00008F3F09F0312C6115710509F4C3C1699643 +:101D10008FAF6997E12EF12CFE0131966F01A7017B +:101D2000CF010E94C331FE01EF5BFF4F80E1DF0175 +:101D30001D928A95E9F71131E0F4B0E16396BFAFE7 +:101D40006397EC0CFD1C812C912C540193946396A9 +:101D5000FFAD63971F1790F40E940A2AA501940112 +:101D60000E94742ED7016D937D011F5FF0CF10E2AA +:101D7000C1CFE0E26396EFAF6397E3CF80E1FE016E +:101D8000EF5BFF4FA1EAB2E001900D928A95E1F777 +:101D90006396BFAD63974B2E512C712C612C4E0175 +:101DA000E1E18E0E911CE1EAF2E08601C8018C1996 +:101DB0009D09B0E0A0E084159505A605B70508F0DB +:101DC000F7C05801D5018D915D0191911197892737 +:101DD0008C93A814B904B1F741EF51E0B80180E049 +:101DE0000E94DB08B1E06296BFAF6297C8010E9413 +:101DF000C708F801818195819183918595839585A7 +:101E00009187858782819285928382878681968554 +:101E10009683868783819785938393859787978118 +:101E2000938787837801D701BC916496BFAF64978D +:101E3000F701F1816596FFAF65978B2F8F27D7014B +:101E40001296BC916696BFAF6697F701F3816796CD +:101E5000FFAF6797BF276896BFAF68972B2E2826DE +:101E60000E94BC0864962FAD649782278225D70113 +:101E70008C9365968FAD65976696BFAD66978B27F9 +:101E80000E94BC086596EFAD65978E278225D70125 +:101E900011968C9368968FAD68970E94BC086696E1 +:101EA000BFAD66978B278225F701828364968FAD3D +:101EB00064976796FFAD67978F270E94BC08679667 +:101EC0002FAD679782278225D70113968C93B4E0B4 +:101ED000EB0EF11CAE14BF0409F0A5CF41EF51E0A9 +:101EE000B80162968FAD62970E94DB086296EFADF3 +:101EF0006297EF5F6296EFAF6297EA3009F076CFB4 +:101F0000C8010E94C708D80111968C9111971596A7 +:101F10009C91159711969C93119719969C911997DE +:101F200015969C9315971D969C911D9719969C93B9 +:101F300019971D968C931D9712968C9112971A964D +:101F40009C911A9712969C9312971A968C931A97B3 +:101F500016968C9116971E969C911E9716969C939A +:101F600016971E968C931E9713968C9113971F9617 +:101F70009C911F9713969C9313971B969C911B976C +:101F80001F969C931F9717969C9117971B969C934F +:101F90001B9717968C9341EF51E0B8018AE00E949D +:101FA000DB08B0E18B0E911CF801005F1F4FFECEE5 +:101FB00080E1A1EAB2E001900D928A95E1F72A98BA +:101FC00083E092E00197F1F76EE080E20E94640DF9 +:101FD0008BE891E00197F1F769966FAD69978AE216 +:101FE0000E94640D69966FAD699780E30E94640D4D +:101FF00081EE0E94BE0D332021F060E584E20E9454 +:10200000640D20E063964FAD6397B60180EA0E94AD +:10201000240D2A9A10E000E08FEF0E94BE0D80731D +:1020200039F40F3F100721F0A8950F5F1F4FF4CF31 +:102030002A9860E787E20E94640D182F84FF03C08E +:1020400081EE0E94BE0D332021F06FE584E20E94F4 +:10205000640D0E94910D15FF02C033243394832D2B +:10206000C85ADF4F0FB6F894DEBF0FBECDBFDF9169 +:10207000CF911F910F91FF90EF90DF90CF90BF9085 +:10208000AF909F908F907F906F905F904F903F9018 +:102090002F900895312CE3CF0F931F93CF938C0192 +:1020A000FC0182819091BD0290FF04C08F3F51F5E9 +:1020B000CFEF04C0811116C0C091E703B8018C2F87 +:1020C0000E94500E9091E7039C1313C09091BE02A2 +:1020D000811113C021E0290F2F70907F922B9093D4 +:1020E000BE0207C08F3F21F3B8010E94500E882323 +:1020F00019F3CF911F910F910895907F9093BE0295 +:1021000081E0F7CF80E0F5CF2091BD0222FD0C9455 +:102110004C1080E00895CF93DF93262FEA0150E022 +:1021200040E0682F80E00E94A103BE010E94DA0D0A +:10213000DF91CF910C9484102091E603FC01218360 +:10214000F8E06F9FB00111246160FC012481207FC1 +:10215000622B64832091B20220FD0C94841080E0F5 +:1021600008952BE043E050E06FEF80E00E94A10370 +:1021700064E474E00E94DA0D0E9484102CE043E0D5 +:1021800050E06FEF80E00E94A10360E474E00E94E1 +:10219000DA0D0E94841064E670E080E090E00E9416 +:1021A000031544E354E066E070E080E00E948B1089 +:1021B00064E670E080E090E00E9403154BE254E09A +:1021C00067E070E081E00E948B1064E670E080E0E0 +:1021D00090E00E9403154EE154E068E070E082E078 +:1021E0000E948B1064E670E080E090E00E9403158E +:1021F00045E154E064E270E083E00E948B1064E605 +:1022000070E080E090E00E9403154EE054E063E04F +:1022100070E084E00E948B1064E670E080E090E063 +:102220000E94031581E08093210108952FE043E08F +:1022300050E06FEF80E00E94A10369E671E00E9428 +:102240000C0E62E087E595E00E948107E7E5F5E086 +:1022500084818F71806C848381E087831086CF01B5 +:102260000E9484108FE00E94CD1421E150E040E0F4 +:102270006FEF80E00E94A1036AE671E00E940C0EFD +:102280000E94841026E043E050E06FEF80E00E945F +:10229000A1036091E7030E94A4070E94841086E0D6 +:1022A0000E94CD140C94B1102F923F924F925F92E6 +:1022B0006F927F928F929F92AF92BF92CF92DF9256 +:1022C000EF92FF920F931F93CF93DF93CDB7DEB7BB +:1022D0006D970FB6F894DEBF0FBECDBF26E0298FF5 +:1022E00087E10E94B80D80FD39C3398D3150398F97 +:1022F000332309F433C321E041E070E060E080E67D +:102300000E94240D182F813220F082EE0E94BE0D13 +:1023100010E021E0412F68E775E081E60E94240D7E +:1023200060E487E20E94640DA1EAB2E0E0E11D9260 +:10233000EA95E9F7212F30E050E040E02A8F3B8F0B +:102340004C8F5D8F48E855E0588F4F8B08E715E0BC +:102350001C8B0B8BC80188579540B0E0A0E02A8DFC +:102360003B8D4C8D5D8D82179307A407B50708F050 +:1023700046C180E1F801DE01119601900D928A9527 +:10238000E1F741EF51E0B8018AE00E94DB0839E053 +:10239000398BD8011D968C911D9719969C91199790 +:1023A0001D969C931D9715969C91159719969C9335 +:1023B000199711969C91119715969C9315971196C4 +:1023C0008C93119712968C9112971A969C911A974A +:1023D00012969C9312971A968C931A9716968C9134 +:1023E00016971E969C911E9716969C9316971E966E +:1023F0008C931E9713968C91139717969C91179711 +:1024000013969C9313971B969C911B9717969C93DE +:1024100017971F969C911F971B969C931B971F962F +:102420008C93C8010E94A80841EF51E0B801898946 +:102430000E94DB084801F401B080C180D280738023 +:102440008B2D0E94BC088D8B0E94BC08E82E0E9438 +:10245000BC08F82E8C2D0E94BC088E8B0E94BC08F4 +:10246000482E0E94BC088A8B8D2D0E94BC08282E05 +:102470000E94BC08382E0E94BC08A82E872D0E94FE +:10248000BC08582E0E94BC08682E0E94BC089D897A +:102490009E259F25FE899F272A89922793259A2585 +:1024A00098279C259D259725D4019C939F2F9F2598 +:1024B0009425922792259A25962598279B259D2538 +:1024C000972511969C931197EF24E2262E2432240F +:1024D0003A24352438263B243C24732412967C92DB +:1024E000BD89FB264F24922F9425A9265A246524C2 +:1024F0008625B826CB24CD24F401C382F4E08F0EC8 +:10250000911C2F89388D2815390509F094CF398908 +:102510003150398B31113DCFD8011D968C911D97CB +:1025200019969C9119971D969C931D9715969C91B1 +:10253000159719969C93199711969C91119715963A +:102540009C93159711968C93119712968C911297D4 +:102550001A969C911A9712969C9312971A968C939E +:102560001A9716968C9116971E969C911E97169602 +:102570009C9316971E968C931E9713968C91139787 +:1025800017969C91179713969C9313971B969C9163 +:102590001B9717969C9317971F969C911F971B96B6 +:1025A0009C931B971F968C93C8010E94A80841EF2B +:1025B00051E0B80180E00E94DB08E1EAF2E0AB897B +:1025C000BC898D91BC8BAB8B9191119789278C9392 +:1025D0002B893C898216930691F780E1FE013196A2 +:1025E000A1EAB2E001900D928A95E1F7005F1F4FDA +:1025F0004F89588D405F5F4F588F4F8BA9CEE09128 +:102600007B058E2F8370823009F06ACE80917C0525 +:10261000582F5770F52E00917D0510917905209166 +:10262000780590917A053091BE0230683093BE02F1 +:102630003091E603931342C1E695E695E695EA31BB +:1026400008F0E9E1F0E0E858FA4F178283FF15C07F +:1026500021E2E8E7F5E0A7E5B5E001900D922A95C3 +:10266000E1F7877E806180935B0590935805109316 +:10267000590587E595E00E94841090917C0594FDB2 +:1026800015C183E0F81212C180917D0521EF280F5A +:10269000233008F425CE0430A9F480917E05909172 +:1026A0002801891719F08F3F09F01ACE88E795E0C5 +:1026B0000E943F0E9FEF980F9E3F08F011CE0E94A0 +:1026C0009E0D0ECE0830D9F40091BD0200FF08CE59 +:1026D00088E795E00E943F0E8F3F09F401CE8F5F9F +:1026E0008F3F09F4FDCD9091E803891708F0F8CDEC +:1026F00001FDF6CD8093E8031093E703F1CD083197 +:1027000059F429E143E050E06FEF812F0E94A103CB +:1027100061E00E94A40720C0093171F48091BD02DC +:1027200083FFDECD877F8093BD0288E795E00E941E +:102730003F0E8093BF02D4CD0E3109F4D1CD0D31BF +:1027400071F42FE143E050E06FEF80E00E94A103BD +:1027500060E07FEF0E948F070E944C10C1CD209156 +:1027600079052111A8C08D3059F488E198E00FB6A1 +:10277000F894A895809360000FBE90936000FFCFFF +:102780008B3179F488E795E00E943F0E91E081114A +:1027900001C090E08091B20290FB80F98093B20278 +:1027A0009FCD863099F490917F0581E0992319F0AF +:1027B0009D3409F080E08093B10241E050E063E095 +:1027C00071E081EB92E00E94522E8ACD833119F4A0 +:1027D0000E94161185CD823101F50E94C1076B015F +:1027E0007C0126E143E050E06FEF80E00E94A1030E +:1027F0008C0164E00E948107D80114968C91149793 +:102800008F71806A14968C93F801C782D086E18616 +:10281000F286C8010E94841063CD8230F9F422E070 +:1028200043E050E06FEF80E00E94A1038C0164E080 +:102830000E948107D80114968C9114978F71806A39 +:1028400014968C9314972FEF32E043E052E01796E2 +:102850002D933D934D935C931A97DBCF813001F517 +:10286000E0E0F0E0EF2B09F43BCD92959695977060 +:10287000953059F460917F057091800580918105B4 +:10288000909182050E9400002BCD911105C08FE729 +:1028900095E00E94802AF6CF60E070E0CB01F2CF95 +:1028A0008A3009F41DCD8C3109F41ACD88E795E002 +:1028B0000E94FA1515CD8A31C9F712CD9F3F09F054 +:1028C0000FCDF3E05F1345C08091BD0282FF03C0CE +:1028D000073009F405CD043109F002CD8091E703FA +:1028E0002813FECC0E94DF056B017C0123E0D2227D +:1028F000EE24FF240E94BA054B015C01C114D104EF +:10290000E104F104D1F425E143E050E06FEF812FC1 +:102910000E94A1036091E703FCCE31E0C31AD10805 +:10292000E108F10848EE840E43E0941EA11CB11C9E +:10293000C114D104E104F10409F30E94BA05681935 +:1029400079098A099B09683E73408105910528F73A +:10295000D5CF8091E7032813C3CCA8CF6D960FB6CF +:10296000F894DEBF0FBECDBFDF91CF911F910F91C5 +:10297000FF90EF90DF90CF90BF90AF909F908F909F +:102980007F906F905F904F903F902F90089580912F +:10299000BD0284FD0C9454110895CF92DF92EF9202 +:1029A000FF92CF93DF93D82F0E94DF056B017C014C +:1029B00080917C05876080937C05C0E00E94DF05E4 +:1029C0006C197D098E099F09603D7740810591054D +:1029D00090F4C11110C0A8950E94B6070E94C714B8 +:1029E00080917C058770833049F7C1E080917D0537 +:1029F0008D13E3CFE3CF8C2FDF91CF91FF90EF903A +:102A0000DF90CF9008958F929F92AF92BF92CF9216 +:102A1000DF92EF92FF924B015C010E94DF056B0198 +:102A20007C010E94DF056C197D098E099F096815DC +:102A300079058A059B0530F4A8950E94B6070E9487 +:102A4000C714EFCFFF90EF90DF90CF90BF90AF9083 +:102A50009F908F900895CF93C0E088E395E00E9407 +:102A60006106892B51F4CA3F41F0CF5F61E070E00D +:102A700080E090E00E940315F0CF88E395E00E948B +:102A800061061816190694F46AE070E080E090E0A0 +:102A90000E94031588E395E00E9461061816190646 +:102AA00064F488E395E00E947906F4CFCA3F29F4E4 +:102AB00082EF95E0CF910C948D0CCF910895EF9219 +:102AC000FF920F931F93CF93DF93CDB7DEB760973D +:102AD0000FB6F894DEBF0FBECDBF9EE799839EE090 +:102AE0009A839CE29B839FEE988B1C828D831E822F +:102AF0006F83188649871A8622502B871C861D866D +:102B00001E870F878E010F5F1F4F7E0181E1E80E48 +:102B1000F11C0E151F0541F0F80161918F0188E34A +:102B200095E00E940506F5CF60960FB6F894DEBFDB +:102B30000FBECDBFDF91CF911F910F91FF90EF900E +:102B40000C942B15EF92FF920F931F93CF937C0160 +:102B500088E395E00E946106892B11F00E94790CB0 +:102B60006EE788E395E00E940506C0E087010C0F40 +:102B7000111DF801649188E395E00E940506F801B3 +:102B800004910F3E09F4C0E1CF5FC03178F3CF91DB +:102B90001F910F91FF90EF900C942B15CF93C82F9E +:102BA00082E296E00E94F8048C2F90E00E94DF0CF5 +:102BB000CC23A1F08FEF809326018AE196E00E945A +:102BC000A21584E08093990461E070E083E994E0C9 +:102BD0000E948F0760E0CF910C949C101092260108 +:102BE0008EE196E00E94A21584E08093990470E043 +:102BF00060E0ECCFCF93DF93EC0186E893E00E9496 +:102C0000F8048E810E94110D8C8184FF0AC081E836 +:102C100093E00E948D0C81E08093F001DF91CF91D1 +:102C200008958D818230D1F78E818430B9F7CE013D +:102C30000E943F0EC1E0811101C0C0E08DE693E02B +:102C40000E94F8048C2F90E00E94DF0C8C2FDF9103 +:102C5000CF910C94CE15EF92FF920F931F93CF93C9 +:102C6000DF9300D000D01F92CDB7DEB79EE79983E7 +:102C700093E09A839B839FEE9D838C838E010F5FED +:102C80001F4F7E0186E0E80EF11C0E151F0541F076 +:102C9000F80161918F0188E395E00E940506F5CF68 +:102CA0000F900F900F900F900F90DF91CF911F9189 +:102CB0000F91FF90EF900C942B15EF92FF920F93D2 +:102CC0001F93CF93DF93CDB7DEB728970FB6F89455 +:102CD000DEBF0FBECDBF9EE7998396E09A8391E059 +:102CE0009B839FEE98871C828D831E826F838E014B +:102CF0000F5F1F4F7E0189E0E80EF11C0E151F05C6 +:102D000041F0F80161918F0188E395E00E9405068A +:102D1000F5CF28960FB6F894DEBF0FBECDBFDF917A +:102D2000CF911F910F91FF90EF900C942B150F9363 +:102D30001F938091F001882379F06AE070E082EDC2 +:102D400090E00E945D1681E00E942B168DE295E0D6 +:102D50001F910F910C94A21510E000E029E130E0E2 +:102D600042E350E070E060E08EEB90E00E945F157F +:102D70001F910F910895FF920F931F93CF93DF93AD +:102D8000F82E192F062F6EE788E395E00E940506BE +:102D900062E0600F88E395E00E94050661E188E348 +:102DA00095E00E940506CF2DD12F0C0F1D2F111D70 +:102DB000C017D10731F0699188E395E00E940506BC +:102DC000F7CF6FEE88E395E00E940506DF91CF9183 +:102DD0001F910F91FF900C942B15FF920F931F934F +:102DE000CF93DF9395E1899F800111240A5C1A4FEC +:102DF000C8010E94A72AF82E6EE788E395E00E949A +:102E0000050662E06F0D88E395E00E94050661E12A +:102E100088E395E00E940506D0E0C0E0CF1550F4AD +:102E2000F801EC0FFD1F649188E395E00E94050610 +:102E30002196F4CF6FEE88E395E00E940506DF91BE +:102E4000CF911F910F91FF900C942B15EF92FF9251 +:102E50000F931F93CF93DF93CDB7DEB72C970FB6A9 +:102E6000F894DEBF0FBECDBF9EE799839AE09A83A8 +:102E700098E29B839FEE9C871C828D837E836F8369 +:102E8000188649873A872B878E010F5F1F4F7E0177 +:102E90008DE0E80EF11C0E151F0541F0F80161915F +:102EA0008F0188E395E00E940506F5CF2C960FB6BA +:102EB000F894DEBF0FBECDBFDF91CF911F910F9170 +:102EC000FF90EF900C942B150F931F93CF93DF93EC +:102ED0000E94790381EF93E00E948D0C60ED77E012 +:102EE00080E090E00E940315109253051092520565 +:102EF000109251051092500510924F0510924E05F8 +:102F00008DE991E090935505809354059091440587 +:102F1000963108F047C02DEB30E030934F052093F9 +:102F20004E052BE931E030935105209350052CE2FA +:102F300031E030935305209352052091680084E0DE +:102F4000983048F031E09E3008F430E081E001C074 +:102F5000880F3A95EAF7822B809368002DE630E0DF +:102F6000983050F02BE630E09E3030F02CE630E028 +:102F7000963110F030E020E030934C0520934B0563 +:102F8000292F30E0983008F0DEC081E001C0880FC2 +:102F90009A95EAF780934D058091540590915505D7 +:102FA0000197F1F78091520590915305892B49F1D2 +:102FB000E0914903F0914A0385E0E833F80709F10D +:102FC000309739F0A389B4899C91858980958923AC +:102FD0008C93809156058E7F8093560510924703FF +:102FE0001092480388E395E090934A038093490345 +:102FF000E0914B05F0914C05808190914D05892B16 +:10300000808360ED77E080E090E00E9403158AE124 +:1030100096E00E94A2158DEE93E00E94A21588EE24 +:1030200093E00E94A21583EE93E00E94A21581E036 +:103030000E942B166AE070E08AE090E00E945D1624 +:1030400080E00E94ED1602E810E120E530E040EF5C +:1030500050E060E570E090E080E00E945F156AE576 +:1030600070E08AE090E00E945D1681E00E94ED161B +:103070006AEA70E08AE090E00E945D1682E00E94B9 +:10308000ED168091BD0282FF6BC08DEC93E00E9433 +:103090008D0C81E08093F0019091B102911101C0FB +:1030A00080E080931F011092C5031092C403109218 +:1030B0007A0310929C031092C30381E08093C203B1 +:1030C0001092BD0361E082E10E943D0561E083E171 +:1030D0000E943D058091B9008E7F8093B900809158 +:1030E000B9008D7F8093B90088E48093B80085E4AF +:1030F0008093BC008BEC96E09093E1028093E00219 +:103100008CEC96E09093E5028093E40280ED0E94BF +:103110008109803651F183EA93E00E948D0C6CE0C6 +:1031200080E791E00E94BB1688E19FE00FB6F8941B +:10313000A895809360000FBE90936000DF91CF91BF +:103140001F910F9108959E3040F42850310981E07D +:1031500001C0880F2A95EAF71DCF2E503109F7CF0D +:1031600082EB93E00E948D0C9ECF88E80E947B0941 +:1031700090935503809354038AE80E947B099093AF +:103180005703809356038CE80E947B099093590360 +:10319000809358038EE80E947B0990935B03809391 +:1031A0005A0380E90E947B0990935D0380935C033E +:1031B00082E90E947B0990935F0380935E0384E918 +:1031C0000E947B09909361038093600386E90E94CB +:1031D0007B09909363038093620388E90E947B09D3 +:1031E00090936503809364038AE90E947B0990931E +:1031F0006703809366038CE90E947B0990936903BF +:10320000809368038EE90E947B0990936B038093FF +:103210006A0381EA0E94810980936C0381EE0E9417 +:103220007B0990936E0380936D0383EE0E94810966 +:1032300080936F0384EE0E948109C82F85EE0E945F +:10324000810990E1C99F900111248F70282B309340 +:1032500071032093700386EE0E948109C82F85EECA +:103260000E94810990E1C99FE001112490E024E0CF +:10327000959587952A95E1F78C2B9D2B9093730359 +:103280008093720387EE0E9481098093740365E046 +:1032900082EF0E944B0767EB84EF0E944B0744CFFD +:1032A0004F925F926F927F928F929F92AF92BF9256 +:1032B000CF92DF92EF92FF920F931F93CF93DF9302 +:1032C000CDB7DEB72A970FB6F894DEBF0FBECDBFDD +:1032D000809121018823A1F01092210184E0809344 +:1032E000990461E070E083E994E00E948F0760E058 +:1032F0000E949C1064E670E080E090E00E9403155C +:1033000088E395E00E9461069A87898789859A8576 +:1033100005970CF446C088E395E00E94EC058E37D3 +:10332000A1F510E000E001C001E088E395E00E9413 +:103330006106892BB1F188E395E00E9479060023AC +:1033400061F0E12FF0E0ED54FE4F8F3E49F08083B5 +:103350001F5F183008F017E001E08E3731F7E4CF37 +:1033600010828091B3018630E1F68091B4018730FC +:10337000C1F680912601811106C081E00E94CE1520 +:103380000E94790CCECF80E0F9CF88E395E00E94CF +:103390007906182F84E594E00E94F804812F0E949A +:1033A000110D0E94DF050091D6011091D7012091E7 +:1033B000D8013091D901601B710B820B930B683ED1 +:1033C000734081059105D0F00E94DF056093D6011E +:1033D0007093D7018093D8019093D90180912001F7 +:1033E0008C3318F110922001A89580912001813032 +:1033F00009F4D5C1F0F0823009F427C32A960FB63C +:10340000F894DEBF0FBECDBFDF91CF911F910F911A +:10341000FF90EF90DF90CF90BF90AF909F908F90F4 +:103420007F906F905F904F9008958F5F8093200101 +:10343000DBCF0E94FA096B017C01C092D201D092CD +:10344000D301E092D401F092D50120E030E04EEFBC +:1034500052EC0E941E2D882389F220E030E04AEAD7 +:1034600052E4C701B6010E941E2D882339F2209133 +:10347000CE013091CF014091D0015091D101C701CF +:10348000B6010E941E2D882309F4B8CFC092CE0148 +:10349000D092CF01E092D001F092D10180911F0132 +:1034A00081111CC020E030E040E151E4C701B601C9 +:1034B0000E94842D20E030E040EA50E40E947B2FFF +:1034C00020E030E040E052E40E940F2F6093D201F0 +:1034D0007093D3018093D4019093D5018EE195E050 +:1034E0000E94F804C090D201D090D301E090D401A2 +:1034F000F090D501A7019601C701B6010E942830BE +:10350000882309F471C08DE791E00E946C0C87E676 +:1035100091E00E946C0C1092F001C090D201D0900A +:10352000D301E090D401F090D50165E087E195E00A +:103530000E94810780911B05806E80931B05C092BD +:103540001E05D0921F05E0922005F092210581E032 +:103550008093220561E087E195E00E949C100E9423 +:10356000971610E000E027E330E040EF50E069E11B +:1035700070E090E080E00E945F1563E270E08AE016 +:1035800090E00E945D1684E00E942B166091D201AB +:103590007091D3018091D4019091D5018E010F5F7C +:1035A0001F4F21E040E00E94A029C090D201D0909E +:1035B000D301E090D401F090D50120E030E0A901E2 +:1035C000C701B6010E94233018160CF09FC020E0FE +:1035D00030E040E251E4C701B6010E941E2D13E025 +:1035E00087FDA9C014E0A7C046015701E894B7F8C9 +:1035F0002FEF3FEF4FE75FE7C501B4010E9428308E +:1036000081110DC02FEF3FEF4FE75FE7C501B40118 +:103610000E941E2D18161CF481E891E076CF2FEF42 +:103620003FEF4FE75FE4C701B6010E942330181651 +:103630001CF485E891E069CF2FEF3FEF4FE75FEC97 +:10364000C701B6010E941E2D87FDF3CF20E030E0B8 +:10365000A901C701B6010E941E2D87FF09C06DE2B6 +:103660008AEE93E00E94AB04F7FAF094F7F8F09436 +:103670002AE037ED43EA5BE3C701B6010E940F2F52 +:103680004B015C010E94F42F6B017C010E94232DF1 +:103690009B01AC01C501B4010E940E2F4B015C01DE +:1036A0004AE0C701B6010E949B0C6EE28AEE93E0ED +:1036B0000E94AB0420E030E040E251E4C501B401D7 +:1036C0000E94842D4B015C010E94F42F6B01F12CB0 +:1036D000E12C4AE0C701B6010E949B0CC701B6016C +:1036E0000E94232D9B01AC01C501B4010E940E2F45 +:1036F00020E030E040E251E40E94842D0E94F42F4B +:1037000090E080E04AE00E949B0C01CF20E030E096 +:1037100040E251ECC701B6010E94233013E01816B5 +:103720000CF014E020E030E0A901C701B6010E94CE +:10373000233018162CF520E030E046EC52E4C701A7 +:10374000B6010E94233018162CF120E030E0A901C8 +:10375000C701B6010E941E2D87FD1F5F612FCE019C +:1037600001960E94BB1682E00E942B1680911F01D9 +:1037700061E0882389F089E891E00E94BB163ECE83 +:1037800020E030E046EC52ECC701B6010E941E2D4D +:1037900087FFDBCF15E0D9CF8BE891E0EECF0E9419 +:1037A000FA098DEF0E944C09809075039090760382 +:1037B000A0907703B09078033CE2931A31E0A30A1B +:1037C000B108B0E0A0E06C017D013EE0CC0CDD1C56 +:1037D000EE1CFF1C3A95D1F7809170039091710314 +:1037E000092E000CAA0BBB0B44E1880F991FAA1FDE +:1037F000BB1F4A95D1F7C81AD90AEA0AFB0AA09159 +:103800007203B0917303A50194010E949E2EC61A03 +:10381000D70AE80AF90A80E4D80EE11CF11C6FE02F +:10382000F594E794D794C7946A95D1F7A0916F0364 +:10383000B0E00E94932E2B013C017BE075946794CD +:10384000579447947A95D1F790E8590E611C711CF2 +:10385000A09174030A2E000CBB0B0E949E2EEAE07E +:103860009595879577956795EA95D1F7A30192018C +:103870000E942D309B01AC01FAE055954795379594 +:103880002795FA95D1F7405E5F4FA0916D03B091F7 +:103890006E030E949E2E705E8F4F9F4FAEE09595F7 +:1038A000879577956795AA95D1F7A70196010E940C +:1038B0002D306B017C01BFE09595879577956795D5 +:1038C000BA95D1F79B01AC010E942D309B01AC0150 +:1038D00017E055954795379527951A95D1F7A091FB +:1038E0006C03B0E00E94932E04E095958795779540 +:1038F00067950A95D1F7C61AD70AE80AF90AF7FEBA +:1039000003C0C12CD12C7601C701B601613071050D +:103910008105E9E19E0724F060E070E080E099E134 +:103920002CE095958795779567952A95D1F70E9414 +:10393000232D20E030E040E85AE30E94842D0E94CD +:10394000F42F162F6093CD018091CC01181709F444 +:1039500055CD1093CC018CE095E00E94F804812FA6 +:103960000E94110D6091CD0186EF94E00E94A407A2 +:1039700060E00E949C100E94971602E810E127E385 +:1039800030E040EF50E069E670E090E080E00E94B7 +:103990005F1563E770E08AE090E00E945D1684E0C6 +:1039A0000E942B161091CD01E12EF12C86E894E0B7 +:1039B0000E94F804C7010E94DF0C8DE891E00E948C +:1039C0006C0CC7010E94DF0C1C821B828EE789836E +:1039D00086E08A8383E18B838AE08D838FEE8887FC +:1039E0001E821F838E010F5F1F4F7E01F9E0EF0ED5 +:1039F000F11CE016F10641F0F80161918F0188E3B6 +:103A000095E00E940506F5CF0E942B1582E00E94EA +:103A10002B1661E085E991E00E94BB1684E00E94CC +:103A20002B168091CD019FEF980F9D3120F464E01B +:103A300087E991E0A2CE813420F465E08CE991E041 +:103A40009CCE66E082EA91E098CE0E94740A0E94C1 +:103A5000740A24E630E040E050E00E94DD2ECA0106 +:103A6000B9010E94232D20E030E040E05FE30E9496 +:103A70000F2F0E94ED2F6B017C017093CB0160939F +:103A8000CA018AEF94E00E94F804C701B6018827B2 +:103A900099274AE00E949B0C87E691E00E946C0CFB +:103AA0006091CA017091CB0190E080E00E94232DCB +:103AB0006B017C0165E085ED94E00E9481078091B7 +:103AC000D904806E8093D904C092DC04D092DD04C6 +:103AD000E092DE04F092DF0481E08093E00460E095 +:103AE00085ED94E00E949C10A090CA01B090CB019B +:103AF0001091C701812F65E00E943D30F4E09F9F47 +:103B0000C00111249C0124523E4F7901C50164E695 +:103B100070E00E94493090E080E00E94232DF70180 +:103B200060837183828393831F5F1A3B08F08CC08C +:103B30001093C7018091C701853009F089C00E94A8 +:103B400067076093C3017093C4018093C50190938C +:103B5000C60180911C01882309F49FC085E08093F1 +:103B60001E012091C8013091C901A216B30629F4A3 +:103B700090911D01981709F441CCB092C901A0920F +:103B8000C80180931D0110E000E027E830E040EF1D +:103B900050E069EB70E090E080E00E945F1563EC1C +:103BA00070E08AE090E00E945D1682E00E942B1691 +:103BB0009E012F5F3F4F790186E0F90111928A95AE +:103BC000E9F76091CA017091CB0190E080E00E941A +:103BD000232D870120E040E00E94A02964E0C70176 +:103BE0000E94BB166CED70E08AE090E00E945D16CA +:103BF00080911E01853081F482EF94E00E948D0C4B +:103C00006AEE74E084EB94E00E94DA0D60E00E94BA +:103C10009C1088E00E94ED1680911E0181116BC1FD +:103C200083EE94E00E948D0C6CED74E084EB94E0E4 +:103C30000E94DA0D60E00E949C1084E00E942B1626 +:103C400083E00E94ED16DACB86E08093C70172CF45 +:103C50008332B9F50E9467072091C3013091C401F6 +:103C60004091C5015091C6010E940E2F2DEC3CECF5 +:103C70004CEC5DE30E94842D20911C01222309F469 +:103C800058C09B01AC010E940F2F6093BF0170933D +:103C9000C0018093C1019093C201C090BF01D09038 +:103CA000C001E090C101F090C20120E030E040E8A6 +:103CB0005EEBC701B6010E941E2D87FFCCC084E0D9 +:103CC0004ECF8134E9F40E9467072091C3013091FF +:103CD000C4014091C5015091C6010E940E2F2DECE8 +:103CE0003CEC4CEC5DE30E94842D20911C012111E1 +:103CF000CCCF20E030E040E05FE30E94842DC5CFD0 +:103D00008F35F1F40E9467072091C3013091C401FF +:103D10004091C5015091C6010E940E2F2DEC3CEC44 +:103D20004CEC5DE30E94842D20911C01222309F4B8 +:103D300044C020E030E040EC5FE30E947B2FA5CF41 +:103D40008D3719F50E9467076093BB017093BC0122 +:103D50008093BD019093BE012091C3013091C401B5 +:103D60004091C5015091C6010E940E2F2DEC3CECF4 +:103D70004CEC5DE30E94842D20911C012111B9CFF0 +:103D800020E030E040E450E4D8CF8B3901F50E94C8 +:103D900067072091C3013091C4014091C501509142 +:103DA000C6010E940E2F2DEC3CEC4CEC5DE30E9412 +:103DB000842D20911C01222329F020E030E040E2F4 +:103DC00050E4BBCF20E030E040E650E4B6CF893B82 +:103DD000F1F50E9467072091C3013091C401409121 +:103DE000C5015091C6010E940E2F2DEC3CEC4CEC0D +:103DF0005DE30E94842D20911C01222309F120E023 +:103E000030E040E450E40E947B2F6093BF01709348 +:103E1000C0018093C1019093C2018091BB01909138 +:103E2000BC01A091BD01B091BE018093C3019093EC +:103E3000C401A093C501B093C60110921C012DCFFF +:103E400020E030E040E85EE30E94842DDECF833244 +:103E500008F47FCE22CF20E030E040E85EE3C701E7 +:103E6000B6010E94233018160CF43FC020E030E069 +:103E700040E85EEBC701B6010E942330181654F4E7 +:103E80002DEC3CEC4CE45DEBC701B6010E941E2D0D +:103E900087FD2DC02DEC3CEC4CE45DE3C701B60181 +:103EA0000E942330181654F420E030E040E85EE32E +:103EB000C701B6010E941E2D87FD1BC02DEC3CECF6 +:103EC0004CE45DEBC701B6010E94233018160CF0DC +:103ED00045CE2DEC3CEC4CE45DE3C701B6010E94FD +:103EE0001E2D87FF3BCE80E03ACE83E038CE82E0C5 +:103EF00036CE81E034CE833091F483ED94E00E949D +:103F00008D0C6AEC74E084EB94E00E94DA0D60E0C2 +:103F10000E949C1084E00E942B1686E092CE813095 +:103F200031F584EC94E00E948D0C6EEB74E084EB30 +:103F300094E00E94DA0D60E00E949C1020EE3FEFBA +:103F400048E250E060EF70E088E790E00E942617BA +:103F500026E63EEF4EE150E060EF70E088E790E04B +:103F60000E94261781E00E942B1684E06ACE8230E0 +:103F7000B1F100E018EF843009F040CA980148E23E +:103F800050E060EF70E088E790E00E94261798010B +:103F90004EE150E06AEF70E080E590E00E94261765 +:103FA000980144E150E064E071E080EA90E00E9412 +:103FB000261781E00E942B1680911E01843091F417 +:103FC00081EB94E00E948D0C64EA74E084EB94E051 +:103FD0000E94DA0D60E00E949C1087E032CE0FEF65 +:103FE0001FEFCCCF8DE994E00E948D0C66E974E060 +:103FF00084EB94E00E94DA0D60E00E949C1085E062 +:1040000020CECF938091B20281FD20C00E94161174 +:104010008091B20281608093B202C4E02AE143E061 +:1040200050E06FEF80E00E94A10362E00E94A407CD +:104030000E9484108BE10E94CD14811102C0C150F6 +:1040400069F78091B20282608093B202CF910895A5 +:104050008FEF8093BF02EDEBF2E080818860808378 +:1040600028E143E050E06FEF80E00E94A10361E0AF +:104070000E94A4070C944C108091E6038F3FB9F482 +:104080000E94DF056F3F81F06093280123E043E049 +:1040900050E06091280180E00E94A10369E671E090 +:1040A0000E940C0E0C944C1010922801EFCF089532 +:1040B000EDEBF2E08081897F81608083E6EEF3E0C2 +:1040C0008FEF8283818327E043E050E06FEF0E940F +:1040D000A10369E671E00E940C0E0C944C100F9342 +:1040E0001F93CF93DF93CDB7DEB760970FB6F894E9 +:1040F000DEBF0FBECDBF8E010F5F1F4F80E1F80105 +:1041000011928A95E9F760E170E089EA91E00E94F6 +:10411000BF2AAC0169EA71E0C8010E94C331229A4A +:10412000219A2A98299A1FB7F8948091B201811197 +:1041300027C0ECE7F6E08491E0E9F6E09491E82FFF +:10414000F0E0EE0FFF1FE05CF94FA591B491EC9108 +:10415000E92321F461E08AE00E943D0561E08AE004 +:104160000E9489058CB580618CBD8CB580648CBD46 +:1041700061E08DE00E94890561E08BE00E94890585 +:104180008091B2018F5F8093B2011FBF2A986EE0C9 +:1041900080E20E94640D8BE496E40197F1F763E0FE +:1041A00083E20E94640D6FE584E20E94640D6CE47A +:1041B00085E20E94640D67E086E20E94640D64E07F +:1041C0008DE30E94640D8DE10E94B80D843041F0B2 +:1041D00063E780E50E94640D64E08DE30E94640D56 +:1041E00086E00E94B80D873009F496C090E089817E +:1041F0008093F1018A818093F2018B818093F30196 +:104200008C818093F4018D818093F5018E81809360 +:10421000F6018F818093F70188858093F801898565 +:104220008093F9018A858093FA018B858093FB0145 +:104230008C858093FC018D858093FD018E85809314 +:10424000FE018F858093FF01888980930002A1EF92 +:10425000B1E024E030E01C968C911C971D967C9177 +:104260001D971E965C911E971F964C911F97F90102 +:10427000E370FF27EF2BF1F4E72FF0E0E359FD4F58 +:104280006491E52FF0E0E359FD4F7491E42FF0E0E5 +:10429000E359FD4F5491E82FF0E0E359FD4F44916D +:1042A000F901F695E795F695E795EE59FD4FE491FE +:1042B000862F8E27EC91E8275096EC935097119615 +:1042C0008C911197872751968C9351971296EC9168 +:1042D0001297E5275296EC9352971396EC91139709 +:1042E000E4275396EC9353972F5F3F4F14962C324D +:1042F000310509F0B0CF911145C088E091E00E94EE +:10430000D40760960FB6F894DEBF0FBECDBFDF9125 +:10431000CF911F910F91089585E00E94B80D8C34C4 +:1043200009F064CF62E082E20E94640D60E081E205 +:104330000E94640D63E08CE30E94640D8FEF809314 +:10434000030120E045E063E071E08BE20E94240D70 +:1043500020E045E063E071E08AE20E94240D20E065 +:1043600045E063E071E080E30E94240D82EE0E944C +:10437000BE0D81EE0E94BE0D60E787E20E94640DD3 +:1043800091E035CF8091BD0280618093BD02809124 +:10439000E9038E7F8093E9038091E6038F3F19F450 +:1043A00088E191E0ACCF9FEF980F9E3F08F0A5CF3A +:1043B0000E949E0D8111F4CFA0CF1F920F920FB6D5 +:1043C0000F9211242F933F934F935F936F937F939B +:1043D0008F939F93AF93BF93EF93FF938AEE93E0F6 +:1043E0000E948904FF91EF91BF91AF919F918F91AE +:1043F0007F916F915F914F913F912F910F900FBEE1 +:104400000F901F9018951F920F920FB60F921124C4 +:104410002F938F939F93EF93FF93E091FA03F09183 +:10442000FB038081E0910004F091010482FD1BC038 +:104430009081809103048F5F8F7320910404821711 +:1044400041F0E0910304F0E0E651FC4F958F80933A +:104450000304FF91EF919F918F912F910F900FBEC9 +:104460000F901F9018958081F4CF1F920F920FB676 +:104470000F9211242F933F938F939F93AF93BF93EA +:1044800080914C0390914D03A0914E03B0914F0346 +:1044900030914B0323E0230F2D3758F50196A11DD2 +:1044A000B11D20934B0380934C0390934D03A09335 +:1044B0004E03B0934F038091500390915103A0910C +:1044C0005203B09153030196A11DB11D8093500377 +:1044D00090935103A0935203B0935303BF91AF91B4 +:1044E0009F918F913F912F910F900FBE0F901F9032 +:1044F000189526E8230F0296A11DB11DD2CF1F9259 +:104500000F920FB60F9211242F933F934F935F9307 +:104510006F937F938F939F93AF93BF93EF93FF938B +:10452000E0912B01F0912C010995FF91EF91BF9142 +:10453000AF919F918F917F916F915F914F913F913B +:104540002F910F900FBE0F901F9018951F920F92F2 +:104550000FB60F9211242F933F934F935F936F9356 +:104560007F938F939F93AF93BF93EF93FF93E091CC +:104570002901F0912A010995FF91EF91BF91AF9127 +:104580009F918F917F916F915F914F913F912F916B +:104590000F900FBE0F901F9018951F920F920FB69D +:1045A0000F9211242F933F934F935F936F937F93B9 +:1045B0008F939F93AF93BF93EF93FF93E091490342 +:1045C000F0914A03309749F0A685B7858585968D89 +:1045D00091FF14C09C918923A1F4FF91EF91BF91A9 +:1045E000AF919F918F917F916F915F914F913F918B +:1045F0002F910F900FBE0F901F9018959C918923BB +:1046000061F7A389B4899C918589809589238C93CE +:10461000868997890197F1F7608D718DA685B78599 +:10462000558538E020E0CB010197F1F7822F90E02B +:1046300095958795282F4C91452309F02068315096 +:1046400091F7868D81FD20958091470390E001963A +:104650008F73992730914803381799F0A091470339 +:10466000B0E0A95FBC4F2C9380934703828D938D5C +:104670000197F1F7A389B4898C919589892B8C9343 +:10468000ACCF868D8160868FF1CF1F920F920FB6CF +:104690000F9211242F933F934F935F936F937F93C8 +:1046A0008F939F93AF93BF93EF93FF938091B90044 +:1046B000887F803609F44AC0F0F5883209F4A7C033 +:1046C00018F5803109F49BC0B8F4882309F4F9C0C7 +:1046D000883009F494C0FF91EF91BF91AF919F9101 +:1046E0008F917F916F915F914F913F912F910F909B +:1046F0000FBE0F901F901895883109F488C0803242 +:1047000051F78093C10314C0803409F49DC040F474 +:104710008033B9F38833F9F68093C10385ECB0C0D8 +:10472000803509F485C0883509F496C0883499F637 +:104730000E949A06D0CF883909F48CC038F58837A2 +:1047400029F050F4883611F0803729F683E0809301 +:10475000C3031092060357C0883809F47BC0803920 +:1047600019F0803809F0B7CF80910603803208F045 +:1047700071C0E091060381E08E0F80930603809163 +:10478000BB00F0E0EA51FD4F80833DC0803B39F033 +:10479000E0F4803A09F479C0883A09F09CCF84E0CB +:1047A0008093C3031092E3021092E202E091E002D0 +:1047B000F091E10209958091E20281110FC081E040 +:1047C0008093E2021092C00209C0803C09F4A6CF97 +:1047D000883C09F4A3CF883B09F07DCFE091E30248 +:1047E00081E08E0F8093E302F0E0E054FD4F808182 +:1047F0008093BB009091E3028091E20229C08091F6 +:10480000BE038093BB0085EC8093BC0064CF909185 +:10481000C0038091BF03981758F5E091C00381E071 +:104820008E0F8093C003F0E0E356FC4F8081E9CF08 +:10483000E091C00381E08E0F8093C0038091BB00A4 +:10484000F0E0E356FC4F80839091C0038091BF035A +:104850009817C8F285E8D8CFE091C00381E08E0FA9 +:104860008093C0038091BB00F0E0E356FC4F80834F +:104870008091C20381115CCF81E08093BD0384EA03 +:104880008093BC001092C30326CF85EC8093BC00BC +:104890001092C30380910603803230F4E091060346 +:1048A000F0E0EA51FD4F10826091060370E0E09164 +:1048B000E402F091E50286EE92E00995109206037B +:1048C0000ACF1092C10334CF1F920F920FB60F92EE +:1048D00011240F900FBE0F901F901895DB01FC0163 +:1048E00058E0149744E018960E9001924A95E1F72B +:1048F0005A95C1F708958F929F92AF92BF92CF922F +:10490000DF920F931F93EDB7FEB7B8970FB6F894E9 +:10491000EDBF0FBEFEBF9F938F9331968F0149016C +:104920005A016B01C801AAD2BB2011F4AA2059F088 +:10493000C801B601A1D030E4C30ED11C32E0931AF5 +:10494000A108B108F1CFC801B601A40115D08F911B +:104950009F91B801C3DFEDB7FEB7B8960FB6F894D4 +:10496000EDBF0FBEFEBF1F910F91DF90CF90BF90A4 +:10497000AF909F908F900895523090F09F938F93B7 +:104980007F936F935F934F9377D04F915F916F9128 +:104990007F918F919F91525030E4630F711DECCF46 +:1049A000EDB7FEB70FB6E054F040F894EDBF0FBE80 +:1049B000FEBF3196242F26952695269550FB25F986 +:1049C000DB01222329F0122E0D9001921A94E1F7B7 +:1049D00030E8032E37E0342329F006943A95E9F7BE +:1049E0003C91032A01922395293308F421C030E435 +:1049F000321B19F011923A95E9F7FF973197BF01F1 +:104A0000FF93EF939F938F935F934F9335D04F9185 +:104A10005F918F919F91EF91FF91DC0191963C9175 +:104A200032503D9326E03C9130403D932A95D9F792 +:104A300038E3321B21F0112411923A95E9F7DC0199 +:104A4000909638960D90040E02920D90051E0292DB +:104A500036E00D90011C02923A95D9F7F897BF0104 +:104A60000BD0EDB7FEB70FB6FF963196F894EDBFB9 +:104A70000FBEFEBF112408954F925F926F927F92F6 +:104A80008F929F92AF92BF92CF92DF92EF92FF925E +:104A90000F931F93CF93DF934DB75EB79A01FB013E +:104AA00040525140DA010FB6F8944DBF0FBE5EBFC1 +:104AB0002F933F938F939F93119640E17191619152 +:104AC000319121912D933D936D937D934A95B1F74B +:104AD00040E3142EFD01FF9731970191119121912F +:104AE0003191C190D190E190F1906D2D7E2D8F2D5F +:104AF0009C2D41E0E9D14B015C01B701C60142E0C8 +:104B0000F9D186269726A826B92673E0F694E7946D +:104B1000D794C7947A95D1F78C249D24AE24BF24D2 +:104B2000080D191D2A1D3B1D648D758D868D978D71 +:104B3000060F171F281F391FC0A8D1A8E2A8F3A885 +:104B4000B701C60141E0D6D15C014B0142E0D2D1B0 +:104B500086269726A826B92672E0F694E794D7947D +:104B60007A95D9F78D249E24AF24080D191D2A1D8E +:104B70003B1D0D931D932D933D931A9409F0AACFDD +:104B8000FF91EF91EF93FF9390E211901D929A9510 +:104B9000E1F79097ED01ECE7FDE4BA9590E4192E6A +:104BA0004C885D886E887F88688979898A899B8925 +:104BB000C88CD98CEA8CFB8C462257226822792239 +:104BC0006095709580959095C622D722E822F922AB +:104BD0004C245D246E247F24F888C988DA88EB8809 +:104BE000B601C70142E070D14B015C01B601C701BB +:104BF00043E080D186269726A826B926B701C601AC +:104C000041E078D186269726A826B926480C591C5B +:104C10006A1C7B1C8C8C9D8CAE8CBF8C480C591CE8 +:104C20006A1C7B1C8D909D90AD90BD90480C591CCA +:104C30006A1C7B1C85909590A590B590480C591CDA +:104C40006A1C7B1C688179818A819B818C809D8014 +:104C5000AE80BF8086229722A822B922C884D98438 +:104C6000EA84FB846C217D218E219F218626972654 +:104C7000A826B9266C817D818E819F816C217D2142 +:104C80008E219F2186269726A826B926C880D980FE +:104C9000EA80FB80B601C70142E02CD18B019C0168 +:104CA000B701C60143E010D1062717272827392767 +:104CB0006F2D7C2D8D2D9E2D42E006D106271727C6 +:104CC00028273927080D191D2A1D3B1D5CE16C960C +:104CD0009A919C835A95E1F7C888D988EA88FB881D +:104CE000C40CD51CE61CF71CC88AD98AEA8AFB8A40 +:104CF000040D151D261D371D088319832A833B8348 +:104D00001A9409F04DCFFF91EF9158E00081118185 +:104D1000228133816991799189919991060F171FA9 +:104D2000281F391F01931193219331935A9571F7DD +:104D3000319652E066E04081450F4193552728F4B3 +:104D40004081451F41936A95D1F75F914F910FB60E +:104D5000F8944DBF0FBE5EBF1124DF91CF911F911C +:104D60000F91FF90EF90DF90CF90BF90AF909F900A +:104D70008F907F906F905F904F900895982F8A4208 +:104D800091443771CFFBC0B5A5DBB5E95BC256399D +:104D9000F111F159A4823F92D55E1CAB98AA07D8B5 +:104DA000015B8312BE853124C37D0C55745DBE72D8 +:104DB000FEB1DE80A706DC9B74F19BC1C1699BE458 +:104DC0008647BEEFC69DC10FCCA10C246F2CE92DE8 +:104DD000AA84744ADCA9B05CDA88F97652513E980C +:104DE0006DC631A8C82703B0C77F59BFF30BE0C613 +:104DF0004791A7D55163CA0667292914850AB727A1 +:104E000038211B2EFC6D2C4D130D385354730A653D +:104E1000BB0A6A762EC9C281852C7292A1E8BFA214 +:104E20004B661AA8708B4BC2A3516CC719E892D17C +:104E3000240699D685350EF470A06A1016C1A419FF +:104E4000086C371E4C774827B5BCB034B30C1C39FE +:104E50004AAAD84E4FCA9C5BF36F2E68EE828F74BD +:104E60006F63A5781478C8840802C78CFAFFBE90D7 +:104E7000EB6C50A4F7A3F9BEF27871C6DC01EEE842 +:104E8000FEE468E275917D936A95E1F7089567E61F +:104E9000096A85AE67BB72F36E3C3AF54FA57F5247 +:104EA0000E518C68059BABD9831F19CDE05B0000C8 +:104EB000000000000000483038F0592F982F872F4D +:104EC000762F652F4850F7CF55278894442339F023 +:104ED000661F771F881F991F551F4A95C9F7652BB5 +:104EE0000895483038F0562F672F782F892F952F47 +:104EF0004850F7CF55278894442339F097958795E4 +:104F00007795679557954A95C9F7952B0895AB0105 +:104F1000692F782F852F942FCF93DF93FC01108278 +:104F200011821282148215821682DC0117968AE1A0 +:104F3000ED0119928A95E9F782E08383DF91CF91A1 +:104F40000895CF92DF92EF92FF92CF93DF9310926A +:104F5000ED031092EC0398EEC92E93E0D92EE12CCC +:104F6000F12CC092EE03D092EF03E092F003F092A6 +:104F7000F10385E391E09093EB038093EA0385ECE2 +:104F800090E09093F7038093F60384EC90E0909385 +:104F9000F9038093F80380EC90E09093FB038093F7 +:104FA000FA0381EC90E09093FD038093FC0382EC84 +:104FB00090E09093FF038093FE0386EC90E0909343 +:104FC00001048093000410920304109204041092D0 +:104FD00005041092060410928A0410928904C0926B +:104FE0008B04D0928C04E0928D04F0928E0487E4BE +:104FF00091E0909388048093870488E795E00E946D +:105000008C2787E595E00E948C2710923B05109233 +:105010003A05C0923C05D0923D05E0923E05F092E3 +:105020003F0589E591E0909339058093380510920A +:105030004F0510924E051092510510925005109296 +:105040005305109252051092550510925405609127 +:1050500056056E7F6D7F609356056695617081E0A1 +:10506000682786E00E943D0561E086E00E94890590 +:10507000ECE8F6E0E491E0934805E8E7F6E0E49137 +:10508000F0E0EE0FFF1FE859F94F8591949190934E +:105090004A058093490560E085E00E94890580917A +:1050A000560581FD04C061E085E00E943D0585E074 +:1050B00080934405EBE8F6E0E491E0934505E7E7EB +:1050C000F6E0E491F0E0EE0FFF1FE25AF94F859110 +:1050D0009491909347058093460587E195E00E945F +:1050E0008C2710921D0510921C0586EF94E00E94FB +:1050F0008C2781E08093FC048093FB0485ED94E091 +:105100000E948C27C2E0C093DB04D4E0D093DA0481 +:1051100084EB94E00E948C2783E08093BA048FE2B2 +:105120008093B90483E994E00E948C27D09399047A +:10513000C0939804DF91CF91FF90EF90DF90CF90D4 +:105140000895CF93DF93CDB7DEB7CA55D1090FB617 +:10515000F894DEBF0FBECDBF789484B5826084BD65 +:1051600084B5816084BD85B5826085BD85B581606B +:1051700085BD80916E00816080936E0010928100E9 +:1051800080918100826080938100809181008160A4 +:105190008093810080918000816080938000809165 +:1051A000B10084608093B1008091B00081608093F1 +:1051B000B00080917A00846080937A0080917A00B8 +:1051C000826080937A0080917A00816080937A0077 +:1051D00080917A00806880937A001092C100A8952F +:1051E00080E090E0892B11F00E9400000E9479037A +:1051F0008091B2028D7F8093B20280E090E0892B93 +:1052000011F00E94000040E250E063E671E0CE0140 +:1052100001960E94422E40E150E06CE871E0CE0120 +:1052200081960E94422E49E050E063E871E0CE0191 +:10523000C1960E94422E41E050E06AE171E0CE0149 +:10524000865A9F4F0E94422E28EC31E040E050E009 +:10525000BE016F5F7F4FCE01CA960E947B2441E062 +:1052600050E063E071E081EB92E00E94422E6FEF2C +:1052700081E090E00E94622E8091BE028F78809340 +:10528000BE021092B4021092B30282E291E00E9438 +:10529000D40781E090E29093DB018093DA010E94D1 +:1052A000DF056B017C010E94DF056C197D098E0909 +:1052B0009F096031774281059105C8F00E9464170B +:1052C000A89500E010E0A8950E94B6078091BD0265 +:1052D00084FD0E9454110E9450190115110599F383 +:1052E0000E947F04882379F30E940000ECCF0E9483 +:1052F000B6070E94C7148091BD0282FB882780F9FF +:10530000A8958111DBCFCFCF89E591E09093390546 +:10531000809338058091490390914A0388539540C2 +:1053200071F4E0914B05F0914C05908180914D0511 +:1053300080958923808310924A031092490308952F +:10534000EF920F931F93CF93DF93E80134E047FD73 +:1053500034E1042E000C550B57FF03C051954195C5 +:105360005109E32E022F242FAE010E94E72ACE011D +:10537000DF91CF911F910F91EF9008958F929F929F +:10538000AF92BF92CF92DF92EF92FF92CF93DF93D3 +:10539000EC01688179818A819B81611571058105A4 +:1053A000910521F464E279ED8BE597E02DE133EF8F +:1053B00041E050E00E94742E49015A019B01AC016A +:1053C000A7EAB1E40E94932E6B017C01ACEEB4EF2E +:1053D000A50194010E94A12EC60ED71EE81EF91E3B +:1053E000F7FE06C081E0C81AD108E10880E8F80A93 +:1053F000C882D982EA82FB82C701B6019F77DF911A +:10540000CF91FF90EF90DF90CF90BF90AF909F90A3 +:105410008F9008958F929F92AF92BF92CF92DF921A +:10542000EF92FF9260912D0170912E0180912F01DA +:1054300090913001611571058105910521F464E2B7 +:1054400079ED8BE597E02DE133EF41E050E00E94EC +:10545000742E49015A019B01AC01A7EAB1E40E94F4 +:10546000932E6B017C01ACEEB4EFA50194010E9478 +:10547000A12EC60ED71EE81EF91EF7FE06C081E05B +:10548000C81AD108E10880E8F80AC0922D01D0922C +:105490002E01E0922F01F0923001C701B6019F77F3 +:1054A000FF90EF90DF90CF90BF90AF909F908F9044 +:1054B000089560932D0170932E0180932F01909396 +:1054C00030010895FC0188279927E8942191203222 +:1054D000E9F3293010F02E30C8F32B3241F02D3291 +:1054E00039F4689404C00E94DF2A820F911D219133 +:1054F00020532A30C0F31EF4909581959F4F089554 +:105500001F93FC0199278827BC01E8941191103260 +:10551000E9F3193010F01E30C8F31B3251F01D3280 +:1055200049F4689406C00E94CA2A610F711D811D4A +:10553000911D119110531A30B0F33EF4909580955F +:10554000709561957F4F8F4F9F4F1F910895FC017C +:1055500005900020E9F7809590958E0F9F1F089584 +:10556000FB01DC014150504048F005900D920020B5 +:10557000C9F701C01D9241505040E0F70895FC0169 +:105580006150704001900110D8F7809590958E0F72 +:105590009F1F0895592F482F372F262F660F771FEB +:1055A000881F991F660F771F881F991F620F731F2F +:1055B000841F951F660F771F881F991F08957AE033 +:1055C000979F902D879F802D910D11240895A9E01C +:1055D000B0E0EDEEFAE20C94AC2E6A01F22EB02EA1 +:1055E000102FFBE3AF2EA01608F41BE31F5F012F63 +:1055F00027E0AE014F5F5F4F0E94462CBC018981BE +:10560000982F9970913009F43FC09BE2E1FC04C0EF +:105610009E2D9170E0FC3AC05E2D5071E82FE8702D +:1056200083FF46C084E0911101C083E0EF2DE81BA9 +:105630008F1508F0E0E0D6018E2F20E2552351F1BE +:10564000992329F0D6019C93C60101966C0196011D +:105650002D5F3F4FE2FE24C08EE4D6018C9391E48F +:1056600011969C93119712968C93D9018E2F90E2EC +:1056700081111BC0E20FF32FF11D10828EEF9FEFFF +:105680002996ECE00C94C82E9DE2C6CF90E2C4CFE0 +:105690002D9381508111FCCFCE0ED11CE0E0D0CFF4 +:1056A0008EE6D6018C9391E6DBCF9D938150E0CFBF +:1056B000482F447082FF3DC084E0911101C083E017 +:1056C0008F1510F4EF2DE81BD6018E2F20E2552305 +:1056D00009F1992329F0D6019C93C60101966C012A +:1056E00096012D5F3F4FE2FE1BC089E4D6018C93EB +:1056F0008EE411968C93119786E412968C93D901BF +:105700008E2F90E2882309F4B5CF9D938150FACF74 +:105710002D9381508111FCCFCE0ED11CE0E0D9CF6A +:1057200089E6D6018C938EE611968C93119786E6C6 +:10573000E4CF9B0177FF02C030E020E0A1E0B0E0C1 +:10574000911102C0B0E0A0E0F9013196AE0FBF1F89 +:105750008B2C912CBB2009F461C05401EFEFAE1AE1 +:10576000BE0AAA0DBB1DFA161B0619F014F04F2D28 +:105770004A1B511188C0E3FC59C07601A42FB0E246 +:10578000A1114FC0C40ED11C992309F45BC040E0A5 +:10579000D6011196F60190836D01552309F448C096 +:1057A000012F060F9A81582F507184FF03C0913347 +:1057B00009F4015010160CF047C0093008F008E059 +:1057C000EEE2FE2E5B01A01AB1088501919481944E +:1057D00091082F3F320729F4D601FC92F601319649 +:1057E0006F016217730794F1021713077CF5DB0151 +:1057F000A21BB30BAA24A394B12CAC0EBD1EAA0D00 +:10580000BB1D11968C9121503109D6011196281596 +:105810003905F4F0F60180836D01DBCFB12CA12CAA +:10582000A0CFF701B1937F01A150AACF9111B0CFC2 +:10583000D601942F50E302C05D9391509111FCCF9B +:10584000C40ED11C40E0ACCF01E0BACF80E3DBCF87 +:105850006217730731F4963388F4953311F45523A6 +:1058600069F0F6018083FD01842F90E2811108C068 +:10587000A40FB11D1C9290E080E002CF81E3F1CF34 +:1058800091938150F3CF911183CF8ACF283008F0C4 +:1058900027E03327DA01990F311D87FD91600096CB +:1058A0006105710539F432602E5F3D9330E32A952E +:1058B000E1F708959F3F30F080387105610509F0E8 +:1058C0003C5F3C5F3D93913008F08068911DDF9311 +:1058D000CF931F930F93FF92EF92192F987F969576 +:1058E000E92F96959695E90FFF27EE53FF4F9927DD +:1058F0003327EE24FF24A701E701059008940794BD +:1059000028F4360FE71EF81E491F511D660F771F3A +:10591000881F991F0694A1F70590079428F4E70EB5 +:10592000F81E491F561FC11D770F881F991F661F3C +:105930000694A1F70590079428F4F80E491F561F06 +:10594000C71FD11D880F991F661F771F0694A1F7E7 +:105950000590079420F4490F561FC71FD81F990FB1 +:10596000661F771F881F0694A9F7849110951770FA +:1059700041F0D695C79557954795F794E7941A95B2 +:10598000C1F7E8E6F0E068941590159135916591BE +:10599000959105907FE27395E118F10A430B560B40 +:1059A000C90BD009C0F7E10CF11E431F561FC91FD8 +:1059B000D01D7EF4703311F48A95E6CFE89401503F +:1059C00030F0080F0AF40027021708F4202F23955F +:1059D000022F7A3328F079E37D932A95E9F710C0F6 +:1059E0007D932A9589F6069497956795379517952F +:1059F0001794E118F10A430B560BC90BD00998F024 +:105A000023957E9173957A3308F070E37C9320138D +:105A1000B8F77E9170617D9330F0839571E37D934B +:105A200070E32A95E1F71124EF90FF900F911F91F9 +:105A3000CF91DF91992787FD909508950E94602D61 +:105A400008F481E00895E89409C097FB3EF490952E +:105A50008095709561957F4F8F4F9F4F9923A9F047 +:105A6000F92F96E9BB279395F695879577956795D6 +:105A7000B795F111F8CFFAF4BB0F11F460FF1BC01A +:105A80006F5F7F4F8F4F9F4F16C0882311F096E9AD +:105A900011C0772321F09EE8872F762F05C066235B +:105AA00071F096E8862F70E060E02AF09A95660F14 +:105AB000771F881FDAF7880F9695879597F90895CD +:105AC000990F0008550FAA0BE0E8FEEF161617060F +:105AD000E807F907C0F012161306E407F50798F077 +:105AE000621B730B840B950B39F40A2661F0232B90 +:105AF000242B252B21F408950A2609F4A140A6950C +:105B00008FEF811D811D08950E94972D0C94082E02 +:105B10000E94FA2D38F00E94012E20F0952311F0FA +:105B20000C94F12D0C94F72D11240C943C2E0E9412 +:105B3000192E70F3959FC1F3950F50E0551F629F8A +:105B4000F001729FBB27F00DB11D639FAA27F00DD6 +:105B5000B11DAA1F649F6627B00DA11D661F829FFD +:105B60002227B00DA11D621F739FB00DA11D621FE2 +:105B7000839FA00D611D221F749F3327A00D611DFF +:105B8000231F849F600D211D822F762F6A2F1124E1 +:105B90009F5750409AF0F1F088234AF0EE0FFF1F14 +:105BA000BB1F661F771F881F91505040A9F79E3F6B +:105BB000510580F00C94F12D0C943C2E5F3FE4F3E2 +:105BC000983ED4F3869577956795B795F795E795C1 +:105BD0009F5FC1F7FE2B880F911D9695879597F9CA +:105BE000089597F99F6780E870E060E008959FEF5F +:105BF00080EC089500240A94161617061806090664 +:105C0000089500240A941216130614060506089532 +:105C1000092E0394000C11F4882352F0BB0F40F4BA +:105C2000BF2B11F460FF04C06F5F7F4F8F4F9F4FFA +:105C3000089557FD9058440F551F59F05F3F71F07C +:105C40004795880F97FB991F61F09F3F79F08795E3 +:105C50000895121613061406551FF2CF4695F1DF6C +:105C600008C0161617061806991FF1CF86957105FC +:105C7000610508940895E894BB2766277727CB0130 +:105C800097F90895DC01CB01FC01F999FECF06C01C +:105C9000F2BDE1BDF89A319600B40D9241505040EA +:105CA000B8F70895DC01A40FB51F4150504048F0EB +:105CB000CB01840F951F2E910E94632E41505040BE +:105CC000D0F70895262FF999FECF92BD81BDF89A9D +:105CD000019700B4021639F01FBA20BD0FB6F89430 +:105CE000FA9AF99A0FBE0895052E97FB1EF40094B8 +:105CF0000E948B2E57FD07D00E94DD2E07FC03D09B +:105D00004EF40C948B2E50954095309521953F4F35 +:105D10004F4F5F4F089590958095709561957F4F97 +:105D20008F4F9F4F08950E94FF2EA59F900DB49F07 +:105D3000900DA49F800D911D11240895B7FF0C9420 +:105D4000932E0E94932E821B930B08952F923F92C5 +:105D50004F925F926F927F928F929F92AF92BF927B +:105D6000CF92DF92EF92FF920F931F93CF93DF9327 +:105D7000CDB7DEB7CA1BDB0B0FB6F894DEBF0FBE84 +:105D8000CDBF09942A88398848885F846E847D84D1 +:105D90008C849B84AA84B984C884DF80EE80FD80D3 +:105DA0000C811B81AA81B981CE0FD11D0FB6F89449 +:105DB000DEBF0FBECDBFED010895A1E21A2EAA1BD2 +:105DC000BB1BFD010DC0AA1FBB1FEE1FFF1FA217AB +:105DD000B307E407F50720F0A21BB30BE40BF50BA8 +:105DE000661F771F881F991F1A9469F76095709531 +:105DF000809590959B01AC01BD01CF010895A29FB4 +:105E0000B001B39FC001A39F700D811D1124911D8E +:105E1000B29F700D811D1124911D08955058BB270C +:105E2000AA270E94262F0C94082E0E94FA2D38F0E3 +:105E30000E94012E20F039F49F3F19F426F40C94AF +:105E4000F72D0EF4E095E7FB0C94F12DE92F0E945D +:105E5000192E58F3BA17620773078407950720F0C5 +:105E600079F4A6F50C943B2E0EF4E0950B2EBA2F88 +:105E7000A02D0B01B90190010C01CA01A001112450 +:105E8000FF27591B99F0593F50F4503E68F11A16FC +:105E9000F040A22F232F342F4427585FF3CF46958D +:105EA00037952795A795F0405395C9F77EF41F16AF +:105EB000BA0B620B730B840BBAF09150A1F0FF0F79 +:105EC000BB1F661F771F881FC2F70EC0BA0F621F65 +:105ED000731F841F48F4879577956795B795F79555 +:105EE0009E3F08F0B0CF9395880F08F09927EE0FEA +:105EF0009795879508950E948F2F0C94082E0E94E5 +:105F0000012E58F00E94FA2D40F029F45F3F29F04D +:105F10000C94F12D51110C943C2E0C94F72D0E94F1 +:105F2000192E68F39923B1F3552391F3951B550B63 +:105F3000BB27AA2762177307840738F09F5F5F4F5C +:105F4000220F331F441FAA1FA9F335D00E2E3AF09B +:105F5000E0E832D091505040E695001CCAF72BD0B3 +:105F6000FE2F29D0660F771F881FBB1F2617370704 +:105F70004807AB07B0E809F0BB0B802DBF01FF2736 +:105F800093585F4F3AF09E3F510578F00C94F12DF5 +:105F90000C943C2E5F3FE4F3983ED4F386957795BE +:105FA0006795B795F7959F5FC9F7880F911D9695EF +:105FB000879597F90895E1E0660F771F881FBB1F4B +:105FC000621773078407BA0720F0621B730B840BF8 +:105FD000BA0BEE1F88F7E09508950E94F42F68949D +:105FE000B1110C943C2E08950E94212E88F09F57E9 +:105FF00098F0B92F9927B751B0F0E1F0660F771FED +:10600000881F991F1AF0BA95C9F714C0B13091F0E2 +:106010000E943B2EB1E008950C943B2E672F782F01 +:106020008827B85F39F0B93FCCF3869577956795A7 +:10603000B395D9F73EF490958095709561957F4F13 +:106040008F4F9F4F08950E94602D08F48FEF0895A1 +:106050000E94602D880B990B0895DB018F939F930D +:106060000E94932EBF91AF91A29F800D911DA39F7F +:10607000900DB29F900D11240895991B79E004C0F2 +:10608000991F961708F0961B881F7A95C9F7809577 +:106090000895AA1BBB1B51E107C0AA1FBB1FA6176F +:1060A000B70710F0A61BB70B881F991F5A95A9F7C1 +:1060B00080959095BC01CD01089597FB072E16F4AD +:1060C000009407D077FD09D00E94493007FC05D025 +:1060D0003EF4909581959F4F0895709561957F4FFF +:1060E0000895DF93CF931F930F939A9DF02D219FD7 +:1060F000F00D8B9DF00D8A9DE02DF10D039FF00DAD +:10610000029FE00DF11D4E9DE00DF11D5E9DF00D15 +:106110004F9DF00D7F936F93BF92AF925F934F931C +:10612000D5010E94FF2E8B01AC01D7010E94FF2EEA +:10613000EB01E80FF91FD6010E94C0302F913F916B +:10614000D6010E94FF2EC60FD71FE81FF91FAF917F +:10615000BF910E94C0302F913F910E94FF2EC60F29 +:10616000D71FE81FF91FD6010E94FF2EE60FF71F69 +:106170009801BE01CF010F911F91CF91DF9108953A +:106180000E94FF2E460F571FC81FD91F08F43196D3 +:106190000895689401C0E894F92FF12B12F00C9443 +:1061A000FD30A0E0B0E0E7EDF0E30C94AC2E092E5A +:1061B000059422F40E945931112392F4F0E80F263D +:1061C000FFEFE094F09400951095B094C094D094B3 +:1061D000A194BF0ACF0ADF0AEF0AFF0A0F0B1F0BB9 +:1061E0000E94083107FC0E945931CDB7DEB7ECE0C0 +:1061F0000C94C82E689401C0E8948F929F92CF931C +:10620000DF930E940831DF91CF919F908F90089586 +:1062100088249924F401E401B0E49F93AA279A15F5 +:106220008B049C04ED05FE05CF05D007A10798F46B +:10623000AD2FDC2FCF2FFE2FE92D982C892E982FF4 +:10624000872F762F652F542F432F322F2227B850B8 +:1062500031F7BF9127C01B2EBF91BB27220F331FE1 +:10626000441F551F661F771F881F991F881C991C84 +:10627000EE1FFF1FCC1FDD1FAA1FBB1F8A149B042C +:10628000EC05FD05CE05DF05A007B10748F08A182B +:106290009B08EC09FD09CE09DF09A00BB10B2160B9 +:1062A0001A94E1F62EF49401AF01BE01CD01000C69 +:1062B000089560957095809590953095409550958E +:1062C00021953F4F4F4F5F4F6F4F7F4F8F4F9F4FE6 +:1062D0000895002E083090F0982F872F762F652F85 +:1062E000542F432F322F22270850F4CF220F331F71 +:1062F000441F551F661F771F881F991F0A95B2F705 +:10630000002D089597FD1094002E083098F0085045 +:10631000232F342F452F562F672F782F892F912D1C +:10632000F4CF1594979587957795679557954795E9 +:10633000379527950A95AAF71124002D08952A0D5F +:106340003B1D4C1D5D1D6E1D7F1D801F911F0895FF +:106350002A193B094C095D096E097F09800B910BD5 +:1063600008950024A7FD00942A1730054005500524 +:1063700060057005800590050895EE0FFF1F0590DC +:10638000F491E02D0994FB01DC0102C001900D9213 +:1063900041505040D8F70895FB01DC014150504076 +:1063A00048F001900D920020C9F701C01D924150A4 +:1063B0005040E0F7089513E0CEE4D3E004C0FE01BE +:1063C0000E94BD312196CF34D107C9F7F894FFCF91 +:1063D000FFFFFF00FCE1A8A8750D00083C206B083A +:1063E00028203C082A08200858208508010A0501B1 +:1063F000FF01C40D6F20FFFFFFEB05EB050100005F +:106400000000000000AB041504420413057304519E +:10641000046504000000001C07EF06640704060E74 +:1064200004DC06CD06000000000506150464070420 +:106430000661067906EC050D0A00322E332E320075 +:1064400053656E736F72206572726F72006E616E4B +:1064500000696E66006F766600430046004E756DFB +:106460006265723A002500204452590020474F4F80 +:106470004400204D4F4953540035427157416B61E0 +:026480003900E1 +:00000001FF diff --git a/code/Temperature_and_more/Temperature_and_more.ino b/code/Temperature_and_more/Temperature_and_more.ino new file mode 100644 index 0000000..91ab48a --- /dev/null +++ b/code/Temperature_and_more/Temperature_and_more.ino @@ -0,0 +1,1181 @@ +/* + * Temperature and more sensor + * + * This device can measure: + * - Temperature + * - Humidity + * - Air pressure (barometer). + * + * By looking at changes in air pressure it can predict the weather. + * - unknown (it's still calculating) + * - stable (expect more of the same) + * - unstable (expect strong changes in weather) + * - sunny (expect improving weather) + * - cloudy (clouds and possibly rain) + * - thunderstorm (strong shift to low pressure) + * + * You can use it indoor or outdoor. It does not have to be outside to be able to predict the weather. + * + * The main sensor used is the Bosch BME280. + * + * + * SETTINGS */ + + +#define SECONDS_BETWEEN_SENDING 60 // Sleep time between reads for the BME sensor (in seconds). Keep this value at 60 if you have enabled the forecast feature, as the forecast algorithm needs a sample every minute. MAximum is 255 (because the value is stored as a byte, instead of the bigger 'int') + +#define HAS_TOUCH_SCREEN // Did you connect a touch screen? + +//#define MY_REPEATER_FEATURE // Act as a repeater? The devices can pass along messages to each other to increase the range of your network. + +#define RF_NANO // RF-Nano. Check this box if you are using the RF-Nano Arduino, which has a built in radio. The Candle project uses the RF-Nano. + + + /* END OF SETTINGS + * + * + * + */ + +// PINS +#define TOUCH_SCREEN_RX_PIN 5 // The receive (RX) pin for the touchscreen. This connects to the transmit (TX) pin of the touchscreen. +#define TOUCH_SCREEN_TX_PIN 6 // The receive (TX) pin for the touchscreen. This connects to the transmit (RX) pin of the touchscreen. + +// The BME280 uses I2C, so its SDA (data) pin should be connected to pin A4 on the Arduino, and the sensor's SCL (clock) pin should be connected to pin A5 on the Arduino. + +#ifdef RF_NANO +// If you are using an RF-Nano, you have to switch CE and CS pins. +#define MY_RF24_CS_PIN 9 // Used by the MySensors library. +#define MY_RF24_CE_PIN 10 // Used by the MySensors library. +#endif + +//#define DEBUG // Do you want to see extra debugging information in the serial output? +//#define DEBUG_SCREEN // Do you want to see extra debugging information about the touch screen in the serial output? +//#define MY_DEBUG // Enable MySensors debug output to the serial monitor, so you can check if the radio is working ok. + + +// Enable and select the attached radio type +#define MY_RADIO_RF24 // This is a common and simple radio used with MySensors. Downside is that it uses the same frequency space as WiFi. +//#define MY_RADIO_NRF5_ESB // This is a new type of device that is arduino and radio all in one. Currently not suitable for beginners yet. +//#define MY_RADIO_RFM69 // This is an open source radio on the 433mhz frequency. Great range and built-in encryption, but more expensive and little more difficult to connect. +//#define MY_RADIO_RFM95 // This is a LoRaWan radio, which can have a range of 10km. + +// MySensors: Choose your desired radio power level. High power can cause issues on cheap Chinese NRF24 radio's. +//#define MY_RF24_PA_LEVEL RF24_PA_MIN +//#define MY_RF24_PA_LEVEL RF24_PA_LOW +//#define MY_RF24_PA_LEVEL RF24_PA_HIGH +#define MY_RF24_PA_LEVEL RF24_PA_MAX + +// Mysensors security +#define MY_ENCRYPTION_SIMPLE_PASSWD "5BqWAka9" // Be aware, the length of the password has an effect on memory use. +//#define MY_SIGNING_SOFT_RANDOMSEED_PIN A7 // Setting a pin to pickup random electromagnetic noise helps make encryption more secure. + + +// Mysensors advanced settings +#define MY_TRANSPORT_WAIT_READY_MS 10000 // Try connecting for 10 seconds. Otherwise just continue. +//#define MY_RF24_CHANNEL 100 // In EU the default channel 76 overlaps with wifi, so you could try using channel 100. But you will have to set this up on every device, and also on the controller. +#define MY_RF24_DATARATE RF24_1MBPS // The datarate influences range. 1MBPS is the most widely supported. 250KBPS will give you more range. +//#define MY_NODE_ID 10 // Giving a node a manual ID can in rare cases fix connection issues. +//#define MY_PARENT_NODE_ID 0 // Fixating the ID of the gatewaynode can in rare cases fix connection issues. +//#define MY_PARENT_NODE_IS_STATIC // Used together with setting the parent node ID. Daking the controller ID static can in rare cases fix connection issues. +#define MY_SPLASH_SCREEN_DISABLED // Saves a little memory. +//#define MY_DISABLE_RAM_ROUTING_TABLE_FEATURE // Saves a little memory. + + + + + + + +// LIBRARIES +#include // The MySensors library. Hurray! +#include "Seeed_BME280.h" // "Grove - Barometer Sensor BME280". A relatively new library (as of 2018), works well with cheap BME280 sensors from China. +#include // The watchdog timer - if the device becomes unresponsive and doesn't periodically reset the timer, then it will automatically reset once the timer reaches 0. + +BME280 bme280; // Create the BME sensor object + +#ifdef HAS_TOUCH_SCREEN + +#include +SoftwareSerial touch_screen_serial(TOUCH_SCREEN_RX_PIN,TOUCH_SCREEN_TX_PIN); // RX (receive) pin, TX (transmit) pin + +#define MAX_BASIC_COMMAND_LENGTH 16 // How many bytes are in the longest basic command? +#define TOUCHSCREEN_WIDTH 240 // +#define TOUCHSCREEN_HEIGHT 320 // +#define SCREEN_PADDING 10 // Used to space things, e.g. the distance of content to the edge of the screen. +#define ITEM_HEIGHT 80 // How many pixels tall a single item on the screen is. +#define LABEL_HEIGHT 25 // How many pixels in height labels like 'temperature' are. + +boolean touched = false; // Was the touchscreen just touched? + +signed int touchX = 0; // Touch screen position X +signed int touchY = 0; // Touch screen position Y + + +// Basic commands for the touch screen. All commands for the TFT should start with 0x7E, but to save storage space this is taken care of in the basicCommand function. +PROGMEM const byte play[] = {0x07, 0x11, ' ', 'P', 'L', 'A', 'Y', 0xEF,}; // Places the word ' play' on the screen. +PROGMEM const byte on[] = {0x07, 0x11, ' ', 'O','N', ' ', ' ', 0xEF,}; // Places the word ' on ' on the screen. +PROGMEM const byte off[] = {0x07, 0x11, ' ', 'O', 'F', 'F', ' ', 0xEF,}; // Places the word ' off ' on the screen. +PROGMEM const byte w[] = {0x07, 0x11, 'w', 0x00, ' ', 0x00, 0x00, 0xEF,}; // Places the word 'w ' on the screen. +PROGMEM const byte menu[] = {0x07, 0x11, 'M', 'E', 'N', 'U', ' ', 0xEF,}; // Places the word 'menu ' on the screen. +PROGMEM const byte more[] = {0x07, 0x11, 'M', 'O', 'R', 'E', '>', 0xEF,}; // Places the word 'menu ' on the screen. + +PROGMEM const byte set_vertical[] = {0x03, 0x04, 0x00, 0xEF,}; // To set rotation of the screen to vertical. Try 0x01 or 0x03 instead of the 0x02. +PROGMEM const byte fill_black[] = {0x04, 0x20, 0x00, 0x00, 0xEF,}; // Fill screen with one color +//PROGMEM const byte fill_blue[] = {0x04, 0x20, 0x00, 0xFF, 0xEF,}; // Fill screen with one color + +PROGMEM const byte text_color_white[] = {0x04, 0x02, 0xFF, 0xFF, 0xEF,}; // white text color. fill screen with one color +//PROGMEM const byte text_color_black[] = {0x04, 0x02, 0x00, 0x00, 0xEF,}; // Dark text color. fill screen with one color +//PROGMEM const byte text_color_red[] = {0x04, 0x02, 0xF8, 0x00, 0xEF,}; // .. text color. fill screen with one color + +//PROGMEM const byte resetTFT[] = {0x02, 0x05, 0xEF,}; // Resets the TFT. But has no real effect. +//PROGMEM const byte testTFT[] = {0x02, 0x00, 0xEF}; // Test the TFT, should respond with "OK". +PROGMEM const byte backlight_on[] = {0x03, 0x06, 0xFF, 0xEF}; // Backlight intensity to half-full +PROGMEM const byte backlight_off[] = {0x03, 0x06, 0x00, 0xEF}; // Backlight intensity to zero +//PROGMEM const byte serialSpeedUp[] = {0x03, 0x40, 0x03, 0xEF,}; // Sets communication speed to 57600 (from 9600) +//PROGMEM const byte serialSlowDown[] = {0x03, 0x40, 0x00, 0xEF,}; // Sets communication speed to 9600 again. Oddly enough, it seems it works fastest at this speed.. + +// Colors +#define BLACK 0 +#define GREY111111 4226 +#define GREY333333 12678 +#define GREY666666 25388 +#define GREYCCCCCC 52857 +#define YELLOW 65504 +#define LIGHT_YELLOW 65510 +#define LIGHT_ORANGE 65126 +#define RED 63488 +#define GREEN 2016 +#define WHITE 65535 + +// A list of strings stored in program memory in order to save ram. +#define SCREEN_TEMPERATURE 0 +#define SCREEN_HUMIDITY 1 +#define SCREEN_FORECAST 2 +#define SCREEN_STABLE 3 +#define SCREEN_SUNNY 4 +#define SCREEN_CLOUDY 5 +#define SCREEN_UNSTABLE 6 +#define SCREEN_THUNDERSTORM 7 +#define SCREEN_UNKNOWN 8 + +typedef byte MessageID; // TODO Is this used? Not really. Simplifying the message table could save some storage space. + +struct MessageDef { + MessageID ID; + char Description[20]; +}; + +const MessageDef MessageTable[] PROGMEM = { + {SCREEN_TEMPERATURE,"Temperature"}, + {SCREEN_HUMIDITY,"Humidity"}, + {SCREEN_FORECAST,"Barometer"}, + {SCREEN_STABLE,"STABLE"}, + {SCREEN_SUNNY,"SUNNY"}, + {SCREEN_CLOUDY,"Cloudy"}, + {SCREEN_UNSTABLE,"Unstable"}, + {SCREEN_THUNDERSTORM,"Thunderstorm"}, + {SCREEN_UNKNOWN,"Unknown"}, + }; + + +const byte metadataArraySize = 8; // The size of the array to store the serial data we're decoding in. +byte metaData[metadataArraySize]; // Holds metadata for a replayable signal. +byte screen_brightness = 255; // When this is 0 the screen is turned off. + + +#endif // End of has_touch_screen + + +#define INTERVAL 1000 // When active, the internal clock runs every 1000 milliseconds, which is a second. + + +float previous_temperature = 0; // Contains the latest temperature value. +float temperature = 0; // Contains the latest temperature value. +byte previous_humidity = 0; // Contains the latest humidity value. +byte current_humidity = 0; // Contains the latest humidity value. +unsigned int previous_pressure = 0; // Contains the previously detected air pressure value. +unsigned int pressure = 0; // Contains the latest air pressure value. +byte forecast = 5; // A number that represents a type of weather forecast + + +// VARIABLES YOU PROBABLY SHOULDN'T CHANGE +#define RADIO_DELAY 100 // milliseconds betweeen radio signals during the presentation phase. +#define TEMP_CHILD_ID 0 // For MySensors. Within this node each sensortype should have its own ID number. +#define HUM_CHILD_ID 1 +#define BARO_CHILD_ID 2 +#define FORECAST_CHILD_ID 3 +#define SCREEN_BUTTON_CHILD_ID 4 + +// Forecast variables +#define CONVERSION_FACTOR (1.0/10.0) // Used by forecast algorithm to convert from Pa to kPa, by dividing hPa by 10. +#define STABLE 0 // "Stable Weather Pattern" +#define SUNNY 1 // "Slowly rising Good Weather", "Clear/Sunny " +#define CLOUDY 2 // "Slowly falling L-Pressure ", "Cloudy/Rain " +#define UNSTABLE 3 // "Quickly rising H-Press", "Not Stable" +#define THUNDERSTORM 4 // "Quickly falling L-Press", "Thunderstorm" +#define UNKNOWNN 5 // "Unknown (More Time needed) +byte lastForecast = 10; // Stores the previous forecast. Icons are only redrawn if necessary. +const byte LAST_SAMPLES_COUNT = 5; +float lastPressureSamples[LAST_SAMPLES_COUNT]; +byte minuteCount = 0; // The forecast algorithm keeps track of time +bool firstRound = true; // The forecast algorithm needs to know if it is creating the first forecast. +float pressureAvg; // Average value after 2 hours is used as reference value for the next iteration. +float pressureAvg2; +float dP_dt; // Used by the forecast algorithm. + + +// MySensors variables +MyMessage temperature_message(TEMP_CHILD_ID, V_TEMP); +MyMessage humidity_message(HUM_CHILD_ID, V_HUM); +MyMessage pressure_message(BARO_CHILD_ID, V_PRESSURE); +MyMessage string_message(FORECAST_CHILD_ID, V_TEXT); +MyMessage button_message(SCREEN_BUTTON_CHILD_ID, V_STATUS); // Allows the controller to turn the screen on and off. + + +boolean connectedToNetwork = false; // Are we connected to the local MySensors network? Used to display the 'w' connection icon. +boolean send_all_values = 1; // Sends the state of the toggle to the controller on startup or when requested by the controller. +boolean metric = true; // Should the device show metric or Fahrenheit? + + + +/* +// A function to measure how much ram is used. +int freeRam () { + extern int __heap_start, *__brkval; + int v; + return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); +} +// via https://playground.arduino.cc/Code/AvailableMemory +*/ + + +void presentation() { + // Send the sketch version information to the gateway and Controller + sendSketchInfo(F("Weather station"), F("1.0")); wait(RADIO_DELAY); + + // Tell the MySensors gateway what kind of sensors this node has, and what their ID's on the node are, as defined in the code above. + present(TEMP_CHILD_ID, S_TEMP, F("Temperature")); wait(RADIO_DELAY); + present(HUM_CHILD_ID, S_HUM, F("Humidity")); wait(RADIO_DELAY); + present(BARO_CHILD_ID, S_BARO, F("Air pressure")); wait(RADIO_DELAY); + present(FORECAST_CHILD_ID, S_INFO, F("Forecast")); wait(RADIO_DELAY); +#ifdef HAS_TOUCH_SCREEN + present(SCREEN_BUTTON_CHILD_ID, S_BINARY, F("Screen")); wait(RADIO_DELAY); + send_all_values = 1; +#endif +} + + +#ifdef HAS_TOUCH_SCREEN +void send_values(){ +#ifdef DEBUG + Serial.println(F("Sending button states")); +#endif + + send(button_message.setSensor(SCREEN_BUTTON_CHILD_ID).set(1)); + wait(RADIO_DELAY); + +} +#endif + + + +void setup() { + Serial.begin(115200); // for serial debugging over USB. + Serial.println(F("Hello, I am a climate sensor")); + +#ifdef HAS_TOUCH_SCREEN + wait(2000); + touch_screen_serial.begin(9600); + wait(2000); + +#ifdef DEBUG_SCREEN + Serial.println(F("BC: backlight_on")); +#endif + basicCommand(backlight_on); // Turn on the backlight (if it isn't already). + +#ifdef DEBUG_SCREEN + Serial.println(F("BC: vertical")); +#endif + basicCommand(set_vertical); // Set the screen to vertical mode. + +#ifdef DEBUG_SCREEN + Serial.println(F("BC: white text")); +#endif + basicCommand(text_color_white); // Set text color to white. + +#ifdef DEBUG_SCREEN + Serial.println(F("BC: fill black")); +#endif + basicCommand(fill_black); // Set the background color of the screen to black. + + fontSize(1); // Set the font size of the touch screen to its smallest size. + + setCur(SCREEN_PADDING,SCREEN_PADDING); + writeText(SCREEN_TEMPERATURE); + + roundedRectangle(0,ITEM_HEIGHT, TOUCHSCREEN_WIDTH, ITEM_HEIGHT, 0, GREY111111); + //simpleHorizontal(ITEM_HEIGHT, GREYCCCCCC); + + setCur(SCREEN_PADDING,SCREEN_PADDING + ITEM_HEIGHT); + writeText(SCREEN_HUMIDITY); + + //simpleHorizontal(ITEM_HEIGHT * 2, GREYCCCCCC); + setCur(SCREEN_PADDING,SCREEN_PADDING + (ITEM_HEIGHT * 2)); + writeText(SCREEN_FORECAST); + + +#endif // end of has_touch_screen + + + if(isTransportReady()){ // If we are connected to the MySensors network. + Serial.println(F("Connected to gateway!")); + connectedToNetwork = true; + metric = getControllerConfig().isMetric; // Ask if Celcius or Fahrenheit is prefered. + }else{ + Serial.println(F("! NOT CONNECTED TO GATEWAY")); + } + + + if(!bme280.init()){ + Serial.println(F("! Sensor error")); +#ifdef HAS_TOUCH_SCREEN + writeString("Sensor error",12); +#endif + } + + wdt_enable(WDTO_2S); // Starts the watchdog timer. If it is not reset once every 2 seconds, then the entire device will automatically restart. +} + + +void loop() +{ + // If a presentation is requested, we also send the values of the children. + if( send_all_values ){ +#ifdef DEBUG + Serial.println(F("RESENDING VALUES")); +#endif + send_all_values = 0; + send_values(); + } + + static unsigned long previousMillis = 0; // Used to keep track of time. + static byte intervalCounter = 255; // How may intervals have passed. + +#ifdef HAS_TOUCH_SCREEN + // Check if the screen is being touched. + readResponse(); +#endif + + + + // + // MAIN LOOP + // Runs every few seconds. By counting how often this loop has run (and resetting that counter back to zero after 250 loops), it becomes possible to schedule all kinds of things without using a lot of memory. + // Maximum time that can be scheduled is 4s * 250 loops = 1000 seconds. So the maximum time between sending data can be 16 minutes. + // + + + if(millis() - previousMillis >= INTERVAL){ // Main loop, runs every second. + previousMillis = millis(); // Store the current time as the previous measurement start time. + + if(intervalCounter >= SECONDS_BETWEEN_SENDING){ + intervalCounter = 0; + }else{ + intervalCounter++; + } + //Serial.println(intervalCounter); + + wdt_reset(); // Reset the watchdog timer + + + // Clock schedule + switch (intervalCounter) { + + // + // TEMPERATURE + // + case 0: // If we are in the first second of the clock + + temperature = bme280.getTemperature(); + + if(temperature != -127.00 && temperature != 85.00 && temperature != previous_temperature) { // Avoids working with measurement errors. + previous_temperature = temperature; + + if(!metric){ + temperature = (temperature * 9.0)/5.0 + 32.0; + } + + Serial.print(F("Sending temp: ")); Serial.println(temperature); + connectedToNetwork = false; // If we receive an echo then this will be set back to true. + send(temperature_message.set(temperature,1),1); // Ask for an echo from the controller + + #ifdef HAS_TOUCH_SCREEN + displayNetworkStatus(); + + // Clear the background for this part of the screen. + roundedRectangle(0,LABEL_HEIGHT, TOUCHSCREEN_WIDTH, ITEM_HEIGHT - LABEL_HEIGHT, 0, BLACK); + + setCur(SCREEN_PADDING,SCREEN_PADDING + LABEL_HEIGHT); + fontSize(4); + + char value_string[7]; // We create a character array to hold a 'string' representation of the temperature value. + dtostrf(temperature, 0, 1, value_string); // Here the floating point value is turned into a 'string'. + + byte string_length = 4; + if( abs(temperature) < 10 ){ string_length = 3; } + if( abs(temperature) > 99 ){ string_length = 5; } + if( temperature < 0 ){ string_length++; } // Accounting for the minus character + + writeString(value_string,string_length); + + fontSize(2); + if(metric){ + writeString("C",1); + } + else{ + writeString("F",1); + } + +#endif + } + break; + + + // + // HUMIDITY + // + + case 1: + + current_humidity = bme280.getHumidity(); + + if( current_humidity != previous_humidity ){ + previous_humidity = current_humidity; + + + Serial.print(F("Sending humidity ")); Serial.println(current_humidity); + send(humidity_message.set(current_humidity)); + + #ifdef HAS_TOUCH_SCREEN + + displayNetworkStatus(); // Show or remove the W icon in the top-right of the screen. + + // Clear the background for this part of the screen. + roundedRectangle(0,ITEM_HEIGHT + LABEL_HEIGHT, TOUCHSCREEN_WIDTH, ITEM_HEIGHT - LABEL_HEIGHT, 0, GREY111111); + + setCur(SCREEN_PADDING,SCREEN_PADDING + ITEM_HEIGHT + LABEL_HEIGHT); + fontSize(4); + + // Show humidity value on the screen + displayNumber(current_humidity); + fontSize(2); + writeString("%",1); + + // Show quality opinion on the screen. + fontSize(4); + if (current_humidity > 0 && current_humidity < 30){ + writeString(" DRY",4); + } + else if (current_humidity < 65){ + writeString(" GOOD",5); + } + else { + writeString(" MOIST",6); + } + +#endif + + } + break; + + + + // + // PRESSURE + // + case 2: + + pressure = round(bme280.getPressure() / 100); + Serial.print(F("Sending pressure ")); Serial.println(pressure); + send(pressure_message.set((pressure),1)); + + forecast = sample(pressure/100); + //forecast = random(5); // Show a random icon. This is used in exhibits, to make it a bit more interesting. + + if (pressure != previous_pressure || forecast != lastForecast) { + previous_pressure = pressure; + lastForecast = forecast; + +#ifdef HAS_TOUCH_SCREEN + // Show pressure value on the screen + + // Clear the background for this part of the screen. + roundedRectangle(0,(ITEM_HEIGHT * 2) + LABEL_HEIGHT, TOUCHSCREEN_WIDTH, (ITEM_HEIGHT * 2) - LABEL_HEIGHT, 0, BLACK); + + setCur(SCREEN_PADDING,SCREEN_PADDING + (ITEM_HEIGHT * 2) + LABEL_HEIGHT); + fontSize(2); + + char pressure_value_string[6]; // We create a character array to hold a 'string' representation of the temperature value. + memset(pressure_value_string,0,sizeof(pressure_value_string));// We fill the character array with zeros + dtostrf(pressure, 0, 0, pressure_value_string); // Here the floating point value is turned into a 'string'. + + writeString(pressure_value_string,4); + + byte circle_size = 40; + unsigned int cloud_color = WHITE; + + setCur(SCREEN_PADDING, SCREEN_PADDING + (ITEM_HEIGHT * 2) + (LABEL_HEIGHT * 2)); +#endif + + // UNKNOWN + if(forecast == 5){ + Serial.println(F("UNKNOWN")); + send(string_message.set(F("Unknown"))); // Sending the latest forecast to the controller. +#ifdef HAS_TOUCH_SCREEN + //fontSize(4); + writeText(SCREEN_UNKNOWN); +#endif + } + + // STABLE WEATHER + if(forecast == STABLE){ + Serial.println(F("STABLE")); + send(string_message.set(F("Stable"))); // Sending the latest forecast to the controller. + cloud_color = WHITE; +#ifdef HAS_TOUCH_SCREEN + fontSize(4); + writeText(SCREEN_STABLE); +#endif + } + + // UNSTABLE WEATHER + else if(forecast == UNSTABLE){ + Serial.println(F("UNSTABLE")); + send(string_message.set(F("Unstable"))); +#ifdef HAS_TOUCH_SCREEN + fontSize(4); + writeText(SCREEN_UNSTABLE); +#endif + } + + // SUNNY WEATHER + else if(forecast == SUNNY){ + Serial.println(F("SUNNY")); + send(string_message.set(F("Sunny"))); + //circle_size = 50; +#ifdef HAS_TOUCH_SCREEN + //circle(TOUCHSCREEN_WIDTH / 2, ITEM_HEIGHT * 3, circle_size, LIGHT_YELLOW); // x, y, radius, color + circle(TOUCHSCREEN_WIDTH / 2, ITEM_HEIGHT * 3, circle_size, YELLOW); + circle(TOUCHSCREEN_WIDTH / 2, ITEM_HEIGHT * 3, circle_size - 10, LIGHT_ORANGE); + fontSize(1); + writeText(SCREEN_SUNNY); +#endif + } + + // CLOUDY WEATHER OR THUNDERSTORM + else if(forecast == CLOUDY || forecast == THUNDERSTORM){ + if(forecast == THUNDERSTORM){ cloud_color = RED; } + +#ifdef HAS_TOUCH_SCREEN + circle(TOUCHSCREEN_WIDTH / 2, ITEM_HEIGHT * 3, circle_size, cloud_color); // x, y, radius, color + circle((TOUCHSCREEN_WIDTH / 2) - circle_size, ITEM_HEIGHT * 3 + 10, circle_size - 10, cloud_color); + circle(int((TOUCHSCREEN_WIDTH / 2) + circle_size), ITEM_HEIGHT * 3 + 20, circle_size - 20, cloud_color); + fontSize(1); +#endif + if(forecast == THUNDERSTORM){ + Serial.println(F("THUNDERSTORM")); + send(string_message.set(F("Thunderstorm"))); +#ifdef HAS_TOUCH_SCREEN + writeText(SCREEN_THUNDERSTORM); +#endif + } + else { + Serial.println(F("CLOUDY")); + send(string_message.set(F("Cloudy"))); +#ifdef HAS_TOUCH_SCREEN + writeText(SCREEN_CLOUDY); +#endif + } + } + } + break; + } + } +} + + + +float getLastPressureSamplesAverage() +{ + float lastPressureSamplesAverage = 0; + for (byte i = 0; i < LAST_SAMPLES_COUNT; i++) { + lastPressureSamplesAverage += lastPressureSamples[i]; + } + lastPressureSamplesAverage /= LAST_SAMPLES_COUNT; + + return lastPressureSamplesAverage; +} + + +// Forecast algorithm found here +// http://www.freescale.com/files/sensors/doc/app_note/AN3914.pdf +// Pressure in hPa --> forecast done by calculating kPa/h +byte sample(float pressure) +{ + // Calculate the average of the last n minutes. + int index = minuteCount % LAST_SAMPLES_COUNT; + lastPressureSamples[index] = pressure; + + minuteCount++; + if (minuteCount > 185) { + minuteCount = 6; + } + + if (minuteCount == 5) { + pressureAvg = getLastPressureSamplesAverage(); + } + else if (minuteCount == 35) { + float lastPressureAvg = getLastPressureSamplesAverage(); + float change = (lastPressureAvg - pressureAvg) * CONVERSION_FACTOR; + if (firstRound) { // first time initial 3 hour + dP_dt = change * 2; // note this is for t = 0.5hour + } + else { + dP_dt = change / 1.5; // divide by 1.5 as this is the difference in time from 0 value. + } + } + else if (minuteCount == 65) { + float lastPressureAvg = getLastPressureSamplesAverage(); + float change = (lastPressureAvg - pressureAvg) * CONVERSION_FACTOR; + if (firstRound) { // First time initial 3 hour + dP_dt = change; // Note this is for t = 1 hour + } + else { + dP_dt = change / 2; // Divide by 2 as this is the difference in time from 0 value + } + } + else if (minuteCount == 95) { + float lastPressureAvg = getLastPressureSamplesAverage(); + float change = (lastPressureAvg - pressureAvg) * CONVERSION_FACTOR; + if (firstRound) { // First time initial 3 hour + dP_dt = change / 1.5; // Note this is for t = 1.5 hour + } + else { + dP_dt = change / 2.5; // Divide by 2.5 as this is the difference in time from 0 value + } + } + else if (minuteCount == 125) { + float lastPressureAvg = getLastPressureSamplesAverage(); + pressureAvg2 = lastPressureAvg; // store for later use. + float change = (lastPressureAvg - pressureAvg) * CONVERSION_FACTOR; + if (firstRound) { // first time initial 3 hour + dP_dt = change / 2; // Note this is for t = 2 hour + } + else { + dP_dt = change / 3; // Divide by 3 as this is the difference in time from 0 value + } + } + else if (minuteCount == 155) { + float lastPressureAvg = getLastPressureSamplesAverage(); + float change = (lastPressureAvg - pressureAvg) * CONVERSION_FACTOR; + if (firstRound) { // First time initial 3 hour + dP_dt = change / 2.5; // Note this is for t = 2.5 hour + } + else { + dP_dt = change / 3.5; // divide by 3.5 as this is the difference in time from 0 value + } + } + else if (minuteCount == 185) { + float lastPressureAvg = getLastPressureSamplesAverage(); + float change = (lastPressureAvg - pressureAvg) * CONVERSION_FACTOR; + if (firstRound) { // first time initial 3 hour + dP_dt = change / 3; // Note this is for t = 3 hour + } + else { + dP_dt = change / 4; // Divide by 4 as this is the difference in time from 0 value + } + pressureAvg = pressureAvg2; // Equating the pressure at 0 to the pressure at 2 hour after 3 hours have past. + firstRound = false; // Flag to let you know that this is on the past 3 hour mark. Initialized to 0 outside main loop. + } + + byte forecast = UNKNOWNN; + if (minuteCount < 35 && firstRound) { // If time is less than 35 min on the first 3 hour interval. + forecast = UNKNOWNN; + } + else if (dP_dt < (-0.25)) { + forecast = THUNDERSTORM; + } + else if (dP_dt > 0.25) { + forecast = UNSTABLE; + } + else if ((dP_dt > (-0.25)) && (dP_dt < (-0.05))) { + forecast = CLOUDY; + } + else if ((dP_dt > 0.05) && (dP_dt < 0.25)) + { + forecast = SUNNY; + } + else if ((dP_dt >(-0.05)) && (dP_dt < 0.05)) { + forecast = STABLE; + } + else { + forecast = UNKNOWNN; + } + + // Useful for debugging + //Serial.print(F("BME280 - Forecast at minute ")); + //Serial.print(minuteCount); + //Serial.print(F(" dP/dt = ")); + //Serial.print(dP_dt); + //Serial.print(F("kPa/h --> ")); + //Serial.println(weather[forecast]); + //Serial.println(freeRam()); + + return forecast; +} + + + //for (float i=0; i<=6 ; i=i+.4) { + // Calculate sun rays end points + //int resultX = sunPositionX + (int)round( sunSize * cos( i ) ); + //int resultY = sunPositionY + (int)round( sunSize * sin( i ) ); + //} + + + + + + + + +#ifdef HAS_TOUCH_SCREEN + +void displayNetworkStatus() // Show connection icon on the display +{ + if( connectedToNetwork ){ +#ifdef DEBUG_SCREEN + Serial.println(F("BC: show W icon")); +#endif + setCur(TOUCHSCREEN_WIDTH - 30, SCREEN_PADDING ); + fontSize(1); + basicCommand(w); + } + else { +#ifdef DEBUG_SCREEN + Serial.println(F("BC: hide W icon")); +#endif + roundedRectangle(TOUCHSCREEN_WIDTH - 50,0, 50, LABEL_HEIGHT, 0, BLACK); + } +} + + + +void simpleHorizontal(unsigned int y, unsigned int color) // Draw a horizontal line on the screen. +{ + +#ifdef DEBUG_SCREEN + Serial.println(F("SIMPLEHORIZONTAL:")); + Serial.print(F("y: ")); Serial.println(y); +#endif + byte command[12] = {0x7E, 0x0A, 0x23, 0,0, highByte(y), lowByte(y), 0,240, 255, 255, 0xEF,}; + for( int i=0; i < sizeof(command); i++ ){ + touch_screen_serial.write( command[i] ); + //Serial.print(command[i],HEX); Serial.print(F(" ")); + } + waitForResponse(); +} + + +void roundedRectangle(int x, int y, int w, int h, int r, int c) // Draw a pixel on the screen. +{ + +#ifdef DEBUG_SCREEN + Serial.println(F("ROUNDEDRECTANGLE")); + //Serial.print(F("x:")); Serial.println(x); // top-right x-position + //Serial.print(F("y:")); Serial.println(y); // top-left y-position + //Serial.print(F("w:")); Serial.println(w); // width + //Serial.print(F("h:")); Serial.println(h); // height + //Serial.print(F("r:")); Serial.println(r); // radius + //Serial.print(F("c:")); Serial.println(c); // color +#endif + byte command[16] = {0x7E, 0x0E, 0x2C, highByte(x), lowByte(x), highByte(y), lowByte(y), highByte(w), lowByte(w), highByte(h), lowByte(h) - 2, highByte(r), lowByte(r), highByte(c), lowByte(c), 0xEF,}; + + for( int i=0; i < sizeof(command); i++ ){ + touch_screen_serial.write( command[i] ); + //Serial.print(command[i],HEX); Serial.print(F(" ")); + } + waitForResponse(); +} + + + +void circle(unsigned int x, unsigned int y, unsigned int r, unsigned int c) // Draw a pixel on the screen. +{ +#ifdef DEBUG_SCREEN + Serial.println(F("CIRCLE")); + Serial.print(F("x:")); Serial.println(x); // top-right x-position + Serial.print(F("y:")); Serial.println(y); // top-left y-position + Serial.print(F("r:")); Serial.println(r); // radius + Serial.print(F("c:")); Serial.println(c); // color +#endif + byte command[12] = {0x7E, 0x0A, 0x28, highByte(x), lowByte(x), highByte(y), lowByte(y), highByte(r), lowByte(r), highByte(c), lowByte(c), 0xEF}; + + for( int i=0; i < sizeof(command); i++ ){ + touch_screen_serial.write( command[i] ); + //Serial.print(command[i],HEX); Serial.print(F(" ")); + } + waitForResponse(); +} + + + + +// This function writes text to the screen. You can use the setCur function to place the cursor in the desired position first. + +// This function writes text to the screen. You can use the setCur function to place the cursor in the desired position first. +void writeText(byte textID) +{ +#ifdef DEBUG_SCREEN + Serial.println(F("WRITETEXT")); +#endif + byte string_length = strlen_P(MessageTable[textID].Description); + + touch_screen_serial.write( 0x7E ); + touch_screen_serial.write( string_length + 2); + touch_screen_serial.write( 0x11 ); + for( byte i=0; i < string_length; i++ ){ + char char_byte = pgm_read_byte(MessageTable[textID].Description + i); + //Serial.print("-"); Serial.print(char_byte); + touch_screen_serial.write( char_byte ); + } + touch_screen_serial.write( 0xEF ); + + waitForResponse(); +} + + +void writeString(char string_array[], byte string_length) +{ +#ifdef DEBUG_SCREEN + Serial.println(F("WRITESTRING")); +#endif + //byte string_length = strlen_P(MessageTable[textID].Description) + 2; + + touch_screen_serial.write( 0x7E ); + touch_screen_serial.write( string_length + 2 ); + touch_screen_serial.write( 0x11 ); + for( byte i=0; i < string_length; i++ ){ + char char_byte = string_array[i]; + //Serial.print("-"); Serial.print(char_byte); + touch_screen_serial.write( char_byte ); + } + touch_screen_serial.write( 0xEF ); + + waitForResponse(); +} + + +#ifdef DEBUG_SCREEN +void drawPix(int x, int y, int c) // Draw a pixel on the screen. +{ + Serial.println(F("DRAWPIX:")); + Serial.print(F("x: ")); Serial.println(x); + Serial.print(F("y: ")); Serial.println(y); + Serial.print(F("c: ")); Serial.println(c); + byte command[10] = {0x7E, 0x08, 0x21, highByte(x), lowByte(x), highByte(y), lowByte(y), highByte(c), lowByte(c), 0xEF}; + for( int i=0; i < sizeof(command); i++ ){ + touch_screen_serial.write( command[i] ); + //Serial.print(command[i],HEX); Serial.print(F(" ")); + } + waitForResponse(); +} +#endif + + +// This function places the text cursor anywhere on the screen. +void setCur(int x, int y) +{ +#ifdef DEBUG_SCREEN + Serial.println(F("SETCUR")); + Serial.print(F("x: ")); Serial.println(x); + Serial.print(F("y: ")); Serial.println(y); +#endif + byte command[8] = {0x7E, 0x06, 0x01, highByte(x), lowByte(x), highByte(y), lowByte(y), 0xEF}; + for( int i=0; i < sizeof(command); i++ ){ + touch_screen_serial.write( command[i] ); + //Serial.print(command[i],HEX); Serial.print(F(" ")); + } + waitForResponse(); +} + + +// This function outputs a variable number to the screen. It can show negative and positive numbers. It cannot show floats. +void displayNumber(int number) +{ +//#ifdef DEBUG_SCREEN + Serial.print(F("DISPLAYNUMBER (")); Serial.println(number); + Serial.print("Number:"); Serial.println(number); +//#endif + byte command[8] = {0x7E, 0x06, 0x13, 0x00, 0x0A, highByte(number), lowByte(number), 0xEF}; + for( int i=0; i < sizeof(command); i++ ){ + touch_screen_serial.write( command[i] ); + //Serial.print(command[i],HEX); Serial.print(F(" ")); + } + waitForResponse(); +} + + +// This function outputs a variable number to the screen. It can show negative and positive numbers. It cannot show floats. +void fontSize(byte font_size) +{ +#ifdef DEBUG_SCREEN + Serial.print(F("TEXTSIZE (")); Serial.println(font_size); +#endif + byte command[5] = {0x7E, 0x03, 0x03, lowByte(font_size), 0xEF}; + //byte command[5] = {0x7E, 0x03, 0x03, font_size, 0xEF,}; + for( int i=0; i < sizeof(command); i++ ){ + touch_screen_serial.write( command[i] ); + //Serial.print(command[i],HEX); Serial.print(F(" ")); + } + waitForResponse(); +} + + +// This function reads the serial data (if available) from the screen. +void readResponse() +{ + volatile int availableSerialCount = touch_screen_serial.available(); + + if( availableSerialCount < 5 ){ + return; + } + //Serial.println(F("READRESPONSE")); + + boolean savingMessage = false; // When touch message, this starts recording data into an array for analysis. + //byte metaData[metadataArraySize]; // An array to store the received serial data // TODO: use the metadata array instead. It's just as long, and their uses don't overlap. + byte metaDataPosition = 0; // The metaData array is recycled: here is holds incoming serial data. + byte startMarker = 0x7E; // Any response from the screen will start with this. + byte endMarker = 0xEF; // Any response from the screen will end with this. + byte rc; // Hold the byte form the serial stream that we're examining. + + byte c = touch_screen_serial.peek(); + if( c != startMarker ){ + rc = touch_screen_serial.read(); + Serial.print(F("throwing away left over touch_screen_serial byte:")); Serial.println(rc); + return; + } + +#ifdef DEBUG_SCREEN + Serial.println(F("GOT RESPONSE")); +#endif + + while( touch_screen_serial.available() ){ // By not checking for this the entire buffer is always cleared. + rc = touch_screen_serial.read(); +#ifdef DEBUG_SCREEN + Serial.print(rc); Serial.print(F("-")); +#endif + if( savingMessage == true ){ // We are now in the part of the response that is the message we were looking for. + if(rc != endMarker){ + metaData[metaDataPosition] = rc; + metaDataPosition++; + if( metaDataPosition >= metadataArraySize ){ + metaDataPosition = metadataArraySize - 1; + } + } + else { // We've arrived at the end marker. + metaData[metaDataPosition] = '\0'; // Terminate the string. + savingMessage = false; + metaDataPosition = 0; + if(metaData[metaDataPosition] == 0x06 && metaData[metaDataPosition + 1] == 0x07){ + touchX = touchToX( (metaData[metaDataPosition + 2] * 256) + metaData[metaDataPosition + 3] ); + touchY = touchToY( (metaData[metaDataPosition + 4] * 256) + metaData[metaDataPosition + 5] ); +#ifdef DEBUG_SCREEN + Serial.print(F("touchX=")); Serial.println(touchX); + Serial.print(F("touchY=")); Serial.println(touchY); +#endif + + if( screen_brightness == 0 ){ +#ifdef DEBUG_SCREEN + Serial.println(F("Turning on backlight")); +#endif + turnOnScreen(true); + } + else{ +#ifdef DEBUG_SCREEN + Serial.println(F("Turning off backlight")); +#endif + turnOnScreen(false); + //basicCommand(backlight_off); + //screen_brightness = 0; // To indicate that a touch event has just occured. + } + clearReceivedBuffer(); + } +#ifdef DEBUG_SCREEN + // OK message + else if( metaData[metaDataPosition] == 0x03 && metaData[metaDataPosition + 1] == 0x6F && metaData[metaDataPosition + 2] == 0x6B ){ + Serial.println(F("-OK")); + } + else{ // Unimplemented response form the touch screen. + Serial.println(F("-X-")); + metaDataPosition++; + if( metaDataPosition == metadataArraySize ){ return; } + } +#endif + } + } + if( rc == startMarker ){ // Once a valid startMarker is found, we start saving the message into the array. + savingMessage = true; +#ifdef DEBUG_SCREEN + Serial.print(F("(startMarker) ")); +#endif + } + } +} + + +// This function can send basic string command that don't have any variable parts in them. +void basicCommand(const char* cmd) +{ +#ifdef DEBUG_SCREEN + Serial.println(F("BASIC COMMAND")); +#endif + if( touch_screen_serial.available() ){ // If necessary, clear old messages from the serial buffer. + clearReceivedBuffer(); + } + + touch_screen_serial.write(0x7E); // Starting byte, is always the same. + byte b = 0; + while( b < MAX_BASIC_COMMAND_LENGTH ){ // How many bytes are the basic commands at most? + touch_screen_serial.write( pgm_read_byte(&cmd[b]) ); + //Serial.print( pgm_read_byte(&cmd[b]),HEX ); Serial.print(F(" ")); + if( pgm_read_byte(&cmd[b]) == 0xEF ){ // This breaks out of the loop. + //waitForResponse(b); + b = MAX_BASIC_COMMAND_LENGTH; + } + b++; + } + waitForResponse(); +} + + +// This function can be activated after sending a command. It will wait until a response has arrived (or 100 milliseconds have passed), and then allow the Arduino to continue. +//void waitForResponse(byte expectedBytes) // From the touch screen + +void waitForResponse() // From the touch screen +{ +#ifdef DEBUG_SCREEN + Serial.println(); Serial.println(F("WAITING FOR RESPONSE FROM SCREEN")); + Serial.print(F("-available now: ")); Serial.println( touch_screen_serial.available() ); +#endif + byte b = 0; + while( touch_screen_serial.available() == 0 && b < 250){ + b++; + wait(1); + } +#ifdef DEBUG_SCREEN + Serial.print(F("wait time: ")); Serial.println(b); +#endif + if( touch_screen_serial.available() > 0 ){ + wait(10); // Perhaps some more bytes will show up. + while( touch_screen_serial.available() > 0 ){ // Throwing away the response. All we care about is touch messages, and they are handled in the readResponse function. + byte x = touch_screen_serial.read(); + //Serial.print(x); Serial.print(F("-")); + } + //Serial.println(); + } + else if( b == 250 ){ + Serial.println(F("Touch screen did not respond to command")); + } +} + + + +void turnOnScreen(boolean desired_state) +{ +//#ifdef DEBUG_SCREEN + Serial.print(F("Setting screen backlicht to: ")); Serial.println(desired_state); +//#endif + if( desired_state == true ){ +#ifdef DEBUG_SCREEN + Serial.println(F("BC: backlight_on")); +#endif + screen_brightness = 255; + basicCommand(backlight_on); // Turn on the touch screen backlight. + send(button_message.setSensor(SCREEN_BUTTON_CHILD_ID).set(1)); + } + else { +#ifdef DEBUG_SCREEN + Serial.println(F("BC: backlight_off")); +#endif + screen_brightness = 0; + basicCommand(backlight_off); // Turn off the touch screen backlight + send(button_message.setSensor(SCREEN_BUTTON_CHILD_ID).set(0)); + } +} + + +int touchToX(int x) +{ + return int constrain(((x - 80) / 3.7), 0, TOUCHSCREEN_WIDTH); +} +int touchToY(int y) +{ + return int constrain(((y - 100) / 2.8), 0, TOUCHSCREEN_HEIGHT); +} + + +void clearReceivedBuffer() +{ +//Serial.print(F("cleaning:")); + while( touch_screen_serial.available() ){ + char x = touch_screen_serial.read(); + Serial.print(x); + } + Serial.println(); +} + +#endif // End of touch screen check. + + + +void receive(const MyMessage &message) +{ + Serial.print(F("INCOMING MESSAGE for child #")); Serial.println(message.sensor); + if (message.isAck()) { + Serial.println(F("-Ack")); + connectedToNetwork = true; + } +#ifdef HAS_TOUCH_SCREEN + else if( message.type==V_STATUS && message.sensor == SCREEN_BUTTON_CHILD_ID ){ + boolean desired_state = message.getBool(); + Serial.print(F("-Requested status: ")); Serial.println(desired_state); + turnOnScreen(desired_state); // Set the touch screen brightness + } +#endif +} + + + + + +/* + * + * The MySensors Arduino library handles the wireless radio link and protocol + * between your home built sensors/actuators and HA controller of choice. + * The sensors forms a self healing radio network with optional repeaters. Each + * repeater and gateway builds a routing tables in EEPROM which keeps track of the + * network topology allowing messages to be routed to nodes. + * + * Created by Henrik Ekblad + * Copyright (C) 2013-2015 Sensnology AB + * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors + * + * Documentation: http://www.mysensors.org + * Support Forum: http://forum.mysensors.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + */