Capacitive sensing with the Mega

Hello. I have some code that only needs one pin on the Uno for capactive sensing. In this case it is pin 2 but when I transfer the code to a Mega I must use pin 19 instead of pin 2 for my sensor to work (without modifying code). It appears that on the Uno I can select various pins within the code but not with the Mega. Could someone kindly explain why this is the case and how I can modify the code for the Mega to control multiple sensors?

Thank you…

// Pin for the LED
int LEDPin = 13;
// Pin to connect to your drawing
int capSensePin = 2;
// This is how high the sensor needs to read in order
//  to trigger a touch.  You'll find this number
//  by trial and error, or you could take readings at 
//  the start of the program to dynamically calculate this.
int touchedCutoff = 60;

void setup(){
  Serial.begin(9600);
  // Set up the LED
  pinMode(LEDPin, OUTPUT);
  digitalWrite(LEDPin, LOW);
}

void loop(){
  // If the capacitive sensor reads above a certain threshold,
  //  turn on the LED
  if (readCapacitivePin(capSensePin) > touchedCutoff) {
    digitalWrite(LEDPin, HIGH);
  }
  else {
    digitalWrite(LEDPin, LOW);
  }
  
  // Every 500 ms, print the value of the capacitive sensor
  if ( (millis() % 500) == 0){
    Serial.print("Capacitive Sensor on Pin 2 reads: ");
    Serial.println(readCapacitivePin(capSensePin));
  }
}

// readCapacitivePin
//  Input: Arduino pin number
//  Output: A number, from 0 to 17 expressing
//          how much capacitance is on the pin
//  When you touch the pin, or whatever you have
//  attached to it, the number will get higher
//  In order for this to work now,
// The pin should have a 1+Megaohm resistor pulling
//  it up to +5v.
uint8_t readCapacitivePin(int pinToMeasure){
  // This is how you declare a variable which
  //  will hold the PORT, PIN, and DDR registers
  //  on an AVR
  volatile uint8_t* port;
  volatile uint8_t* ddr;
  volatile uint8_t* pin;
  // Here we translate the input pin number from
  //  Arduino pin number to the AVR PORT, PIN, DDR,
  //  and which bit of those registers we care about.
  byte bitmask;
  if ((pinToMeasure >= 0) && (pinToMeasure <= 7)){
    port = &PORTD;
    ddr = &DDRD;
    bitmask = 1 << pinToMeasure;
    pin = &PIND;
  }
  if ((pinToMeasure > 7) && (pinToMeasure <= 13)){
    port = &PORTB;
    ddr = &DDRB;
    bitmask = 1 << (pinToMeasure - 8);
    pin = &PINB;
  }
  if ((pinToMeasure > 13) && (pinToMeasure <= 19)){
    port = &PORTC;
    ddr = &DDRC;
    bitmask = 1 << (pinToMeasure - 13);
    pin = &PINC;
  }
  // Discharge the pin first by setting it low and output
  *port &= ~(bitmask);
  *ddr  |= bitmask;
  delay(1);
  // Make the pin an input WITHOUT the internal pull-up on
  *ddr &= ~(bitmask);
  // Now see how long the pin to get pulled up
  int cycles = 16000;
  for(int i = 0; i < cycles; i++){
    if (*pin & bitmask){
      cycles = i;
      break;
    }
  }
  // Discharge the pin again by setting it low and output
  //  It's important to leave the pins low if you want to 
  //  be able to touch more than 1 sensor at a time - if
  //  the sensor is left pulled high, when you touch
  //  two sensors, your body will transfer the charge between
  //  sensors.
  *port &= ~(bitmask);
  *ddr  |= bitmask;
  
  return cycles;
}

I'll guess (without doing the necessary research) that the issue is due to your use of direct port manipulation. I would not expect the Mega and the Uno to necessarily have their port to pin mapping the same for pins 0-17 and your experience rather bears that out.

Are you aware that there is a capacitive sensing library already written to handle this?

https://playground.arduino.cc/Main/CapacitiveSensor?from=Main.CapSense

Unless you are doing this as a learning exercise?

Thank you boylesg and wildbill. I guess I need to spend more time looking over the library. However, the example I found seemed to work very easily and without error for my needs. As it presented an issue transferring to the Mega I wanted to find out why and as with all these things I thought it a good idea to ask the forum for a simplified explanation as to why. A place to start so to speak and yes, it will inevitable become a learning exercise :slight_smile:

Is it worth modifying the code? or should I start back at the basics?

As wildbill has correctly ‘guessed’, there are in fact NO direct mappings between Uno pins and Mega2560 pins.

Pin Uno Mega2560
0 D0 E0
1 D1 E1
2 D2 E4
3 D3 E5
4 D4 G5
5 D5 E3
6 D6 H3
7 D7 H4

8 B0 H5
9 B1 H6
10 B2 B4
11 B3 B5
12 B4 B6
13 B5 B7

A0(14) C0 F0
A1(15) C1 F1
A2(16) C2 F2
A3(17) C3 F3
A4(18) C4 F4
A5(19) C5 F5

If you stick to using Arduino pin numbers and Arduino libraries, the compiler will link the appropriate iom (I/O map) file for the 328P or the 2560 and do all the mapping for you. Otherwise you’re on your own. And yes, D2, which is pin 2 on the Uno is pin 19 on the mega;

14 J1
15 J0
16 H1
17 H0
18 D3
19 D2
20 D1
21 D0

And to save you asking why???, theb328P is a 28/32 pin chip where the mega2560 is a 100 pin chip - it’s all about the layout of the traces on the circuit board.

Thankyou DKWatson!

Hence the use of the capacitive sensing library. If I was to use that, all my pins would be automatically mapped to the Mega. Is that correct? And the reason I have this issue is that I'm not using a library that will do that for me. Is that correct? I ask just to be sure.

Without knowing the library I would have to say probably. It's not a terribly bad idea though to get cushy with the PORT and PIN concept as with time-critical ops, digitalWite() takes 67 ticks where bitSet(PORT,PIN) takes 2.

FYI

Arduino_Speed_Tests.pdf (12.2 KB)