dev #1
@ -3,6 +3,7 @@ import RPi.GPIO as GPIO
|
|||||||
from .util import sleep, get_addr
|
from .util import sleep, get_addr
|
||||||
from .init_regs_value import init_regs_value
|
from .init_regs_value import init_regs_value
|
||||||
from .regs_addr import regs_addr
|
from .regs_addr import regs_addr
|
||||||
|
|
||||||
CONFIG_REGS = regs_addr["CONFIG_REGS"]
|
CONFIG_REGS = regs_addr["CONFIG_REGS"]
|
||||||
STROBES = regs_addr["STROBES"]
|
STROBES = regs_addr["STROBES"]
|
||||||
STATUS = regs_addr["STATUS"]
|
STATUS = regs_addr["STATUS"]
|
||||||
@ -20,6 +21,7 @@ def write_reg(spi: SpiDev, addr: int, value: int):
|
|||||||
spi.xfer2([addr, value])
|
spi.xfer2([addr, value])
|
||||||
sleep(0.1)
|
sleep(0.1)
|
||||||
|
|
||||||
|
|
||||||
def read_register(spi: SpiDev, addr: int):
|
def read_register(spi: SpiDev, addr: int):
|
||||||
READ_SINGLE = get_addr("READ_SINGLE")
|
READ_SINGLE = get_addr("READ_SINGLE")
|
||||||
# Send address | 0x80 (read), then 0x00 dummy to clock in response
|
# Send address | 0x80 (read), then 0x00 dummy to clock in response
|
||||||
@ -27,6 +29,7 @@ def read_register(spi: SpiDev, addr: int):
|
|||||||
# sleep(0.1)
|
# sleep(0.1)
|
||||||
return response[1]
|
return response[1]
|
||||||
|
|
||||||
|
|
||||||
def write_burst(spi: SpiDev, addr: int, data):
|
def write_burst(spi: SpiDev, addr: int, data):
|
||||||
"""Write multiple bytes using burst write"""
|
"""Write multiple bytes using burst write"""
|
||||||
WRITE_BURST = get_addr("WRITE_BURST")
|
WRITE_BURST = get_addr("WRITE_BURST")
|
||||||
@ -34,6 +37,7 @@ def write_burst(spi: SpiDev, addr: int, data):
|
|||||||
spi.xfer2(tbuf)
|
spi.xfer2(tbuf)
|
||||||
sleep(0.1)
|
sleep(0.1)
|
||||||
|
|
||||||
|
|
||||||
def read_burst(spi: SpiDev, addr: int, length):
|
def read_burst(spi: SpiDev, addr: int, length):
|
||||||
"""Read multiple bytes using burst read"""
|
"""Read multiple bytes using burst read"""
|
||||||
READ_BURST = get_addr("READ_BURST")
|
READ_BURST = get_addr("READ_BURST")
|
||||||
@ -42,16 +46,19 @@ def read_burst(spi: SpiDev, addr: int, length):
|
|||||||
sleep(0.1)
|
sleep(0.1)
|
||||||
return response[1:] # Skip status byte
|
return response[1:] # Skip status byte
|
||||||
|
|
||||||
|
|
||||||
def strobe(spi: SpiDev, command: int):
|
def strobe(spi: SpiDev, command: int):
|
||||||
"""Send a command strobe to CC2500"""
|
"""Send a command strobe to CC2500"""
|
||||||
spi.xfer2([command])
|
spi.xfer2([command])
|
||||||
sleep(0.1)
|
sleep(0.1)
|
||||||
|
|
||||||
|
|
||||||
def init_cc_2500(spi: SpiDev):
|
def init_cc_2500(spi: SpiDev):
|
||||||
for reg_name, value in init_regs_value.items():
|
for reg_name, value in init_regs_value.items():
|
||||||
addr = get_addr(reg_name)
|
addr = get_addr(reg_name)
|
||||||
write_reg(spi, addr, value)
|
write_reg(spi, addr, value)
|
||||||
|
|
||||||
|
|
||||||
def setup_spi():
|
def setup_spi():
|
||||||
spi = SpiDev()
|
spi = SpiDev()
|
||||||
spi.open(0, 0) # Bus 0, CE0 (Pin 24)
|
spi.open(0, 0) # Bus 0, CE0 (Pin 24)
|
||||||
@ -66,6 +73,7 @@ def setup_gpio(GDO0_PIN=17, GDO2_PIN=27):
|
|||||||
GPIO.setup(GDO2_PIN, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
|
GPIO.setup(GDO2_PIN, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
|
||||||
sleep(0.1)
|
sleep(0.1)
|
||||||
|
|
||||||
|
|
||||||
def reset(spi: SpiDev):
|
def reset(spi: SpiDev):
|
||||||
# print("Sending SRES (reset)...")
|
# print("Sending SRES (reset)...")
|
||||||
spi.xfer2([SRES])
|
spi.xfer2([SRES])
|
||||||
@ -93,10 +101,10 @@ def test_read_write_reg(spi: SpiDev, dbg=False):
|
|||||||
write_reg(spi, FIFOTHR, test_value)
|
write_reg(spi, FIFOTHR, test_value)
|
||||||
check = read_register(spi, FIFOTHR)
|
check = read_register(spi, FIFOTHR)
|
||||||
write_reg(spi, FIFOTHR, initial_val)
|
write_reg(spi, FIFOTHR, initial_val)
|
||||||
if((check != test_value) or dbg):
|
if (check != test_value) or dbg:
|
||||||
print("initial value ", initial_val)
|
print("initial value ", initial_val)
|
||||||
print("test value ", test_value)
|
print("test value ", test_value)
|
||||||
print("check ", check)
|
print("check ", check)
|
||||||
if(not dbg):
|
if not dbg:
|
||||||
raise Exception("Test Read+Write failed")
|
raise Exception("Test Read+Write failed")
|
||||||
return check == test_value
|
return check == test_value
|
26
src/main.py
26
src/main.py
@ -2,8 +2,19 @@ import spidev
|
|||||||
from .util import print_gdo_state, sleep, get_addr, dump_regs, debug, GDO0_PIN, GDO2_PIN
|
from .util import print_gdo_state, sleep, get_addr, dump_regs, debug, GDO0_PIN, GDO2_PIN
|
||||||
from .receive import rx_data_rf
|
from .receive import rx_data_rf
|
||||||
from .transmit import transmit_packet
|
from .transmit import transmit_packet
|
||||||
from .common import reset, setup_spi, setup_gpio, read_register, test_read_write_reg, init_cc_2500, write_reg, SRES, SNOP, MARCSTATE, VERSION
|
from .common import (
|
||||||
|
reset,
|
||||||
|
setup_spi,
|
||||||
|
setup_gpio,
|
||||||
|
read_register,
|
||||||
|
test_read_write_reg,
|
||||||
|
init_cc_2500,
|
||||||
|
write_reg,
|
||||||
|
SRES,
|
||||||
|
SNOP,
|
||||||
|
MARCSTATE,
|
||||||
|
VERSION,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def menu():
|
def menu():
|
||||||
@ -39,7 +50,14 @@ if __name__ == "__main__":
|
|||||||
reg_name = input("Register name: ")
|
reg_name = input("Register name: ")
|
||||||
addr = get_addr(reg_name)
|
addr = get_addr(reg_name)
|
||||||
value = read_register(spi, addr)
|
value = read_register(spi, addr)
|
||||||
print("Address: " + hex(addr)+ ", Value: " + hex(value) +" == " + str(value))
|
print(
|
||||||
|
"Address: "
|
||||||
|
+ hex(addr)
|
||||||
|
+ ", Value: "
|
||||||
|
+ hex(value)
|
||||||
|
+ " == "
|
||||||
|
+ str(value)
|
||||||
|
)
|
||||||
elif cmd == 2:
|
elif cmd == 2:
|
||||||
reg_name = input("Register name: ")
|
reg_name = input("Register name: ")
|
||||||
addr = get_addr(reg_name)
|
addr = get_addr(reg_name)
|
||||||
@ -51,7 +69,7 @@ if __name__ == "__main__":
|
|||||||
elif cmd == 3:
|
elif cmd == 3:
|
||||||
dump_regs(spi, True)
|
dump_regs(spi, True)
|
||||||
elif cmd == 4:
|
elif cmd == 4:
|
||||||
while(True):
|
while True:
|
||||||
rx_data_rf(spi)
|
rx_data_rf(spi)
|
||||||
elif cmd == 5:
|
elif cmd == 5:
|
||||||
transmit_packet(spi)
|
transmit_packet(spi)
|
||||||
|
@ -3,19 +3,20 @@ from pathlib import Path
|
|||||||
|
|
||||||
from spidev import SpiDev
|
from spidev import SpiDev
|
||||||
from .util import sleep, get_addr, digital_read, GDO0_PIN, GDO2_PIN, debug, delay
|
from .util import sleep, get_addr, digital_read, GDO0_PIN, GDO2_PIN, debug, delay
|
||||||
import time
|
|
||||||
from .common import strobe, read_register
|
from .common import strobe, read_register
|
||||||
|
|
||||||
SIDLE = get_addr('SIDLE')
|
SIDLE = get_addr("SIDLE")
|
||||||
SFRX = get_addr('SFRX')
|
SFRX = get_addr("SFRX")
|
||||||
SRX = get_addr('SRX')
|
SRX = get_addr("SRX")
|
||||||
RXFIFO = get_addr('RXFIFO')
|
RXFIFO = get_addr("RXFIFO")
|
||||||
RSSI = get_addr('RSSI')
|
RSSI = get_addr("RSSI")
|
||||||
|
|
||||||
|
|
||||||
def print_packet(spi: SpiDev, packet_length):
|
def print_packet(spi: SpiDev, packet_length):
|
||||||
for i in range(packet_length):
|
for i in range(packet_length):
|
||||||
print(f", byte: {0}: 0x{1}", i, read_register(spi, RXFIFO))
|
print(f", byte: {0}: 0x{1}", i, read_register(spi, RXFIFO))
|
||||||
|
|
||||||
|
|
||||||
# Save packet to a timestamped binary file
|
# Save packet to a timestamped binary file
|
||||||
def save_packet(packet: list[int]):
|
def save_packet(packet: list[int]):
|
||||||
# Create directory to store packets if it doesn't exist
|
# Create directory to store packets if it doesn't exist
|
||||||
@ -43,6 +44,7 @@ def get_rssi_from_pkt(packet: list[int]):
|
|||||||
def get_rssi_from_reg(spi: SpiDev):
|
def get_rssi_from_reg(spi: SpiDev):
|
||||||
return read_register(spi, RSSI)
|
return read_register(spi, RSSI)
|
||||||
|
|
||||||
|
|
||||||
def get_signal_strength_rssi_raw(rssi_raw: int) -> float:
|
def get_signal_strength_rssi_raw(rssi_raw: int) -> float:
|
||||||
if rssi_raw >= 128:
|
if rssi_raw >= 128:
|
||||||
rssi_dec = rssi_raw - 256
|
rssi_dec = rssi_raw - 256
|
||||||
@ -50,6 +52,7 @@ def get_signal_strength_rssi_raw(rssi_raw: int) -> float:
|
|||||||
rssi_dec = rssi_raw
|
rssi_dec = rssi_raw
|
||||||
return rssi_dec / 2.0 - 74 # According to CC2500 datasheet
|
return rssi_dec / 2.0 - 74 # According to CC2500 datasheet
|
||||||
|
|
||||||
|
|
||||||
def flush_rx(spi: SpiDev):
|
def flush_rx(spi: SpiDev):
|
||||||
# Make sure that the radio is in IDLE state before flushing the FIFO
|
# Make sure that the radio is in IDLE state before flushing the FIFO
|
||||||
# (Unless RXOFF_MODE has been changed, the radio should be in IDLE state at this point)
|
# (Unless RXOFF_MODE has been changed, the radio should be in IDLE state at this point)
|
||||||
@ -60,12 +63,13 @@ def flush_rx(spi: SpiDev):
|
|||||||
strobe(spi, SFRX)
|
strobe(spi, SFRX)
|
||||||
delay(10)
|
delay(10)
|
||||||
|
|
||||||
|
|
||||||
def rx_data_rf(spi: SpiDev):
|
def rx_data_rf(spi: SpiDev):
|
||||||
strobe(spi, SRX)
|
strobe(spi, SRX)
|
||||||
gdo2_state = False
|
gdo2_state = False
|
||||||
count = 0
|
count = 0
|
||||||
strength = 0
|
strength = 0
|
||||||
while(gdo2_state == False):
|
while gdo2_state == False:
|
||||||
gdo2_state = digital_read(GDO2_PIN)
|
gdo2_state = digital_read(GDO2_PIN)
|
||||||
delay(1)
|
delay(1)
|
||||||
count = count + 1
|
count = count + 1
|
||||||
@ -73,7 +77,7 @@ def rx_data_rf(spi: SpiDev):
|
|||||||
flush_rx(spi)
|
flush_rx(spi)
|
||||||
# print("ERR NO DATA")
|
# print("ERR NO DATA")
|
||||||
return
|
return
|
||||||
while(gdo2_state == True):
|
while gdo2_state == True:
|
||||||
gdo2_state = digital_read(GDO2_PIN)
|
gdo2_state = digital_read(GDO2_PIN)
|
||||||
delay(100)
|
delay(100)
|
||||||
packet_length: int = read_register(spi, RXFIFO)
|
packet_length: int = read_register(spi, RXFIFO)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
from typing import TypedDict
|
from typing import TypedDict
|
||||||
|
|
||||||
|
|
||||||
class ConfigRegs(TypedDict):
|
class ConfigRegs(TypedDict):
|
||||||
IOCFG2: int
|
IOCFG2: int
|
||||||
IOCFG1: int
|
IOCFG1: int
|
||||||
@ -49,6 +50,7 @@ class ConfigRegs(TypedDict):
|
|||||||
TEST1: int
|
TEST1: int
|
||||||
TEST0: int
|
TEST0: int
|
||||||
|
|
||||||
|
|
||||||
class Strobes(TypedDict):
|
class Strobes(TypedDict):
|
||||||
SRES: int
|
SRES: int
|
||||||
SFSTXON: int
|
SFSTXON: int
|
||||||
@ -65,6 +67,7 @@ class Strobes(TypedDict):
|
|||||||
SWORRST: int
|
SWORRST: int
|
||||||
SNOP: int
|
SNOP: int
|
||||||
|
|
||||||
|
|
||||||
class Status(TypedDict):
|
class Status(TypedDict):
|
||||||
PARTNUM: int
|
PARTNUM: int
|
||||||
VERSION: int
|
VERSION: int
|
||||||
@ -80,20 +83,24 @@ class Status(TypedDict):
|
|||||||
RXBYTES: int
|
RXBYTES: int
|
||||||
NUM_RXBYTES: int
|
NUM_RXBYTES: int
|
||||||
|
|
||||||
|
|
||||||
class Memory(TypedDict):
|
class Memory(TypedDict):
|
||||||
PATABLE: int
|
PATABLE: int
|
||||||
TXFIFO: int
|
TXFIFO: int
|
||||||
RXFIFO: int
|
RXFIFO: int
|
||||||
|
|
||||||
|
|
||||||
class Masks(TypedDict):
|
class Masks(TypedDict):
|
||||||
LQI_RX: int
|
LQI_RX: int
|
||||||
CRC_OK: int
|
CRC_OK: int
|
||||||
|
|
||||||
|
|
||||||
class Access(TypedDict):
|
class Access(TypedDict):
|
||||||
WRITE_BURST: int
|
WRITE_BURST: int
|
||||||
READ_SINGLE: int
|
READ_SINGLE: int
|
||||||
READ_BURST: int
|
READ_BURST: int
|
||||||
|
|
||||||
|
|
||||||
class RegsAddr(TypedDict):
|
class RegsAddr(TypedDict):
|
||||||
CONFIG_REGS: ConfigRegs
|
CONFIG_REGS: ConfigRegs
|
||||||
STROBES: Strobes
|
STROBES: Strobes
|
||||||
@ -151,7 +158,7 @@ regs_addr: RegsAddr = {
|
|||||||
"AGCTEST": 0x2B,
|
"AGCTEST": 0x2B,
|
||||||
"TEST2": 0x2C,
|
"TEST2": 0x2C,
|
||||||
"TEST1": 0x2D,
|
"TEST1": 0x2D,
|
||||||
"TEST0": 0x2E
|
"TEST0": 0x2E,
|
||||||
},
|
},
|
||||||
"STROBES": {
|
"STROBES": {
|
||||||
"SRES": 0x30,
|
"SRES": 0x30,
|
||||||
@ -167,7 +174,7 @@ regs_addr: RegsAddr = {
|
|||||||
"SFRX": 0x3A,
|
"SFRX": 0x3A,
|
||||||
"SFTX": 0x3B,
|
"SFTX": 0x3B,
|
||||||
"SWORRST": 0x3C,
|
"SWORRST": 0x3C,
|
||||||
"SNOP": 0x3D
|
"SNOP": 0x3D,
|
||||||
},
|
},
|
||||||
"STATUS": {
|
"STATUS": {
|
||||||
"PARTNUM": 0x30,
|
"PARTNUM": 0x30,
|
||||||
@ -182,20 +189,9 @@ regs_addr: RegsAddr = {
|
|||||||
"VCO_VC_DAC": 0x39,
|
"VCO_VC_DAC": 0x39,
|
||||||
"TXBYTES": 0x3A,
|
"TXBYTES": 0x3A,
|
||||||
"RXBYTES": 0x3B,
|
"RXBYTES": 0x3B,
|
||||||
"NUM_RXBYTES": 0x7F
|
"NUM_RXBYTES": 0x7F,
|
||||||
},
|
},
|
||||||
"MEMORY": {
|
"MEMORY": {"PATABLE": 0x3E, "TXFIFO": 0x3F, "RXFIFO": 0x3F},
|
||||||
"PATABLE": 0x3E,
|
"MASKS": {"LQI_RX": 0x01, "CRC_OK": 0x80},
|
||||||
"TXFIFO": 0x3F,
|
"ACCESS": {"WRITE_BURST": 0x40, "READ_SINGLE": 0x80, "READ_BURST": 0xC0},
|
||||||
"RXFIFO": 0x3F
|
|
||||||
},
|
|
||||||
"MASKS": {
|
|
||||||
"LQI_RX": 0x01,
|
|
||||||
"CRC_OK": 0x80
|
|
||||||
},
|
|
||||||
"ACCESS": {
|
|
||||||
"WRITE_BURST": 0x40,
|
|
||||||
"READ_SINGLE": 0x80,
|
|
||||||
"READ_BURST": 0xC0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -2,12 +2,13 @@ from .common import strobe, write_burst
|
|||||||
from .util import get_addr
|
from .util import get_addr
|
||||||
|
|
||||||
|
|
||||||
SIDLE = get_addr('SIDLE')
|
SIDLE = get_addr("SIDLE")
|
||||||
SFTX = get_addr('SFTX')
|
SFTX = get_addr("SFTX")
|
||||||
STX = get_addr('STX')
|
STX = get_addr("STX")
|
||||||
WRITE_BURST = get_addr("WRITE_BURST")
|
WRITE_BURST = get_addr("WRITE_BURST")
|
||||||
TXFIFO_BURST = 0x7F
|
TXFIFO_BURST = 0x7F
|
||||||
|
|
||||||
|
|
||||||
def transmit_packet(spi):
|
def transmit_packet(spi):
|
||||||
strobe(spi, SIDLE)
|
strobe(spi, SIDLE)
|
||||||
strobe(spi, SFTX)
|
strobe(spi, SFTX)
|
||||||
@ -18,4 +19,3 @@ def transmit_packet(spi):
|
|||||||
data[3] = 3
|
data[3] = 3
|
||||||
data[4] = 4
|
data[4] = 4
|
||||||
write_burst(spi, TXFIFO_BURST, data)
|
write_burst(spi, TXFIFO_BURST, data)
|
||||||
|
|
||||||
|
10
src/util.py
10
src/util.py
@ -2,10 +2,12 @@ from spidev import SpiDev
|
|||||||
from .regs_addr import regs_addr
|
from .regs_addr import regs_addr
|
||||||
import RPi.GPIO as GPIO
|
import RPi.GPIO as GPIO
|
||||||
import time
|
import time
|
||||||
|
|
||||||
debug = True
|
debug = True
|
||||||
GDO0_PIN = 17
|
GDO0_PIN = 17
|
||||||
GDO2_PIN = 27
|
GDO2_PIN = 27
|
||||||
|
|
||||||
|
|
||||||
def rr(spi: SpiDev, addr):
|
def rr(spi: SpiDev, addr):
|
||||||
READ_SINGLE = get_addr("READ_SINGLE")
|
READ_SINGLE = get_addr("READ_SINGLE")
|
||||||
# Send address | 0x80 (read), then 0x00 dummy to clock in response
|
# Send address | 0x80 (read), then 0x00 dummy to clock in response
|
||||||
@ -13,15 +15,18 @@ def rr(spi: SpiDev, addr):
|
|||||||
sleep(0.1)
|
sleep(0.1)
|
||||||
return response[1]
|
return response[1]
|
||||||
|
|
||||||
|
|
||||||
def print_gdo_state(GDO0_PIN=17, GDO2_PIN=27):
|
def print_gdo_state(GDO0_PIN=17, GDO2_PIN=27):
|
||||||
gdo0 = GPIO.input(GDO0_PIN) # Reads 1 or 0
|
gdo0 = GPIO.input(GDO0_PIN) # Reads 1 or 0
|
||||||
gdo2 = GPIO.input(GDO2_PIN)
|
gdo2 = GPIO.input(GDO2_PIN)
|
||||||
print(f"GDO0 (GPIO{GDO0_PIN}): {gdo0}, GDO2 (GPIO{GDO2_PIN}): {gdo2}")
|
print(f"GDO0 (GPIO{GDO0_PIN}): {gdo0}, GDO2 (GPIO{GDO2_PIN}): {gdo2}")
|
||||||
sleep(0.1)
|
sleep(0.1)
|
||||||
|
|
||||||
|
|
||||||
def digital_read(GDO_PIN: int):
|
def digital_read(GDO_PIN: int):
|
||||||
return GPIO.input(GDO_PIN)
|
return GPIO.input(GDO_PIN)
|
||||||
|
|
||||||
|
|
||||||
def get_addr(name: str):
|
def get_addr(name: str):
|
||||||
addr = 0x00
|
addr = 0x00
|
||||||
stop = False
|
stop = False
|
||||||
@ -34,6 +39,7 @@ def get_addr(name: str):
|
|||||||
raise Exception("Failed to find address for " + name)
|
raise Exception("Failed to find address for " + name)
|
||||||
return addr
|
return addr
|
||||||
|
|
||||||
|
|
||||||
def dump_regs(spi: SpiDev, cfgonly=False):
|
def dump_regs(spi: SpiDev, cfgonly=False):
|
||||||
if cfgonly:
|
if cfgonly:
|
||||||
for reg_name, reg_addr in regs_addr["CONFIG_REGS"].items():
|
for reg_name, reg_addr in regs_addr["CONFIG_REGS"].items():
|
||||||
@ -47,16 +53,20 @@ def dump_regs(spi: SpiDev, cfgonly = False):
|
|||||||
value = rr(spi, reg_addr)
|
value = rr(spi, reg_addr)
|
||||||
print((name + ":").ljust(15) + hex(value).ljust(4) + "\t" + str(value))
|
print((name + ":").ljust(15) + hex(value).ljust(4) + "\t" + str(value))
|
||||||
|
|
||||||
|
|
||||||
# def disable_crc(spi):
|
# def disable_crc(spi):
|
||||||
# spi.xfer2([CONFIG_REGS["PKTCTRL0"], 0x01])
|
# spi.xfer2([CONFIG_REGS["PKTCTRL0"], 0x01])
|
||||||
|
|
||||||
|
|
||||||
def sleep(t):
|
def sleep(t):
|
||||||
return time.sleep(t)
|
return time.sleep(t)
|
||||||
|
|
||||||
|
|
||||||
def delay(t):
|
def delay(t):
|
||||||
"""Delay for t milliseconds."""
|
"""Delay for t milliseconds."""
|
||||||
time.sleep(t / 1000.0)
|
time.sleep(t / 1000.0)
|
||||||
|
|
||||||
|
|
||||||
def delayMicroseconds(t):
|
def delayMicroseconds(t):
|
||||||
"""Delay for t microseconds."""
|
"""Delay for t microseconds."""
|
||||||
time.sleep(t / 1_000_000.0)
|
time.sleep(t / 1_000_000.0)
|
||||||
|
Loading…
Reference in New Issue
Block a user