Monitor Raspberry Pi CPU&GPU temperature

This is a small script from to monitor Raspberry Pi CPU&GPU temperature.

# Script:
# Purpose: Display the ARM CPU and GPU  temperature of Raspberry Pi
# Author: Vivek Gite <> under GPL v2.x+
# -------------------------------------------------------
echo "$(date) @ $(hostname)"
echo "-------------------------------------------"
echo "GPU => $(/opt/vc/bin/vcgencmd measure_temp)"
echo "CPU => $((cpu/1000))'C"

Save and close the file. Set permission:

chmod +x

Raspberry Pi 4 setup headless

Recently, I bought a Raspberry Pi 4. It’s better for me to record all the steps to setup my Raspberry Pi headless.

  • Download Raspberry Pi OS from
  • Burn the image to the micro SD card, I use rufus, which can be found at
  • Write an empty text file named “ssh (no file extension) to the root of the directory of the card. 
  • Setup a Wi-Fi connection for your Raspberry Pi. Create a text file called “wpa_supplicant.conf“, and place it in the root directory of the microSD card. You will need the following text in the file.
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
  • Open “config.txt” from the micro SD card root folder. Uncomment out the following code

  • Insert the micro SD card to Raspberry Pi and boot up.
  • Connecting via SSH. I use Putty, which can be downloaded from
  • Enter “raspberrypi” or “raspberrypi.local “as the address you wish to connect to in Putty, and click Open. 
  • Enter “pi” as your username and “raspberry “as your password. 
  • Enabling and Connecting over VNC
sudo raspi-config
  • Download, install and launch VNC Viewer to connect raspberry.

Odometer in Unity

To create a odometer in Unity is not easy. I couldn’t find a better one for my project.

Prepare all possible characters in a spinning roll and then stack them

 void Prepare()
     //keep the orginal text vector for future use
     sourceVertices = (Vector3[])Meter.textInfo.CopyMeshInfoVertexData()[0].vertices.Clone();
     var templateString = string.Empty;
     foreach (var s in SpinStrip)
         templateString += s + "\n";

     //Update text to spinning text
     Meter.text = templateString;
     cachedMeshInfo = Meter.textInfo.CopyMeshInfoVertexData();

     Spin(Meter.textInfo.characterInfo[currentValue], centerOfRotation, 0);

 void HideSpinCharacters()
     for (var i = 0; i < Meter.textInfo.characterCount; i++)
         var charInfo = Meter.textInfo.characterInfo[i];
         if (charInfo.isVisible)
             Spin(charInfo, centerOfRotation, -90);

Spin each character one by one

 IEnumerator Spin(Tuple<int, int, float> param)
     for (var curChar = param.Item1; curChar < param.Item2;curChar += 2)
         var curCharInfo = Meter.textInfo.characterInfo[curChar];
         var nextCharInfo = Meter.textInfo.characterInfo[curChar + 2];

         for (float degree = -90; degree <= 0; degree += param.Item3)
             Spin(curCharInfo, centerOfRotation, (degree + 90) < 90 ? (degree + 90) : 90);
             Spin(nextCharInfo, centerOfRotation, degree);
             yield return null;
         yield return null;

void Spin(TMP_CharacterInfo charInfo, Vector3 centerOfRotation, float angleOfRotation)
     // Get the index of the material used by the current character.
     int materialIndex = charInfo.materialReferenceIndex;

     // Get the index of the first vertex used by this text element.
     int vertexIndex = charInfo.vertexIndex;

     // Get the cached vertices of the mesh used by this text element (character or sprite).

     // Determine the center point of each character at the baseline.
     // Determine the center point of each character.
     Vector2 charMidBasline = (sourceVertices[0] + sourceVertices[2]) / 2;

     // Need to translate all 4 vertices of each quad to aligned with middle of character / baseline.
     // This is needed so the matrix TRS is applied at the origin for each character.
     Vector3 offset = charMidBasline;

     Vector3[] destinationVertices = Meter.textInfo.meshInfo[materialIndex].vertices;

     destinationVertices[vertexIndex + 0] = sourceVertices[0] - offset;
     destinationVertices[vertexIndex + 1] = sourceVertices[1] - offset;
     destinationVertices[vertexIndex + 2] = sourceVertices[2] - offset;
     destinationVertices[vertexIndex + 3] = sourceVertices[3] - offset;

     // This should calculate the matrix, which helps to roate odometer
     Matrix4x4 translationToCenterPoint = Matrix4x4.Translate(centerOfRotation);
     Matrix4x4 rotation = Matrix4x4.Rotate(Quaternion.AngleAxis(angleOfRotation, Vector3.right));
     Matrix4x4 translationBackToOrigin = Matrix4x4.Translate(-centerOfRotation);

     Matrix4x4 matrix = translationToCenterPoint * rotation * translationBackToOrigin;

     destinationVertices[vertexIndex + 0] = matrix.MultiplyPoint3x4(destinationVertices[vertexIndex + 0]);
     destinationVertices[vertexIndex + 1] = matrix.MultiplyPoint3x4(destinationVertices[vertexIndex + 1]);
     destinationVertices[vertexIndex + 2] = matrix.MultiplyPoint3x4(destinationVertices[vertexIndex + 2]);
     destinationVertices[vertexIndex + 3] = matrix.MultiplyPoint3x4(destinationVertices[vertexIndex + 3]);

     destinationVertices[vertexIndex + 0] += offset;
     destinationVertices[vertexIndex + 1] += offset;
     destinationVertices[vertexIndex + 2] += offset;
     destinationVertices[vertexIndex + 3] += offset;

The complete Unity project can be found at my github.

Unity Bink player

I have been asked to play a bink video in Unity. I googled a lot and couldn’t find any solution. Then I decided to write my own one and here is my solution to integrate Bink video into Unity.


    internal static class BinkNativeMethods

        public const ulong BINKOLDFRAMEFORMAT = 0x00008000L; // using the old Bink frame format (internal use only)
        public const ulong BINKCUSTOMCOLORSPACE = 0x00010000L; // file contains a custom colorspace matrix (only internal now)

        public const ulong BINKNOYPLANE = 0x00000200L; // Don't decompress the Y plane (internal flag)
        public const ulong BINKWILLLOOP = 0x00000080L; // You are planning to loop this Bink.

        public const ulong BINKYCRCBNEW = 0x00000010L; // File uses the new ycrcb colorspace (usually Bink 2)
        public const ulong BINKHDR = 0x00000004L; // Video is an HDR video
        public const ulong BINK_SLICES_2 = 0x00000000L; // Bink 2 file has two slices
        public const ulong BINK_SLICES_3 = 0x00000001L; // Bink 2 file has three slices
        public const ulong BINK_SLICES_4 = 0x00000002L; // Bink 2 file has four slices
        public const ulong BINK_SLICES_8 = 0x00000003L; // Bink 2 file has eight slices
        public const ulong BINK_SLICES_MASK = 0x00000003L; // mask against openflags to get the slice flags

        internal static extern void BinkClose(IntPtr Bink);

        internal static extern IntPtr BinkOpen(string name, BINK_OPEN_FLAGS flags);

        internal static extern string BinkGetError();

        internal static extern IntPtr BinkDoFrame(IntPtr Bink);

        internal static extern int BinkWait(IntPtr bink);

        internal static extern void BinkNextFrame(IntPtr bink);

        internal static extern int BinkCopyToBuffer(IntPtr bink, byte[] dest_addr, int dest_pitch, uint dest_height, uint dest_x, uint dest_y, BINK_COPY_FLAGS copy_flags);

        public struct BINK
            public int Width;
            public int Height;
            public uint Frames;
            public uint FrameNum;
            public uint FrameRate;
            public uint FrameRateDiv;
            public uint ReadError;
            public BINK_OPEN_FLAGS OpenFlags;
            public BINKRECT FrameRects;
            public uint NumRects;
            public uint FrameChangePercent;

        public struct BINKRECT
            public int Left;
            public int Top;
            public int Width;
            public int Height;

        public enum BINK_OPEN_FLAGS : ulong
            BINKFILEOFFSET = 0x00000020L, // Use file offset specified by BinkSetFileOffset
            BINKFILEHANDLE = 0x00800000L, // Use when passing in a file handle
            BINKFROMMEMORY = 0x04000000L, // Use when passing in a pointer to the file
            BINKNOFRAMEBUFFERS = 0x00000400L, // Don't allocate internal frame buffers - application must call BinkRegisterFrameBuffers
            BINKUSETRIPLEBUFFERING = 0x00000008L, // Use triple buffering in the framebuffers
            BINKSNDTRACK = 0x00004000L, // Set the track number to play
            BINKDONTCLOSETHREADS = 0x00000040L, // Don't close threads on BinkClose (call BinkFreeGlobals to close threads)
            BINKGPU = 0x00000100L, // Open Bink in GPU mode
            BINKNOSKIP = 0x00080000L, // Don't skip frames if falling behind
            BINKPRELOADALL = 0x00002000L, // Preload the entire animation
            BINKALPHA = 0x00100000L, // Decompress alpha plane (if present)
            BINKGRAYSCALE = 0x00020000L, // Force Bink to use grayscale
            BINKFRAMERATE = 0x00001000L, // Override fr (call BinkFrameRate first)
            BINKSIMULATE = 0x00400000L, // Simulate the speed (call BinkSim first)
            BINKIOSIZE = 0x01000000L, // Set an io size (call BinkIOSize first)
            BINKNOFILLIOBUF = 0x00200000L, // Don't Fill the IO buffer (in BinkOpen and BinkCopyTo)
            BINKIOPROCESSOR = 0x02000000L, // Set an io processor (call BinkIO first)
            BINKNOTHREADEDIO = 0x08000000L // Don't use a background thread for IO

        public enum BINK_COPY_FLAGS : ulong
            BINKSURFACE32BGRx = 3,
            BINKSURFACE32RGBx = 4,
            BINKSURFACE32BGRA = 5,
            BINKSURFACE32RGBA = 6,
            BINKSURFACE5551 = 8,
            BINKSURFACE555 = 9,
            BINKSURFACE565 = 10,
            BINKSURFACE32ARGB = 12,
            BINKSURFACEMASK = 15,
            BINKGRAYSCALE = 0x00020000L, // Force Bink to use grayscale
            BINKNOSKIP = 0x00080000L, // Don't skip frames if falling behind
            BINKYAINVERT = 0x00000800L // Reverse Y and A planes when blitting (for debugging)


public class BinkPlayer : MonoBehaviour
    BinkNativeMethods.BINK bk;
    Texture2D texture;
    IntPtr bink;
    byte[] buffer;
    // Start is called before the first frame update
    void Start()
        bink = BinkNativeMethods.BinkOpen("sample.bk2", 0);

        bk = Marshal.PtrToStructure<BinkNativeMethods.BINK>(bink);

        texture = new Texture2D(bk.Width, bk.Height, TextureFormat.RGBA32, false);
        var renderer = GetComponent<SpriteRenderer>();
        renderer.sprite = Sprite.Create(texture, new Rect(0, 0, bk.Width, bk.Height),;
        buffer = new byte[bk.Width * 4 * bk.Height];

    private void Update()
        if (bink != IntPtr.Zero && BinkNativeMethods.BinkWait(bink) == 0)
            // do you stuff here
            BinkNativeMethods.BinkCopyToBuffer(bink, buffer, bk.Width * 3, (uint)bk.Height, 0, 0, BinkNativeMethods.BINK_COPY_FLAGS.BINKSURFACE32RGBA);


The complete Unity project also can be found at my github.

Get Processor Id (CPU) in C++

This piece of C++ code helps to get CPU (Processor) Id

#include <array>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>
std::string GetProcessorId() {
	std::array<int, 4> cpuInfo;
	__cpuid(, 1);
	std::ostringstream buffer;
		<< std::uppercase << std::hex << std::setfill('0')
		<< std::setw(8) <<
		<< std::setw(8) <<;
	return buffer.str();

int main(void) {
	std::cout << "Processor Serial number is:  ";
		std::cout<<GetProcessorId() << std::endl;
	return 0;

Scanning QR codes with C# in Unity 3D

It’s been a while since my last post. These days, I have been busy with our Unity project to integrate Unity3D graphic engine into our software eco system. An interesting sub-task is to implement a QR code scanner in our Unity project.

The ZXing.Net library did a wonderful job for me. You can download from nuget or github.

using System.Collections;
using System.Collections.Generic;
using System.Net;
using UnityEngine;
using UnityEngine.UI;
using ZXing;

public class QRScanner : MonoBehaviour
    WebCamTexture webcamTexture;
    string QrCode = string.Empty;
    public AudioSource beepSound;

    void Start()
        var renderer = GetComponent<RawImage>();
        webcamTexture = new WebCamTexture(512, 512);
        renderer.material.mainTexture = webcamTexture;

    IEnumerator GetQRCode()
        IBarcodeReader barCodeReader = new BarcodeReader();
        var snap = new Texture2D(webcamTexture.width, webcamTexture.height, TextureFormat.ARGB32, false);
        while (string.IsNullOrEmpty(QrCode))
                var Result= barCodeReader.Decode(snap.GetRawTextureData(), webcamTexture.width, webcamTexture.height, RGBLuminanceSource.BitmapFormat.ARGB32);
                if (Result!= null)
                    QrCode = Result.Text;
                    if (!string.IsNullOrEmpty(QrCode))
                         Debug.Log("DECODED TEXT FROM QR: " + QrCode);
            catch (Exception ex) { Debug.LogWarning(ex.Message); }
            yield return null;

I was using Unity 2019 LTS to experiment with this.

The whole project can be found at

Common security network camera port and RTSP address

Default IP address: username admin password set
Port: “HTTP port” (default is 80), “RTSP port” (default is 554), “HTTPS port” (default 443) and “service port” (default 8000), ONVIF port 80.
RTSP address: rtsp://[username]:[password]@[ip]:[port]/[codec]/[channel]/[subtype]/av_stream
Username: The username. For example admin.
Password: password. For example 12345.
Ip: is the device IP. For example,
Port: The port number defaults to 554. If it is the default, it can be left blank.
Codec: There are several types of h264, MPEG-4, and mpeg4.
Channel: The channel number, starting at 1. For example, channel 1 is ch1.
Subtype: The stream type, the main stream is main, and the auxiliary stream is sub.
For example, request the main stream of Haikang camera channel 1, Url is as follows
Main stream:

Default IP address: Username/password: admin/admin
Port: TCP port 37777/UDP port 37778/http Port 80/RTSP Port number defaults to 554/HTTPs 443/ONVIF Function is off by default, port 80
RTSP address: rtsp://username:password@ip:port/cam/realmonitor?channel=1&subtype=0
Username: The username. For example admin.
Password: password. For example admin.
Ip: is the device IP. For example,
Port: The port number defaults to 554. If it is the default, it can be left blank.
Channel: The channel number, starting at 1. For example, channel 2 is channel=2.
Subtype: The stream type, the primary stream is 0 (ie subtype=0), and the secondary stream is 1 (ie subtype=1).
For example, request the secondary stream of channel 2 of a device, Url is as follows

Xiongmai / Jufeng
Default IP address: User name admin Password is empty
Port: TCP port: 34567 and HTTP port: 80, onvif port is 8899
RTSP address: rtsp:// This is the IP of the connected device
554 This is the port number of the RTSP service, which can be changed in the network service of the device.
User=admin This is the login username of the device.
Password= password is empty
Channel=1 first channel
Stream=0.sdp? Main stream
Stream=1.sdp? Secondary stream
Image capture address: http://ip/webcapture.jpg?command=snap&channel=1

Default IP address: User name admin Password 123456
Port: http port 80 data port 8091 RTSP port 554 ONVIF port 80
RTSP address: primary stream address: rtsp://
Substream address: rtsp://
Address that requires a password: Main stream rtsp://admin:123456@
Substream rtsp://admin:123456@
Photo capture address: http://ip/snapshot.cgi

Default IP Address: DHCP Default User Name admin Default Password Empty
RTSP address: rtsp:// (secondary stream)
Rtsp:// (main stream)

RTSP address: rtsp://IP:port(website port)/ch0_0.264 (main stream)
Rtsp://IP:port(website port)/ch0_1.264 (substream)

Default IP address: DHCP username admin password 123
RTSP address: primary stream: rtsp://IPadr:554/onvif1
Secondary stream: rtsp://IPadr:554/onvif2
Onvif port is 5000
The port discovered by the device is 3702.

Default IP address: DHCP username admin password empty /admin
Onvif port 8899
RTSP address: main stream rtsp://ip//live/ch00_1
Substream rtsp://ip//live/ch00_0

Default IP address: default username admin and default password 123456
Port: HTTP 80/RTSP 554/HTTPS 110(443)/onvif port 80
RTSP address: rtsp://username:password@ip:port number/video123 123 corresponds to 3 streams

Heaven and earth
Default IP address: User name “Admin”, password “1111”
Onvif port number “8080”
RTSP address: rtsp://

Dragon / JVT
Default IP address: Default username admin Default password admin
RTSP address:
Primary stream address: rtsp://IP address/av0_0
Secondary stream address: rtsp://IP address/av0_1
Onvif port 2000
Image capture address: http://ip/capture/webCapture.jpg?channel=1&FTpsend=0&checkinfo=0

Learning OpenCL programming

This post is to record the steps how I run my first “Hello World” OpenCL C++ program.

  1. To make things easier, I created this,, OpenCL library and C/C++ header files.
  2. Create a Visual Studio C++ project
  3. The following code is to add two 2^25 array altogether using GPU:
#include <iostream>
#include <vector>
#include <string>
using namespace std;

#include <CL\cl.hpp>

// Compute c = a + b.
static const char source[] =
"kernel void add(\n"
"       ulong n,\n"
"       global const float *a,\n"
"       global const float *b,\n"
"       global float *c\n"
"       )\n"
"    size_t i = get_global_id(0);\n"
"    if (i < n) {\n"
"       c[i] = a[i] + b[i];\n"
"    }\n"

int main() {

	const size_t N = 1 << 25;

	try {
		// Get list of OpenCL platforms.
		std::vector platform;

		if (platform.empty()) {
			std::cerr << "OpenCL platforms not found." << std::endl;
			return 1;

		// Get first available GPU device.
		cl::Context context;
		std::vector device;
		for (auto p = platform.begin(); device.empty() && p != platform.end(); p++) {
			std::vector pldev;

			try {
				p->getDevices(CL_DEVICE_TYPE_DEFAULT, &pldev);

				for (auto d = pldev.begin(); device.empty() && d != pldev.end(); d++) {
					if (!d->getInfo()) continue;

					std::string ext = d->getInfo();

					context = cl::Context(device);
			catch (...) {

		if (device.empty()) {
			std::cerr << "GPUs device not found." << std::endl;
			return 1;

		std::cout << device[0].getInfo() << std::endl;

		// Create command queue.
		cl::CommandQueue queue(context, device[0]);

		// Compile OpenCL program for found device.
		cl::Program program(context, cl::Program::Sources(
			1, std::make_pair(source, strlen(source))

		try {;
		catch (const cl::Error&) {
				<< "OpenCL compilation error" << std::endl
				<< program.getBuildInfo(device[0])
				<< std::endl;
			return 1;

		cl::Kernel add(program, "add");

		// Prepare input data.
		std::vector a(N, 1);
		std::vector b(N, 2);
		std::vector c(N);

		// Allocate device buffers and transfer input data to device.
		cl::Buffer A(context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
			a.size() * sizeof(float),;

		cl::Buffer B(context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
			b.size() * sizeof(float),;

		cl::Buffer C(context, CL_MEM_READ_WRITE,
			c.size() * sizeof(float));

		// Set kernel parameters.
		add.setArg(0, static_cast(N));
		add.setArg(1, A);
		add.setArg(2, B);
		add.setArg(3, C);

		// Launch kernel on the compute device.
		queue.enqueueNDRangeKernel(add, cl::NullRange, N, cl::NullRange);

		// Get result back to host.
		queue.enqueueReadBuffer(C, CL_TRUE, 0, c.size() * sizeof(float),;

		// Should get '3' here.
		std::cout << c[42] << std::endl;
	catch (const cl::Error &err) {
			<< "OpenCL error: "
			<< err.what() << "(" << err.err() << ")"
			<< std::endl;
		return 1;

Generate large prime numbers using boost library

Recently I am working on a RSA like algorithm project, which needs to generate large secret prime numbers, e.g. 512 bits, or 1024 bits. Luckily we are using boost library, which is inbuilt lots of functions to assist this generation.

#include <boost/multiprecision/miller_rabin.hpp>
#include <boost/random/mersenne_twister.hpp>

using namespace boost::multiprecision;
using namespace boost::random;

//Generate a secret RSA-like 512 bits primes p
cpp_int p = GetPrime();
cpp_int GetPrime()
    mt11213b base_gen(clock());
    independent_bits_engine<mt11213b, 512, cpp_int> gen(base_gen);

    // Generate some large random primes
    // Note 25 trials of Miller-Rabin 
    // likelihood that number is prime
    cpp_int n;
        n = gen();
    }while (!miller_rabin_test(n, 25));
    return n;

FFmpeg is very handy utility to edit multimedia file

FFmpeg is very powerful open source multimedia tool. The source code is hosted at, and the pre-compiled binary version can be found at However, sometimes, it not user friendly, and you have to remember many parameters.

Here are some samples, which I regular used:

Converting video and audio into different format

$ ffmpeg -i input.mp4 output.avi

Compressing video

$ ffmpeg -i input.mp4 -s 640x360 output.mp4

Changing bitrate

$ ffmpeg.exe -i input.avi -b 3000k  -maxrate 4000k -bufsize 1835k output.avi

Concating video

$ ffmpeg.exe -f concat -i filelist.txt -c copy concat.avi


file '1.AVI'   
file '2.AVI'   
file '3.AVI'

Remove audio from video file

$ ffmpeg -i input.mp4 -c copy -an output.mp4

Extract audio from video

$ ffmpeg -i input.avi -vn -acodec copy output.aac

Combining audio into video

$ ffmpeg -i video.mp4 -i audio.m4a -c:v copy -c:a copy output.mp4

Cutting the videos based on start and end time

$ ffmpeg -i input.mp4 -ss 00:00:45 -codec copy -t 40 output.mp4

Adding audio into video file on start and time

$ ffmpeg -i video.mp4 -ss 00:04:00 -i audio.mp3 -c copy -t 40 output.mkv

Adding Poster Image to an Audio File

$ ffmpeg -loop 1 -y -i image.jpg -i audio.mp3 -acodec copy -vcode
c libx264 -shortest ouput.mp4

Download m3u8 url and save as MP4 file

$ ffmpeg -i "http://url/file.m3u8" ouput.mp4