S3 plus port addresses

On the S3 plus. I need to read a bunch of pins simultaneously (arduino code).
What is the mapping between pins and ports ?
What is the port addresses for the above ports ?

If some pins are allocated already, and I need to ignore them, which ones for which ports ?

I can’t find this doc anywhere of Xiao esp32S3+ “pins to ports”.
Thanks

1 Like

Hi there,

And welcome here…

SO the Xiao Wiki always has a pin out Silk pin numbers i.e. (D3) or GPIO4 for example,
otherwise the Short answer for the ESP32-S3 (e.g., XIAO ESP32S3 “Plus”):

  • There are no AVR-style “PORTA/PORTB”. Instead, the S3 exposes two 32-bit GPIO input registers:
    • bits 0…31 → GPIO0…31
    • bits 0…31 of a second register → GPIO32…63
  • In Arduino (which uses ESP-IDF under the hood), you can snapshot all pins at once via the IDF GPIO structs/regs and then mask the bits you care about.

1) One-shot read of “all pins”

Drop this at the top of your sketch:

#include "soc/gpio_struct.h"   // GPIO.in / GPIO.in1
#include "soc/gpio_reg.h"      // REG_READ(), etc.

static inline uint64_t read_all_gpio()
{
  // Low 32 (GPIO0..31)
  uint32_t lo = GPIO.in;
  // High 32 (GPIO32..63) live in GPIO.in1.val (bit0 == GPIO32)
  uint32_t hi = GPIO.in1.val;
  return ( (uint64_t)hi << 32 ) | lo;
}

2) Map your Arduino pin list to a mask (so you only look at your pins)

Arduino’s core has digitalPinToGPIO(pin) which converts the Arduino pin number to the SoC GPIO number. Build a mask once, then use it every read:

// Build a 64-bit mask from your Arduino pin list
uint64_t make_mask(const int *pins, size_t n)
{
  uint64_t m = 0;
  for (size_t i = 0; i < n; ++i) {
    int gpio = digitalPinToGPIO(pins[i]); // Arduino pin -> GPIO#
    if (gpio >= 0) {
      m |= (uint64_t)1 << gpio;
    }
  }
  return m;
}

// Example: read only these pins in one shot
const int MY_PINS[] = { D0, D1, D2, D3, D4, D5 };  // <- your Arduino pins
uint64_t my_mask;

void setup() {
  // Set your pins as inputs (with pullups if you need)
  for (int p : MY_PINS) pinMode(p, INPUT_PULLUP);
  my_mask = make_mask(MY_PINS, sizeof(MY_PINS)/sizeof(MY_PINS[0]));
  Serial.begin(115200);
}

void loop() {
  uint64_t snap = read_all_gpio();       // snapshot of *all* GPIOs
  uint64_t only_mine = snap & my_mask;   // keep only your pins
  // test one pin, e.g., D2:
  int gpioD2 = digitalPinToGPIO(D2);
  bool d2_high = (only_mine >> gpioD2) & 1;
  // or print the whole masked word
  Serial.printf("mask=0x%016llX  vals=0x%016llX\n",
                (unsigned long long)my_mask,
                (unsigned long long)only_mine);
  delay(10);
}

3) “Which pins are already allocated / should I ignore?”

It depends on your sketch and board settings, but common ESP32-S3 caveats:

  • USB (native) uses GPIO19 (D-) and GPIO20 (D+) if you’re using USB-CDC; don’t poke them then.
  • Boot/strapping pins you generally leave alone: GPIO0, GPIO3, GPIO45, GPIO46.
  • JTAG defaults (if enabled) occupy GPIO39…GPIO42.
  • Your board (XIAO “Plus/Sense”) may route certain GPIOs to onboard devices (SD, I²C, display, etc.). Those are still readable, but you should exclude them if actively used by those peripherals.

f you’re unsure about your board’s Arduino-pin → GPIO mapping, you can print it at runtime:

void dump_pin_map(const int *pins, size_t n) {
  for (size_t i = 0; i < n; ++i) {
    int gpio = digitalPinToGPIO(pins[i]);
    Serial.printf("Arduino pin %-3d -> GPIO%-2d\n", pins[i], gpio);
  }
}

Make a MY_PINS list and mask that avoids the occupied lines. :crossed_fingers:

HTH
GL :slight_smile: PJ :v:

When using an Arduino - traditionally - if I want to read several pins I will use digitalRead() to read a bit at a time and assemble them into a byte. But this will take time.
Instead, if I want to read all 8 bits of the port at the exact same time I have to use another mechanism.

The other mechanism which reads all 8 bits assigned to a port requires knowing the port register locations. This is different for each machine port e.g. XiaoS3+ which is an esp32 device.

The program would set the direction of the pins in the port using DDRD
Then read the port directly:
uint8_t portd_state = PIND;

The pins on the S3+ are D0 to D19.
What is the variable name to read these pins as bytes

OK. Thanks - that’s what I needed to know.

  • sorry my reply came after your reply.
1 Like