diff --git a/src/devices/Seesaw/README.md b/src/devices/Seesaw/README.md
index e6f3977b57f25e2c8dbc93ad16012b0f0b124d82..76fa09f694980d6c7c1cd7f5941f6f9516c9f8cc 100644
--- a/src/devices/Seesaw/README.md
+++ b/src/devices/Seesaw/README.md
@@ -76,6 +76,12 @@ This sample duplicates the functionality of the rpi-more-blinking-lights sample
![eesaw Sample Blinking Lights](SeesawSampleBlinkingLights_bb.png)
+### Connecting to a Seesaw based rotary encoder sample
+
+This sample connects a Raspberry Pi to an Adafruit I2C QT Rotary Encoder
+
+![Seesaw sample encoder](SeesawSampleEncoder.png)
+
## Binding Notes
When using Seesaw devices with a Raspberry Pi it has been observed that errors sometimes happen on the I2C bus. The nature of this error may be the 'clock stretching' [bug](http://www.advamation.com/knowhow/raspberrypi/rpi-i2c-bug.html) or may just be that the breakout board cannot accommodate the default I2C speed.
@@ -96,4 +102,4 @@ In general the Seesaw technology allows user the embedding of the following type
* [X] EEPROM (although untested)
* [X] Capacitive Touch
* [ ] Keypad
-* [ ] Rotary Encoder
+* [X] Rotary Encoder
diff --git a/src/devices/Seesaw/Seesaw.sln b/src/devices/Seesaw/Seesaw.sln
index 6b1aa7e1761d527435859e1c6527b2577bb2553d..4bc275435682a970e267bed7e5b0eae9d1808187 100644
--- a/src/devices/Seesaw/Seesaw.sln
+++ b/src/devices/Seesaw/Seesaw.sln
@@ -1,17 +1,19 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 15
-VisualStudioVersion = 15.0.26124.0
+# Visual Studio Version 17
+VisualStudioVersion = 17.2.32505.173
MinimumVisualStudioVersion = 15.0.26124.0
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{A15390DB-F07B-4014-9840-5F792089E026}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Seesaw.Sample.BlinkingLights", "samples\Seesaw.Sample.BlinkingLights.csproj", "{413D2811-7A97-44AC-9D82-099209920BBE}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Seesaw.Sample.BlinkingLights", "samples\Seesaw.Sample.BlinkingLights.csproj", "{413D2811-7A97-44AC-9D82-099209920BBE}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Seesaw.Sample.Capabilities", "samples\Seesaw.Sample.Capabilities.csproj", "{5807BB95-184F-47CC-A8A4-63519079B525}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Seesaw.Sample.Capabilities", "samples\Seesaw.Sample.Capabilities.csproj", "{5807BB95-184F-47CC-A8A4-63519079B525}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Seesaw.Sample.SoilSensor", "samples\Seesaw.Sample.SoilSensor.csproj", "{164336C0-7E90-4082-B98D-0AEBB60717E3}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Seesaw.Sample.SoilSensor", "samples\Seesaw.Sample.SoilSensor.csproj", "{164336C0-7E90-4082-B98D-0AEBB60717E3}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Seesaw", "Seesaw.csproj", "{F87E6203-1DA1-4648-9A1C-AB39C0CDE7E7}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Seesaw", "Seesaw.csproj", "{F87E6203-1DA1-4648-9A1C-AB39C0CDE7E7}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Seesaw.Sample.Encoder", "samples\Seesaw.Sample.Encoder.csproj", "{2AF6BC30-E051-45A3-AA35-9CBB7E6352BA}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -22,9 +24,6 @@ Global
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{413D2811-7A97-44AC-9D82-099209920BBE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{413D2811-7A97-44AC-9D82-099209920BBE}.Debug|Any CPU.Build.0 = Debug|Any CPU
@@ -74,10 +73,29 @@ Global
{F87E6203-1DA1-4648-9A1C-AB39C0CDE7E7}.Release|x64.Build.0 = Release|Any CPU
{F87E6203-1DA1-4648-9A1C-AB39C0CDE7E7}.Release|x86.ActiveCfg = Release|Any CPU
{F87E6203-1DA1-4648-9A1C-AB39C0CDE7E7}.Release|x86.Build.0 = Release|Any CPU
+ {2AF6BC30-E051-45A3-AA35-9CBB7E6352BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2AF6BC30-E051-45A3-AA35-9CBB7E6352BA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2AF6BC30-E051-45A3-AA35-9CBB7E6352BA}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {2AF6BC30-E051-45A3-AA35-9CBB7E6352BA}.Debug|x64.Build.0 = Debug|Any CPU
+ {2AF6BC30-E051-45A3-AA35-9CBB7E6352BA}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {2AF6BC30-E051-45A3-AA35-9CBB7E6352BA}.Debug|x86.Build.0 = Debug|Any CPU
+ {2AF6BC30-E051-45A3-AA35-9CBB7E6352BA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2AF6BC30-E051-45A3-AA35-9CBB7E6352BA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2AF6BC30-E051-45A3-AA35-9CBB7E6352BA}.Release|x64.ActiveCfg = Release|Any CPU
+ {2AF6BC30-E051-45A3-AA35-9CBB7E6352BA}.Release|x64.Build.0 = Release|Any CPU
+ {2AF6BC30-E051-45A3-AA35-9CBB7E6352BA}.Release|x86.ActiveCfg = Release|Any CPU
+ {2AF6BC30-E051-45A3-AA35-9CBB7E6352BA}.Release|x86.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{413D2811-7A97-44AC-9D82-099209920BBE} = {A15390DB-F07B-4014-9840-5F792089E026}
{5807BB95-184F-47CC-A8A4-63519079B525} = {A15390DB-F07B-4014-9840-5F792089E026}
{164336C0-7E90-4082-B98D-0AEBB60717E3} = {A15390DB-F07B-4014-9840-5F792089E026}
+ {2AF6BC30-E051-45A3-AA35-9CBB7E6352BA} = {A15390DB-F07B-4014-9840-5F792089E026}
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {FAE88427-BD54-45C3-B93A-0FCBC92E93F0}
EndGlobalSection
EndGlobal
diff --git a/src/devices/Seesaw/SeesawEncoder.cs b/src/devices/Seesaw/SeesawEncoder.cs
new file mode 100644
index 0000000000000000000000000000000000000000..6fd4d771fd4aa1e6d2d9f70303178cfdfc9c99a0
--- /dev/null
+++ b/src/devices/Seesaw/SeesawEncoder.cs
@@ -0,0 +1,92 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Buffers.Binary;
+
+namespace Iot.Device.Seesaw
+{
+ public partial class Seesaw : IDisposable
+ {
+ ///
+ /// Read the current position of the encoder.
+ ///
+ /// Which encoder to use, defaults to 0.
+ /// The encoder position as a 32 bit signed integer.
+ /// The hardware does not support Adafruit SeeSaw encoder functionality.
+ public int GetEncoderPosition(byte encoder = 0)
+ {
+ if (!HasModule(SeesawModule.Encoder))
+ {
+ throw new InvalidOperationException($"The hardware on I2C Bus {I2cDevice.ConnectionSettings.BusId}, Address 0x{I2cDevice.ConnectionSettings.DeviceAddress:X2} does not support Adafruit SeeSaw encoder functionality");
+ }
+
+ return BinaryPrimitives.ReadInt32BigEndian(Read(SeesawModule.Encoder, SeesawFunction.EncoderPosition + encoder, 4, 8000));
+ }
+
+ ///
+ /// Set the current position of the encoder.
+ ///
+ /// Encoder position.
+ /// Which encoder to use, defaults to 0.
+ /// The hardware does not support Adafruit SeeSaw encoder functionality.
+ public void SetEncoderPosition(int position, byte encoder = 0)
+ {
+ if (!HasModule(SeesawModule.Encoder))
+ {
+ throw new InvalidOperationException($"The hardware on I2C Bus {I2cDevice.ConnectionSettings.BusId}, Address 0x{I2cDevice.ConnectionSettings.DeviceAddress:X2} does not support Adafruit SeeSaw encoder functionality");
+ }
+
+ Span buffer = stackalloc byte[4];
+ BinaryPrimitives.WriteInt32BigEndian(buffer, position);
+
+ Write(SeesawModule.Encoder, SeesawFunction.EncoderPosition + encoder, buffer);
+ }
+
+ ///
+ /// The change in encoder position since it was last read.
+ ///
+ /// Which encoder to use, defaults to 0.
+ /// The encoder change as a 32 bit signed integer.
+ /// The hardware does not support Adafruit SeeSaw encoder functionality.
+ public int GetEncoderDelta(byte encoder = 0)
+ {
+ if (!HasModule(SeesawModule.Encoder))
+ {
+ throw new InvalidOperationException($"The hardware on I2C Bus {I2cDevice.ConnectionSettings.BusId}, Address 0x{I2cDevice.ConnectionSettings.DeviceAddress:X2} does not support Adafruit SeeSaw encoder functionality");
+ }
+
+ return BinaryPrimitives.ReadInt32BigEndian(Read(SeesawModule.Encoder, SeesawFunction.EncoderDelta + encoder, 4, 8000));
+ }
+
+ ///
+ /// Enable the interrupt to fire when the encoder changes position.
+ ///
+ /// Which encoder to use, defaults to 0.
+ /// The hardware does not support Adafruit SeeSaw encoder functionality.
+ public void EnableEncoderInterrupt(byte encoder = 0)
+ {
+ if (!HasModule(SeesawModule.Encoder))
+ {
+ throw new InvalidOperationException($"The hardware on I2C Bus {I2cDevice.ConnectionSettings.BusId}, Address 0x{I2cDevice.ConnectionSettings.DeviceAddress:X2} does not support Adafruit SeeSaw encoder functionality");
+ }
+
+ WriteByte(SeesawModule.Encoder, SeesawFunction.EncoderIntenset + encoder, 0x01);
+ }
+
+ ///
+ /// Disable the interrupt from firing when the encoder changes.
+ ///
+ /// Which encoder to use, defaults to 0.
+ /// The hardware does not support Adafruit SeeSaw encoder functionality.
+ public void DisableEncoderInterrupt(byte encoder = 0)
+ {
+ if (!HasModule(SeesawModule.Encoder))
+ {
+ throw new InvalidOperationException($"The hardware on I2C Bus {I2cDevice.ConnectionSettings.BusId}, Address 0x{I2cDevice.ConnectionSettings.DeviceAddress:X2} does not support Adafruit SeeSaw encoder functionality");
+ }
+
+ WriteByte(SeesawModule.Encoder, SeesawFunction.EncoderIntenclr + encoder, 0x01);
+ }
+ }
+}
diff --git a/src/devices/Seesaw/SeesawModules.cs b/src/devices/Seesaw/SeesawModules.cs
index 08c371068b16839a9f75f2eca19b9ca176416dc8..4e9e6ad48364a5c8ae1a3b3d47e74ad49ef2ce51 100644
--- a/src/devices/Seesaw/SeesawModules.cs
+++ b/src/devices/Seesaw/SeesawModules.cs
@@ -138,6 +138,24 @@ namespace Iot.Device.Seesaw
/// Touch channel offset
TouchChannelOffset = 0x10,
+
+ // encoder functions
+
+ /// Status
+ EncoderStatus = 0x00,
+
+ /// Enable encoder interrupt
+ EncoderIntenset = 0x10,
+
+ /// Clear encoder interrupt
+ EncoderIntenclr = 0x20,
+
+ /// Encoder position
+ EncoderPosition = 0x30,
+
+ /// Encoder position delta
+ EncoderDelta = 0x40
+
}
}
}
diff --git a/src/devices/Seesaw/SeesawSampleEncoder.fzz b/src/devices/Seesaw/SeesawSampleEncoder.fzz
new file mode 100644
index 0000000000000000000000000000000000000000..0b6b75e403e5a1f6c67f0060eb831c523a053dc8
Binary files /dev/null and b/src/devices/Seesaw/SeesawSampleEncoder.fzz differ
diff --git a/src/devices/Seesaw/SeesawSampleEncoder.png b/src/devices/Seesaw/SeesawSampleEncoder.png
new file mode 100644
index 0000000000000000000000000000000000000000..49e18a6f90ff25b41c71d86841906f8186c27ee8
Binary files /dev/null and b/src/devices/Seesaw/SeesawSampleEncoder.png differ
diff --git a/src/devices/Seesaw/samples/Seesaw.Sample.Encoder.cs b/src/devices/Seesaw/samples/Seesaw.Sample.Encoder.cs
new file mode 100644
index 0000000000000000000000000000000000000000..7df8fc41b324e494fe3a08a3556cc396e2cb1aa1
--- /dev/null
+++ b/src/devices/Seesaw/samples/Seesaw.Sample.Encoder.cs
@@ -0,0 +1,48 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Device.Gpio;
+using System.Device.I2c;
+using Iot.Device.Seesaw;
+
+const byte AdafruitSeesawRotaryEncoderI2cAddress = 0x36;
+const byte AdafruitSeesawRotaryEncoderI2cBus = 0x1;
+const byte AdafruitSeesawRotaryEncoderPushSwitchPin = 24;
+
+const int EncoderPositionInitialValue = 100;
+const int HostInterruptPin = 6;
+
+using GpioController gpioController = new(PinNumberingScheme.Logical);
+using Seesaw seesawDevice = new(I2cDevice.Create(new I2cConnectionSettings(AdafruitSeesawRotaryEncoderI2cBus, AdafruitSeesawRotaryEncoderI2cAddress)));
+
+// set initial encoder position value
+seesawDevice.SetEncoderPosition(EncoderPositionInitialValue);
+
+// enable interrupt for position changes on Seesaw encoder
+seesawDevice.EnableEncoderInterrupt();
+
+// enable interrupt for rotary encoder push switch
+uint encoderPushSwitchPinMask = 1U << (AdafruitSeesawRotaryEncoderPushSwitchPin);
+seesawDevice.SetGpioPinMode(AdafruitSeesawRotaryEncoderPushSwitchPin, PinMode.InputPullUp);
+seesawDevice.SetGpioInterrupts(encoderPushSwitchPinMask, true);
+
+// enable host interrupt and register callback
+gpioController.OpenPin(HostInterruptPin, PinMode.InputPullUp);
+gpioController.RegisterCallbackForPinValueChangedEvent(HostInterruptPin, PinEventTypes.Falling, (s, e) =>
+{
+ uint interruptFlags = seesawDevice.ReadGpioInterruptFlags();
+ if ((interruptFlags & encoderPushSwitchPinMask) > 0)
+ {
+ // interrupt for push switch pin -> push switch status changed
+ bool pushSwitchReleased = seesawDevice.ReadGpioDigital(AdafruitSeesawRotaryEncoderPushSwitchPin);
+ Console.WriteLine($"Encoder switch {(pushSwitchReleased ? "released" : "pressed")}");
+ return;
+ }
+
+ int encoderPosition = seesawDevice.GetEncoderPosition();
+ Console.WriteLine($"Encoder position changed: {encoderPosition}");
+});
+
+// press Enter to exit application
+Console.ReadLine();
diff --git a/src/devices/Seesaw/samples/Seesaw.Sample.Encoder.csproj b/src/devices/Seesaw/samples/Seesaw.Sample.Encoder.csproj
new file mode 100644
index 0000000000000000000000000000000000000000..656c4af8169ddecc2d4ce9ea60ebbf0c97301e70
--- /dev/null
+++ b/src/devices/Seesaw/samples/Seesaw.Sample.Encoder.csproj
@@ -0,0 +1,13 @@
+
+
+ Exe
+ $(DefaultSampleTfms)
+ false
+
+
+
+
+
+
+
+