Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
iXyles committed Jun 23, 2024
0 parents commit 85d6b7f
Show file tree
Hide file tree
Showing 35 changed files with 1,429 additions and 0 deletions.
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
bin/
obj/
/packages/
riderModule.iml
.DS_Store
/_ReSharper.Caches/

.idea/
.vs/
Web.Dashboard/data/
14 changes: 14 additions & 0 deletions FlircTestingTool/FlircTestingTool.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\FlircWrapper\FlircWrapper.csproj" />
</ItemGroup>

</Project>
97 changes: 97 additions & 0 deletions FlircTestingTool/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
using System.Diagnostics;
using FlircWrapper;

// dummy program for testing purposes
// used to build the wrappers around IR & Flirc libraries & communicatation with the actual device

var service = new FlircService();
IrProt? lastPacket = null;

Trace.Listeners.Add(new ConsoleTraceListener());

Console.WriteLine("Initializing Flirc...");
Console.WriteLine(service.FetchFlircLibraryVersion());
Console.WriteLine(service.FetchIrLibraryVersion());

// try open connection to device
var connected = service.OpenConnection();
if (connected)
{
service.RegisterTransmitter();
Console.WriteLine("Flirc device opened successfully...");
}

// if failed, maybe disconnected, then wait till it is connected
if (!connected)
{
Console.WriteLine("Unable to connect, device not found (most likely), please plug it in...");
var result = service.WaitForDevice();
if (!result)
{
Console.WriteLine("Failed connecting, closing program...");
return;
}
service.RegisterTransmitter();
Console.WriteLine("Flirc device opened successfully...");
}

if (!service.RegisterTransmitter())
{
Console.WriteLine("Failed to register transmit callback...");
return;
}
Console.WriteLine("Transmitter callback successfully registered...");

Console.WriteLine("Listening to commands...");
Console.WriteLine("'send' to resend, 'listen' to record packet for send, 'exit' to close");
ListenForCommands();
Console.WriteLine("Closing connection to device...");
service.CloseConnection();

void ListenForCommands()
{
while (true)
{
var command = Console.ReadLine() ?? string.Empty;
if (command.Equals("exit", StringComparison.OrdinalIgnoreCase))
break;

if (command.Equals("listen", StringComparison.OrdinalIgnoreCase))
RecordPacket();
else if (command.Equals("send", StringComparison.OrdinalIgnoreCase))
TransmitRecordedPacket();
else
Console.WriteLine("Unknown command. Available commands: 'send', 'listen', 'exit'.");
}
Console.WriteLine("Stopped listening for commands...");
}

void RecordPacket()
{
Console.WriteLine("Listening, press a key on your remote...");
var protos = service.ListenToPoll(CancellationToken.None);

// return first "proto/packet" - this is from testing of my devices that has worked...
// if we need to send more than a single packet for something, then this gotta be changed, along "mapped" object
if (protos.Any())
{
lastPacket = protos.FirstOrDefault();
Console.WriteLine("Packet recorded, can now be sent with 'send' command...");
}
else
{
Console.WriteLine("No packet recorded, type next command...");
}
}

void TransmitRecordedPacket()
{
if (!lastPacket.HasValue)
{
Console.WriteLine("No packet recorded to re-transmit.");
return;
}

var result = service.SendPacket(lastPacket.Value);
Console.WriteLine(result == 0 ? "Successfully sent packet..." : $"Failed to send packet, status code: {result}");
}
30 changes: 30 additions & 0 deletions FlircWrapper/FlircSdkWrapper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System.Runtime.InteropServices;

namespace FlircWrapper;

public static class FlircSdkWrapper
{
// Dll for Mac ARM (m1>)
private const string DllName = "libs/macos-arm/libflirc.3.27.15.dylib";

[DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
public static extern int fl_open_device_alt(ushort vendorId, string deviceId);

[DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
public static extern int fl_wait_for_device(ushort vendorId, string deviceId);

[DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr fl_lib_version();

[DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
public static extern int fl_ir_packet_poll(ref IrPacket packet);

[DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
public static extern void fl_close_device();

[DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
public static extern void fl_dev_flush();

[DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
public static extern int fl_transmit_raw(IntPtr buf, ushort len, ushort ik, byte repeat);
}
137 changes: 137 additions & 0 deletions FlircWrapper/FlircService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;

namespace FlircWrapper;

/// <summary>
/// Basic service to utilize underlaying libraries of Flirc
/// </summary>
public class FlircService
{
const ushort VendorId = 0x20A0; // Flirc vendor ID
const string DeviceId = "flirc.tv"; // Flirc device ID

public string FetchFlircLibraryVersion() =>
Marshal.PtrToStringAnsi(FlircSdkWrapper.fl_lib_version());

public string FetchIrLibraryVersion() =>
Marshal.PtrToStringAnsi(IrSdkWrapper.ir_lib_version());

public bool OpenConnection()
{
try
{
return FlircSdkWrapper.fl_open_device_alt(VendorId, DeviceId) >= 0;
}
catch
{
return false;
}
}

public bool WaitForDevice()
{
try
{
return FlircSdkWrapper.fl_wait_for_device(VendorId, DeviceId) >= 0;
}
catch
{
return false;
}
}

public bool RegisterTransmitter()
{
IrSdkWrapper.IrRegisterTxCallback callback = FlircSdkWrapper.fl_transmit_raw;
return IrSdkWrapper.ir_register_tx(callback) == 0;
}

/// <summary>
/// Returns a response when it has successfully decoded a valid packet and returned a NOFRAME
/// </summary>
public List<IrProt> ListenToPoll(CancellationToken token)
{
var protos = new List<IrProt>();
var packet = new IrPacket();

while (!token.IsCancellationRequested)
{
if (token.IsCancellationRequested)
break;

Console.WriteLine("Polling for packet...");
var result = FlircSdkWrapper.fl_ir_packet_poll(ref packet);
switch (result)
{
case 0:
if (protos.Count > 0)
{
Debug.WriteLine("NOFRAME recieved after packets decoded, returning protos...");
return protos;
}
break;

case 1:
var processed = new IrProt
{
buf = new ushort[256],
pronto = new ushort[256]
};
var proto = IrSdkWrapper.ir_decode_packet(ref packet, ref processed);

Debug.WriteLine($"Received IR signal: Scancode = 0x{processed.scancode:X}, Protocol = {processed.protocol}, Repeat = {processed.repeat}");
Debug.WriteLine(GetBufValue(ref packet));

// if decoded packet is invalid, repeat or unknown, ignore it.
// UNSURE if there are other "proto"s that should be handled differently...
// Or if these are needed for some special protocol/sequence
if (proto == RcProto.RC_PROTO_INVALID ||
proto == RcProto.RC_PROTO_NEC_REPEAT ||
proto == RcProto.RC_PROTO_UNKNOWN)
{
Debug.WriteLine($"Dropping packet because of type: {proto}");
break;
}

protos.Add(processed);
break;

case -1:
throw new Exception("Failed polling IR signal, disconnected...");

default:
throw new Exception("Failed polling IR signal, unknown error...");
}
}

// fallback, send back current list of packets
return protos;
}

public void CloseConnection()
{
FlircSdkWrapper.fl_close_device();
}

public nint SendPacket(IrProt packet)
{
// ensure we flush the interface of pending packets before sending a packet
Debug.WriteLine("Flush interface of pending packets...");
FlircSdkWrapper.fl_dev_flush();
Debug.WriteLine("Transmit packet...");
return IrSdkWrapper.ir_tx(packet.protocol, packet.scancode, packet.repeat);
}

private string GetBufValue(ref IrPacket packet)
{
var value = new StringBuilder();
for (int i = 0; i < packet.len && i < packet.buf.Length; i++)
{
value.Append($"{packet.buf[i]} ");
}

return value.ToString();
}
}
25 changes: 25 additions & 0 deletions FlircWrapper/FlircWrapper.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<!-- MAC ARM OS -->
<ItemGroup>
<None Update="libs\macos-arm\libflirc.3.27.15.dylib">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="libs\macos-arm\libhidapi.0.dylib">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="libs\macos-arm\libir.3.27.15.dylib">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="libs\macos-arm\libusb-1.0.0.dylib">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>

</Project>
12 changes: 12 additions & 0 deletions FlircWrapper/IrPacket.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System.Runtime.InteropServices;

namespace FlircWrapper;

[StructLayout(LayoutKind.Sequential)]
public struct IrPacket
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
public ushort[] buf; // Buffer
public ushort len; // Length of buffer
public ushort elapsed; // Elapsed time
}
22 changes: 22 additions & 0 deletions FlircWrapper/IrProt.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System.Runtime.InteropServices;

namespace FlircWrapper;

[StructLayout(LayoutKind.Sequential)]
public struct IrProt
{
public RcProto protocol; // Protocol
public uint scancode; // 32 Bit Scancode from remote
public ulong fullcode; // 64 bit entire code extracted from remote (don't use this)
public uint hash; // 32 Bit Unique Hash
public byte repeat; // repeat detected = 1, 0 = no repeat
public uint bits; // amount of bits collected
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100)] // size from underlaying library
public string desc; // Used to describe our collected signal
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)] // size from underlaying library
public ushort[] buf; // cleaned signal after decode
public ushort len; // length of clean buf
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)] // size from underlaying library
public ushort[] pronto; // pronto hex code
public ushort pronto_len; // pronto length
}
22 changes: 22 additions & 0 deletions FlircWrapper/IrSdkWrapper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System.Runtime.InteropServices;

namespace FlircWrapper;

public static class IrSdkWrapper
{
private const string DllName = "libs/macos-arm/libir.3.27.15.dylib";

[DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr ir_lib_version();

[DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
public static extern RcProto ir_decode_packet(ref IrPacket packet, ref IrProt protocol);

[DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr ir_tx(RcProto protocol, uint scancode, int repeat);

[DllImport(DllName)]
public static extern IntPtr ir_register_tx(IrRegisterTxCallback transmitFunction);

public delegate int IrRegisterTxCallback(IntPtr buf, ushort len, ushort ik, byte rep);
}
Loading

0 comments on commit 85d6b7f

Please sign in to comment.