I have a NAS, and it can be easily access from Raspberry Pi by using the following script:
- Create a NAS folder
mkdir nas
- mount NAS to the local nas folder
sudo mount -t cifs //lenovoez/documents /home/pi/nas/
A place to share
I have a NAS, and it can be easily access from Raspberry Pi by using the following script:
mkdir nas
sudo mount -t cifs //lenovoez/documents /home/pi/nas/
This is a small script from https://www.cyberciti.biz/faq/linux-find-out-raspberry-pi-gpu-and-arm-cpu-temperature-command/ to monitor Raspberry Pi CPU&GPU temperature.
#!/bin/bash
# Script: my-pi-temp.sh
# Purpose: Display the ARM CPU and GPU temperature of Raspberry Pi
# Author: Vivek Gite <www.cyberciti.biz> under GPL v2.x+
# -------------------------------------------------------
cpu=$(</sys/class/thermal/thermal_zone0/temp)
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 my-pi-temp.sh
Recently, I bought a Raspberry Pi 4. It’s better for me to record all the steps to setup my Raspberry Pi headless.
country=US
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
network={
scan_ssid=1
ssid="your_wifi_ssid"
psk="your_wifi_password"
}
hdmi_force_hotplug=1
hdmi_drive=2
sudo raspi-config
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
Meter.ForceMeshUpdate();
sourceVertices = (Vector3[])Meter.textInfo.CopyMeshInfoVertexData()[0].vertices.Clone();
Meter.textInfo.Clear();
var templateString = string.Empty;
foreach (var s in SpinStrip)
{
templateString += s + "\n";
}
//Update text to spinning text
Meter.text = templateString;
Meter.ForceMeshUpdate();
cachedMeshInfo = Meter.textInfo.CopyMeshInfoVertexData();
HideSpinCharacters();
Spin(Meter.textInfo.characterInfo[currentValue], centerOfRotation, 0);
Apply();
}
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);
Apply();
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.
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.
BinkNativeMethods
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
[DllImport("bink2w64")]
internal static extern void BinkClose(IntPtr Bink);
[DllImport("bink2w64")]
internal static extern IntPtr BinkOpen(string name, BINK_OPEN_FLAGS flags);
[DllImport("bink2w64")]
internal static extern string BinkGetError();
[DllImport("bink2w64")]
internal static extern IntPtr BinkDoFrame(IntPtr Bink);
[DllImport("bink2w64")]
internal static extern int BinkWait(IntPtr bink);
[DllImport("bink2w64")]
internal static extern void BinkNextFrame(IntPtr bink);
[DllImport("bink2w64")]
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)
}
}
BinkPlayer
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), Vector2.zero);
buffer = new byte[bk.Width * 4 * bk.Height];
}
private void Update()
{
if (bink != IntPtr.Zero && BinkNativeMethods.BinkWait(bink) == 0)
{
BinkNativeMethods.BinkDoFrame(bink);
// do you stuff here
BinkNativeMethods.BinkCopyToBuffer(bink, buffer, bk.Width * 3, (uint)bk.Height, 0, 0, BinkNativeMethods.BINK_COPY_FLAGS.BINKSURFACE32RGBA);
texture.LoadRawTextureData(buffer);
texture.Apply();
BinkNativeMethods.BinkNextFrame(bink);
}
}
}
The complete Unity project also can be found at my github.