硬件介绍:
XG24-EK2703A—EFR32xG24 Explorer套件是一个基于EFR32MG24片上系统的超低成本、小尺寸开发和评估平台,该套件专注于物联网应用的快速原型化和概念创建2.4 GHz无线协议的IoT应用程序,包括蓝牙LE、蓝牙Mesh、Zigbee、Thread 和 Matter。
板子上有两个LED和两个按钮,和一个SEGGER J-Link 调试器。支持在线调试。但是这么大个板子,没有集成什么传感器,只有内核上有一个测量芯片温度的传感器。不过编程方面除了可以使用C还能支持circuitpython编程。
任务选择:
芯科科技对这个XG24-EK2703A开发板提供了完整的编程IDE。但是安装很困难,需要在线安装,而且服务器基本都是在国外,安装缓慢,且安装后需要几十G的硬盘空间。仅仅是一个开发环境安装就困难重重。在群里老师的指导下,才了解到这个板卡还支持circuitpython编程,于是就决定选择任务1:使用芯片内部的温度检测外设,测量温度并通过蓝牙发送至上位机,在上位机中以“绘图”的形式对温度数据进行可视化。
任务实现:
定下了任务,选择了编程语言。下位机端就是读取温度传感器,然后写入蓝牙。这个板子的温度传感器是通过寄存器直接读取的。这里使用了群里老师提供的circuitpython的固件,能够支持uctypes读取寄存器。通过寄存器很容易就获得了温度信息。不得不说circuitpython在读取传感器方便,真的是快捷高效。
from uctypes import BF_POS, BF_LEN, UINT32,BFUINT32,BFUINT16, struct
from time import sleep
EMU_BASE = 0x50004000
#EMU_BASE = 0x50008000
EMU_LAYOUT = {
"EMU_TEMP": (0x88,{
"TEMP":2 << BF_POS|9 << BF_LEN|BFUINT32,
"TEMPLSB":0<< BF_POS |2 << BF_LEN|BFUINT32
})
}
def say(n):
temp="{:.2f}".format(n)
print(temp)
return temp
while True:
emu = struct(EMU_BASE,EMU_LAYOUT)
say(emu.EMU_TEMP.TEMP + emu.EMU_TEMP.TEMPLSB *0.25 -273.15)
sleep(0.5)
读取到了温度后,就是写入蓝牙。XG24-EK2703A开发板在这里是作为服务端。这里使用了adafruit_ble这个包。adafruit_ble这个包能够将蓝牙内容打包发送出去,类似于一个串口。这里自己建立了一个“CPY_TEMP”的蓝牙服务。
import time
import board
from adafruit_ble import BLERadio
from adafruit_ble.advertising.standard import ProvideServicesAdvertisement
from adafruit_ble.services.nordic import UARTService
from uctypes import BF_POS, BF_LEN, UINT32,BFUINT32,BFUINT16, struct
from time import sleep
EMU_BASE = 0x50004000
EMU_LAYOUT = {
"EMU_TEMP": (0x88,{
"TEMP":2 << BF_POS|9 << BF_LEN|BFUINT32,
"TEMPLSB":0<< BF_POS |2 << BF_LEN|BFUINT32
})
}
def say(n):
temp="{:.2f}".format(n)
print(temp)
return temp
ble = BLERadio()
uart = UARTService()
advertisement = ProvideServicesAdvertisement(uart)
advertisement.short_name = "CPY_TEMP"
advertisement.connectable = True
num=0
while True:
ble.start_advertising(advertisement)
print("Waiting to connect")
while not ble.connected:
pass
print("Connected")
while ble.connected:
emu = struct(EMU_BASE,EMU_LAYOUT)
#temp=say(emu.EMU_TEMP.TEMP + emu.EMU_TEMP.TEMPLSB *0.25 -273.15)
temp=emu.EMU_TEMP.TEMP + emu.EMU_TEMP.TEMPLSB *0.25 -273.15
print("Temperature: %0.2f℃" % temp)
result = int(temp*100)
#num=num+1;
#uart.write(result.encode("utf-8"))
#uart.write(str(num).encode("utf-8"))
uart.write(str(result))
time.sleep(0.5)
接下来是上位机的部分。在电脑上使用gatttool能够很容易地连上XG24-EK2703A的“CPY_TEMP”蓝牙服务,但是却不知道如何来读取数据,使用char-read-hnd始终是读取不到数据。使用手机APP,通过Notify的方式就能正确读取广播数据。
最终决定使用手头的Wio Terminal通过Arduino编程来实现上位机功能。
Wio Terminal支持蓝牙,并且官方提供了Line Charts库,专门用于绘制折线图,但是Line Charts库和蓝牙库应该是有冲突,两个库同时使用,屏幕就无法点亮。所以这里绘图部分仅仅使用TFT_eSPI库手工绘制温度变化曲线。
#include "rpcBLEDevice.h"
#include <BLEScan.h>
#include <BLEAdvertisedDevice.h>
#include "TFT_eSPI.h"
TFT_eSPI tft;
#define MAX_SIZE 40 // maximum size of data
float queue[MAX_SIZE] = {0};
uint8_t queuesize = 0;
// The remote service we wish to connect to.
static BLEUUID serviceUUID("6e400001-b5a3-f393-e0a9-e50e24dcca9e");
// The characteristic of the remote service we are interested in.
static BLEUUID charUUID("6e400003-b5a3-f393-e0a9-e50e24dcca9e");
static boolean doConnect = false;
static boolean connected = false;
static boolean doScan = false;
static BLERemoteCharacteristic *pRemoteCharacteristic;
static BLEAdvertisedDevice *myDevice;
uint8_t bd_addr[6] = {0xa9, 0xad, 0xe9, 0x27, 0x87, 0x04};
BLEAddress BattServer(bd_addr);
//--图形------------------------------
void addval(float val)
{
if (queuesize < MAX_SIZE)
{ // 队列未满
queue[queuesize] = val;
queuesize++;
}
else
{ // 队列已满
for (uint8_t i = 1; i < MAX_SIZE; i++)
{
queue[i - 1] = queue[i];
}
queue[MAX_SIZE - 1] = val;
}
}
// 绘制坐标轴 和显示温度值
void drawaxis(float val)
{
tft.setFreeFont(&FreeSansBoldOblique9pt7b); // select Free, Sans, Bold, Oblique, 12pt.
tft.drawString(String(val, 2)+" `C", 5, 2); // prints string at (70,80)
tft.drawLine(0, 120, 320, 120, TFT_RED);
}
// 绘制曲线 范围 从 10~50摄氏度(映射到0~240)
void drawwave(uint16_t color)
{
int x0, y0, x1, y1;
float val;
for (uint8_t i = 1; i < queuesize; i++)
{
val = queue[i - 1];
if (val < 10)
val = 10.0;
if (val > 50)
val = 50.0;
y0 = int((val - 10.0) * 6);
x0 = (i - 1) * 320 / (queuesize - 1);
val = queue[i];
if (val < 10)
val = 10.0;
if (val > 50)
val = 50.0;
y1 = int((val - 10) * 6);
x1 = (i) * 320 / (queuesize - 1);
// Serial.printf("i=%d ,queuesize=%d ,%.2f,%.2f, %d,%d , %d,%d\n",i,queuesize,queue[i-1],queue[i],x0,y0,x1,y1);
tft.drawLine(x0, 240-y0, x1, 240-y1, color);
}
}
// 蓝牙----------------------------------------------
static void notifyCallback(BLERemoteCharacteristic *pBLERemoteCharacteristic, uint8_t *pData, size_t length, bool isNotify)
{
char temp[6]; // 定义缓冲,用来存放串口传来的数据
float blu_temp;
if (length > 0 && length < 6)
{ // 使用字符串来存放温度,温度扩大了100倍。
memset(temp, 0, sizeof(char) * 6);
for (uint8_t i = 0; i < length; i++)
{
temp[i] = pData[i];
}
blu_temp = float(String(temp).toInt()) / 100.00;
// data.push(blu_temp);
Serial.println(blu_temp);
drawwave(TFT_WHITE);
addval(blu_temp);
drawaxis(blu_temp);
drawwave(TFT_BLUE);
}
}
class MyClientCallback : public BLEClientCallbacks
{
void onConnect(BLEClient *pclient)
{
}
void onDisconnect(BLEClient *pclient)
{
connected = false;
Serial.println("onDisconnect");
}
};
bool connectToServer()
{
Serial.println("+++++++++++++++++++++++++++++++++++++++++++++");
Serial.print("Forming a connection to ");
Serial.println(myDevice->getAddress().toString().c_str());
BLEClient *pClient = BLEDevice::createClient();
Serial.println(" - Created client");
pClient->setClientCallbacks(new MyClientCallback());
// Connect to the remove BLE Server.
pClient->connect(myDevice); // if you pass BLEAdvertisedDevice instead of address, it will be recognized type of peer device address (public or private)
Serial.println(" - Connected to server");
// Obtain a reference to the service we are after in the remote BLE server.
BLERemoteService *pRemoteService = pClient->getService(serviceUUID);
Serial.println(serviceUUID.toString().c_str());
if (pRemoteService == nullptr)
{
Serial.print("Failed to find our service UUID: ");
Serial.println(serviceUUID.toString().c_str());
pClient->disconnect();
return false;
}
Serial.println(" - Found our service");
// Obtain a reference to the characteristic in the service of the remote BLE server.
pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID);
if (pRemoteCharacteristic == nullptr)
{
Serial.print("Failed to find our characteristic UUID: ");
Serial.println(charUUID.toString().c_str());
pClient->disconnect();
return false;
}
Serial.println(" - Found our characteristic");
// Read the value of the characteristic.
if (pRemoteCharacteristic->canRead())
{
Serial.println(" - can read start");
std::string value = pRemoteCharacteristic->readValue();
Serial.print("The characteristic value was: ");
Serial.println(value.c_str());
}
if (pRemoteCharacteristic->canNotify())
pRemoteCharacteristic->registerForNotify(notifyCallback);
connected = true;
return true;
}
/**
* Scan for BLE servers and find the first one that advertises the service we are looking for.
*/
class MyAdvertisedDeviceCallbacks : public BLEAdvertisedDeviceCallbacks
{
/**
* Called for each advertising BLE server.
*/
void onResult(BLEAdvertisedDevice advertisedDevice)
{
Serial.print("BLE Advertised Device found: ");
Serial.println(advertisedDevice.toString().c_str());
// We have found a device, let us now see if it contains the service we are looking for.
if (memcmp(advertisedDevice.getAddress().getNative(), BattServer.getNative(), 6) == 0)
{
Serial.print("BATT Device found: ");
Serial.println(advertisedDevice.toString().c_str());
BLEDevice::getScan()->stop();
Serial.println("new BLEAdvertisedDevice");
myDevice = new BLEAdvertisedDevice(advertisedDevice);
Serial.println("new BLEAdvertisedDevice done");
doConnect = true;
doScan = true;
} // onResult
}
}; // MyAdvertisedDeviceCallbacks
void setup()
{
Serial.begin(115200);
while (!Serial)
{
};
delay(2000);
Serial.println("Starting Arduino BLE Client application...");
tft.begin();
tft.setRotation(3);
tft.fillScreen(TFT_WHITE); // Green background
BLEDevice::init("");
BLEScan *pBLEScan = BLEDevice::getScan();
pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
pBLEScan->setInterval(1349);
pBLEScan->setWindow(449);
pBLEScan->setActiveScan(true);
pBLEScan->start(5, false);
} // End of setup.
// This is the Arduino main loop function.
void loop()
{
if (doConnect == true)
{
if (connectToServer())
{
Serial.println("We are now connected to the BLE Server.");
}
else
{
Serial.println("We have failed to connect to the server; there is nothin more we will do.");
}
doConnect = false;
}
delay(500);
} // End of loop
心得体会:非常荣幸参加funpack的这期活动。XG24-EK2703A板子很优秀,可惜板子上的传感器太少了,玩起来不过瘾,而且他家的开发工具安装起来真有点秃头。感谢群里的各位老师,在老师们的指导下,终于玩起来啦!