The CH9143 is an UART to BLE transceiver.
When connected will be a Peripheral/Slave BLE device advertising as CH9143BLE2U
name. When a Central/Master is connected it will have two characteristic (channels) one for reading (Notifications) and other for writing.
When you write from Central you can read from Serial and when you write to Serial you will get a notification in the Central.
The board is from WeAct: https://github.com/WeActStudio/WeActStudio.CH9143_BLE2USB2UART
The chip contains two modes that can be set through Low/High levels:
-
Master and Slave modes (
BLE_MODE
pin in IC). The master setting only works to pair with other CH9143 / CH9140 / CH9141 creating a point to point serial communication. If don't find any other device to pair in the first seconds after powered, it will just go into Slave mode. -
AT Mode (
AT
pin in IC) this allows to set the chip in AT mode so from UART can be send AT commands to set some settings like device MAC, paired device MAC, TX Power or UART settings. If the AT mode is disabled then is in "transparent mode" which is sending and receiving data through serial console. Is important to note that when AT mode is enabled, you won't receive or send data to BLE Central device.
The following image shows the switch in the board and what enables each position.
To test you will need to connect the CH9143 board either through USB or through a USB to TTL converter. As a note, use either USB or RX/TX pins, if you use both you will get garbled data in the RX/TX
You need also a BLE device available, to check if you have it, you can try with hcitool dev
:
$ hcitool dev
Devices:
hci0 XX:XX:XX:XX:XX:XX
The code can be found here: https://github.com/danguer/embedded-boards101/tree/main/weact-ch9143 and there are two parts: UART
and BLE
UART
requirements.txt
pyserial==3.5
uart.py
import serial
import threading
import argparse
import time
keep_reading = True
send_idx = 0
def read_serial(ser: serial.Serial):
while keep_reading:
if ser.in_waiting > 0:
waiting = ser.in_waiting
line = ser.read(waiting).decode("utf-8")
print("Serial Input: ", line)
def send_at_command(ser: serial.Serial, command: str) -> str:
# send command
ser.write(f"{command}\r\n".encode("ascii"))
# read command back
return ser.readline().decode("ascii").strip()
parser = argparse.ArgumentParser(prog="CH9143 Uart Example")
parser.add_argument(
"--use-at-commands",
help="Use AT Commands",
action=argparse.BooleanOptionalAction,
)
parser.add_argument(
"serial_port",
help="Serial port like /dev/ttyACM0",
)
args = parser.parse_args()
params = {
"port": args.serial_port,
"baudrate": 115200,
"bytesize": serial.EIGHTBITS,
"parity": serial.PARITY_NONE,
"stopbits": serial.STOPBITS_ONE,
"timeout": 1,
}
print("Opening port: ", args.serial_port)
with serial.Serial(**params) as ser:
if args.use_at_commands:
print("Sending AT... (checking if AT is enabled)")
print("Response: ", send_at_command(ser, "AT..."))
print("Sending AT+MAC? (checking current MAC)")
mac = send_at_command(ser, "AT+MAC?")
if mac:
mac = mac.split(":")
mac.reverse()
mac = ":".join(mac)
print("Response: ", mac)
print("Sending AT+CCADD? (checking connected MAC)")
print("Response: ", send_at_command(ser, "AT+CCADD?"))
print("Creating background receiving thread")
thread_read = threading.Thread(target=read_serial, args=(ser,))
thread_read.start()
try:
while True:
print(f"Sending ping {send_idx}")
ser.write(f"UART PING {send_idx}".encode("ascii"))
send_idx += 1
time.sleep(1)
except KeyboardInterrupt:
pass
finally:
print("Finishing reading thread")
keep_reading = False
thread_read.join()
To run:
python3 uart/uart.py /dev/ttyACM0
And the output will look like this:
Opening port: /dev/ttyACM0
Creating background receiving thread
Sending ping 0
Sending ping 1
Sending ping 2
Sending ping 3
Sending ping 4
Serial Input: HOST PING 0
Sending ping 5
Serial Input: HOST PING 1
Sending ping 6
Serial Input: HOST PING 2
Sending ping 7
Sending ping 8
The HOST PING
messages are when the BLE central is connected
BLE
For the BLE Central side will be under ble
folder:
requirements.txt
bleak==0.22.3
ble-central.py
import asyncio
import argparse
import time
import sys
from bleak import *
async def discover_mac():
scanner = BleakScanner()
device = await scanner.find_device_by_name("CH9143BLE2U")
if not device:
print("Failed to find CH9143BLE2U device")
sys.exit(1)
print("Found device:", device)
return device.address
def notify_callback(sender: BleakGATTCharacteristic, data: bytearray):
print("Got from device:", data.decode("utf-8"))
async def connect_to_mac(device_mac: str):
print("Connecting to mac", device_mac)
client = BleakClient(device_mac)
await client.connect()
print("Listing services")
uuid_write = None
uuid_read = None
for service in client.services:
print("[Service] {0}: {1}".format(service.uuid, service.description))
if service.uuid[0:4] == "0000" and service.uuid[4:8] == "fff0":
print("Found transparent service")
for char in service.characteristics:
# print("Char", char, char.properties) # debug printing
if char.uuid[4:8] == "fff1":
uuid_read = char.uuid
elif char.uuid[4:8] == "fff2":
uuid_write = char.uuid
if uuid_read and uuid_write:
print(
"Found needed characteristics, read:",
uuid_read,
", write:",
uuid_write,
)
send_idx = 0
await client.start_notify(uuid_read, notify_callback)
try:
while True:
print(f"Sending ping {send_idx}")
await client.write_gatt_char(
uuid_write,
f"HOST PING {send_idx}".encode("ascii"),
)
send_idx += 1
time.sleep(1)
except KeyboardInterrupt:
pass
finally:
print("Finishing app")
async def main():
parser = argparse.ArgumentParser(prog="BLE CH9143 Host")
parser.add_argument(
"--device-mac",
default=None,
type=str,
help="Device MAC to connect",
)
args = parser.parse_args()
if not args.device_mac:
device_mac = await discover_mac()
else:
device_mac = args.device_mac
await connect_to_mac(device_mac)
asyncio.run(main())
You can run like this:
python3 ble/ble-central.py
This will discover a device with CH9143BLE2U
, if you have multiple devices then you need to supply the mac as will connect to the first device it finds
The output looks like:
Found device: XX:XX:XX:XX:XX:XX: CH9143BLE2U
Connecting to mac XX:XX:XX:XX:XX:XX
Listing services
[Service] 0000fff0-0000-1000-8000-00805f9b34fb: Vendor specific
Found transparent service
[Service] 00001801-0000-1000-8000-00805f9b34fb: Generic Attribute Profile
Found needed characteristics, read: 0000fff1-0000-1000-8000-00805f9b34fb , write: 0000fff2-0000-1000-8000-00805f9b34fb
Sending ping 0
Sending ping 1
Got from device: UART PING 5
Sending ping 2
Got from device: UART PING 6
Got from device: UART PING 7
The UART PING
are messages received from the UART serial data
This chip is a tool to easily create a BLE device and pass data to a MCU through UART; but lacking things like ability to set device name it will limit the number of applications. Also switching to AT mode can receive wrong data from previous packets received so data needs to be cleaned. In the case of the board, this setting is through a physical switch which make it impossible for real apps to use it as AT modes are things that needs to be switched through a MCU
Top comments (0)