发送端
import ustruct as struct
import utime
from machine import Pin, SoftSPI, ADC
from NRF24L01 import NRF24L01
import time
# 名称与引脚联系
# "miso": 17, "mosi": 16, "sck": 4, "csn": 2, "ce": 15
# 0xe1f0f0f0e1, 0xd2f0f0f0d2,两个地址
# 相当于两个电话号码,用于标记 nrf24l01,
# 两部手机通话只有知道对方的号码才可以互相拨打
# b 前缀表示该字符串是二进制数据,称为字节串
# 字节串的一个字符对应一个8位字节,可以表示从0-255之间的整数
# 如果要和常用字符串转化,可以这样:
# b"hello world".decode("utf-8")
pipes = (b"\xe1\xf0\xf0\xf0\xe1", b"\xd2\xf0\xf0\xf0\xd2")
def main():
# PS2
ps2_y = ADC(Pin(33)) # 设置33引脚为ADC数模转化引脚,用于转化ps2的y轴方向上的模拟值
ps2_y.atten(ADC.ATTN_11DB) # 配置测量量程为3.3V
ps2_x = ADC(Pin(32)) # 设置32引脚为ADC数模转化引脚,用于转化ps2的x轴方向上的模拟值
ps2_x.atten(ADC.ATTN_11DB) # 配置测量量程为3.3V
btn = Pin(25, Pin.IN, Pin.PULL_UP) # 配置ps2的按钮,可以配置成中断引脚,调用回调函数
# NRF24L01
csn = Pin(2, mode=Pin.OUT, value=1) # 设置csn,并拉高电平
ce = Pin(15, mode=Pin.OUT, value=0) # 设置ce,并拉低电平
spi = SoftSPI(sck=Pin(4), mosi=Pin(16), miso=Pin(17)) # 实例化类 SoftSPI
nrf = NRF24L01(spi, csn, ce, payload_size=4) # 实例化类 NRF24L01,并设置传播数据大小为4字节
nrf.open_tx_pipe(pipes[0]) # 开启nrf24l01的发送功能和要发送的地址(接收端的地址)
nrf.open_rx_pipe(1, pipes[1]) # 开启nrf24l01的数据接收功能和要接收地址(发送端的地址)
nrf.stop_listening() # 停止监听功能,本实例中不用接收另一个设备的数据
# 死循环
while True:
# 读取y轴模拟值
val_y = ps2_y.read()
# 读取x轴模拟值
val_x = ps2_x.read()
# 定义一个变量,为了检测是否有人控制ps2
# 如果有人控制ps2,则经过下面的循环后这个变量的值一定会变
# 反之就不变
move_num = -1
# 转动ps2的模拟值,判断ps2的移动方向和距离,试出来的
# 分别判断y轴和x轴两个方向
# 把move_num的值通过选择结构落在1,2,3,0这4个数字上
# 分别代表摇杆的4个方向
if val_y > 2000:
move_num = 0
elif val_y < 1600:
move_num = 1
if val_x > 2000:
move_num = 3
elif val_x < 1600:
move_num = 2
# 这里容易出错,为防止程序复位,采用try
if move_num != -1:
try:
# 10 表示经过一个while循环就发送多少个数据,比如move_num=0,就发送10个0
# 这里的数字可以调节ps2与操控设备的灵敏度(延迟),自己试一下,调成最好的结果
for i in range(10):
# 发送数据,打包发送 move_num,这里的字符串i不同与for循环的i,表示要发送的数据为4字节
# 若为"ii"则表示8字节
nrf.send(struct.pack("i", move_num))
# 控制台打印发送成功的消息
print("发送成功")
except OSError:
pass
main()
把上面的代码保存为main.py 放到esp32里
还有一个代码也一起放到esp32里,命名为NRF24L01.py
from micropython import const
import utime
CONFIG = const(0x00)
EN_RXADDR = const(0x02)
SETUP_AW = const(0x03)
SETUP_RETR = const(0x04)
RF_CH = const(0x05)
RF_SETUP = const(0x06)
STATUS = const(0x07)
RX_ADDR_P0 = const(0x0A)
TX_ADDR = const(0x10)
RX_PW_P0 = const(0x11)
FIFO_STATUS = const(0x17)
DYNPD = const(0x1C)
# CONFIG register
EN_CRC = const(0x08) # enable CRC
CRCO = const(0x04) # CRC encoding scheme; 0=1 byte, 1=2 bytes
PWR_UP = const(0x02) # 1=power up, 0=power down
PRIM_RX = const(0x01) # RX/TX control; 0=PTX, 1=PRX
# RF_SETUP register
POWER_0 = const(0x00) # -18 dBm
POWER_1 = const(0x02) # -12 dBm
POWER_2 = const(0x04) # -6 dBm
POWER_3 = const(0x06) # 0 dBm
SPEED_1M = const(0x00)
SPEED_2M = const(0x08)
SPEED_250K = const(0x20)
# STATUS register
RX_DR = const(0x40) # RX data ready; write 1 to clear
TX_DS = const(0x20) # TX data sent; write 1 to clear
MAX_RT = const(0x10) # max retransmits reached; write 1 to clear
# FIFO_STATUS register
RX_EMPTY = const(0x01) # 1 if RX FIFO is empty
# constants for instructions
R_RX_PL_WID = const(0x60) # read RX payload width
R_RX_PAYLOAD = const(0x61) # read RX payload
W_TX_PAYLOAD = const(0xA0) # write TX payload
FLUSH_TX = const(0xE1) # flush TX FIFO
FLUSH_RX = const(0xE2) # flush RX FIFO
NOP = const(0xFF) # use to read STATUS register
class NRF24L01:
def __init__(self, spi, cs, ce, channel=46, payload_size=16):
assert payload_size <= 32
self.buf = bytearray(1)
# store the pins
self.spi = spi
self.cs = cs
self.ce = ce
# init the SPI bus and pins
self.init_spi(4000000)
# reset everything
ce.init(ce.OUT, value=0)
cs.init(cs.OUT, value=1)
self.payload_size = payload_size
self.pipe0_read_addr = None
utime.sleep_ms(5)
# set address width to 5 bytes and check for device present
self.reg_write(SETUP_AW, 0b11)
if self.reg_read(SETUP_AW) != 0b11:
raise OSError("nRF24L01+ Hardware not responding")
# disable dynamic payloads
self.reg_write(DYNPD, 0)
# auto retransmit delay: 1750us
# auto retransmit count: 8
self.reg_write(SETUP_RETR, (6 << 4) | 8)
# set rf power and speed
self.set_power_speed(POWER_3, SPEED_250K) # Best for point to point links
# init CRC
self.set_crc(2)
# clear status flags
self.reg_write(STATUS, RX_DR | TX_DS | MAX_RT)
# set channel
self.set_channel(channel)
# flush buffers
self.flush_rx()
self.flush_tx()
def init_spi(self, baudrate):
try:
master = self.spi.MASTER
except AttributeError:
self.spi.init(baudrate=baudrate, polarity=0, phase=0)
else:
self.spi.init(master, baudrate=baudrate, polarity=0, phase=0)
def reg_read(self, reg):
self.cs(0)
self.spi.readinto(self.buf, reg)
self.spi.readinto(self.buf)
self.cs(1)
return self.buf[0]
def reg_write_bytes(self, reg, buf):
self.cs(0)
self.spi.readinto(self.buf, 0x20 | reg)
self.spi.write(buf)
self.cs(1)
return self.buf[0]
def reg_write(self, reg, value):
self.cs(0)
self.spi.readinto(self.buf, 0x20 | reg)
ret = self.buf[0]
self.spi.readinto(self.buf, value)
self.cs(1)
return ret
def flush_rx(self):
self.cs(0)
self.spi.readinto(self.buf, FLUSH_RX)
self.cs(1)
def flush_tx(self):
self.cs(0)
self.spi.readinto(self.buf, FLUSH_TX)
self.cs(1)
# power is one of POWER_x defines; speed is one of SPEED_x defines
def set_power_speed(self, power, speed):
setup = self.reg_read(RF_SETUP) & 0b11010001
self.reg_write(RF_SETUP, setup | power | speed)
# length in bytes: 0, 1 or 2
def set_crc(self, length):
config = self.reg_read(CONFIG) & ~(CRCO | EN_CRC)
if length == 0:
pass
elif length == 1:
config |= EN_CRC
else:
config |= EN_CRC | CRCO
self.reg_write(CONFIG, config)
def set_channel(self, channel):
self.reg_write(RF_CH, min(channel, 125))
# address should be a bytes object 5 bytes long
def open_tx_pipe(self, address):
assert len(address) == 5
self.reg_write_bytes(RX_ADDR_P0, address)
self.reg_write_bytes(TX_ADDR, address)
self.reg_write(RX_PW_P0, self.payload_size)
# address should be a bytes object 5 bytes long
# pipe 0 and 1 have 5 byte address
# pipes 2-5 use same 4 most-significant bytes as pipe 1, plus 1 extra byte
def open_rx_pipe(self, pipe_id, address):
assert len(address) == 5
assert 0 <= pipe_id <= 5
if pipe_id == 0:
self.pipe0_read_addr = address
if pipe_id < 2:
self.reg_write_bytes(RX_ADDR_P0 + pipe_id, address)
else:
self.reg_write(RX_ADDR_P0 + pipe_id, address[0])
self.reg_write(RX_PW_P0 + pipe_id, self.payload_size)
self.reg_write(EN_RXADDR, self.reg_read(EN_RXADDR) | (1 << pipe_id))
def start_listening(self):
self.reg_write(CONFIG, self.reg_read(CONFIG) | PWR_UP | PRIM_RX)
self.reg_write(STATUS, RX_DR | TX_DS | MAX_RT)
if self.pipe0_read_addr is not None:
self.reg_write_bytes(RX_ADDR_P0, self.pipe0_read_addr)
self.flush_rx()
self.flush_tx()
self.ce(1)
utime.sleep_us(130)
def stop_listening(self):
self.ce(0)
self.flush_tx()
self.flush_rx()
# returns True if any data available to recv
def any(self):
return not bool(self.reg_read(FIFO_STATUS) & RX_EMPTY)
def recv(self):
# get the data
self.cs(0)
self.spi.readinto(self.buf, R_RX_PAYLOAD)
buf = self.spi.read(self.payload_size)
self.cs(1)
# clear RX ready flag
self.reg_write(STATUS, RX_DR)
return buf
# blocking wait for tx complete
def send(self, buf, timeout=500):
self.send_start(buf)
start = utime.ticks_ms()
result = None
while result is None and utime.ticks_diff(utime.ticks_ms(), start) < timeout:
result = self.send_done() # 1 == success, 2 == fail
if result == 2:
raise OSError("send failed")
# non-blocking tx
def send_start(self, buf):
# power up
self.reg_write(CONFIG, (self.reg_read(CONFIG) | PWR_UP) & ~PRIM_RX)
utime.sleep_us(150)
# send the data
self.cs(0)
self.spi.readinto(self.buf, W_TX_PAYLOAD)
self.spi.write(buf)
if len(buf) < self.payload_size:
self.spi.write(b"\x00" * (self.payload_size - len(buf))) # pad out data
self.cs(1)
# enable the chip so it can send the data
self.ce(1)
utime.sleep_us(15) # needs to be >10us
self.ce(0)
# returns None if send still in progress, 1 for success, 2 for fail
def send_done(self):
if not (self.reg_read(STATUS) & (TX_DS | MAX_RT)):
return None # tx not finished
# either finished or failed: get and clear status flags, power down
status = self.reg_write(STATUS, RX_DR | TX_DS | MAX_RT)
self.reg_write(CONFIG, self.reg_read(CONFIG) & ~PWR_UP)
return 1 if status & TX_DS else 2
下面是接收端
将以下代码保存为main.py,存入esp32中
import ustruct as struct
import utime
from machine import Pin, SoftSPI, ADC, PWM
from NRF24L01 import NRF24L01
import time
# 定义 18 19 引脚为PWM,频率50hz,即一个周期0.02秒,并初始化占空比为0
p18 = PWM(Pin(18, Pin.OUT), freq=50, duty=0)
p19 = PWM(Pin(19, Pin.OUT), freq=50, duty=0)
# 设置舵机初始角度值为0
angle = 0
# 相关引脚接口
# "spi": -1, "miso": 17, "mosi": 16, "sck": 4, "csn": 2, "ce": 15
pipes = (b"\xe1\xf0\xf0\xf0\xe1", b"\xd2\xf0\xf0\xf0\xd2")
# 定义一个布尔值
nrf = None
# 限定舵机的转动角度为0-180
# 型号 sg90
# 舵机在20ms为一个周期内:若高电平时间为0.5ms,则转动方向为0
# 有下表:0.5ms 0°
# 1ms 45°
# 1.5ms 90°
# 2ms 135°
# 2.5ms 180°
# 从0-180之间一共有2ms的时间间隔,推得舵机转动1°的占空比公式
# pw = ((angle / 180 * 2000) + 500) / 20000 * 65535
# 同理 pw = ((angle / 180 * 2) + 0.5) / 20 * 65535
# 水平方向的左转动
def move_l():
# 定义全局变量,方便在函数内部修改全局变量
global angle
# 让舵机转1度
angle += 1
if angle > 180:
angle = 180
pw = ((angle / 180 * 2000) + 500) / 20000 * 65535
# 括号内的值为0-65535,其与65535的比值为占空比
p19.duty_u16(int(pw))
# 水平方向的右转动
def move_r():
# 定义全局变量,方便在函数内部修改全局变量
global angle
# 让舵机相反方向转1度
angle -= 1
if angle < 0:
angle = 0
pw = ((angle / 180 * 2000) + 500) / 20000 * 65535
# 括号内的值为0-65535,其与65535的比值为占空比
p19.duty_u16(int(pw))
def move_u():
# 定义全局变量,方便在函数内部修改全局变量
global angle
# 让舵机转1度
angle += 1
if angle > 180:
angle = 180
pw = ((angle / 180 * 2000) + 500) / 20000 * 65535
# 括号内的值为0-65535,其与65535的比值为占空比
p18.duty_u16(int(pw))
def move_d():
# 定义全局变量,方便在函数内部修改全局变量
global angle
# 让舵机相反方向转1度
angle -= 1
if angle < 0:
angle = 0
pw = ((angle / 180 * 2000) + 500) / 20000 * 65535
# 括号内的值为0-65535,其与65535的比值为占空比
p18.duty_u16(int(pw))
# 回调函数
# 当NRF24L01接收到数据时在 8 脚会产生一个低电平
def call_back(*argc):
print("接收到数据")
# 如果接收的数据不为空,即 nrf.any()=True
if nrf.any():
while nrf.any():
# 读取数据
buf = nrf.recv()
# 解包数据
(move_direction,) = struct.unpack("i", buf)
# 打印接收到的数据
print("received:", move_direction)
if move_direction == 0:
move_l()
elif move_direction == 1:
move_r()
elif move_direction == 2:
move_u()
elif move_direction == 3:
move_d()
# 调解数值可以调节灵敏度
time.sleep_ms(100)
def main():
# 声明全局变量
global nrf
# 把NRF24L01的8脚接到esp32的5脚,作为外部中断
p5 = Pin(5, Pin.IN)
# 设置GPIO5为低电平中断
p5.irq(trigger=Pin.IRQ_FALLING, handler=call_back)
# 相关引脚设置
csn = Pin(2, mode=Pin.OUT, value=1)
ce = Pin(15, mode=Pin.OUT, value=0)
# 实例化类
spi = SoftSPI(sck=Pin(4), mosi=Pin(16), miso=Pin(17))
nrf = NRF24L01(spi, csn, ce, payload_size=4)
# 打开发送与接收功能
nrf.open_tx_pipe(pipes[1])
nrf.open_rx_pipe(1, pipes[0])
# 停止监听
nrf.stop_listening()
# 开始监听,先关再开降低启动时的噪音信号
nrf.start_listening()
# 一直循环,直到监听到信号,就进入中断函数
while True:
print("waiting ....")
time.sleep(2)
main()
再把前面的NRF24L01.py 复制,放到接收端的esp32,即可。