From 0b776b2086961230d7c0acfef9f4fcebd575c943 Mon Sep 17 00:00:00 2001 From: StefanStrobel <> Date: Mon, 6 Feb 2017 18:26:50 +0000 Subject: [PATCH] 98_Arducounter.pm: added firmware for arduino git-svn-id: https://svn.fhem.de/fhem/trunk@13346 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/FHEM/firmware/ArduCounter.hex | 501 +++++++++++++++ fhem/contrib/arduino/ArduCounter.ino | 464 -------------- fhem/contrib/arduino/ArduCounter1.8.ino | 804 ++++++++++++++++++++++++ 3 files changed, 1305 insertions(+), 464 deletions(-) create mode 100755 fhem/FHEM/firmware/ArduCounter.hex delete mode 100755 fhem/contrib/arduino/ArduCounter.ino create mode 100755 fhem/contrib/arduino/ArduCounter1.8.ino diff --git a/fhem/FHEM/firmware/ArduCounter.hex b/fhem/FHEM/firmware/ArduCounter.hex new file mode 100755 index 000000000..ff6dba664 --- /dev/null +++ b/fhem/FHEM/firmware/ArduCounter.hex @@ -0,0 +1,501 @@ +:100000000C9456010C947E010C947E010C947008A3 +:100010000C944B080C9426080C947E010C947E01E1 +:100020000C947E010C947E010C947E010C947E0154 +:100030000C947E010C947E010C947E010C947E0144 +:100040000C94DC070C947E010C94AA070C94840792 +:100050000C947E010C947E010C947E010C947E0124 +:100060000C947E010C947E014572726F723A2000EE +:1000700041726475436F756E7465722056312E3807 +:1000800000000000002300260029002C2061766774 +:10009000206C656E20002C206C61737420617420CC +:1000A000004D202020666972737420617420002046 +:1000B00052656A2000206D73002920696E2000209F +:1000C000282B002C20636F756E7420002C206E6F1F +:1000D000206D696E206C656E002066616C6C696EC7 +:1000E000670020726973696E6700206D73002C20B1 +:1000F0006D696E206C656E20006368616E676500D7 +:1001000066616C6C696E6700726973696E67002C5A +:1001100020694D6F646520005043496E74207069FA +:100120006E20005374617274656400000000080062 +:1001300002010000030407000000000000000000AE +:10014000000000250028002B0000000000240027EC +:10015000002A0020004D20646566696E6564207089 +:10016000696E2000416464496E7400496C6C656777 +:10017000616C2070696E2073706563696669636184 +:1001800074696F6E2000496C6C6567616C207069E2 +:100190006E2073706563696669636174696F6E2050 +:1001A0000004040404040404040202020202020320 +:1001B000030303030301020408102040800102042A +:1001C0000810200102040810204D2072656D6F7622 +:1001D0006564200052656D496E7400496C6C6567FA +:1001E000616C2070696E2073706563696669636114 +:1001F00074696F6E20002000200020004D20696E81 +:1002000074657276616C732073657420746F20005E +:1002100073697A6500204D696C6C697365636F6EF4 +:100220006473004D204E657874207265706F72742F +:1002300020696E200020004D2070696E20004D2046 +:100240006D696E20636F756E7420004D206D696E50 +:1002500020696E74657276616C20004D206D617846 +:1002600020696E74657276616C20004D206E6F722D +:100270006D616C20696E74657276616C20004D2032 +:100280005374617475733A200048656C6C6F00207C +:100290004100204C00204600205800204E002054F1 +:1002A000002044002043005200000F0F11241FBE05 +:1002B000CFEFD8E0DEBFCDBF11E0A0E0B1E0E4EFCA +:1002C000FEE102C005900D92A034B107D9F726E0F7 +:1002D000A0E4B1E001C01D92A836B207E1F711E039 +:1002E000C6E5D1E004C02197FE010E946D0FC5351F +:1002F000D107C9F70E9495080C94780F0C94000060 +:10030000E091CB05F091CC050190F081E02D682FB4 +:100310008BEC95E009940F931F93CF93DF938C019F +:10032000C0E0D0E0F801EC0FFD1F6491662371F08E +:10033000E091CB05F091CC050190F081E02D8BECA4 +:1003400095E00995892B11F02196ECCFCE01DF9134 +:10035000CF911F910F910895CF92DF92EF92FF926C +:100360000F931F93CF93DF936C017A018B01C0E051 +:10037000D0E0CE15DF0589F0D8016D918D01D60151 +:10038000ED91FC910190F081E02DC6010995892B3A +:1003900011F47E0102C02196ECCFC701DF91CF910D +:1003A0001F910F91FF90EF90DF90CF900895FC0187 +:1003B000918D828D981761F0828DDF01A80FB11D9C +:1003C0005D968C91928D9F5F9F73928F90E00895C0 +:1003D0008FEF9FEF0895FC01918D828D981731F07A +:1003E000828DE80FF11D858D90E008958FEF9FEFCE +:1003F0000895FC01918D228D892F90E0805C9F4FA4 +:10040000821B91098F73992708958BEC95E00E94C8 +:10041000F90121E0892B09F420E0822F0895FC01E5 +:10042000848DDF01A80FB11DA35ABF4F2C91848D7D +:1004300090E001968F739927848FA689B7892C93B2 +:10044000A089B1898C9180648C93938D848D98134D +:1004500006C00288F389E02D80818F7D8083089516 +:10046000EF92FF920F931F93CF93DF93EC0181E004 +:10047000888F9B8D8C8D981305C0E889F9898081C0 +:1004800085FD24C0F62E0B8D10E00F5F1F4F0F73FC +:100490001127E02E8C8DE8120CC00FB607FCFACFA6 +:1004A000E889F989808185FFF5CFCE010E940F028E +:1004B000F1CF8B8DFE01E80FF11DE35AFF4FF08263 +:1004C0000B8FEA89FB898081806207C0EE89FF89F2 +:1004D0006083E889F98980818064808381E090E08D +:1004E000DF91CF911F910F91FF90EF900895CF93DF +:1004F000DF93EC01888D8823C9F0EA89FB8980812C +:1005000085FD05C0A889B9898C9186FD0FC00FB6FD +:1005100007FCF5CF808185FFF2CFA889B9898C913E +:1005200085FFEDCFCE010E940F02E7CFDF91CF9183 +:10053000089580E090E0892B29F00E940502811146 +:100540000C94000008953FB7F894809108029091B0 +:100550000902A0910A02B0910B0226B5A89B05C022 +:100560002F3F19F00196A11DB11D3FBFBA2FA92F32 +:10057000982F8827820F911DA11DB11DBC01CD01AF +:1005800042E0660F771F881F991F4A95D1F708959B +:100590002FB7F894609104027091050280910602D1 +:1005A000909107022FBF08952F923F924F925F9232 +:1005B0006F927F928F929F92AF92BF92CF92DF9273 +:1005C000EF92FF920F931F93CF93DF93CDB7DEB7D8 +:1005D00060970FB6F894DEBF0FBECDBF082F0E9404 +:1005E000C8026B017C01202F30E0F901EE0FFF1FE4 +:1005F000EB57FF4FA591B4916C91F901E05CFE4F70 +:10060000708176276083220F331FF901E25EFE4F6F +:100610000190F081E02D8081782309F4F8C0000F6B +:10062000000F000F40E050E011E0A1E0B0E0042E28 +:1006300001C0AA0F0A94EAF7872F8A2309F4E1C0C0 +:10064000202F240F30E0F901E45FFD4F80818130DD +:1006500069F0833021F4862F8A2341F4D2C082309E +:1006600009F0CFC0862F8A2309F0CBC01901220CD4 +:10067000331CF101E156FC4F408051805A8249827F +:10068000C901880F991F880F991F452809F47EC05A +:10069000A623F901E45CFD4FAA2321F0A081A33039 +:1006A00061F403C0A081A23041F4FC01ED55FE4F7E +:1006B000C082D182E282F382A4C0FC01ED55FE4FDC +:1006C000408051806280738049865A866B867C8622 +:1006D000DC01AD5BBE4F4D905D906D907C901397AB +:1006E0004D825E826F827886408051806280738006 +:1006F0004601570184189508A608B7088D869E867E +:10070000AF86B88ACD92DD92ED92FC921397698004 +:100710007A802301612C712C49825A826B827C82FF +:1007200084149504A604B704A8F089849A84AB8441 +:10073000BC844D805E806F80788484189508A608FC +:10074000B70849805A806B807C8084149504A60485 +:10075000B70448F4F101E159FC4F80819181019681 +:10076000918380834EC0FC01E158FD4F80809180D1 +:10077000A280B3804D845E846F847888480C591CB5 +:100780006A1C7B1C4082518262827382FC01E157A9 +:10079000FB4FC082D182E282F382FC01E152FD4F25 +:1007A00080809180A280B38089288A288B2821F4B8 +:1007B000C082D182E282F382F901EC5AFD4F20819E +:1007C000222379F0FC01E15DFB4F80819181A281C0 +:1007D000B3810196A11DB11D80839183A283B38350 +:1007E00010C0DC01A157BB4F8D909D90AD90BC9087 +:1007F000DC01A151BB4F8D929D92AD92BC9213979B +:1008000010834F5F5F4F4830510509F00ECF60965F +:100810000FB6F894DEBF0FBECDBFDF91CF911F9111 +:100820000F91FF90EF90DF90CF90BF90AF909F908F +:100830008F907F906F905F904F903F902F90089592 +:10084000CF93DF93C0E7D0E0F0E0C038DF0731F0AE +:10085000FE0184910E9480012196F6CFDF91CF9115 +:100860000895CF93DF93C8E6D0E0F0E0CF36DF07FE +:1008700031F0FE0184910E9480012196F6CFDF9134 +:10088000CF910895E82FF0E0EF55FE4FE491E2504C +:10089000E13011F4865005C0877098E0E99F800D23 +:1008A000112490E0089542E050E06CE371E08BEC9D +:1008B00095E00C94AC01CF93DF930E948B01EC0187 +:1008C0000E9453048C0F9D1FDF91CF9108958F924A +:1008D0009F92AF92BF92CF92DF92EF92FF920F93CF +:1008E0001F93CF93DF930E94C8020FEA15E0AFEC8D +:1008F000CA2EA3E0DA2ECFE9D3E0AFE3B3E02FE4D2 +:10090000822E25E0922E20E030E0BB24BA94F8013C +:10091000B1928F01A9014C5A5D4FFA011082A901D1 +:10092000440F551F440F551FFA01E15DFB4F108224 +:10093000118212821382F601119211921192119278 +:100940006F01FA01E151FB4F6083718382839383CE +:10095000FA01E157FB4F6083718382839383FA012D +:10096000ED55FE4F60837183828393834D5B5E4FB1 +:10097000FA01608371838283938319921992A9018A +:10098000440F551F41595C4FFA01118210821D928C +:100990001D92F40161937193819391934F012F5FA5 +:1009A0003F4F2831310509F0B2CF0E94C8020091B3 +:1009B0000601109107012091080130910901DC0125 +:1009C000CB01800F911FA21FB31F8093C705909387 +:1009D000C805A093C905B093CA05E091DB05F09165 +:1009E000DC0582E08083E091D705F091D805108284 +:1009F000E091D905F091DA0583E380831092E30555 +:100A0000E091DF05F091E00586E08083E091DD056F +:100A1000F091DE05808180618083E091DD05F091B9 +:100A2000DE05808188608083E091DD05F091DE0540 +:100A3000808180688083E091DD05F091DE05808112 +:100A40008F7D80830E94A3024B015C0184EFC82E3E +:100A5000DD24D394E12CF12C0E94A302DC01CB0114 +:100A600088199909AA09BB09883E9340A105B105D7 +:100A700058F0F1E0CF1AD108E108F10828EE820E13 +:100A800023E0921EA11CB11CC114D104E104F104A5 +:100A900019F778940E9453040E94200483E291E0A5 +:100AA000DF91CF911F910F91FF90EF90DF90CF904A +:100AB000BF90AF909F908F900C945B048F929F9209 +:100AC000AF92BF920F931F93CF93DF93CDB7DEB753 +:100AD000A1970FB6F894DEBF0FBECDBF19A242306A +:100AE00008F44AE08E010F5D1F4F842E912CA12C3B +:100AF000B12CA50194010E943C0FE62FB901CA0157 +:100B000001501109EA3014F4E05D01C0E95CD8013C +:100B1000EC93232B242B252B61F70115110571F084 +:100B2000F80101900020E9F73197AF01401B510B0C +:100B3000B8018BEC95E00E94AC0102C080E090E02F +:100B4000A1960FB6F894DEBF0FBECDBFDF91CF9157 +:100B50001F910F91BF90AF909F908F9008954AE0A2 +:100B60000C945E05CF93DF93BC0180E090E04AE0F7 +:100B70000E945E05EC010E9453048C0F9D1FDF91C3 +:100B8000CF910895CF92DF92EF92FF92CF93DF93B0 +:100B900097FF19C06B017C018DE20E948001EC017E +:100BA00066277727CB016C197D098E099F094AE0DA +:100BB0000E945E058C0F9D1FDF91CF91FF90EF90FB +:100BC000DF90CF9008954AE0DF91CF91FF90EF90B2 +:100BD000DF90CF900C945E05CF93DF93BC01990F0B +:100BE000880B990B0E94C205EC010E9453048C0FE4 +:100BF0009D1FDF91CF910895CF93DF934AE00E942C +:100C00005E05EC010E9453048C0F9D1FDF91CF9174 +:100C100008952F923F924F925F926F927F928F92A0 +:100C20009F92AF92BF92CF92DF92EF92FF920F937B +:100C30001F93CF93DF93CDB7DEB727970FB6F89406 +:100C4000DEBF0FBECDBF8983282E312CC101880F96 +:100C5000991F880F991F9F838E83FC01E157FB4FDB +:100C6000C080D180E280F3808C0101511B4FF801DC +:100C700080819181A281B381C81AD90AEA0AFB0A4C +:100C8000EE81FF81E15DFB4F408051806280738087 +:100C9000EE81FF81E153FC4F80819181A281B3817C +:100CA00053014201881A990AAA0ABB0AC114D10445 +:100CB000E104F10469F40E94C802F8010081118185 +:100CC000228133816B017C01C01AD10AE20AF30A46 +:100CD00088E191E00E948B012981622F70E080E021 +:100CE00090E04AE00E945E058FE091E00E948B0157 +:100CF000F101E45FFD4F8081823051F0833029F0B3 +:100D0000813051F489EF90E005C088E091E002C0A5 +:100D100080E091E00E948B018101000F111F98017A +:100D200021563C4FF90180819181892B11F18EEE82 +:100D300090E02C833D830E948B012C813D81F90141 +:100D40006081718180E090E04AE00E945E058AEE59 +:100D500090E00E948B01F101E45CFD4F80818230C4 +:100D600029F0833051F482EE90E005C089ED90E0E7 +:100D700002C08CEC90E00E948B0183EC90E00E941A +:100D80008B01C301B2010E94AF058FEB90E00E947E +:100D90008B01C501B4010E94AF0589EB90E00E9470 +:100DA0008B01C701B6010E94AF0585EB90E00E9460 +:100DB0008B01F801E156FC4F80819181892BA9F0CC +:100DC0008FEA90E00E948B01F801E159FC4F6081AD +:100DD0007181F801E15CFC4F80819181681B790B86 +:100DE00080E090E04AE00E945E0581149104A10435 +:100DF000B10409F46EC00E94530481EA90E00E949D +:100E00008B01EE81FF81E152FD4F80819181A281B2 +:100E1000B3810E811F81015B1A4FF801C080D18020 +:100E2000E280F380BC01CD016C197D098E099F0918 +:100E30000E94AF0586E990E00E948B01EE81FF8160 +:100E4000E157FB4F80819181A281B381F80100813C +:100E5000118122813381BC01CD01601B710B820B9A +:100E6000930B0E94AF05F894EE81FF81E158FD4F8E +:100E70006081718182819381A50194010E943C0F60 +:100E800078948BE890E02C833D834A835B830E94B7 +:100E90008B012C813D814A815B81CA01B901279672 +:100EA0000FB6F894DEBF0FBECDBFDF91CF911F917B +:100EB0000F91FF90EF90DF90CF90BF90AF909F90F9 +:100EC0008F907F906F905F904F903F902F900C94F9 +:100ED000AF0527960FB6F894DEBF0FBECDBFDF91EA +:100EE000CF911F910F91FF90EF90DF90CF90BF9027 +:100EF000AF909F908F907F906F905F904F903F90BA +:100F00002F9008950E94730F1F920F920FB60F92A9 +:100F100011242F933F934F935F936F937F938F93FE +:100F20009F93AF93BF93EF93FF938BEC95E00E9459 +:100F30000F02FF91EF91BF91AF919F918F917F91A0 +:100F40006F915F914F913F912F910F900FBE0F9036 +:100F50001F9018951F920F920FB60F9211242F9386 +:100F60008F939F93EF93FF93E091DB05F091DC0566 +:100F70008081E091E105F091E20582FD12C090814F +:100F80008091E4058F5F8F732091E505821751F002 +:100F9000E091E405F0E0E553FA4F958F8093E40586 +:100FA00001C08081FF91EF919F918F912F910F90C0 +:100FB0000FBE0F901F9018951F920F920FB60F92B1 +:100FC00011242F933F938F939F93AF93BF9380915F +:100FD000040290910502A0910602B091070230919F +:100FE000030223E0230F2D3720F40196A11DB11D2C +:100FF00005C026E8230F0296A11DB11D2093030210 +:101000008093040290930502A0930602B093070216 +:101010008091080290910902A0910A02B0910B02FE +:101020000196A11DB11D8093080290930902A0931F +:101030000A02B0930B02BF91AF919F918F913F91A4 +:101040002F910F900FBE0F901F9018951F920F9227 +:101050000FB60F9211242F933F934F935F936F938B +:101060007F938F939F93AF93BF93EF93FF9382E010 +:101070000E94D402FF91EF91BF91AF919F918F9108 +:101080007F916F915F914F913F912F910F900FBE84 +:101090000F901F9018951F920F920FB60F92112468 +:1010A0002F933F934F935F936F937F938F939F9370 +:1010B000AF93BF93EF93FF9381E00E94D402FF911F +:1010C000EF91BF91AF919F918F917F916F915F91C0 +:1010D0004F913F912F910F900FBE0F901F90189539 +:1010E0001F920F920FB60F9211242F933F934F939D +:1010F0005F936F937F938F939F93AF93BF93EF9380 +:10110000FF9380E00E94D402FF91EF91BF91AF91D5 +:101110009F918F917F916F915F914F913F912F910F +:101120000F900FBE0F901F901895CF93DF93CDB700 +:10113000DEB7A8970FB6F894DEBF0FBECDBF789488 +:1011400084B5826084BD84B5816084BD85B58260CC +:1011500085BD85B5816085BD80916E00816080937D +:101160006E001092810080918100826080938100E6 +:1011700080918100816080938100809180008160F6 +:10118000809380008091B10084608093B100809151 +:10119000B00081608093B00080917A008460809379 +:1011A0007A0080917A00826080937A0080917A0040 +:1011B000816080937A0080917A00806880937A00C1 +:1011C0001092C1000E9467048FEA95E068E1C62E84 +:1011D000C80ECF8E0E94C8026D877E878F87988B3E +:1011E0008BEC95E00E94F901892B09F47CC38BEC10 +:1011F00095E00E94D7018C32B9F420917E02822FB3 +:1012000090E0873091050CF06EC32F5F20937E0233 +:10121000880F991FFC01E459FD4F80917C02909149 +:101220007D02918380835BC390ED980F9A30B0F478 +:1012300040917C0250917D029AE0949F9001959F8D +:10124000300D1124205331097901E80EF11C87FD7E +:10125000FA94F0927D02E0927C0245C39FE9980FD8 +:101260009A3108F040C3883609F42FC334F481362C +:1012700071F0843609F454C130C3823709F479C25D +:10128000833709F479C2893609F027C39EC180915A +:101290007E02E82FF0E0EE0FFF1FE459FD4F209192 +:1012A0007C0230917D02318320838F5F80937E02A8 +:1012B0000E94C8024B015C0160906C0270906D024C +:1012C000862D0E9442047C01C3010197479748F490 +:1012D000F301EC5DFE4F8081882319F028E12E1583 +:1012E00040F40E94310486E891E00E948B01C30122 +:1012F00093C580916E0290916F0287011127823011 +:10130000910581F083309105A9F00197D9F00E94F1 +:1013100031048BE691E00E948B01C3010E94EC0531 +:10132000D12C12C0F801E45CFD4F32E0308332E092 +:10133000D32E0AC0F801E45CFD4F43E0408323E074 +:10134000D22E02C0DD24D394D301AB54BE4FFD0195 +:10135000C490A3014F555E4FFA0124912223C9F096 +:1013600030E0220F331FF901E75BFE4F8591949126 +:10137000F901E15CFE4F659174913FB7F894FC016F +:101380002081C0942C212083FB018081C822C0824F +:101390003FBF8091700290917102892B09F442C085 +:1013A000F301E55DFE4F8491FD012491FA019491D2 +:1013B0009923C1F1882339F1833091F038F48130D9 +:1013C000A9F0823001F584B58F7D12C0873091F08D +:1013D0008830A1F08430B9F4809180008F7D03C003 +:1013E000809180008F77809380000DC084B58F77C7 +:1013F00084BD09C08091B0008F7703C08091B00098 +:101400008F7D8093B000E92FF0E0EE0FFF1FE15CCD +:10141000FE4F859194913FB7F894FC016081262B93 +:1014200020833FBFF801EC5DFD4FD08280917202B6 +:1014300090917302009711F0DD24D394FD01A491E3 +:10144000FA014491442309F4AAC44250E42FF0E085 +:10145000EE0FFF1FE25EFE4F0190F081E02D208134 +:10146000A22BA0835091680021E030E001C0220F40 +:101470004A95EAF7252B20936800F801E45FFD4FB9 +:10148000D082F801EE0FFF1FE156FC4F918380835D +:10149000F801E155FA4F8081082E000C990B861552 +:1014A000970561F16082F801EC5AFD4F1082000F40 +:1014B000111F000F111FF801E15DFB4F1082118217 +:1014C00012821382F801E153FC4F108211821282C2 +:1014D0001382F801E151FB4F80829182A282B38294 +:1014E000F801E157FB4F80829182A282B382F8011A +:1014F000E15BFA4F80829182A282B38285E591E01E +:101500000E948B01B301770C880B990B0E94C205D6 +:1015100083E591E00E948B018E2D0E940906D2C1C5 +:1015200080917E02E82FF0E0EE0FFF1FE459FD4F9F +:1015300020917C0230917D02318320838F5F8093E4 +:101540007E0200916C0210916D02802F0E94420475 +:101550009801215031092731310540F4F801EC5D43 +:10156000FE4F2081222311F0893128F00E9431049E +:101570008BED91E04EC4F801EB54FE4F2491F8013D +:10158000EF55FE4FE491EE2309F40EC4E250AE2F66 +:10159000B0E0AA0FBB1FA25EBE4F0D90BC91A02D64 +:1015A0003C91209523232C932C91211102C430913E +:1015B000680041E050E001C0440FEA95EAF7242FAB +:1015C0002095232320936800F4C380917E02E82FA6 +:1015D000F0E0EE0FFF1FE459FD4F20917C023091A7 +:1015E0007D02318320838F5F80937E02843028F4D4 +:1015F0000E94310480E192E063C120916C0230913D +:101600006D02C901019780319E4038F00E9431047B +:1016100080916C0290916D025BC0A8EEB3E00E94D5 +:101620005E0F6B017C01609306017093070180934C +:101630000801909309010E94C802DC01CB018C0DC6 +:101640009D1DAE1DBF1D0091C7051091C8052091BD +:10165000C9053091CA0580179107A207B30770F436 +:101660000E94C802C60ED71EE81EF91EC092C7050A +:10167000D092C805E092C905F092CA0520916E0289 +:1016800030916F02C901019780319E4038F00E946D +:10169000310480916E0290916F021AC0A8EEB3E0FF +:1016A0000E945E0F60930A0170930B0180930C01FE +:1016B00090930D01209170023091710221315EE012 +:1016C000350748F00E9431048091700290917102B8 +:1016D0000E94B20502C12115310561F0A8EEB3E008 +:1016E0000E945E0F609300017093010180930201DC +:1016F000909303018091720290917302009721F000 +:1017000090930501809304018CEF91E00E948B017E +:1017100060916C0270916D0280E090E04AE00E945E +:101720005E058AEF91E00E948B0160916E027091DC +:101730006F0280E090E04AE00E945E0588EF91E051 +:101740000E948B01609170027091710280E090E0C4 +:101750004AE00E945E0586EF91E00E948B01609155 +:1017600072027091730280E090E04AE00E945E0590 +:10177000A9C00E946704B1C08EE792E00E948B016D +:101780000E9420040E9453048BE692E00E948B0189 +:101790006091060170910701809108019091090103 +:1017A0000E94FC058BE592E00E948B0160910A018A +:1017B00070910B0180910C0190910D010E94FC052C +:1017C0008BE492E00E948B01609100017091010115 +:1017D00080910201909103010E94FC058EE392E04A +:1017E0000E948B0180910401909105010E94B20535 +:1017F0000FEA15E0612C712CC62CD801DD908D010B +:10180000BFEFDB16D9F19301220F331F220F331FD5 +:10181000F901E157FB4F4081518162817381F901E8 +:10182000E151FB4F7F0180819181A281B381F90158 +:10183000E15DFB4F80809180A280B38048175907FB +:101840006A077B0739F40E94C802F7018081918101 +:10185000A281B38187E392E00E948B016D2DDD0CA4 +:10186000770B880B990B0E94C20585E392E00E94DA +:101870008B018C2D0E9409060E945304FFEF6F1A02 +:101880007F0A28E16216710409F0B6CF83E292E084 +:101890000E948B01C090C705D090C805E090C90593 +:1018A000F090CA050E94C802A7019601261B370BBB +:1018B000480B590BCA01B9010E94AF0585E192E0BE +:1018C0000E948B010E94530408C00E9453040E948E +:1018D000200489E892E00E945B0410927E0210923C +:1018E0007D0210927C028091C7059091C805A0915D +:1018F000C905B091CA05CD84DE84EF84F888C81A82 +:10190000D90AEA0AFB0AF7FE2CC080910A019091DD +:101910000B01A0910C01B0910D014D855E856F8585 +:101920007889481B590B6A0B7B0BAFE4B5E0EFEAF3 +:10193000F5E080E0919197FD0DC00D911D912D91E5 +:101940003C9113976A017B01C01AD10AE20AF30A9B +:10195000F7FE81E01496DF8CDE12ECCF882309F4C9 +:101960001BC20E94C8026B017C012FEA35E03E8B4E +:101970002D8B4FE455E05C874B876FE973E07A87E6 +:1019800069878FE393E098878F83AFECB3E0BA83E6 +:10199000A983212C312CED89FE892191FE8BED8BC1 +:1019A0002CA327FDC2C1F8948101000F111F000F65 +:1019B000111F980121513B4FD9014D915D916D91BE +:1019C0007C91498B5A8B6B8B7C8BF801E157FB4FD9 +:1019D00040815181628173814DA35EA36FA378A77B +:1019E000F801E15DFB4F4080518062807380789404 +:1019F0004A015B0169897A898B899C89861A970AD1 +:101A0000A80AB90AA981BA814D915D916D917C9125 +:101A1000D301C201841B950BA60BB70B8B839C8350 +:101A2000AD83BE83AB85BC854D915D916D917C91FD +:101A30004F8B588F698F7A8F40910A0150910B011B +:101A400060910C0170910D014B8F5C8F6D8F7E8FBB +:101A5000D701C601841B950BA60BB70B4F89588D78 +:101A6000698D7A8D841B950BA60BB70BB7FD4FC004 +:101A70008091040190910501A0E0B0E04B815C8170 +:101A80006D817E81481759076A077B07F8F08091BE +:101A9000000190910101A0910201B091030188150C +:101AA0009905AA05BB0590F4809106019091070164 +:101AB000A0910801B09109014B8D5C8D6D8D7E8DDB +:101AC00084179507A607B70709F059C0F894F801DD +:101AD000E157FB4FC082D182E282F382F801E151EB +:101AE000FB4FC082D182E282F3827894E981FA814D +:101AF0004082518262827382A701960169897A8944 +:101B00008B899C89261B370B480B590B4DC0809144 +:101B1000060190910701A0910801B0910901B70158 +:101B2000A601481B590B6A0B7B0BDB01CA014F89CD +:101B3000588D698D7A8D841B950BA60BB70BB7FD5D +:101B4000F4C08091040190910501A0E0B0E04B81C8 +:101B50005C816D817E81481759076A077B0708F40D +:101B6000E4C08091000190910101A0910201B09127 +:101B7000030188159905AA05BB0508F0D6C0A981FF +:101B8000BA814D925D926D927C921397F8944DA11B +:101B90005EA16FA178A5D9014D935D936D937C9360 +:101BA00013977894A501940187EA92E028A339A3BA +:101BB0004AA35BA30E948B01BCA16B2FBB0F770BC9 +:101BC000880B990B0E94C20584EA92E00E948B0167 +:101BD000C301B2010E94AF0581EA92E00E948B012D +:101BE0006B817C818D819E810E94AF058EE992E0A0 +:101BF0000E948B0128A139A14AA15BA1CA01B901A8 +:101C00000E94AF058BE992E00E948B01C701B601EB +:101C10000E94C205E985FA8580819181892B11F1A5 +:101C200088E992E00E948B01C101880F991F9C01F5 +:101C300021593C4F3901D9016D917C91EF81F88593 +:101C400080819181681B790B80E090E04AE00E94DE +:101C50005E05F894D3018D919C91EF81F885918375 +:101C6000808378942B813C814D815E81232B242BB2 +:101C7000252B09F451C085E992E00E948B01A8014F +:101C800041525D4F3A01FA0180819181A281B38175 +:101C9000BC01CD0129893A894B895C89621B730B90 +:101CA000840B950B0E94AF0582E992E00E948B01A4 +:101CB000C501B4010E94AF05D3011D921D921D9272 +:101CC0001C921397E985FA8580819181892B21F1F6 +:101CD000F89401581D4FD8016D917D918D919C9183 +:101CE0002B813C814D815E810E943C0FF801108266 +:101CF00011821282138278948FE892E028A339A38C +:101D00004AA35BA30E948B0128A139A14AA15BA130 +:101D1000CA01B9010E94AF050E945304AB85BC857E +:101D2000CD92DD92ED92FC921397BFEF2B1A3B0AF6 +:101D3000EB85FC853496FC87EB8729853A852E5FF9 +:101D40003F4F3A8729874F8158854E5F5F4F5887AD +:101D50004F8369817A816C5F7F4F7A83698378E1F1 +:101D60002716310409F017CE8091060190910701E2 +:101D7000A0910801B0910901CD84DE84EF84F88838 +:101D80008C0D9D1DAE1DBF1D8093C7059093C8058A +:101D9000A093C905B093CA050E9499021BCA0E946C +:101DA000310484E691E097CD0E94310484ED91E006 +:101DB00092CD9927FC01E155FA4F2FEF2083FC01CA +:101DC000EC5AFD4F10829C01220F331F220F331F4C +:101DD000F901E15DFB4F1082118212821382F90139 +:101DE000E153FC4F1082118212821382880F991FD7 +:101DF000FC01E156FC4F11821082FC01E15CFC4FBA +:101E000011821082FC01E159FC4F1182108289EC91 +:101E100091E00E948B01C8010E94EC055ECDEBECC5 +:101E2000F5E01382128288EE93E0A0E0B0E08483B4 +:101E30009583A683B78382E191E09183808385ECCB +:101E400090E09587848784EC90E09787868780EC84 +:101E500090E0918B808B81EC90E0938B828B82EC75 +:101E600090E0958B848B86EC90E0978B868B118E1F +:101E7000128E138E148E0895A1E21A2EAA1BBB1B7C +:101E8000FD010DC0AA1FBB1FEE1FFF1FA217B30746 +:101E9000E407F50720F0A21BB30BE40BF50B661F5C +:101EA000771F881F991F1A9469F760957095809520 +:101EB00090959B01AC01BD01CF010895A29FB00197 +:101EC000B39FC001A39F700D811D1124911DB29F6E +:101ED000700D811D1124911D0895EE0FFF1F0590B7 +:101EE000F491E02D099481E090E0F8940C94780F3F +:041EF000F894FFCF94 +:101EF400D007000001003075000060EA0000000017 +:101F040000003002AC01F901D701EB0177026B004C +:101F14006C006D00000000030405060700090A0BAD +:101F24000C000E0F10110000000000000D0A00004C +:00000001FF diff --git a/fhem/contrib/arduino/ArduCounter.ino b/fhem/contrib/arduino/ArduCounter.ino deleted file mode 100755 index eb9506d56..000000000 --- a/fhem/contrib/arduino/ArduCounter.ino +++ /dev/null @@ -1,464 +0,0 @@ -/* - * Sketch for counting impulses in a defined interval - * e.g. for power meters with an s0 interface that can be - * connected to an input of an arduino board - * - * the sketch uses pin change interrupts which can be anabled - * for any of the inputs on e.g. an arduino uno or a jeenode - * - * the pin change Interrupt handling used here - * is based on the arduino playground example on PCINT: - * http://playground.arduino.cc/Main/PcInt - * - * Refer to avr-gcc header files, arduino source and atmega datasheet. - */ - -/* Pin to interrupt map: - * D0-D7 = PCINT 16-23 = PCIR2 = PD = PCIE2 = pcmsk2 - * D8-D13 = PCINT 0-5 = PCIR0 = PB = PCIE0 = pcmsk0 - * A0-A5 (D14-D19) = PCINT 8-13 = PCIR1 = PC = PCIE1 = pcmsk1 - */ - -#include "pins_arduino.h" - -char* version = "ArduCounter V1.0"; -char* error = "error "; - -/* arduino pins that are typically ok to use - * (some are left out because they are used - * as reset, serial, led or other things on most boards) */ -byte allowedPins[20] = - { 0, 0, 0, 3, 4, 5, 6, 7, - 0, 9, 10, 11, 12, 0, - 14, 15, 16, 17, 0, 0}; - - -/* Pin change mask for each chip port */ -volatile uint8_t *port_to_pcmask[] = { - &PCMSK0, - &PCMSK1, - &PCMSK2 -}; - -/* last PIN States to detect individual pin changes in ISR */ -volatile static uint8_t PCintLast[3]; - - -unsigned long intervalMin = 10000; // default 10 sec -unsigned long intervalMax = 120000; // default 2 min - -unsigned long timeNextReport; -unsigned long now; - -boolean doReport = false; - -/* index to the following arrays is the internal PCINT pin number, not the arduino - * pin number because the PCINT pin number corresponds to the physical ports - * and this saves time for mapping to the arduino numbers - */ - -/* pin change mode (RISING etc.) as parameter for ISR */ -byte PCintMode[24]; - -/* pin number for PCINT number if active - otherwise -1 */ -char PCintActivePin[24]; - -/* did we get first interrupt yet? */ -volatile boolean initialized[24]; - -/* individual counter for each real pin */ -volatile unsigned long counter[24]; - -/* count at last report to get difference */ -unsigned long lastCount[24]; - -/* millis at first interrupt for current interval - * (is also last interrupt of old interval) */ -volatile unsigned long startTime[24]; - -/* millis at last interrupt */ -volatile unsigned long lastTime[24]; - -/* millis at last report - * to find out when maxInterval is over - * and report has to be done even if - * no impulses were counted */ -unsigned long lastReport[24]; - - -/* max for SplitLine */ -#define MAXLINEPARTS 5 - -String inputString = ""; // a string to hold incoming data -boolean newCommand = false; // whether the command string is complete - -String linePart[MAXLINEPARTS]; -int lineParts = 0; - - -/* Add a pin to be handled */ -int AddPin(uint8_t aPin, int mode) { - uint8_t pcintPin; // PCINT pin number for the pin to be added (this is used as index for most arrays) - volatile uint8_t *pcmask; // pointer to PCMSK0 or 1 or 2 depending on the port corresponding to the pin - unsigned long now = millis(); - - uint8_t bit = digitalPinToBitMask(aPin); // bit in PCMSK to enable pin change interrupt for this pin (arduino pin number!) - uint8_t port = digitalPinToPort(aPin); // port that this pin belongs to for enabling interrupts for the whole port (arduino pin number!) - - if (port == NOT_A_PORT) { - return 1; - } else { // map port to bit in PCIR register - port -= 2; - pcmask = port_to_pcmask[port]; // point to PCMSK0 or 1 or 2 depending on the port corresponding to the pin - } - if (port == 1) { // now calculate the PCINT pin number that corresponds to the arduino pin number - pcintPin = aPin - 6; // port 1: PC0-PC5 (A0-A5 or D14-D19) is PCINT 8-13 (PC6 is reset) - } else { // arduino numbering continues at D14 since PB6/PB7 are used for other things - pcintPin = port * 8 + (aPin % 8); // port 0: PB0-PB5 (D8-D13) is PCINT 0-5 (PB6/PB7 is crystal) - } // port 2: PD0-PD7 (D0-D7) is PCINT 16-23 - - PCintMode[pcintPin] = mode; // save mode for ISR which uses the pcintPin as index because this is easy to get in ISR - PCintActivePin[pcintPin] = aPin; // save real arduino pin number and flag this pin as active for reporting - - initialized[pcintPin] = false; // initialize arrays for this pin - counter[pcintPin] = 0; - lastCount[pcintPin] = 0; - startTime[pcintPin] = now; - lastTime[pcintPin] = now; - lastReport[pcintPin] = now; - - *pcmask |= bit; // set the pin change interrupt mask through a pointer to PCMSK0 or 1 or 2 depending on the port corresponding to the pin - PCICR |= 0x01 << port; // enable the interrupt - return 0; -} - - -/* Remove a pin to be handled */ -int RemovePin(uint8_t aPin) { - uint8_t pcintPin; - volatile uint8_t *pcmask; - uint8_t bit = digitalPinToBitMask(aPin); - uint8_t port = digitalPinToPort(aPin); - - if (port == NOT_A_PORT) { - return 1; - } else { - port -= 2; - pcmask = port_to_pcmask[port]; - } - if (port == 1) { // see comments at AddPin above - pcintPin = port * 8 + (aPin - 14); - } else { - pcintPin = port * 8 + (aPin % 8); - } - PCintActivePin[pcintPin] = -1; - - *pcmask &= ~bit; // disable the mask. - if (*pcmask == 0) { // if that's the last one, disable the interrupt. - PCICR &= ~(0x01 << port); - } - return 0; -} - - -// common interrupt handler. "port" is the PCINT port number (0-2) -static void PCint(uint8_t port) { - uint8_t bit; - uint8_t curr; - uint8_t mask; - uint8_t pcintPin; - - // get the pin states for the indicated port. - curr = *portInputRegister(port+2); // current pin states at port - mask = curr ^ PCintLast[port]; // xor gets bits that are different - PCintLast[port] = curr; // store new pin state for next interrupt - - if ((mask &= *port_to_pcmask[port]) == 0) { // mask is pins that have changed. screen out non pcint pins. - return; /* no handled pin changed */ - } - - for (uint8_t i=0; i < 8; i++) { - bit = 0x01 << i; // loop over each pin that changed - if (bit & mask) { // is pin change interrupt enabled for this pin? - pcintPin = port * 8 + i; // pcint pin numbers directly follow the bit numbers, only arduino pin numbers are special - - // count if mode is CHANGE, or if mode is RISING and - // the bit is currently high, or if mode is FALLING and bit is low. - if ((PCintMode[pcintPin] == CHANGE - || ((PCintMode[pcintPin] == RISING) && (curr & bit)) - || ((PCintMode[pcintPin] == FALLING) && !(curr & bit)))) { - lastTime[pcintPin] = millis(); // remember time of this impulse in case it will be the last in the interval - if (initialized[pcintPin]) { - counter[pcintPin]++; // count - } else { - startTime[pcintPin] = lastTime[pcintPin]; // if this is the first impulse on this pin, remember time as first impulse in interval - initialized[pcintPin] = true; // and start counting the next impulse - } - } - } - } -} - - -SIGNAL(PCINT0_vect) { - PCint(0); -} -SIGNAL(PCINT1_vect) { - PCint(1); -} -SIGNAL(PCINT2_vect) { - PCint(2); -} - - -/* split a line read from serial into individual words - * and store them in the array lineParts[] */ -void SplitLine () { - int index = 0; - int sepPos = 0; - lineParts = 0; - while (sepPos > -1 && index < inputString.length()) { // as long as a blank was found an not at end of line ... - while (inputString.charAt(index) == ' ' // if next char is another blank - && lineParts < MAXLINEPARTS - && index < inputString.length()) index++; // skip more blanks - if (index < inputString.length()) { - sepPos = inputString.indexOf(' ', index+1); // find next blank after the word? - if (sepPos == -1) - linePart[lineParts] = inputString.substring(index); // no more blanks -> take rest of string - else - linePart[lineParts] = inputString.substring(index, sepPos); // more blanks -> take word nefore next blank and go on - index = sepPos + 1; // continue looking after last blank - lineParts ++; - } - } - for (int i=lineParts; i= 0) { // -1 means pin is not active for reporting - newCount = counter[pcintPin]; // get current counter - countDiff = newCount - lastCount[pcintPin]; // how many impulses since last report? - if (countDiff == 0 && - (now - lastReport[pcintPin] < intervalMax)) // if nothing to report, take next pin - continue; - if (countDiff > 0) { // if there was an impulse, report - timeDiff = lastTime[pcintPin] - startTime[pcintPin]; // time between first and last impulse during interval - lastCount[pcintPin] = newCount; // remember current count for next interval - startTime[pcintPin] = lastTime[pcintPin]; // time of last impulse in this interval becomes also time of first impulse in next interval - } else { - timeDiff = now - startTime[pcintPin]; // there was no impulse, but maxInterval is over: show from last impulse to now - startTime[pcintPin] = now; // start a new interval for next report - last one will be reported as 0 imulses ... - lastTime[pcintPin] = now; // also time of first impulse in next interval - } - Serial.println((String) "R" + aPin + // report on serial out - " C" + newCount + - " D" + countDiff + - " T" + timeDiff); - lastReport[pcintPin] = now; // remember when we reported - } - } -} - - -/* give status report in between if requested over serial input */ -void showCmd() { - unsigned long newCount; - unsigned long countDiff; - unsigned long timeDiff; - char* pName; - - Serial.println((String) version); - Serial.println((String) "Min " + intervalMin); - Serial.println((String) "Max " + intervalMax); - - for (int i=0; i<24; i++) { - int aPin = PCintActivePin[i]; - if (aPin != -1) { - timeDiff = lastTime[i] - startTime[i]; - newCount = counter[i]; - countDiff = newCount - lastCount[i]; - if (!timeDiff) - timeDiff = millis() - startTime[i]; - Serial.println((String) "PCInt " + i + " aPin " + aPin - + " Cnt " + newCount + " (+" + countDiff - + " ) in " + timeDiff + " Millis"); - } - } - Serial.println((String) "Next in " + (timeNextReport - millis())); -} - - -void addCmd() { - String pinArg = linePart[1]; // given arduino pin number or name as string - String modeArg = linePart[2]; // mode falling, rising or change - String pullArg = linePart[3]; // optional pullup - int aPin = -1; - int mode = RISING; // default - - if (pinArg.charAt(0) == 'D' || pinArg.charAt(0) == 'd') - aPin = pinArg.substring(1).toInt(); // arduino pin name starting with a "d"? - if (aPin == -1) { - aPin = pinArg.toInt(); // interpret string as pin number - if (aPin >= 20 || aPin < 1) { - Serial.print(error); Serial.println(aPin); - return; - } - } - if (allowedPins[aPin] == 0) { - Serial.print(error); Serial.println(aPin); - return; - }; - if (lineParts > 2) { - if (modeArg.equalsIgnoreCase("f")) - mode = FALLING; - else if (modeArg.equalsIgnoreCase("c")) - mode = CHANGE; - else if (modeArg.equalsIgnoreCase("r")) - mode = RISING; - else { - Serial.print(error); Serial.println(modeArg); - return; - } - } - pinMode (aPin, INPUT); - if (lineParts > 3) { - if (pullArg.equalsIgnoreCase("p")) - digitalWrite (aPin, HIGH); // enable pullup resistor - else { - Serial.print(error); Serial.println(pullArg); - return; - } - } - - if (AddPin(aPin, mode) == 0) { // call AddPin with arduino pin number - Serial.print("added "); Serial.println(aPin); - } else { - Serial.print(error); Serial.println(pinArg); - } -} - - -void removeCmd() { - String pinArg = linePart[1]; // given arduino pin number or name as string in first part - int aPin = -1; - if (pinArg.charAt(0) == 'D' || pinArg.charAt(0) == 'd') - aPin = pinArg.substring(1).toInt(); // arduino pin name starting with a "d"? - if (aPin == -1) { - aPin = pinArg.toInt(); // interpret string as pin number - if (aPin >= 20 || aPin < 1) { - Serial.print(error); Serial.println(aPin); - return; - } - } - if (allowedPins[aPin] == 0) { - Serial.print(error); Serial.println(aPin); - return; - }; - if (RemovePin(aPin) == 0) { // call RemovePin with arduino pin number - Serial.println((String)"removed " + aPin); - } else { - Serial.print(error); Serial.println(pinArg); - } -} - - -void intervalCmd() { - String timeArgMin = linePart[1]; - String timeArgMax = linePart[2]; - int timeMin = timeArgMin.toInt(); - if (timeMin < 1 || timeMin > 3600) { - Serial.print(error); Serial.println(timeArgMin); - return; - } - int timeMax = timeArgMax.toInt(); - if (timeMax < 1 || timeMax > 3600 || timeMax < timeMin) { - Serial.print(error); Serial.println(timeArgMax); - return; - } - intervalMin = (long)timeMin * 1000; - intervalMax = (long)timeMax * 1000; - if (millis() + intervalMin < timeNextReport) - timeNextReport = millis() + intervalMin; -} - - -void doCommand() { - SplitLine(); - if (linePart[0].equals("show")) { - showCmd(); - } else if (linePart[0].equals("add")) { - addCmd(); - } else if (linePart[0].equals("rem")) { - removeCmd(); - } else if (linePart[0].equals("int")) { - intervalCmd(); - } else { - Serial.print(error); Serial.println(linePart[0]); - } -} - - -void setup() { - unsigned long now = millis(); - for (int pcintPin=0; pcintPin < 24; pcintPin++) { - PCintActivePin[pcintPin] = -1; // set all pins to inactive (-1) - } - timeNextReport = millis() + intervalMin; // time for first output - Serial.begin(9600); // initialize serial - inputString.reserve(200); // reserve 200 bytes for the inputString - interrupts(); - Serial.println((String) version + " Setup done."); -} - - - -void loop() { - now = millis(); - doReport = false; // check if report nedds to be called - if((long)(now - timeNextReport) >= 0) - doReport = true; // intervalMin is over - else - for (byte pcintPin=0; pcintPin<24; pcintPin++) - if (PCintActivePin[pcintPin] >= 0) - if((long)(now - (lastReport[pcintPin] + intervalMax)) >= 0) - doReport = true; // active pin has not been reported for langer than maxInterval - if (doReport) { - report(); - timeNextReport = now + intervalMin; // do it again after interval millis - } - if (newCommand) { - doCommand(); - inputString = ""; - newCommand = false; - } -} - - -/* - SerialEvent occurs whenever a new data comes in the - hardware serial RX. This routine is run between each - time loop() runs, so using delay inside loop can delay - response. Multiple bytes of data may be available. - */ -void serialEvent() { - while (Serial.available()) { - char inChar = (char)Serial.read(); - if (inChar == '\n' or inChar == '\r') { - if (inputString.length() > 0) - newCommand = true; - } else { - inputString += inChar; - } - } -} - - diff --git a/fhem/contrib/arduino/ArduCounter1.8.ino b/fhem/contrib/arduino/ArduCounter1.8.ino new file mode 100755 index 000000000..3563a1b48 --- /dev/null +++ b/fhem/contrib/arduino/ArduCounter1.8.ino @@ -0,0 +1,804 @@ +/* + * Sketch for counting impulses in a defined interval + * e.g. for power meters with an s0 interface that can be + * connected to an input of an arduino board + * + * the sketch uses pin change interrupts which can be anabled + * for any of the inputs on e.g. an arduino uno or a jeenode + * + * the pin change Interrupt handling used here + * is based on the arduino playground example on PCINT: + * http://playground.arduino.cc/Main/PcInt + * + * Refer to avr-gcc header files, arduino source and atmega datasheet. + */ + +/* Pin to interrupt map: + * D0-D7 = PCINT 16-23 = PCIR2 = PD = PCIE2 = pcmsk2 + * D8-D13 = PCINT 0-5 = PCIR0 = PB = PCIE0 = pcmsk0 + * A0-A5 (D14-D19) = PCINT 8-13 = PCIR1 = PC = PCIE1 = pcmsk1 + */ + +/* + Changes: + V1.2 + 27.10.16 - use noInterrupts in report() + - avoid reporting very short timeDiff in case of very slow impulses after a report + - now reporting is delayed if impulses happened only within in intervalSml + - reporting is also delayed if less than countMin pulses counted + - extend command "int" for optional intervalSml and countMin + 29.10.16 - allow interval Min >= Max or Sml > Min + which changes behavior to take fixed calculation interval instead of timeDiff between pulses + -> if intervalMin = intervalMax, counting will allways follow the reporting interval + 3.11.16 - more noInterrupt blocks when accessing the non byte volatiles in report + V1.3 + 4.11.16 - check min pulse width and add more output, + - prefix show output with M + V1.4 + 10.11.16 - restructure add Cmd + - change syntax for specifying minPulseLengh + - res (reset) command + V1.6 + 13.12.16 - new startup message logic?, newline before first communication? + 18.12.16 - replace all code containing Strings, new communication syntax and parsing from Jeelink code + V1.7 + 2.1.17 - change message syntax again, report time as well, first and last impulse are reported relative to start of intervall + not start of reporting intervall + V1.8 + 4.1.17 - fixed a missing break in the case statement for pin definition + 5.1.17 - cleanup debug logging + + ToDo / Ideas: + + new index scheme to save memory: + array to map from pcintPin to new index, limit allowed pins. + unused pcintpins point to -1 and vomment states arduino pin number + insread of allowedPins array use new function from aPin to pcintPin + and then look up in new array for index or -1 +*/ + +#include "pins_arduino.h" + +const char versionStr[] PROGMEM = "ArduCounter V1.8"; +const char errorStr[] PROGMEM = "Error: "; + +#define enablePulseLenChecking 1 + +#define SERIAL_SPEED 38400 +#define MAX_ARDUINO_PIN 24 +#define MAX_PCINT_PIN 24 +#define MAX_INPUT_NUM 8 + +/* arduino pins that are typically ok to use + * (some are left out because they are used + * as reset, serial, led or other things on most boards) */ +byte allowedPins[MAX_ARDUINO_PIN] = + { 0, 0, 0, 3, 4, 5, 6, 7, + 0, 9, 10, 11, 12, 0, + 14, 15, 16, 17, 0, 0}; + + +/* Pin change mask for each chip port */ +volatile uint8_t *port_to_pcmask[] = { + &PCMSK0, + &PCMSK1, + &PCMSK2 +}; + +/* last PIN States to detect individual pin changes in ISR */ +volatile static uint8_t PCintLast[3]; + +unsigned long intervalMin = 30000; // default 30 sec - report after this time if nothing else delays it +unsigned long intervalMax = 60000; // default 60 sec - report after this time if it didin't happen before +unsigned long intervalSml = 2000; // default 2 secs - continue count if timeDiff is less and intervalMax not over +unsigned int countMin = 1; // continue counting if count is less than this and intervalMax not over + +unsigned long timeNextReport; + +/* index to the following arrays is the internal PCINT pin number, not the arduino + * pin number because the PCINT pin number corresponds to the physical ports + * and this saves time for mapping to the arduino numbers + */ + +/* pin change mode (RISING etc.) as parameter for ISR */ +byte PCintMode[MAX_PCINT_PIN]; +/* mode for timing pulse length - derived from PCintMode (RISING etc. */ +byte PulseMode[MAX_PCINT_PIN]; + +/* pin number for PCINT number if active - otherwise -1 */ +char PCintActivePin[MAX_PCINT_PIN]; + +/* did we get first interrupt yet? */ +volatile boolean initialized[MAX_PCINT_PIN]; + +/* individual counter for each real pin */ +volatile unsigned long counter[MAX_PCINT_PIN]; +/* count at last report to get difference */ +unsigned long lastCount[MAX_PCINT_PIN]; + +#ifdef enablePulseLenChecking +/* individual reject counter for each real pin */ +volatile unsigned int rejectCounter[MAX_PCINT_PIN]; +unsigned int lastRejCount[MAX_PCINT_PIN]; + +/* millis at last interrupt when signal was rising (for filtering with min pulse length) */ +volatile unsigned long lastPulseStart[MAX_PCINT_PIN]; + +/* millis at last interrupt when signal was falling (for filtering with min pulse length) */ +volatile unsigned long lastPulseEnd[MAX_PCINT_PIN]; + +/* minimal pulse length in millis */ +/* specified instead of rising or falling. isr needs to check change anyway */ +unsigned int pulseWidthMin[MAX_PCINT_PIN]; + +/* sum of pulse lengths for average output */ +volatile unsigned long pulseWidthSum[MAX_PCINT_PIN]; + +/* start of pulse for measuring length */ +byte pulseWidthStart[MAX_PCINT_PIN]; + +#endif + +/* millis at first interrupt for current calculation + * (is also last interrupt of old interval) */ +volatile unsigned long startTime[MAX_PCINT_PIN]; + +/* millis at last interrupt */ +volatile unsigned long lastTime[MAX_PCINT_PIN]; + +/* millis at first interrupt in a reporting cycle */ +volatile unsigned long startTimeRepInt[MAX_PCINT_PIN]; + + +/* millis at last report + * to find out when maxInterval is over + * and report has to be done even if + * no impulses were counted */ +unsigned long lastReport[MAX_PCINT_PIN]; + +unsigned int commandData[MAX_INPUT_NUM]; +byte commandDataPointer = 0; + + +int digitalPinToPcIntPin(uint8_t aPin) { + uint8_t pcintPin; // PCINT pin number for the pin to be added (index for most arrays) + uint8_t port = digitalPinToPort(aPin) - 2; // port that this arduno pin belongs to for enabling interrupts + + if (port == 1) { // now calculate the PCINT pin number that corresponds to the arduino pin number + pcintPin = aPin - 6; // port 1: PC0-PC5 (A0-A5 or D14-D19) is PCINT 8-13 (PC6 is reset) + } else { // arduino numbering continues at D14 since PB6/PB7 are used for other things + pcintPin = port * 8 + (aPin % 8); // port 0: PB0-PB5 (D8-D13) is PCINT 0-5 (PB6/PB7 is crystal) + } // port 2: PD0-PD7 (D0-D7) is PCINT 16-23 + return pcintPin; +} + + +/* Add a pin to be handled */ +byte AddPinChangeInterrupt(uint8_t aPin) { + uint8_t pcintPin; // PCINT pin number for the pin to be added (used as index for most arrays) + volatile uint8_t *pcmask; // pointer to PCMSK0 or 1 or 2 depending on the port corresponding to the pin + + uint8_t bit = digitalPinToBitMask(aPin); // bit in PCMSK to enable pin change interrupt for this arduino pin + uint8_t port = digitalPinToPort(aPin); // port that this arduno pin belongs to for enabling interrupts + + if (port == NOT_A_PORT) + return 0; + + port -= 2; + pcmask = port_to_pcmask[port]; // point to PCMSK0 or 1 or 2 depending on the port corresponding to the pin + *pcmask |= bit; // set the pin change interrupt mask through a pointer to PCMSK0 or 1 or 2 + PCICR |= 0x01 << port; // enable the interrupt + return 1; +} + + +/* Remove a pin to be handled */ +byte RemovePinChangeInterrupt(uint8_t aPin) { + uint8_t pcintPin; + volatile uint8_t *pcmask; + + uint8_t bit = digitalPinToBitMask(aPin); + uint8_t port = digitalPinToPort(aPin); + + if (port == NOT_A_PORT) + return 0; + + port -= 2; + pcmask = port_to_pcmask[port]; + *pcmask &= ~bit; // disable the mask. + if (*pcmask == 0) { // if that's the last one, disable the interrupt. + PCICR &= ~(0x01 << port); + } + return 1; +} + + + +void PrintErrorMsg() { + int len = strlen_P(errorStr); + char myChar; + for (unsigned char k = 0; k < len; k++) { + myChar = pgm_read_byte_near(errorStr + k); + Serial.print(myChar); + } +} + +void printVersion() { + int len = strlen_P(versionStr); + char myChar; + for (unsigned char k = 0; k < len; k++) { + myChar = pgm_read_byte_near(versionStr + k); + Serial.print(myChar); + } +} + + +/* + common interrupt handler. "port" is the PCINT port number (0-2) + + do counting and set start / end time of interval. + reporting is not triggered from here. + + only here counter[] is modified + lastTime[] is set here and in report + startTime[] is set in case a pin was not initialized yet and in report +*/ +static void PCint(uint8_t port) { + uint8_t bit; + uint8_t curr; + uint8_t mask; + uint8_t pcintPin; + unsigned long now = millis(); +#ifdef enablePulseLenChecking + unsigned long len, gap; +#endif + // get the pin states for the indicated port. + curr = *portInputRegister(port+2); // current pin states at port + mask = curr ^ PCintLast[port]; // xor gets bits that are different + PCintLast[port] = curr; // store new pin state for next interrupt + + if ((mask &= *port_to_pcmask[port]) == 0) // mask is pins that have changed. screen out non pcint pins. + return; /* no handled pin changed */ + + for (uint8_t i=0; i < 8; i++) { + bit = 0x01 << i; // loop over each pin that changed + if (bit & mask) { // did this pin change? + pcintPin = port * 8 + i; // pcint pin numbers follow the bits, only arduino pin nums are special + + // count if mode is CHANGE, or if RISING and bit is high, or if mode is FALLING and bit is low. + if ((PCintMode[pcintPin] == CHANGE + || ((PCintMode[pcintPin] == RISING) && (curr & bit)) + || ((PCintMode[pcintPin] == FALLING) && !(curr & bit)))) { +#ifdef enablePulseLenChecking + if (pulseWidthMin[pcintPin]) { // check minimal pulse length and gap + if ( ( (curr & bit) && pulseWidthStart[pcintPin] == RISING) + || (!(curr & bit) && pulseWidthStart[pcintPin] == FALLING)) { // edge does fit defined start + lastPulseStart[pcintPin] = now; + continue; + } else { // End of defined pulse + gap = lastPulseStart[pcintPin] - lastPulseEnd[pcintPin]; + len = now - lastPulseStart[pcintPin]; + lastPulseEnd[pcintPin] = now; + if (len < pulseWidthMin[pcintPin] || gap < pulseWidthMin[pcintPin]) { + rejectCounter[pcintPin]++; // pulse too short + continue; + } + pulseWidthSum[pcintPin] += len; // for average calculation + } + } +#endif + lastTime[pcintPin] = now; // remember time of in case pulse will be the last in the interval + if (!startTimeRepInt[pcintPin]) startTimeRepInt[pcintPin] = now; // time of first impulse in this reporting interval + if (initialized[pcintPin]) { + counter[pcintPin]++; // count + } else { + startTime[pcintPin] = lastTime[pcintPin]; // if this is the very first impulse on this pin -> start interval now + initialized[pcintPin] = true; // and start counting the next impulse (so far counter is 0) + } + } + } + } +} + + +/* + report count and time for pins that are between min and max interval + + lastCount[] is only modified here (count at time of last reporting) + lastTime[] is modified here and in ISR - disable interrupts in critcal moments to avoid garbage in var + startTime[] is modified only here (or for very first Interrupt in ISR) -> no problem. +*/ +void report() { + int aPin; + unsigned long count, countDiff; + unsigned long timeDiff, now; + unsigned long startT, endT; + unsigned long avgLen; + now = millis(); + for (int pcintPin=0; pcintPin < MAX_PCINT_PIN; pcintPin++) { // go through all observed pins as PCINT pin number + aPin = PCintActivePin[pcintPin]; // take saved arduino pin number + if (aPin < 0) continue; // -1 means pin is not active for reporting + noInterrupts(); + startT = startTime[pcintPin]; + endT = lastTime[pcintPin]; + count = counter[pcintPin]; // get current counter + interrupts(); + + timeDiff = endT - startT; // time between first and last impulse during interval + countDiff = count - lastCount[pcintPin]; // how many impulses since last report? (works with wrapping) + + if((long)(now - (lastReport[pcintPin] + intervalMax)) >= 0) { // intervalMax is over + if ((countDiff >= countMin) && (timeDiff > intervalSml) && (intervalMin != intervalMax)) { + // normal procedure + lastCount[pcintPin] = count; // remember current count for next interval + noInterrupts(); + startTime[pcintPin] = endT; // time of last impulse in this interval becomes also time of first impulse in next + interrupts(); + } else { + // nothing counted or counts happened during a fraction of intervalMin only + noInterrupts(); + lastTime[pcintPin] = now; // don't calculate with last impulse, use now instead + startTime[pcintPin] = now; // start a new interval for next report now + interrupts(); + lastCount[pcintPin] = count; // remember current count for next interval + timeDiff = now - startT; // special handling - calculation ends now instead of last impulse + } + } else if((long)(now - (lastReport[pcintPin] + intervalMin)) >= 0) { // minInterval has elapsed + if ((countDiff >= countMin) && (timeDiff > intervalSml)) { + // normal procedure + lastCount[pcintPin] = count; // remember current count for next interval + noInterrupts(); + startTime[pcintPin] = endT; // time of last impulse in this interval becomes also time of first impulse in next + interrupts(); + } else continue; // not enough counted - wait + } else continue; // intervalMin not over - wait + + Serial.print(F("R")); // R Report + Serial.print(aPin); + Serial.print(F(" C")); // C - Count + Serial.print(count); + Serial.print(F(" D")); // D - Count Diff + Serial.print(countDiff); + Serial.print(F(" T")); // T - Time + Serial.print(timeDiff); + Serial.print(F(" N")); // N - now + Serial.print((long)now); + +#ifdef enablePulseLenChecking + // rejected count ausgeben + // evt auch noch average pulse len und gap len + if (pulseWidthMin[pcintPin]) { // check minimal pulse length and gap + Serial.print(F(" X")); // X Reject + Serial.print(rejectCounter[pcintPin] - lastRejCount[pcintPin]); + noInterrupts(); + lastRejCount[pcintPin] = rejectCounter[pcintPin]; + interrupts(); + } +#endif + + if (countDiff) { + Serial.print(F(" F")); // F - first impulse after the one that started the interval + Serial.print((long)startTimeRepInt[pcintPin] - startT); + Serial.print(F(" L")); // L - last impulse - marking the end of this interval + Serial.print((long)endT - startT); + startTimeRepInt[pcintPin] = 0; + +#ifdef enablePulseLenChecking + if (pulseWidthMin[pcintPin]) {// check minimal pulse length and gap + noInterrupts(); + avgLen = pulseWidthSum[pcintPin] / countDiff; + pulseWidthSum[pcintPin] = 0; + interrupts(); + Serial.print(F(" A")); + Serial.print(avgLen); + } +#endif + } + Serial.println(); + lastReport[pcintPin] = now; // remember when we reported + } +} + + +/* print status for one pin */ +void showPin(byte pcintPin) { + unsigned long newCount; + unsigned long countDiff; + unsigned long timeDiff; + unsigned long avgLen; + + timeDiff = lastTime[pcintPin] - startTime[pcintPin]; + newCount = counter[pcintPin]; + countDiff = newCount - lastCount[pcintPin]; + if (!timeDiff) + timeDiff = millis() - startTime[pcintPin]; + + Serial.print(F("PCInt pin ")); + Serial.print(pcintPin); + + Serial.print(F(", iMode ")); + switch (PCintMode[pcintPin]) { + case RISING: Serial.print(F("rising")); break; + case FALLING: Serial.print(F("falling")); break; + case CHANGE: Serial.print(F("change")); break; + } +#ifdef enablePulseLenChecking + if (pulseWidthMin[pcintPin] > 0) { + Serial.print(F(", min len ")); + Serial.print(pulseWidthMin[pcintPin]); + Serial.print(F(" ms")); + switch (pulseWidthStart[pcintPin]) { + case RISING: Serial.print(F(" rising")); break; + case FALLING: Serial.print(F(" falling")); break; + } + } else { + Serial.print(F(", no min len")); + } +#endif + Serial.print(F(", count ")); + Serial.print(newCount); + Serial.print(F(" (+")); + Serial.print(countDiff); + Serial.print(F(") in ")); + Serial.print(timeDiff); + Serial.print(F(" ms")); +#ifdef enablePulseLenChecking + // rejected count ausgeben + // evt auch noch average pulse len und gap len + if (pulseWidthMin[pcintPin]) { // check minimal pulse length and gap + Serial.print(F(" Rej ")); + Serial.print(rejectCounter[pcintPin] - lastRejCount[pcintPin]); + } +#endif + if (countDiff) { + Serial.println(); + Serial.print(F("M first at ")); + Serial.print((long)startTimeRepInt[pcintPin] - lastReport[pcintPin]); + Serial.print(F(", last at ")); + Serial.print((long)lastTime[pcintPin] - lastReport[pcintPin]); +#ifdef enablePulseLenChecking + noInterrupts(); + avgLen = pulseWidthSum[pcintPin] / countDiff; + interrupts(); + Serial.print(F(", avg len ")); + Serial.print(avgLen); +#endif + } +} + + +/* give status report in between if requested over serial input */ +void showCmd() { + unsigned long newCount; + unsigned long countDiff; + unsigned long timeDiff; + unsigned long avgLen; + char myChar; + + Serial.print(F("M Status: ")); + printVersion(); + Serial.println(); + Serial.print(F("M normal interval ")); + Serial.println(intervalMin); + Serial.print(F("M max interval ")); + Serial.println(intervalMax); + Serial.print(F("M min interval ")); + Serial.println(intervalSml); + Serial.print(F("M min count ")); + Serial.println(countMin); + + for (byte pcintPin=0; pcintPin < MAX_PCINT_PIN; pcintPin++) { + int aPin = PCintActivePin[pcintPin]; + if (aPin != -1) { + timeDiff = lastTime[pcintPin] - startTime[pcintPin]; + newCount = counter[pcintPin]; + countDiff = newCount - lastCount[pcintPin]; + if (!timeDiff) + timeDiff = millis() - startTime[pcintPin]; + Serial.print(F("M pin ")); + Serial.print(aPin); + Serial.print(F(" ")); + showPin(pcintPin); + Serial.println(); + } + } + Serial.print(F("M Next report in ")); + Serial.print(timeNextReport - millis()); + Serial.print(F(" Milliseconds")); + Serial.println(); +} + + +/* + handle add command. +*/ +void addCmd(unsigned int *values, byte size) { + uint8_t pcintPin; // PCINT pin number for the pin to be added (used as index for most arrays) + byte mode; + unsigned int pw; + unsigned long now = millis(); + + //Serial.println(F("M Add called")); + int aPin = values[0]; + pcintPin = digitalPinToPcIntPin(aPin); + if (aPin >= MAX_ARDUINO_PIN || aPin < 1 + || allowedPins[aPin] == 0 || pcintPin > MAX_PCINT_PIN) { + PrintErrorMsg(); + Serial.print(F("Illegal pin specification ")); + Serial.println(aPin); + return; + }; + + switch (values[1]) { + case 2: + mode = FALLING; + pulseWidthStart[pcintPin] = FALLING; + break; + case 3: + mode = RISING; + pulseWidthStart[pcintPin] = RISING; + break; + case 1: + mode = CHANGE; + break; + default: + PrintErrorMsg(); + Serial.print(F("Illegal pin specification ")); + Serial.println(aPin); + } + + pinMode (aPin, INPUT); + if (values[2]) { + digitalWrite (aPin, HIGH); // enable pullup resistor + } + +#ifdef enablePulseLenChecking + PulseMode[pcintPin] = mode; // specified mode also defines pulse level in this case + if (values[3] > 0) { + pw = values[3]; + mode = CHANGE; + } else { + pw = 0; + } +#endif + + if (!AddPinChangeInterrupt(aPin)) { // add Pin Change Interrupt + PrintErrorMsg(); Serial.println(F("AddInt")); + return; + } + PCintMode[pcintPin] = mode; // save mode for ISR which uses the pcintPin as index + +#ifdef enablePulseLenChecking + pulseWidthMin[pcintPin] = pw; // minimal pulse width in millis, 3 if not specified n add cmd +#endif + + if (PCintActivePin[pcintPin] != aPin) { // in case this pin is already active counting + PCintActivePin[pcintPin] = aPin; // save real arduino pin number and flag this pin as active for reporting + initialized[pcintPin] = false; // initialize arrays for this pin + counter[pcintPin] = 0; + lastCount[pcintPin] = 0; + startTime[pcintPin] = now; + lastTime[pcintPin] = now; + lastReport[pcintPin] = now; + } + Serial.print(F("M defined pin ")); + Serial.print(aPin); + Serial.print(F(" ")); + showPin(pcintPin); + Serial.println(); + +} + + +/* + handle rem command. +*/ +void removeCmd(unsigned int *values, byte size) { + uint8_t pcintPin; // PCINT pin number for the pin to be added (used as index for most arrays) + int aPin = values[0]; + pcintPin = digitalPinToPcIntPin(aPin); + if (aPin >= MAX_ARDUINO_PIN || aPin < 1 + || allowedPins[aPin] == 0 || pcintPin > MAX_PCINT_PIN) { + PrintErrorMsg(); + Serial.print(F("Illegal pin specification ")); + Serial.println(aPin); + return; + }; + + if (!RemovePinChangeInterrupt(aPin)) { + PrintErrorMsg(); Serial.println(F("RemInt")); + return; + } + + PCintActivePin[pcintPin] = -1; + initialized[pcintPin] = false; // reset for next add + counter[pcintPin] = 0; + lastCount[pcintPin] = 0; +#ifdef enablePulseLenChecking + pulseWidthMin[pcintPin] = 0; + lastRejCount[pcintPin] = 0; + rejectCounter[pcintPin] = 0; +#endif + + Serial.print(F("M removed ")); + Serial.println(aPin); +} + + + +void intervalCmd(unsigned int *values, byte size) { + if (size < 4) { + PrintErrorMsg(); + Serial.print(F("size")); + Serial.println(); + return; + } + if (values[0] < 1 || values[0] > 3600) { + PrintErrorMsg(); Serial.println(values[0]); + return; + } + intervalMin = (long)values[0] * 1000; + if (millis() + intervalMin < timeNextReport) + timeNextReport = millis() + intervalMin; + + if (values[1] < 1 || values[1] > 3600) { + PrintErrorMsg(); Serial.println(values[1]); + return; + } + intervalMax = (long)values[1]* 1000; + + if (values[2] > 3600) { + PrintErrorMsg(); Serial.println(values[2]); + return; + } + if (values[2] > 0) { + intervalSml = (long)values[2] * 1000; + } + + if (values[3]> 0) { + countMin = values[3]; + } + Serial.print(F("M intervals set to ")); + Serial.print(values[0]); + Serial.print(F(" ")); + Serial.print(values[1]); + Serial.print(F(" ")); + Serial.print(values[2]); + Serial.print(F(" ")); + Serial.print(values[3]); + Serial.println(); +} + + +void helloCmd() { + Serial.println(); + printVersion(); + Serial.println(F("Hello")); +} + + +static void HandleSerialPort(char c) { + static unsigned int value; + + if (c == ',') { + if (commandDataPointer + 1 < MAX_INPUT_NUM) { + commandData[commandDataPointer++] = value; + value = 0; + } + } + else if ('0' <= c && c <= '9') { + value = 10 * value + c - '0'; + } + else if ('a' <= c && c <= 'z') { + switch (c) { + case 'a': + commandData[commandDataPointer] = value; + addCmd(commandData, ++commandDataPointer); + commandDataPointer = 0; + break; + + case 'd': + commandData[commandDataPointer] = value; + removeCmd(commandData, ++commandDataPointer); + commandDataPointer = 0; + break; + + case 'i': + commandData[commandDataPointer] = value; + intervalCmd(commandData, ++commandDataPointer); + commandDataPointer = 0; + break; + + case 'r': + setup(); + commandDataPointer = 0; + break; + + case 's': + showCmd(); + commandDataPointer = 0; + break; + + case 'h': + helloCmd(); + commandDataPointer = 0; + break; + + default: + commandDataPointer = 0; + //PrintErrorMsg(); Serial.println(); + break; + } + value = 0; + } +} + + + +SIGNAL(PCINT0_vect) { + PCint(0); +} +SIGNAL(PCINT1_vect) { + PCint(1); +} +SIGNAL(PCINT2_vect) { + PCint(2); +} + + +void setup() { + unsigned long now = millis(); + + for (int pcintPin=0; pcintPin < MAX_PCINT_PIN; pcintPin++) { + PCintActivePin[pcintPin] = -1; // set all pins to inactive (-1) + initialized[pcintPin] = false; // initialize arrays for this pin + counter[pcintPin] = 0; + lastCount[pcintPin] = 0; + startTime[pcintPin] = now; + lastTime[pcintPin] = now; +#ifdef enablePulseLenChecking + lastPulseStart[pcintPin] = now; + lastPulseEnd[pcintPin] = now; + pulseWidthMin[pcintPin] = 0; + rejectCounter[pcintPin] = 0; + lastRejCount[pcintPin] = 0; +#endif + lastReport[pcintPin] = now; + } + + timeNextReport = millis() + intervalMin; // time for first output + Serial.begin(SERIAL_SPEED); // initialize serial + delay (500); + interrupts(); + Serial.println(); + printVersion(); + Serial.println(F("Started")); +} + + +/* + Main Loop + checks if report should be called because timeNextReport is reached + or lastReport for one pin is older than intervalMax + timeNextReport is only set here (and when interval is changed / at setup) +*/ +void loop() { + unsigned long now = millis(); + + if (Serial.available()) { + HandleSerialPort(Serial.read()); + } + boolean doReport = false; // check if report nedds to be called + if((long)(now - timeNextReport) >= 0) // works fine when millis wraps. + doReport = true; // intervalMin is over + else + for (byte pcintPin=0; pcintPin < MAX_PCINT_PIN; pcintPin++) + if (PCintActivePin[pcintPin] >= 0) + if((long)(now - (lastReport[pcintPin] + intervalMax)) >= 0) + doReport = true; // active pin has not been reported for langer than intervalMax + if (doReport) { + report(); + timeNextReport = now + intervalMin; // do it again after intervalMin millis + } +} +