Skip to content

9. Input devices

Overview of week 9 assignment

  1. Group assignment
    1. probe an input device’s analog levels and digital signals
  2. Individual assignment
    1. measure something: add a sensor to a microcontroller board that you have designed and read it

1. Group assignment

For more information, see the Week 09: Group assignment page.

2. Individual assignment

First, I needed to fix an issue on LED of my development board I made last week.

In the final project, a mobile 3D printer with infinite print size will use input devices such as end stops, rotary encoders, and thermistors, but I am also interested in using optical flow sensors, ArUco markers, etc. to compensate for the accumulated slip of the wheels. More details will be documented on the Project page.

But for this week, I ended up trying two random sensors that were in stock at FabLab Kannai.

Google Lens

When trying to identify what sensors they are, Google Lens is very useful as it allowed me to take a photo of each sensor and identify their names.

A. KY-020 Tilt Switch Sensor

First, I tested KY-020 Tilt Switch Sensor Module.

  • Detects movement by closing its circuit when tilted using metallic ball switch, but does not measure tilt angle
  • Operates at 3.3V–5V, providing a digital output for microcontrollers, includes a 10kΩ resistor

a. Wiring

KY-020 Pin RP2040 (Pico) Pin Description
S (Signal) Any GPIO (in my case, GP29) Digital output (HIGH when level, LOW when tilted)
Middle (+) 3.3V Power (RP2040 operates at 3.3V)
– (Ground) GND Ground

tilt_sensor

b. Programming

The following sketch, based on the test code included on the website above, will turn on the LED on pin 3 of the board and serial print "tilt" when the tilt of the module changes.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
int tiltPin = 29;    // pin number for tilt switch signal 
int ledPin =  3;     // pin number of LED 
int tiltState = 0;   // variable for reading the tilt switch status

void setup() {  
  pinMode(ledPin, OUTPUT);  // set the LED pin as output      
  pinMode(tiltPin, INPUT);  // set the tilt switch pin as input
  Serial.begin(115200);     // Start serial communication (UART1) at 115200 baud
  Serial.setTimeout(10); // Set serial read timeout to 10ms
}

void loop(){
  // get the tilt switch state
  tiltState = digitalRead(tiltPin);

  // check if tilt switch is tilted.
  if (tiltState == HIGH) {     
    digitalWrite(ledPin, HIGH);  
    Serial.println("tilt: ");
    delay(1000); // Keep the LED on for 1 second
  } 
  else {
    digitalWrite(ledPin, LOW); 
    Serial.println("no tilt: ");
  }
}

I like the simple, mechanical feel of the tilt sensor.


B. VL53L1X Time of Flight Distance Sensor

Secondaly, I tried using Adafruit VL53L1X, Time of Flight (ToF) distance sensor, and it was an interesting device. How ToF distance sensors work (Japanese) helped me get a basic idea of ​​the mechanism of the ToF sensor.

Feature Description
Range 30 to 4000mm
Technology 940 nm invisible laser for precise distance measurement
How it works Measures the time of flight (time taken for light to travel to an object and back) to calculate the distance.
Communication I²C interface (up to 400 kHz)
Update Rate Up to 50Hz

a. Wiring

VL53L1X Pin RP2040 Pin Description
Vin 3.3V Power supply (RP2040 operates at 3.3V)
GND GND Ground connection
SCL SCL (GP7) I2C Clock Line
SDA SDA (GP6) I2C Data Line
GPIO Any GPIO (in my case, GP2) Interrupt or data-ready pin (optional)
XSHUT Any GPIO (in my case, GP3) Shutdown pin (used to enable/disable the sensor)

b. I2C

The VL53L1X sensor uses the I2C protocol (SCL, SDA) which I only vaguely understood, so I needed to understand what it was and how it worked.

I2C

I2C (Inter-Integrated Circuit) is a simple communication protocol that uses just two wires to connect multiple devices, making it easy to implement. It supports multiple devices on the same bus, but it's limited to low-speed communication and works best over short distances. Addressing can become tricky with many devices:

  • SDA (Serial Data Line) – Transfers data between devices.
  • SCL (Serial Clock Line) – Synchronizes data transfer.

How It Works:

  • Master-Slave Architecture: A master device (e.g., a microcontroller) controls communication, while one or more slave devices (e.g., sensors, displays, memory chips) respond.
  • Each slave has a unique address (7-bit or 10-bit) for identification.
  • Master initiates communication by sending the address and read/write commands.

To use I2C, I needed to know the unique I2C address for each device (in this case the VL53L1X). Addresses use hex (hexadecimal) numbers between 0x01 and 0x7F.

Hex numbers

Hex (Hexadecimal) is a base-16 number system (digits: 0-9 and A-F), for example:

  • Decimal (Base 10) → 0, 1, 2, ... 9, 10, 11, ...
  • Binary (Base 2) → 0000, 0001, 0010, ... 1001, 1010, 1011, ...
  • Hex (Base 16) → 0, 1, 2, ... 9, A, B, C, D, E, F, 10, 11, ...

c. I2C scanner

There are several ways to find the address, including reading the datasheet or using an I2C scanner. I could find the I2C adress using the I2C Scanner in the Arduino IDE with the code from this site.

  • Open a new Arduino sketch and paste the code and run it
  • Serial print
    → Scanning...
    → Found an I2C device at address 0x29
    → Done
    
    I2C_scanner

For some reason the board I was using with the Arduino IDE did not work with the I2C scanner, so thanks to some advice I changed the board from "Arduino Mbed OS RP2040 board to "Raspberry Pi Pico/RP2040/RP2050" by Earle F. Philhower, III.

  • Tools > Board > Board manager > Search for "RP2040"
  • Install Raspberry Pi Pico/RP2040/RP2050
  • Tools > Board > Raspberry Pi Pico/RP2040/RP2050 > Seeed XIAO RP2040

board_manager


d. Programming

I used the example code provided on the above page. The I2C address 0x29 is used to define the device to the microcontroller.

The sample code for the sensor uses a library.

  • Sketch > Include Library > Manage Libraries
  • Search "Adafruit_VL53L1X" and install it

VL53L1X

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include "Adafruit_VL53L1X.h"

#define IRQ_PIN 2
#define XSHUT_PIN 3

Adafruit_VL53L1X vl53 = Adafruit_VL53L1X(XSHUT_PIN, IRQ_PIN);

void setup() {
  Serial.begin(115200);
  while (!Serial) delay(10);

  Serial.println(F("Adafruit VL53L1X sensor demo"));

  Wire.begin();
  if (! vl53.begin(0x29, &Wire)) {
    Serial.print(F("Error on init of VL sensor: "));
    Serial.println(vl53.vl_status);
    while (1)       delay(10);
  }
  Serial.println(F("VL53L1X sensor OK!"));

  Serial.print(F("Sensor ID: 0x"));
  Serial.println(vl53.sensorID(), HEX);

  if (! vl53.startRanging()) {
    Serial.print(F("Couldn't start ranging: "));
    Serial.println(vl53.vl_status);
    while (1)       delay(10);
  }
  Serial.println(F("Ranging started"));

  // Valid timing budgets: 15, 20, 33, 50, 100, 200 and 500ms!
  vl53.setTimingBudget(50);
  Serial.print(F("Timing budget (ms): "));
  Serial.println(vl53.getTimingBudget());

  /*
  vl.VL53L1X_SetDistanceThreshold(100, 300, 3, 1);
  vl.VL53L1X_SetInterruptPolarity(0);
  */
}

void loop() {
  int16_t distance;

  if (vl53.dataReady()) {
    // new measurement for the taking!
    distance = vl53.distance();
    if (distance == -1) {
      // something went wrong!
      Serial.print(F("Couldn't get distance: "));
      Serial.println(vl53.vl_status);
      return;
    }
    Serial.print(F("Distance: "));
    Serial.print(distance);
    Serial.println(" mm");

    // data is read out, time for another reading!
    vl53.clearInterrupt();
  }
}

Measuring the distance betwee a duck. Seems pretty accurate.

3. Files

No files this week, code in the text.

Afterthoughts

  • I feel like learning about input devices expands possibilities.
  • I2C is interesting.