Inside Firefox’s DOH engine | daniel.haxx.se

INSIDE FIREFOX’S DOH ENGINE

DNS over HTTPS (DOH) is a feature where a client shortcuts the standard native resolver and instead asks a dedicated DOH server to resolve names.

Compared to regular unprotected DNS lookups done over UDP or TCP, DOH increases privacy, security and sometimes even performance. It also makes it easy to use a name server of your choice for a particular application instead of the one configured globally (often by someone else) for your entire system.

DNS over HTTPS is quite simply the same regular DNS packets (RFC 1035 style) normally sent in clear-text over UDP or TCP but instead sent with HTTPS requests. Your typical DNS server provider (like your ISP) might not support this yet.

To get the finer details of this concept, check out Lin Clark’s awesome cartoon explanation of DNS and DOH.

This new Firefox feature is planned to get ready and ship in Firefox release 62 (early September 2018). You can test it already now in Firefox Nightly by setting preferences manually as described below.

This article will explain some of the tweaks, inner details and the finer workings of the Firefox TRR implementation (TRR == Trusted Recursive Resolver) that speaks DOH.

Preferences

All preferences (go to “about:config”) for this functionality are located under the “network.trr” prefix.

network.trr.mode – set which resolver mode you want.

0 – Off (default). use standard native resolving only (don’t use TRR at all)
1 – Race native against TRR. Do them both in parallel and go with the one that returns a result first.
2 – TRR first. Use TRR first, and only if the name resolve fails use the native resolver as a fallback.
3 – TRR only. Only use TRR. Never use the native (after the initial setup).
4 – Shadow mode. Runs the TRR resolves in parallel with the native for timing and measurements but uses only the native resolver results.
5 – Explicitly off. Also off, but selected off by choice and not default.

network.trr.uri – (default: none) set the URI for your DOH server. That’s the URL Firefox will issue its HTTP request to. It must be a HTTPS URL (non-HTTPS URIs will simply be ignored). If “useGET” is enabled, Firefox will append “?ct&dns=….” to the URI when it makes its HTTP requests. For the default POST requests, they will be issued to exactly the specified URI.

“mode” and “uri” are the only two prefs required to set to activate TRR. The rest of them listed below are for tweaking behavior.

We list some publicly known DOH servers here. If you prefer to, it is easy to setup and run your own.

network.trr.credentials – (default: none) set credentials that will be used in the HTTP requests to the DOH end-point. It is the right side content, the value, sent in the Authorization: request header. Handy if you for example want to run your own public server and yet limit who can use it.

network.trr.wait-for-portal – (default: true) this boolean tells Firefox to first wait for the captive portal detection to signal “okay” before TRR is used.

network.trr.allow-rfc1918 – (default: false) set this to true to allow RFC 1918 private addresses in TRR responses. When set false, any such response will be considered a wrong response that won’t be used.

network.trr.useGET – (default: false) When the browser issues a request to the DOH server to resolve host names, it can do that using POST or GET. By default Firefox will use POST, but by toggling this you can enforce GET to be used instead. The DOH spec says a server MUST support both methods.

network.trr.confirmationNS – (default: example.com) At startup, Firefox will first check an NS entry to verify that TRR works, before it gets enabled for real and used for name resolves. This preference sets which domain to check. The verification only checks for a positive answer, it doesn’t actually care what the response data says.

network.trr.bootstrapAddress – (default: none) by setting this field to the IP address of the host name used in “network.trr.uri”, you can bypass using the system native resolver for it. This avoids that initial (native) name resolve for the host name mentioned in the network.trr.uri pref.

network.trr.blacklist-duration – (default: 60) is the number of seconds a name will be kept in the TRR blacklist until it expires and can be tried again. The default duration is one minute. (Update: this has been cut down from previous longer defaults.)

network.trr.request-timeout – (default: 3000) is the number of milliseconds a request to and corresponding response from the DOH server is allowed to spend until considered failed and discarded.

network.trr.early-AAAA – (default: false) For each normal name resolve, Firefox issues one HTTP request for A entries and another for AAAA entries. The responses come back separately and can come in any order. If the A records arrive first, Firefox will – as an optimization – continue and use those addresses without waiting for the second response. If the AAAA records arrive first, Firefox will only continue and use them immediately if this option is set to true.

network.trr.max-fails – (default: 5) If this many DoH requests in a row fails, consider TRR broken and go back to verify-NS state. This is meant to detect situations when the DoH server dies.

network.trr.disable-ECS – (default: true) If set, TRR asks the resolver to disable ECS (EDNS Client Subnet – the method where the resolver passes on the subnet of the client asking the question). Some resolvers will use ECS to the upstream if this request is not passed on to them.

Split-horizon and blacklist

With regular DNS, it is common to have clients in different places get different results back. This can be done since the servers know from where the request comes (which also enables quite a degree of spying) and they can then respond accordingly. When switching to another resolver with TRR, you may experience that you don’t always get the same set of addresses back. At times, this causes problems.

As a precaution, Firefox features a system that detects if a name can’t be resolved at all with TRR and can then fall back and try again with just the native resolver (the so called TRR-first mode). Ending up in this scenario is of course slower and leaks the name over clear-text UDP but this safety mechanism exists to avoid users risking ending up in a black hole where certain sites can’t be accessed. Names that causes such TRR failures are then put in an internal dynamic blacklist so that subsequent uses of that name automatically avoids using DNS-over-HTTPS for a while (see the blacklist-duration pref to control that period). Of course this fall-back is not in use if TRR-only mode is selected.

In addition, if a host’s address is retrieved via TRR and Firefox subsequently fails to connect to that host, it will redo the resolve without DOH and retry the connect again just to make sure that it wasn’t a split-horizon situation that caused the problem.

When a host name is added to the TRR blacklist, its domain also gets checked in the background to see if that whole domain perhaps should be blacklisted to ensure a smoother ride going forward.

Additionally, “localhost” and all names in the “.local” TLD are sort of hard-coded as blacklisted and will never be resolved with TRR. (Unless you run TRR-only…)

TTL as a bonus!

With the implementation of DNS-over-HTTPS, Firefox now gets the TTL (Time To Live, how long a record is valid) value for each DNS address record and can store and use that for expiry time in its internal DNS cache. Having accurate lifetimes improves the cache as it then knows exactly how long the name is meant to work and means less guessing and heuristics.

When using the native name resolver functions, this time-to-live data is normally not provided and Firefox does in fact not use the TTL on other platforms than Windows and on Windows it has to perform some rather awkward quirks to get the TTL from DNS for each record.

Server push

Still left to see how useful this will become in real-life, but DOH servers can push new or updated DNS records to Firefox. HTTP/2 Server Push being responses to requests the client didn’t send but the server thinks the client might appreciate anyway as if it sent requests for those resources.

These pushed DNS records will be treated as regular name resolve responses and feed the Firefox in-memory DNS cache, making subsequent resolves of those names to happen instantly.

Bootstrap

You specify the DOH service as a full URI with a name that needs to be resolved, and in a cold start Firefox won’t know the IP address of that name and thus needs to resolve it first (or use the provided address you can set with network.trr.bootstrapAddress). Firefox will then use the native resolver for that, until TRR has proven itself to work by resolving the network.trr.confirmationNS test domain. Firefox will also by default wait for the captive portal check to signal “OK” before it uses TRR, unless you tell it otherwise.

As a result of this bootstrap procedure, and if you’re not in TRR-only mode, you might still get  a few native name resolves done at initial Firefox startups. Just telling you this so you don’t panic if you see a few show up.

CNAME

The code is aware of CNAME records and will “chase” them down and use the final A/AAAA entry with its TTL as if there were no CNAMEs present and store that in the in-memory DNS cache. This initial approach, at least, does not cache the intermediate CNAMEs nor does it care about the CNAME TTL values.

Firefox currently allows no more than 64(!) levels of CNAME redirections.

about:networking

Enter that address in the Firefox URL bar to reach the debug screen with a bunch of networking information. If you then click the DNS entry in the left menu, you’ll get to see the contents of Firefox’s in-memory DNS cache. The TRR column says true or false for each name if that was resolved using TRR or not. If it wasn’t, the native resolver was used instead for that name.

Private Browsing

When in private browsing mode, DOH behaves similar to regular name resolves: it keeps DNS cache entries separately from the regular ones and the TRR blacklist is then only kept in memory and not persisted to disk. The DNS cache is flushed when the last PB session is exited.

Tools

I wrote up dns2doh, a little tool to create DOH requests and responses with, that can be used to build your own toy server with and to generate requests to send with curl or similar.

It allows you to manually issue a type A (regular IPv4 address) DOH request like this:

$ dns2doh --A --onlyq --raw daniel.haxx.se | \
curl --data-binary @- \
https://dns.cloudflare.com/.well-known/dns \
-H "Content-Type: application/dns-udpwireformat"

I also wrote doh, which is a small stand-alone tool (based on libcurl) that issues requests for the A and AAAA records of a given host name from the given DOH URI.

Why HTTPS

Some people giggle and think of this as a massive layer violation. Maybe it is, but doing DNS over HTTPS makes a lot of sense compared to for example using plain TLS:

  1. We get transparent and proxy support “for free”
  2. We get multiplexing and the use of persistent connections from the get go (this can be supported by DNS-over-TLS too, depending on the implementation)
  3. Server push is a potential real performance booster
  4. Browsers often already have a lot of existing HTTPS connections to the same CDNs that might offer DOH.

Further explained in Patrick Mcmanus’ The Benefits of HTTPS for DNS.

It still leaks the SNI!

Yes, the Server Name Indication field in the TLS handshake is still clear-text, but we hope to address that as well in the future with efforts like encrypted SNI.

Bugs?

File bug reports in Bugzilla! (in “Core->Networking:DNS” please)

If you want to enable HTTP logging and see what TRR is doing, set the environment variable MOZ_LOG component and level to “nsHostResolver:5”. The TRR implementation source code in Firefox lives in netwerk/dns.

Caveats

Credits

While I have written most of the Firefox TRR implementation, I’ve been greatly assisted by Patrick Mcmanus. Valentin Gosu, Nick Hurley and others in the Firefox Necko team.

DOH in curl?

Since I am also the lead developer of curl people have asked. The work on DOH for curl has not really started yet, but I’ve collected some thoughts on how DNS-over-HTTPS could be implemented in curl and the doh tool I mentioned above has the basic function blocks already written.

Other efforts to enhance DNS security

There have been other DNS-over-HTTPS protocols and efforts. Recently there was one offered by at least Google that was a JSON style API. That’s different.

There’s also DNS-over-TLS which shares some of the DOH characteristics, but lacks for example the nice ability to work through proxies, do multiplexing and share existing connections with standard web traffic.

DNScrypt is an older effort that encrypts regular DNS packets and sends them over UDP or TCP.

Source: Inside Firefox’s DOH engine | daniel.haxx.se

MYSQL Table Sizes

Table size for all databases:

SELECT TABLE_SCHEMA AS `Database`,
TABLE_NAME AS `Table`,
ROUND((DATA_LENGTH + INDEX_LENGTH) / 1024 / 1024) AS `Size (MB)`
FROM information_schema.TABLES
ORDER BY (DATA_LENGTH + INDEX_LENGTH) DESC;

Table sizes for a single database:

SELECT TABLE_NAME AS `Table`,
ROUND(((DATA_LENGTH + INDEX_LENGTH) / 1024 / 1024), 2) AS `Size (MB)` 
FROM information_schema.TABLES 
WHERE table_schema = "<database name>" 
ORDER BY (data_length + index_length) DESC;

Size of a single database:

SELECT TABLE_SCHEMA AS `Database`, 
ROUND(SUM(DATA_LENGTH + INDEX_LENGTH) / 1024 / 1024, 2) AS `Size (MB)` 
FROM information_schema.TABLES
WHERE TABLE_SCHEMA="<database name>";

Arduino Interrupt : There are more than Two external interrupts!

Arduino Interrupt: There are more than Two external interrupts! It’s true; it is a little known fact that there are more than 2 external interrupts (available on virtually any I/O pin) but how do you use them? Find out here.

Arduino interrupts:

  • Let your program react instantly to events.
  • Are easy to setup when you know how.
  • Let your program work efficiently instead of polling for an event.
  • Find out why you need two functions attachInterrupt() and digitalPinToInterrupt().

An Arduino Interrupt is useful when you need a program to react virtually instantly to an event. If you apply a digital signal to pins 2 or 3 of an Arduino Uno then these can trigger an interrupt (There are more).

There are also many more interrupt sources that are used internally in the chip. For example there are serial port completion interrupts (data received or transmitted), timer match or overflow interrupts etc.

These all allow efficient operation of the internal modules while still allowing the processor to get on with other tasks. But the focus of this page is on external physical event (i.e. pin voltage change) interrupts.

Setting up an external interrupt

Why do we need two functions to setup an external interrupt?

  1. attachInterrupt().
  2. digitalPinToInterrupt().

There are two aspects to an external interrupt:

  1. Which physical Arduino pin is in use.
  2. Which software interrupt are we talking about. For the Arduino Uno it us INT0 or INT1.

In Arduino code we are all used to specifying a pin to which to apply a selected operation for example digitalRead(5) will read the digital value from Arduino pin 5. For an external interrupt you can’t select the pin as the interrupt source because the attachInterrupt function is wanting the interrupt number (0 or 1).

Therefore a mediating function us used to translate the pin (on which the interrupt must be attached – because it is physically fixed in the microcontroller architecture). The function digitalPinToInterrupt translates the pin on which the interrupt is attached into the interrupt number. This follows the Arduino convention that the pin is specified as the input to the function.

You could say why bother with all this and just make a function that accepts the pin number and translate in the background to the interrupt number. The answer is that there are many different Arduino boards – all have different pins and associated interrupts. So these functions allow other boards to operate.

As long as you select the pin that is associated with the interrupt then these functions return and attach the interrupt without you having to think about the interrupt number. Just select the right pin and the relevant interrupt is attached.

On the software side (for Arduino code) there are two setup functions you need to use:

  1.     attachInterrupt( <interrupt>,my_isr_function, mode ).
  2.     digitalPinToInterrupt( INTPIN ).

Since different Arduino boards have interrupt capability on different pins, you should use the function digitalPinToInterrupt( INTPIN ) as follows:

#define INTPIN 3

attachInterrupt( digitalPinToInterrupt( INTPIN ), my_isr_function, FALLING );

This would set an interrupt on pin3 (INT1) to call “my_isr_function” on the falling edge of the digital input signal. Note: mode can be LOW, CHANGE, RISING or FALLING.

Note that “my_isr_function” is simply a standard Arduino function that you write – you can call it anything you like but it is best to indicate it is an interrupt routine in the function name (for your own reference).

Note: Physical Arduino Uno pin 3 has INT1, and physical Arduino pin 2 has INT0.

Stopping an external interrupt

if you want to stop the interrupt action then use the function:

detachInterrupt ( digitalPinToInterrupt( INTPIN ) )

Again there are two functions detachInterrupt() and digitalPinToInterrupt() used as shown above.

Note: The opposite function for detatching an interrupt is: detachInterrupt().

This will remove the interrupt function from the related interrupt pin “INTPIN”.

How Many Arduino Interrupts are there?

…On the Arduino Uno there only two external interrupt pins:

  • pin 2 – interrupt 0.
  • pin 3 – interrupt 1.

Do you think this is a true statement?: “There only two external interrupts”

See if you know the right answer:
How many external Interrupts are available on an Arduino Uno?

  • 2
  • 5
  • 26
  • 32
2, 5, 26, or 32? (click to find out)

Surprisingly there are 26!

There are the 2 external interrupts (INT0, INT1) plus three sets of 8 pin change interrupts.

Arduino Interrupt Overview

Microcontrollers are very good at repeatedly doing a task by executing one  instruction after another. They do this by incrementing the program counter and fetching the next instruction from memory. The problem comes when the processor has to deal with unexpected events.

For an external event you need the program to branch off and do something without waiting around for the current task to complete. The mechanism to do this is the interrupt.

Consider a program waiting for serial port data. it is in a polling loop constantly reading the serial port register waiting for data. However, you want the program to react to a button press to start another activity.

While polling the serial port, your program does nothing else and this is where interrupts come in. [Yes I know the serial port has its own interrupt – give me a break – its just an example for illustration ok – lets assume it does not have an interrupt].

An external interrupt will temporarily divert the processor from its current operation to a so-called ISR (Interrupt Service Routine) which will perform the operation you need. Not only that, it will execute the ISR virtually instantly providing an easy way to respond quickly to events.

Arduino interrupt vectors

When an interrupt is triggered your program has to do something. You tell the Arduino what to do by providing a function that services the interrupt (an ISR or Interrupt Service Routine).

The address of the ISR is placed into a specific location in a table of interrupt vectors. Each location in that table is specific to each interrupt. When the interrupt triggers, your ISR is called.

There are 6 dedicated external interrupts. These are the first six entries in the interrupt vector table:

Address Vector Name
0 RESET
1 INT0
2 INT1
3 PCINT0
4 PCINT1
5 PCINT2

Two, you already know about: INT0 and INT1 but there is also the reset vector which sits at address zero, so when the program counter is set to its power-up state (0x0000) the code at the reset vector is executed – you place a jump to your reset handing routine at this vector address. PCINT0 ~ 2 are the pin interrupt change vectors.

The Arduino Uno has a total of 26 interrupt vectors, and most of these are internal interrupts that are triggered by internal peripherals such as the ADC, timers or UARTs etc.

The total number of interrupt sources (any single external signal or internal module that can cause an interrupt to trigger) is:

26-3+3*8  =  47

[ This includes internal module interrupts and 3×8 pin change signals. ]

Arduino Interrupt Call Stack

For normal function calls, a stack is used to save and restore the Program Counter (PC). The PC contains the address of the current instruction.

When a subroutine is called, the value of the PC is pushed onto the stack, and when the subroutine is finished, the value is popped back from the stack. Thus returning the PC to its original address before the function call. The same process is used for an ISR subroutine.

For a subroutine call, the assembler instruction CALL pushes the PC onto the stack. To return from the call, RET pops the PC from the stack.

The only difference between a subroutine return and an ISR return is that the ISR return uses the RETI instruction, which re-enables interrupts.

ISR operation

So an ISR is virtually identical to a normal subroutine call. The problem is that an ISR can be executed at any time corrupting any existing register values.

To allow the ISR to work current register values must be stored at the beginning and restored at the end. Fortunately this is taken care of for you within the compiler. All relevant flags and registers are saved before, and then restored after an interrupt.

In this way the current program is interrupted in the middle of its operation and does not know anything about the interrupt.

Hardware connection

Connect a push button on one side to Arduino pin D2 and on the other side to Ground.

Connect a 100nF capacitor from Arduino pin D2 to Ground. To explore interrupts and switch debouncing and why this works see the page on switch debouncing.

Examples Showing Arduino Interrupt Operation

Software

Arduino Ide: 1.8.9+

Libraries

None used.

Example Sketch 1

Arduino Interrupt Hardware setup

Connect a push button from pin D2 to ground.

Arduino Interrupt Code Description

To illustrate the concept, consider the simple program below, all it does is flash an led on and off at 500ms intervals and then repeats. If the button is pressed causing an interrupt, then the delay time is toggled to 50ms.

The delay function shows that the interrupt is operating – even while most of the time the processor is sitting within the delay function doing nothing.

Adding interrupt code allows the program to react instantly and change the delay time for the LED flash (delayTime is updated immediately in the interrupt) however the flash rate of the LED will not update immediately since the delay operation must finish first. The second example below, shows how to avoid this using arduino millis.

 

// By John Main © best-microcontroller-projects.com
// Simple LED flash and button interrupt using delay().
#define LED LED_BUILTIN
#define interruptPin 2  // Can only be pin 2 or 3 on the Uno.

volatile uint16_t delayTime= 500;

//////////////////////////////////////////////
void setup(void) {
  pinMode(LED,OUTPUT);
  pinMode(interruptPin,INPUT_PULLUP);

  attachInterrupt(digitalPinToInterrupt(interruptPin), myisr, RISING);
}

//////////////////////////////////////////////
// Arduino Delay LED flash.
void loop(){

  delay(delayTime);
  digitalWrite(LED,HIGH);
  delay(delayTime);
  digitalWrite(LED,LOW);
}

//////////////////////////////////////////////
// Interrupt Service Routine
// Do not use this ISR in 'real' code because 
// of switch bouncing see this page for useful
// code:
// https://www.best-microcontroller-projects.com/easy_switch_debounce.html
//
void myisr() {
static byte state = 1;
  state = !state;  if (state) delayTime = 500; else delayTime = 50; }

[File:flash_led_on_interrupt_button.ino]

 

Example Sketch 2

Arduino Interrupt Hardware setup

Connect a push button from pin D2 to ground (the same as sketch 1).

Arduino Interrupt Code Description

This is a more realistic example, where the main loop reacts more quickly to the information sent from the interrupt. Instead of using the delay function (which is a do nothing operation) the millis() function is used. This allows the processor to do other useful work; In this case executing the loop function more quickly.

Note: Because we use millis() in this code it is also used in the ISR to filter out too much bouncing i.e. if we get 2 or more interrupts within 300ms the routine only acts on one interrupt.

TIP: See the page on switch debouncing to see how much the capacitor filters out switch bounces.

// By John Main © best-microcontroller-projects.com
// Simple LED flash and button interrupt using millis().
#define LED LED_BUILTIN
#define interruptPin 2  // Can only be pin 2 or 3 on the Uno.

volatile uint16_t delayTime= 500;

uint32_t timeWas = 0;

//////////////////////////////////////////////
void setup(void) {
  pinMode(LED,OUTPUT);
  pinMode(interruptPin,INPUT_PULLUP);
  digitalWrite(LED,LOW);
  Serial.begin(115200);
  attachInterrupt(digitalPinToInterrupt(interruptPin), myisr, RISING);
}

//////////////////////////////////////////////
// Arduino millis delay LED flash.
void loop(){
  static byte LEDstate=0;

  if (millis()-timeWas>delayTime) {
    timeWas = millis();
    LEDstate = LEDstate ? 0 : 1;
    if (LEDstate) digitalWrite(LED,HIGH); else digitalWrite(LED,LOW);
  }
}

//////////////////////////////////////////////
// Interrupt Service Routine
void myisr() {
static byte state = 1;
static uint32_t lastButtonTime=0;

  // Filter out too quick buttons = errors.
  if (millis()-lastButtonTime > 300) {
    state = !state;
    if (state) delayTime = 500; else delayTime = 50;
    lastButtonTime = millis();
  }
}

[File:flash_led_on_interrupt_button_millis.ino]

Arduino – When To Use Volatile

The Arduino volatile command is only required if a variable can be changed outside normal program flow.

You can see examples of volatile in the Sketches above.

In a normal function, variables are input to a function as parameters. These are then used within the function and can be returned as a value from the function.

In this case the compiler knows all the operations that can happen on that variable and when they happen. It is totally defined by the code written within the function. Therefore the compiler can make assumptions about the variable to optimise operations.

For a function that receives information from an interrupt routine, communicated via a global variable, the interrupt can change the value of that variable at any time even while the function is operating!

Compilers do fancy optimisations behind the scenes and the Arduino volatile keyword tells the compiler to “leave this variable alone” and not optimise it. The variable must be left available for both the function and the ISR to read and update.  It tells the compiler to re-read the value and not read the cached version, since the variable may have been updated by the interrupt.

PCINT Arduino Interrupt

These are the pin change interrupts associated with the each port on the Arduino. So there are 3 ports each with 8 pins that can be detected for interrupt use. These are labelled PCINT0..23.

TIP: PCINT0..23 can be used to wake the part from sleep mode (other than idle).

Interrupts are PCINT2..0 registers. To use them you have to use hardware register names ( read-the-datasheet ) i.e. its not quite as easy as the normal Arduino interrupt method.

Warning: Warning: PCINT0..23 will trigger an interrupt even if pins are outputs.

How to use PCINT

Hardware setup

  • Connect a push button from pin A0 to ground.
  • Connect a push button from pin A4 to ground.
  • Connect a sounder from A3 to ground (piezo disc : optional).

How to Setup Pin Change Interrupts

Since these interrupts are simpler than INT0 and INT1, they only trigger on a pin change. They also trigger only for blocks of 8 bits and there is no indication on which pin caused the interrupt. The three problems are:

  1. A single interrupt triggers for any of 8 pins.
  2. No indication of which pin triggered the interrupt.
  3. These interrupts only trigger on a pin change.

The first thing to do is use a mask register to enable or disable individual pins in an 8 bit block. Then only these will cause an interrupt to trigger. You don’t want outputs triggering the interrupt – they can do that and could be used as a software interrupt mechanism.

The second thing to do is to record the port value before (and after) interrupt operations. That way you can use an XOR expression to find out the pin changes.

Lets get some interrupts from port C specifically PC4 and PC0 which are on PCINT8 and PCINT12 respectively. The mask register for PORT C is PCMSK1.

Code Description

The serial port shows some information as the buttons are pushed. Pushing the A0 button will set the LED flash rate to 500ms and pushing the A4 button will set the flash rate to 50ms.

Loop code

In the loop() code the LEDs are flashed at the rate set in delayTime (changed in the ISR). if an interrupt is detected (flgTone is set) then the number of interrupts is printed to the serial port. If there were more than one interrupt (intsFound ) a high frequency sound is made otherwise a low frequency sound is made.

Both flgTone and intsFound are reset to zero ready for the next button push.

Warning: Since any change on the pin is detected as an interrupt both falling and rising edges will cause an interrupt but switch bounce also causes many more interrupts.

Due to switch bounce there can be many more than one bounce for each button press. See here for how to eliminate switch bounce in interrupt routines.

ISR code

In the ISR the old port value is xored with the new value (any different bits show up as ones). v1 and v2 are given the old values of the port.

For the following code; If a change in A0 was detected, and v1 was zero then the delay time is set to 500ms.

if (v1==0 && change & (1<<PCINT8) ) delayTime = 500;

A similar process is done for A4.

If any rising edge is found then flgTone is incremented.

if (change && (v1==0 || v2==0) ) flgTone++; // rising edge only

The oldPorta value is updated and the number if interrupt detections (intsFound) is incremented.

// By John Main © best-microcontroller-projects.com // Using Pin Change interrupts. const byte LED = LED_BUILTIN; const byte TONE_PIN = A3; const byte PC0_pin = A0; const byte PC4_pin = A4; volatile byte state = LOW; volatile static byte oldPorta; volatile static byte PCMask; volatile uint16_t delayTime= 500; volatile byte flgTone = 0,intsFound = 0; void setup() {  Serial.begin(115200);  pinMode(LED, OUTPUT);  pinMode(PC0_pin, INPUT_PULLUP);  pinMode(PC4_pin, INPUT_PULLUP);  oldPorta = PINC;  PCMSK1 |= (1<<PCINT8);  PCMSK1 |= (1<<PCINT12);  Serial.println(PCMSK1,HEX); // What is the total mask.  PCMask = PCMSK1;  PCICR |= (1<<PCIE1); // Enable PCINT on portc. } ////////////////////////////////////////////// // Arduino Delay LED flash. void loop(){  delay(delayTime);  digitalWrite(LED,HIGH);  delay(delayTime);  digitalWrite(LED,LOW);  if(flgTone) {  Serial.print("Found ");Serial.print(intsFound);  Serial.print(" interrupts, using: ");Serial.println(flgTone);  if(intsFound>1)  tone(TONE_PIN,600,100);  else  tone(TONE_PIN,300,100);  intsFound = flgTone = 0;  } } ISR( PCINT1_vect ) {  byte change,v1,v2;  change = oldPorta ^ PINC;  v1 = oldPorta & (1<<PCINT8);  v2 = oldPorta & (1<<PCINT12);  // v1==0 previously so detecting rising edge.  if (v1==0 && change & (1<<PCINT8) ) delayTime = 500;  if (v2==0 && change & (1<<PCINT12)) delayTime = 50;  if (change && (v1==0 || v2==0) ) {  intsFound++; // Only count rising edge interrupts (as this interrupt reacts to both).  flgTone = 1; // rising edge only  }  oldPorta = PINC; }  

[File:arduino-pinchange-interrupt.ino]

 

Warning: Button presses will cause multiple bounces. To properly debounce the button inputs on interrupt pins see this page.

Pin Change Registers

PCICR Interrupt Control Register

Control which bank of 8 interrupts is active (enabled).

Bits: PCIE[2..0] : Interrupt Enable for PORT B(0), C(1), D(2).

PCMSK Interrupt Mask Register[2..0]

Enable individual pins to interrupt with the mask register.

PCMSK2 : Contains PCINT[23..16] – PORTD[7..0].
PCMSK1 : Contains PCINT[14..8] – PORTC[6..0].
PCMSK1 : Contains PCINT[7..0] – PORTB[7..0].

Note: PORTC bit 7 (PCINT15) is not available [ATMega328p datasheet]. Also PC6 is the reset pin – unless programmed by fuses as an I/O pin.

PCINT Pin Change Interrupts

PCINT0 for PORTB (Input using PINB) – compiler vector is PCINT0_vect.
PCINT1 for PORTC (Input using PINC) – compiler vector is PCINT1_vect.
PCINT2 for PORTD (Input using PIND) – compiler vector is PCINT2_vect.

PCIFR Interrupt Flag Register

Flag bit is set when an interrupt is triggered (useful for polling).

Bits: PCIF[2..0] : Interrupt Flag.

 

 

Source: Arduino Interrupt : There are more than Two external interrupts!