ROCK SBC Advanced Status Monitor
Introduction
Adding a status monitor to your ROCK SBC project is a great idea. It will let you keep an eye on real-time CPU performance, temperature, RAM usage, and other important connectivity details. If you're into overclocking, this tool becomes even more valuable, helping you gauge the impact of your settings on your ROCK SBC and make adjustments as needed.
For this project, the REPTOR-250 display paired with the Pi adapter is an excellent choice. Not only is it user-friendly, but setting it up and getting it running requires minimal code complexity.
One cool feature is the touch capability, allowing you to swipe through different status page styles. Plus, our Python library gives you a hassle-free Python coding experience. Let’s make your ROCK SBC project even more awesome with this simple yet powerful status monitor setup.
Note
When using REPTOR-250 as a Serial UART device, it is always recommended to simply use Mates Studio Architect or Commander environment since it provides a simple command protocol without the need for you to write code for the display.
However, some controller boards may require custom syncing and timing requirements that projects made using Architect and Commander environments can conflict with. This example project shows how a custom Genius project can solve such requirements.
Requirements
To proceed with the project, the following are required.
Hardware
- ROCK SBC
- REPTOR-250
- Pi Adapter
- HDMI Monitor or TV
- Keyboard & Mouse
- Storage Device
- microSD card
- eMMC module
Software
REPTOR-250 Setup
The Pi Adapter will need to be attached to the ROCK SBC GPIO Header and the REPTOR-250 attached to the adapter as shown below.
As the display needs to be configured for the Status Monitor, the switch on the Pi adapter needs to be set to PROG.
Connect a USB cable to the Mates Programmer and to a PC USB port. The REPTOR-250 is now ready for the Status Monitor project to be installed.
Mates Studio will be required to configure the REPTOR-250.
Uploading the Genius Project
Step 1: When you start Mates Studio you will be prompted to select your product. As we are using an already-created project, we can simply load the project from this screen.
Step 2: Simply Click on Browse Computer and navigate to the downloaded project file. The project will open in the Genius environment.
Step 3: To upload the project to the REPTOR we need to first select the correct COM port from the Tools menu.
Step 4: Next, click on the upload button.
Step 5: After an animated logo has shown the Status Monitor will now be displayed on the REPTOR-250.
The USB lead and Mates Programmer can now be removed from the Pi Adapter. The Mates Programmer switch can now be set to HOST ready to receive commands from the ROCK SBC.
ROCK SBC Setup
Setting up the ROCK SBC is very easy to achieve by visiting Armbian for ROCK 4 and following the instructions for installing the OS.
When the OS has been transferred to the uSD card or eMMC and insert into the ROCK SBC and then power it up. ROCK SBC can consume quite a lot of current which may exceed the available current from a PC USB port so it may be necessary to use the official ROCK SBC power supply.
The ROCK SBC OS will need to be configured to connect to the internet, SSH, and also to enable the Serial port (UART) that we will use to talk to the REPTOR-250.
Armbian Configuration
Use the armbian configuration utility to connect to network for internet connectivity, enable SSH for remote control and enable UART. Type the command below on the terminal.
This will open the Armbian Configuration window.
First, set the internet connection. Select Network from the armbian config option then Select Wifi.
Select the network that you want to connect. Activate and save the configuration.
After a successful connection to the network, enable the SSH by select System from the armbian config option then select SSH.
Use arrow keys to navigate to SSH Key login then use space bar to enable and save the configuration
You now have an option to control the ROCK SBC from remote computer using SSH client software.
To enable the UART, select System from the armbian-config option and select Hardware.
Use arrow keys to navigate to rk-3328-uart1 then use space bar to enable and save the configuration
Reboot the system to apply the configuration changes.
Installing Python Libraries
All recent Armbian OS Distro's are pre-loaded with Python 3, but you need to check if pip and python3-dev are installed.
To install pip, type the command on the terminal.
To install python3-dev, type the command on the terminal
The psutil and uptime can be installed by entering the following commands.
Since the REPTOR-250 display needs to communicate via serial UART. You need to install pyserial
library to access serial(UART) using python. Type the command below to install.
The Serial port of the ROCK SBC is used by the console so it needs to be released after boot. This can be achieved by using these commands:
Reboot the ROCK SBC.
The source code for this project can be found in this GitHub repository: BreadBoardMates/ROCKSBC-Status-Monitor
First, install git using the command below.
Clone the project repository to the user folder using the commands:
Project Discussion
Genius Graphics Design
This project use 10 pages which contains different widgets.
Page 0: It contains animation and image widgets which both need to source to display on the screen. The animation shows a 120 frames of BBM logo.
Page 1: This page contains a pre-built page design. It can be found under Application > Navigations > Status and Resource Monitor.
Page 2: This page is done by duplicating Page 1, and changing the widgets in the lower right corner. The lower right corner widgets were replaced by the same widgets present in the other corners, a gauge, led digits and a label. For the complete widget properties, please refer to the project.
Page 3: This page is made by duplicating Page 1 and modifying the widget properties to have different full round gauges.
Please refer to the project file for the complete widget properties.
Page 4: This page is made by duplicating Page 2 and modifying the widget properties to have different full round gauges.
Please refer to the project file for the complete widget properties.
Page 5: This page is a custom page which include Led Digits, Media Gauges and Media Temperature for displaying CPU temperature, CPU and RAM use. Below is the layout of the page.
Page 6: This page is from the default page templates Application > Environmental > Dual Thermometer Centigrade which is for displaying CPU temperature values. There are property changes on the widgets. Please refer to the project file for the complete widget properties.
Page 7: This page is a custom page which includes Labels and Text Area for displaying System informtaion like System processor, number of CPU cores, OS Release and Version, System RAM and System HDD. This page also uses an image as its background. Below is the layout of the page.
Please refer to the project file for the complete page and widget properties.
Page 8: This page is made by duplicating and modifying Page 7. It includes Labels and Text Area for dislaying WiFi Information such as WiFi SSID, Host Name, IP Address, MAC Address, Bytes Rx and Bytes Tx. Below is the layout of the page.
Please refer to the project file for the complete widget properties.
Page 9: This page is made by duplicating and modifying Page 7 or 8. It includes Labels and Text Area for displaying Display Information like Module which is a REPTOR-250, Resolution, External flash size, Baud rate used, Bytes Rx and Bytes Tx. Below is the layout of the page.
Please refer to the project file for the complete widget properties.
Genius Code
Genius projects allow you to custumize the behavior of the display. For this project, this is used to introduce a simple Serial UART command protocol. The code starts by listing the commands and declaring variables to be used.
#CONST
CPU_COUNT 0x10
CPU_PERCENT 0x11
CPU_TEMPERATURE 0x12
GPU_TEMPERATURE 0x13
RAM_USE 0x14
DISK_USE 0x15
UP_TIME 0x16
IP_WIFI 0x17
IP_ETHERNET 0x18
MOVE_TO 0x19
FONT_CHANGE 0x1A
PRINT_STRING 0x1B
TXT_FG 0x1C
TXT_BG 0x1D
START_STRING 0x1E
PAGE_SWAP 0x1F
SYSTEM_INFO_0 0x20
SYSTEM_INFO_1 0x21
SYSTEM_INFO_2 0x22
SYSTEM_INFO_3 0x23
SYSTEM_INFO_4 0x24
SYSTEM_INFO_5 0x25
WIFI_INFO_0 0x30
WIFI_INFO_1 0x31
WIFI_INFO_2 0x32
WIFI_INFO_3 0x33
WIFI_INFO_4 0x34
WIFI_INFO_5 0x35
SEND_STARTUP_SIG 0xC8
MAX_PAGE 0x08
#END
var RXbuff[1000];
var baud := 11520;
var recString[200];
var startString[10];
var messageID;
var recData[3];
var mCount;
var valueUpdate[100];
var cpuCount;
var cpuPercent;
var cpuTemp;
var gpuTemp;
var ramUse;
var diskUse;
var strLength;
var txEnable;
var buffer[15];
var bufferBAK[15];
var sbPos;
var sb;
var sbb;
var swipeEvent;
var currPage;
var valueStr[10];
var tempstring[20];
Setup
In this project, the setup function is used to initialize string pointers and the Serial UART. It also runs a start up sequence which includes an animation. Finally, a startup signal is sent to indicate that the display is ready to accept commands.
/*
* User Setup Function
*/
func setup()
// put your setup code here, to run once:
sb := str_Ptr(buffer);
sbb := str_Ptr(bufferBAK);
CommsInit();
to(startString);
print("BBMROCKSBCSTART");
pause(1000);
runLogo();
pause(1500);
setPage(1);
serout(SEND_STARTUP_SIG);
endfunc
Loop
The loop function is mainly responsible for handling the serial commands coming from the host controller, in this case the ROCK SBC.
It features a simple command structure which is as follows:
Message ID, Checksum, Data 1, Data 2
Each item in this is an 8-bit data.
- Message ID: indicates the command
- Checksum: a simple checksum value computed by the formula
((MessageID + Data1 + Data2) & 0xFF)
- Data 1 and Data 2: Values to use for the command when necessary, otherwise ignored. For more information, please refer to the code below.
/*
* User Loop Function
*/
func loop()
// put your main code here, to run repeatedly:
if (com_Count() > 3)
messageID := getSerialByte();
recData[0] := getSerialByte();
recData[1] := getSerialByte();
recData[2] := getSerialByte();
if (((messageID + recData[1] + recData[2]) & 0xff) == recData[0])
switch (messageID)
case CPU_COUNT:
cpuCount := recData[1];
updateCPU_COUNT();
break;
case CPU_PERCENT:
cpuPercent := recData[1];
updateCPU_PERCENT();
break;
case CPU_TEMPERATURE:
cpuTemp := recData[1] + (recData[2] << 8);
updateCPU_TEMPERATURE();
break;
case GPU_TEMPERATURE:
gpuTemp := recData[1] + (recData[2] << 8);
updateGPU_TEMPERATURE();
break;
case RAM_USE:
ramUse := recData[1];
updateRAM_USE();
break;
case DISK_USE:
diskUse := recData[1];
updateDISK_USE();
break;
case UP_TIME:
strLength := recData[1];
updateUP_TIME();
break;
case IP_WIFI:
strLength := recData[1];
updateIP_WIFI();
break;
case IP_ETHERNET:
strLength := recData[1];
updateIP_ETHERNET();
break;
case MOVE_TO:
updateMOVE_TO();
break;
case FONT_CHANGE:
updateFONT_CHANGE();
break;
case PRINT_STRING:
strLength := recData[1];
updatePRINT_STRING();
break;
case TXT_FG:
updateTXT_FG();
break;
case TXT_BG:
updateTXT_BG();
break;
case START_STRING:
strLength := recData[1];
updateSTART_STRING();
break;
case SYSTEM_INFO_0:
strLength := recData[1];
updateSYSTEM_INFO_0();
break;
case SYSTEM_INFO_1:
strLength := recData[1];
updateSYSTEM_INFO_1();
break;
case SYSTEM_INFO_2:
strLength := recData[1];
updateSYSTEM_INFO_2();
break;
case SYSTEM_INFO_3:
strLength := recData[1];
updateSYSTEM_INFO_3();
break;
case SYSTEM_INFO_4:
strLength := recData[1];
updateSYSTEM_INFO_4();
break;
case SYSTEM_INFO_5:
strLength := recData[1];
updateSYSTEM_INFO_5();
break;
case WIFI_INFO_0:
strLength := recData[1];
updateWIFI_INFO_0();
break;
case WIFI_INFO_1:
strLength := recData[1];
updateWIFI_INFO_1();
break;
case WIFI_INFO_2:
strLength := recData[1];
updateWIFI_INFO_2();
break;
case WIFI_INFO_3:
strLength := recData[1];
updateWIFI_INFO_3();
break;
case WIFI_INFO_4:
strLength := recData[1];
updateWIFI_INFO_4();
break;
case WIFI_INFO_5:
strLength := recData[1];
updateWIFI_INFO_5();
break;
endswitch
endif
endif
if (getSwipeEventCount() > 0)
swipeEvent := getNextSwipeEvent();
if (swipeEvent == MATES_SWIPE_WEST)
currPage ++;
if (currPage > MAX_PAGE) currPage := 1;
setPage(currPage);
serout(currPage + 201);
else if (swipeEvent == MATES_SWIPE_EAST)
currPage --;
if (currPage < 1) currPage := MAX_PAGE;
setPage(currPage);
serout(currPage + 201);
endif
endif
endfunc
The loop function uses getSerialByte
to read the next byte received from the ROCK SBC.
func getSerialByte()
var getchr;
getchr := serin();
str_PutByte(sbb + sbPos, getchr);
sbPos ++;
if (sbPos >= 15)
str_CopyN(sb, sbb + 1, 14);
str_CopyN(sbb, sb, 14);
sbPos := 14;
endif
if (mem_Compare(startString, bufferBAK, 15))
txEnable := 0;
else
Reset();
txEnable := 1;
endif
return getchr;
endfunc
This function additionally keeps track of the last 14 characters and checks it against the string "BBMROCKSBCSTART". If this string is found, the REPTOR module will reset the Serial RX buffer. This provides a "hotplug" or hotswap solution to the project.
The loop function also handles swipes detected by the touch display. In this project, it is used to navigate between pages. This also sends a report to the host indicating the new active page number.
The supporting functions used when handling commands are primarily used to update widgets included in the project.
Some widgets require string handling and therefore string data sometimes needs to be received as well. In this case, the Data 1 value indicates the number of characters and the display will expect to receive the string. Some examples for these are the functions:
Please refer to the project for the information on which widgets get updated by each command and which involves a string.
Python Program
The Python application is consisted of two files:
-
- serialcontrol.py
-
This is responsible for communicating with the REPTOR-250. This works in conjunction with the Genius Project
-
- BBMROCKSBCStatusMonitor.py
-
This is the main python project which is responsible in data gathering and evaluation.
serialcontrol.py
This file contains a Python class RockPiBBMController
which uses the serial
class to handle the Serial UART port of the ROCK SBC. It also manages the commands sent to the display.
The ROCKSBCBBMController class needs to be initialized using the begin
function provided:
The main code should look like this:
There are two main functions used to send commands to the REPTOR-250: sendCommand and sendCommandString
The sendCommand function sends the command structure as described here. While sendCommandString takes it another step and handles sending a string for commands that requires it, as described here.
Another utility function sendCommandReset
is available to add hotswap/hotplug feature to the REPTOR project.
def sendCommandReset(self):
text_string = "BBMROCKSBCSTART"
self.__write_string(text_string)
self.__write_int8(0)
self.__write_int8(0)
self.__write_int8(0)
self.__write_int8(0)
self.__write_int8(0)
self.__write_int8(0)
This is used by the main python script to reset the REPTOR RX buffer effectively restarting the commands.
This Python file also includes "get" functions which handles receiving commands from the REPTOR-250. This is primarily used to receive command indicating the new active page.
BBMROCKSBCStatusMonitor.py
This is the main Python program which uses serialcontrol.py
and its ROCKSBCBBMController
class.
The program starts by defining helper functions for getting network and system information, uptime, temperature and storage information.
# Collect data specific to ROCK Pi platform and HDD data and write it to the display
def getSystemInfo():
BBM.sendCommandString(33, "Total number of Cores: " + str(psutil.cpu_count(logical=True),))
BBM.sendCommandString(34, platform.release())
BBM.sendCommandString(35, str(platform.version()))
BBM.sendCommandString(32, str(platform.machine()))
BBM.sendCommandString(36, "Total RAM: "+ str(round(psutil.virtual_memory().total / (1024.0 **3)))+" GB")
hdd = psutil.disk_usage('/')
BBM.sendCommandString(37, "Total HDD Capacity: %d GB" % (hdd.total / (2**30)))
# Collect data specific to the current network connection and write it to the display
def getNetworkInfo():
BBM.sendCommandString(48, getSSID())
BBM.sendCommandString(49, socket.gethostname())
BBM.sendCommandString(50, get_interface_ipaddress('wlan0'))
BBM.sendCommandString(51, ':'.join(re.findall('..', '%012x' % uuid.getnode())))
iostat = psutil.net_io_counters(pernic=False)
BBM.sendCommandString(52, str(iostat[1]) + " bytes")
BBM.sendCommandString(53, str(iostat[0]) + " bytes")
# Calculate time elapsed since boot and output it as string
def up():
t = int(time.clock_gettime(time.CLOCK_BOOTTIME))
days = 0
hours = 0
min = 0
out = ''
days = int(t / 86400)
t = t - (days * 86400)
hours = int(t / 3600)
t = t - (hours * 3600)
min = int(t / 60)
out += str(days) + 'd '
out += str(hours) + 'h '
out += str(min) + 'm'
return out
Some Armbian operating systems does not support thermal sensors which is required for the psutil library as of this writing. To make it work, you may need to manually create an overlay.
From your home directory, create the .dts file, type
Then enter the following into the file.
/dts-v1/;
/plugin/;
/ {
compatible = "radxa,rockpi4c-plus", "radxa,rockpi4", "rockchip,rk3399";
fragment@0 {
target=<&tsadc>;
__overlay__ {
status = "okay";
/* tshut mode 0:CRU 1:GPIO */
rockchip,hw-tshut-mode = <1>;
/* tshut polarity 0:LOW 1:HIGH */
rockchip,hw-tshut-polarity = <1>;
};
};
};
Save the file. Then add the file to the overlay using the command below.
This will compile, add to the overlays directory and add to the configuration file. You need to reboot the system to use the overlay.
If this file is run as the main file, it will initialize variables and the Serial UART through the class file. It also gathers initial information of the network and system.
if __name__ == '__main__':
BBM = ROCKSBCBBMController()
BBM.begin(115200)
gtime = up()
lastCpuUse = 0
lastTemp = 0
lastTempG = 0
lastlTemp = 0
lastlTempG = 0
lastRamUse = 0
lastHDD = 0
lastWIPaddr = '0.0.0.0'
lastEIPaddr = '0.0.0.0'
lastPage = 0
currPage = 0
lastbytesRX = 0
lastbytesTX = 0
BBM.sendCommandReset()
BBM.sendCommandString(22, gtime)
lcpu = int(get_temp("cpu_thermal") * 10)
lgpu = int(get_temp("gpu_thermal") * 10)
IPinterval = 0
getSystemInfo()
getNetworkInfo()
The program proceeds by updating the display with the updated values every short interval
while True:
reccommand = BBM.getCommand()
if reccommand == 200:
BBM.sendCommandReset()
tempTime = up()
BBM.sendCommandString(22, tempTime)
BBM.sendCommand(20, lastRamUse)
BBM.sendCommand(18, lastlTemp)
BBM.sendCommandString(24, lastEIPaddr)
BBM.sendCommandString(23, lastWIPaddr)
getSystemInfo()
getNetworkInfo()
currPage = 1
if reccommand > 200:
currPage = reccommand - 201
lcpu = int(get_temp("cpu_thermal") * 10)
lgpu = int(get_temp("gpu_thermal") * 10)
cpuuse = int(psutil.cpu_percent())
ramuse = int(psutil.virtual_memory().percent)
hdd = get_hdd()
if currPage == 8:
iostat = psutil.net_io_counters(pernic=False)
if lastbytesRX != iostat[1]:
lastbytesRX = iostat[1]
BBM.sendCommandString(52, str(iostat[1]) + " bytes")
if lastbytesTX != iostat[0]:
lastbytesTX = iostat[0]
BBM.sendCommandString(53, str(iostat[0]) + " bytes")
if cpuuse != lastCpuUse:
lastCpuUse = lastCpuUse - increment(cpuuse, lastCpuUse)
BBM.sendCommand(17, lastCpuUse)
if lcpu != lastlTemp:
lastlTemp = lastlTemp - increment(lcpu, lastlTemp)
BBM.sendCommand(18, lastlTemp)
if lgpu != lastlTempG:
lastlTempG = lastlTempG - increment(lgpu, lastlTempG)
BBM.sendCommand(19, lastlTempG)
if ramuse != lastRamUse:
lastRamUse = lastRamUse - increment(ramuse, lastRamUse)
BBM.sendCommand(20, lastRamUse)
if hdd != lastHDD:
lastHDD = lastHDD - increment(hdd, lastHDD)
BBM.sendCommand(21, lastHDD)
if IPinterval > 20:
tempIPaddr = get_interface_ipaddress('eth0')
if tempIPaddr != lastEIPaddr:
BBM.sendCommandString(24, tempEIPaddr)
lastEIPaddr = tempIPaddr
tempIPaddr = get_interface_ipaddress('wlan0')
if tempIPaddr != lastWIPaddr:
BBM.sendCommandString(23, tempIPaddr)
lastWIPaddr = tempIPaddr
getNetworkInfo()
IPinterval = 0
IPinterval = IPinterval + 1
time.sleep(0.060)
tempTime = up()
if tempTime != gtime:
BBM.sendCommandString(22, gtime)
gtime = tempTime
refresh = 0
time.sleep(0.040)
Running the Python Script
Go into the ROCKSBC-Status-Monitor folder the run the project using the code below
The REPTOR-250 will then start showing the boot logo followed by the status of CPU use, CPU temp, and RAM use along with the connected IP address and uptime.
CPU use and RAM use percentages are calculated in the main loop and sent like other values when there is a change in state. If the display is disconnected from the ROCK SBC while running and then reconnected all values will be refreshed, and the display will continue to run.
You can then swipe through the available pages.
This project can be simply altered or improved to get the desired look by creating a new page in Mates Studio and changing the Python code to match with any new widgets used. The only limit is imagination.