ArduCounter1.8.ino 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805
  1. /*
  2. * Sketch for counting impulses in a defined interval
  3. * e.g. for power meters with an s0 interface that can be
  4. * connected to an input of an arduino board
  5. *
  6. * the sketch uses pin change interrupts which can be anabled
  7. * for any of the inputs on e.g. an arduino uno or a jeenode
  8. *
  9. * the pin change Interrupt handling used here
  10. * is based on the arduino playground example on PCINT:
  11. * http://playground.arduino.cc/Main/PcInt
  12. *
  13. * Refer to avr-gcc header files, arduino source and atmega datasheet.
  14. */
  15. /* Pin to interrupt map:
  16. * D0-D7 = PCINT 16-23 = PCIR2 = PD = PCIE2 = pcmsk2
  17. * D8-D13 = PCINT 0-5 = PCIR0 = PB = PCIE0 = pcmsk0
  18. * A0-A5 (D14-D19) = PCINT 8-13 = PCIR1 = PC = PCIE1 = pcmsk1
  19. */
  20. /*
  21. Changes:
  22. V1.2
  23. 27.10.16 - use noInterrupts in report()
  24. - avoid reporting very short timeDiff in case of very slow impulses after a report
  25. - now reporting is delayed if impulses happened only within in intervalSml
  26. - reporting is also delayed if less than countMin pulses counted
  27. - extend command "int" for optional intervalSml and countMin
  28. 29.10.16 - allow interval Min >= Max or Sml > Min
  29. which changes behavior to take fixed calculation interval instead of timeDiff between pulses
  30. -> if intervalMin = intervalMax, counting will allways follow the reporting interval
  31. 3.11.16 - more noInterrupt blocks when accessing the non byte volatiles in report
  32. V1.3
  33. 4.11.16 - check min pulse width and add more output,
  34. - prefix show output with M
  35. V1.4
  36. 10.11.16 - restructure add Cmd
  37. - change syntax for specifying minPulseLengh
  38. - res (reset) command
  39. V1.6
  40. 13.12.16 - new startup message logic?, newline before first communication?
  41. 18.12.16 - replace all code containing Strings, new communication syntax and parsing from Jeelink code
  42. V1.7
  43. 2.1.17 - change message syntax again, report time as well, first and last impulse are reported relative to start of intervall
  44. not start of reporting intervall
  45. V1.8
  46. 4.1.17 - fixed a missing break in the case statement for pin definition
  47. 5.1.17 - cleanup debug logging
  48. ToDo / Ideas:
  49. new index scheme to save memory:
  50. array to map from pcintPin to new index, limit allowed pins.
  51. unused pcintpins point to -1 and vomment states arduino pin number
  52. insread of allowedPins array use new function from aPin to pcintPin
  53. and then look up in new array for index or -1
  54. */
  55. #include "pins_arduino.h"
  56. const char versionStr[] PROGMEM = "ArduCounter V1.8";
  57. const char errorStr[] PROGMEM = "Error: ";
  58. #define enablePulseLenChecking 1
  59. #define SERIAL_SPEED 38400
  60. #define MAX_ARDUINO_PIN 24
  61. #define MAX_PCINT_PIN 24
  62. #define MAX_INPUT_NUM 8
  63. /* arduino pins that are typically ok to use
  64. * (some are left out because they are used
  65. * as reset, serial, led or other things on most boards) */
  66. byte allowedPins[MAX_ARDUINO_PIN] =
  67. { 0, 0, 0, 3, 4, 5, 6, 7,
  68. 0, 9, 10, 11, 12, 0,
  69. 14, 15, 16, 17, 0, 0};
  70. /* Pin change mask for each chip port */
  71. volatile uint8_t *port_to_pcmask[] = {
  72. &PCMSK0,
  73. &PCMSK1,
  74. &PCMSK2
  75. };
  76. /* last PIN States to detect individual pin changes in ISR */
  77. volatile static uint8_t PCintLast[3];
  78. unsigned long intervalMin = 30000; // default 30 sec - report after this time if nothing else delays it
  79. unsigned long intervalMax = 60000; // default 60 sec - report after this time if it didin't happen before
  80. unsigned long intervalSml = 2000; // default 2 secs - continue count if timeDiff is less and intervalMax not over
  81. unsigned int countMin = 1; // continue counting if count is less than this and intervalMax not over
  82. unsigned long timeNextReport;
  83. /* index to the following arrays is the internal PCINT pin number, not the arduino
  84. * pin number because the PCINT pin number corresponds to the physical ports
  85. * and this saves time for mapping to the arduino numbers
  86. */
  87. /* pin change mode (RISING etc.) as parameter for ISR */
  88. byte PCintMode[MAX_PCINT_PIN];
  89. /* mode for timing pulse length - derived from PCintMode (RISING etc. */
  90. byte PulseMode[MAX_PCINT_PIN];
  91. /* pin number for PCINT number if active - otherwise -1 */
  92. char PCintActivePin[MAX_PCINT_PIN];
  93. /* did we get first interrupt yet? */
  94. volatile boolean initialized[MAX_PCINT_PIN];
  95. /* individual counter for each real pin */
  96. volatile unsigned long counter[MAX_PCINT_PIN];
  97. /* count at last report to get difference */
  98. unsigned long lastCount[MAX_PCINT_PIN];
  99. #ifdef enablePulseLenChecking
  100. /* individual reject counter for each real pin */
  101. volatile unsigned int rejectCounter[MAX_PCINT_PIN];
  102. unsigned int lastRejCount[MAX_PCINT_PIN];
  103. /* millis at last interrupt when signal was rising (for filtering with min pulse length) */
  104. volatile unsigned long lastPulseStart[MAX_PCINT_PIN];
  105. /* millis at last interrupt when signal was falling (for filtering with min pulse length) */
  106. volatile unsigned long lastPulseEnd[MAX_PCINT_PIN];
  107. /* minimal pulse length in millis */
  108. /* specified instead of rising or falling. isr needs to check change anyway */
  109. unsigned int pulseWidthMin[MAX_PCINT_PIN];
  110. /* sum of pulse lengths for average output */
  111. volatile unsigned long pulseWidthSum[MAX_PCINT_PIN];
  112. /* start of pulse for measuring length */
  113. byte pulseWidthStart[MAX_PCINT_PIN];
  114. #endif
  115. /* millis at first interrupt for current calculation
  116. * (is also last interrupt of old interval) */
  117. volatile unsigned long startTime[MAX_PCINT_PIN];
  118. /* millis at last interrupt */
  119. volatile unsigned long lastTime[MAX_PCINT_PIN];
  120. /* millis at first interrupt in a reporting cycle */
  121. volatile unsigned long startTimeRepInt[MAX_PCINT_PIN];
  122. /* millis at last report
  123. * to find out when maxInterval is over
  124. * and report has to be done even if
  125. * no impulses were counted */
  126. unsigned long lastReport[MAX_PCINT_PIN];
  127. unsigned int commandData[MAX_INPUT_NUM];
  128. byte commandDataPointer = 0;
  129. int digitalPinToPcIntPin(uint8_t aPin) {
  130. uint8_t pcintPin; // PCINT pin number for the pin to be added (index for most arrays)
  131. uint8_t port = digitalPinToPort(aPin) - 2; // port that this arduno pin belongs to for enabling interrupts
  132. if (port == 1) { // now calculate the PCINT pin number that corresponds to the arduino pin number
  133. pcintPin = aPin - 6; // port 1: PC0-PC5 (A0-A5 or D14-D19) is PCINT 8-13 (PC6 is reset)
  134. } else { // arduino numbering continues at D14 since PB6/PB7 are used for other things
  135. pcintPin = port * 8 + (aPin % 8); // port 0: PB0-PB5 (D8-D13) is PCINT 0-5 (PB6/PB7 is crystal)
  136. } // port 2: PD0-PD7 (D0-D7) is PCINT 16-23
  137. return pcintPin;
  138. }
  139. /* Add a pin to be handled */
  140. byte AddPinChangeInterrupt(uint8_t aPin) {
  141. uint8_t pcintPin; // PCINT pin number for the pin to be added (used as index for most arrays)
  142. volatile uint8_t *pcmask; // pointer to PCMSK0 or 1 or 2 depending on the port corresponding to the pin
  143. uint8_t bit = digitalPinToBitMask(aPin); // bit in PCMSK to enable pin change interrupt for this arduino pin
  144. uint8_t port = digitalPinToPort(aPin); // port that this arduno pin belongs to for enabling interrupts
  145. if (port == NOT_A_PORT)
  146. return 0;
  147. port -= 2;
  148. pcmask = port_to_pcmask[port]; // point to PCMSK0 or 1 or 2 depending on the port corresponding to the pin
  149. *pcmask |= bit; // set the pin change interrupt mask through a pointer to PCMSK0 or 1 or 2
  150. PCICR |= 0x01 << port; // enable the interrupt
  151. return 1;
  152. }
  153. /* Remove a pin to be handled */
  154. byte RemovePinChangeInterrupt(uint8_t aPin) {
  155. uint8_t pcintPin;
  156. volatile uint8_t *pcmask;
  157. uint8_t bit = digitalPinToBitMask(aPin);
  158. uint8_t port = digitalPinToPort(aPin);
  159. if (port == NOT_A_PORT)
  160. return 0;
  161. port -= 2;
  162. pcmask = port_to_pcmask[port];
  163. *pcmask &= ~bit; // disable the mask.
  164. if (*pcmask == 0) { // if that's the last one, disable the interrupt.
  165. PCICR &= ~(0x01 << port);
  166. }
  167. return 1;
  168. }
  169. void PrintErrorMsg() {
  170. int len = strlen_P(errorStr);
  171. char myChar;
  172. for (unsigned char k = 0; k < len; k++) {
  173. myChar = pgm_read_byte_near(errorStr + k);
  174. Serial.print(myChar);
  175. }
  176. }
  177. void printVersion() {
  178. int len = strlen_P(versionStr);
  179. char myChar;
  180. for (unsigned char k = 0; k < len; k++) {
  181. myChar = pgm_read_byte_near(versionStr + k);
  182. Serial.print(myChar);
  183. }
  184. }
  185. /*
  186. common interrupt handler. "port" is the PCINT port number (0-2)
  187. do counting and set start / end time of interval.
  188. reporting is not triggered from here.
  189. only here counter[] is modified
  190. lastTime[] is set here and in report
  191. startTime[] is set in case a pin was not initialized yet and in report
  192. */
  193. static void PCint(uint8_t port) {
  194. uint8_t bit;
  195. uint8_t curr;
  196. uint8_t mask;
  197. uint8_t pcintPin;
  198. unsigned long now = millis();
  199. #ifdef enablePulseLenChecking
  200. unsigned long len, gap;
  201. #endif
  202. // get the pin states for the indicated port.
  203. curr = *portInputRegister(port+2); // current pin states at port
  204. mask = curr ^ PCintLast[port]; // xor gets bits that are different
  205. PCintLast[port] = curr; // store new pin state for next interrupt
  206. if ((mask &= *port_to_pcmask[port]) == 0) // mask is pins that have changed. screen out non pcint pins.
  207. return; /* no handled pin changed */
  208. for (uint8_t i=0; i < 8; i++) {
  209. bit = 0x01 << i; // loop over each pin that changed
  210. if (bit & mask) { // did this pin change?
  211. pcintPin = port * 8 + i; // pcint pin numbers follow the bits, only arduino pin nums are special
  212. // count if mode is CHANGE, or if RISING and bit is high, or if mode is FALLING and bit is low.
  213. if ((PCintMode[pcintPin] == CHANGE
  214. || ((PCintMode[pcintPin] == RISING) && (curr & bit))
  215. || ((PCintMode[pcintPin] == FALLING) && !(curr & bit)))) {
  216. #ifdef enablePulseLenChecking
  217. if (pulseWidthMin[pcintPin]) { // check minimal pulse length and gap
  218. if ( ( (curr & bit) && pulseWidthStart[pcintPin] == RISING)
  219. || (!(curr & bit) && pulseWidthStart[pcintPin] == FALLING)) { // edge does fit defined start
  220. lastPulseStart[pcintPin] = now;
  221. continue;
  222. } else { // End of defined pulse
  223. gap = lastPulseStart[pcintPin] - lastPulseEnd[pcintPin];
  224. len = now - lastPulseStart[pcintPin];
  225. lastPulseEnd[pcintPin] = now;
  226. if (len < pulseWidthMin[pcintPin] || gap < pulseWidthMin[pcintPin]) {
  227. rejectCounter[pcintPin]++; // pulse too short
  228. continue;
  229. }
  230. pulseWidthSum[pcintPin] += len; // for average calculation
  231. }
  232. }
  233. #endif
  234. lastTime[pcintPin] = now; // remember time of in case pulse will be the last in the interval
  235. if (!startTimeRepInt[pcintPin]) startTimeRepInt[pcintPin] = now; // time of first impulse in this reporting interval
  236. if (initialized[pcintPin]) {
  237. counter[pcintPin]++; // count
  238. } else {
  239. startTime[pcintPin] = lastTime[pcintPin]; // if this is the very first impulse on this pin -> start interval now
  240. initialized[pcintPin] = true; // and start counting the next impulse (so far counter is 0)
  241. }
  242. }
  243. }
  244. }
  245. }
  246. /*
  247. report count and time for pins that are between min and max interval
  248. lastCount[] is only modified here (count at time of last reporting)
  249. lastTime[] is modified here and in ISR - disable interrupts in critcal moments to avoid garbage in var
  250. startTime[] is modified only here (or for very first Interrupt in ISR) -> no problem.
  251. */
  252. void report() {
  253. int aPin;
  254. unsigned long count, countDiff;
  255. unsigned long timeDiff, now;
  256. unsigned long startT, endT;
  257. unsigned long avgLen;
  258. now = millis();
  259. for (int pcintPin=0; pcintPin < MAX_PCINT_PIN; pcintPin++) { // go through all observed pins as PCINT pin number
  260. aPin = PCintActivePin[pcintPin]; // take saved arduino pin number
  261. if (aPin < 0) continue; // -1 means pin is not active for reporting
  262. noInterrupts();
  263. startT = startTime[pcintPin];
  264. endT = lastTime[pcintPin];
  265. count = counter[pcintPin]; // get current counter
  266. interrupts();
  267. timeDiff = endT - startT; // time between first and last impulse during interval
  268. countDiff = count - lastCount[pcintPin]; // how many impulses since last report? (works with wrapping)
  269. if((long)(now - (lastReport[pcintPin] + intervalMax)) >= 0) { // intervalMax is over
  270. if ((countDiff >= countMin) && (timeDiff > intervalSml) && (intervalMin != intervalMax)) {
  271. // normal procedure
  272. lastCount[pcintPin] = count; // remember current count for next interval
  273. noInterrupts();
  274. startTime[pcintPin] = endT; // time of last impulse in this interval becomes also time of first impulse in next
  275. interrupts();
  276. } else {
  277. // nothing counted or counts happened during a fraction of intervalMin only
  278. noInterrupts();
  279. lastTime[pcintPin] = now; // don't calculate with last impulse, use now instead
  280. startTime[pcintPin] = now; // start a new interval for next report now
  281. interrupts();
  282. lastCount[pcintPin] = count; // remember current count for next interval
  283. timeDiff = now - startT; // special handling - calculation ends now instead of last impulse
  284. }
  285. } else if((long)(now - (lastReport[pcintPin] + intervalMin)) >= 0) { // minInterval has elapsed
  286. if ((countDiff >= countMin) && (timeDiff > intervalSml)) {
  287. // normal procedure
  288. lastCount[pcintPin] = count; // remember current count for next interval
  289. noInterrupts();
  290. startTime[pcintPin] = endT; // time of last impulse in this interval becomes also time of first impulse in next
  291. interrupts();
  292. } else continue; // not enough counted - wait
  293. } else continue; // intervalMin not over - wait
  294. Serial.print(F("R")); // R Report
  295. Serial.print(aPin);
  296. Serial.print(F(" C")); // C - Count
  297. Serial.print(count);
  298. Serial.print(F(" D")); // D - Count Diff
  299. Serial.print(countDiff);
  300. Serial.print(F(" T")); // T - Time
  301. Serial.print(timeDiff);
  302. Serial.print(F(" N")); // N - now
  303. Serial.print((long)now);
  304. #ifdef enablePulseLenChecking
  305. // rejected count ausgeben
  306. // evt auch noch average pulse len und gap len
  307. if (pulseWidthMin[pcintPin]) { // check minimal pulse length and gap
  308. Serial.print(F(" X")); // X Reject
  309. Serial.print(rejectCounter[pcintPin] - lastRejCount[pcintPin]);
  310. noInterrupts();
  311. lastRejCount[pcintPin] = rejectCounter[pcintPin];
  312. interrupts();
  313. }
  314. #endif
  315. if (countDiff) {
  316. Serial.print(F(" F")); // F - first impulse after the one that started the interval
  317. Serial.print((long)startTimeRepInt[pcintPin] - startT);
  318. Serial.print(F(" L")); // L - last impulse - marking the end of this interval
  319. Serial.print((long)endT - startT);
  320. startTimeRepInt[pcintPin] = 0;
  321. #ifdef enablePulseLenChecking
  322. if (pulseWidthMin[pcintPin]) {// check minimal pulse length and gap
  323. noInterrupts();
  324. avgLen = pulseWidthSum[pcintPin] / countDiff;
  325. pulseWidthSum[pcintPin] = 0;
  326. interrupts();
  327. Serial.print(F(" A"));
  328. Serial.print(avgLen);
  329. }
  330. #endif
  331. }
  332. Serial.println();
  333. lastReport[pcintPin] = now; // remember when we reported
  334. }
  335. }
  336. /* print status for one pin */
  337. void showPin(byte pcintPin) {
  338. unsigned long newCount;
  339. unsigned long countDiff;
  340. unsigned long timeDiff;
  341. unsigned long avgLen;
  342. timeDiff = lastTime[pcintPin] - startTime[pcintPin];
  343. newCount = counter[pcintPin];
  344. countDiff = newCount - lastCount[pcintPin];
  345. if (!timeDiff)
  346. timeDiff = millis() - startTime[pcintPin];
  347. Serial.print(F("PCInt pin "));
  348. Serial.print(pcintPin);
  349. Serial.print(F(", iMode "));
  350. switch (PCintMode[pcintPin]) {
  351. case RISING: Serial.print(F("rising")); break;
  352. case FALLING: Serial.print(F("falling")); break;
  353. case CHANGE: Serial.print(F("change")); break;
  354. }
  355. #ifdef enablePulseLenChecking
  356. if (pulseWidthMin[pcintPin] > 0) {
  357. Serial.print(F(", min len "));
  358. Serial.print(pulseWidthMin[pcintPin]);
  359. Serial.print(F(" ms"));
  360. switch (pulseWidthStart[pcintPin]) {
  361. case RISING: Serial.print(F(" rising")); break;
  362. case FALLING: Serial.print(F(" falling")); break;
  363. }
  364. } else {
  365. Serial.print(F(", no min len"));
  366. }
  367. #endif
  368. Serial.print(F(", count "));
  369. Serial.print(newCount);
  370. Serial.print(F(" (+"));
  371. Serial.print(countDiff);
  372. Serial.print(F(") in "));
  373. Serial.print(timeDiff);
  374. Serial.print(F(" ms"));
  375. #ifdef enablePulseLenChecking
  376. // rejected count ausgeben
  377. // evt auch noch average pulse len und gap len
  378. if (pulseWidthMin[pcintPin]) { // check minimal pulse length and gap
  379. Serial.print(F(" Rej "));
  380. Serial.print(rejectCounter[pcintPin] - lastRejCount[pcintPin]);
  381. }
  382. #endif
  383. if (countDiff) {
  384. Serial.println();
  385. Serial.print(F("M first at "));
  386. Serial.print((long)startTimeRepInt[pcintPin] - lastReport[pcintPin]);
  387. Serial.print(F(", last at "));
  388. Serial.print((long)lastTime[pcintPin] - lastReport[pcintPin]);
  389. #ifdef enablePulseLenChecking
  390. noInterrupts();
  391. avgLen = pulseWidthSum[pcintPin] / countDiff;
  392. interrupts();
  393. Serial.print(F(", avg len "));
  394. Serial.print(avgLen);
  395. #endif
  396. }
  397. }
  398. /* give status report in between if requested over serial input */
  399. void showCmd() {
  400. unsigned long newCount;
  401. unsigned long countDiff;
  402. unsigned long timeDiff;
  403. unsigned long avgLen;
  404. char myChar;
  405. Serial.print(F("M Status: "));
  406. printVersion();
  407. Serial.println();
  408. Serial.print(F("M normal interval "));
  409. Serial.println(intervalMin);
  410. Serial.print(F("M max interval "));
  411. Serial.println(intervalMax);
  412. Serial.print(F("M min interval "));
  413. Serial.println(intervalSml);
  414. Serial.print(F("M min count "));
  415. Serial.println(countMin);
  416. for (byte pcintPin=0; pcintPin < MAX_PCINT_PIN; pcintPin++) {
  417. int aPin = PCintActivePin[pcintPin];
  418. if (aPin != -1) {
  419. timeDiff = lastTime[pcintPin] - startTime[pcintPin];
  420. newCount = counter[pcintPin];
  421. countDiff = newCount - lastCount[pcintPin];
  422. if (!timeDiff)
  423. timeDiff = millis() - startTime[pcintPin];
  424. Serial.print(F("M pin "));
  425. Serial.print(aPin);
  426. Serial.print(F(" "));
  427. showPin(pcintPin);
  428. Serial.println();
  429. }
  430. }
  431. Serial.print(F("M Next report in "));
  432. Serial.print(timeNextReport - millis());
  433. Serial.print(F(" Milliseconds"));
  434. Serial.println();
  435. }
  436. /*
  437. handle add command.
  438. */
  439. void addCmd(unsigned int *values, byte size) {
  440. uint8_t pcintPin; // PCINT pin number for the pin to be added (used as index for most arrays)
  441. byte mode;
  442. unsigned int pw;
  443. unsigned long now = millis();
  444. //Serial.println(F("M Add called"));
  445. int aPin = values[0];
  446. pcintPin = digitalPinToPcIntPin(aPin);
  447. if (aPin >= MAX_ARDUINO_PIN || aPin < 1
  448. || allowedPins[aPin] == 0 || pcintPin > MAX_PCINT_PIN) {
  449. PrintErrorMsg();
  450. Serial.print(F("Illegal pin specification "));
  451. Serial.println(aPin);
  452. return;
  453. };
  454. switch (values[1]) {
  455. case 2:
  456. mode = FALLING;
  457. pulseWidthStart[pcintPin] = FALLING;
  458. break;
  459. case 3:
  460. mode = RISING;
  461. pulseWidthStart[pcintPin] = RISING;
  462. break;
  463. case 1:
  464. mode = CHANGE;
  465. break;
  466. default:
  467. PrintErrorMsg();
  468. Serial.print(F("Illegal pin specification "));
  469. Serial.println(aPin);
  470. }
  471. pinMode (aPin, INPUT);
  472. if (values[2]) {
  473. digitalWrite (aPin, HIGH); // enable pullup resistor
  474. }
  475. #ifdef enablePulseLenChecking
  476. PulseMode[pcintPin] = mode; // specified mode also defines pulse level in this case
  477. if (values[3] > 0) {
  478. pw = values[3];
  479. mode = CHANGE;
  480. } else {
  481. pw = 0;
  482. }
  483. #endif
  484. if (!AddPinChangeInterrupt(aPin)) { // add Pin Change Interrupt
  485. PrintErrorMsg(); Serial.println(F("AddInt"));
  486. return;
  487. }
  488. PCintMode[pcintPin] = mode; // save mode for ISR which uses the pcintPin as index
  489. #ifdef enablePulseLenChecking
  490. pulseWidthMin[pcintPin] = pw; // minimal pulse width in millis, 3 if not specified n add cmd
  491. #endif
  492. if (PCintActivePin[pcintPin] != aPin) { // in case this pin is already active counting
  493. PCintActivePin[pcintPin] = aPin; // save real arduino pin number and flag this pin as active for reporting
  494. initialized[pcintPin] = false; // initialize arrays for this pin
  495. counter[pcintPin] = 0;
  496. lastCount[pcintPin] = 0;
  497. startTime[pcintPin] = now;
  498. lastTime[pcintPin] = now;
  499. lastReport[pcintPin] = now;
  500. }
  501. Serial.print(F("M defined pin "));
  502. Serial.print(aPin);
  503. Serial.print(F(" "));
  504. showPin(pcintPin);
  505. Serial.println();
  506. }
  507. /*
  508. handle rem command.
  509. */
  510. void removeCmd(unsigned int *values, byte size) {
  511. uint8_t pcintPin; // PCINT pin number for the pin to be added (used as index for most arrays)
  512. int aPin = values[0];
  513. pcintPin = digitalPinToPcIntPin(aPin);
  514. if (aPin >= MAX_ARDUINO_PIN || aPin < 1
  515. || allowedPins[aPin] == 0 || pcintPin > MAX_PCINT_PIN) {
  516. PrintErrorMsg();
  517. Serial.print(F("Illegal pin specification "));
  518. Serial.println(aPin);
  519. return;
  520. };
  521. if (!RemovePinChangeInterrupt(aPin)) {
  522. PrintErrorMsg(); Serial.println(F("RemInt"));
  523. return;
  524. }
  525. PCintActivePin[pcintPin] = -1;
  526. initialized[pcintPin] = false; // reset for next add
  527. counter[pcintPin] = 0;
  528. lastCount[pcintPin] = 0;
  529. #ifdef enablePulseLenChecking
  530. pulseWidthMin[pcintPin] = 0;
  531. lastRejCount[pcintPin] = 0;
  532. rejectCounter[pcintPin] = 0;
  533. #endif
  534. Serial.print(F("M removed "));
  535. Serial.println(aPin);
  536. }
  537. void intervalCmd(unsigned int *values, byte size) {
  538. if (size < 4) {
  539. PrintErrorMsg();
  540. Serial.print(F("size"));
  541. Serial.println();
  542. return;
  543. }
  544. if (values[0] < 1 || values[0] > 3600) {
  545. PrintErrorMsg(); Serial.println(values[0]);
  546. return;
  547. }
  548. intervalMin = (long)values[0] * 1000;
  549. if (millis() + intervalMin < timeNextReport)
  550. timeNextReport = millis() + intervalMin;
  551. if (values[1] < 1 || values[1] > 3600) {
  552. PrintErrorMsg(); Serial.println(values[1]);
  553. return;
  554. }
  555. intervalMax = (long)values[1]* 1000;
  556. if (values[2] > 3600) {
  557. PrintErrorMsg(); Serial.println(values[2]);
  558. return;
  559. }
  560. if (values[2] > 0) {
  561. intervalSml = (long)values[2] * 1000;
  562. }
  563. if (values[3]> 0) {
  564. countMin = values[3];
  565. }
  566. Serial.print(F("M intervals set to "));
  567. Serial.print(values[0]);
  568. Serial.print(F(" "));
  569. Serial.print(values[1]);
  570. Serial.print(F(" "));
  571. Serial.print(values[2]);
  572. Serial.print(F(" "));
  573. Serial.print(values[3]);
  574. Serial.println();
  575. }
  576. void helloCmd() {
  577. Serial.println();
  578. printVersion();
  579. Serial.println(F("Hello"));
  580. }
  581. static void HandleSerialPort(char c) {
  582. static unsigned int value;
  583. if (c == ',') {
  584. if (commandDataPointer + 1 < MAX_INPUT_NUM) {
  585. commandData[commandDataPointer++] = value;
  586. value = 0;
  587. }
  588. }
  589. else if ('0' <= c && c <= '9') {
  590. value = 10 * value + c - '0';
  591. }
  592. else if ('a' <= c && c <= 'z') {
  593. switch (c) {
  594. case 'a':
  595. commandData[commandDataPointer] = value;
  596. addCmd(commandData, ++commandDataPointer);
  597. commandDataPointer = 0;
  598. break;
  599. case 'd':
  600. commandData[commandDataPointer] = value;
  601. removeCmd(commandData, ++commandDataPointer);
  602. commandDataPointer = 0;
  603. break;
  604. case 'i':
  605. commandData[commandDataPointer] = value;
  606. intervalCmd(commandData, ++commandDataPointer);
  607. commandDataPointer = 0;
  608. break;
  609. case 'r':
  610. setup();
  611. commandDataPointer = 0;
  612. break;
  613. case 's':
  614. showCmd();
  615. commandDataPointer = 0;
  616. break;
  617. case 'h':
  618. helloCmd();
  619. commandDataPointer = 0;
  620. break;
  621. default:
  622. commandDataPointer = 0;
  623. //PrintErrorMsg(); Serial.println();
  624. break;
  625. }
  626. value = 0;
  627. }
  628. }
  629. SIGNAL(PCINT0_vect) {
  630. PCint(0);
  631. }
  632. SIGNAL(PCINT1_vect) {
  633. PCint(1);
  634. }
  635. SIGNAL(PCINT2_vect) {
  636. PCint(2);
  637. }
  638. void setup() {
  639. unsigned long now = millis();
  640. for (int pcintPin=0; pcintPin < MAX_PCINT_PIN; pcintPin++) {
  641. PCintActivePin[pcintPin] = -1; // set all pins to inactive (-1)
  642. initialized[pcintPin] = false; // initialize arrays for this pin
  643. counter[pcintPin] = 0;
  644. lastCount[pcintPin] = 0;
  645. startTime[pcintPin] = now;
  646. lastTime[pcintPin] = now;
  647. #ifdef enablePulseLenChecking
  648. lastPulseStart[pcintPin] = now;
  649. lastPulseEnd[pcintPin] = now;
  650. pulseWidthMin[pcintPin] = 0;
  651. rejectCounter[pcintPin] = 0;
  652. lastRejCount[pcintPin] = 0;
  653. #endif
  654. lastReport[pcintPin] = now;
  655. }
  656. timeNextReport = millis() + intervalMin; // time for first output
  657. Serial.begin(SERIAL_SPEED); // initialize serial
  658. delay (500);
  659. interrupts();
  660. Serial.println();
  661. printVersion();
  662. Serial.println(F("Started"));
  663. }
  664. /*
  665. Main Loop
  666. checks if report should be called because timeNextReport is reached
  667. or lastReport for one pin is older than intervalMax
  668. timeNextReport is only set here (and when interval is changed / at setup)
  669. */
  670. void loop() {
  671. unsigned long now = millis();
  672. if (Serial.available()) {
  673. HandleSerialPort(Serial.read());
  674. }
  675. boolean doReport = false; // check if report nedds to be called
  676. if((long)(now - timeNextReport) >= 0) // works fine when millis wraps.
  677. doReport = true; // intervalMin is over
  678. else
  679. for (byte pcintPin=0; pcintPin < MAX_PCINT_PIN; pcintPin++)
  680. if (PCintActivePin[pcintPin] >= 0)
  681. if((long)(now - (lastReport[pcintPin] + intervalMax)) >= 0)
  682. doReport = true; // active pin has not been reported for langer than intervalMax
  683. if (doReport) {
  684. report();
  685. timeNextReport = now + intervalMin; // do it again after intervalMin millis
  686. }
  687. }