diff --git a/ServoSkull.sln b/ServoSkull.sln index 0ae9619..ab8d276 100644 --- a/ServoSkull.sln +++ b/ServoSkull.sln @@ -8,6 +8,9 @@ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "skullOS.Core", "skullOS.Core\skullOS.Core.csproj", "{464B162B-62FC-49CC-A6AB-72DBE959DD8C}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "skullOS", "skullOS\skullOS.csproj", "{6D3C5D36-0002-4EFD-A3B9-0924D6D3CCD8}" + ProjectSection(ProjectDependencies) = postProject + {9AC8960C-539A-4B2C-9D24-5169AF4337FA} = {9AC8960C-539A-4B2C-9D24-5169AF4337FA} + EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "skullOS.Camera", "skullOS.Camera\skullOS.Camera.csproj", "{D4E9CB35-2264-4717-BDB6-7797E84B5DFE}" EndProject @@ -24,6 +27,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "skullOS.Input.Tests", "skul EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "skullOS.Core.Tests", "skullOS.Core.Tests\skullOS.Core.Tests.csproj", "{BA89208E-E059-45A3-8A0D-4052063C79D4}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{B50B951E-DF9B-49DD-9CAC-ED8B01C6B1E7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "skullOS.Output", "skullOS.Output\skullOS.Output.csproj", "{9AC8960C-539A-4B2C-9D24-5169AF4337FA}" + ProjectSection(ProjectDependencies) = postProject + {464B162B-62FC-49CC-A6AB-72DBE959DD8C} = {464B162B-62FC-49CC-A6AB-72DBE959DD8C} + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -66,10 +76,20 @@ Global {BA89208E-E059-45A3-8A0D-4052063C79D4}.Debug|Any CPU.Build.0 = Debug|Any CPU {BA89208E-E059-45A3-8A0D-4052063C79D4}.Release|Any CPU.ActiveCfg = Release|Any CPU {BA89208E-E059-45A3-8A0D-4052063C79D4}.Release|Any CPU.Build.0 = Release|Any CPU + {9AC8960C-539A-4B2C-9D24-5169AF4337FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9AC8960C-539A-4B2C-9D24-5169AF4337FA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9AC8960C-539A-4B2C-9D24-5169AF4337FA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9AC8960C-539A-4B2C-9D24-5169AF4337FA}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {9B4C6E1D-BE16-40F3-AFB7-369ACA5BFDBC} = {B50B951E-DF9B-49DD-9CAC-ED8B01C6B1E7} + {45513FCC-E0E9-4C2A-A48F-239A380347BC} = {B50B951E-DF9B-49DD-9CAC-ED8B01C6B1E7} + {237A08B2-E8F4-4384-A894-4C356CE45E92} = {B50B951E-DF9B-49DD-9CAC-ED8B01C6B1E7} + {BA89208E-E059-45A3-8A0D-4052063C79D4} = {B50B951E-DF9B-49DD-9CAC-ED8B01C6B1E7} + EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {FA7BE016-5699-47F3-A654-08A907CE1F11} EndGlobalSection diff --git a/skullOS.Camera/Camera.cs b/skullOS.Camera/Camera.cs index f9e9030..9efa466 100644 --- a/skullOS.Camera/Camera.cs +++ b/skullOS.Camera/Camera.cs @@ -1,6 +1,7 @@ using Iot.Device.Button; using Iot.Device.Media; using skullOS.Core; +using System.Device.Gpio; namespace skullOS.Camera { @@ -10,27 +11,40 @@ public class Camera : Controller VideoDevice device; VideoConnectionSettings deviceSettings = new(busId: 0, captureSize: (2560, 1440), pixelFormat: VideoPixelFormat.JPEG); + skullOS.Core.LED_Elements.SkullLed cameraLight; + bool hasCameraLed = false; public Camera() { } - public override void Run() + public override void Run(GpioController controller) { } - public override bool Setup() + public override bool Setup(GpioController controller) { var settings = SettingsLoader.LoadConfig(@"Data/Settings.txt"); + var defaultValue = new KeyValuePair("", ""); var cameraMode = settings .Select(x => x) .Where(x => x.Key == "Mode") - .FirstOrDefault(); + .FirstOrDefault(defaultValue); var pinToActOn = settings .Select(x => x) .Where(x => x.Key == "Pin") - .FirstOrDefault(); + .FirstOrDefault(defaultValue); + var ledPin = settings + .Select(x => x) + .Where(x => x.Key == "LedPin") + .FirstOrDefault(defaultValue); + + if (!ledPin.Equals(defaultValue)) + { + cameraLight = new Core.LED_Elements.SkullLed("Camera Light", int.Parse(ledPin.Value), controller); + hasCameraLed = true; + } switch (cameraMode.Value) { @@ -39,8 +53,16 @@ public override bool Setup() GpioButton button = new(int.Parse(pinToActOn.Value)); button.Press += (sender, e) => { + if (hasCameraLed) + { + cameraLight.ToggleState(); + } Console.WriteLine($"({DateTime.Now}) Picture taken!"); device.Capture($"{DateTime.Now:yyyyMMddHHmmss}.jpg"); + if (hasCameraLed) + { + cameraLight.ToggleState(); + } }; break; diff --git a/skullOS.Camera/Data/Settings.txt b/skullOS.Camera/Data/Settings.txt index 1c78816..3464845 100644 --- a/skullOS.Camera/Data/Settings.txt +++ b/skullOS.Camera/Data/Settings.txt @@ -1,2 +1,3 @@ Pin=25 -Mode=Image \ No newline at end of file +Mode=Image +LedPin=26 \ No newline at end of file diff --git a/skullOS.Core/Controller.cs b/skullOS.Core/Controller.cs index dfd703b..a6145b5 100644 --- a/skullOS.Core/Controller.cs +++ b/skullOS.Core/Controller.cs @@ -1,12 +1,13 @@ using skullOS.Core.Interfaces; +using System.Device.Gpio; namespace skullOS.Core { public abstract class Controller : ISubSystem { - public abstract void Run(); + public abstract void Run(GpioController controller); - public abstract bool Setup(); + public abstract bool Setup(GpioController controller); public abstract void Stop(); } diff --git a/skullOS.Core/Interfaces/ISubSystem.cs b/skullOS.Core/Interfaces/ISubSystem.cs index afef78c..7fec549 100644 --- a/skullOS.Core/Interfaces/ISubSystem.cs +++ b/skullOS.Core/Interfaces/ISubSystem.cs @@ -1,10 +1,12 @@ -namespace skullOS.Core.Interfaces +using System.Device.Gpio; + +namespace skullOS.Core.Interfaces { public interface ISubSystem { - bool Setup(); + bool Setup(GpioController controller); - void Run(); + void Run(GpioController controller); void Stop(); } diff --git a/skullOS.Core/LED Elements/SkullLed.cs b/skullOS.Core/LED Elements/SkullLed.cs new file mode 100644 index 0000000..1bfa7f2 --- /dev/null +++ b/skullOS.Core/LED Elements/SkullLed.cs @@ -0,0 +1,33 @@ +using System.Device.Gpio; + +namespace skullOS.Core.LED_Elements +{ + public enum LedBehaviour + { + On, + Off, + OnEvent + } + + public class SkullLed + { + public string name = ""; + public int pin = 0; + public LedBehaviour state = LedBehaviour.Off; + + private GpioController gpioController; + bool ledOn = false; + + public SkullLed(string LedName, int ledPin, GpioController controller) + { + name = LedName; + pin = ledPin; + gpioController = controller; + } + + public void ToggleState() + { + gpioController.Write(pin, ((ledOn) ? PinValue.High : PinValue.Low)); + } + } +} diff --git a/skullOS.Core/skullOS.Core.csproj b/skullOS.Core/skullOS.Core.csproj index 8edb9fa..b3b3de4 100644 --- a/skullOS.Core/skullOS.Core.csproj +++ b/skullOS.Core/skullOS.Core.csproj @@ -1,4 +1,4 @@ - + net7.0 diff --git a/skullOS.Input/Input.cs b/skullOS.Input/Input.cs index 912a2c5..83c9673 100644 --- a/skullOS.Input/Input.cs +++ b/skullOS.Input/Input.cs @@ -1,4 +1,5 @@ using skullOS.Core; +using System.Device.Gpio; namespace skullOS.Input { @@ -15,12 +16,12 @@ public List InputDevices get { return inputDevices; } } - public override void Run() + public override void Run(GpioController controller) { throw new NotImplementedException(); } - public override bool Setup() + public override bool Setup(GpioController controller) { throw new NotImplementedException(); } diff --git a/skullOS.Output/Data/Output.txt b/skullOS.Output/Data/Output.txt new file mode 100644 index 0000000..e751d8f --- /dev/null +++ b/skullOS.Output/Data/Output.txt @@ -0,0 +1,4 @@ +BuzzerPin = 17 +NeoPixel = True +NeoPixelCount = 8 +LedPin = 23 \ No newline at end of file diff --git a/skullOS.Output/Output.cs b/skullOS.Output/Output.cs new file mode 100644 index 0000000..0a5b144 --- /dev/null +++ b/skullOS.Output/Output.cs @@ -0,0 +1,50 @@ +using skullOS.Core; +using System.Device.Gpio; +using System.Drawing; + +namespace skullOS.Output +{ + public class Output : Controller + { + public List outputDevices = new(); + + public override void Run(GpioController controller) + { + var pixelDisplay = (SkullNeoPixel)outputDevices.Select(x => x).Where(x => x.Name == "NeoPixel").FirstOrDefault(); + pixelDisplay.device.Image.SetPixel(0, 0, Color.AliceBlue); + } + + public override bool Setup(GpioController controller) + { + var settings = SettingsLoader.LoadConfig(@"Data/Settings.txt"); + var defaultValue = new KeyValuePair("", ""); + + if (settings.ContainsKey("Buzzer")) + { + settings.TryGetValue("Buzzer", out string BuzzerPin); + var deviceBuzzer = new SkullBuzzer("Buzzer", int.Parse(BuzzerPin)); + outputDevices.Add(deviceBuzzer); + } + if (settings.ContainsKey("NeoPixel")) + { + settings.TryGetValue("NeoPixelCount", out string count); + var deviceNeoPixel = new SkullNeoPixel("NeoPixel", int.Parse(count)); + outputDevices.Add(deviceNeoPixel); + } + + if (settings.ContainsKey("LedPin")) + { + settings.TryGetValue("LedPin", out string pin); + var deviceLed = new skullOS.Output.SkullLed("Life Light", int.Parse(pin), controller); + outputDevices.Add(deviceLed); + } + + return true; + } + + public override void Stop() + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/skullOS.Output/SkullBuzzer.cs b/skullOS.Output/SkullBuzzer.cs new file mode 100644 index 0000000..d4f6db3 --- /dev/null +++ b/skullOS.Output/SkullBuzzer.cs @@ -0,0 +1,22 @@ +using Iot.Device.Buzzer; + +namespace skullOS.Output +{ + internal class SkullBuzzer : SkullOutputDevice + { + int Pin = 0; + public Buzzer device + { + get { return buzzer; } + } + Buzzer buzzer; + + + public SkullBuzzer(string name, int pin) + { + this.Name = name; + this.Pin = pin; + buzzer = new Buzzer(pin); + } + } +} diff --git a/skullOS.Output/SkullLed.cs b/skullOS.Output/SkullLed.cs new file mode 100644 index 0000000..1594e18 --- /dev/null +++ b/skullOS.Output/SkullLed.cs @@ -0,0 +1,27 @@ +using skullOS.Core.LED_Elements; +using System.Device.Gpio; + +namespace skullOS.Output +{ + public class SkullLed : SkullOutputDevice + { + public string name = ""; + public int pin = 0; + public LedBehaviour state = LedBehaviour.Off; + + private GpioController gpioController; + bool ledOn = false; + + public SkullLed(string LedName, int ledPin, GpioController controller) + { + name = LedName; + pin = ledPin; + gpioController = controller; + } + + public void ToggleState() + { + gpioController.Write(pin, ((ledOn) ? PinValue.High : PinValue.Low)); + } + } +} diff --git a/skullOS.Output/SkullNeoPixel.cs b/skullOS.Output/SkullNeoPixel.cs new file mode 100644 index 0000000..58b7adb --- /dev/null +++ b/skullOS.Output/SkullNeoPixel.cs @@ -0,0 +1,30 @@ +using Iot.Device.Ws28xx; +using System.Device.Spi; + +namespace skullOS.Output +{ + internal class SkullNeoPixel : SkullOutputDevice + { + public Ws2812b device + { + get { return neoPixel; } + } + Ws2812b neoPixel; + + public SkullNeoPixel(string name, int count) + { + this.Name = name; + + SpiConnectionSettings spiSettings = new(0, 0) + { + ClockFrequency = 2_400_000, + Mode = SpiMode.Mode0, + DataBitLength = 8 + }; + using SpiDevice spi = SpiDevice.Create(spiSettings); + + Ws2812b neopixel = new(spi, count); + + } + } +} diff --git a/skullOS.Output/SkullOutputDevice.cs b/skullOS.Output/SkullOutputDevice.cs new file mode 100644 index 0000000..edc45ce --- /dev/null +++ b/skullOS.Output/SkullOutputDevice.cs @@ -0,0 +1,7 @@ +namespace skullOS.Output +{ + public abstract class SkullOutputDevice + { + public string Name = ""; + } +} diff --git a/skullOS.Output/skullOS.Output.csproj b/skullOS.Output/skullOS.Output.csproj new file mode 100644 index 0000000..c84b806 --- /dev/null +++ b/skullOS.Output/skullOS.Output.csproj @@ -0,0 +1,19 @@ + + + + net7.0 + enable + enable + + + + + + + + + Always + + + + diff --git a/skullOS.Tests/MockGpioController.cs b/skullOS.Tests/MockGpioController.cs new file mode 100644 index 0000000..55b8b83 --- /dev/null +++ b/skullOS.Tests/MockGpioController.cs @@ -0,0 +1,12 @@ +using System.Device.Gpio; + +namespace skullOS.Tests +{ + internal class MockGpioController : GpioController + { + public MockGpioController() + { + + } + } +} diff --git a/skullOS.Tests/ModulesTests.cs b/skullOS.Tests/ModulesTests.cs index 40129ce..8a2b390 100644 --- a/skullOS.Tests/ModulesTests.cs +++ b/skullOS.Tests/ModulesTests.cs @@ -6,7 +6,7 @@ public class ModulesTests [Fact] public void TestModuleConstructor() { - string testDataLocation = @"Data\Modules.txt"; + string testDataLocation = @"Data/Modules.txt"; Modules modules = new Modules(testDataLocation); Assert.Single(modules.Get()); Assert.Equal("Camera", modules.Get()[0].ModuleName); diff --git a/skullOS.Tests/ProgramTests.cs b/skullOS.Tests/ProgramTests.cs index dc75b73..0075ecb 100644 --- a/skullOS.Tests/ProgramTests.cs +++ b/skullOS.Tests/ProgramTests.cs @@ -7,7 +7,7 @@ public class ProgramTests { static List CreateTestModules() { - string testDataLocation = @"Data\Modules.txt"; + string testDataLocation = @"Data/Modules.txt"; Modules modules = new(testDataLocation); return Program.LoadModules(modules); } @@ -22,15 +22,17 @@ public void TestLoadModules() [Fact] public void TestSetupModules() { + var mockController = new MockGpioController(); var modulesLoaded = CreateTestModules(); - Assert.True(Program.SetupModules(modulesLoaded)); + Assert.True(Program.SetupModules(modulesLoaded, mockController)); } [Fact] public void TestRunModules() { + var mockController = new MockGpioController(); var modulesLoaded = CreateTestModules(); - Assert.True(Program.RunModules(modulesLoaded)); + Assert.True(Program.RunModules(modulesLoaded, mockController)); } } } diff --git a/skullOS.Tests/skullOS.Tests.csproj b/skullOS.Tests/skullOS.Tests.csproj index 98e3aa1..b487c87 100644 --- a/skullOS.Tests/skullOS.Tests.csproj +++ b/skullOS.Tests/skullOS.Tests.csproj @@ -10,6 +10,7 @@ + runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/skullOS/Data/Modules.txt b/skullOS/Data/Modules.txt index 6705890..993e8d9 100644 --- a/skullOS/Data/Modules.txt +++ b/skullOS/Data/Modules.txt @@ -1 +1,2 @@ -Camera = True \ No newline at end of file +Camera = True +Output = True \ No newline at end of file diff --git a/skullOS/Program.cs b/skullOS/Program.cs index e9a55b8..d76d719 100644 --- a/skullOS/Program.cs +++ b/skullOS/Program.cs @@ -1,4 +1,5 @@ using skullOS.Core.Interfaces; +using System.Device.Gpio; using System.Reflection; namespace skullOS @@ -33,27 +34,28 @@ public static async Task Main(string[] args) static void Run(Modules modulesToLoad = null) { + GpioController controller = new(); List systemsLoaded = LoadModules(modulesToLoad); - SetupModules(systemsLoaded); + SetupModules(systemsLoaded, controller); - RunModules(systemsLoaded); + RunModules(systemsLoaded, controller); } - public static bool RunModules(List systemsLoaded) + public static bool RunModules(List systemsLoaded, GpioController controller) { foreach (ISubSystem system in systemsLoaded) { - system.Run(); + system.Run(controller); } return true; } - public static bool SetupModules(List systemsLoaded) + public static bool SetupModules(List systemsLoaded, GpioController controller) { foreach (var system in systemsLoaded) { - if (!system.Setup()) + if (!system.Setup(controller)) { throw new Exception($"{system} failed to load"); } diff --git a/skullOS/skullOS.csproj b/skullOS/skullOS.csproj index 366ca9d..b673c00 100644 --- a/skullOS/skullOS.csproj +++ b/skullOS/skullOS.csproj @@ -8,6 +8,7 @@ +