The jPC has an electro-mechanical buzzer attached to one of the SoC's PWMs.
Use the beep program to generate audible feedback for a specific frequency and duration: e.g. beep -f 2000 -l 100.
Write EV_SND events to device /dev/input/by-path/platform-buzzer-event to generate audible feedback in your programs. The following example generates a siren sound on the buzzer. See the Linux input subsystem for more information.
using System; using System.IO; using System.Runtime.InteropServices; using System.Threading; class Siren { static void Main(string[] args) { Console.CancelKeyPress += (sender, e) => { e.Cancel = true; exiting = true; Console.WriteLine("\nCtrl+C received. Stopping siren..."); }; const string eventPath = "/dev/input/by-path/platform-buzzer-event"; Console.WriteLine("Siren generator running. Press Ctrl+C to stop."); double time = 0; double step = 0.02; // 50 Hz update rate (20ms) FileStream? fs = null; try { fs = new FileStream(eventPath, FileMode.Open, FileAccess.Write, FileShare.ReadWrite); while (!exiting) { // Siren: 500 Hz to 1500 Hz and back in 2 seconds double cycleTime = time % 2.0; double frequency; if (cycleTime < 1.0) frequency = 500 + (cycleTime * 1000); // Rise else frequency = 1500 - ((cycleTime - 1.0) * 1000); // Fall int hz = (int)Math.Round(frequency); SendTone(fs, hz); Thread.Sleep((int)(step * 1000)); time += step; } } catch (Exception ex) { Console.Error.WriteLine(ex.Message); } finally { // Ensure buzzer is silenced try { if (fs != null) { SendTone(fs, 0); fs.Dispose(); } } catch (Exception ex) { Console.Error.WriteLine(ex.Message); } Console.WriteLine("Siren stopped."); } } // Linux input event constants const ushort EV_SYN = 0x00; const ushort EV_SND = 0x12; const ushort SYN_REPORT = 0x00; const ushort SND_BELL = 0x01; const ushort SND_TONE = 0x02; static bool exiting = false; // input_event struct from <linux/input.h> // struct timeval { long tv_sec; long tv_usec; }; // struct input_event { struct timeval time; __u16 type; __u16 code; __s32 value; }; [StructLayout(LayoutKind.Sequential)] struct TimeVal { public long tv_sec; public long tv_usec; } [StructLayout(LayoutKind.Sequential)] struct InputEvent { public TimeVal time; public ushort type; public ushort code; public int value; } // Compute once to avoid repeated reflection-ish cost static readonly int InputEventSize = Marshal.SizeOf<InputEvent>(); static void SendTone(FileStream fs, int hz) { // hz > 0 => play tone at hz // hz == 0 => stop tone WriteEvent(fs, EV_SND, SND_TONE, hz); WriteEvent(fs, EV_SYN, SYN_REPORT, 0); fs.Flush(); } static void WriteEvent(FileStream fs, ushort type, ushort code, int value) { // Local struct lives on the stack (no heap allocation) InputEvent ev = new InputEvent { time = default, // kernel will timestamp; zeroed is fine type = type, code = code, value = value }; // Stack-allocated byte buffer Span<byte> buffer = stackalloc byte[InputEventSize]; // Copy struct bytes into the stack buffer MemoryMarshal.Write(buffer, in ev); // Write without allocating fs.Write(buffer); } }