Пульт управления LORA

Пульт.

Двухосевой джойстик , Кнопка включает фары. 





Контроллер заряда аккумуляторов.
ESP32 и  RA-02




 
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()


Комментарии

Популярные сообщения из этого блога

Установка micropython на ESP32

LORA Приемник и передатчик

LORA Upgrade E32 400M30S