Пульт управления LORA
ESP32 > Джойстик
GND > GND
3V3 > VCC
D33 > VRX
D32 > VRY
D13 > SW
ESP32 > RA-02
GND > GND
3.3V > 3.3V
D14 > RST
D26 > DIO0
D18 > NSS
D27 > MOSI
D19 > MISO
D5 > SCK
Передатчик.
main.py
# tx_lora.py
from machine import Pin, ADC, SPI
import time
import ujson
# Импортируем класс ULoRa (файл ulora.py должен быть в той же папке)
from ulora import ULoRa
# ===== LoRa (использует ULoRa.__init__(spi, pins, parameters=None)) =====
spi = SPI(1, baudrate=10000000, polarity=0, phase=0,
sck=Pin(5), mosi=Pin(27), miso=Pin(19))
pins = {"ss": 18, "reset": 14, "dio0": 26}
lora = ULoRa(spi=spi, pins=pins, parameters={
"frequency": 433000000,
"tx_power_level": 20,
"signal_bandwidth": 250000,
"spreading_factor": 7,
"coding_rate": 5,
"preamble_length": 8,
"implicitHeader": False,
"sync_word": 0x12,
"enable_CRC": True,
})
# ===== Inputs =====
joy_x = ADC(Pin(32))
joy_y = ADC(Pin(33))
joy_x.atten(ADC.ATTN_11DB)
joy_y.atten(ADC.ATTN_11DB)
joy_x.width(ADC.WIDTH_10BIT)
joy_y.width(ADC.WIDTH_10BIT)
btn = Pin(13, Pin.IN, Pin.PULL_UP)
DEADZONE = 10 # допустимая мёртвая зона (в значениях -100..100)
def map_adc_to_range(v):
# ADC 0..1023 -> -100 .. 100
return int((v - 512) * 100 / 512)
print("LoRa TX ready")
while True:
raw_x = joy_x.read()
raw_y = joy_y.read()
x = map_adc_to_range(raw_x)
y = map_adc_to_range(raw_y)
# deadzone
if abs(x) < DEADZONE: x = 0
if abs(y) < DEADZONE: y = 0
btn_state = 0 if btn.value() == 0 else 1 # 0 pressed, 1 released (keep same as earlier)
# Формат сообщения: "x,y,btn"
payload = "{},{},{}".format(x, y, btn_state)
# Отправляем (println использует begin/write/end)
try:
lora.println(payload, repeat=1) # repeat можно увеличить при помехах
except Exception as e:
# если что-то пошло не так: покажем ошибку и продолжим
print("TX error:", e)
time.sleep_ms(40)
Простой пример приемника.
main.py
# rx_lora.py
from machine import Pin, PWM, SPI
import time
import ujson
from ulora import ULoRa
# ===== LoRa init (тот же SPI/pins как на передатчике) =====
spi = SPI(1, baudrate=10000000, polarity=0, phase=0,
sck=Pin(5), mosi=Pin(27), miso=Pin(19))
pins = {"ss": 18, "reset": 14, "dio0": 26}
lora = ULoRa(spi=spi, pins=pins, parameters={
"frequency": 433000000,
"tx_power_level": 20,
"signal_bandwidth": 250000,
"spreading_factor": 7,
"coding_rate": 5,
"preamble_length": 8,
"implicitHeader": False,
"sync_word": 0x12,
"enable_CRC": True,
})
# ===== Hardware: motors + LED =====
FREQ = 1000
MAX_DUTY = 1023
# Motor A — движение (forward/back) (MX1508 IN1/IN2)
PIN_A_FWD = 12
PIN_A_BWD = 13
# Motor B — поворот (IN3/IN4)
PIN_B_FWD = 33
PIN_B_BWD = 32
pwm_a_f = PWM(Pin(PIN_A_FWD), freq=FREQ)
pwm_a_b = PWM(Pin(PIN_A_BWD), freq=FREQ)
pwm_b_f = PWM(Pin(PIN_B_FWD), freq=FREQ)
pwm_b_b = PWM(Pin(PIN_B_BWD), freq=FREQ)
led = Pin(2, Pin.OUT)
def set_motor(p_fwd, p_bwd, speed):
# speed: -100 .. 100
if speed > 0:
duty = int((speed / 100) * MAX_DUTY)
p_fwd.duty(duty)
p_bwd.duty(0)
elif speed < 0:
duty = int(((-speed) / 100) * MAX_DUTY)
p_fwd.duty(0)
p_bwd.duty(duty)
else:
p_fwd.duty(0)
p_bwd.duty(0)
def stop_all():
set_motor(pwm_a_f, pwm_a_b, 0)
set_motor(pwm_b_f, pwm_b_b, 0)
stop_all()
# ===== Control params =====
INVERT_TURN = True # если нужно инвертировать, ставь True
DEADZONE = 8
SMOOTH = 0.5 # простое IIR smoothing: new = old*SMOOTH + rx*(1-SMOOTH)
TIMEOUT_MS = 500
filtered_drive = 0.0
filtered_turn = 0.0
last_rx_ms = time.ticks_ms()
print("LoRa RX ready — listening...")
while True:
# lora.listen() устанавливает приём, listen(timeout) ждёт и отдаёт payload или None
payload = lora.listen(timeout=200) # ждём до 200 ms
if payload:
try:
text = payload.decode()
print(text)
except:
continue
parts = text.split(",")
if len(parts) != 3:
# некорректный пакет — пропустить
# можно добавить логирование
continue
try:
rx_x = int(parts[0])
rx_y = int(parts[1])
rx_btn = int(parts[2])
except:
continue
last_rx_ms = time.ticks_ms()
# LED по кнопке: если btn==1 (released) — LED off?
# В твоём старом коде led.value(1 if btn==1 else 0).
# Здесь сделаем: btn==0 (нажата) -> led.on()
if rx_btn == 0:
led.on()
else:
led.off()
# Применяем deadzone
if abs(rx_x) < DEADZONE: rx_x = 0
if abs(rx_y) < DEADZONE: rx_y = 0
# Инверсия поворота по флагу
turn = -rx_x if INVERT_TURN else rx_x
drive = rx_y
# Применяем сглаживание (IIR)
filtered_drive = filtered_drive * SMOOTH + drive * (1 - SMOOTH)
filtered_turn = filtered_turn * SMOOTH + turn * (1 - SMOOTH)
# Ограничение
left_speed = int(max(-100, min(100, filtered_drive))) # мотор A — движение
turn_speed = int(max(-100, min(100, filtered_turn))) # мотор B — поворот
# Устанавливаем моторы
set_motor(pwm_a_f, pwm_a_b, left_speed)
set_motor(pwm_b_f, pwm_b_b, turn_speed)
# debug
#print("RX:", rx_x, rx_y, rx_btn, " -> drive:", left_speed, "turn:", turn_speed)
try:
rssi = lora.packet_rssi()
print(f"[{rssi} dBm ]")
except:
rssi = "N/A"
# Таймаут потери сигнала
if time.ticks_diff(time.ticks_ms(), last_rx_ms) > TIMEOUT_MS:
stop_all()
# обновим last_rx_ms, чтобы не спамить
last_rx_ms = time.ticks_ms()


Комментарии
Отправить комментарий