{"action":"created","issue":{"url":"https://api.github.com/repos/esp8266/Arduino/issues/4491","repository_url":"https://api.github.com/repos/esp8266/Arduino","labels_url":"https://api.github.com/repos/esp8266/Arduino/issues/4491/labels{/name}","comments_url":"https://api.github.com/repos/esp8266/Arduino/issues/4491/comments","events_url":"https://api.github.com/repos/esp8266/Arduino/issues/4491/events","html_url":"https://github.com/esp8266/Arduino/issues/4491","id":304073662,"node_id":"MDU6SXNzdWUzMDQwNzM2NjI=","number":4491,"title":"FunctionalInterrupt latency problem","user":{"login":"philbowles","id":27769598,"node_id":"MDQ6VXNlcjI3NzY5NTk4","avatar_url":"https://avatars2.githubusercontent.com/u/27769598?v=4","gravatar_id":"","url":"https://api.github.com/users/philbowles","html_url":"https://github.com/philbowles","followers_url":"https://api.github.com/users/philbowles/followers","following_url":"https://api.github.com/users/philbowles/following{/other_user}","gists_url":"https://api.github.com/users/philbowles/gists{/gist_id}","starred_url":"https://api.github.com/users/philbowles/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/philbowles/subscriptions","organizations_url":"https://api.github.com/users/philbowles/orgs","repos_url":"https://api.github.com/users/philbowles/repos","events_url":"https://api.github.com/users/philbowles/events{/privacy}","received_events_url":"https://api.github.com/users/philbowles/received_events","type":"User","site_admin":false},"labels":[{"id":332802539,"node_id":"MDU6TGFiZWwzMzI4MDI1Mzk=","url":"https://api.github.com/repos/esp8266/Arduino/labels/waiting%20for%20feedback","name":"waiting for feedback","color":"0052cc","default":false}],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":1,"created_at":"2018-03-10T14:13:56Z","updated_at":"2018-08-14T21:53:49Z","closed_at":null,"author_association":"NONE","body":"### Basic Infos\r\n\r\n- [ X] This issue complies with the [issue POLICY doc](https://github.com/esp8266/Arduino/blob/master/POLICY.md).\r\n- [X ] I have read the documentation at [readthedocs](https://arduino-esp8266.readthedocs.io/en/latest) and the issue is not addressed there.\r\n- [ X] I have tested that the issue is present in current master branch (aka latest git).\r\n- [ ] I have searched the issue tracker for a similar issue.\r\n- [- ] If there is a stack dump, I have decoded it.\r\n- [ X] I have filled out all fields below.\r\n\r\n#### Platform\r\n\r\n- Hardware:\t\t\t\tWemos D1 mini\r\n- Core Version:\t\t\t2.4.1\r\n- Development Env:\t\t1.8.5\r\n- Operating System:\t\tWindows10\r\n\r\n### Settings in IDE\r\n\r\n- Module:  \t\t\tWemos D1 mini r2\r\n- Flash Mode:\t\t\tN/A\r\n- Flash Size: \t\t        4MB/1MB\r\n- lwip Variant:                  v2 Lower Memory\r\n- Reset Method:\t\tN/A\r\n- Flash Frequency:\t        N/A\r\n- CPU Frequency:\t\t80Mhz\r\n- Upload Using:\t\tSERIAL\r\n- Upload Speed:              921600\r\n\r\n\r\n### Problem Description\r\n\r\nI think the signature of attachInterrupt in FunctionalInterrupt.h needs to change, either to\r\nstd::function<void(uint8_t)> [ where the uint8_t is the pin state that caused the transition] or better still: std::function<void(uint8_t,uint32_t)> where the 32-bit value is the micros() value closest in time to the initiating event.\r\n\r\n### [MCVE](https://stackoverflow.com/help/mcve) Sketch\r\n\r\n```cpp\r\n#include<FunctionalInterrupt.h>\r\n#include<functional>\r\nusing namespace std;\r\n              \r\n#define PUSHBUTTON 0\r\nuint32_t volatile last=0;\r\n\r\nfunction<void(void)> fvv=[](){\r\n   uint8_t s=digitalRead(PUSHBUTTON);\r\n   uint32_t m=micros();\r\n   uint32_t delta=m-last;\r\n   Serial.printf(\"T=%d S=%d delta=%d\\n\",m,s,delta);\r\n   last=m;\r\n};\r\n          \r\nvoid setup(){\r\n    Serial.begin(74880);\r\n    Serial.printf(\"Functional Interrupt CHANGE Example, pin=%d\\n\",PUSHBUTTON); \r\n    pinMode(PUSHBUTTON,INPUT_PULLUP);\r\n    attachInterrupt(PUSHBUTTON,fvv,CHANGE);\r\n}\r\n\r\nvoid loop(){\r\n}\r\n\r\n```\r\n\r\nThere are two reasons, the first of style/convenience, the second – more serious – regarding problems with latency and missed interrupts.\r\n\r\n1)\tStyle: function<void(void)> - let’s call it “fvv” - has to be explicitly declared as such. If one uses auto fvv=[](){…} or even an anonymous lambda, the compiler objects to the ambiguity between that and the void function pointer that the “normal” i.e. non-functional attachInterrupt expects.\r\n\r\nI suspect there are performance issues that prevent the functional version totally replacing the non-fn version, especially in the classic case of void function pointers vs std::function objects. Should the user be subjected to wrangling this – shouldn’t there be some internal optimisations that select a faster implementation in the case where the functor “decays” to equivalent a void fn pointer? Either way, a change of signature would prevent the ambiguity at least.\r\n\r\n2)\tNow the real problems: missed interrupts. I appreciate that any interrupt is going to be “missed” if the ISR takes longer than the time between “now” and the next transition. It is incumbent upon us then to “get out soon as possible”. Unless we have a GHz core, we are likely to miss “common” sub-millisecond events like some switch bounces…My thoughts here are on how to minimise and/or remedy them.\r\n\r\nAll of my comments/observations are based on the following assumptions:\r\n* Where mode = FALLING, pin state @ interrupt time should always == 0\r\n* Where mode = RISING, pin state @ interrupt time should always == 1\r\n* Where mode = CHANGE, pin state @ interrupt time should always alternate between 1 and 0\r\n\r\nThis will only be true of course if the state is captured at the same time as the cause. Let’s call Ti the time of the interrupt and Tp the time of the pin state read. Any delay between Ti and Tp allows the pin to revert to its previous state. The longer this is the more likely an “incorrect” cause will be read at Tp. The examples show that “when fvv is invoked” is already too late. Thus it must be captured as close to Ti as possible and passed to fvv as a parameter.\r\n\r\nI’m no ESP8266 asm expert but I have written an AVR interrupt library and I know I can get the causing pin state with a few machine cycles of the event. Also, users wishing to analyse / decode waveforms need the most accurate transition time…the less code between Ti and Tfvv the better…again the closest micros() to Ti needs to be captured and sent to fvv as a parameter.\r\n\r\nSo, the proof that Tfvv is too late:\r\n### Debug Messages\r\n```\r\nWith attachInterrupt(PUSHBUTTON,fvv,FALLING);\r\n…\r\nT=68373927 S=0 delta=397245\r\nT=68467394 S=0 delta=93467\r\nT=68467606 S=0 delta=212\r\nT=68467706 S=0 delta=100\r\nT=68467805 S=1 delta=99\t\t<-\r\nT=68467982 S=1 delta=177\t\t<-\r\nT=68468245 S=0 delta=263\r\nT=68470630 S=0 delta=2385\r\nT=68757482 S=0 delta=286852\r\nT=68848709 S=0 delta=912\r\n…\r\n\r\nWith attachInterrupt(PUSHBUTTON,fvv,RISING); [ straight out of the gate!]\r\nFunctional Interrupt RISING Example, pin=0\r\nT=9460081 S=1 delta=9460081\r\nT=9460477 S=0 delta=396\t\t<-\r\nT=9540895 S=1 delta=80418\r\nT=9541165 S=1 delta=270\r\nT=17917718 S=1 delta=8376553\r\nT=19087823 S=1 delta=1170105\r\nT=19644068 S=1 delta=556245\r\nT=20070703 S=1 delta=426635\r\nT=20076957 S=1 delta=6254\r\n…\r\n```\r\nThere are of course those who will argue “you always know the pin state when a RISING or FALLING int occurs” – but of course only in an ideal world where your ints occur at a slow enough rate. But when CHANGE occurs? Is it a “late” Tp read or the sign of a missed interrupt?\r\n\r\n### Debug Messages\r\n```\r\nFunctional Interrupt CHANGE Example, pin=0 [ again straight out of the gate…]\r\nT=12533062 S=0 delta=12533062\t\t[ Bouncy keypress\t\t\r\nT=12533454 S=0 delta=392\t\t\t<- did I miss 0->1 at T<392?\r\nT=12602193 S=1 delta=68739\r\nT=12602874 S=0 delta=681\r\nT=12606496 S=1 delta=3622\r\nT=21534981 S=0 delta=8928485\t\t\t[ 2nd bouncy keypress\r\nT=21535318 S=1 delta=337\t\t\t        [ expected\r\nT=25658921 S=0 delta=4123603\t\t\t[ expected\r\nT=25659229 S=0 delta=308\t\t\t<- unexpected…\"incorrect \" 0->1 ?\r\nT=25809738 S=1 delta=150509\r\n```\r\nThe pin is pulled up internally – hence its final “correct” state will always be 1. This tends to suggest that an interrupt has been missed between T=25658921 and T=25659229 S=0.\r\n\r\ncThe pulse train can be successfully \"re-created\" *if the uSecond times are available…* by synthesizing a 1 transition at  - say - T=25658921 + 308 / 2... Would this even be a good idea?\r\n\t\t\r\n"},"comment":{"url":"https://api.github.com/repos/esp8266/Arduino/issues/comments/413028645","html_url":"https://github.com/esp8266/Arduino/issues/4491#issuecomment-413028645","issue_url":"https://api.github.com/repos/esp8266/Arduino/issues/4491","id":413028645,"node_id":"MDEyOklzc3VlQ29tbWVudDQxMzAyODY0NQ==","user":{"login":"hreintke","id":4587225,"node_id":"MDQ6VXNlcjQ1ODcyMjU=","avatar_url":"https://avatars2.githubusercontent.com/u/4587225?v=4","gravatar_id":"","url":"https://api.github.com/users/hreintke","html_url":"https://github.com/hreintke","followers_url":"https://api.github.com/users/hreintke/followers","following_url":"https://api.github.com/users/hreintke/following{/other_user}","gists_url":"https://api.github.com/users/hreintke/gists{/gist_id}","starred_url":"https://api.github.com/users/hreintke/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/hreintke/subscriptions","organizations_url":"https://api.github.com/users/hreintke/orgs","repos_url":"https://api.github.com/users/hreintke/repos","events_url":"https://api.github.com/users/hreintke/events{/privacy}","received_events_url":"https://api.github.com/users/hreintke/received_events","type":"User","site_admin":false},"created_at":"2018-08-14T21:53:49Z","updated_at":"2018-08-14T21:53:49Z","author_association":"CONTRIBUTOR","body":"@devyte Sure, will look into this and put results into the comments of this issue."}}
{"actor":{"display_login":"hreintke"}}