Raspberry Pi в Velxio — это плата на холсте, которая выполняет скрипт Python для схемы, собранной вокруг неё. Компилировать ничего не нужно. Вы пишете скрипт, нажимаете Run, и один из двух движков его выполняет:
- Базовый режим выполняет скрипт в интерпретаторе Python прямо во вкладке браузера. Он запускается за несколько секунд, и это режим, которым вы будете пользоваться чаще всего.
- Режим Linux загружает настоящего гостя Linux на серверах Velxio и подключает его к той же схеме. Вы получаете оболочку, файловую систему и собственные файлы устройств ядра для I2C, SPI, UART и GPIO. Загрузка занимает около минуты.
Эта статья собирает два проекта, по одному на режим, и показывает каждый шаг. Первый — охранная станция из модулей Grove на Raspberry Pi 5: датчик движения, датчик вибрации и DHT11 на входе, зуммер, два светодиода и реле с вентилятором на выходе. Второй — стенд с датчиками на Raspberry Pi 4 с тремя устройствами I2C. Оба скрипта используют библиотеки, входящие в состав Raspberry Pi OS, поэтому они работают без изменений и на настоящей плате.
Прежде чем начать, два слова о доступе. Любой может разместить Raspberry Pi на холсте и собрать схему вокруг него. Для запуска нужен аккаунт: каждый бесплатный аккаунт получает три пробные сессии по 15 минут на семействе Raspberry Pi в любом режиме, а любой платный план снимает ограничение. Подробности — в разделе Сколько это стоит ближе к концу.
Два способа запустить один и тот же скрипт
| Базовый режим | Режим Linux | |
|---|---|---|
| Где выполняется скрипт | Интерпретатор Python во вкладке браузера | Гость Alpine Linux под QEMU на серверах Velxio |
| Время запуска | Несколько секунд | Около минуты |
| GPIO | RPi.GPIO, gpiozero, Adafruit Blinka | То же самое, плюс /dev/gpiochip0 и утилиты libgpiod |
| I2C, SPI, UART, 1-Wire | Настоящие smbus2, spidev, pyserial и w1thermsensor через эмулированные файлы устройств | Те же библиотеки через реальные узлы устройств ядра |
| Оболочка и файловая система | Нет | Да, в Serial Monitor |
| Сторонние пакеты | Из requirements.txt | Из requirements.txt, разрешаются офлайн до загрузки гостя |
| Лучше всего подходит для | Практически всего | Инструментов оболочки, подпроцессов, файлов и всего, что руководство делает из терминала |
Движок выбирается за вас. Скрипт, который запрашивает то, что умеет только Linux, например subprocess, отправляется в режим Linux, а значок на панели инструментов сообщает вам почему. Вы также можете принудительно включить режим Linux сами — именно так поступает второй проект.
Проект 1: охранная станция на Grove для Raspberry Pi 5
Модули Grove — самый быстрый способ подключить датчики к Pi: у каждого модуля одинаковый четырёхконтактный разъём, и в Velxio каждый из них подключается к гребёнке тремя проводами — SIG, VCC и GND. Эта станция следит за комнатой с помощью PIR-датчика движения и датчика вибрации, измеряет воздух с помощью DHT11 и отвечает через зуммер, зелёный светодиод для «всё спокойно», синий светодиод для «тревога» и реле, которое запускает вентилятор, когда в комнате становится тепло.
Шаг 1: создайте проект на Raspberry Pi 5
Откройте velxio.dev/editor. Диалоговое окно Start a new project открывается само. Прокрутите до раздела Raspberry Pi и нажмите на карточку Raspberry Pi 5. Карточка Raspberry Pi 4B работает так же: обе платы используют одну и ту же 40-контактную гребёнку и одни и те же библиотеки, поэтому всё нижеприведённое применимо к обеим.

Шаг 2: добавьте компоненты
Нажмите синюю кнопку Add в правом конце панели инструментов. Откроется каталог компонентов.

Введите название в поле поиска и нажмите на карточку, чтобы перетащить компонент на холст. Все модули Grove перечислены под своими именами Seeed, поэтому «Grove PIR» или «Grove relay» их находят. Добавьте эти восемь компонентов по одному:
- Grove - PIR Motion Sensor
- Grove - Vibration Sensor (SW-420)
- Grove - Temperature & Humidity Sensor (DHT11)
- Grove - Piezo Buzzer
- Grove - Green LED
- Grove - Blue LED
- Grove - Relay
- DC Motor / Fan

Компоненты можно перетаскивать в любое место холста. Если оставить Pi внизу, а модули над его гребёнкой, провода останутся короткими.
Шаг 3: подключите
Чтобы нарисовать провод, нажмите на контакт, затем нажмите на контакт на другом конце. Названия контактов появляются при наведении на компонент. Каждый модуль Grove подключается одинаково тремя проводами: SIG к GPIO, VCC к 3V3, GND к GND. Реле — исключение по питанию: его катушке нужно 5 В.
| Модуль | SIG | VCC | GND |
|---|---|---|---|
| PIR-датчик движения | GPIO17 | 3V3 | GND |
| Датчик вибрации (SW-420) | GPIO27 | 3V3 | GND |
| DHT11 | GPIO4 | 3V3 | GND |
| Зуммер | GPIO24 | 3V3 | GND |
| Зелёный светодиод | GPIO5 | 3V3 | GND |
| Синий светодиод | GPIO6 | 3V3 | GND |
| Реле | GPIO23 | 5V | GND |
У вентилятора нет собственного сигнального провода. Он подключён к переключаемым контактам реле, как нагрузка на стенде: питание идёт на COM и выходит из NO только пока катушка под напряжением.
| От | К |
|---|---|
| Raspberry Pi 5V | Реле COM |
| Реле NO | Вентилятор + |
| Вентилятор - | Raspberry Pi GND |

Шаг 4: скрипт
В проводнике файлов слева уже есть файл script.py с коротким скриптом мигания. Оставьте это имя. Базовый режим запускает первый найденный файл .py, но режим Linux запускает именно script.py, поэтому проект, чей главный файл сохраняет это имя, работает в обоих режимах.

Замените его содержимое этим скриптом:
"""Grove alarm station on a Raspberry Pi 5.
A PIR sensor and a vibration switch watch the room, a DHT11 reads the air,
and the actuators answer: a buzzer and a blue LED for the alarm, a green LED
while all is quiet, and a relay that runs a fan when the room gets warm.
Every module hangs off the header with a Grove cable, one signal pin each.
Wiring, BCM numbers (VCC and GND of each module to 3V3 and GND):
PIR motion sensor SIG GPIO17 HIGH while motion is seen
vibration SW-420 SIG GPIO27 LOW while shaking, HIGH at rest
DHT11 SIG GPIO4
buzzer SIG GPIO24
green LED GPIO5, blue LED GPIO6
relay SIG GPIO23, VCC on 5V; the contacts switch the fan:
5V to COM, NO to the fan's +, the fan's - to GND
"""
import time
import adafruit_dht
import board
from gpiozero import LED, Button, Buzzer, MotionSensor
FAN_ON_AT = 28 # degrees C
HOLD = 2.5 # seconds the alarm light stays on after the beeps
pir = MotionSensor(17)
shaker = Button(27, pull_up=True) # the SW-420 pulls SIG low while it vibrates
dht = adafruit_dht.DHT11(board.D4, use_pulseio=False)
buzzer = Buzzer(24)
quiet_led = LED(5)
alarm_led = LED(6)
fan = LED(23) # the relay coil: on() closes COM to NO
def alarm(reason, beeps, beep_time):
print(f"ALARM: {reason}")
quiet_led.off()
alarm_led.on()
buzzer.beep(on_time=beep_time, off_time=0.1, n=beeps, background=False)
time.sleep(HOLD)
alarm_led.off()
quiet_led.on()
quiet_led.on()
motion_seen = False
shaking = False
next_reading = 0
print("Station armed")
while True:
if pir.motion_detected and not motion_seen:
alarm("motion in the room", beeps=3, beep_time=0.15)
motion_seen = pir.motion_detected
if shaker.is_pressed and not shaking:
alarm("vibration on the door", beeps=1, beep_time=0.6)
shaking = shaker.is_pressed
if time.monotonic() >= next_reading:
next_reading = time.monotonic() + 2
try:
temperature, humidity = dht.temperature, dht.humidity
except RuntimeError as err: # a DHT11 misses a reading now and then
print(f"DHT11: {err}")
else:
fan.value = temperature >= FAN_ON_AT
print(f"{temperature:.0f} C {humidity:.0f} %RH fan {'on' if fan.value else 'off'}")
time.sleep(0.1)
На три вещи в нём стоит обратить внимание, потому что на реальном железе всё так же:
- SW-420 подключён так, как описано в его даташите: SIG высокий, пока модуль стоит неподвижно, и становится низким, пока он трясётся.
Buttonиз gpiozero с подтяжкой читает это как «нажато» во время тряски, поэтому скрипту не нужна никакая логика инверсии. - Реле управляется как
LED. В gpiozero нет класса для реле; катушке реле достаточно выходного устройства, которое включено или выключено. adafruit_dhtвызываетRuntimeError, когда чтение не удаётся, а DHT11 время от времени действительно пропускает чтение. Блокtry— не защитное украшение, а способ, которым предполагается пользоваться библиотекой.
Шаг 5: запустите
На панели инструментов нажмите зелёный треугольник, чтобы запустить проект. Внизу открывается Serial Monitor и сообщает, что делает движок: он загружает среду выполнения Python, устанавливает библиотеку GPIO и печатает строку вроде [Velxio] ready in 6068 ms, а затем имя запускаемого скрипта. С этого момента каждый print() в скрипте попадает туда.

Скрипт печатает Station armed, затем строку с температурой и влажностью каждые две секунды. DHT11 на холсте начинает с 31 C, поэтому реле сразу замыкается и вентилятор раскручивается: у мотора появляется стрелка направления и скорость в об/мин, а на самом реле загорается индикатор. Теперь взаимодействуйте с датчиками:
- Нажмите на купол PIR или нажмите Motion на его панели датчика. SIG становится высоким на пять секунд, скрипт печатает
ALARM: motion in the room, синий светодиод загорается, а зуммер издаёт три сигнала. - Нажмите на датчик вибрации или переключите Vibration на его панели. Один длинный сигнал и
ALARM: vibration on the door. - Перетащите ползунок температуры DHT11 ниже 28 C. При следующем чтении реле размыкается, и вентилятор замедляется до остановки.

Красный квадрат рядом с кнопкой Run останавливает скрипт. Это и есть базовый режим целиком: скрипт выполняется в вашей вкладке, контакты напрямую достигают холста, и весь цикл от Run до реагирующей схемы занимает секунды.
Проект 2: стенд с I2C-датчиками на Raspberry Pi 4, затем режим Linux
Второй проект размещает три I2C-устройства на шине разъёма и использует настоящую smbus2 для связи с ними: аналого-цифровой преобразователь Grove ADS1115 с двумя потенциометрами, датчик температуры и влажности Grove SHT31 и OLED-дисплей SSD1306. Кнопка и светодиод на обычных GPIO завершают картину. Та же схема работает на Raspberry Pi 5 без изменения ни одного провода или строки: разъём тот же.
ADS1115 здесь по причине, которая ловит многих: у Raspberry Pi нет аналогового входа. Потенциометр, подключённый напрямую к GPIO, всегда читается только как высокий или низкий уровень — и на реальной плате, и в Velxio, — поэтому ручки идут через АЦП.
Схема
Создайте новый проект на карточке Raspberry Pi 4B и добавьте эти детали из каталога:
- Grove - 4 Channel 16-bit ADC (ADS1115)
- Potentiometer, дважды
- Grove - Temperature&Humidity Sensor (SHT31)
- SSD1306 OLED (I2C, 4-pin)
- LED, Resistor 220 Ω и Pushbutton
Затем соедините их. Все три I2C-устройства используют общие GPIO2 (SDA) и GPIO3 (SCL):
| От (Raspberry Pi 4) | К |
|---|---|
| GPIO2 (SDA) | SDA ADS1115, SHT31 и OLED |
| GPIO3 (SCL) | SCL ADS1115, SHT31 и OLED |
| 3V3 | VCC ADS1115, SHT31, OLED и обоих потенциометров |
| GND | GND ADS1115, SHT31, OLED и обоих потенциометров |
| GPIO27 | Анод светодиода; катод светодиода к 220 Ω, затем к GND |
| GPIO22 | Кнопка; другая сторона кнопки к GND |
| От | К |
|---|---|
| Потенциометр 1 SIG | ADS1115 A0 |
| Потенциометр 2 SIG | ADS1115 A1 |

Скрипт
Сохраните это как script.py. Сначала он спрашивает каждый из трёх адресов, отвечает ли кто-нибудь, затем зацикливается: два канала АЦП, температура и влажность, состояние кнопки на светодиоде и показания на OLED через luma.oled.
"""Sensor rig check for a Raspberry Pi 4.
Probes the three I2C devices, then loops: two knobs through the ADS1115,
temperature and humidity from the SHT31, the readings on the OLED, and a
button that lights the LED. Everything here is what Raspberry Pi OS ships:
smbus2 for the bus, gpiozero for the LED and the button, luma.oled for
the display.
Wiring, on the 40-pin header:
I2C : ADS1115, SHT31 and OLED on GPIO2 (SDA) and GPIO3 (SCL), 3V3, GND
ADC : one potentiometer on ADS1115 A0, another on A1
GPIO : LED through 220 ohm on GPIO27, button between GPIO22 and GND
"""
import time
import smbus2
from gpiozero import LED, Button
from luma.core.interface.serial import i2c
from luma.core.render import canvas
from luma.oled.device import ssd1306
ADS1115, SHT31, OLED = 0x48, 0x44, 0x3C
NAMES = {ADS1115: "ADS1115", SHT31: "SHT31", OLED: "SSD1306 OLED"}
# ADS1115 config register, field by field (datasheet, table 8)
ADS_START = 0x8000 # OS: begin one conversion
ADS_MUX_SINGLE = 0x4000 # AINx against GND; the channel goes in bits 12 and 13
ADS_PGA_4V = 0x0200 # +/-4.096 V full scale
ADS_ONE_SHOT = 0x0100 # power down between conversions
ADS_128_SPS = 0x0080 # 128 samples per second
ADS_NO_COMP = 0x0003 # comparator off
ADS_LSB_VOLTS = 4.096 / 32768
bus = smbus2.SMBus(1)
led = LED(27)
button = Button(22, pull_up=True)
def present(address):
"""True when a device acknowledges its address on bus 1."""
try:
bus.write_quick(address)
return True
except OSError:
return False
def read_ads1115(channel):
"""One single-shot conversion on AIN0..AIN3, returned in volts."""
config = (ADS_START | ADS_MUX_SINGLE | (channel << 12)
| ADS_PGA_4V | ADS_ONE_SHOT | ADS_128_SPS | ADS_NO_COMP)
bus.write_i2c_block_data(ADS1115, 0x01, [config >> 8, config & 0xFF])
time.sleep(0.01)
hi, lo = bus.read_i2c_block_data(ADS1115, 0x00, 2)
raw = (hi << 8) | lo
if raw & 0x8000:
raw -= 0x10000
return raw * ADS_LSB_VOLTS
def read_sht31():
"""Single shot, high repeatability, no clock stretching (command 0x2400)."""
bus.i2c_rdwr(smbus2.i2c_msg.write(SHT31, [0x24, 0x00]))
time.sleep(0.02)
reply = smbus2.i2c_msg.read(SHT31, 6)
bus.i2c_rdwr(reply)
d = list(reply)
temperature = -45 + 175 * ((d[0] << 8) | d[1]) / 65535
humidity = 100 * ((d[3] << 8) | d[4]) / 65535
return temperature, humidity
print("I2C bus 1:")
for address, name in NAMES.items():
print(f" 0x{address:02X} {name}: {'found' if present(address) else 'missing'}")
print()
oled = ssd1306(i2c(port=1, address=OLED))
while True:
knob_a = read_ads1115(0)
knob_b = read_ads1115(1)
temperature, humidity = read_sht31()
pressed = button.is_pressed
led.value = pressed
print(f"A0 {knob_a:5.3f} V A1 {knob_b:5.3f} V {temperature:4.1f} C {humidity:3.0f} %RH"
+ (" button pressed" if pressed else ""))
with canvas(oled) as draw:
draw.text((0, 0), "Pi 4 sensor rig", fill="white")
draw.text((0, 16), f"A0 {knob_a:.2f}V A1 {knob_b:.2f}V", fill="white")
draw.text((0, 32), f"{temperature:.1f} C {humidity:.0f} %RH", fill="white")
draw.text((0, 48), "button " + ("down" if pressed else "up"), fill="white")
time.sleep(2)
Три детали заслуживают внимания. ADS1115 настраивается поле за полем, так, как его даташит раскладывает регистр конфигурации, поэтому каждую константу можно сверить с таблицей 8 этого документа. SHT31 читается через i2c_rdwr, потому что его команда измерения — это два байта без адреса регистра, что обычные вызовы SMBus выразить не могут. А luma.oled — та же библиотека дисплея, которую туториалы по Raspberry Pi устанавливают через pip; здесь она предустановлена, как smbus2 и gpiozero.
Сначала запустите в базовом режиме
Нажмите Run. Проверка находит три чипа, цикл печатает показания, и OLED их отображает. Поверните потенциометр — и напряжение A0 или A1 последует за ним; кликните по SHT31 — и вы сможете задать температуру и влажность, которые он сообщает; удерживайте кнопку — и светодиод загорится.

Всё до сих пор выполнялось в браузере. Следующие шаги запускают тот же самый проект на машине с Linux.
Переключение в режим Linux
- Если Serial Monitor не открыт, нажмите Serial на панели инструментов.
- В строке заголовка Serial Monitor справа нажмите Linux terminal.

- Диалоговое окно Advanced Linux mode объясняет, что сейчас произойдёт: проект перезапустится, загрузка займёт около минуты, и всё будет работать медленнее, чем в браузере. Нажмите Start Linux mode.

Гостевая система загружается в Serial Monitor, который стал настоящим терминалом. Пока она загружается, чип рядом с селектором платы на панели инструментов показывает Linux, а его подсказка сообщает, что режим Linux включён для этого проекта.

Когда появляется приглашение, Velxio копирует script.py в домашний каталог гостевой системы и запускает его. На тихом сервере приглашение появляется примерно через 20 секунд, а первые показания — примерно через минуту после нажатия кнопки. Вывод тот же, что и раньше, и OLED управляется тем же вызовом luma.oled, теперь изнутри гостевой системы через /dev/i2c-1 ядра.

Используйте оболочку
Вот для чего нужен режим Linux. Кликните внутри терминала, нажмите Ctrl+C, чтобы остановить скрипт, и вы окажетесь в приглашении root в системе Alpine Linux. Несколько строк, которые стоит попробовать:
cat /etc/os-release | head -2
ls /dev/i2c* /dev/spidev* /dev/gpiochip*
gpioset gpiochip0 27=1
ls -la /home/pi
Вторая строка перечисляет шины разъёма как узлы устройств: /dev/i2c-0, /dev/i2c-1, /dev/spidev0.0, /dev/spidev0.1 и /dev/gpiochip0. Они отвечают на те же системные вызовы, что и на Raspberry Pi, поэтому библиотека, которая открывает их сама, программа на C или ваш собственный код с ioctl общаются с деталями на холсте.
Третья строка — современный способ управлять пином из оболочки, с помощью инструментов libgpiod, которые поставляет Raspberry Pi OS Bookworm. В момент нажатия Enter светодиод на GPIO27 загорается на холсте. В гостевой системе установлена libgpiod версии 1, поэтому чип — это позиционный аргумент, как написано выше, а написание версии 2 с --chip не понимается.

Чтобы снова запустить скрипт вручную, введите python3 script.py. Чтобы вернуться к браузерному движку, нажмите Exit Linux terminal в той же строке заголовка. Выбор не сохраняется вместе с проектом: откройте его завтра — и Run снова вернётся в базовый режим.

Что работает в каждом режиме
Таблица ниже — краткая версия страницы Raspberry Pi в документации, которая была измерена по частям на живом продукте.
| Модуль | Базовый режим | Режим Linux |
|---|---|---|
RPi.GPIO, gpiozero | Предустановлены | Предустановлены |
smbus2, spidev | Предустановлены, настоящие библиотеки | Предустановлены, настоящие библиотеки поверх узлов ядра |
serial (pyserial) | Только пути UART на Pi | Настоящий pyserial на настоящем tty, /dev/serial0 |
adafruit_dht, Adafruit_DHT | Предустановлены | Предустановлены |
w1thermsensor, дерево sysfs для 1-Wire | Предустановлены | Через requirements.txt; дерево sysfs на месте |
luma.oled, RPLCD, ST7789, Adafruit Blinka | Предустановлены | Предустановлены |
gpiod и инструменты gpio* | Нет символьного устройства в браузере | Предустановлены |
cv2 (OpenCV) | Да | Нет сборки для гостевой системы |
picamera2 | Да, через вашу веб-камеру | Нет; используйте rpicam-jpeg и его аналоги |
requests, urllib | Через прокси-сервер выхода со списком разрешённых адресов | Нет сети внутри гостевой системы |
| Всё остальное на PyPI | Через requirements.txt | Через requirements.txt, когда на PyPI есть wheel для musl aarch64 |
Чего не делает ни один режим, чтобы вы не тратили пробную сессию на выяснение:
- Аналоговый вход на GPIO. У Pi нет АЦП. Поставьте ADS1115 или MCP3008 между датчиком и платой, как во втором проекте.
pigpio. Он отображает регистры периферии через/dev/gpiomem, и ничто здесь такого отображения не предоставляет. Используйте RPi.GPIO, gpiozero или, в режиме Linux, libgpiod.apt,raspi-config, рабочий стол. Гостевая система — это Alpine Linux, а не Raspberry Pi OS. Python 3 и pip там есть; менеджера пакетов и инструментов прошивки Pi там нет.- Сеть изнутри гостевой системы. Ничто в гостевой системе не может выйти в интернет или на PyPI. Пакеты, перечисленные в
requirements.txt, разрешаются до загрузки и доступны для импорта в командной строке. - Большие стеки машинного обучения. PyTorch и TensorFlow отклоняются с объяснением.
Две привычки экономят время. Назовите главный файл script.py, чтобы оба режима запускали один и тот же файл. И положите сторонние пакеты в requirements.txt рядом с ним: когда скрипт импортирует отсутствующий пакет, консоль запуска предлагает строку, а кнопка записывает её за вас.
Сколько это стоит
Любой может собрать схему с Raspberry Pi без учётной записи. Запуск же ограничен:
- Бесплатная учётная запись получает три пробные сессии по 15 минут на семействе Raspberry Pi. Сессия открывается, когда вы нажимаете Run, в любом режиме, и каждый запуск до её окончания бесплатен. Без входа в систему панель инструментов показывает чип, который предлагает войти до первого запуска.
- Любой платный план снимает ограничение. Самый дешёвый, No AI, стоит 4 USD в месяц или 36 USD в год и включает платы Raspberry Pi и STM32 с неограниченным временем запуска; Maker и Pro тоже их включают. Актуальные цены на странице цен.

Пробные сессии одинаковы на обоих движках, поэтому скрипт, которому нужен только базовый режим, должен оставаться там: он запускается за секунды вместо минуты, а гостевая система Linux — для того, что может только Linux.
Куда двигаться дальше
- Примеры из галереи для Pi: кнопка и светодиод на Pi 4, сигнализация движения PIR на Pi 5, MPU6050 через smbus2 и все шины сразу на Pi 4.
- Каталог Grove: статья о модулях Grove рассказывает, как модуль выглядит на холсте и как он эмулируется.
- Документация: страница семейства Raspberry Pi с полными таблицами совместимости и распиновками Raspberry Pi 4 и Raspberry Pi 5.
Итог
Два проекта, два движка. Сигнальная станция Grove работала в базовом режиме на Raspberry Pi 5, от пустого холста до реагирующей схемы за несколько минут, с кодом gpiozero и Adafruit DHT, который использовал бы любой обучающий материал по Raspberry Pi. Стенд с датчиками сначала работал в базовом режиме на Raspberry Pi 4, затем в режиме Linux, где тот же скрипт общался с ADS1115, SHT31 и OLED через /dev/i2c-1 ядра, а команда gpioset, набранная в командной строке, зажгла светодиод на холсте. Базовый режим покрывает большую часть того, что требует обучающий материал по Raspberry Pi; режим Linux существует для терминала, файлов и системных инструментов, ценой минуты загрузки.