4-direction tilt sensor/Reading it/07. Lighting the low side
Reading it · 07 of 11

Lighting the low side

After each scan, hold both lines of the pair the roller is joining LOW until the next one. The two LEDs beside the low edge light and point downhill. The roller is joining those two lines anyway, so holding both LOW is safe, and a scan every 20 ms is far too quick to see as a flicker.

The lights become the display

The low side lights
The edge that is down
Lit
A and B
Per LED, 5 V
about 3.0 mA
Both, 5 V
about 6.0 mA
With the left edge down the scan finds A–B, and the sketch holds both lines LOW until the next scan. The two LEDs beside that edge light, and they point downhill. The roller joins those lines anyway, so holding both LOW is safe; the scan every 20 ms takes microseconds, so the flicker is far too short to see.

The first read finds the pair and lets every line go again, so its LEDs light for microseconds. This sketch does one thing more: after the scan, it holds both lines of the pair it found LOW, and leaves them there until the next scan 20 ms later. Both of that pair's LEDs light, and because each LED sits beside its own line's leg of the switch, the two that light are the two on the low side.

Tip the board left and the two LEDs at the left edge light. Tip it towards the header and the two nearest the header light. Tipped towards a corner, the roller may join nothing, and then nothing lights.

Why both LOW is safe

Two outputs held LOW next to each other cannot fight: they are driving the same way. The roller is joining that pair already, so they were going to be at the same voltage regardless.

If the roller rolls on before the next scan, say from the left side to the top, it joins B, which is held LOW, to C, which is released. C is pulled LOW through the roller against its own pull-up, a fraction of a milliamp, and C's LED lights too until the next scan puts things right. That is the rule doing its job: because nothing is ever driven HIGH, the roller can join anything to anything and no pin is at risk.

What it costs

Two LEDs lit from 5 V draw about 6 mA from VCC, about 3 mA through each of the two held pins. From 3.3 V it is about 2.6 mA in all. The currents are worked out from a red LED's typical forward voltage, not measured.

The scan itself lets go of every line for a few tens of microseconds in every 20 ms. That is a gap of a fraction of a percent, and the LEDs look steady. Make the loop slower, with a longer delay() or more work in it, and the display simply updates less often; the gap does not grow.

The code

The first read's scan, plus holdPair(), which holds both lines of the pair it found LOW until the next scan. The LEDs on the low side light. It prints only when the side changes.

tilt_light_low_side.ino
/*
  4-Direction Tilt Sensor - lighting the low side        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
            (the LEDs need it; the scan does not)
    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;

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"};

void releaseAll() {
  pinMode(PIN_A, INPUT_PULLUP);
  pinMode(PIN_B, INPUT_PULLUP);
  pinMode(PIN_C, INPUT_PULLUP);
  pinMode(PIN_D, INPUT_PULLUP);
}

void holdLow(int pin) {
  digitalWrite(pin, LOW);       // LOW first, then output
  pinMode(pin, OUTPUT);
}

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

int scanTilt() {
  releaseAll();
  holdLow(PIN_A);
  delayMicroseconds(10);
  bool ab = readsLow(PIN_B);    // left
  bool da = readsLow(PIN_D);    // header

  releaseAll();
  holdLow(PIN_C);
  delayMicroseconds(10);
  bool bc = readsLow(PIN_B);    // top
  bool cd = readsLow(PIN_D);    // right

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

// Light the two LEDs on the low side: hold both lines of the pair LOW.
// The roller is joining them already, so both LOW is safe.
void holdPair(int side) {
  if (side == LEFT_DOWN)   { holdLow(PIN_A); holdLow(PIN_B); }
  if (side == TOP_DOWN)    { holdLow(PIN_B); holdLow(PIN_C); }
  if (side == RIGHT_DOWN)  { holdLow(PIN_C); holdLow(PIN_D); }
  if (side == HEADER_DOWN) { holdLow(PIN_D); holdLow(PIN_A); }
}

int lastSide = -1;

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

void loop() {
  int side = scanTilt();        // releases every line, scans, releases
  holdPair(side);               // then lights the low side until next time

  if (side != lastSide) {
    Serial.println(SIDE_NAME[side]);
    lastSide = side;
  }
  delay(20);
}

Holding both lines of a joined pair LOW is safe because they are joined anyway. Every scan starts with releaseAll(), which lets them go again. At one scan every 20 ms the lines are released for a few tens of microseconds in each 20, which no eye can see.

When it does not work

The wrong pair of LEDs lights.

The sketch lights the pair it found, so if the pair is wrong, the scan is reading two swapped lines. Check the wiring in order from the square pad: GND, VCC, A, B, C, D. The LEDs are fixed to their lines on the board, so they cannot be the thing that is wrong.

Nothing lights, but the monitor names the side.

VCC is not connected. The scan needs nothing from VCC and still works; the LEDs need VCC and nothing else. Wire it to 3V3, or 5V on an Uno.

Is it safe to hold two lines LOW while the roller moves?

Yes. If the roller rolls on to join a held line to a released one, the released line is pulled LOW through the roller against its own pull-up, which is a fraction of a milliamp, and its LED lights too until the next scan. No line is ever driven HIGH, so no two pins can fight.

The LEDs flicker when I carry the board.

That is the roller rattling, and the LEDs are showing it faithfully: each scan lights whatever it found. A knock is not a tilt adds the filter that waits for the same answer five times in a row before believing it.

Where this goes next

What the switch says when the board is flat, or tipped towards a corner.

Level is not a reading

Edit this page — content/books/four-direction-tilt-sensor/lighting-the-low-side.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