Reading it · 06 of 11

The first read

Six wires, no library, and a sketch that scans the switch five times a second and prints which edge is down: left, top, right, header, or none. Two lines are held LOW in turn, B and D are read each time, and no line is ever driven HIGH.

Six wires

Six wires
Your board
Wires
6
VCC to
3V3
A B C D to
4 5 6 7
GND to GND, VCC to 3V3, and A, B, C, D to GPIO 4, GPIO 5, GPIO 6, GPIO 7. On a 3.3 V board VCC has to be 3V3, never 5V. The four pins can all be outputs and none of them is read by the ESP32-S3 at boot, which matters because the roller may be joining two of them through a reset.

GND to GND. VCC to your board's own supply: 5V on an Uno, 3V3 on the three 3.3 V boards. A, B, C and D to four digital pins, in that order.

The pins are the same in every sketch in this book: D2 to D5 on an Uno, GPIO 25, 26, 27 and 32 on an ESP32, GPIO 4 to 7 on an ESP32-S3, and GP10 to GP13 on a Pico. Each can be an output and an input with a pull-up, and none is one the chip reads at boot, which matters because the roller may be joining two of them while the board resets.

The sketch

Three small functions do the work. releaseAll() makes every line an input with its pull-up. holdLow() holds one line LOW. scanTilt() holds A LOW and reads B and D, then holds C LOW and reads them again, exactly as in no line goes to ground, and lets everything go.

Two details are there to protect the pins. holdLow() writes LOW before it makes the pin an output, so the pin goes straight to LOW; the other way round, an Uno's pin drives HIGH for a moment first. And nothing in the sketch ever writes HIGH.

delayMicroseconds(10) gives each line time to settle after the switch. A pull-up is tens of kilohms, and it takes a moment to lift a line back up.

What you should see

The serial monitor at 115200 prints a line five times a second. Tip the board one way, then another, holding it a moment each time:

left
left
none
top
top
right
header

A none between two sides is the roller crossing the cell, or resting against a single contact. The LEDs look dark while this sketch runs: each line is held LOW for a few microseconds per scan, too short to see. The next article keeps the low side lit.

The code

No library. scanTilt() holds A LOW and reads B and D, then holds C LOW and reads them again, and returns which edge is down. Change the four PIN_ numbers to the GPIOs you wired.

tilt_first_read.ino
/*
  4-Direction Tilt Sensor - first read                   TK19 / /p/tk19

  Wiring. Count from the square pad on the TinkerBlock board, parts
  up, header at the bottom:

    GND  -> GND
    VCC  -> 5V on an Uno; 3V3 on an ESP32, ESP32-S3 or Pico
            (every LED hangs from VCC onto a line your pin holds)
    A    -> D2 on an Uno, GPIO 25 on an ESP32, GPIO 4 on an
            ESP32-S3, GP10 on a Raspberry Pi Pico
    B    -> D3, GPIO 26, GPIO 5, GP11      (same order)
    C    -> D4, GPIO 27, GPIO 6, GP12
    D    -> D5, GPIO 32, GPIO 7, GP13

  Arduino IDE
    Tools > Board                 your board, e.g. ESP32S3 Dev Module
    Tools > Port                  the one that appears when you plug in
    Tools > USB CDC On Boot       Enabled   (ESP32-S3 only)
    No library needed.
*/

// The GPIO numbers A, B, C and D are wired to.
// Uno: 2 3 4 5. ESP32: 25 26 27 32. ESP32-S3: 4 5 6 7. Pico: 10 11 12 13.
const int PIN_A = 4;
const int PIN_B = 5;
const int PIN_C = 6;
const int PIN_D = 7;

// Which edge is down. NO_SIDE: the roller is joining no pair.
const int NO_SIDE = 0;
const int LEFT_DOWN = 1;
const int TOP_DOWN = 2;
const int RIGHT_DOWN = 3;
const int HEADER_DOWN = 4;
const char* SIDE_NAME[] = {"none", "left", "top", "right", "header"};

// Every line an input with its pull-up. No line is ever driven HIGH.
void releaseAll() {
  pinMode(PIN_A, INPUT_PULLUP);
  pinMode(PIN_B, INPUT_PULLUP);
  pinMode(PIN_C, INPUT_PULLUP);
  pinMode(PIN_D, INPUT_PULLUP);
}

// Hold one line LOW: LOW first, then output, so it never goes HIGH.
void holdLow(int pin) {
  digitalWrite(pin, LOW);
  pinMode(pin, OUTPUT);
}

bool readsLow(int pin) {
  return digitalRead(pin) == LOW;
}

// Hold A LOW and read B and D, then hold C LOW and read them again.
int scanTilt() {
  releaseAll();
  holdLow(PIN_A);
  delayMicroseconds(10);        // let the lines settle
  bool ab = readsLow(PIN_B);    // A-B joined: left edge down
  bool da = readsLow(PIN_D);    // D-A joined: header edge down

  releaseAll();
  holdLow(PIN_C);
  delayMicroseconds(10);
  bool bc = readsLow(PIN_B);    // B-C joined: top edge down
  bool cd = readsLow(PIN_D);    // C-D joined: right edge down

  releaseAll();
  if (ab) return LEFT_DOWN;
  if (da) return HEADER_DOWN;
  if (bc) return TOP_DOWN;
  if (cd) return RIGHT_DOWN;
  return NO_SIDE;
}

void setup() {
  Serial.begin(115200);
  releaseAll();
}

void loop() {
  Serial.println(SIDE_NAME[scanTilt()]);
  delay(200);                   // five scans a second, to keep it readable
}

Every released line is INPUT_PULLUP and the only value the sketch ever writes is LOW. holdLow() writes LOW before switching the pin to an output, so the pin never drives HIGH even for a moment. The delay(200) only keeps the output readable.

When it does not work

It prints none whatever I do.

No line ever followed the one held LOW. Check that the four pin numbers in the sketch are the four GPIOs you wired, in the order A, B, C, D, and that the cable is not one pin over: count from the square pad. Then tip the board firmly, a good way past level, and hold it still for a moment.

It names the wrong side.

Two lines are swapped, usually in the wiring. Check the order from the square pad: GND, VCC, A, B, C, D. The names also assume the board is parts up with the header at the bottom; hold it another way round and the names turn with it.

It reads a side when the board is flat on the desk.

That is the roller staying where it last rolled. Nothing pushes it back to the middle, so level usually reads the last side, or none. Level is not a reading covers it.

The serial monitor stays empty on my ESP32-S3.

Set Tools > USB CDC On Boot to Enabled and upload again. Without it the S3's USB port does not bring up a serial port at boot, so the sketch runs with nowhere to print.

Where this goes next

The same scan, with the two LEDs on the downhill side held lit.

Lighting the low side

Edit this page — content/books/four-direction-tilt-sensor/the-first-read.mdx

Community

Questions about this product

See what other owners have asked, and read their solutions.

Ask a question ↗

4-Direction Tilt Sensor

Loading discussions…

Discuss this article

Ask about this page. The answer stays here, on the page it belongs to, for whoever hits the same wall next.

Browse Modules and blocks on the forum