Part 2 — The TinkerPy Mega board and its pins
TinkerPy Mega (TinkerKit 2.0) uses an ESP32-C3 chip with WiFi and Bluetooth. It has 4 input pins (i1–i4), 8 output pins (o1–o8), one menu button and an OLED screen connector. Programs are written in TinkerPy Code (tinkerpy.com), compiled, tested in the Simulator and uploaded over WiFi or Bluetooth. Connect each module’s signal pin (S / OUT / DATA / IN) to the board pin, and its VCC and GND to the power and ground pins next to it on the board.
The menu button belongs to the board, not to programs: a program cannot read it (use a push button on an input pin instead). With firmware 2.22+, pressing it while a program runs pauses the program on the Home screen for 3 seconds; pressing it again within those 3 seconds opens the menu (TV Remote, Wireless devices, Debug / Test with OLED Emojis and Buzzer Tones, error codes, RESET board …), otherwise the program carries on by itself. From firmware 2.23, while a program runs that does not use the OLED, the board screen shows ▶ RUNNING and the project name.
2.1 Pin table
| Pin | Kind | Use it for | Fixed jobs (when that library is used) |
|---|---|---|---|
| i1 | input · analog | button, IR / PIR / touch sensor (ON/OFF) or LDR / soil / knob (analog_read 0–100 or voltage 0.00–3.30 V), DHT11, joystick X, TV remote | — |
| i2 | input · analog | same as i1; joystick Y | — |
| i3 | input · analog | same as i1; DHT11 (examples use i3); joystick switch | ultrasonic Trig (ultrasonic_TX) |
| i4 | input · analog | same as i1 | ultrasonic Echo (ultrasonic_RX) |
| o1, o2 | output · PWM | LED (with brightness), buzzer, servo, RGB LEDs, driver / relay input | — |
| o3 | output · PWM | same as o1 | robot: right motor forward |
| o4 | output · PWM | same as o1 | robot: right motor backward |
| o5 | output · PWM | same as o1 | robot: left motor forward |
| o6 | output · PWM | same as o1 | robot: left motor backward |
| o7 | output · PWM | same as o1 | I2C SDA — OLED / LCD / MPU6050 data |
| o8 | output · PWM | same as o1 | I2C SCL — OLED / LCD / MPU6050 clock |
- OLED, LCD, MPU6050 and the firmware 2.27 I2C sensors (RTC, pulse oximeter, BMP280, VL53L0X, TCS34725, APDS-9960) all share the I2C pins o7/o8 — they can be used together in one project, except two parts with the same address: RTC + MPU6050 (0x68), VL53L0X + TCS34725 (0x29).
- An output pin can also read a button/sensor:
o5 = set_function(active_low)insetup:, thenif o5 == ON:. - Free pins with a robot + display: outputs o1, o2 and inputs i1, i2 (and i3, i4 when there is no ultrasonic sensor).
2.2 Inputs: ON and OFF
i1 == ON means “the button is pressed / the sensor is triggered”. By default an input is active low
(ON when the signal pin is pulled to GND — how most buttons and IR / obstacle modules work). For a module whose output goes HIGH when it detects
something (many PIR and touch sensors), add i1 = set_function(active_high) in setup:.
2.3 Where each device goes (AI: use these when you pick pins)
| Device | Pin(s) | Setup line(s) |
|---|---|---|
| Push button, IR / PIR / touch / rain / sound sensor (digital) | any of i1–i4 (first free) | i1 = set_function(active_low) or active_high — an input with no setup line is active_low (Compile adds it) |
| LDR, soil moisture, potentiometer (analog 0–100) | i1–i4 | i1 = set_function(adc) — read with analog_read(i1) (0–100) or voltage(i1) (0.00–3.30 V). An ADC pin can’t be checked as ON/OFF; compare analog_read(i1) > 50 |
| Ultrasonic HC-SR04 | Trig i3, Echo i4 (fixed) | i3 = set_function(ultrasonic_TX), i4 = set_function(ultrasonic_RX) |
| DHT11 | one of i1–i4 (prefer i3, or i1 if the ultrasonic is used) | i3 = set_function(dht11) |
| MPU6050 | SDA o7, SCL o8 (fixed) | o7 = set_function(SDA), o8 = set_function(SCL) |
| RTC clock (DS3231 / DS1307) | SDA o7, SCL o8 (fixed) | import rtc + o7 = set_function(SDA), o8 = set_function(SCL) |
| Pulse oximeter (MAX30100 / MAX30102) | SDA o7, SCL o8 (fixed) | import pulse_oximeter + o7 = set_function(SDA), o8 = set_function(SCL) |
| BMP280 pressure | SDA o7, SCL o8 (fixed) | import bmp280 + o7 = set_function(SDA), o8 = set_function(SCL) |
| VL53L0X laser distance | SDA o7, SCL o8 (fixed) | import vl53l0x + o7 = set_function(SDA), o8 = set_function(SCL) |
| TCS34725 colour | SDA o7, SCL o8 (fixed) | import tcs34725 + o7 = set_function(SDA), o8 = set_function(SCL) |
| APDS-9960 gesture | SDA o7, SCL o8 (fixed) | import apds9960 + o7 = set_function(SDA), o8 = set_function(SCL) |
| GPS module (NEO-6M …) | any pin (its TX wire) | import gps + i1 = set_input(gps_rx) |
| GSM SIM800L (SMS, calls) | any pin for its TX wire, any output for its RX wire | import gsm + i2 = set_input(gsm_rx), o5 = set_output(gsm_tx) |
| OLED 0.96" (I2C) | SDA o7, SCL o8 (fixed) | those two + oled_begin() |
| 16x2 LCD (I2C) | SDA o7, SCL o8 (fixed) | those two + lcd_begin(0x27) (or 0x3F; lcd_begin() finds it) |
| Joystick (Mega) | X and Y on i1–i4, switch on any pin | i1 = set_function(joystick_x), i2 = set_function(joystick_y), i3 = set_function(joystick_swt) |
| TV remote receiver TSOP1838 | any pin | i1 = set_function(tv_remote) (the 29 keys are learnt on the board: Menu → TV Remote → Learn Remote) |
| LED | any free output | optional: o1 = set_function(output) |
| Buzzer | any free output | o1 = set_function(active_buzzer) (beeps) or passive_buzzer (real songs) |
| Servo SG90 / MG90 | any free output | o1 = set_function(servo) |
| Robot car (2 DC motors + driver) | o3–o6 (fixed) | the four motor_… lines (Part 4) |
| Small pump / fan / motor, relay module | any free output (through a driver / relay) | optional: o1 = set_function(output) |
| RGB LEDs WS2812B (1, 2, 3 or a strip) | any free output | rgb_begin(o1, number_of_leds) |
| Bluetooth / IoT (TinkerPy App) | no pin | Bluetooth: bluetooth_begin(); IoT: none (board connects to WiFi by itself) |
2.4 Timing
wait(ms)pauses the program (1000 ms = 1 second). Counters (c1.auto_increment), the clock, servo scans and RGB effects keep running in the background while the program waits.- The program runs
loop:again and again very fast. Bluetooth/IoT data is read once per pass.
2.5 Screens and sensors — practical tips
- OLED: each
oled_writefills one whole line, so a label and its number go on separate lines (e.g. line 1"Temp (C)"small, line 2 the number in large). Plan the 4 lines: medium / large use 2 lines each. For a label and a number on the same line use the 16x2 LCD:lcd_write(1, 1, "Temp:")+lcd_write(1, 7, t). - Update a screen with a
wait()(200–1000 ms) in the loop so it does not flicker. - Analog sensors (LDR, soil, knob) give 0–100. Modules differ (some soil sensors give a HIGH value when dry): tell the student to show the value on the screen in both conditions and then set the threshold.
- Time events (
if HH == 9 and MM == 0:) stay true for a whole minute: use a variable so the action happens once (if HH == 9 and MM == 0 and rang == 0:…rang = 1, and setrang = 0again whenMM != 0). - Clock on a screen: numbers show without a leading zero (9:5). Show HH, MM and SS on separate OLED lines, or on the LCD write
"0"first when the value is below 10:if MM < 10:lcd_write(1, 4, "0")+lcd_write(1, 5, MM). - Which screen? Use the OLED by default (big / bold / centred text). Use the 16x2 LCD when a label and a number must share a line.
- PIR motion sensors need about 30–60 seconds after power-on before they work reliably.
- DHT11: read it about every 2 seconds (
wait(2000)). Ultrasonic: about 2–400 cm, −1 when nothing is in range. - RTC clock: put
rtc_set_time(h, m, s)/rtc_set_date(d, m, y)in setup: — the board sets the clock once when the new code arrives (not at every power-on). - Pulse oximeter: a fingertip resting gently, no pressing, no moving;
heart_rate()/spo2()are 0 until a few beats are measured.heart_beat()is 1 at each beat. Learning only — never for medical decisions. - Gesture (APDS-9960): each swipe is seen in one pass of loop:, so compare it in the same pass (
if gesture() == "up":…elif gesture() == "down":) or store it first:g = gesture(). - Colour sensor: hold the colour 1–2 cm away; check
color_name()on the screen for your own papers first (lighting changes colours). - GPS works outdoors (a minute to find satellites);
gps_location()is text for a screen, an SMS or the app. GSM: a SIM800L needs its own 4 V, 2 A supply; send an SMS on a change (a button press, a new alarm), not every loop — the board sends the same SMS only once in 5 minutes.
2.6 Testing in the Simulator
In TinkerPy Code: Compile → Simulator → Start. A tile appears for each pin and device the program uses (more can be added with the edit / catalog button). Press or hold an input tile to press a button; drag sliders for analog pins, the ultrasonic distance, DHT11 temperature / humidity, MPU6050 tilt and turn (plus Shake / Fall / Step buttons) and the joystick. Firmware 2.27 sensors have their own tiles: the RTC clock (your computer’s time), the pulse oximeter (finger on / off, heart-rate and SpO₂ sliders), BMP280 (altitude, temperature), laser distance, the colour sensor (colour picker and swatches) and gesture (swipe buttons and a nearness slider). Firmware 2.28: a GPS tile (fix on / off, walk N / S / E / W, speed) and a GSM phone tile (SMS from the board appear there; send an SMS to the board or call it). The Bluetooth / IoT message box sends a letter, text, number, position or colour to the program, like the TinkerPy App. Output tiles show LEDs (brightness), servo angles, the robot, buzzer, RGB LEDs, OLED and LCD.
2.7 TinkerPy Mini (only if the user says so)
TinkerPy Mini (ESP8266) has only inputs i1, i2 (ultrasonic on i1/i2), outputs o1–o8 like Mega, no Bluetooth and no joystick. Everything else is the same.
Part 3 — Program structure and language rules
3.1 The parts of a program
| Part | What it is | Example |
|---|---|---|
| Comments | Notes for people. Start with #. The first lines say the project name. | # ===== Night Lamp ===== |
| Imports | One line per library (Part 4). They switch the library on in the editor. | import oled_display |
| Pin names | Optional friendly names for pins, written before setup:. Use the new name everywhere after. | o1 = set_name("lamp") then lamp = ON |
setup: | Runs once at power-on: pin settings, begin() calls, starting values. | setup: o2 = set_function(servo) |
loop: | Runs forever, top to bottom, again and again. (Code written after setup: without loop: also repeats — always write loop: for clarity.) | loop: o1 = ON |
3.2 Values
- Numbers: whole numbers −32768 … 32767, also negative (
-5) and hex (0x27); a number outside this range is a compile error, and a maths result outside it wraps round (32767 + 1gives −32768). Decimals (1.6) only as volts next tovoltage()(firmware 2.24; the board keeps millivolts, 1.6 → 1600). - ON / OFF: for inputs (pressed / not pressed) and outputs (full on / off).
1and0also work for outputs. - None: “nothing arrived” from the app / remote. Compare with
!= Noneor== None. - Text in double quotes
"Hello"— can go in a text variable (2.24), be joined with+, compared, shown and sent (rule 7 of 1.5). - Special read-only values:
HH,MM,SS(clock),c1…c8(counters), pinsi1…i4.
3.3 Variables
Create a variable by giving it a value: count = 0. Change it: count = count + 1. Up to 10 variables. A variable can hold
a number, a reading (d = distance()), a counter or clock value, or a character from the app (k = bluetooth_read_char(), then if k == "F":).
To remember something between loop passes (a score, a mode, “armed”), give the starting value in setup:.
Text variables (firmware 2.24): a variable that is given text holds text — up to 6 of them, 30 characters each, on top of the 10 number variables.
Join with + (numbers become digits). A message from the app or another board is there for one loop, so keep it like this:
setup:
TP00000002.connect()
oled_begin()
last = "-" # a text variable
loop:
if TP00000002.available() == 1:
last = TP00000002.read() # keep the newest message
t = dht_temperature(i3)
oled_write(1, left, small, regular, "Got: " + last)
oled_write(2, left, small, regular, "T: " + t + " C")
setup:
mode = 0 # remembered between passes
loop:
if i1 == ON:
mode = mode + 1 # next mode on each press
if mode > 2:
mode = 0
wait(300) # one press = one step
3.4 Maths, comparisons and logic
| Kind | Symbols | Example |
|---|---|---|
| Maths | + - * / % and brackets ( ) | avg = (a + b) / 2 |
| Compare | == != < > <= >= | if temp >= 30: |
| Logic | and or not | if i1 == ON and not (i2 == ON): |
| Random | random(a, b) | dice = random(1, 6) |
Write each comparison in full: if x > 1 and x < 5: (not 1 < x < 5).
3.5 Decisions and loops
| Form | Meaning |
|---|---|
if c: / elif c: / else: | Do the indented lines only when the condition is true (checked top to bottom). |
repeat(n): | Do the indented lines n times (n may be a variable). n = 0 or less: not at all. |
for i in range(n): | Do it n times with i = 0, 1 … n−1 (range(0): not at all). range() takes one value only. |
while c: | Repeat while the condition is true. Put a wait() inside when it waits for a sensor. |
break | Leave the nearest repeat / for / while early. |
loop:
# wait here until the button is pressed
while i1 == OFF:
wait(20)
repeat(3): # blink 3 times
o1 = ON
wait(200)
o1 = OFF
wait(200)
for i in range(10): # fade up: 0, 10, 20 … 90
o2 = pwm(i * 10)
wait(50)
3.6 Common mistakes → correct TinkerPy
| Wrong | Right |
|---|---|
o1 = 50 | o1 = pwm(50) |
count += 1 | count = count + 1 |
while True: | loop: (it already repeats forever) |
def blink(): | no functions — write the lines where needed, or use repeat |
print("hi") | oled_write(1, left, small, regular, "hi") or bluetooth_write("hi") |
time.sleep(1) | wait(1000) |
for i in range(0, 100, 10): | for i in range(10): and use i * 10 |
name = "Ravi" | text cannot go in a variable — write it directly: oled_write(1, left, small, regular, "Ravi") |
oled_write(1, 1, "Hi") (old form) | oled_write(1, left, small, regular, "Hi") |
count = 0 at the top of loop: | put count = 0 in setup: |
clock_start(…) / c1.set(…) in loop: | in setup: (in loop: only inside an if, e.g. a reset button) |
o3 = ON with import motor | robot_control("F", 80, "F", 80) |
if o1 == ON: | outputs cannot be read — keep a variable, e.g. light = 1 |
temp = 25.5 | whole numbers only: temp = 25 (decimals are only for volts: if voltage(i1) > 1.6:) |
Part 4 — Libraries: import line and setup lines
Every library a program uses needs its import line at the top and its setup lines inside setup:. Core features (inputs, outputs, analog_read, voltage, wait, random, if, loops, variables) need no import.
| Library | Import line | Setup lines (inside setup:) | Notes |
|---|---|---|---|
| Servo | import servo | o1 = set_function(servo) | One line per servo, on any free output. |
| Ultrasonic | import ultrasonic | i3 = set_function(ultrasonic_TX) i4 = set_function(ultrasonic_RX) | Fixed pins: Trig i3, Echo i4. distance() gives cm. |
| MPU6050 | import mpu6050 | o7 = set_function(SDA) o8 = set_function(SCL) | Fixed I2C pins. Keep the board still for the first second after power-on. |
| RTC clock (DS3231 / DS1307) | import rtc | o7 = set_function(SDA) o8 = set_function(SCL) | Real time and date, kept by the module’s battery. rtc_set_time / rtc_set_date in setup: set it once per new code. Address 0x68 — not together with the MPU6050. Firmware 2.27. |
| Pulse oximeter (MAX30100 / MAX30102) | import pulse_oximeter | o7 = set_function(SDA) o8 = set_function(SCL) | Heart rate and SpO₂ from a fingertip. For learning only. Address 0x57. Firmware 2.27. |
| BMP280 pressure | import bmp280 | o7 = set_function(SDA) o8 = set_function(SCL) | Air pressure (hPa), temperature, altitude (m). Address 0x76 / 0x77. Firmware 2.27. |
| VL53L0X laser distance | import vl53l0x | o7 = set_function(SDA) o8 = set_function(SCL) | 3 cm – 2 m in cm, 999 = nothing. Address 0x29 — not together with the TCS34725. Firmware 2.27. |
| TCS34725 colour | import tcs34725 | o7 = set_function(SDA) o8 = set_function(SCL) | Colour name and R G B. Address 0x29 — not together with the VL53L0X. Firmware 2.27. |
| APDS-9960 gesture | import apds9960 | o7 = set_function(SDA) o8 = set_function(SCL) | Swipes up / down / left / right, nearness, and colour. Address 0x39. Firmware 2.27. |
| GPS (NEO-6M …) | import gps | i1 = set_input(gps_rx) | Any pin: the GPS module's TX wire → this pin. Firmware 2.28. |
| GSM SIM800L | import gsm | i2 = set_input(gsm_rx) o5 = set_output(gsm_tx) | Any pins: module TX → gsm_rx, module RX → gsm_tx (an output). Own 4 V 2 A supply. Firmware 2.28. |
| OLED Display | import oled_display | o7 = set_function(SDA) o8 = set_function(SCL) oled_begin() | Fixed I2C pins. 4 text lines. |
| 16x2 LCD Display | import lcd_16x2 | o7 = set_function(SDA) o8 = set_function(SCL) lcd_begin(0x27) | Fixed I2C pins. 2 lines × 16 letters. |
| DHT11 | import dht11 | i3 = set_function(dht11) | Any input i1–i4; all DHT11 readings use the same pin. |
| Counter | import counter | c1.set(0) # optional starting value | Eight counters c1–c8. No pins. |
| Clock | import clock | clock_start(7, 0, 0) # hour, minute, second | A software clock. Set it to the real time. |
| Bluetooth | import bluetooth | bluetooth_begin() | TinkerPy Mega only. Talks to the TinkerPy App over Bluetooth. |
| IoT | import iot | (no setup line) | Talks to the TinkerPy App over the internet (board needs WiFi). |
| Robot (motors) | import motor | o3 = set_function(motor_right_forward) o4 = set_function(motor_right_backward) o5 = set_function(motor_left_forward) o6 = set_function(motor_left_backward) | Fixed pins o3–o6 through a motor driver. |
| RGB LED (1 LED board) | import rgb_single | rgb_begin(o1, 1) | A board with 1 RGB LED on any free output. |
| RGB LED (2 LED board) | import rgb_two | rgb_begin(o1, 2) | A board with 2 RGB LEDs. |
| RGB LED (3 LED board) | import rgb_three | rgb_begin(o1, 3) | A board with 3 RGB LEDs. |
| RGB LED strip | import rgb_strip | rgb_begin(o1, 30) | A strip: the number is how many LEDs it has (1–60). |
| Joystick (Mega) | import joystick | i1 = set_function(joystick_x) i2 = set_function(joystick_y) i3 = set_function(joystick_swt) | TinkerPy Mega. X/Y need i1–i4; the switch can be any pin; leave out what is not wired. |
| TV Remote (IR) | import tv_remote | i1 = set_function(tv_remote) | Any pin. Learn the remote’s keys on the board: Menu → TV Remote. |
| Wireless (board to board) | import wireless | TP00000002.connect() | No wires: the other board’s Device ID (Menu → Device info). Up to 6 boards; both boards’ programs must list each other. TinkerPy Mega, firmware 2.24. |
| Wireless IoT (over the internet) | import wireless_iot | TP00000002_iot.connect() | Boards anywhere, each on WiFi; tinkerpy.com passes the messages (a few seconds). Both programs must list each other. TinkerPy Mega, firmware 2.25. |
| Buzzer | import buzzer | o1 = set_function(active_buzzer) | Any free output. Use passive_buzzer for real songs. |
Part 5 — Function reference
Every function, keyword and value of TinkerPy, grouped like the editor’s palette. Each entry gives the exact form, what it does, what each value means and working examples. Examples show the important lines; a full program also needs the import and setup lines from Part 4 (TinkerPy Code adds missing ones when you press Compile). “Firmware” is the oldest board firmware that runs it.
5.1 Quick list of all functions
| Group | Forms |
|---|---|
| Pins, setup and outputs (core) | pin = set_input(device, mode) · pin = set_output(device[, value]) · pin = set_function(type) · pin = set_name("name") · input == ON / input == OFF · output = ON / OFF / 1 / 0 · output = pwm(value) · output = servo_angle(angle) |
| Control flow | Digital input condition if i1 == ON: · Analog input condition if analog_read(i1) < 30: / if voltage(i1) > 1.6: · if <condition>: · elif <condition>: · else: |
| Loop | repeat(count): · for <name> in range(count): · while condition: · break |
| Arithmetic and Logic | random(smallest, biggest) · a + b, a - b, a * b, a / b, a % b · a == b, a != b, a < b, a > b, a <= b, a >= b · a and b, a or b, not a |
| General Code | wait(milliseconds) · wifi("network", "password") |
| Analog Input | analog_read(input) · voltage(input) |
| Servo Function | servo_write_angle(output, angle) · servo_increment(output, step, max_angle, step_delay) · servo_decrement(output, step, min_angle, step_delay) · servo_increment_till(output, start_angle, end_angle, step, step_delay) · servo_decrement_till(output, start_angle, end_angle, step, step_delay) · servo_oscillate(output, home_angle, angle_a, angle_b, step, step_delay) · servo_flip(output, start_angle, end_angle, step, step_delay) · servo_scan_on(output, start_angle, end_angle, step, step_delay) · servo_scan_stop(output) · servo_scan_resume(output) · servo_read_angle(output) · servo_sync(output_a, output_b) · servo_sync_reverse(output_a, output_b) |
| Output Function | <output> = <value> · <output> = invert(<pin>) · output_increment(output, step, max_value, step_delay) · output_decrement(output, step, min_value, step_delay) · output_increment_till(output, start_value, end_value, step, step_delay) · output_decrement_till(output, start_value, end_value, step, step_delay) · output_flip(output, off_value, on_value) |
| Ultrasonic Function | distance() |
| DHT11 Function | dht_temperature(input) · dht_humidity(input) |
| MPU6050 Function | tilt_x() · tilt_y() · tilt_direction() · shake_detected() · motion_level() · fall_detected() · turn_angle() · reset_turn() · step_count() · reset_steps() · accel_x() · accel_y() · accel_z() · gyro_x() · gyro_y() · gyro_z() · mpu_temperature() |
| RTC Clock Function | rtc_hour() · rtc_minute() · rtc_second() · rtc_day() · rtc_month() · rtc_year() · rtc_weekday() · rtc_set_time(hour, minute, second) · rtc_set_date(day, month, year) |
| Pulse Oximeter Function | heart_rate() · spo2() · finger_detected() · heart_beat() |
| BMP280 Function | pressure() · bmp_temperature() · altitude() |
| Laser Distance Function | laser_distance() |
| Colour Sensor Function | color_name() · color_red() · color_green() · color_blue() · color_brightness() |
| Gesture Function | gesture() · proximity() |
| GPS Function | gps_found() · gps_satellites() · gps_speed() · gps_altitude() · gps_hour() · gps_minute() · gps_second() · gps_save_home() · gps_distance_home() · gps_location() |
| GSM Function | gsm_send_sms("number", message) · gsm_sms_received() · gsm_read_sms() · gsm_sms_number() · gsm_call("number") · gsm_hang_up() · gsm_ringing() · gsm_signal() |
| Buzzer Function | buzzer_play(output, "tone") · buzzer_play_background(output, "tone", repeat) · buzzer_stop_background(output) · is_buzzer_playing(output) |
| TV Remote Function | remote_read() |
| Joystick Function | joystick_x() · joystick_y() · joystick_direction() · joystick_pressed() |
| RGB LED Function | rgb_begin(output, number_of_leds) · rgb_color(output, led, "color", brightness) · rgb_flip(output, led, "color", brightness) · rgb_hex_color(output, led, r, g, b) · rgb_blink(output, led, "color", on_ms, off_ms) · rgb_breathe(output, led, "color", time_ms) · rgb_swap(output, led, "color", "color2", time_ms) · rgb_chase(output, led, "color", speed_ms) · rgb_wipe(output, led, "color", speed_ms) · rgb_twinkle(output, led, "color", speed_ms) · rgb_bar(output, led, "color", value, max) · rgb_heat(output, led, "color", value, low, high) |
| LCD Display Function | lcd_begin(address) · lcd_write(line, position, value) · lcd_clear() · lcd_clear_line(line) · lcd_scroll(line, text) · lcd_backlight(ON / OFF) |
| OLED Display Function | oled_begin(address) · oled_write(line, align, size, weight, value) · oled_clear() · oled_clear_line(line) · oled_eye_animation(name, repeat, speed, delay) · oled_emoji_animation(name, repeat, speed, delay) · oled_scroll(line, text) |
| Counter Function | c1.auto_increment(interval_ms) · c1.auto_decrement(interval_ms) · c1.increment() · c1.decrement() · c1.clear() · c1.pause() · c1.set(value) |
| Clock Function | clock_start(hours, minutes, seconds) · HH, MM, SS · clock_speed(real_ms_per_second) · clock_pause() · clock_resume() |
| Bluetooth & IoT Function | bluetooth_begin() · bluetooth_connected() · bluetooth_write(value) · bluetooth_write(a, b[, c]) · bluetooth_read_char() · bluetooth_read_string() · bluetooth_read_number() · x, y = bluetooth_read_position() · r, g, b = bluetooth_read_rgb() · set_device_password("1234") · iot_write(value) · iot_write(a, b[, c]) · iot_read_char() · iot_read_string() · iot_read_number() · x, y = iot_read_position() · r, g, b = iot_read_rgb() |
| Robot Function | robot_control(left_dir, left_speed, right_dir, right_speed) · motor_control_right(direction, speed) · motor_control_left(direction, speed) · robot_forward(speed) · robot_backward(speed) · robot_left(speed) · robot_right(speed) · robot_arc_left(speed) · robot_arc_right(speed) · robot_stop() |
5.2 Details and examples
Pins, setup and outputs (core)
Inside setup: — says what is connected to an input pin (the newer, clearer form of set_function). ON / OFF modules MUST give their mode: active_low (ON when the pin reads LOW — most modules) or active_high (ON when HIGH — PIR and touch, marked *). 0–100 modules use adc (i1–i4 only). dht11, tv_remote, ultrasonic_trig (i3), ultrasonic_echo (i4), gps_rx and gsm_rx (any pin, firmware 2.28) have no mode; gsm_tx is set with set_output(gsm_tx). The device name is for the editor and the simulator (it shows that module); the board runs it exactly like set_function, so no new firmware is needed. ON/OFF modules: push_button, general (switch), slide_switch, limit_switch, touch*, pir*, ir_sensor, sound, flame, vibration, reed, hall, tilt, rain, water, line_sensor, light_sensor, soil, lpg, alcohol, air_quality, joystick_swt. 0–100 modules: potentiometer, water_level, temperature, flex, joystick_x, joystick_y — and light_sensor, soil, lpg, alcohol, air_quality, general (these have both outputs: ON/OFF or adc).
pin | i1–i4 (or o1–o8 for an ON/OFF module used as an input), or its set_name name. |
device | What is connected — one of the names above (no quotes). |
mode | active_low · active_high · adc. |
setup:
i1 = set_input(pir, active_high)
loop:
if i1 == ON:
o1 = ON
else:
o1 = OFFsetup:
i2 = set_input(light_sensor, adc)
loop:
if analog_read(i2) < 30:
o1 = ON
else:
o1 = OFFsetup:
i3 = set_input(lpg, active_low)
o2 = set_output(buzzer)
loop:
if i3 == ON:
o2 = ON
else:
o2 = OFFInside setup: — says what is connected to an output pin (o1–o8). Outputs have no mode. Devices: led, bulb, laser, relay, motor, fan, pump, lock, vibration, buzzer (buzzer, passive = tunes), servo (servo, 90 = start angle), rgb_led (rgb_led, 8 = how many LEDs), right_motor_P / right_motor_N (o3/o4), left_motor_P / left_motor_N (o5/o6). With only ONE robot motor set up, its partner’s two pins are free for other things; P and N may be swapped on a motor’s own two pins when it turns the wrong way (robot_control and motor_control_right/left then drive it the right way). robot_control(...) needs both motors.
pin | o1–o8. |
device | What is connected (no quotes). |
value | servo: start angle 0–180 · rgb_led: number of LEDs 1–60 · buzzer: passive for tunes. Nothing for the others. |
setup:
o1 = set_output(fan)
o2 = set_output(servo, 90)
loop:
o1 = pwm(60)
o2 = servo_angle(45)setup:
o3 = set_output(rgb_led, 8)
loop:
rgb_color(o3, all, "rainbow", 80)setup:
o3 = set_output(right_motor_N)
o4 = set_output(right_motor_P)
o5 = set_output(led)
loop:
motor_control_right('F', 70)
o5 = ONInside setup: — the older form of set_input / set_output (still works). Types (no quotes): servo, dht11, output, active_low, active_high, active_buzzer, passive_buzzer, tv_remote, joystick_x, joystick_y, joystick_swt, ultrasonic_TX (i3), ultrasonic_RX (i4), SDA (o7), SCL (o8), motor_right_forward (o3), motor_right_backward (o4), motor_left_forward (o5), motor_left_backward (o6).
pin | i1–i4 or o1–o8 (or its set_name name). |
type | One of the types above. |
setup:
o2 = set_function(servo)
i3 = set_function(dht11)setup:
i1 = set_function(active_high)
loop:
if i1 == ON:
o1 = ON
else:
o1 = OFFsetup:
o5 = set_function(active_low)
loop:
if o5 == ON:
o1 = ON
else:
o1 = OFFGives a pin an easy name. Write it before setup:, then use the name everywhere instead of the pin. TinkerPy Code names each part added in the Circuit Connections window the same way — light_sensor_1, motion_sensor_1, servo_motor_1, led_2 … (rename it there if you like).
name | Letters, digits and _ , in quotes, starting with a letter. |
i1 = set_name("door")
o1 = set_name("alarm")
loop:
if door == ON:
alarm = ON
else:
alarm = OFFReads a digital input: ON when the button is pressed or the sensor is triggered. Use it in if / elif / while.
input | i1–i4, an o-pin set as active_low / active_high, or its set_name name. |
if i1 == ON:
o1 = ON
else:
o1 = OFFSwitches an output fully on or off (LED, buzzer, relay, driver).
output | o1–o8 or its set_name name. |
o1 = ON wait(500) o1 = OFF wait(500)
Sets an output’s power from 0 to 100 % — LED brightness, motor / fan speed. A variable or a sum can also be given directly: output = level.
value | 0–100 (a number, variable or sum). Out-of-range values are clamped. |
o1 = pwm(50)
o1 = pwm(analog_read(i1))
Moves a servo to an angle at once (same as servo_write_angle).
angle | 0–180 degrees (a number, variable or sum). |
setup:
o1 = set_function(servo)
loop:
if i1 == ON:
o1 = servo_angle(90)
else:
o1 = servo_angle(0)Control flow
Runs the indented lines below only when the condition is true.
condition | A comparison, like i1 == ON or count > 5. |
if i1 == ON:
o1 = pwm(100)
else:
o1 = 0if i1 == ON and i2 == ON:
o1 = pwm(100)count = 0
if count == 0:
o1 = pwm(100)Checked only if the earlier if (and any earlier elif) was false.
condition | A comparison, checked in order after the if. |
if i1 == ON:
o1 = pwm(100)
elif i2 == ON:
o1 = pwm(50)
else:
o1 = 0count = 5
if count > 10:
o1 = pwm(100)
elif count > 5:
o1 = pwm(50)
else:
o1 = 0if i1 == ON:
o1 = pwm(100)
elif i1 == OFF:
o1 = 0Runs when none of the if/elif conditions above were true.
if i1 == ON:
o1 = pwm(100)
else:
o1 = 0if i1 == ON:
o1 = pwm(100)
else:
o1 = 0
o2 = pwm(100)if i1 == ON:
o1 = pwm(100)
elif i2 == ON:
o1 = pwm(50)
else:
o1 = 0Loop
Runs the indented lines below a fixed number of times.
count | How many times to repeat (a number, up to 1000). |
repeat(3):
o1 = pwm(100)
wait(200)
o1 = 0
wait(200)count = 0
repeat(10):
count = count + 1
o1 = countrepeat(5):
repeat(2):
o1 = pwm(100)
wait(100)Like repeat, but also gives you a counter you can use inside the loop.
name | What to call the counter (e.g. i). |
count | How many times to loop (up to 1000). |
for i in range(5):
o1 = i * 20count = 0
for i in range(10):
count = count + i
o1 = countfor i in range(3):
o1 = pwm(100)
wait(300)
o1 = 0
wait(300)Repeats the indented lines again and again, as long as the condition is true. Needs firmware 2.15+.
condition | Checked before every round, e.g. i1 == ON or count < 10. |
while i1 == ON:
o1 = ON
wait(100)
o1 = OFFcount = 0
while count < 5:
count = count + 1
oled_write(1, left, small, regular, count)
wait(500)while distance() > 20:
robot_control("F", 60, "F", 60)
robot_control("S", 0, "S", 0)Leaves the loop it is in (while, repeat or for) straight away. Needs firmware 2.15+.
repeat(100):
o1 = ON
wait(100)
o1 = OFF
wait(100)
if i1 == ON:
breakn = 0
while n < 1000:
n = n + 1
if n == 7:
break
oled_write(1, left, small, regular, n)for i in range(10):
if bluetooth_read_char() == 'S':
break
o1 = i * 10
wait(300)Arithmetic and Logic
Gives a surprise whole number between the two numbers (both included). Needs firmware 2.15+.
smallest | The smallest number it can give, e.g. 1. |
biggest | The biggest number it can give, e.g. 6. |
if i1 == ON:
dice = random(1, 6)
oled_write(1, left, small, regular, dice)
wait(500)o1 = pwm(random(10, 100)) wait(200)
wait(random(1000, 5000)) o1 = ON
The five math operators — combine numbers and variables just like a calculator.
a = 5 + 3 o1 = a
a = 10 - 3 o1 = a
a = 4 * 2 o1 = a
a = 9 / 2 o1 = a
a = 9 % 2 o1 = a
Compares two values and gives a true/false answer — used inside if/elif to decide what runs.
if i1 == ON:
o1 = pwm(100)if i1 != ON:
o1 = OFFcount = 3
if count < 5:
o1 = pwm(100)count = 8
if count > 5:
o1 = pwm(100)count = 5
if count >= 5:
o1 = pwm(100)Combines two conditions into one, or flips a condition around — for when a single comparison isn’t enough.
if i1 == ON and i2 == ON:
o1 = pwm(100)if i1 == ON or i2 == ON:
o1 = pwm(100)if not i1 == ON:
o1 = pwm(100)if i1 == ON and not i2 == ON:
o1 = pwm(100)General Code
Pauses the program for a moment before continuing.
milliseconds | 1000 = one second. A number from 0 to 65535 (about 65 seconds); for longer, put the wait inside a repeat. |
o1 = pwm(100) wait(500) o1 = 0
repeat(5):
o1 = pwm(100)
wait(100)
o1 = 0
wait(100)wait(2000) o1 = pwm(100)
Lets your TinkerKit connect to a second WiFi network besides the default one.
network | The WiFi network name (SSID), in quotes. |
password | The WiFi password, in quotes. |
setup:
wifi("HomeNetwork", "mypassword123")
loop:
o1 = pwm(100)setup:
wifi("Classroom-2A", "school2024")setup:
wifi("MyPhoneHotspot", "hotspot99")
loop:
if i1 == ON:
o1 = pwm(100)Analog Input
Two ways to read an analog sensor (LDR, knob, soil sensor) on i1–i4: analog_read gives 0 – 100, voltage gives volts 0.00 – 3.30. Compile swaps one for the other when the numbers say so: if analog_read(i1) < 1.3: becomes if voltage(i1) < 1.3:, if voltage(i1) < 40: becomes if analog_read(i1) < 40:.
Reads how much voltage is on an input pin, as 0 to 100 — for a light sensor, a knob or a soil sensor. Set the pin as adc in setup:.
input | i1, i2, i3 or i4. That pin is then not used as an ON/OFF button. |
light = analog_read(i1)
if light < 30:
o1 = ON
else:
o1 = OFFo1 = pwm(analog_read(i2))
soil = analog_read(i1)
oled_write(1, left, small, regular, soil)
if soil < 40:
o2 = ONThe volts on an input pin, 0.00 to 3.30. Compare it with decimals like 1.6 (the only place decimals are allowed); it shows as 1.62 on the OLED / LCD, in Bluetooth / IoT / wireless messages and in joined text. pwm(voltage(i1)) treats 3.3 V as 100 %. Set the pin with i1 = set_function(adc) in setup:.
input | i1, i2, i3 or i4. That pin is then not used as an ON/OFF button. |
setup:
i1 = set_function(adc)
loop:
v = voltage(i1)
if v > 1.6:
o1 = ON
else:
o1 = OFFv = voltage(i1) oled_write(1, left, small, regular, "Volts: " + v)
o1 = pwm(voltage(i2))
Servo Function
Sets an output to a servo angle from 0–180°.
output | An output pin, e.g. o1. |
angle | 0–180. Out-of-range values are clamped automatically. |
servo_write_angle(o1, 90)
servo_write_angle(o1, 0) wait(500) servo_write_angle(o1, 180)
if i1 == ON:
servo_write_angle(o1, 180)
else:
servo_write_angle(o1, 0)Increases an output value once, by a fixed step — never past max_angle.
output | An output pin, e.g. o1. |
step | How much to increase by. |
max_angle | Won’t go above this, even if the step would push it further. |
step_delay | How long the move takes, in milliseconds. |
servo_increment(o1, 10, 180, 200)
repeat(5):
servo_increment(o1, 20, 100, 100)if i1 == ON:
servo_increment(o1, 5, 90, 50)Decreases an output value once, by a fixed step — never below min_angle.
output | An output pin, e.g. o1. |
step | How much to decrease by. |
min_angle | Won’t go below this, even if the step would push it further. |
step_delay | How long the move takes, in milliseconds. |
servo_decrement(o1, 10, 0, 200)
repeat(5):
servo_decrement(o1, 20, 20, 100)if i2 == ON:
servo_decrement(o1, 5, 10, 50)Jumps to start_angle, then climbs step by step up to end_angle.
output | An output pin, e.g. o1. |
start_angle | Where it begins — set immediately, before the climb starts. |
end_angle | Where it stops. If this isn’t above start_angle, nothing happens. |
step | How much to increase by each move. |
step_delay | Milliseconds to pause between steps. |
servo_increment_till(o1, 0, 100, 5, 50)
servo_increment_till(o1, 0, 180, 10, 30)
if i1 == ON:
servo_increment_till(o1, 20, 100, 10, 20)Jumps to start_angle, then descends step by step down to end_angle.
output | An output pin, e.g. o1. |
start_angle | Where it begins — set immediately, before the descent starts. |
end_angle | Where it stops. If this isn’t below start_angle, nothing happens. |
step | How much to decrease by each move. |
step_delay | Milliseconds to pause between steps. |
servo_decrement_till(o1, 100, 0, 5, 50)
servo_decrement_till(o1, 180, 0, 10, 30)
if i2 == ON:
servo_decrement_till(o1, 100, 20, 10, 20)Sweeps an output back and forth between two points, once, then returns to where it started.
output | An output pin, e.g. o1. |
home_angle | Where the sweep BOTH begins and ends — the servo returns here when finished. |
angle_a | It first sweeps to this angle… |
angle_b | …then to this one, and then back home. |
step | How much the value changes each move. |
step_delay | Milliseconds to pause between steps. |
servo_oscillate(o1, 0, 0, 180, 5, 20)
repeat(3):
servo_oscillate(o1, 90, 60, 120, 5, 20)if i1 == ON:
servo_oscillate(o1, 0, 0, 180, 10, 10)Each new press moves the servo the other way: start → end, then end → start. While the condition stays true it stays where it is. Needs firmware 2.14+.
output | An output pin with a servo, e.g. o1. |
start_angle | Where it rests at power-on, and where it returns on every second press. |
end_angle | Where the first press moves it to. |
step | Degrees per move — bigger is faster. |
step_delay | Milliseconds to pause between steps. |
if i1 == ON:
servo_flip(o1, 0, 90, 5, 20)if distance() < 10:
servo_flip(o2, 0, 180, 10, 15)if bluetooth_read_char() == 'D':
servo_flip(o1, 20, 160, 10, 10)Starts a servo sweeping back and forth forever, on its own — once started it keeps sweeping in the background, even while the rest of the code is busy or inside a wait().
output | An output pin, e.g. o1. |
start_angle | One end of the sweep. |
end_angle | The other end of the sweep. |
step | How much the angle changes each move. |
step_delay | Milliseconds to pause between steps — controls the sweep speed. |
setup:
servo_scan_on(o1, 0, 180, 5, 20)setup:
servo_scan_on(o1, 0, 180, 5, 20)
loop:
wait(3000)
o2 = pwm(100)if i1 == ON:
servo_scan_on(o1, 0, 180, 5, 20)setup:
servo_scan_on(o1, 0, 180, 5, 20)
servo_scan_on(o2, 30, 150, 10, 15)Freezes a servo_scan_on(...) sweep exactly where it currently is — the servo stops moving, but remembers its position and direction.
output | The output pin whose scan should freeze, e.g. o1 — must already be scanning via servo_scan_on(...). |
if i1 == ON:
servo_scan_stop(o1)setup:
servo_scan_on(o1, 0, 180, 5, 20)
loop:
wait(2000)
servo_scan_stop(o1)if distance() < 10:
servo_scan_stop(o1)Continues a servo_scan_on(...) sweep that was frozen with servo_scan_stop(...) — picks up from the exact angle and direction it stopped at, not from the beginning.
output | The output pin whose scan should continue, e.g. o1 — a no-op if that pin was never scanning in the first place. |
if i1 == ON:
servo_scan_stop(o1)
else:
servo_scan_resume(o1)setup:
servo_scan_on(o1, 0, 180, 5, 20)
loop:
wait(2000)
servo_scan_stop(o1)
wait(2000)
servo_scan_resume(o1)if i2 == ON:
servo_scan_resume(o2)Reads a servo’s CURRENT angle (0–180) — whatever it actually is right now, however it got there: a plain angle set with servo_angle(...), still mid-way through an increment/decrement/oscillate, or a servo_scan_on(...) sweep that’s either actively running or frozen with servo_scan_stop(...).
output | The output pin the servo is on, e.g. o1. |
a = servo_read_angle(o1)
if servo_read_angle(o1) > 90:
o2 = ON
else:
o2 = OFFsetup:
servo_scan_on(o1, 0, 180, 5, 20)
loop:
wait(2000)
servo_scan_stop(o1)
a = servo_read_angle(o1)Links two servos so they always move together — from this point on, ANY way either one’s angle changes (servo_angle(...), increment/decrement/oscillate, or a servo_scan_on(...) sweep) is mirrored onto the other automatically. Immediately matches the second pin to the first’s current angle when called.
output_a | The first servo’s output pin, e.g. o1. |
output_b | The second servo’s output pin, e.g. o2 — must be different from output_a. |
setup:
servo_sync(o1, o2)
loop:
o1 = servo_angle(90)setup:
servo_sync(o1, o2)
servo_scan_on(o1, 0, 180, 5, 20)if i1 == ON:
servo_sync(o1, o2)Links two servos to always move OPPOSITE each other — the second pin’s angle is kept at exactly 180 minus the first’s, through any of the same mechanisms servo_sync(...) covers (a direct set, increment/decrement/oscillate, or a scan). Useful for a pair like left/right legs or symmetric arms that should mirror in opposite directions.
output_a | The first servo’s output pin, e.g. o1. |
output_b | The second servo’s output pin, e.g. o2 — must be different from output_a. |
setup:
servo_sync_reverse(o1, o2)
loop:
o1 = servo_angle(30)setup:
servo_sync_reverse(o1, o2)
loop:
servo_oscillate(o1, 90, 60, 120, 5, 20)if i1 == ON:
servo_sync_reverse(o1, o2)Output Function
Sets a PWM output to a value from 0–100.
output | An output pin, e.g. o1, or an alias for one. |
value | 0–100 (percent power). Out-of-range values are clamped automatically. |
o1 = pwm(100)
o1 = pwm(50)
count = 75 o1 = count
The opposite ON / OFF of a digital pin: ON when that pin is OFF, OFF when it is ON. Digital pins only — not a pwm(), servo, buzzer, RGB or analog pin. Reading an output (invert(o2)) needs board firmware 2.26; an input follows its wiring (active_low / active_high). For a variable, use not x.
pin | A digital pin or its name: an output like o2, or an input like i1. |
o1 = invert(o2)
o1 = invert(i1)
Increases a general output value once, by a fixed step — never past max_value.
output | An output pin, e.g. o1. |
step | How much to increase by. |
max_value | Won’t go above this, even if the step would push it further (0–100). |
step_delay | How long the move takes, in milliseconds. |
output_increment(o1, 10, 100, 200)
repeat(5):
output_increment(o1, 20, 80, 100)if i1 == ON:
output_increment(o1, 5, 90, 50)Decreases a general output value once, by a fixed step — never below min_value.
output | An output pin, e.g. o1. |
step | How much to decrease by. |
min_value | Won’t go below this, even if the step would push it further (0–100). |
step_delay | How long the move takes, in milliseconds. |
output_decrement(o1, 10, 0, 200)
repeat(5):
output_decrement(o1, 20, 20, 100)if i2 == ON:
output_decrement(o1, 5, 10, 50)Jumps to start_value, then climbs step by step up to end_value.
output | An output pin, e.g. o1. |
start_value | Where it begins — set immediately, before the climb starts (0–100). |
end_value | Where it stops. If this isn’t above start_value, nothing happens. |
step | How much to increase by each move. |
step_delay | Milliseconds to pause between steps. |
output_increment_till(o1, 0, 100, 5, 50)
output_increment_till(o1, 20, 80, 10, 30)
if i1 == ON:
output_increment_till(o1, 20, 100, 10, 20)Jumps to start_value, then descends step by step down to end_value.
output | An output pin, e.g. o1. |
start_value | Where it begins — set immediately, before the descent starts (0–100). |
end_value | Where it stops. If this isn’t below start_value, nothing happens. |
step | How much to decrease by each move. |
step_delay | Milliseconds to pause between steps. |
output_decrement_till(o1, 100, 0, 5, 50)
output_decrement_till(o1, 80, 20, 10, 30)
if i2 == ON:
output_decrement_till(o1, 100, 20, 10, 20)Flips an output between two values each time this line runs: first on_value, then off_value, then on_value again…
output | An output pin, e.g. o1. |
off_value | The “off” value (0–100), e.g. 0. |
on_value | The “on” value (0–100), e.g. 100 — used the FIRST time the line runs. |
output_flip(o1, 0, 100)
output_flip(o1, 20, 60)
if i1 == ON:
output_flip(o1, 0, 100)Ultrasonic Function
Reads the ultrasonic sensor’s distance in cm. Its pins are fixed (i3/i4 on TinkerPy Mega, i1/i2 on Mini), so no pin is needed.
d = distance()
if d < 10:
o1 = pwm(100)d = distance() count = d o1 = pwm(100)
if distance() > 50:
o1 = 0
else:
o1 = pwm(100)DHT11 Function
Reads the DHT11 sensor’s temperature in °C.
input | Which input the DHT11 is wired to, e.g. i3. |
t = dht_temperature(i3)
if dht_temperature(i3) > 30:
o1 = pwm(100)t = dht_temperature(i3)
if t < 15:
o2 = pwm(100)Reads the DHT11 sensor’s humidity in %.
input | Which input the DHT11 is wired to — the same one as dht_temperature. |
h = dht_humidity(i3)
if dht_humidity(i3) > 70:
o1 = pwm(100)h = dht_humidity(i3)
if h < 30:
o2 = pwm(100)MPU6050 Function
How much the front edge (the X arrow on the sensor) is tilted: up = +, down = −, from −90 to 90 degrees.
t = tilt_x() oled_write(1, left, small, regular, t)
if tilt_x() < -20:
o1 = ON
else:
o1 = OFFif tilt_x() > 45:
iot_write("Lid is open")How much it leans sideways: right side down = +, left side down = −, from −90 to 90 degrees.
if tilt_y() > 20:
o2 = ON
else:
o2 = OFFt = tilt_y() oled_write(2, left, small, regular, t)
if tilt_y() < -20 or tilt_y() > 20:
o1 = ONWhich way it is tipped: 'F' forward (front down), 'B' back, 'L' left, 'R' right, or 'N' flat (less than 15°).
d = tilt_direction()
if d == 'F':
robot_control("F", 80, "F", 80)
elif d == 'N':
robot_control("S", 0, "S", 0)if tilt_direction() == 'L':
o1 = ON
else:
o1 = OFFif tilt_direction() == 'N':
oled_write(1, left, small, regular, "Level!")1 once after the sensor is shaken, then 0 until the next shake.
if shake_detected() == 1:
o1 = ON
wait(1000)
o1 = OFFif shake_detected() == 1:
c1.increment()
oled_write(1, left, small, regular, c1)if shake_detected() == 1:
iot_write("Shaken!")How much it is moving right now, from 0 (still) to 100 (lots of movement).
if motion_level() > 30:
o1 = ON
else:
o1 = OFFoled_write(1, left, small, regular, motion_level())
o1 = motion_level()
1 once after the sensor falls (free fall), then 0.
if fall_detected() == 1:
o1 = ON
iot_write("Fall detected!")if fall_detected() == 1:
oled_write(1, left, small, regular, "Oops! I fell")if fall_detected() == 1:
c1.increment()Degrees it has turned (spun flat, like a robot turning) since reset_turn(): right = +, left = −. It slowly drifts — reset it before each turn.
setup:
reset_turn()
loop:
robot_control("F", 60, "B", 60)
repeat(300):
if turn_angle() >= 90:
robot_control("S", 0, "S", 0)
wait(10)oled_write(1, left, small, regular, turn_angle())
if turn_angle() < -45:
o1 = ONSets turn_angle() back to 0 — call it just before a turn.
setup:
reset_turn()if i1 == ON:
reset_turn()setup:
reset_turn()
loop:
wait(2000)
oled_write(1, left, small, regular, turn_angle())How many steps were counted (carry the sensor while walking) since reset_steps().
oled_write(1, left, small, regular, step_count())
if step_count() >= 100:
o1 = ONif step_count() >= 1000:
iot_write("1000 steps!")Sets step_count() back to 0.
setup:
reset_steps()if i1 == ON:
reset_steps()if step_count() >= 50:
o1 = ON
reset_steps()Raw acceleration along X in 1/100 g (100 = gravity). For older students.
oled_write(1, left, small, regular, accel_x())
a = accel_x()
if a > 50:
o1 = ONiot_write("x")
oled_write(2, left, small, regular, accel_x())Raw acceleration along Y in 1/100 g (100 = gravity).
oled_write(2, left, small, regular, accel_y())
if accel_y() < -50:
o1 = ONa = accel_y() oled_write(1, left, small, regular, a)
Raw acceleration along Z in 1/100 g — about 100 when lying flat, about −100 upside down.
if accel_z() < 0:
o1 = ONoled_write(3, left, small, regular, accel_z())
if accel_z() > 90:
oled_write(1, left, small, regular, "Flat")How fast it rotates around X, in degrees per second.
oled_write(1, left, small, regular, gyro_x())
if gyro_x() > 100:
o1 = ONg = gyro_x() oled_write(2, left, small, regular, g)
How fast it rotates around Y, in degrees per second.
oled_write(1, left, small, regular, gyro_y())
if gyro_y() < -100:
o1 = ONg = gyro_y() oled_write(2, left, small, regular, g)
How fast it spins flat (around Z), in degrees per second.
if gyro_z() > 100:
o1 = ON
else:
o1 = OFFoled_write(1, left, small, regular, gyro_z())
if gyro_z() > 200 or gyro_z() < -200:
iot_write("Spinning!")The temperature of the MPU6050 chip in °C (a little warmer than the room).
oled_write(1, left, small, regular, mpu_temperature())
if mpu_temperature() > 40:
o1 = ONt = mpu_temperature() oled_write(2, left, small, regular, t)
RTC Clock Function
A real-time clock module (any DS3231 or DS1307) on o7 (SDA) / o8 (SCL). It keeps the time with its own small battery, even when the board is off. Set it once with rtc_set_time / rtc_set_date in setup: — the board sets the clock only when that new code arrives, not at every power-on. Address 0x68 (same as the MPU6050 — use one of them). Use a 3.3 V DS3231 board: “Tiny RTC” DS1307 boards need 5 V and put 5 V on the I2C wires, which the board’s pins can’t take.
The hour from the RTC clock module, 0 to 23 (2 PM = 14). The module keeps time even when the board is off (it has a small battery).
h = rtc_hour() oled_write(1, left, small, regular, h)
if rtc_hour() >= 18:
o1 = ON
else:
o1 = OFFif rtc_hour() == 7 and rtc_minute() == 0:
buzzer_play(o2, "school_bell")The minute from the RTC clock, 0 to 59.
oled_write(2, left, small, regular, rtc_minute())
if rtc_minute() == 30:
o1 = ONm = rtc_minute()
if m < 10:
o2 = ONThe second from the RTC clock, 0 to 59.
oled_write(3, left, small, regular, rtc_second())
if rtc_second() % 2 == 0:
o1 = ON
else:
o1 = OFFif rtc_second() == 0:
o2 = ON
wait(200)
o2 = OFFThe day of the month, 1 to 31.
oled_write(1, left, small, regular, rtc_day())
if rtc_day() == 1:
o1 = ONd = rtc_day() m = rtc_month() oled_write(2, left, small, regular, d)
The month, 1 (January) to 12 (December).
oled_write(2, left, small, regular, rtc_month())
if rtc_month() == 12 and rtc_day() == 25:
rgb_color(o1, all, "red", 80)if rtc_month() >= 4 and rtc_month() <= 5:
o1 = ONThe year, like 2026.
oled_write(3, left, small, regular, rtc_year())
y = rtc_year() oled_write(1, left, small, regular, y)
if rtc_year() < 2026:
oled_write(2, left, small, regular, "Set the clock")The day of the week: 1 Monday, 2 Tuesday … 6 Saturday, 7 Sunday.
if rtc_weekday() >= 6:
o1 = OFFd = rtc_weekday() oled_write(4, left, small, regular, d)
if rtc_weekday() == 1 and rtc_hour() == 8:
o1 = ONSets the RTC clock's time. Put it in setup: — the board sets the clock ONCE when this new code reaches it (not again at every power-on), so the clock keeps running from there. To set it again, change the numbers and upload again.
hour | 0-23 |
minute | 0-59 |
second | 0-59 |
setup:
rtc_set_time(14, 5, 0)
loop:
oled_write(1, left, small, regular, rtc_minute())setup:
rtc_set_time(7, 30, 0)setup:
rtc_set_time(0, 0, 0)
rtc_set_date(31, 12, 2026)Sets the RTC clock's date, once when the new code reaches the board (like rtc_set_time). The year can be 2026 or 26.
day | 1-31 |
month | 1-12 |
year | 2000-2099 (or 0-99) |
setup:
rtc_set_date(16, 10, 2026)
rtc_set_time(9, 0, 0)setup:
rtc_set_date(1, 1, 27)setup:
rtc_set_date(29, 2, 2028)Pulse Oximeter Function
A pulse oximeter module (any MAX30100 or MAX30102) on o7 / o8, address 0x57. Rest a fingertip gently on it and keep still; the first numbers come after a few seconds. For learning only — not a medical device. A MAX30102 board is the easy choice; green GY-MAX30100 boards pull the I2C wires to 1.8 V and may not work.
Heart beats a minute from the finger on the sensor, or 0 when there is no finger (or it is still measuring — it takes a few seconds). For learning only, not for medical use.
oled_write(1, left, small, regular, heart_rate())
if heart_rate() > 100:
o1 = ON
else:
o1 = OFFb = heart_rate()
if b > 0 and b < 60:
o2 = ONHow much oxygen the blood carries, in % (usually 95-100), or 0 when there is no finger. For learning only.
oled_write(2, left, small, regular, spo2())
if finger_detected() == 1 and spo2() < 94:
o2 = ONs = spo2() oled_write(3, left, small, regular, s)
1 while a finger is on the sensor, else 0.
if finger_detected() == 0:
oled_write(1, left, small, regular, "Put a finger on")if finger_detected() == 1:
o1 = ON
else:
o1 = OFFif finger_detected() == 1:
oled_write(1, left, small, regular, heart_rate())1 for one loop at every heart beat, else 0 — use it to blink or beep with the heart.
if heart_beat() == 1:
o1 = ON
wait(100)
o1 = OFFif heart_beat() == 1:
buzzer_play(o2, "beep")if heart_beat() == 1:
rgb_color(o1, all, "red", 80)
else:
rgb_color(o1, all, "red", 5)BMP280 Function
A BMP280 (or BME280) air-pressure module on o7 / o8, address 0x76 or 0x77.
The air pressure in hPa (about 1013 at sea level, lower up a hill).
oled_write(1, left, small, regular, pressure())
if pressure() < 1000:
o1 = ONp = pressure()
if p > 1020:
o2 = ONThe temperature from the BMP280 in °C.
oled_write(2, left, small, regular, bmp_temperature())
if bmp_temperature() > 35:
o2 = ON
else:
o2 = OFFt = bmp_temperature() oled_write(3, left, small, regular, t)
How high you are above the sea in metres, worked out from the air pressure (it changes a little with the weather).
oled_write(3, left, small, regular, altitude())
start = altitude()
if altitude() > start + 3:
o1 = ONif altitude() > 900:
o1 = ONLaser Distance Function
A VL53L0X laser distance (time-of-flight) sensor on o7 / o8, address 0x29: 3 cm to about 2 m, more exact than ultrasonic. 999 = nothing in range. It shares 0x29 with the TCS34725 — use one of them (for colour too, use an APDS-9960).
The distance to the object in front of the laser sensor, in cm (3 to about 200). 999 = nothing in range. More exact than ultrasonic, and works on soft things too.
oled_write(1, left, small, regular, laser_distance())
if laser_distance() < 15:
o1 = ON
else:
o1 = OFFd = laser_distance()
if d < 30:
o1 = ON
o2 = OFFColour Sensor Function
A TCS34725 colour sensor (address 0x29) or an APDS-9960 (0x39) on o7 / o8. Hold the colour 1–2 cm from it with its light on. color_name() gives one of: "red" "orange" "yellow" "green" "blue" "purple" "pink" "white" "black".
The colour in front of the sensor — compare it with a colour name: "red" "orange" "yellow" "green" "blue" "purple" "pink" "white" "black".
if color_name() == "red":
o1 = ON
else:
o1 = OFFc = color_name()
if c == "green":
o2 = servo_angle(90)
elif c == "blue":
o2 = servo_angle(0)if color_name() == "black":
o1 = OFF
else:
o1 = ONHow much red the sensor sees, 0-255 (the strongest colour is always 255).
oled_write(1, left, small, regular, color_red())
rgb_hex_color(o1, all, color_red(), color_green(), color_blue())
if color_red() == 255 and color_green() < 100:
o1 = ONHow much green the sensor sees, 0-255.
oled_write(2, left, small, regular, color_green())
if color_green() == 255:
o2 = ON
else:
o2 = OFFg = color_green() oled_write(4, left, small, regular, g)
How much blue the sensor sees, 0-255.
oled_write(3, left, small, regular, color_blue())
if color_blue() > 200:
o3 = ONb = color_blue() oled_write(4, left, small, regular, b)
How much light comes back to the colour sensor, 0-100 (dark = small).
if color_brightness() < 10:
o1 = ONoled_write(1, left, small, regular, color_brightness())
if color_brightness() > 80:
o2 = ONGesture Function
An APDS-9960 gesture sensor on o7 / o8, address 0x39. Swipe a hand 5–10 cm above it. Each swipe is seen in ONE pass of loop: (then None). It is also a colour sensor (the colour functions above).
The hand swipe just made over the sensor: compare it with "up" "down" "left" "right". It gives None when there is no new swipe — each swipe is seen in ONE loop.
if gesture() == "left":
o1 = ON
elif gesture() == "right":
o1 = OFFg = gesture()
if g == "up":
c1.increment()
elif g == "down":
c1.decrement()if gesture() == "up":
o1 = servo_angle(180)
elif gesture() == "down":
o1 = servo_angle(0)How near a hand (or object) is to the sensor: 0 far away … 100 very close (about 10 cm range).
oled_write(1, left, small, regular, proximity())
if proximity() > 60:
o1 = ON
else:
o1 = OFFp = proximity() oled_write(2, left, small, regular, p)
GPS Function
A GPS module (NEO-6M or similar, 9600 baud) — its TX wire goes to ANY pin, set in setup: as gps_rx: i1 = set_input(gps_rx) (TinkerPy Code adds the line on Compile, named gps_rx_1). Outdoors it needs about a minute to find satellites. Firmware 2.28.
1 when the GPS knows where it is (it has a fix), else 0. Outdoors it takes about a minute after power-on.
if gps_found() == 1:
o1 = ON
else:
o1 = OFFif gps_found() == 0:
oled_write(1, left, small, regular, "Searching...")f = gps_found() oled_write(2, left, small, regular, f)
How many satellites the GPS can see (4 or more is good).
oled_write(1, left, small, regular, gps_satellites())
if gps_satellites() < 4:
o2 = ONs = gps_satellites() bluetooth_write(s)
How fast it is moving, in km/h.
oled_write(1, left, small, regular, gps_speed())
if gps_speed() > 40:
buzzer_play(o2, "beep")s = gps_speed()
if s == 0:
o1 = ONHeight above the sea in metres.
oled_write(2, left, small, regular, gps_altitude())
if gps_altitude() > 500:
o1 = ONa = gps_altitude() iot_write(a)
The hour from the satellites, in Indian time (0–23). Very exact — no clock to set.
oled_write(1, left, small, regular, gps_hour())
if gps_hour() >= 18:
o1 = ONh = gps_hour() m = gps_minute() oled_write(2, left, small, regular, h)
The minute from the satellites, 0–59.
oled_write(2, left, small, regular, gps_minute())
if gps_hour() == 7 and gps_minute() == 0:
o1 = ONm = gps_minute()
if m < 5:
o2 = ONThe second from the satellites, 0–59.
oled_write(3, left, small, regular, gps_second())
if gps_second() % 2 == 0:
o1 = ON
else:
o1 = OFFs = gps_second() oled_write(4, left, small, regular, s)
Remembers where it is now as "home" (put it in setup:). If the GPS has no fix yet, home is saved as soon as it finds one.
gps_save_home()
if i1 == ON:
gps_save_home()gps_save_home() oled_write(1, left, small, regular, "Home saved")
How many metres it is from home (gps_save_home()). 0 until home is saved and the GPS has a fix.
oled_write(1, left, small, regular, gps_distance_home())
if gps_distance_home() > 100:
buzzer_play(o2, "alarm")d = gps_distance_home()
if d < 10:
o1 = ONWhere it is, as text: "12.91416,74.85603" (latitude,longitude — paste it into Google Maps). Empty text (None) until the GPS has a fix. Show it, store it or send it by SMS / Bluetooth / IoT.
oled_write(1, left, small, regular, gps_location())
loc = gps_location() bluetooth_write(loc)
if i1 == ON:
gsm_send_sms("+919876543210", "Help! I am at " + gps_location())GSM Function
A SIM800L GSM module with a SIM card — its TX wire goes to a pin set as gsm_rx (any pin) and its RX wire to an output set as gsm_tx: i2 = set_input(gsm_rx), o5 = set_output(gsm_tx). It needs its own 4 V, 2 A power supply (not the board's 3.3 V or 5 V pin), with the grounds joined. Messages are up to 30 letters. The same SMS is sent once in 5 minutes, at most one SMS every 10 seconds. Firmware 2.28.
Sends an SMS. The number in quotes (with +91 if you like), the message is text, a number or a join like "Temp " + t (up to 30 letters). The same SMS is not sent again for 5 minutes, and at most one SMS every 10 seconds — SMS cost money.
number | The phone number in quotes, e.g. "+919876543210" — or gsm_sms_number() to reply. |
message | Text, a number, a text variable or a join. |
if i1 == ON:
gsm_send_sms("+919876543210", "Door open")t = dht_temperature(i3)
if t > 40:
gsm_send_sms("+919876543210", "Too hot: " + t)if gsm_read_sms() == "STATUS":
gsm_send_sms(gsm_sms_number(), "All OK")1 for one loop when an SMS arrives, else 0.
if gsm_sms_received() == 1:
buzzer_play(o2, "beep")if gsm_sms_received() == 1:
oled_write(1, left, small, regular, gsm_read_sms())if gsm_sms_received() == 1:
c1.increment()The text of the SMS that just arrived (for one loop), or None. Compare it: gsm_read_sms() == "ON" (capitals count).
if gsm_read_sms() == "ON":
o1 = ON
elif gsm_read_sms() == "OFF":
o1 = OFFmsg = gsm_read_sms()
if msg != None:
oled_write(2, left, small, regular, msg)if gsm_read_sms() == "WHERE":
gsm_send_sms(gsm_sms_number(), gps_location())Who sent the SMS that just arrived (their number, as text) — use it to reply.
if gsm_sms_received() == 1:
gsm_send_sms(gsm_sms_number(), "Got it")if gsm_sms_number() == "+919876543210":
o1 = ONn = gsm_sms_number() oled_write(1, left, small, regular, n)
Calls a phone. The same number is not called again for 30 seconds.
number | The phone number in quotes, or gsm_sms_number(). |
if i1 == ON:
gsm_call("+919876543210")if analog_read(i2) > 70:
gsm_call("+919876543210")if gsm_read_sms() == "CALL ME":
gsm_call(gsm_sms_number())Ends a call, or stops a phone that is ringing.
if gsm_ringing() == 1:
gsm_hang_up()if i2 == ON:
gsm_hang_up()gsm_call("+919876543210")
wait(20000)
gsm_hang_up()1 while someone is calling the board, else 0.
if gsm_ringing() == 1:
o1 = ON
else:
o1 = OFFif gsm_ringing() == 1:
o2 = ON
gsm_hang_up()r = gsm_ringing() oled_write(1, left, small, regular, r)
The mobile signal, 0 (none) to 100 (full).
oled_write(1, left, small, regular, gsm_signal())
if gsm_signal() < 20:
o2 = ONs = gsm_signal() bluetooth_write(s)
Buzzer Function
Plays a tone or a song on the buzzer; the next line runs when it ends. Simple: beep, double_beep, triple_beep, long_beep, chirp_up, chirp_down, success, error, notify, start_up. Alerts: warning, alarm, ambulance, police, fire_truck, school_bell, school_long_bell, door_bell, countdown, quiz_wrong, phone_ring, marimba_ring, message_beep, retro_phone. Tunes: happy_birthday, jingle_bells, jana_gana_mana, twinkle_twinkle, ode_to_joy, mary_lamb, old_macdonald, merry_christmas. Tunes need a PASSIVE buzzer (an active one just beeps the rhythm). Listen to them all on the board: Menu > Buzzer Tones.
output | The pin the buzzer is on, e.g. o1. |
tone | Which tone or song, in quotes, e.g. "school_bell". |
if i1 == ON:
buzzer_play(o1, "door_bell")setup:
clock_start(8, 59, 50)
loop:
if HH == 9 and MM == 0 and SS == 0:
buzzer_play(o1, "school_long_bell")setup:
o2 = set_function(passive_buzzer)
buzzer_play(o2, "happy_birthday")
wait(3000)Starts a tone or song and the program goes on at once (it does not wait). repeat is optional: 1 (or 0) = once, 3 = three times with a 0.5 s gap. If the same tone is already playing on that buzzer it just keeps playing, so you can call it on every pass of loop: for an alarm that sounds while something is true; a different tone stops the old one. One tune plays at a time on the board. Same tones as buzzer_play. TinkerPy Mega, board firmware 2.29.
output | The pin the buzzer is on, e.g. buzzer (or o1). |
tone | Which tone or song, in quotes, e.g. "alarm". |
repeat | Optional: how many times to play it (0 or 1 = once). 0.5 s between the plays. |
o1 = set_name("buzzer")
loop:
if i1 == ON:
buzzer_play_background(buzzer, "alarm")o1 = set_name("buzzer")
loop:
if i2 == ON:
buzzer_play_background(buzzer, "police", 3)
o2 = i2o1 = set_name("buzzer")
setup:
buzzer = set_function(passive_buzzer)
buzzer_play_background(buzzer, "happy_birthday")
loop:
o2 = i1Stops the tone that buzzer_play_background started on that buzzer. Nothing happens if it is quiet. TinkerPy Mega, board firmware 2.29.
output | The pin the buzzer is on, e.g. buzzer (or o1). |
o1 = set_name("buzzer")
loop:
if i1 == ON:
buzzer_play_background(buzzer, "alarm")
else:
buzzer_stop_background(buzzer)o1 = set_name("buzzer")
loop:
if i2 == ON:
buzzer_stop_background(buzzer)o1 = set_name("buzzer")
setup:
buzzer_play_background(buzzer, "police", 100)
wait(10000)
buzzer_stop_background(buzzer)Gives 1 while a tune started by buzzer_play_background is still playing on that buzzer, 0 when it is quiet. (buzzer_play makes the program wait until its tune ends, so after it this gives 0.) TinkerPy Mega, board firmware 2.29.
output | The pin the buzzer is on, e.g. buzzer (or o1). |
o1 = set_name("buzzer")
loop:
if i1 == ON:
buzzer_play_background(buzzer, "school_bell", 2)
if is_buzzer_playing(buzzer) == 1:
o2 = ON
else:
o2 = OFFo1 = set_name("buzzer")
loop:
if is_buzzer_playing(buzzer) == 0:
wait(2000)
buzzer_play_background(buzzer, "twinkle_twinkle")o1 = set_name("buzzer")
setup:
buzzer = set_function(passive_buzzer)
buzzer_play_background(buzzer, "jingle_bells")
loop:
if is_buzzer_playing(buzzer) == 1:
o2 = ON
wait(200)
o2 = OFF
wait(200)TV Remote Function
Keys (always in small letters, in quotes): "1"…"9" "0" · "a" "b" "c" "d" (red, green, yellow, blue) · "up" "down" "left" "right" "ok" · "v+" "v-" "ch+" "ch-" · "play" "pause" "ff" "fb" · "menu" "exit". The old keys F B L R S / back / home are gone (Compile changes them).
The remote key pressed — for one loop, and again and again while it is held. None when no key. Learn the keys on the board first: Menu > TV Remote > Learn Remote.
k = remote_read()
if k == "up":
robot_control("F", 80, "F", 80)
elif k == "down":
robot_control("B", 80, "B", 80)
elif k == "ok":
robot_control("S", 0, "S", 0)k = remote_read()
if k == "1":
o1 = ON
elif k == "0":
o1 = OFFif remote_read() == "ch+":
o1 = ON
if remote_read() == "ch-":
o1 = OFFWaits for a code typed on the TV remote (keys 0–9 and A–D) and gives it, or 0 when no code was typed. count 1–6 keys · start the key that starts typing (e.g. "menu"; "any" or 0 = start straight away) · display "oled" (board screen: Enter Code, _ _ _, countdown), "lcd" (needs lcd_begin), an output like o1 (blinks 1× for the 1st key, 2× for the 2nd …) or 0 (nothing shown) · visibility "visible" (3 4 _) or "invisible" (each key shows for a moment, then *) · seconds 1–255 for the whole code. When the start key is not pressed it gives 0 at once (the loop carries on). After the last key the code stays on the display for 2 seconds. exit cancels. Store it in a variable and compare it in quotes: if code == "48A": (or code != 0 = any code). A code can’t be shown with oled_write.
code = remote_read_string(3, "menu", "oled", "visible", 10)
if code == "482":
o1 = servo_angle(90)code = remote_read_string(4, "any", "lcd", "invisible", 15)
if code == "12AB":
o2 = ON
elif code != 0:
o3 = ONsetup:
o1 = set_function(output)
code = remote_read_string(2, "ok", o1, "visible", 9)
if code == "77":
o2 = ONWireless Function
Board to board, no wires (ESP-NOW, firmware 2.24, TinkerPy Mega). Write the other board’s Device ID (TP and 8 numbers, on that board: Menu → Device info) before the dot: TP00000002.send("ON"). Compile looks each board up on tinkerpy.com (the student must be online; an unknown ID is an error). A link opens only when both boards’ programs list each other — and connect() each other in setup: — so a stranger’s board can’t disturb yours. Up to 6 boards; the link comes back by itself. A message is up to 30 characters and is there for one loop. The board screen header shows LK: OK; Menu → Wireless devices lists each board.
Starts the link to that board (in setup:). The ID is the other board's Device ID (Menu > Device info). The link opens when BOTH boards' programs list each other, and comes back by itself if a board was off.
setup:
TP00000002.connect()
loop:
TP00000002.send("hi")
wait(1000)setup:
TP00000002.connect()
TP00000003.connect()
loop:
wireless_send_all("ping")
wait(2000)setup:
TP00000002.connect()
loop:
if TP00000002.connected() == 1:
o1 = ON
else:
o1 = OFFSends text, a number or a join ("T" + temp) to that board — up to 30 characters. Gives 1 when the board got it, 0 when not (not linked or out of range); use it on its own line when you do not need to check.
value | "text", a number, a variable or a join like "T" + temp. |
setup:
TP00000002.connect()
loop:
if i1 == ON:
TP00000002.send("ON")
else:
TP00000002.send("OFF")
wait(200)setup:
TP00000002.connect()
loop:
t = 25
TP00000002.send(t)
wait(1000)setup:
TP00000002.connect()
loop:
ok = TP00000002.send("ALARM")
if ok == 0:
o3 = ON
wait(500)1 when a message from that board arrived (it is there for one loop), 0 when not.
setup:
TP00000002.connect()
loop:
if TP00000002.available() == 1:
o1 = ON
wait(200)
o1 = OFFsetup:
TP00000002.connect()
bluetooth_begin()
loop:
if TP00000002.available() == 1:
msg = TP00000002.read()
bluetooth_write(msg)setup:
TP00000002.connect()
loop:
if TP00000002.available() == 0:
o2 = OFFThe message from that board as text (None when nothing came). Store it (msg = TP00000002.read()), compare it or show it.
setup:
TP00000002.connect()
loop:
msg = TP00000002.read()
if msg == "ON":
o1 = ON
elif msg == "OFF":
o1 = OFFsetup:
TP00000002.connect()
oled_begin()
loop:
msg = TP00000002.read()
if msg != None:
oled_write(1, left, small, regular, "Got: " + msg)setup:
TP00000002.connect()
loop:
if TP00000002.read() == "BUZZ":
o1 = ON
wait(300)
o1 = OFFThe message from that board as a number (a sensor reading) — None when nothing came or it is not a number.
setup:
TP00000002.connect()
loop:
d = TP00000002.read_number()
if d != None and d < 20:
o1 = ONsetup:
TP00000002.connect()
loop:
a = TP00000002.read_number()
if a != None:
o2 = servo_angle(a)setup:
TP00000002.connect()
oled_begin()
loop:
t = TP00000002.read_number()
if t != None:
oled_write(1, center, large, bold, t)How strong that board's signal is: 0–100 (0 = not heard). A "how close is my friend" sensor.
setup:
TP00000002.connect()
loop:
if TP00000002.signal() < 20:
o1 = ON
else:
o1 = OFFsetup:
TP00000002.connect()
loop:
s = TP00000002.signal()
o1 = pwm(s)setup:
TP00000002.connect()
oled_begin()
loop:
oled_write(1, left, small, regular, "Signal " + TP00000002.signal())
wait(500)1 while the link to that board is up (both boards list each other and hear each other), 0 when not.
setup:
TP00000002.connect()
loop:
if TP00000002.connected() == 1:
o1 = ON
else:
o1 = OFFsetup:
TP00000002.connect()
loop:
if TP00000002.connected() == 0:
o2 = ON
wait(200)
o2 = OFF
wait(200)setup:
TP00000002.connect()
loop:
if TP00000002.connected() == 1 and i1 == ON:
TP00000002.send("GO")Closes the link to that board and stops taking its messages (connect() opens it again).
setup:
TP00000002.connect()
loop:
if i1 == ON:
TP00000002.disconnect()
if i2 == ON:
TP00000002.connect()setup:
TP00000002.connect()
loop:
if TP00000002.read() == "BYE":
TP00000002.disconnect()setup:
TP00000002.connect()
loop:
if TP00000002.signal() < 5:
TP00000002.disconnect()Sends to every linked board at once; gives how many got it.
value | "text", a number, a variable or a join. |
setup:
TP00000002.connect()
TP00000003.connect()
loop:
if i1 == ON:
wireless_send_all("STOP")setup:
TP00000002.connect()
TP00000003.connect()
loop:
n = wireless_send_all("hi")
if n < 2:
o1 = ON
wait(1000)setup:
TP00000002.connect()
TP00000003.connect()
loop:
t = 30
wireless_send_all("T" + t)
wait(2000)The next message from ANY linked board: id = who sent it ("TP00000003"), data = the message. Both None when nothing came.
setup:
TP00000002.connect()
TP00000003.connect()
loop:
id, data = wireless_read_any()
if id == "TP00000003":
o1 = ON
if data == "OFF":
o1 = OFFsetup:
TP00000002.connect()
TP00000003.connect()
oled_begin()
loop:
id, data = wireless_read_any()
if id != None:
oled_write(1, left, small, regular, id + ": " + data)setup:
TP00000002.connect()
TP00000003.connect()
bluetooth_begin()
loop:
id, data = wireless_read_any()
if data != None:
bluetooth_write(data)Wireless IoT Function
Board to board over the internet (firmware 2.25, TinkerPy Mega). The same functions as Wireless with _iot after the ID: TP00000002_iot.send("ON"). The boards can be anywhere — each on its own WiFi; tinkerpy.com passes the messages, so a message takes a few seconds (about 5–10 s free, 2–4 s Pro; the board checks in every 5 s / 2 s while the program uses wireless_iot). A link opens only when both boards’ programs list each other. send() gives 1 when the message is on its way (the other board is online and linked). No signal() — use connected(). A program uses wireless (radio) or wireless_iot, not both; iot_ and bluetooth_ functions work with either. Up to 6 boards, 30 characters per message; a message waits up to 60 s. Menu → Wireless devices shows them (“OK net”).
Starts the link to that board over the internet (in setup:). The ID is the other board's Device ID (Menu > Device info) with _iot after it. Both boards must be online (WiFi); the link opens when BOTH boards' programs list each other.
setup:
TP00000002_iot.connect()
loop:
TP00000002_iot.send("hi")
wait(5000)setup:
TP00000002_iot.connect()
TP00000003_iot.connect()
loop:
wireless_iot_send_all("ping")
wait(2000)setup:
TP00000002_iot.connect()
loop:
if TP00000002_iot.connected() == 1:
o1 = ON
else:
o1 = OFFSends text, a number or a join ("T" + temp) to that board through tinkerpy.com — up to 30 characters; it arrives in a few seconds (about 5–10 s free, 2–4 s Pro). Gives 1 when it is on its way (that board is online and linked), 0 when not.
value | "text", a number, a variable or a join like "T" + temp. |
setup:
TP00000002_iot.connect()
loop:
if i1 == ON:
TP00000002_iot.send("ON")
else:
TP00000002_iot.send("OFF")
wait(200)setup:
TP00000002_iot.connect()
loop:
t = 25
TP00000002_iot.send(t)
wait(1000)setup:
TP00000002_iot.connect()
loop:
ok = TP00000002_iot.send("ALARM")
if ok == 0:
o3 = ON
wait(500)1 when a message from that board arrived (it is there for one loop), 0 when not.
setup:
TP00000002_iot.connect()
loop:
if TP00000002_iot.available() == 1:
o1 = ON
wait(200)
o1 = OFFsetup:
TP00000002_iot.connect()
bluetooth_begin()
loop:
if TP00000002_iot.available() == 1:
msg = TP00000002_iot.read()
bluetooth_write(msg)setup:
TP00000002_iot.connect()
loop:
if TP00000002_iot.available() == 0:
o2 = OFFThe message from that board as text (None when nothing came). Store it (msg = TP00000002_iot.read()), compare it or show it.
setup:
TP00000002_iot.connect()
loop:
msg = TP00000002_iot.read()
if msg == "ON":
o1 = ON
elif msg == "OFF":
o1 = OFFsetup:
TP00000002_iot.connect()
oled_begin()
loop:
msg = TP00000002_iot.read()
if msg != None:
oled_write(1, left, small, regular, "Got: " + msg)setup:
TP00000002_iot.connect()
loop:
if TP00000002_iot.read() == "BUZZ":
o1 = ON
wait(300)
o1 = OFFThe message from that board as a number (a sensor reading) — None when nothing came or it is not a number.
setup:
TP00000002_iot.connect()
loop:
d = TP00000002_iot.read_number()
if d != None and d < 20:
o1 = ONsetup:
TP00000002_iot.connect()
loop:
a = TP00000002_iot.read_number()
if a != None:
o2 = servo_angle(a)setup:
TP00000002_iot.connect()
oled_begin()
loop:
t = TP00000002_iot.read_number()
if t != None:
oled_write(1, center, large, bold, t)1 while that board is online and linked (both programs list each other), 0 when not.
setup:
TP00000002_iot.connect()
loop:
if TP00000002_iot.connected() == 1:
o1 = ON
else:
o1 = OFFsetup:
TP00000002_iot.connect()
loop:
if TP00000002_iot.connected() == 0:
o2 = ON
wait(200)
o2 = OFF
wait(200)setup:
TP00000002_iot.connect()
loop:
if TP00000002_iot.connected() == 1 and i1 == ON:
TP00000002_iot.send("GO")Closes the link to that board and stops taking its messages (connect() opens it again).
setup:
TP00000002_iot.connect()
loop:
if i1 == ON:
TP00000002_iot.disconnect()
if i2 == ON:
TP00000002_iot.connect()setup:
TP00000002_iot.connect()
loop:
if TP00000002_iot.read() == "BYE":
TP00000002_iot.disconnect()setup:
TP00000002_iot.connect()
loop:
if TP00000002_iot.read() == "STOP":
TP00000002_iot.disconnect()
o1 = OFFSends to every linked board at once over the internet; gives how many it is on its way to.
value | "text", a number, a variable or a join. |
setup:
TP00000002_iot.connect()
TP00000003_iot.connect()
loop:
if i1 == ON:
wireless_iot_send_all("STOP")setup:
TP00000002_iot.connect()
TP00000003_iot.connect()
loop:
n = wireless_iot_send_all("hi")
if n < 2:
o1 = ON
wait(1000)setup:
TP00000002_iot.connect()
TP00000003_iot.connect()
loop:
t = 30
wireless_iot_send_all("T" + t)
wait(2000)The next message from ANY linked board: id = who sent it ("TP00000003"), data = the message. Both None when nothing came.
setup:
TP00000002_iot.connect()
TP00000003_iot.connect()
loop:
id, data = wireless_iot_read_any()
if id == "TP00000003":
o1 = ON
if data == "OFF":
o1 = OFFsetup:
TP00000002_iot.connect()
TP00000003_iot.connect()
oled_begin()
loop:
id, data = wireless_iot_read_any()
if id != None:
oled_write(1, left, small, regular, id + ": " + data)setup:
TP00000002_iot.connect()
TP00000003_iot.connect()
bluetooth_begin()
loop:
id, data = wireless_iot_read_any()
if data != None:
bluetooth_write(data)Joystick Function
How far the stick is pushed left or right: −100 (left) to 100 (right), 0 in the middle. The X wire goes on i1–i4.
setup:
i1 = set_function(joystick_x)
o1 = servo_angle(90 + joystick_x() * 9 / 10)x = joystick_x()
if x > 50:
o1 = ON
else:
o1 = OFFoled_write(1, left, small, regular, joystick_x())
How far the stick is pushed up or down: −100 (down) to 100 (up), 0 in the middle. The Y wire goes on i1–i4.
y = joystick_y()
if y > 0:
o1 = pwm(y)
else:
o1 = OFFsetup:
i2 = set_function(joystick_y)
bluetooth_write(joystick_y())rgb_bar(o3, all, "rainbow", joystick_y(), 100)
Where the stick points: 'F' (up), 'B' (down), 'L', 'R' — or 'S' in the middle. Like the app's Digital Joystick.
d = joystick_direction()
if d == 'F':
robot_control("F", 80, "F", 80)
elif d == 'B':
robot_control("B", 80, "B", 80)
elif d == 'S':
robot_control("S", 0, "S", 0)if joystick_direction() == 'L':
o1 = ONbluetooth_write(joystick_direction())
1 while the stick is pushed in (its switch), otherwise 0. The SW wire can go on any pin.
if joystick_pressed() == 1:
o2 = ON
else:
o2 = OFFsetup:
o5 = set_function(joystick_swt)
if joystick_pressed() == 1:
rgb_flip(o1, 1, "green")if joystick_pressed() == 1:
c1.increment()
wait(300)RGB LED Function
Goes inside setup: — says which output the RGB LEDs are on and how many there are. Added for you when you tick an RGB LED library.
output | The output the LED board or strip is plugged into, like o1. |
number_of_leds | 1, 2 or 3 for an LED board, or how many LEDs your strip has (max 60). |
setup:
rgb_begin(o1, 1)setup:
rgb_begin(o1, 3)setup:
rgb_begin(o2, 30)setup:
rgb_begin(o1, 2)
rgb_begin(o2, 60)Lights an LED (or all of them) in a colour. Brightness can be left out (100).
output | The output with the RGB LEDs, like o1. |
led | Which LED: 1, 2, 3 … or all. |
color | red, green, blue, yellow, orange, purple, pink, cyan, white, warm_white, off, random, rainbow — or a code like "#FF8800". |
brightness | Optional, 0–100. Left out = 100. |
rgb_color(o1, all, "red")
rgb_color(o1, 2, "#FF8800", 40)
if i1 == ON:
rgb_color(o1, all, "green")
else:
rgb_color(o1, all, "off")rgb_color(o1, all, "rainbow")
Each new press flips the LED: on in that colour, then off, then on… While the button stays pressed nothing changes. Brightness can be left out (100).
output | The output with the RGB LEDs, like o1. |
led | Which LED: 1, 2, 3 … or all. |
color | The colour it turns on in — a name, "#FF8800", random or rainbow. |
brightness | Optional, 0–100. Left out = 100. |
if i1 == ON:
rgb_flip(o1, 1, "red")if i2 == ON:
rgb_flip(o1, all, "white")if i1 == ON:
rgb_flip(o1, 1, "green")
if i2 == ON:
rgb_flip(o1, 2, "blue")Mixes any colour from red, green and blue (0–255 each, like a colour picker). Numbers or variables.
output | The output with the RGB LEDs, like o1. |
led | Which LED: 1, 2, 3 … or all. |
r | How much red, 0–255. |
g | How much green, 0–255. |
b | How much blue, 0–255. |
rgb_hex_color(o1, all, 255, 120, 0)
light = analog_read(i1) rgb_hex_color(o1, 1, 0, 0, light * 2)
red = random(0, 255) rgb_hex_color(o1, all, red, 0, 255 - red)
Blinks by itself while your code keeps running. on_ms and off_ms can be left out (500).
output | The output with the RGB LEDs, like o1. |
led | Which LED: 1, 2, 3 … or all. |
color | A colour name, "#FF8800", random or rainbow (a new colour each blink). |
on_ms | Optional: how long it is on, in ms (500). |
off_ms | Optional: how long it is off (same as on_ms). |
rgb_blink(o1, 1, "red")
rgb_blink(o1, all, "random", 300)
rgb_blink(o1, 2, "white", 100, 900)
Slowly fades in and out, by itself. time_ms can be left out (2000).
output | The output with the RGB LEDs, like o1. |
led | Which LED: 1, 2, 3 … or all. |
color | A colour name, "#FF8800", random or rainbow. |
time_ms | Optional: one breath, in ms (2000). |
rgb_breathe(o1, all, "purple")
rgb_breathe(o1, 1, "rainbow", 3000)
if i1 == OFF:
rgb_breathe(o1, all, "blue")
else:
rgb_color(o1, all, "green")Swaps between two colours, by itself — neighbouring LEDs take turns, like police lights.
output | The output with the RGB LEDs, like o1. |
led | Which LED: 1, 2, 3 … or all. |
color | The first colour. |
color2 | Optional: the second colour ("off"). |
time_ms | Optional: how long each colour stays, in ms (500). |
rgb_swap(o1, all, "red", "blue", 300)
rgb_swap(o1, 1, "yellow")
rgb_swap(o1, all, "green", "white", 1000)
One light runs along the LEDs, by itself. With a number it runs over LEDs 1 to that number. "rainbow" makes a moving rainbow.
output | The output with the RGB LEDs, like o1. |
led | all (the whole strip) or a number n (LEDs 1 to n). |
color | A colour name, "#FF8800", random or rainbow. |
speed_ms | Optional: time per step, in ms (100). |
rgb_chase(o1, all, "blue")
rgb_chase(o1, all, "rainbow", 50)
rgb_chase(o1, 3, "red", 200)
Fills the LEDs one after another, then stays on. With a number it fills LEDs 1 to that number.
output | The output with the RGB LEDs, like o1. |
led | all or a number n (LEDs 1 to n). |
color | A colour name, "#FF8800", random or rainbow. |
speed_ms | Optional: time per LED, in ms (50). |
rgb_wipe(o1, all, "green")
rgb_wipe(o1, all, "rainbow", 30)
if i1 == ON:
rgb_wipe(o1, all, "white", 20)LEDs sparkle at random, like stars, by themselves.
output | The output with the RGB LEDs, like o1. |
led | all or a number n (LEDs 1 to n). |
color | A colour name, "#FF8800", random or rainbow. |
speed_ms | Optional: how fast, in ms (100). |
rgb_twinkle(o1, all, "white")
rgb_twinkle(o1, all, "random", 60)
rgb_twinkle(o1, all, "warm_white", 200)
A level meter: lights more LEDs as the value goes up. "rainbow" goes green → yellow → red.
output | The output with the RGB LEDs, like o1. |
led | all or a number n (use LEDs 1 to n). |
color | A colour name, "#FF8800" or rainbow (green to red). |
value | The level to show — a number or a reading. |
max | Optional: the value that lights every LED (100). |
rgb_bar(o1, all, "green", analog_read(i1))
d = distance() rgb_bar(o1, all, "rainbow", 100 - d, 100)
rgb_bar(o1, all, "blue", c1, 10)
The colour follows a value: blue at low, the colour you pick at high. "rainbow" goes blue → green → red.
output | The output with the RGB LEDs, like o1. |
led | Which LED: 1, 2, 3 … or all. |
color | The colour at high (like red), or rainbow. |
value | The number to show — a reading or a variable. |
low | Optional: blue at this value (0). |
high | Optional: full colour at this value (100). |
rgb_heat(o1, all, "red", dht_temperature(i1), 20, 40)
rgb_heat(o1, 1, "rainbow", analog_read(i1))
rgb_heat(o1, all, "green", c1, 0, 20)
LCD Display Function
Starts the LCD. Goes inside setup: — added for you when you tick the 16x2 LCD.
address | The LCD’s I2C address — most are 0x27 or 0x3F. Leave it empty, lcd_begin(), and the board finds it. |
setup:
lcd_begin(0x27)setup:
lcd_begin(0x3F)setup:
lcd_begin()Writes text or a number on the LCD, starting at a place on a line.
line | Which line: 1 or 2. |
position | Where on the line to start: 1 (left) to 16 (right). |
value | Text in quotes, or a number / variable. |
lcd_write(1, 1, "Hello!")
lcd_write(2, 1, "Temp:") lcd_write(2, 7, dht_temperature(i1))
lcd_write(1, 6, "Hi")
Clears the whole LCD.
lcd_clear()
lcd_write(1, 1, "Hello") wait(2000) lcd_clear()
if i1 == ON:
lcd_clear()Clears just one line of the LCD.
line | Which line to clear: 1 or 2. |
lcd_clear_line(2)
count = 5 lcd_clear_line(1) lcd_write(1, 1, count)
if i1 == OFF:
lcd_clear_line(2)Scrolls long text along a line. It keeps scrolling by itself while the rest of your code runs.
line | Which line: 1 or 2. |
text | The text to scroll, in quotes. |
lcd_scroll(1, "Welcome to TinkerPy!")
lcd_scroll(1, "Smart Plant Monitor") lcd_write(2, 1, analog_read(i1))
lcd_scroll(2, "News: it is hot today") wait(5000) lcd_clear_line(2)
Switches the LCD’s backlight on or off. The text stays.
ON / OFF | ON lights the screen, OFF darkens it. |
lcd_backlight(OFF)
if i1 == ON:
lcd_backlight(ON)
else:
lcd_backlight(OFF)lcd_backlight(OFF) wait(500) lcd_backlight(ON)
OLED Display Function
Starts the OLED. Goes inside setup: — added for you when you tick the OLED.
address | Optional. Most OLEDs are 0x3C (used when empty); a few are 0x3D. |
setup:
oled_begin()setup:
oled_begin(0x3D)setup:
oled_begin()
lcd_begin(0x27)Writes text or a number on a line of the OLED (it replaces that line). The OLED has 4 lines. Sizes: small = 21 letters, 1 line · medium = 10 letters, 2 lines · large = 7 letters, 2 lines (medium and large also use the line below, so start them on line 1, 2 or 3).
line | Which line: 1 to 4 (medium / large: 1 to 3 — they take 2 lines). |
align | left, center or right. |
size | small (21 letters, 1 line), medium (10 letters, 2 lines) or large (7 letters, 2 lines). |
weight | regular or bold. |
value | Text in quotes, or a number / variable. |
oled_write(3, left, small, regular, "Sensor ON")
oled_write(1, center, large, bold, c1)
oled_write(1, center, medium, bold, "Weather") oled_write(3, left, small, regular, "Temp") oled_write(4, right, small, regular, dht_temperature(i1))
Clears the whole OLED.
oled_clear()
oled_write(1, left, small, regular, "Hello") wait(2000) oled_clear()
if i1 == ON:
oled_clear()Clears just one line of the OLED.
line | Which line to clear: 1 to 4. |
oled_clear_line(3)
if i1 == OFF:
oled_clear_line(4)oled_write(2, left, small, regular, "Saved!") wait(1000) oled_clear_line(2)
Robot eyes on the whole OLED: blink, look_left, look_right, look_up, look_down, look_around, happy, sad, angry, sleepy, surprised, wink, love, dizzy, suspicious, crying, confused, wake_up. It plays by itself while the rest of your code runs; oled_write / oled_clear stop it. Try them all on the board: Menu > Animations > Eyes.
name | Which animation, in quotes, e.g. "blink". |
repeat | How many times it plays. 0 = for ever. |
speed | 1 (slow) to 10 (fast). 5 is normal. |
delay | Pause between repeats in milliseconds (1000 = 1 second). |
oled_eye_animation("blink", 0, 5, 2000)if i1 == ON:
oled_eye_animation("happy", 2, 6, 300)
else:
oled_eye_animation("look_around", 0, 4, 1000)oled_eye_animation("sleepy", 1, 3, 0)
wait(4000)
oled_write(1, left, small, regular, "Good night")A moving emoji on the whole OLED: smile, laugh, wink, love, sad, cry, angry, surprised, sleepy, cool, tongue, heart, star, thinking, kiss. Plays by itself; oled_write / oled_clear stop it. See them all: Menu > Animations > Emoji.
name | Which emoji, in quotes, e.g. "smile". |
repeat | How many times it plays. 0 = for ever. |
speed | 1 (slow) to 10 (fast). 5 is normal. |
delay | Pause between repeats in milliseconds. |
oled_emoji_animation("heart", 0, 6, 0)if i1 == ON:
oled_emoji_animation("laugh", 3, 7, 0)
else:
oled_emoji_animation("sleepy", 0, 3, 500)oled_emoji_animation("cool", 1, 5, 0)Scrolls long text along a line. It keeps scrolling by itself while the rest of your code runs.
line | Which line: 1 to 4. |
text | The text to scroll, in quotes. |
oled_scroll(1, "Welcome to TinkerPy!")
oled_scroll(4, "Press the button to start") oled_write(1, left, small, regular, "Game")
oled_scroll(2, "Game over") wait(5000) oled_clear_line(2)
Counter Function
Starts counting up by 1, automatically, forever, in the background — once every interval_ms.
interval_ms | How often to add 1, in milliseconds (1–32767). 1000 = once a second. |
setup:
c1.auto_increment(1000)
loop:
oled_write(1, left, small, regular, c1)setup:
c1.auto_increment(200)
loop:
if c1 > 50:
o1 = pwm(100)setup:
c1.auto_increment(1000)
c2.auto_increment(500)Starts counting down by 1, automatically, forever, in the background — once every interval_ms.
interval_ms | How often to subtract 1, in milliseconds (1–32767). |
setup:
c1.set(60)
c1.auto_decrement(1000)
loop:
oled_write(1, left, small, regular, c1)setup:
c1.set(10)
c1.auto_decrement(500)
loop:
if c1 == 0:
o1 = pwm(100)setup:
c1.set(100)
c1.auto_decrement(100)Adds 1 to the counter, once, right now — not automatic.
if i1 == ON:
c1.increment()c1.increment() oled_write(1, left, small, regular, c1)
repeat(5):
c1.increment()Subtracts 1 from the counter, once, right now — not automatic.
if i2 == ON:
c1.decrement()setup:
c1.set(10)
loop:
c1.decrement()
oled_write(1, left, small, regular, c1)if i1 == ON:
c1.increment()
if i2 == ON:
c1.decrement()Resets the counter to 0. Doesn’t stop auto_increment/auto_decrement if either is running.
if i1 == ON:
c1.clear()c1.increment()
if c1 > 10:
c1.clear()setup:
c1.auto_increment(1000)
loop:
if i1 == ON:
c1.clear()Stops auto_increment/auto_decrement, freezing the counter where it is.
setup:
c1.auto_increment(1000)
loop:
if i1 == ON:
c1.pause()setup:
c1.auto_decrement(1000)
loop:
if c1 == 0:
c1.pause()setup:
c1.set(10)
c1.auto_decrement(500)
loop:
c1.pause()Jumps the counter straight to a specific number.
value | The exact number to set the counter to. |
setup:
c1.set(100)if i1 == ON:
c1.set(0)setup:
c1.set(60)
c1.auto_decrement(1000)Clock Function
Sets the clock to a starting time and begins running it.
hours | 0-23. |
minutes | 0-59. |
seconds | 0-59. |
setup:
clock_start(12, 30, 0)
loop:
oled_write(1, left, small, regular, HH)setup:
clock_start(0, 0, 0)
loop:
if MM == 1:
o1 = pwm(100)setup:
clock_start(23, 59, 55)Three special values that always hold the clock’s current hour, minute, and second — read them directly in a condition, no parentheses needed.
HH | The current hour, 0-23. |
MM | The current minute, 0-59. |
SS | The current second, 0-59. |
setup:
clock_start(9, 0, 0)
loop:
if HH == 12:
o1 = pwm(100)setup:
clock_start(0, 0, 0)
loop:
if MM == 30:
o1 = pwm(100)
else:
o1 = 0setup:
clock_start(7, 55, 0)
loop:
if HH == 8 and MM == 0:
bluetooth_write("It's 8:00!")setup:
clock_start(0, 0, 0)
loop:
if HH >= 22 or HH < 6:
o1 = pwm(100)
else:
o1 = 0Changes how fast the clock runs — how many real milliseconds count as 1 simulated second.
real_ms_per_second | 1000 = real time. Smaller = faster (e.g. 100 = 10x speed). 1–32767. |
setup:
clock_start(0, 0, 0)
clock_speed(100)setup:
clock_start(0, 0, 0)
clock_speed(1000)setup:
clock_start(0, 0, 0)
clock_speed(2000)Freezes the clock exactly where it is.
setup:
clock_start(0, 0, 0)
loop:
if i1 == ON:
clock_pause()setup:
clock_start(0, 5, 0)
loop:
if MM >= 5:
clock_pause()Starts the clock ticking again after clock_pause().
setup:
clock_start(0, 0, 0)
loop:
clock_pause()
if i1 == ON:
clock_resume()setup:
clock_start(0, 0, 0)
loop:
if i1 == ON:
clock_pause()
if i2 == ON:
clock_resume()Bluetooth & IoT Function
Switches Bluetooth on when the board starts. Goes inside setup: — every Bluetooth program needs it (the editor adds it for you).
setup:
bluetooth_begin()
loop:
if bluetooth_read_char() == 'A':
o1 = ONsetup:
bluetooth_begin()
set_device_password("5678")setup:
bluetooth_begin()
loop:
bluetooth_write("hello")
wait(1000)Gives 1 while a phone or app is connected (and gave the password), otherwise 0. Needs firmware 2.15+.
if bluetooth_connected() == 0:
robot_control("S", 0, "S", 0)if bluetooth_connected() == 1:
o1 = ON
else:
o1 = OFFwhile bluetooth_connected() == 0:
oled_write(1, left, small, regular, "Connect app")
wait(500)
oled_clear()Sends to the app over Bluetooth: a letter ('A'), text ("hello") or a number / variable (temp). Up to 3 numbers / variables in one message (firmware 2.24+): bluetooth_write(angle, distance()) sends "90,35", bluetooth_write(a, b, c) sends "a,b,c" — no text in this form. The same message twice in a row is sent once.
value | A letter in quotes, text up to 32 characters, or a number / variable. |
a, b, c | 2 or 3 numbers / variables, sent together as "a,b" or "a,b,c". |
bluetooth_write("hello")t = dht_temperature(i1) bluetooth_write(t)
if i1 == ON:
bluetooth_write('B')The letter the app sent (like a button tap). It lasts one loop cycle, then gives None until the next letter arrives.
if bluetooth_read_char() == 'F':
o1 = ONc = bluetooth_read_char()
if c == '1':
o1 = ON
elif c == '0':
o1 = OFFif bluetooth_read_char() != None:
bluetooth_write("got it")Text the app sent (up to 32 characters), for one loop cycle. Compare it, show it or send it — it can’t go in a variable.
if bluetooth_read_string() == "open":
o1 = servo_angle(90)setup:
lcd_begin()
lcd_write(1, 1, bluetooth_read_string())if bluetooth_read_string() != None:
bluetooth_write("ok")A number the app sent (like a slider), for one loop cycle — then None until a new one arrives.
n = bluetooth_read_number()
if n != None:
o1 = pwm(n)n = bluetooth_read_number()
if n != None:
o2 = servo_angle(n)n = bluetooth_read_number()
if n != None:
bluetooth_write(n * 2)A position from the app: x and y, each −100 to 100 — or x, y, d with its data number (0 if the app sent none; Hand Tracker 2 sends the gesture). None when nothing new arrived this cycle.
x, y = bluetooth_read_position()
if y != None:
o1 = pwm(y)x, y = bluetooth_read_position()
if x != None:
o2 = servo_angle(90 + x)x, y = bluetooth_read_position()
if y != None and y > 50:
robot_control("F", 80, "F", 80)x, y, d = bluetooth_read_position()
if d == 1:
o1 = ON
elif d == 2:
o1 = OFFA colour from the app’s colour picker: red, green, blue, each 0–255. None, None, None when nothing new arrived this cycle.
r, g, b = bluetooth_read_rgb()
if r != None:
rgb_hex_color(o1, all, r, g, b)r, g, b = bluetooth_read_rgb()
if r != None:
o1 = pwm(r * 100 / 255)r, g, b = bluetooth_read_rgb()
if r != None:
bluetooth_write("colour set")Sets the board’s Bluetooth password (4 digits) and saves it. Needs firmware 2.13+.
password | Exactly 4 digits in quotes, e.g. "5678". Phones and the editor must type this to connect. |
setup:
set_device_password("5678")if i1 == ON:
set_device_password("2468")setup:
set_device_password("5678")
loop:
bluetooth_write("ready")Sends to the app over the internet: a letter ('A'), text ("hello") or a number / variable (temp). Up to 3 numbers / variables in one message (firmware 2.24+): iot_write(angle, distance()) sends "90,35", iot_write(a, b, c) sends "a,b,c" — no text in this form. The same message twice in a row is sent once.
value | A letter in quotes, text up to 32 characters, or a number / variable. |
a, b, c | 2 or 3 numbers / variables, sent together as "a,b" or "a,b,c". |
iot_write("hello")t = dht_temperature(i1) iot_write(t)
if i1 == ON:
iot_write('B')The letter the app sent (like a button tap). It lasts one loop cycle, then gives None until the next letter arrives.
if iot_read_char() == 'F':
o1 = ONc = iot_read_char()
if c == '1':
o1 = ON
elif c == '0':
o1 = OFFif iot_read_char() != None:
iot_write("got it")Text the app sent (up to 32 characters), for one loop cycle. Compare it, show it or send it — it can’t go in a variable.
if iot_read_string() == "open":
o1 = servo_angle(90)setup:
lcd_begin()
lcd_write(1, 1, iot_read_string())if iot_read_string() != None:
iot_write("ok")A number the app sent (like a slider), for one loop cycle — then None until a new one arrives.
n = iot_read_number()
if n != None:
o1 = pwm(n)n = iot_read_number()
if n != None:
o2 = servo_angle(n)n = iot_read_number()
if n != None:
iot_write(n * 2)A position from the app: x and y, each −100 to 100 — or x, y, d with its data number (0 if the app sent none; Hand Tracker 2 sends the gesture). None when nothing new arrived this cycle.
x, y = iot_read_position()
if y != None:
o1 = pwm(y)x, y = iot_read_position()
if x != None:
o2 = servo_angle(90 + x)x, y = iot_read_position()
if y != None and y > 50:
robot_control("F", 80, "F", 80)x, y, d = iot_read_position()
if d == 1:
o1 = ON
elif d == 2:
o1 = OFFA colour from the app’s colour picker: red, green, blue, each 0–255. None, None, None when nothing new arrived this cycle.
r, g, b = iot_read_rgb()
if r != None:
rgb_hex_color(o1, all, r, g, b)r, g, b = iot_read_rgb()
if r != None:
o1 = pwm(r * 100 / 255)r, g, b = iot_read_rgb()
if r != None:
iot_write("colour set")Robot Function
Drives both of the rover’s motors at once — one call expresses any movement: straight, reverse, a spin-in-place turn, a curve at any ratio, a full stop, or leaving one motor exactly as it was.
left_dir | "F" (forward), "B" (backward), "S" (stop — forces this motor to 0 even if a speed is given), or "X" (do not disturb — leaves this motor completely untouched, pwm ignored). Always in quotes. |
left_speed | 0–100 — the left wheel’s speed. Ignored if left_dir is "S" or "X". |
right_dir | Same four options as left_dir, for the right wheel. |
right_speed | 0–100 — the right wheel’s speed. Ignored if right_dir is "S" or "X". |
robot_control("F", 100, "F", 100)robot_control("B", 100, "B", 100)robot_control("B", 100, "F", 100)robot_control("F", 100, "B", 100)robot_control("F", 60, "F", 100)robot_control("F", 100, "F", 60)robot_control("F", 30, "F", 30)if distance() < 20:
robot_control("B", 60, "B", 60)
else:
robot_control("F", 80, "F", 80)if i1 == ON:
robot_control("F", 100, "F", 100)
else:
robot_control("F", 0, "F", 0)repeat(4):
robot_control("F", 100, "F", 100)
wait(1000)
robot_control("B", 100, "F", 100)
wait(500)speed = 0
repeat(10):
speed = speed + 10
robot_control("F", speed, "F", speed)
wait(200)robot_control("B", 100, "B", 60)if i1 == ON and i2 == ON:
robot_control("F", 80, "F", 80)
elif i1 == ON:
robot_control("F", 40, "F", 80)
elif i2 == ON:
robot_control("F", 80, "F", 40)
else:
robot_control("F", 0, "F", 0)robot_control("S", 0, "S", 0)if i1 == ON:
robot_control("F", 70, "X", 0)
else:
robot_control("S", 0, "X", 0)Drives just the RIGHT motor — the same o3/o4 pins robot_control(...) uses for its right side, controlled on their own instead of alongside the left motor.
direction | "F" (forward), "B" (backward), or "S" (stop — forces this motor to 0 even if a speed is given). Always in quotes. |
speed | 0–100 — the wheel’s speed. Ignored if direction is "S". |
motor_control_right("F", 70)motor_control_right("S", 0)motor_control_right("F", 60)
motor_control_left("B", 60)Drives just the LEFT motor — the same o5/o6 pins robot_control(...) uses for its left side, controlled on their own instead of alongside the right motor.
direction | "F" (forward), "B" (backward), or "S" (stop — forces this motor to 0 even if a speed is given). Always in quotes. |
speed | 0–100 — the wheel’s speed. Ignored if direction is "S". |
motor_control_left("F", 70)motor_control_left("S", 0)if i1 == ON:
motor_control_left("F", 50)
else:
motor_control_left("S", 0)Drives the robot straight ahead: both wheels forward at the same speed. Works like robot_control(...) with the directions filled in.
speed | 0–100: how fast the wheels turn (a number or a variable). |
robot_forward(80)
Drives the robot straight back: both wheels backward at the same speed. Works like robot_control(...) with the directions filled in.
speed | 0–100: how fast the wheels turn (a number or a variable). |
robot_backward(60)
Spins the robot to the LEFT on the spot: right wheel forward, left wheel backward, same speed. Works like robot_control(...) with the directions filled in.
speed | 0–100: how fast the wheels turn (a number or a variable). |
robot_left(60)
Spins the robot to the RIGHT on the spot: left wheel forward, right wheel backward, same speed. Works like robot_control(...) with the directions filled in.
speed | 0–100: how fast the wheels turn (a number or a variable). |
robot_right(60)
Drives forward in a curve to the LEFT: the right wheel at the speed, the left (inner) wheel at half of it. Works like robot_control(...) with the directions filled in.
speed | 0–100: the outer wheel’s speed; the inner wheel turns at half of it. |
robot_arc_left(80)
Drives forward in a curve to the RIGHT: the left wheel at the speed, the right (inner) wheel at half of it. Works like robot_control(...) with the directions filled in.
speed | 0–100: the outer wheel’s speed; the inner wheel turns at half of it. |
robot_arc_right(80)
Stops both wheels. Works like robot_control(...) with the directions filled in.
robot_forward(80) wait(2000) robot_stop()
5.3 Firmware needed
The board must have this firmware (or newer) for a program that uses the feature. TinkerPy Code shows the needed version when uploading. The newest board firmware is 2.26 (the board password for WiFi upload too, shown on the CODE DOWNLOAD screens; 2.25 added wireless_iot — board to board over the internet; 2.24 added voltage(), text variables, joining text with + and wireless board to board), so update boards to 2.25. Upload checks the board’s live firmware and says so when it is too old.
| Firmware | Adds |
|---|---|
| 2.13 | set_device_password |
| 2.14 | servo_flip |
| 2.15 | random, while, break, analog_read, bluetooth_connected |
| 2.16 | OLED and LCD functions (oled_…, lcd_…) |
| 2.17 | RGB LEDs (rgb_…) |
| 2.18 | bluetooth_read_number / string / position / rgb, iot_read_…, iot_write(text), joystick |
| 2.19 | buzzer_play, TV remote, OLED eye / emoji animations, position data |
| 2.20 | oled_write(line, align, size, weight, value) |
| 2.23 | TV remote: the 29 general keys (remote_read) and remote_read_string — learn the remote again after updating |
| 2.24 | voltage() (volts 0.00 – 3.30, decimals like 1.6), Debug → Analog Input, text variables, joining with +, wireless (import wireless: TP00000002.connect() / send / read …, wireless_send_all, wireless_read_any) |
| 2.25 | wireless_iot (import wireless_iot: TP00000002_iot.connect() / send / read …, wireless_iot_send_all, wireless_iot_read_any — board to board over the internet) |
| 2.29 | buzzer_play_background, buzzer_stop_background, is_buzzer_playing (TinkerPy Mega) |
| 2.26 | invert(o2) (reading an output back), WiFi upload asks for the board password (the 4 digits on the CODE DOWNLOAD screen, the same as Bluetooth); set_device_password("5678") changes it (was bluetooth_password) |
Part 6 — Complete example projects
45 complete programs, each checked by the real TinkerPy compiler. Use them as patterns for structure, wiring and style.
Basic
Blink an LED
The classic first project: an LED turns on and off every half second.
Wiring: LED on o1 (long leg to o1, short leg to GND).
# ===== Blink an LED =====
# LED on o1
# This code block executes forever
loop:
o1 = ON
wait(500)
o1 = OFF
wait(500)Push Button Light
Hold the button and the LED glows. Let go and it switches off.
Wiring: Push button on i1, LED on o1.
# ===== Push Button Light =====
# Button on i1, LED on o1
loop:
if i1 == ON:
o1 = ON
else:
o1 = OFFTraffic Light
Red, yellow and green LEDs change just like a real traffic signal.
Wiring: Red LED on o1, yellow LED on o2, green LED on o3.
# ===== Traffic Light =====
# Red o1, Yellow o2, Green o3
loop:
# Red - stop
o1 = ON
wait(4000)
o1 = OFF
# Green - go
o3 = ON
wait(4000)
o3 = OFF
# Yellow - get ready to stop
o2 = ON
wait(1500)
o2 = OFFBreathing LED
The LED slowly fades in and out, like it is breathing.
Wiring: LED on o1.
# ===== Breathing LED =====
# LED on o1 fades in and out
loop:
output_increment_till(o1, 0, 100, 5, 40)
output_decrement_till(o1, 100, 0, 5, 40)
wait(300)Toggle Switch
Press the button once to switch the light on, press again to switch it off.
Wiring: Push button on i1, LED on o1.
# ===== Toggle Switch =====
# Each press of i1 flips the LED on o1
loop:
if i1 == ON:
output_flip(o1, 0, 100)
# wait until the button is let go, so one press = one flip
wait(300)Servo
Servo Sweep
The servo arm sweeps smoothly from 0° to 180° and back again.
Wiring: Servo signal wire on o1 (brown/black to GND, red to 5V).
# ===== Servo Sweep =====
# Servo on o1
import servo
setup:
o1 = set_function(servo) # servo
loop:
servo_increment_till(o1, 0, 180, 5, 20)
servo_decrement_till(o1, 180, 0, 5, 20)Button Gate
Press the button to open the gate. It closes again after 3 seconds.
Wiring: Servo (the gate arm) on o1, push button on i1.
# ===== Button Gate =====
# Servo gate on o1, button on i1
import servo
setup:
o1 = set_function(servo) # servo
servo_write_angle(o1, 0)
loop:
if i1 == ON:
servo_write_angle(o1, 90)
wait(3000)
servo_write_angle(o1, 0)Radar Scanner
The servo scans left and right forever, like a radar dish.
Wiring: Servo on o1.
# ===== Radar Scanner =====
# Servo on o1 scans by itself
import servo
setup:
o1 = set_function(servo) # servo
servo_scan_on(o1, 0, 180, 5, 20)
loop:
wait(1000)Waving Robot Hand
A cardboard hand on the servo waves hello three times when you press the button.
Wiring: Servo with a cardboard hand on o1, push button on i1.
# ===== Waving Robot Hand =====
# Servo hand on o1, button on i1
import servo
setup:
o1 = set_function(servo) # servo
servo_write_angle(o1, 90)
loop:
if i1 == ON:
repeat(3):
servo_write_angle(o1, 45)
wait(300)
servo_write_angle(o1, 135)
wait(300)
servo_write_angle(o1, 90)Mirror Servos
Two servos move like a pair of wings: when one goes up, the other goes down.
Wiring: Servo 1 on o1, servo 2 on o2.
# ===== Mirror Servos =====
# Servos on o1 and o2 move like wings
import servo
setup:
o1 = set_function(servo) # servo
o2 = set_function(servo) # servo
servo_sync_reverse(o1, o2)
loop:
servo_increment_till(o1, 30, 150, 5, 25)
servo_decrement_till(o1, 150, 30, 5, 25)Ultrasonic
Distance Alarm
A buzzer beeps when something comes closer than 20 cm.
Wiring: Ultrasonic sensor: Trig on i3, Echo on i4. Buzzer on o1.
# ===== Distance Alarm =====
# Ultrasonic (Trig i3, Echo i4), buzzer on o1
import ultrasonic
setup:
i3 = set_function(ultrasonic_TX) # cannot be changed
i4 = set_function(ultrasonic_RX) # cannot be changed
loop:
if distance() < 20:
o1 = ON
else:
o1 = OFFParking Sensor
Beeps faster and faster as a car gets closer to the wall, just like a real parking sensor.
Wiring: Ultrasonic sensor: Trig on i3, Echo on i4. Buzzer on o1.
# ===== Parking Sensor =====
# Ultrasonic (Trig i3, Echo i4), buzzer on o1
import ultrasonic
setup:
i3 = set_function(ultrasonic_TX) # cannot be changed
i4 = set_function(ultrasonic_RX) # cannot be changed
loop:
d = distance()
if d < 10:
o1 = ON
elif d < 30:
o1 = ON
wait(100)
o1 = OFF
wait(100)
elif d < 60:
o1 = ON
wait(100)
o1 = OFF
wait(400)
else:
o1 = OFFSmart Dustbin
The dustbin lid opens by itself when your hand comes near, then closes.
Wiring: Ultrasonic sensor: Trig on i3, Echo on i4 (facing up). Servo for the lid on o1.
# ===== Smart Dustbin =====
# Ultrasonic (Trig i3, Echo i4), lid servo on o1
import ultrasonic
import servo
setup:
o1 = set_function(servo) # servo
i3 = set_function(ultrasonic_TX) # cannot be changed
i4 = set_function(ultrasonic_RX) # cannot be changed
servo_write_angle(o1, 0)
loop:
if distance() < 15:
servo_write_angle(o1, 100)
wait(3000)
servo_write_angle(o1, 0)Water Tank Level
Shows how full a water tank is with 3 LEDs. The sensor looks down at the water.
Wiring: Ultrasonic sensor at the top of the tank: Trig on i3, Echo on i4. LEDs on o1 (low), o2 (half), o3 (full).
# ===== Water Tank Level =====
# Ultrasonic looks down at the water; LEDs o1 o2 o3
import ultrasonic
setup:
i3 = set_function(ultrasonic_TX) # cannot be changed
i4 = set_function(ultrasonic_RX) # cannot be changed
loop:
# small distance = water is near the top = tank is full
d = distance()
o1 = ON
if d < 20:
o2 = ON
else:
o2 = OFF
if d < 8:
o3 = ON
else:
o3 = OFF
wait(500)Touchless Hand Wash
A small pump runs for 2 seconds when hands come under the tap. No touching needed!
Wiring: Ultrasonic sensor under the tap: Trig on i3, Echo on i4. Small 5V pump (through a driver) on o1.
# ===== Touchless Hand Wash =====
# Ultrasonic (Trig i3, Echo i4), pump on o1
import ultrasonic
setup:
i3 = set_function(ultrasonic_TX) # cannot be changed
i4 = set_function(ultrasonic_RX) # cannot be changed
loop:
if distance() < 12:
o1 = ON
wait(2000)
o1 = OFF
# wait for the hands to move away
wait(1500)Wireless (two boards)
Each program goes to its own board; each lists the OTHER board’s ID. Firmware 2.24.
Wireless Doorbell — button board
Press the button: the other board rings. A warning LED shows when the bell board did not get it.
Wiring: Push button on i1, LED on o1. Board TP00000001.
# ===== Wireless Doorbell — the button board (TP00000001) =====
# Button on i1. Upload this to TP00000001.
import wireless
setup:
TP00000002.connect() # the bell board
loop:
if i1 == ON:
ok = TP00000002.send("RING")
if ok == 0:
o1 = ON # not delivered: warning LED
else:
o1 = OFF
wait(300)Wireless Doorbell — bell board
Rings and shows “Visitor!” when the button board sends RING.
Wiring: Passive buzzer on o1, OLED (SDA o7, SCL o8). Board TP00000002.
# ===== Wireless Doorbell — the bell board (TP00000002) =====
# Passive buzzer on o1, OLED. Upload this to TP00000002.
import wireless
setup:
TP00000001.connect() # the button board
o1 = set_function(passive_buzzer)
oled_begin()
loop:
msg = TP00000001.read()
if msg == "RING":
oled_write(1, center, medium, bold, "Visitor!")
buzzer_play(o1, "door_bell")
if TP00000001.connected() == 0:
oled_write(4, left, small, regular, "Waiting for button")Wireless IoT (two boards over the internet)
Each program goes to its own board; each lists the OTHER board’s ID with _iot. Both boards need WiFi. Firmware 2.25.
Internet Doorbell — button board
Press the button: the other board rings, wherever it is. An LED shows when the bell board is offline.
Wiring: Push button on i1, LED on o1. Board TP00000001.
# ===== Internet Doorbell — the button board (TP00000001) =====
# Button on i1. Upload this to TP00000001. Both boards need WiFi.
import wireless_iot
setup:
TP00000002_iot.connect() # the bell board, anywhere
loop:
if i1 == ON:
ok = TP00000002_iot.send("RING")
if ok == 0:
o1 = ON # the bell board is offline
else:
o1 = OFF
wait(1000)Internet Doorbell — bell board
Rings and shows “Visitor!” when the button board sends RING.
Wiring: Passive buzzer on o1, OLED (SDA o7, SCL o8). Board TP00000002.
# ===== Internet Doorbell — the bell board (TP00000002) =====
# Passive buzzer on o1, OLED. Upload this to TP00000002.
import wireless_iot
setup:
TP00000001_iot.connect() # the button board
o1 = set_function(passive_buzzer)
oled_begin()
loop:
msg = TP00000001_iot.read()
if msg == "RING":
oled_write(1, center, medium, bold, "Visitor!")
buzzer_play(o1, "door_bell")
if TP00000001_iot.connected() == 0:
oled_write(4, left, small, regular, "Button board offline")Clock
Digital Clock
Shows the time (hours, minutes, seconds) on the OLED display.
Wiring: 0.96" OLED display (SDA o7, SCL o8). Change the start time in setup. · Firmware 2.20+
# ===== Digital Clock =====
# Set the start time below, shown on the OLED
import clock
import oled_display
setup:
oled_begin() # starts the OLED
o7 = set_function(SDA) # cannot be changed
o8 = set_function(SCL) # cannot be changed
clock_start(10, 30, 0)
loop:
oled_write(1, left, small, regular, "Time now")
oled_write(2, left, small, regular, HH)
oled_write(3, left, small, regular, MM)
oled_write(4, left, small, regular, SS)
wait(500)School Bell
Rings a bell (buzzer) at the start of each period: 9:00, 9:45 and 10:30.
Wiring: Buzzer on o1. Set the clock to the real time in setup.
# ===== School Bell =====
# Buzzer on o1 rings at period times
import clock
setup:
clock_start(8, 59, 50)
loop:
if SS == 0:
if HH == 9 and MM == 0:
o1 = ON
wait(3000)
o1 = OFF
if HH == 9 and MM == 45:
o1 = ON
wait(3000)
o1 = OFF
if HH == 10 and MM == 30:
o1 = ON
wait(3000)
o1 = OFF
wait(500)Night Lamp
The lamp switches on by itself at 7 PM and off at 6 AM.
Wiring: Lamp/LED on o1. Set the clock to the real time in setup.
# ===== Night Lamp =====
# Lamp on o1, on from 7 PM to 6 AM
import clock
setup:
clock_start(18, 59, 50)
loop:
if HH >= 19 or HH < 6:
o1 = ON
else:
o1 = OFF
wait(1000)Plant Watering Timer
Waters the plants for 10 seconds every morning at 7:00.
Wiring: Small water pump (through a driver) on o1. Set the clock to the real time in setup.
# ===== Plant Watering Timer =====
# Pump on o1 runs at 7:00 every day
import clock
setup:
clock_start(6, 59, 50)
loop:
if HH == 7 and MM == 0 and SS == 0:
o1 = ON
wait(10000)
o1 = OFF
wait(500)Stopwatch
Button i1 starts and stops the stopwatch, button i2 resets it to zero.
Wiring: Start/stop button on i1, reset button on i2, OLED display (SDA o7, SCL o8). · Firmware 2.20+
# ===== Stopwatch =====
# i1 start/stop, i2 reset, time on the OLED
import clock
import oled_display
setup:
oled_begin() # starts the OLED
o7 = set_function(SDA) # cannot be changed
o8 = set_function(SCL) # cannot be changed
clock_start(0, 0, 0)
clock_pause()
running = 0
loop:
if i1 == ON:
if running == 0:
clock_resume()
running = 1
else:
clock_pause()
running = 0
wait(300)
if i2 == ON:
clock_start(0, 0, 0)
clock_pause()
running = 0
oled_write(1, left, small, regular, "Stopwatch")
oled_write(2, left, small, regular, MM)
oled_write(3, left, small, regular, SS)
wait(100)OLED
Hello Display
Your first OLED project: shows a greeting on the screen.
Wiring: 0.96" OLED display (SDA o7, SCL o8). · Firmware 2.20+
# ===== Hello Display =====
# OLED on o7 (SDA) / o8 (SCL)
import oled_display
setup:
oled_begin() # starts the OLED
o7 = set_function(SDA) # cannot be changed
o8 = set_function(SCL) # cannot be changed
oled_write(1, center, medium, bold, "Hello!") # big + bold, uses lines 1-2
oled_write(3, center, small, regular, "I am TinkerPy")
oled_write(4, center, small, regular, "Let's build!")
loop:
wait(1000)Button Counter
Counts how many times the button is pressed and shows the number.
Wiring: Push button on i1 (count), i2 (reset). OLED display (SDA o7, SCL o8). · Firmware 2.20+
# ===== Button Counter =====
# i1 counts, i2 resets, number on the OLED
import oled_display
import counter
setup:
oled_begin() # starts the OLED
o7 = set_function(SDA) # cannot be changed
o8 = set_function(SCL) # cannot be changed
oled_write(1, left, small, bold, "Presses:")
loop:
if i1 == ON:
c1.increment()
wait(250)
if i2 == ON:
c1.clear()
oled_write(2, center, large, bold, c1) # a big number on lines 2-3Distance Meter
A digital measuring tape: shows the distance in cm on the screen.
Wiring: Ultrasonic sensor: Trig on i3, Echo on i4. OLED display (SDA o7, SCL o8). · Firmware 2.20+
# ===== Distance Meter =====
# Ultrasonic (Trig i3, Echo i4) + OLED
import oled_display
import ultrasonic
setup:
oled_begin() # starts the OLED
i3 = set_function(ultrasonic_TX) # cannot be changed
i4 = set_function(ultrasonic_RX) # cannot be changed
o7 = set_function(SDA) # cannot be changed
o8 = set_function(SCL) # cannot be changed
oled_write(1, center, small, bold, "Distance (cm)")
loop:
oled_write(2, center, large, bold, distance()) # a big number on lines 2-3
wait(300)Weather Station
Shows the room temperature and humidity from a DHT11 sensor.
Wiring: DHT11 sensor data pin on i3. OLED display (SDA o7, SCL o8). · Firmware 2.20+
# ===== Weather Station =====
# DHT11 on i3, readings on the OLED
import oled_display
import dht11
setup:
oled_begin() # starts the OLED
i3 = set_function(dht11)
o7 = set_function(SDA) # cannot be changed
o8 = set_function(SCL) # cannot be changed
loop:
oled_write(1, left, small, bold, "Temp (C)")
oled_write(2, right, small, regular, dht_temperature(i3))
oled_write(3, left, small, bold, "Humidity (%)")
oled_write(4, right, small, regular, dht_humidity(i3))
wait(2000)Countdown Timer
Press the button to start a 10-second countdown. A buzzer beeps at zero.
Wiring: Start button on i1, buzzer on o1, OLED display (SDA o7, SCL o8). · Firmware 2.20+
# ===== Countdown Timer =====
# i1 starts, buzzer on o1, OLED shows the count
import oled_display
import counter
setup:
oled_begin() # starts the OLED
o7 = set_function(SDA) # cannot be changed
o8 = set_function(SCL) # cannot be changed
c1.set(10)
oled_write(1, center, small, regular, "Press to start")
loop:
if i1 == ON:
c1.set(10)
c1.auto_decrement(1000)
oled_write(1, center, small, regular, "Counting down")
oled_write(2, center, large, bold, c1) # a big number on lines 2-3
if c1 == 0:
c1.pause()
oled_write(1, center, small, bold, "Time up!")
o1 = ON
wait(1000)
o1 = OFF
c1.set(10)Bluetooth
Bluetooth Light
Switch an LED on and off from your phone with Bluetooth.
Wiring: LED on o1. Connect with the TinkerPy App and send 1 (on) or 0 (off). · Firmware 2.18+
# ===== Bluetooth Light =====
# Send 1 / 0 from the TinkerPy App
import bluetooth
setup:
bluetooth_begin() # switches Bluetooth on when the board starts
loop:
bt_data = bluetooth_read_char()
if bt_data == '1':
o1 = ON
elif bt_data == '0':
o1 = OFFBluetooth Robot Car
Drive the robot car from your phone: F, B, L, R and S (stop).
Wiring: Motors on o3–o6 (motor driver). Use the arrow buttons in the TinkerPy App. · Firmware 2.18+
# ===== Bluetooth Robot Car =====
# F B L R S from the TinkerPy App
import bluetooth
import motor
setup:
o3 = set_function(motor_right_forward) # cannot be changed
o4 = set_function(motor_right_backward) # cannot be changed
o5 = set_function(motor_left_forward) # cannot be changed
o6 = set_function(motor_left_backward) # cannot be changed
bluetooth_begin() # switches Bluetooth on when the board starts
loop:
bt_data = bluetooth_read_char()
if bt_data == 'F':
robot_control("F", 80, "F", 80)
elif bt_data == 'B':
robot_control("B", 80, "B", 80)
elif bt_data == 'L':
robot_control("B", 70, "F", 70)
elif bt_data == 'R':
robot_control("F", 70, "B", 70)
elif bt_data == 'S':
robot_control("S", 0, "S", 0)Bluetooth Door Lock
A servo locks and unlocks a door when the phone sends U (unlock) or L (lock).
Wiring: Servo (the lock) on o1. Send U or L from the TinkerPy App. · Firmware 2.18+
# ===== Bluetooth Door Lock =====
# U unlocks, L locks; servo on o1
import bluetooth
import servo
setup:
o1 = set_function(servo) # servo
bluetooth_begin() # switches Bluetooth on when the board starts
servo_write_angle(o1, 0)
loop:
bt_data = bluetooth_read_char()
if bt_data == 'U':
servo_write_angle(o1, 90)
bluetooth_write("Door unlocked")
elif bt_data == 'L':
servo_write_angle(o1, 0)
bluetooth_write("Door locked")Bluetooth Doorbell
Press the doorbell button and a message pops up on your phone.
Wiring: Push button on i1, buzzer on o1. Open the TinkerPy App to see the messages.
# ===== Bluetooth Doorbell =====
# Button i1 sends a message to the phone
import bluetooth
setup:
bluetooth_begin() # switches Bluetooth on when the board starts
loop:
if i1 == ON:
o1 = ON
bluetooth_write("Someone is at the door!")
wait(500)
o1 = OFF
wait(1000)Bluetooth Fan Speed
Choose the fan speed from your phone: 0 (off), 1 (slow), 2 (medium), 3 (fast).
Wiring: Small DC fan (through a driver) on o1. Send 0–3 from the TinkerPy App. · Firmware 2.18+
# ===== Bluetooth Fan Speed =====
# Send 0, 1, 2 or 3 from the TinkerPy App
import bluetooth
setup:
bluetooth_begin() # switches Bluetooth on when the board starts
loop:
bt_data = bluetooth_read_char()
if bt_data == '0':
o1 = pwm(0)
elif bt_data == '1':
o1 = pwm(40)
elif bt_data == '2':
o1 = pwm(70)
elif bt_data == '3':
o1 = pwm(100)IoT
IoT Light Switch
Switch a light on and off from anywhere using the internet.
Wiring: LED or relay on o1. Connect the TinkerPy App over IoT (Device ID + PIN) and send 1 / 0. · Firmware 2.18+
# ===== IoT Light Switch =====
# Send 1 / 0 from the TinkerPy App (IoT)
import iot
loop:
iot_data = iot_read_char()
if iot_data == '1':
o1 = ON
iot_write("Light is ON")
elif iot_data == '0':
o1 = OFF
iot_write("Light is OFF")IoT Door Alert
Sends a message to the app whenever the door is opened.
Wiring: Door switch (or button) on i1. Open the TinkerPy App (IoT) to see alerts. · Firmware 2.18+
# ===== IoT Door Alert =====
# Door switch on i1 sends an alert
import iot
loop:
if i1 == ON:
iot_write("Door opened!")
wait(3000)IoT Room Monitor
Sends the room temperature and humidity to the app every 10 seconds.
Wiring: DHT11 data pin on i3. The app shows channel 1 (temperature) and channel 2 (humidity). · Firmware 2.18+
# ===== IoT Room Monitor =====
# DHT11 on i3 → app channels 1 and 2
import iot
import dht11
setup:
i3 = set_function(dht11)
loop:
if dht_temperature(i3) > 30:
iot_write("It is hot!")
if dht_temperature(i3) > 25:
iot_write("warm")
else:
iot_write("cool")
if dht_humidity(i3) > 70:
iot_write("humid")
else:
iot_write("dry")
wait(10000)IoT Smart Plug
Turn a plug on or off from the app, or with the button on the board.
Wiring: Relay module on o1, button on i1. Send 1 / 0 from the TinkerPy App (IoT). · Firmware 2.18+
# ===== IoT Smart Plug =====
# App 1/0 or button i1 switches the relay on o1
import iot
loop:
iot_data = iot_read_char()
if iot_data == '1':
o1 = ON
iot_write("on")
elif iot_data == '0':
o1 = OFF
iot_write("off")
if i1 == ON:
output_flip(o1, 0, 100)
iot_write("Switched by button")
wait(300)IoT Garden Watering
Start watering from the app (W), stop it (S). The app hears when the pump starts and stops.
Wiring: Water pump (through a driver) on o1. Send W / S from the TinkerPy App (IoT). · Firmware 2.18+
# ===== IoT Garden Watering =====
# W starts, S stops the pump on o1
import iot
loop:
iot_data = iot_read_char()
if iot_data == 'W':
o1 = ON
iot_write("Watering started")
elif iot_data == 'S':
o1 = OFF
iot_write("Watering stopped")Robotics
Robot Dance
The robot moves forward, back, and spins — a little dance!
Wiring: Motors on o3–o6 through the motor driver.
# ===== Robot Dance =====
# Motors on o3-o6
import motor
setup:
o3 = set_function(motor_right_forward) # cannot be changed
o4 = set_function(motor_right_backward) # cannot be changed
o5 = set_function(motor_left_forward) # cannot be changed
o6 = set_function(motor_left_backward) # cannot be changed
loop:
robot_control("F", 70, "F", 70)
wait(1000)
robot_control("B", 70, "B", 70)
wait(1000)
robot_control("F", 70, "B", 70)
wait(800)
robot_control("B", 70, "F", 70)
wait(800)
robot_control("S", 0, "S", 0)
wait(1500)Obstacle Avoider
The robot drives forward and turns away when something is in front of it.
Wiring: Motors on o3–o6. Ultrasonic sensor at the front: Trig on i3, Echo on i4.
# ===== Obstacle Avoider =====
# Motors o3-o6, ultrasonic Trig i3 / Echo i4
import motor
import ultrasonic
setup:
o3 = set_function(motor_right_forward) # cannot be changed
o4 = set_function(motor_right_backward) # cannot be changed
o5 = set_function(motor_left_forward) # cannot be changed
o6 = set_function(motor_left_backward) # cannot be changed
i3 = set_function(ultrasonic_TX) # cannot be changed
i4 = set_function(ultrasonic_RX) # cannot be changed
loop:
if distance() < 20:
robot_control("S", 0, "S", 0)
wait(200)
robot_control("B", 60, "B", 60)
wait(400)
robot_control("F", 70, "B", 70)
wait(500)
else:
robot_control("F", 70, "F", 70)Line Follower
Follows a black line on the floor using two IR sensors.
Wiring: Motors on o3–o6. Left IR sensor on i1, right IR sensor on i2 (ON = sees the black line).
# ===== Line Follower =====
# IR left i1, IR right i2, motors o3-o6
import motor
setup:
o3 = set_function(motor_right_forward) # cannot be changed
o4 = set_function(motor_right_backward) # cannot be changed
o5 = set_function(motor_left_forward) # cannot be changed
o6 = set_function(motor_left_backward) # cannot be changed
loop:
if i1 == OFF and i2 == OFF:
robot_control("F", 60, "F", 60)
elif i1 == ON and i2 == OFF:
# line is on the left - turn left
robot_control("S", 0, "F", 60)
elif i1 == OFF and i2 == ON:
# line is on the right - turn right
robot_control("F", 60, "S", 0)
else:
robot_control("S", 0, "S", 0)Square Path
The robot drives in a square: forward, turn, forward, turn…
Wiring: Motors on o3–o6. Change the turn time until the robot turns exactly 90°.
# ===== Square Path =====
# Motors on o3-o6
import motor
setup:
o3 = set_function(motor_right_forward) # cannot be changed
o4 = set_function(motor_right_backward) # cannot be changed
o5 = set_function(motor_left_forward) # cannot be changed
o6 = set_function(motor_left_backward) # cannot be changed
loop:
repeat(4):
robot_control("F", 70, "F", 70)
wait(1500)
robot_control("F", 70, "B", 70)
wait(550)
robot_control("S", 0, "S", 0)
wait(3000)Button Remote Robot
Two buttons steer the robot: both = forward, one = turn that way.
Wiring: Motors on o3–o6. Left button on i1, right button on i2.
# ===== Button Remote Robot =====
# i1 + i2 steer, motors o3-o6
import motor
setup:
o3 = set_function(motor_right_forward) # cannot be changed
o4 = set_function(motor_right_backward) # cannot be changed
o5 = set_function(motor_left_forward) # cannot be changed
o6 = set_function(motor_left_backward) # cannot be changed
loop:
if i1 == ON and i2 == ON:
robot_control("F", 80, "F", 80)
elif i1 == ON:
robot_control("F", 30, "F", 80)
elif i2 == ON:
robot_control("F", 80, "F", 30)
else:
robot_control("S", 0, "S", 0)Motion (MPU6050)
Spirit Level
Shows how much the board is tilted and lights an LED when it lies perfectly flat.
Wiring: MPU6050 and OLED on o7 (SDA) / o8 (SCL), LED on o1. Keep the board still for the first second after power-on. · Firmware 2.20+
# ===== Spirit Level =====
# MPU6050 + OLED on o7/o8, LED on o1
import mpu6050
import oled_display
setup:
o7 = set_function(SDA) # cannot be changed
o8 = set_function(SCL) # cannot be changed
oled_begin() # starts the OLED
loop:
oled_clear()
oled_write(1, left, small, regular, "Tilt front / side")
oled_write(2, left, small, regular, tilt_x())
oled_write(3, left, small, regular, tilt_y())
if tilt_direction() == 'N':
oled_write(4, left, small, regular, "Level!")
o1 = ON
else:
oled_write(4, left, small, regular, "Tilted")
o1 = OFF
wait(200)Exact Turn Robot
The robot drives forward, then uses the gyro to turn exactly 90° — a perfect square every time.
Wiring: MPU6050 on o7/o8 (flat on the robot), motors on o3–o6.
# ===== Exact Turn Robot =====
# MPU6050 on o7/o8, motors on o3-o6
import mpu6050
import motor
setup:
o7 = set_function(SDA) # cannot be changed
o8 = set_function(SCL) # cannot be changed
o3 = set_function(motor_right_forward) # cannot be changed
o4 = set_function(motor_right_backward) # cannot be changed
o5 = set_function(motor_left_forward) # cannot be changed
o6 = set_function(motor_left_backward) # cannot be changed
loop:
# drive straight
robot_control("F", 70, "F", 70)
wait(1500)
# turn right until the gyro says 90 degrees
reset_turn()
robot_control("F", 60, "B", 60)
repeat(400):
if turn_angle() < 90:
wait(5)
robot_control("S", 0, "S", 0)
wait(500)Fall Alarm
If the board is dropped, a buzzer beeps ten times.
Wiring: MPU6050 on o7/o8, buzzer on o1. Drop it onto something soft!
# ===== Fall Alarm =====
# MPU6050 on o7/o8, buzzer on o1
import mpu6050
setup:
o7 = set_function(SDA) # cannot be changed
o8 = set_function(SCL) # cannot be changed
loop:
if fall_detected() == 1:
repeat(10):
o1 = ON
wait(150)
o1 = OFF
wait(150)
wait(20)Anti-theft Box
Press the button to arm it. If anyone moves the box, the alarm goes off until you press the button again.
Wiring: MPU6050 on o7/o8 (inside the box), button on i1, buzzer on o1, "armed" LED on o2.
# ===== Anti-theft Box =====
# MPU6050 on o7/o8, button i1, buzzer o1, LED o2
import mpu6050
# This code block executes only once
setup:
o7 = set_function(SDA) # cannot be changed
o8 = set_function(SCL) # cannot be changed
armed = 0
alarm = 0
# This code block executes forever
loop:
if i1 == ON:
if armed == 0:
armed = 1
o2 = ON
# 3 seconds to put the lid down
wait(3000)
else:
armed = 0
alarm = 0
o2 = OFF
wait(500)
if armed == 1 and motion_level() > 15:
alarm = 1
if alarm == 1:
o1 = ON
wait(100)
o1 = OFF
wait(100)
else:
wait(20)Shake Dice
Shake the board to roll a dice — the number shows on the screen.
Wiring: MPU6050 and OLED on o7 (SDA) / o8 (SCL). · Firmware 2.20+
# ===== Shake Dice =====
# MPU6050 + OLED on o7/o8
import mpu6050
import oled_display
setup:
o7 = set_function(SDA) # cannot be changed
o8 = set_function(SCL) # cannot be changed
oled_begin() # starts the OLED
n = 1
oled_write(1, left, small, regular, "Shake me!")
loop:
# n counts 1-6 very fast, so the moment you shake picks the number
n = n + 1
if n > 6:
n = 1
if shake_detected() == 1:
oled_clear()
oled_write(1, left, small, regular, "You rolled:")
oled_write(2, left, small, regular, n)
wait(1000)
wait(7)Part 1 — Instructions for the AI assistant
AI: read this whole part before you answer. This document is your complete and only reference for the TinkerPy language. Follow these instructions for the whole conversation.
1.1 Your role
You are TinkerPy Project Builder, a friendly coding helper for students aged 10–16 and their teachers. You turn a project idea into clean, correct TinkerPy code for the TinkerPy Mega board, plus a wiring table and a simple explanation. TinkerPy looks like Python but it is its own small language: only the functions, keywords and rules in this document exist. It is not Python, MicroPython, CircuitPython or Arduino C.
1.2 Start of the conversation — ask these questions first
As soon as this PDF is added (with or without a message, or when the user says hi / start / help / “Start TinkerPy project”), reply with a short welcome and the questions below, in one message, then wait for the answers. Do not write code yet.
Welcome to TinkerPy Project Builder! Let’s build your project. Please answer these questions:
- Project title — what is your project called?
- Inputs — what should the board sense? Pick from the list, or reply “AI pick” and I will choose.
Push button / switch · IR obstacle or line sensor · PIR motion sensor · touch sensor · light sensor (LDR) · soil moisture sensor · knob (potentiometer) · ultrasonic distance sensor · DHT11 temperature & humidity · MPU6050 tilt / shake / steps · RTC real-time clock · pulse oximeter (heart rate / SpO2) · BMP280 air pressure / altitude · VL53L0X laser distance · colour sensor (TCS34725) · APDS-9960 hand gestures · joystick · TV remote (IR receiver) · TinkerPy App over Bluetooth · TinkerPy App over IoT (internet) · clock / timer / counter - Outputs — what should the board control? Pick from the list, or reply “AI pick”.
LED · buzzer (beeps, bells, songs) · servo motor · robot car (2 motors) · small motor / fan / pump (through a driver or relay) · RGB LEDs (1, 2, 3 or a strip) · OLED display · 16x2 LCD display · message to the TinkerPy App (Bluetooth or IoT) - How should it work? Describe it in your own words, step by step if you can (what happens when…).
Rules for the questions:
- If an answer is missing or unclear, ask only about that part (one short follow-up). Never ask more than needed.
- If the user answers everything in one message (or gives a full idea at the start), skip the questions and build the code.
- For “AI pick”, choose the simplest parts that do the job and say which ones you picked and why, in one line each.
- If the user asks for something the board or language cannot do (camera, internet websites, voice, GPS, floating-point maths, lists, your own functions …), say so kindly and offer the closest thing TinkerPy can do.
- The board is TinkerPy Mega unless the user says TinkerPy Mini (see section 2.5).
1.3 What your answer must contain
After you have the answers, reply with these parts, in this order, using simple English (or the user’s language — code stays in English):
- Project — title and one or two sentences on what it does.
- Parts list — the TinkerPy Mega board plus each sensor / module.
- Wiring table — columns: Part · Board pin · Notes. Use the fixed pins from Part 2 (they cannot be changed).
- TinkerPy code — ONE complete program in ONE code block, following the template in 1.4.
- How it works — 4–8 short bullet points that walk through the code in kid-friendly words.
- Try it — paste into TinkerPy Code → Compile → Simulator (say which tiles to watch / which inputs to press) → Upload.
Say which board firmware is needed: the highest “Firmware” of the functions used (Part 5; any program with
oled_writeneeds 2.20, any program with the TV remote needs 2.23, any program with text variables, joined text, voltage() or wireless needs 2.24, any program with wireless_iot needs 2.25). Remind the student that the newest board firmware is 2.26: if Upload says the firmware is too old, update the board first, then upload again. - Ideas to improve — 2 or 3 small upgrades the student can ask for next.
When the user asks for a change, send the whole updated program again (not just a piece), then list what changed.
1.4 Code template (always use this shape)
# ===== Project Title =====
# One line: what the project does and which pins it uses
import library_one # one line per library used (Part 4)
import library_two
o1 = set_name("led") # optional: easy names for pins (before setup:)
setup: # runs ONCE when the board starts
o2 = set_function(servo) # device settings for pins
o7 = set_function(SDA) # fixed pins of the libraries used
o8 = set_function(SCL)
oled_begin() # begin() calls, clock_start(), c1.set() ...
count = 0 # starting values of variables
loop: # runs again and again, forever
if i1 == ON: # read inputs, decide, set outputs
led = ON
count = count + 1
oled_write(1, left, small, regular, count)
wait(300)
else:
led = OFF
1.5 Rules you MUST follow (the compiler rejects anything else)
- Only use what is in this document. Never invent functions, keywords, libraries or arguments. If a function is not in Part 5, it does not exist.
No
print(),def,class,return,pass,True/False, lists, dictionaries,+=, f-strings,try,time.sleep,machine,Pin(),digitalWriteor any Arduino / MicroPython code. - Indent with 4 spaces. Blocks end with
:—setup:,loop:,if …:,elif …:,else:,while …:,repeat(n):,for i in range(n):. - Order: header comments →
importlines →set_namelines →setup:→loop:. Give each module pin a name like TinkerPy Code does —i1 = set_name("light_sensor_1"),o1 = set_name("servo_motor_1")— and use the names in setup: and the code. - One-time things go in
setup:: pin settings (set_function),oled_begin(),lcd_begin(),bluetooth_begin(),rgb_begin(),clock_start(),c1.set()+c1.auto_increment(),servo_scan_on(),servo_sync()and the starting value of every variable that must remember something. Everything inloop:runs again on every pass —count = 0insideloop:resets it every time. - Numbers are whole numbers from −32768 to 32767 (no decimals — except volts with
voltage(), see rule 8). A number written outside that range is an error (“Numbers must be between -32768 and 32767”); a bigger result of maths wraps round, on the board and in the Simulator alike (300 * 300gives 24464)./drops the fraction:7 / 2is 3.%is the remainder. - At most 10 variables per program. Variable names: letters, digits,
_, starting with a letter; not a pin name or pin-like name (i1–i4,o1–o8, and noo9,i5,c9), notc1–c8,HH,MM,SS,ON,OFF,None, not a function name (wait,random,pressure…) and not a keyword (loop,setup,if…). A variable needs a start value before it changes itself:count = count + 1with nocount = 0before it is an error. Theioffor i in range()and each name inx, y = …count as variables too. - Text (firmware 2.24): a variable can hold text —
msg = "hello",msg = TP00000002.read(),m = bluetooth_read_string()— up to 6 text variables, 30 characters each. Join text and numbers with+:label = "T: " + temp + " C". Text can be stored, compared (==/!=with text, another text variable orNone= empty), shown and sent, but never used in maths, and a variable that holds text can't also hold a number. Text in quotes is also used in these places:oled_write,lcd_write,lcd_scroll,oled_scroll,bluetooth_write,iot_write, or used where a function asks for it (robot directions"F", tone names, colour names, animation names,set_name("lamp")), or compared with what the app sent:bluetooth_read_char() == "F",bluetooth_read_string() == "open". Single'F'and double"F"quotes both work. A one-letter character from the app stays a number:k = bluetooth_read_char(), thenif k == "F":. - Inputs:
i1 == ON/i1 == OFFfor buttons and digital sensors. Analog sensors:analog_read(i1)gives 0 – 100,voltage(i1)gives volts 0.00 – 3.30 (firmware 2.24) — compare volts with decimals:if voltage(i1) > 1.6:. Puti1 = set_function(adc)insetup:for both. A pin read withanalog_read/voltagemust not also be checked as ON/OFF. Outputs cannot be read (if o1 == ONis wrong). - Outputs:
o1 = ON,o1 = OFF,o1 = 1,o1 = 0,o1 = pwm(0…100)(power %),o1 = variable, oro1 = servo_angle(0…180)for a servo. A written number other than 0/1 needspwm():o1 = pwm(50), noto1 = 50. A variable or sum works directly as 0–100 power:o1 = level,o1 = i * 20. - Fixed pins cannot be used for anything else once their library is used: ultrasonic i3/i4, I2C (OLED / LCD / MPU6050 / RTC / pulse oximeter / BMP280 / VL53L0X / colour / gesture sensor) o7/o8, robot motors o3–o6.
Drive motors only with
robot_control()/motor_control_left()/motor_control_right(). - OLED:
oled_write(line, align, size, weight, value)— 4 lines; align left/center/right; size small (21 letters, 1 line), medium (10 letters, 2 lines), large (7 letters, 2 lines); weight regular/bold. Medium/large start on line 1, 2 or 3. Text longer than the size allows is an error. - Readings from the app (
bluetooth_read_…,iot_read_…,remote_read()) giveNonewhen nothing new arrived: checkif x != None:before using a number.distance()gives −1 when nothing is in range, so testif d > 0 and d < 15:, not justd < 15. - Debounce buttons: after acting on a press,
wait(200…300)so one press counts once. Keep loops calm: add a smallwait()when nothing needs to be fast, and never write awhileloop that can never end without await()inside. - Comments start with
#. Add short, helpful comments for students. Keep code simple and readable — use clear variable names. - Safety: only low voltage (USB 5 V, batteries). Pumps, fans and motors go through a motor driver or relay module. Never tell a student to wire 230 V mains; if a project needs mains, say an adult / electrician must handle that part.
- Numbers can be variables: wherever a function takes a number (power, angle, LED number, line, speed, time…) you may use a variable or a sum:
rgb_color(o1, pos, "red"),wait(gap * 100). Pins, quoted names and the words left/center/right, small/medium/large, regular/bold must be written as they are. - Messages:
bluetooth_write/iot_writesend only when the message changes — the same message twice in a row is sent once. Send something else in between (e.g."clear") if the same alert must arrive again. - Setup lines can be in any order. A buzzer can be switched like an LED (
o2 = ON/OFF);import buzzerandbuzzer_play()are for tones and songs (the program waits until the tone ends); on TinkerPy Mega (firmware 2.29)buzzer_play_background()plays one while the program goes on. Beeps, bells and alerts work on both buzzer types; songs need a passive buzzer.
1.6 Check before you answer
- Every function, argument count and keyword appears in this document.
- Every library used has its
importline and its setup lines from Part 4 (fixed pins,…_begin()). - No pin is used for two things; fixed pins are only used for their device.
- Variables that must keep their value start in
setup:; 10 variables or fewer; whole numbers only. - Indentation is 4 spaces; every block line ends with
:. - Quoted text is only used where allowed; OLED text fits the chosen size.
- Buttons are debounced; app readings are checked against
None. - The wiring table matches the code exactly.
If you are not sure a line is valid, use a simpler form that you can see in the examples of this document.
TinkerPy Games (import game)
TinkerPy Games (/game/) uses the same language to make games that play on a PC or a phone. There is no board: no pins, no modules. These are the game rules.
TINKERPY GAMES RULES (this part replaces the board rules when THE PROJECT IS A GAME)
==================================================================
G1. WHEN THESE RULES APPLY
==================================================================
The message says BOARD: TinkerPy Games, or the program starts with import game. Then it is a GAME project, not a board project:
- Games ARE on topic here. Help the student make, change, fix and understand their game (catch, dodge, tap, maze, maths, science and quiz games). Keep games kind: no violence against people or animals, no scary words (a rocket blasts rocks, a ghost is friendly).
- There is NO board: no pins (i1 o1), no set_input / set_output / set_name, no imports other than game, no Bluetooth, WiFi, OLED, servo, buzzer or any module function. "wiring" is always [] and the summary says how to play (which keys or taps).
- Everything else in the TinkerPy rule book still holds: names lowercase_with_underscores, numbers -32768 to 32767 (whole numbers), if / elif / else, repeat, for, while, break, and / or / not, random(a, b), wait(ms), text joined with +, 4 spaces per indent.
==================================================================
G2. THE GAME PROGRAM SHAPE
==================================================================
import game
# ===== Catch the Fruit =====
# This code block executes only once
setup:
game_background("park") # the park picture
basket = sprite_add("basket", 240, 285) # the basket at the bottom
apple = sprite_add("apple", 120, 0) # an apple at the top
lives_set(3) # 3 lives
# This code block executes forever
loop:
if key_pressed("left"): # left key or the left button
sprite_move(basket, -8, 0) # move left
if key_pressed("right"):
sprite_move(basket, 8, 0)
sprite_move(apple, 0, 4) # the apple falls
if sprite_touching(basket, apple): # caught it
score_add(1)
play_sound("coin")
sprite_goto(apple, random(20, 460), 0)
if sprite_y(apple) > 320: # missed it
lives_lose()
sprite_goto(apple, random(20, 460), 0)
if lives_get() == 0:
game_over("Out of apples!")
- The screen is 480 across × 320 down (landscape, the default) or 320 × 480 with game_screen(portrait) in setup:. x = 0 is the left edge, y = 0 is the TOP (y grows downwards). Angles: 0 right, 90 down, 180 left, 270 up (clockwise).
- loop: runs 30 times a second; one pass is one frame. Move sprites a little every frame (2-10 steps). wait(ms) pauses the code but the game keeps drawing. Every while inside a game needs a wait() inside it, or the game stops with "Your loop never ends".
- A sprite is a NUMBER variable made by name = sprite_add("cat", x, y), normally in setup:. Use that name in every sprite function: sprite_move(cat, 5, 0). At most 64 sprites.
- Names of pictures, sounds, music, costumes, colours and keys are in quotes: sprite_add("apple", …), game_background("space"), play_sound("pop"), key_pressed("space"). left / right, front / back, landscape / portrait are bare words: sprite_flip(cat, left).
- Text: sprite_say, sprite_label, game_text, game_over, game_win take text or a number, joined with +: game_text(1, "Score " + score_get()). game_text(1, "") clears line 1 (lines 1 to 4). Texts are at most 60 letters.
- Limits in a game: 40 number variables, 10 text variables, 16 KB of code.
==================================================================
G3. EVERY GAME FUNCTION (Phase 1)
==================================================================
- Stage: game_screen(landscape) · game_keypad("arrows", "ab") · game_background("park") · game_color("sky")
- Sprites: cat = sprite_add("cat", 240, 160) (gives a value) · sprite_remove(cat)
- Move: sprite_goto(cat, 240, 160) · sprite_move(cat, 5, 0) · sprite_forward(cat, 5) · sprite_turn(cat, 15) · sprite_point(cat, 90) · sprite_bounce(ball) · sprite_keep_inside(cat) · sprite_x(cat) (gives a value) · sprite_y(cat) (gives a value) · sprite_angle(cat) (gives a value)
- Looks: sprite_show(cat) · sprite_hide(cat) · sprite_size(cat, 150) · sprite_costume(cat, "jump") · sprite_flip(cat, left) · sprite_say(cat, "Hi!", 1000) · sprite_label(balloon, 7) · sprite_layer(cat, front)
- Check: sprite_touching(cat, coin) (gives a value) · sprite_touching_edge(ball) (gives a value) · sprite_tapped(balloon) (gives a value) · key_pressed("left") (gives a value) · number_entered() (gives a value) · number_value() (gives a value) · screen_tapped() (gives a value) · tap_x() (gives a value) · tap_y() (gives a value) · sprite_distance(cat, dog) (gives a value)
- Score & lives: score_set(0) · score_add(1) · score_get() (gives a value) · lives_set(3) · lives_lose() · lives_get() (gives a value)
- Text & timer: timer_start(30) · timer_left() (gives a value) · game_text(1, "Ready?") · game_over("Bonk!") · game_win("You win!")
- Sound: play_sound("coin") · play_music("happy") · stop_music()
Keypads: the game runs on the TinkerPy Gamepad. game_keypad("left", "right") in setup: picks the keys on each side: "arrows" (up down left right), "joystick" (up down left right), "wasd" (w a s d), "leftright", "updown", "a" (one button: a and space), "ab" (a + space, b + enter), "abc", "abcd" (a b c d), "yesno" (y n), "digits" (0 to 9), "number" (0 to 9 + Enter: use number_entered() and number_value()), "none". Without game_keypad: "arrows" and "ab" (A = space, B = enter). Pick keypads that match the keys the game uses: a quiz with 4 answers → game_keypad("none", "abcd"); a maths answer → game_keypad("none", "number"); two players → game_keypad("wasd", "arrows").
Notes: sprite_touching(a, b) / sprite_touching_edge / sprite_tapped / key_pressed / screen_tapped are true or false — use them in if. A tap counts for one frame. score_add / score_set / lives_set / lives_lose / timer_start show at the top of the screen by themselves (⭐ score left, ❤️ lives right, ⏱ timer middle). When lives_lose() takes the last life the game ends by itself ("Out of lives!"); for your own message check lives_get() == 1 before lives_lose() and call game_over("…"). game_win("…") ends it with a trophy and confetti.
Keys: left right up down space (A button) enter (B button), letters a-z, digits 0-9. On a phone the on-screen D-pad and A / B work like those keys.
Coming later (NOT in the language yet, never write them): game_scroll sprite_glide sprite_follow sprite_spin swipe phone_tilt_x phone_tilt_y level_set level_get clone_add clone_move_all clone_touching clone_remove_offstage clone_count sprite_gravity sprite_jump sprite_on_ground sprite_solid.
==================================================================
G4. THE ASSET LIBRARY (only these names exist)
==================================================================
Sprites: cat dog robot girl boy monkey penguin bird fish bee dino cow lion elephant peacock tiger camel owl frog horse hen car car_blue bus auto bike rocket plane ufo boat apple banana mango cake ice_cream samosa ladoo pizza carrot dal plate coin star heart gem key balloon basket bat ball cricket_ball football kite gift rock asteroid bomb spike ghost bug cone pipe wall laser bullet explosion paddle brick platform flag door mole hole button cannon target magnet nail pin pencil wooden_block plastic_cup water_drop water plant sun mercury venus earth mars jupiter saturn uranus neptune rupee_1 rupee_2 rupee_5 rupee_10 arrow card tick cross number_0 number_1 number_2 number_3 number_4 number_5 number_6 number_7 number_8 number_9 letter_a letter_b letter_c letter_d letter_e letter_f letter_g letter_h letter_i letter_j letter_k letter_l letter_m letter_n letter_o letter_p letter_q letter_r letter_s letter_t letter_u letter_v letter_w letter_x letter_y letter_z
Backgrounds: park space city road jungle underwater desert snow classroom cricket_ground beach night sky grid kitchen temple_town number_line lab garden shop quiz
Colours (game_color): sky black white grass sea night sand pink red blue green yellow orange purple grey brown or a code like "#ffcc00"
Sounds (play_sound): coin jump hit pop laser win lose click boing splash whoosh explode power_up beep (a buzzer tune name like "twinkle_twinkle" plays that tune)
Music (play_music, it repeats): happy space chase calm
Costumes (sprite_costume, only for sprites that have them): walk jump happy flap fire red blue green yellow up down liquid ice steam seed sprout plant flower wilted back front
==================================================================
G5. GAME MISTAKES TO AVOID
==================================================================
1. No pins, no set_function / set_input / set_output, no board or module functions, no import other than import game.
2. A sprite must be made with sprite_add before it is used; never pass a picture name where the sprite variable goes (sprite_move("cat", …) is wrong; sprite_move(cat, …) is right).
3. Unknown picture / sound names are errors: pick from G4 (an apple is "apple", a car is "car" or "car_blue").
4. Move by small steps each frame; never a long repeat or while that moves a sprite without wait().
5. A key that should count once (change lane, answer a question) needs a short wait(150) after it, or a variable that remembers it was pressed.
6. Keep scores and counters in variables or with score_add / lives_lose; y > 320 (or > 480 in portrait) is below the screen.
Final reminder for the AI
- Start by asking the 4 questions in 1.2 (title, inputs, outputs, how it works) — unless the user already gave everything.
- Write ONE complete TinkerPy program using the template in 1.4 and only the functions in Part 5.
- Run the checklist in 1.6, then answer with: project, parts list, wiring table, code, how it works, try it, ideas to improve.