Skip to content

Commit f66b718

Browse files
rkalwakEllerbach
andauthored
Added sample for Esp32.Rmt that shows hot to use infrared receiver VS1838 (#287)
* Added sample for Esp32.Rmt that shows hot to use infrared receiver VS1838 * License * Updated readmes * Update samples/Hardware.Esp32.Rmt/InfraredRemoteReceiver/InfraredRemoteReceiverSample/Decoder.cs Co-authored-by: Laurent Ellerbach <[email protected]> * Update samples/Hardware.Esp32.Rmt/InfraredRemoteReceiver/README.md Co-authored-by: Laurent Ellerbach <[email protected]> * Update samples/Hardware.Esp32.Rmt/InfraredRemoteReceiver/README.md Co-authored-by: Laurent Ellerbach <[email protected]> * Update samples/Hardware.Esp32.Rmt/InfraredRemoteReceiver/InfraredRemoteReceiverSample/Decoder.cs Co-authored-by: Laurent Ellerbach <[email protected]> * Resolving PR comments * Resolving PR Comments * Fix --------- Co-authored-by: Rafał <Kałwak> Co-authored-by: Laurent Ellerbach <[email protected]>
1 parent cb35cfd commit f66b718

15 files changed

+642
-0
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ Our samples uses 🌶️ to show how easy or complicated those samples are. The
9090
* [🌶️🌶️ - Hardware ESP32 RMT sample pack](samples/Hardware.Esp32.Rmt)
9191
* [🌶️🌶️ - NeoPixel Strip WS2812 with RMT](samples/Hardware.Esp32.Rmt/NeoPixelStrip)
9292
* [🌶️🌶️ - NeoPixel Strip WS2812 with RMT low memory](samples/Hardware.Esp32.Rmt/NeoPixelStripLowMemory)
93+
* [🌶️🌶️ - Infrared remote receiver based on VS1838 with RMT](samples/Hardware.Esp32.Rmt/InfraredRemoteReceiver)
9394
* [🌶️🌶️ - Simple sample **with** Azure lib and retry pattern for connection](samples/AzureSDK/AzureSDKBasic)
9495
* [🌶️🌶️ - Ultrasonic HC-SR04 sensor with RMT](samples/Hardware.Esp32.Rmt/Ultrasonic)
9596
* [🌶️🌶️🌶️ - Complete Azure MQTT sample using BMP280 sensor **with** Azure lib and deep sleep](samples/AzureSDK/AzureSDKSleepBMP280)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio Version 17
4+
VisualStudioVersion = 17.3.32929.385
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{11A8DD76-328B-46DF-9F39-F559912D0360}") = "InfraredRemoteReceiverSample", "InfraredRemoteReceiverSample\InfraredRemoteReceiverSample.nfproj", "{F4CA8E73-E9A5-42A1-99BE-045ACCB5BA31}"
7+
EndProject
8+
Global
9+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
10+
Debug|Any CPU = Debug|Any CPU
11+
Release|Any CPU = Release|Any CPU
12+
EndGlobalSection
13+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
14+
{F4CA8E73-E9A5-42A1-99BE-045ACCB5BA31}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15+
{F4CA8E73-E9A5-42A1-99BE-045ACCB5BA31}.Debug|Any CPU.Build.0 = Debug|Any CPU
16+
{F4CA8E73-E9A5-42A1-99BE-045ACCB5BA31}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
17+
{F4CA8E73-E9A5-42A1-99BE-045ACCB5BA31}.Release|Any CPU.ActiveCfg = Release|Any CPU
18+
{F4CA8E73-E9A5-42A1-99BE-045ACCB5BA31}.Release|Any CPU.Build.0 = Release|Any CPU
19+
{F4CA8E73-E9A5-42A1-99BE-045ACCB5BA31}.Release|Any CPU.Deploy.0 = Release|Any CPU
20+
EndGlobalSection
21+
GlobalSection(SolutionProperties) = preSolution
22+
HideSolutionNode = FALSE
23+
EndGlobalSection
24+
GlobalSection(ExtensibilityGlobals) = postSolution
25+
SolutionGuid = {D7737F61-6B54-4C52-BB81-BC30AA7929BD}
26+
EndGlobalSection
27+
EndGlobal
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
//
2+
// Copyright (c) .NET Foundation and Contributors
3+
// See LICENSE file in the project root for full license information.
4+
//
5+
6+
using System;
7+
using System.Text;
8+
using nanoFramework.Hardware.Esp32.Rmt;
9+
10+
namespace InfraredRemote
11+
{
12+
public abstract class Decoder
13+
{
14+
/// <summary>
15+
/// Defines constructor of <see cref="Decoder"/>.</typeparam>
16+
/// </summary>
17+
/// <param name="signalLengthTolerance">Tolerance of signal length represented as fraction value. </param>
18+
protected Decoder(double signalLengthTolerance)
19+
{
20+
SignalLengthTolerance = signalLengthTolerance;
21+
}
22+
23+
protected SignalData lastData = null;
24+
protected abstract int PulseLengthInMicroseconds { get; }
25+
protected abstract int HeaderMark { get; }
26+
protected abstract int HeaderSpace { get; }
27+
protected abstract int RepeatSpace { get; }
28+
protected abstract int ZeroSpace { get; }
29+
protected abstract int OneSpace { get; }
30+
protected abstract Protocol Protocol { get; }
31+
protected double SignalLengthTolerance { get; private set; } = 0.2;
32+
protected abstract int SignalLength { get; }
33+
protected abstract bool UseLessSignificantBitFirst { get; }
34+
protected abstract int AddressBits { get; }
35+
protected abstract int CommandBits { get; }
36+
37+
/// <summary>
38+
/// Decodes Rmt signal into SignalData.
39+
/// </summary>
40+
/// <param name="receivedSignal">Array representing decoded signal.</param>
41+
/// <returns>SignalData object.</returns>
42+
public virtual SignalData Decode(RmtCommand[] receivedSignal)
43+
{
44+
var firstPulse = receivedSignal[0];
45+
var lastPulse = receivedSignal[receivedSignal.Length - 1];
46+
bool isHeaderMark = Match(firstPulse.Duration0, HeaderMark);
47+
bool isSpaceMark = Match(firstPulse.Duration1, HeaderSpace);
48+
bool isEndOfTransmission = Match(lastPulse.Duration0, PulseLengthInMicroseconds) && !lastPulse.Level0 && lastPulse.Duration1 == 0 &&
49+
lastPulse.Level1;
50+
bool isRepeat = receivedSignal.Length == 2 && Match(firstPulse.Duration0, HeaderMark) && !firstPulse.Level0 && Match(firstPulse.Duration1, RepeatSpace) && firstPulse.Level1;
51+
52+
if (isRepeat)
53+
{
54+
return lastData;
55+
}
56+
57+
SignalData signalData = null;
58+
if (isHeaderMark && isSpaceMark && (receivedSignal.Length == SignalLength) && isEndOfTransmission)
59+
{
60+
var message = ExtractRawPayload(receivedSignal);
61+
var address = ExtractAddress(message);
62+
var command = ExtractCommand(message);
63+
int addressNumber = 0;
64+
int commandNumber = 0;
65+
if (UseLessSignificantBitFirst)
66+
{
67+
addressNumber = Convert.ToInt32(Reverse(address), 2);
68+
commandNumber = ToInt32(Reverse(command));
69+
}
70+
else
71+
{
72+
addressNumber = ToInt32(address);
73+
commandNumber = ToInt32(command);
74+
}
75+
76+
signalData = new SignalData(addressNumber, commandNumber, address, command, Protocol, message);
77+
lastData = signalData;
78+
}
79+
80+
return signalData;
81+
}
82+
83+
protected virtual string ExtractCommand(string message)
84+
{
85+
return message.Substring(AddressBits, CommandBits);
86+
}
87+
88+
protected virtual string ExtractAddress(string message)
89+
{
90+
return message.Substring(0, AddressBits);
91+
}
92+
93+
private string ExtractRawPayload(RmtCommand[] response)
94+
{
95+
StringBuilder sb = new StringBuilder();
96+
for (int i = 1; i < response.Length - 1; i++)
97+
{
98+
var rmtCommand = response[i];
99+
if (Match(rmtCommand.Duration1, OneSpace))
100+
{
101+
sb.Append("1");
102+
}
103+
else
104+
{
105+
sb.Append("0");
106+
}
107+
}
108+
109+
return sb.ToString();
110+
}
111+
112+
protected bool Match(int tick, int expectedTick)
113+
{
114+
var ticksLow = expectedTick * (1 - SignalLengthTolerance);
115+
var ticksHigh = expectedTick * (1 + SignalLengthTolerance);
116+
return (tick >= ticksLow &&
117+
tick <= ticksHigh);
118+
}
119+
120+
private string Reverse(string valueToBeReversed)
121+
{
122+
StringBuilder sb = new StringBuilder();
123+
for (int i = valueToBeReversed.Length - 1; i >= 0; i--)
124+
{
125+
sb.Append(valueToBeReversed[i]);
126+
}
127+
128+
return sb.ToString();
129+
}
130+
131+
private int ToInt32(string binaryValue)
132+
{
133+
int dec_value = 0;
134+
135+
int base1 = 1;
136+
137+
for (int i = binaryValue.Length - 1; i >= 0; i--)
138+
{
139+
if (binaryValue[i] == '1')
140+
{
141+
dec_value += base1;
142+
}
143+
base1 = base1 * 2;
144+
}
145+
146+
return dec_value;
147+
}
148+
}
149+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
//
2+
// Copyright (c) .NET Foundation and Contributors
3+
// See LICENSE file in the project root for full license information.
4+
//
5+
6+
using System;
7+
using System.Threading;
8+
using nanoFramework.Hardware.Esp32.Rmt;
9+
10+
namespace InfraredRemote
11+
{
12+
/// <summary>
13+
/// This class is a listener that uses Esp32.Rmt to receive infrared signals
14+
/// and publish them as an event.
15+
/// </summary>
16+
public class InfraredListener
17+
{
18+
private ReceiverChannel _rxChannel;
19+
private Thread _t;
20+
private int _receiveTimeoutMs=60;
21+
22+
/// <summary>
23+
/// Create an instance of InfraredListener device class.
24+
/// </summary>
25+
/// <param name="pinNumber">GPIO pin number for IR receiver.</param>
26+
/// </summary>
27+
public InfraredListener(int pinNumber)
28+
{
29+
var settings = new ReceiverChannelSettings(pinNumber)
30+
{
31+
// filter out 100Us / noise
32+
EnableFilter = true,
33+
FilterThreshold = 100,
34+
// 1us clock ( 80Mhz / 80 ) = 1Mhz
35+
ClockDivider = 80,
36+
// 40ms based on 1us clock
37+
IdleThreshold = 40000,
38+
// 60 millisecond timeout
39+
ReceiveTimeout = TimeSpan.FromMilliseconds(_receiveTimeoutMs),
40+
41+
};
42+
_rxChannel = new ReceiverChannel(settings);
43+
}
44+
45+
/// <summary>
46+
/// Event handler for new signal.
47+
/// </summary>
48+
/// <param name="sender">Sender of event.</param>
49+
/// <param name="signal">Signal representation.</param>
50+
public delegate void SignalEventHandler(object sender, RmtCommand[] signal);
51+
52+
/// <summary>
53+
/// Event raised when new signal arrives.
54+
/// </summary>
55+
public event SignalEventHandler? SignalEvent;
56+
57+
/// <summary>
58+
/// Starts listener.
59+
/// </summary>
60+
public void Start()
61+
{
62+
_t = new Thread(Run);
63+
_t.Start();
64+
}
65+
66+
/// <summary>
67+
/// Stops listener.
68+
/// </summary>
69+
public void Stop()
70+
{
71+
_t.Abort();
72+
_rxChannel.Stop();
73+
}
74+
75+
private void Run()
76+
{
77+
_rxChannel.Start(true);
78+
while (true)
79+
{
80+
var response = _rxChannel.GetAllItems();
81+
if (response != null)
82+
{
83+
SignalEvent?.Invoke(this, response);
84+
}
85+
}
86+
}
87+
}
88+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Project ToolsVersion="Current" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3+
<PropertyGroup Label="Globals">
4+
<NanoFrameworkProjectSystemPath>$(MSBuildExtensionsPath)\nanoFramework\v1.0\</NanoFrameworkProjectSystemPath>
5+
</PropertyGroup>
6+
<Import Project="$(NanoFrameworkProjectSystemPath)NFProjectSystem.Default.props" Condition="Exists('$(NanoFrameworkProjectSystemPath)NFProjectSystem.Default.props')" />
7+
<PropertyGroup>
8+
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
9+
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
10+
<ProjectTypeGuids>{11A8DD76-328B-46DF-9F39-F559912D0360};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
11+
<ProjectGuid>f4ca8e73-e9a5-42a1-99be-045accb5ba31</ProjectGuid>
12+
<OutputType>Exe</OutputType>
13+
<AppDesignerFolder>Properties</AppDesignerFolder>
14+
<FileAlignment>512</FileAlignment>
15+
<RootNamespace>IRController</RootNamespace>
16+
<AssemblyName>IRController</AssemblyName>
17+
<TargetFrameworkVersion>v1.0</TargetFrameworkVersion>
18+
</PropertyGroup>
19+
<Import Project="$(NanoFrameworkProjectSystemPath)NFProjectSystem.props" Condition="Exists('$(NanoFrameworkProjectSystemPath)NFProjectSystem.props')" />
20+
<ItemGroup>
21+
<Compile Include="Decoder.cs" />
22+
<Compile Include="InfraredListener.cs" />
23+
<Compile Include="NecSignalDecoder.cs" />
24+
<Compile Include="Program.cs" />
25+
<Compile Include="Properties\AssemblyInfo.cs" />
26+
<Compile Include="Protocol.cs" />
27+
<Compile Include="SignalData.cs" />
28+
</ItemGroup>
29+
<ItemGroup>
30+
<Reference Include="mscorlib">
31+
<HintPath>..\packages\nanoFramework.CoreLibrary.1.14.2\lib\mscorlib.dll</HintPath>
32+
</Reference>
33+
<Reference Include="nanoFramework.Hardware.Esp32.Rmt">
34+
<HintPath>..\packages\nanoFramework.Hardware.Esp32.Rmt.2.0.1\lib\nanoFramework.Hardware.Esp32.Rmt.dll</HintPath>
35+
</Reference>
36+
<Reference Include="nanoFramework.Runtime.Events">
37+
<HintPath>..\packages\nanoFramework.Runtime.Events.1.11.6\lib\nanoFramework.Runtime.Events.dll</HintPath>
38+
</Reference>
39+
<Reference Include="nanoFramework.System.Text">
40+
<HintPath>..\packages\nanoFramework.System.Text.1.2.37\lib\nanoFramework.System.Text.dll</HintPath>
41+
</Reference>
42+
</ItemGroup>
43+
<ItemGroup>
44+
<None Include="packages.config" />
45+
</ItemGroup>
46+
<Import Project="$(NanoFrameworkProjectSystemPath)NFProjectSystem.CSharp.targets" Condition="Exists('$(NanoFrameworkProjectSystemPath)NFProjectSystem.CSharp.targets')" />
47+
<ProjectExtensions>
48+
<ProjectCapabilities>
49+
<ProjectConfigurationsDeclaredAsItems />
50+
</ProjectCapabilities>
51+
</ProjectExtensions>
52+
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
//
2+
// Copyright (c) .NET Foundation and Contributors
3+
// See LICENSE file in the project root for full license information.
4+
//
5+
6+
namespace InfraredRemote
7+
{
8+
/// <summary>
9+
/// Nec protocol signal decoder.
10+
/// </summary>
11+
public class NecSignalDecoder : Decoder
12+
{
13+
/// <summary>
14+
/// Creates instance of <see cref="NecSignalDecoder"/>.</typeparam>
15+
/// </summary>
16+
/// <param name="signalLengthTolerance">Tolerance of signal length represented as percentage value.</param>
17+
public NecSignalDecoder(double signalLengthTolerance)
18+
: base(signalLengthTolerance)
19+
{
20+
}
21+
22+
protected override int PulseLengthInMicroseconds => 560;
23+
protected override int HeaderMark => PulseLengthInMicroseconds * 16;
24+
protected override int HeaderSpace => PulseLengthInMicroseconds * 8;
25+
protected override int RepeatSpace => PulseLengthInMicroseconds * 4;
26+
protected override int ZeroSpace => PulseLengthInMicroseconds;
27+
protected override int OneSpace => PulseLengthInMicroseconds * 3;
28+
protected override Protocol Protocol => Protocol.Nec;
29+
protected override int SignalLength => 34;
30+
protected override bool UseLessSignificantBitFirst => true;
31+
protected override int AddressBits => 16;
32+
protected override int CommandBits => 16;
33+
34+
protected override string ExtractCommand(string message)
35+
{
36+
return message.Substring(16, 8);
37+
}
38+
39+
protected override string ExtractAddress(string message)
40+
{
41+
return message.Substring(0, 8);
42+
}
43+
}
44+
}

0 commit comments

Comments
 (0)