diff --git a/Source/Meadow.Foundation.Libraries_and_Frameworks/Graphics.MicroLayout/Driver/Controls/Circle.cs b/Source/Meadow.Foundation.Libraries_and_Frameworks/Graphics.MicroLayout/Driver/Controls/Circle.cs index a1de5fe3f8..534847e5da 100644 --- a/Source/Meadow.Foundation.Libraries_and_Frameworks/Graphics.MicroLayout/Driver/Controls/Circle.cs +++ b/Source/Meadow.Foundation.Libraries_and_Frameworks/Graphics.MicroLayout/Driver/Controls/Circle.cs @@ -67,6 +67,28 @@ public Point Center set => SetInvalidatingProperty(ref center, value); } + /// + public override int Left + { + get => center.X - radius; + set + { + center.X = value + radius; + Invalidate(); + } + } + + /// + public override int Top + { + get => center.Y - radius; + set + { + center.Y = value + radius; + Invalidate(); + } + } + /// /// Gets or sets the foreground color of the Circle. /// diff --git a/Source/Meadow.Foundation.Libraries_and_Frameworks/Graphics.MicroLayout/Driver/Controls/Picture.cs b/Source/Meadow.Foundation.Libraries_and_Frameworks/Graphics.MicroLayout/Driver/Controls/Picture.cs index 307fdba929..3db4fa7963 100644 --- a/Source/Meadow.Foundation.Libraries_and_Frameworks/Graphics.MicroLayout/Driver/Controls/Picture.cs +++ b/Source/Meadow.Foundation.Libraries_and_Frameworks/Graphics.MicroLayout/Driver/Controls/Picture.cs @@ -11,7 +11,7 @@ public class Picture : ThemedControl private Color _backColor = Color.Transparent; private VerticalAlignment _verticalAlignment = VerticalAlignment.Center; private HorizontalAlignment _horizontalAlignment = HorizontalAlignment.Center; - private MF.Image _image = default!; + private MF.Image? _image = default!; /// /// Initializes a new instance of the class with the specified dimensions and image. @@ -21,7 +21,7 @@ public class Picture : ThemedControl /// The width of the image display control. /// The height of the image display control. /// The image to be displayed. - public Picture(int left, int top, int width, int height, MF.Image image) + public Picture(int left, int top, int width, int height, MF.Image? image = null) : base(left, top, width, height) { Image = image; @@ -42,7 +42,7 @@ public override void ApplyTheme(DisplayTheme theme) /// /// Gets or sets the image to be displayed on the image display control. /// - public MF.Image Image + public MF.Image? Image { get => _image; set => SetInvalidatingProperty(ref _image, value); @@ -81,6 +81,8 @@ public Color BackColor /// The surface to draw the image display control on. protected override void OnDraw(MicroGraphics graphics) { + if (Image == null) { return; } + if (BackColor != Color.Transparent) { graphics.DrawRectangle( diff --git a/Source/Meadow.Foundation.Libraries_and_Frameworks/Serialization.MicroJson/Driver/MicroJson.TypeSafe.cs b/Source/Meadow.Foundation.Libraries_and_Frameworks/Serialization.MicroJson/Driver/MicroJson.TypeSafe.cs index 363e97ea72..b0ae15a214 100644 --- a/Source/Meadow.Foundation.Libraries_and_Frameworks/Serialization.MicroJson/Driver/MicroJson.TypeSafe.cs +++ b/Source/Meadow.Foundation.Libraries_and_Frameworks/Serialization.MicroJson/Driver/MicroJson.TypeSafe.cs @@ -2,9 +2,15 @@ using System.Collections; using System.Collections.Generic; using System.Linq; +using System.Text; namespace Meadow.Foundation.Serialization; +[AttributeUsage(AttributeTargets.Property, Inherited = true)] +public class JsonIgnoreAttribute : Attribute +{ +} + public static partial class MicroJson { /// @@ -26,7 +32,7 @@ public static List DeserializeList(ArrayList array) /// /// The type of objects in the list. /// The JSON array to deserialize. - /// The type of objects in the list as a . + /// The type of objects in the list as a . /// /// A list of objects of type T. private static void DeserializeList(ArrayList array, Type type, ref List instance) @@ -73,55 +79,14 @@ public static T[] DeserializeArray(ArrayList array) } /// - /// Deserializes a JSON array into an array of objects of the specified type. - /// - /// The JSON array to deserialize. - /// The type of objects in the array as a . - /// The array instance to populate. - private static void DeserializeArray(ArrayList array, Type type, ref Array instance) - { - var index = 0; - - foreach (Hashtable item in array) - { - if (type == typeof(string)) - { - var e = item.GetEnumerator(); - e.MoveNext(); - instance.SetValue(((DictionaryEntry)e.Current).Value, index); - index++; - } - else - { - var arrayItem = Activator.CreateInstance(type); - Deserialize(item, type, ref arrayItem); - instance.SetValue(arrayItem, index++); - } - } - } - - /// - /// Deserializes an object of type T from a JSON string or Hashtable. + /// Deserializes an object of type T from a JSON string. /// /// The type of object to deserialize. - /// The JSON string or Hashtable to deserialize. + /// A UTF8-encoded JSON string to deserialize. /// An object of type T. - public static T Deserialize(object data) + public static T Deserialize(byte[] encodedData) { - if (data is string json) - { - return Deserialize(json); - } - else if (data is Hashtable hashtable) - { - object? instance = Activator.CreateInstance(); - Deserialize(hashtable, typeof(T), ref instance!); - return (T)instance; - } - else - { - throw new ArgumentException("Unsupported data type for deserialization."); - } + return Deserialize(Encoding.UTF8.GetString(encodedData)); } /// @@ -134,6 +99,17 @@ public static T Deserialize(string json) { var type = typeof(T); + return (T)Deserialize(json, type); + } + + /// + /// Deserializes an object of type T from a JSON string. + /// + /// The type of object to deserialize. + /// The JSON string to deserialize. + /// An object of the specified type + public static object Deserialize(string json, Type type) + { if (type.IsArray) { var elementType = type.GetElementType(); @@ -152,7 +128,7 @@ public static T Deserialize(string json) } } - return (T)(object)targetArray; + return targetArray; } else if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>)) { @@ -172,14 +148,14 @@ public static T Deserialize(string json) } } - return (T)targetList; + return targetList; } else { object instance = Activator.CreateInstance(type); - Deserialize(json, typeof(T), ref instance); + Deserialize(json, type, ref instance); - return (T)instance; + return instance; } } @@ -206,87 +182,107 @@ private static void Deserialize(Hashtable? root, Type type, ref object instance) { var values = root ?? throw new ArgumentException(); - var props = type.GetProperties(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic).ToList(); + var props = type.GetProperties( + System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic) + .Where(p => p.GetCustomAttributes(typeof(JsonIgnoreAttribute), true).Length == 0) + .ToList(); foreach (string v in values.Keys) { - var prop = props.FirstOrDefault(p => string.Compare(p.Name, v, true) == 0); + var prop = props.FirstOrDefault(p => string.Compare(p.Name, v, StringComparison.OrdinalIgnoreCase) == 0); if (prop != null && prop.CanWrite) { - switch (true) + Type propType = prop.PropertyType; + + if (propType.IsEnum) + { + prop.SetValue(instance, Enum.Parse(propType, values[v].ToString())); + } + else if (propType.IsArray) + { + var al = values[v] as ArrayList; + var elementType = propType.GetElementType(); + var targetArray = Array.CreateInstance(elementType, al!.Count); + for (int i = 0; i < al.Count; i++) + { + object arrayItem = Activator.CreateInstance(elementType); + Deserialize(al[i] as Hashtable, elementType, ref arrayItem); + targetArray.SetValue(arrayItem, i); + } + prop.SetValue(instance, targetArray); + } + else if (propType.IsGenericType && propType.GetGenericTypeDefinition() == typeof(List<>)) + { + var listType = propType.GetGenericArguments()[0]; + var list = Activator.CreateInstance(propType); + var addMethod = propType.GetMethod("Add"); + + foreach (var item in (ArrayList)values[v]) + { + object listItem = Activator.CreateInstance(listType); + Deserialize(item as Hashtable, listType, ref listItem); + addMethod.Invoke(list, new[] { listItem }); + } + + prop.SetValue(instance, list); + } + else if (propType.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IDictionary<,>))) + { + var dictionaryType = propType.GetGenericTypeDefinition(); + var keyType = propType.GetGenericArguments()[0]; + var valueType = propType.GetGenericArguments()[1]; + + var dictionary = (IDictionary)Activator.CreateInstance(propType); + + foreach (DictionaryEntry entry in (Hashtable)values[v]) + { + object key = Convert.ChangeType(entry.Key, keyType); + object value = Activator.CreateInstance(valueType); + Deserialize((Hashtable)entry.Value, valueType, ref value); + dictionary.Add(key, value); + } + + prop.SetValue(instance, dictionary); + } + else if (IsComplexType(propType)) { - case bool _ when prop.PropertyType.IsEnum: - var enumValue = Enum.Parse(prop.PropertyType, values[v].ToString()); - prop.SetValue(instance, enumValue); - break; - case bool _ when prop.PropertyType == typeof(ulong): - prop.SetValue(instance, Convert.ToUInt64(values[v])); - break; - case bool _ when prop.PropertyType == typeof(long): - prop.SetValue(instance, Convert.ToInt64(values[v])); - break; - case bool _ when prop.PropertyType == typeof(uint): - prop.SetValue(instance, Convert.ToUInt32(values[v])); - break; - case bool _ when prop.PropertyType == typeof(int): - prop.SetValue(instance, Convert.ToInt32(values[v])); - break; - case bool _ when prop.PropertyType == typeof(ushort): - prop.SetValue(instance, Convert.ToUInt16(values[v])); - break; - case bool _ when prop.PropertyType == typeof(short): - prop.SetValue(instance, Convert.ToInt16(values[v])); - break; - case bool _ when prop.PropertyType == typeof(byte): - prop.SetValue(instance, Convert.ToByte(values[v])); - break; - case bool _ when prop.PropertyType == typeof(sbyte): - prop.SetValue(instance, Convert.ToBoolean(values[v])); - break; - case bool _ when prop.PropertyType == typeof(double): - prop.SetValue(instance, Convert.ToDouble(values[v])); - break; - case bool _ when prop.PropertyType == typeof(float): - prop.SetValue(instance, Convert.ToSingle(values[v])); - break; - case bool _ when prop.PropertyType == typeof(bool): - prop.SetValue(instance, Convert.ToBoolean(values[v])); - break; - case bool _ when prop.PropertyType == typeof(string): - prop.SetValue(instance, values[v].ToString()); - break; - default: - if (prop.PropertyType.IsArray) - { - var al = values[v] as ArrayList; - var elementType = prop.PropertyType.GetElementType(); - var targetArray = Array.CreateInstance(elementType, al!.Count); - DeserializeArray(al, elementType, ref targetArray); - prop.SetValue(instance, targetArray); - } - else if (prop.PropertyType.IsGenericType && prop.PropertyType.GetGenericTypeDefinition() == typeof(List<>)) - { - var listType = prop.PropertyType.GetGenericArguments()[0]; - var list = Activator.CreateInstance(prop.PropertyType); - var addMethod = prop.PropertyType.GetMethod("Add"); - - foreach (var item in (ArrayList)values[v]) - { - var listItem = Activator.CreateInstance(listType); - Deserialize(item as Hashtable, listType, ref listItem); - addMethod.Invoke(list, new[] { listItem }); - } - - prop.SetValue(instance, list); - } - else - { - throw new NotSupportedException($"Type '{prop.PropertyType}' not supported"); - } - break; + if (values[v] is Hashtable hashtableValue) + { + object complexInstance = Activator.CreateInstance(propType); + Deserialize(hashtableValue, propType, ref complexInstance); + prop.SetValue(instance, complexInstance); + } + } + else + { + if (values[v] != null && values[v] != DBNull.Value) + { + prop.SetValue(instance, Convert.ChangeType(values[v], propType)); + } } } } } + + private static bool IsComplexType(Type type) + { + if (type.IsPrimitive || + type.IsEnum || + type == typeof(string) || + type == typeof(decimal) || + type == typeof(DateTime) || + type == typeof(Guid) + ) + { + return false; + } + + if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) + { + return IsComplexType(Nullable.GetUnderlyingType(type)); + } + + return true; + } } \ No newline at end of file diff --git a/Source/Meadow.Foundation.Libraries_and_Frameworks/Serialization.MicroJson/Driver/MicroJson.cs b/Source/Meadow.Foundation.Libraries_and_Frameworks/Serialization.MicroJson/Driver/MicroJson.cs index 5e7a008d01..7177e79c07 100644 --- a/Source/Meadow.Foundation.Libraries_and_Frameworks/Serialization.MicroJson/Driver/MicroJson.cs +++ b/Source/Meadow.Foundation.Libraries_and_Frameworks/Serialization.MicroJson/Driver/MicroJson.cs @@ -1,5 +1,6 @@ using System; using System.Collections; +using System.Linq; using System.Reflection; using System.Text; @@ -113,7 +114,10 @@ public static partial class MicroJson var hashtable = new Hashtable(); // Use PropertyInfo instead of MethodInfo for better performance - PropertyInfo[] properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance); + var properties = type + .GetProperties(BindingFlags.Public | BindingFlags.Instance) + .Where(p => p.GetCustomAttributes(typeof(JsonIgnoreAttribute), true).Length == 0); + foreach (PropertyInfo property in properties) { object returnObject = property.GetValue(o); diff --git a/Source/Meadow.Foundation.Libraries_and_Frameworks/Serialization.MicroJson/Driver/MicroJson.csproj b/Source/Meadow.Foundation.Libraries_and_Frameworks/Serialization.MicroJson/Driver/MicroJson.csproj index 569f5ca7bf..a78da0d52d 100644 --- a/Source/Meadow.Foundation.Libraries_and_Frameworks/Serialization.MicroJson/Driver/MicroJson.csproj +++ b/Source/Meadow.Foundation.Libraries_and_Frameworks/Serialization.MicroJson/Driver/MicroJson.csproj @@ -22,6 +22,5 @@ - \ No newline at end of file diff --git a/Source/Meadow.Foundation.Libraries_and_Frameworks/Serialization.MicroJson/Samples/MicroJson_Complex_Sample/MicroJson_Complex_Sample.csproj b/Source/Meadow.Foundation.Libraries_and_Frameworks/Serialization.MicroJson/Samples/MicroJson_Complex_Sample/MicroJson_Complex_Sample.csproj new file mode 100644 index 0000000000..cf5ff6cc19 --- /dev/null +++ b/Source/Meadow.Foundation.Libraries_and_Frameworks/Serialization.MicroJson/Samples/MicroJson_Complex_Sample/MicroJson_Complex_Sample.csproj @@ -0,0 +1,21 @@ + + + + Exe + net8.0 + enable + + + + + + + + + + + + + + + diff --git a/Source/Meadow.Foundation.Libraries_and_Frameworks/Serialization.MicroJson/Samples/MicroJson_Complex_Sample/Program.cs b/Source/Meadow.Foundation.Libraries_and_Frameworks/Serialization.MicroJson/Samples/MicroJson_Complex_Sample/Program.cs new file mode 100644 index 0000000000..1e0ec3a93b --- /dev/null +++ b/Source/Meadow.Foundation.Libraries_and_Frameworks/Serialization.MicroJson/Samples/MicroJson_Complex_Sample/Program.cs @@ -0,0 +1,33 @@ +using Meadow.Foundation.Serialization; +using System; +using System.IO; +using System.Reflection; +using WifiWeather.DTOs; + +namespace MicroJson_Complex_Sample; + +internal class Program +{ + static void Main(string[] args) + { + Console.WriteLine("Hello, MicroJson - Complex Json"); + + var jsonData = LoadResource("weather.json"); + + var weather = MicroJson.Deserialize(jsonData); + + Console.WriteLine($"Temperature is: {weather.main.temp - 273.15:N1}C"); + } + + static byte[] LoadResource(string filename) + { + var assembly = Assembly.GetExecutingAssembly(); + var resourceName = $"MicroJson_Complex_Sample.{filename}"; + + using Stream stream = assembly.GetManifestResourceStream(resourceName); + using var ms = new MemoryStream(); + + stream?.CopyTo(ms); + return ms.ToArray(); + } +} \ No newline at end of file diff --git a/Source/Meadow.Foundation.Libraries_and_Frameworks/Serialization.MicroJson/Samples/MicroJson_Complex_Sample/WeatherReadingDTO.cs b/Source/Meadow.Foundation.Libraries_and_Frameworks/Serialization.MicroJson/Samples/MicroJson_Complex_Sample/WeatherReadingDTO.cs new file mode 100644 index 0000000000..9a9fc40d89 --- /dev/null +++ b/Source/Meadow.Foundation.Libraries_and_Frameworks/Serialization.MicroJson/Samples/MicroJson_Complex_Sample/WeatherReadingDTO.cs @@ -0,0 +1,63 @@ +namespace WifiWeather.DTOs +{ + public class WeatherReadingDTO + { + public Coordinates coord { get; set; } + public Weather[] weather { get; set; } + public WeatherValues main { get; set; } + public int visibility { get; set; } + public Wind wind { get; set; } + public Clouds clouds { get; set; } + public int dt { get; set; } + public System sys { get; set; } + public long timezone { get; set; } + public int id { get; set; } + public string name { get; set; } + public int cod { get; set; } + } + + public class Coordinates + { + public double lon { get; set; } + public double lat { get; set; } + } + + public class Weather + { + public int id { get; set; } + public string nain { get; set; } + public string description { get; set; } + public string icon { get; set; } + } + + public class WeatherValues + { + public double temp { get; set; } + public double feels_like { get; set; } + public double temp_min { get; set; } + public double temp_max { get; set; } + public int pressure { get; set; } + public int humidity { get; set; } + } + + public class Wind + { + public decimal speed { get; set; } + public int deg { get; set; } + public double gust { get; set; } + } + + public class Clouds + { + public int all { get; set; } + } + + public class System + { + public int Type { get; set; } + public int Id { get; set; } + public string country { get; set; } + public long sunrise { get; set; } + public long sunset { get; set; } + } +} \ No newline at end of file diff --git a/Source/Meadow.Foundation.Libraries_and_Frameworks/Serialization.MicroJson/Samples/MicroJson_Complex_Sample/weather.json b/Source/Meadow.Foundation.Libraries_and_Frameworks/Serialization.MicroJson/Samples/MicroJson_Complex_Sample/weather.json new file mode 100644 index 0000000000..8d6cebfcd0 --- /dev/null +++ b/Source/Meadow.Foundation.Libraries_and_Frameworks/Serialization.MicroJson/Samples/MicroJson_Complex_Sample/weather.json @@ -0,0 +1,43 @@ +{ + "coord": { + "lon": -123.1193, + "lat": 49.2497 + }, + "weather": [ + { + "id": 803, + "main": "Clouds", + "description": "broken clouds", + "icon": "04d" + } + ], + "base": "stations", + "main": { + "temp": 279.01, + "feels_like": 276, + "temp_min": 277.58, + "temp_max": 279.94, + "pressure": 1028, + "humidity": 84 + }, + "visibility": 10000, + "wind": { + "speed": 4.12, + "deg": 90 + }, + "clouds": { + "all": 75 + }, + "dt": 1710347018, + "sys": { + "type": 2, + "id": 2011597, + "country": "CA", + "sunrise": 1710340135, + "sunset": 1710382486 + }, + "timezone": -25200, + "id": 6173331, + "name": "Vancouver", + "cod": 200 +} \ No newline at end of file diff --git a/Source/Meadow.Foundation.Libraries_and_Frameworks/Serialization.MicroJson/Tests/MicroJson.Unit.Tests/BasicTests.cs b/Source/Meadow.Foundation.Libraries_and_Frameworks/Serialization.MicroJson/Tests/MicroJson.Unit.Tests/BasicTests.cs new file mode 100644 index 0000000000..1c45506988 --- /dev/null +++ b/Source/Meadow.Foundation.Libraries_and_Frameworks/Serialization.MicroJson/Tests/MicroJson.Unit.Tests/BasicTests.cs @@ -0,0 +1,21 @@ +using Meadow.Foundation.Serialization; +using Xunit; + +namespace Unit.Tests; + +public class BasicTests +{ + [Fact] + public void SimpleIntegerPropertyTest() + { + var input = """ + { + "Value": 23 + } + """; + + var result = MicroJson.Deserialize(input); + + Assert.Equal(23, result.Value); + } +} diff --git a/Source/Meadow.Foundation.Libraries_and_Frameworks/Serialization.MicroJson/Tests/MicroJson.Unit.Tests/IgnorePropertyTests.cs b/Source/Meadow.Foundation.Libraries_and_Frameworks/Serialization.MicroJson/Tests/MicroJson.Unit.Tests/IgnorePropertyTests.cs new file mode 100644 index 0000000000..d7f48d1a87 --- /dev/null +++ b/Source/Meadow.Foundation.Libraries_and_Frameworks/Serialization.MicroJson/Tests/MicroJson.Unit.Tests/IgnorePropertyTests.cs @@ -0,0 +1,38 @@ +using Meadow.Foundation.Serialization; +using Xunit; + +namespace Unit.Tests; + +public class IgnorePropertyTests +{ + [Fact] + public void SkipDeserializingIgnoredPropertyTest() + { + var input = """ + { + "ValueA": 23 + "ValueB": "This should not appear" + "ValueC": true + } + """; + + var result = MicroJson.Deserialize(input); + + Assert.Null(result.ValueB); + } + + [Fact] + public void SkipSerializingIgnoredPropertyTest() + { + var item = new IgnorableContainerClass + { + ValueA = 42, + ValueB = "This should not appear", + ValueC = true + }; + + var result = MicroJson.Serialize(item); + + Assert.DoesNotContain("ValueB", result); + } +} diff --git a/Source/Meadow.Foundation.Libraries_and_Frameworks/Serialization.MicroJson/Tests/MicroJson.Unit.Tests/Inputs.cs b/Source/Meadow.Foundation.Libraries_and_Frameworks/Serialization.MicroJson/Tests/MicroJson.Unit.Tests/Inputs.cs new file mode 100644 index 0000000000..8b34047640 --- /dev/null +++ b/Source/Meadow.Foundation.Libraries_and_Frameworks/Serialization.MicroJson/Tests/MicroJson.Unit.Tests/Inputs.cs @@ -0,0 +1,27 @@ +using System; +using System.IO; +using System.Linq; +using System.Reflection; + +namespace Unit.Tests; + +public static class Inputs +{ + public static string GetInputResource(string name) + { + var resName = Assembly + .GetExecutingAssembly() + .GetManifestResourceNames() + .Where(n => n.EndsWith(name)) + .FirstOrDefault(); + + if (resName == null) + { + throw new Exception("Resource not found"); + } + + return new StreamReader( + Assembly.GetExecutingAssembly().GetManifestResourceStream(resName)) + .ReadToEnd(); + } +} diff --git a/Source/Meadow.Foundation.Libraries_and_Frameworks/Serialization.MicroJson/Tests/MicroJson.Unit.Tests/MicroJson.Unit.Tests.csproj b/Source/Meadow.Foundation.Libraries_and_Frameworks/Serialization.MicroJson/Tests/MicroJson.Unit.Tests/MicroJson.Unit.Tests.csproj new file mode 100644 index 0000000000..9a719d1aa4 --- /dev/null +++ b/Source/Meadow.Foundation.Libraries_and_Frameworks/Serialization.MicroJson/Tests/MicroJson.Unit.Tests/MicroJson.Unit.Tests.csproj @@ -0,0 +1,40 @@ + + + + net8.0 + enable + + false + true + + + + + + + + + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/Source/Meadow.Foundation.Libraries_and_Frameworks/Serialization.MicroJson/Tests/MicroJson.Unit.Tests/WeatherJsonTests.cs b/Source/Meadow.Foundation.Libraries_and_Frameworks/Serialization.MicroJson/Tests/MicroJson.Unit.Tests/WeatherJsonTests.cs new file mode 100644 index 0000000000..551881028f --- /dev/null +++ b/Source/Meadow.Foundation.Libraries_and_Frameworks/Serialization.MicroJson/Tests/MicroJson.Unit.Tests/WeatherJsonTests.cs @@ -0,0 +1,37 @@ +using Meadow.Foundation.Serialization; +using Xunit; + +namespace Unit.Tests; + +public class WeatherJsonTests +{ + [Fact] + public void MatchedCaseWeatherDeserializationTest() + { + var json = Inputs.GetInputResource("weather.json"); + var result = MicroJson.Deserialize(json); + + Assert.NotNull(result); + Assert.NotNull(result.clouds); + Assert.NotNull(result.wind); + Assert.NotNull(result.main); + Assert.NotNull(result.weather); + Assert.NotNull(result.coord); + Assert.NotNull(result.sys); + } + + [Fact] + public void CamelCasedWeatherDeserializationTest() + { + var json = Inputs.GetInputResource("weather.json"); + var result = MicroJson.Deserialize(json); + + Assert.NotNull(result); + Assert.NotNull(result.Clouds); + Assert.NotNull(result.Wind); + Assert.NotNull(result.Main); + Assert.NotNull(result.Weather); + Assert.NotNull(result.Coord); + Assert.NotNull(result.Sys); + } +} diff --git a/Source/Meadow.Foundation.Libraries_and_Frameworks/Serialization.MicroJson/Tests/MicroJson.Unit.Tests/inputs/menu.json b/Source/Meadow.Foundation.Libraries_and_Frameworks/Serialization.MicroJson/Tests/MicroJson.Unit.Tests/inputs/menu.json new file mode 100644 index 0000000000..92ba26244c --- /dev/null +++ b/Source/Meadow.Foundation.Libraries_and_Frameworks/Serialization.MicroJson/Tests/MicroJson.Unit.Tests/inputs/menu.json @@ -0,0 +1,71 @@ +{ + "menu": [ + { + "text": "My Temp: {value}", + "id": "displayTemp", + "value": 77 + }, + { + "text": "My Age: {value}", + "id": "displayAge", + "value": 12 + }, + { + "text": "My Time", + "id": "time", + "type": "TimeDetailed" + }, + { + "text": "Edit Temp", + "id": "temp", + "type": "Temperature", + "value": 77 + }, + { + "text": "Edit Age", + "id": "age", + "type": "Age", + "value": 12 + }, + { + "text": "Parent", + "sub": [ + { "text": "Child 1" }, + { "text": "Child 2" }, + { "text": "Child 3" } + ] + }, + { + "text": "My Command", + "command": "DoSomething" + }, + { "text": "Quit" }, + { "text": "Item 7" }, + { + "text": "Item 8", + "sub": [ + { "text": "Sub Item A" }, + { "text": "Sub Item B" }, + { + "text": "Sub Item C", + "sub": [ + { "text": "Sub Item D" }, + { "text": "Sub Item E" }, + { "text": "Sub Item F" } + ] + } + ] + }, + { "text": "Item 9" }, + { "text": "Item 10" }, + { "text": "Item 11" }, + { + "text": "Item 12", + "sub": [ + { "text": "Sub Item X" }, + { "text": "Sub Item Y" }, + { "text": "Sub Item Z" } + ] + } + ] +} \ No newline at end of file diff --git a/Source/Meadow.Foundation.Libraries_and_Frameworks/Serialization.MicroJson/Tests/MicroJson.Unit.Tests/inputs/puzzles.json b/Source/Meadow.Foundation.Libraries_and_Frameworks/Serialization.MicroJson/Tests/MicroJson.Unit.Tests/inputs/puzzles.json new file mode 100644 index 0000000000..49b9df2c14 --- /dev/null +++ b/Source/Meadow.Foundation.Libraries_and_Frameworks/Serialization.MicroJson/Tests/MicroJson.Unit.Tests/inputs/puzzles.json @@ -0,0 +1 @@ +[{"Pieces":[{"X":0,"Y":2,"PieceType":4,"IsSolved":false,"IsHorizontalPiece":true},{"X":1,"Y":1,"PieceType":0,"IsSolved":false,"IsHorizontalPiece":true},{"X":3,"Y":0,"PieceType":1,"IsSolved":false,"IsHorizontalPiece":true},{"X":0,"Y":3,"PieceType":1,"IsSolved":false,"IsHorizontalPiece":true},{"X":3,"Y":2,"PieceType":3,"IsSolved":false,"IsHorizontalPiece":false},{"X":4,"Y":2,"PieceType":2,"IsSolved":false,"IsHorizontalPiece":false},{"X":5,"Y":2,"PieceType":3,"IsSolved":false,"IsHorizontalPiece":false},{"X":4,"Y":4,"PieceType":2,"IsSolved":false,"IsHorizontalPiece":false},{"X":1,"Y":5,"PieceType":0,"IsSolved":false,"IsHorizontalPiece":true},{"X":3,"Y":1,"PieceType":0,"IsSolved":false,"IsHorizontalPiece":true},{"X":0,"Y":0,"PieceType":2,"IsSolved":false,"IsHorizontalPiece":false}],"NumberOfMoves":14},{"Pieces":[{"X":0,"Y":2,"PieceType":4,"IsSolved":false,"IsHorizontalPiece":true},{"X":2,"Y":4,"PieceType":1,"IsSolved":false,"IsHorizontalPiece":true},{"X":1,"Y":5,"PieceType":0,"IsSolved":false,"IsHorizontalPiece":true},{"X":2,"Y":3,"PieceType":1,"IsSolved":false,"IsHorizontalPiece":true},{"X":5,"Y":2,"PieceType":2,"IsSolved":false,"IsHorizontalPiece":false},{"X":2,"Y":1,"PieceType":2,"IsSolved":false,"IsHorizontalPiece":false},{"X":3,"Y":0,"PieceType":2,"IsSolved":false,"IsHorizontalPiece":false},{"X":3,"Y":5,"PieceType":0,"IsSolved":false,"IsHorizontalPiece":true},{"X":5,"Y":4,"PieceType":2,"IsSolved":false,"IsHorizontalPiece":false},{"X":0,"Y":0,"PieceType":0,"IsSolved":false,"IsHorizontalPiece":true},{"X":4,"Y":1,"PieceType":0,"IsSolved":false,"IsHorizontalPiece":true},{"X":0,"Y":4,"PieceType":2,"IsSolved":false,"IsHorizontalPiece":false},{"X":1,"Y":3,"PieceType":2,"IsSolved":false,"IsHorizontalPiece":false}],"NumberOfMoves":15},{"Pieces":[{"X":0,"Y":2,"PieceType":4,"IsSolved":false,"IsHorizontalPiece":true},{"X":2,"Y":4,"PieceType":1,"IsSolved":false,"IsHorizontalPiece":true},{"X":2,"Y":1,"PieceType":3,"IsSolved":false,"IsHorizontalPiece":false},{"X":0,"Y":4,"PieceType":0,"IsSolved":false,"IsHorizontalPiece":true},{"X":1,"Y":0,"PieceType":1,"IsSolved":false,"IsHorizontalPiece":true},{"X":5,"Y":4,"PieceType":2,"IsSolved":false,"IsHorizontalPiece":false},{"X":4,"Y":1,"PieceType":2,"IsSolved":false,"IsHorizontalPiece":false},{"X":2,"Y":5,"PieceType":0,"IsSolved":false,"IsHorizontalPiece":true},{"X":5,"Y":2,"PieceType":2,"IsSolved":false,"IsHorizontalPiece":false},{"X":0,"Y":5,"PieceType":0,"IsSolved":false,"IsHorizontalPiece":true},{"X":0,"Y":0,"PieceType":2,"IsSolved":false,"IsHorizontalPiece":false},{"X":3,"Y":2,"PieceType":2,"IsSolved":false,"IsHorizontalPiece":false}],"NumberOfMoves":15},{"Pieces":[{"X":0,"Y":2,"PieceType":4,"IsSolved":false,"IsHorizontalPiece":true},{"X":2,"Y":0,"PieceType":0,"IsSolved":false,"IsHorizontalPiece":true},{"X":1,"Y":5,"PieceType":0,"IsSolved":false,"IsHorizontalPiece":true},{"X":3,"Y":3,"PieceType":3,"IsSolved":false,"IsHorizontalPiece":false},{"X":4,"Y":3,"PieceType":2,"IsSolved":false,"IsHorizontalPiece":false},{"X":5,"Y":1,"PieceType":3,"IsSolved":false,"IsHorizontalPiece":false},{"X":0,"Y":3,"PieceType":2,"IsSolved":false,"IsHorizontalPiece":false},{"X":1,"Y":4,"PieceType":0,"IsSolved":false,"IsHorizontalPiece":true},{"X":2,"Y":1,"PieceType":3,"IsSolved":false,"IsHorizontalPiece":false},{"X":3,"Y":1,"PieceType":0,"IsSolved":false,"IsHorizontalPiece":true},{"X":0,"Y":0,"PieceType":0,"IsSolved":false,"IsHorizontalPiece":true}],"NumberOfMoves":18},{"Pieces":[{"X":0,"Y":2,"PieceType":4,"IsSolved":false,"IsHorizontalPiece":true},{"X":4,"Y":0,"PieceType":3,"IsSolved":false,"IsHorizontalPiece":false},{"X":3,"Y":3,"PieceType":2,"IsSolved":false,"IsHorizontalPiece":false},{"X":1,"Y":0,"PieceType":1,"IsSolved":false,"IsHorizontalPiece":true},{"X":0,"Y":4,"PieceType":2,"IsSolved":false,"IsHorizontalPiece":false},{"X":2,"Y":3,"PieceType":2,"IsSolved":false,"IsHorizontalPiece":false},{"X":5,"Y":0,"PieceType":3,"IsSolved":false,"IsHorizontalPiece":false},{"X":0,"Y":3,"PieceType":0,"IsSolved":false,"IsHorizontalPiece":true},{"X":3,"Y":1,"PieceType":2,"IsSolved":false,"IsHorizontalPiece":false},{"X":4,"Y":5,"PieceType":0,"IsSolved":false,"IsHorizontalPiece":true},{"X":4,"Y":3,"PieceType":0,"IsSolved":false,"IsHorizontalPiece":true},{"X":0,"Y":1,"PieceType":0,"IsSolved":false,"IsHorizontalPiece":true}],"NumberOfMoves":16},{"Pieces":[{"X":0,"Y":2,"PieceType":4,"IsSolved":false,"IsHorizontalPiece":true},{"X":5,"Y":2,"PieceType":3,"IsSolved":false,"IsHorizontalPiece":false},{"X":2,"Y":1,"PieceType":1,"IsSolved":false,"IsHorizontalPiece":true},{"X":1,"Y":0,"PieceType":0,"IsSolved":false,"IsHorizontalPiece":true},{"X":1,"Y":3,"PieceType":2,"IsSolved":false,"IsHorizontalPiece":false},{"X":1,"Y":5,"PieceType":1,"IsSolved":false,"IsHorizontalPiece":true},{"X":2,"Y":2,"PieceType":3,"IsSolved":false,"IsHorizontalPiece":false},{"X":0,"Y":0,"PieceType":2,"IsSolved":false,"IsHorizontalPiece":false},{"X":4,"Y":4,"PieceType":2,"IsSolved":false,"IsHorizontalPiece":false},{"X":4,"Y":2,"PieceType":2,"IsSolved":false,"IsHorizontalPiece":false},{"X":4,"Y":0,"PieceType":0,"IsSolved":false,"IsHorizontalPiece":true},{"X":0,"Y":4,"PieceType":2,"IsSolved":false,"IsHorizontalPiece":false}],"NumberOfMoves":15},{"Pieces":[{"X":0,"Y":2,"PieceType":4,"IsSolved":false,"IsHorizontalPiece":true},{"X":1,"Y":4,"PieceType":2,"IsSolved":false,"IsHorizontalPiece":false},{"X":5,"Y":4,"PieceType":2,"IsSolved":false,"IsHorizontalPiece":false},{"X":4,"Y":1,"PieceType":0,"IsSolved":false,"IsHorizontalPiece":true},{"X":0,"Y":0,"PieceType":2,"IsSolved":false,"IsHorizontalPiece":false},{"X":2,"Y":3,"PieceType":2,"IsSolved":false,"IsHorizontalPiece":false},{"X":2,"Y":1,"PieceType":2,"IsSolved":false,"IsHorizontalPiece":false},{"X":3,"Y":4,"PieceType":0,"IsSolved":false,"IsHorizontalPiece":true},{"X":0,"Y":3,"PieceType":0,"IsSolved":false,"IsHorizontalPiece":true},{"X":1,"Y":0,"PieceType":0,"IsSolved":false,"IsHorizontalPiece":true},{"X":4,"Y":2,"PieceType":2,"IsSolved":false,"IsHorizontalPiece":false},{"X":3,"Y":2,"PieceType":2,"IsSolved":false,"IsHorizontalPiece":false}],"NumberOfMoves":14},{"Pieces":[{"X":0,"Y":2,"PieceType":4,"IsSolved":false,"IsHorizontalPiece":true},{"X":4,"Y":1,"PieceType":2,"IsSolved":false,"IsHorizontalPiece":false},{"X":1,"Y":3,"PieceType":2,"IsSolved":false,"IsHorizontalPiece":false},{"X":3,"Y":1,"PieceType":2,"IsSolved":false,"IsHorizontalPiece":false},{"X":2,"Y":3,"PieceType":0,"IsSolved":false,"IsHorizontalPiece":true},{"X":5,"Y":2,"PieceType":3,"IsSolved":false,"IsHorizontalPiece":false},{"X":5,"Y":0,"PieceType":2,"IsSolved":false,"IsHorizontalPiece":false},{"X":2,"Y":0,"PieceType":1,"IsSolved":false,"IsHorizontalPiece":true},{"X":2,"Y":4,"PieceType":2,"IsSolved":false,"IsHorizontalPiece":false},{"X":2,"Y":1,"PieceType":2,"IsSolved":false,"IsHorizontalPiece":false},{"X":0,"Y":1,"PieceType":0,"IsSolved":false,"IsHorizontalPiece":true}],"NumberOfMoves":13},{"Pieces":[{"X":0,"Y":2,"PieceType":4,"IsSolved":false,"IsHorizontalPiece":true},{"X":0,"Y":4,"PieceType":2,"IsSolved":false,"IsHorizontalPiece":false},{"X":3,"Y":2,"PieceType":3,"IsSolved":false,"IsHorizontalPiece":false},{"X":2,"Y":1,"PieceType":3,"IsSolved":false,"IsHorizontalPiece":false},{"X":4,"Y":2,"PieceType":2,"IsSolved":false,"IsHorizontalPiece":false},{"X":0,"Y":3,"PieceType":0,"IsSolved":false,"IsHorizontalPiece":true},{"X":4,"Y":1,"PieceType":0,"IsSolved":false,"IsHorizontalPiece":true},{"X":3,"Y":0,"PieceType":0,"IsSolved":false,"IsHorizontalPiece":true},{"X":1,"Y":5,"PieceType":0,"IsSolved":false,"IsHorizontalPiece":true},{"X":4,"Y":5,"PieceType":0,"IsSolved":false,"IsHorizontalPiece":true},{"X":1,"Y":0,"PieceType":0,"IsSolved":false,"IsHorizontalPiece":true},{"X":1,"Y":4,"PieceType":0,"IsSolved":false,"IsHorizontalPiece":true}],"NumberOfMoves":18},{"Pieces":[{"X":0,"Y":2,"PieceType":4,"IsSolved":false,"IsHorizontalPiece":true},{"X":2,"Y":4,"PieceType":1,"IsSolved":false,"IsHorizontalPiece":true},{"X":5,"Y":1,"PieceType":2,"IsSolved":false,"IsHorizontalPiece":false},{"X":1,"Y":3,"PieceType":2,"IsSolved":false,"IsHorizontalPiece":false},{"X":2,"Y":3,"PieceType":1,"IsSolved":false,"IsHorizontalPiece":true},{"X":0,"Y":0,"PieceType":2,"IsSolved":false,"IsHorizontalPiece":false},{"X":0,"Y":5,"PieceType":0,"IsSolved":false,"IsHorizontalPiece":true},{"X":3,"Y":0,"PieceType":2,"IsSolved":false,"IsHorizontalPiece":false},{"X":2,"Y":1,"PieceType":2,"IsSolved":false,"IsHorizontalPiece":false},{"X":2,"Y":5,"PieceType":0,"IsSolved":false,"IsHorizontalPiece":true},{"X":5,"Y":3,"PieceType":3,"IsSolved":false,"IsHorizontalPiece":false},{"X":1,"Y":0,"PieceType":0,"IsSolved":false,"IsHorizontalPiece":true},{"X":4,"Y":1,"PieceType":2,"IsSolved":false,"IsHorizontalPiece":false}],"NumberOfMoves":13}] \ No newline at end of file diff --git a/Source/Meadow.Foundation.Libraries_and_Frameworks/Serialization.MicroJson/Tests/MicroJson.Unit.Tests/inputs/weather.json b/Source/Meadow.Foundation.Libraries_and_Frameworks/Serialization.MicroJson/Tests/MicroJson.Unit.Tests/inputs/weather.json new file mode 100644 index 0000000000..8d6cebfcd0 --- /dev/null +++ b/Source/Meadow.Foundation.Libraries_and_Frameworks/Serialization.MicroJson/Tests/MicroJson.Unit.Tests/inputs/weather.json @@ -0,0 +1,43 @@ +{ + "coord": { + "lon": -123.1193, + "lat": 49.2497 + }, + "weather": [ + { + "id": 803, + "main": "Clouds", + "description": "broken clouds", + "icon": "04d" + } + ], + "base": "stations", + "main": { + "temp": 279.01, + "feels_like": 276, + "temp_min": 277.58, + "temp_max": 279.94, + "pressure": 1028, + "humidity": 84 + }, + "visibility": 10000, + "wind": { + "speed": 4.12, + "deg": 90 + }, + "clouds": { + "all": 75 + }, + "dt": 1710347018, + "sys": { + "type": 2, + "id": 2011597, + "country": "CA", + "sunrise": 1710340135, + "sunset": 1710382486 + }, + "timezone": -25200, + "id": 6173331, + "name": "Vancouver", + "cod": 200 +} \ No newline at end of file diff --git a/Source/Meadow.Foundation.Libraries_and_Frameworks/Serialization.MicroJson/Tests/MicroJson.Unit.Tests/test types/TestTypes.cs b/Source/Meadow.Foundation.Libraries_and_Frameworks/Serialization.MicroJson/Tests/MicroJson.Unit.Tests/test types/TestTypes.cs new file mode 100644 index 0000000000..5d634ff8c7 --- /dev/null +++ b/Source/Meadow.Foundation.Libraries_and_Frameworks/Serialization.MicroJson/Tests/MicroJson.Unit.Tests/test types/TestTypes.cs @@ -0,0 +1,16 @@ +using Meadow.Foundation.Serialization; + +namespace Unit.Tests; + +internal class IntegerClass +{ + public int Value { get; set; } +} + +internal class IgnorableContainerClass +{ + public int ValueA { get; set; } + [JsonIgnore] + public string? ValueB { get; set; } + public bool ValueC { get; set; } +} diff --git a/Source/Meadow.Foundation.Libraries_and_Frameworks/Serialization.MicroJson/Tests/MicroJson.Unit.Tests/test types/WeatherReadingDTO.cs b/Source/Meadow.Foundation.Libraries_and_Frameworks/Serialization.MicroJson/Tests/MicroJson.Unit.Tests/test types/WeatherReadingDTO.cs new file mode 100644 index 0000000000..2fc07cb9f7 --- /dev/null +++ b/Source/Meadow.Foundation.Libraries_and_Frameworks/Serialization.MicroJson/Tests/MicroJson.Unit.Tests/test types/WeatherReadingDTO.cs @@ -0,0 +1,78 @@ +namespace Unit.Tests; + +public class WeatherReadingDTOCamelCase +{ + public Coordinates Coord { get; set; } + public Weather[] Weather { get; set; } + public WeatherValues Main { get; set; } + public int Visibility { get; set; } + public Wind Wind { get; set; } + public Clouds Clouds { get; set; } + public int Dt { get; set; } + public System Sys { get; set; } + public long Timezone { get; set; } + public int Id { get; set; } + public string Name { get; set; } + public int Cod { get; set; } +} + +public class WeatherReadingDTO +{ + public Coordinates coord { get; set; } + public Weather[] weather { get; set; } + public WeatherValues main { get; set; } + public int visibility { get; set; } + public Wind wind { get; set; } + public Clouds clouds { get; set; } + public int dt { get; set; } + public System sys { get; set; } + public long timezone { get; set; } + public int id { get; set; } + public string name { get; set; } + public int cod { get; set; } +} + +public class Coordinates +{ + public double lon { get; set; } + public double lat { get; set; } +} + +public class Weather +{ + public int id { get; set; } + public string nain { get; set; } + public string description { get; set; } + public string icon { get; set; } +} + +public class WeatherValues +{ + public double temp { get; set; } + public double feels_like { get; set; } + public double temp_min { get; set; } + public double temp_max { get; set; } + public int pressure { get; set; } + public int humidity { get; set; } +} + +public class Wind +{ + public decimal speed { get; set; } + public int deg { get; set; } + public double gust { get; set; } +} + +public class Clouds +{ + public int all { get; set; } +} + +public class System +{ + public int Type { get; set; } + public int Id { get; set; } + public string country { get; set; } + public long sunrise { get; set; } + public long sunset { get; set; } +} \ No newline at end of file diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Pcx857x/Driver/Drivers/Pca8574.cs b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Pcx857x/Driver/Drivers/Pca8574.cs index 0c024c7af4..839da99c55 100644 --- a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Pcx857x/Driver/Drivers/Pca8574.cs +++ b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Pcx857x/Driver/Drivers/Pca8574.cs @@ -26,5 +26,15 @@ public Pca8574(II2cBus i2cBus, byte address, IPin? interruptPin) public Pca8574(II2cBus i2cBus, byte address, IDigitalInterruptPort? interruptPort = default) : base(i2cBus, address, interruptPort) { } + + /// + /// Helper method to get address from address pin configuration + /// + /// State of A0 address pin - true if high + /// State of A1 address pin - true if high + /// State of A2 address pin - true if high + /// The device address + public static byte GetAddressForPins(bool pinA0, bool pinA1, bool pinA2) + => GetAddressFromPins(pinA0, pinA1, pinA2, false); } } \ No newline at end of file diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Pcx857x/Driver/Drivers/Pca8574a.cs b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Pcx857x/Driver/Drivers/Pca8574a.cs new file mode 100644 index 0000000000..7283be9057 --- /dev/null +++ b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Pcx857x/Driver/Drivers/Pca8574a.cs @@ -0,0 +1,40 @@ +using Meadow.Hardware; + +namespace Meadow.Foundation.ICs.IOExpanders +{ + /// + /// Represents the Pca8574a 8-bit I/O I2C expander + /// + public class Pca8574a : Pcx8574 + { + /// + /// Initializes a new instance of the Pca8574a device + /// + /// The I2C bus the peripheral is connected to + /// The I2C bus address of the peripheral + /// The interrupt pin + public Pca8574a(II2cBus i2cBus, byte address, IPin? interruptPin) + : base(i2cBus, address, interruptPin) + { } + + /// + /// Initializes a new instance of the Pca8574a device + /// + /// The I2C bus the peripheral is connected to + /// The I2C bus address of the peripheral + /// The interrupt port + public Pca8574a(II2cBus i2cBus, byte address, IDigitalInterruptPort? interruptPort = default) + : base(i2cBus, address, interruptPort) + { } + + /// + /// Helper method to get address from address pin configuration + /// + /// State of A0 address pin - true if high + /// State of A1 address pin - true if high + /// State of A2 address pin - true if high + /// The device address + public static byte GetAddressForPins(bool pinA0, bool pinA1, bool pinA2) + => GetAddressFromPins(pinA0, pinA1, pinA2, true); + } +} diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Pcx857x/Driver/Drivers/Pca8575.cs b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Pcx857x/Driver/Drivers/Pca8575.cs index c084cc7e85..e34a01a675 100644 --- a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Pcx857x/Driver/Drivers/Pca8575.cs +++ b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Pcx857x/Driver/Drivers/Pca8575.cs @@ -26,5 +26,15 @@ public Pca8575(II2cBus i2cBus, byte address, IPin? interruptPin) public Pca8575(II2cBus i2cBus, byte address, IDigitalInterruptPort? interruptPort = default) : base(i2cBus, address, interruptPort) { } + + /// + /// Helper method to get address from address pin configuration + /// + /// State of A0 address pin - true if high + /// State of A1 address pin - true if high + /// State of A2 address pin - true if high + /// The device address + public static byte GetAddressForPins(bool pinA0, bool pinA1, bool pinA2) + => GetAddressFromPins(pinA0, pinA1, pinA2, false); } } \ No newline at end of file diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Pcx857x/Driver/Drivers/Pcf8574.cs b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Pcx857x/Driver/Drivers/Pcf8574.cs index 756beb84aa..5c9332cd92 100644 --- a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Pcx857x/Driver/Drivers/Pcf8574.cs +++ b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Pcx857x/Driver/Drivers/Pcf8574.cs @@ -26,5 +26,15 @@ public Pcf8574(II2cBus i2cBus, byte address, IPin? interruptPin) public Pcf8574(II2cBus i2cBus, byte address, IDigitalInterruptPort? interruptPort = default) : base(i2cBus, address, interruptPort) { } + + /// + /// Helper method to get address from address pin configuration + /// + /// State of A0 address pin - true if high + /// State of A1 address pin - true if high + /// State of A2 address pin - true if high + /// The device address + public static byte GetAddressForPins(bool pinA0, bool pinA1, bool pinA2) + => GetAddressFromPins(pinA0, pinA1, pinA2, false); } } \ No newline at end of file diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Pcx857x/Driver/Drivers/Pcf8574a.cs b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Pcx857x/Driver/Drivers/Pcf8574a.cs new file mode 100644 index 0000000000..a34a1bbe7e --- /dev/null +++ b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Pcx857x/Driver/Drivers/Pcf8574a.cs @@ -0,0 +1,40 @@ +using Meadow.Hardware; + +namespace Meadow.Foundation.ICs.IOExpanders +{ + /// + /// Represents the Pcf8574a 8-bit I/O I2C expander + /// + public class Pcf8574a : Pcx8574 + { + /// + /// Initializes a new instance of the Pcf8574a device + /// + /// The I2C bus the peripheral is connected to + /// The I2C bus address of the peripheral + /// The interrupt pin + public Pcf8574a(II2cBus i2cBus, byte address, IPin? interruptPin) + : base(i2cBus, address, interruptPin) + { } + + /// + /// Initializes a new instance of the Pcf8574a device + /// + /// The I2C bus the peripheral is connected to + /// The I2C bus address of the peripheral + /// The interrupt port + public Pcf8574a(II2cBus i2cBus, byte address, IDigitalInterruptPort? interruptPort = default) + : base(i2cBus, address, interruptPort) + { } + + /// + /// Helper method to get address from address pin configuration + /// + /// State of A0 address pin - true if high + /// State of A1 address pin - true if high + /// State of A2 address pin - true if high + /// The device address + public static byte GetAddressForPins(bool pinA0, bool pinA1, bool pinA2) + => GetAddressFromPins(pinA0, pinA1, pinA2, true); + } +} \ No newline at end of file diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Pcx857x/Driver/Drivers/Pcf8575.cs b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Pcx857x/Driver/Drivers/Pcf8575.cs index f5e921841a..30112d90a5 100644 --- a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Pcx857x/Driver/Drivers/Pcf8575.cs +++ b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Pcx857x/Driver/Drivers/Pcf8575.cs @@ -26,5 +26,15 @@ public Pcf8575(II2cBus i2cBus, byte address, IPin? interruptPin) public Pcf8575(II2cBus i2cBus, byte address, IDigitalInterruptPort? interruptPort = default) : base(i2cBus, address, interruptPort) { } + + /// + /// Helper method to get address from address pin configuration + /// + /// State of A0 address pin - true if high + /// State of A1 address pin - true if high + /// State of A2 address pin - true if high + /// The device address + public static byte GetAddressForPins(bool pinA0, bool pinA1, bool pinA2) + => GetAddressFromPins(pinA0, pinA1, pinA2, false); } } \ No newline at end of file diff --git a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Pcx857x/Driver/Pcx857x.AddressTable.cs b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Pcx857x/Driver/Pcx857x.AddressTable.cs index fa03e3818f..2d80b6a562 100644 --- a/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Pcx857x/Driver/Pcx857x.AddressTable.cs +++ b/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Pcx857x/Driver/Pcx857x.AddressTable.cs @@ -10,7 +10,7 @@ public partial class Pcx857x /// State of A2 address pin - true if high /// Is an A hardware variant, this shifts the address returned by 24 /// The device address - public static byte GetAddressFromPins(bool pinA0, bool pinA1, bool pinA2, bool isATypeDevice = false) + internal static byte GetAddressFromPins(bool pinA0, bool pinA1, bool pinA2, bool isATypeDevice) { /* A2 A1 A0 HexAddr. Dec.Addr. diff --git a/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bme280/Driver/Bme280.Address.cs b/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bme280/Driver/Bme280.Address.cs deleted file mode 100644 index d252eafac3..0000000000 --- a/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bme280/Driver/Bme280.Address.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace Meadow.Foundation.Sensors.Atmospheric -{ - public partial class Bme280 - { - /// - /// Valid I2C addresses for the sensor - /// - public enum Addresses : byte - { - /// - /// Bus address 0x76 - /// - Address_0x76 = 0x76, - /// - /// Bus address 0x77 - /// - Address_0x77 = 0x77, - /// - /// Default bus address - /// - Default = Address_0x76 - } - } -} \ No newline at end of file diff --git a/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bme280/Driver/Bme280.ChipType.cs b/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bme280/Driver/Bme280.ChipType.cs deleted file mode 100644 index 2bda5db263..0000000000 --- a/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bme280/Driver/Bme280.ChipType.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace Meadow.Foundation.Sensors.Atmospheric -{ - public partial class Bme280 - { - /// - /// BMx280 type to support both the BME280 and the BMP280 - /// - public enum ChipType : byte - { - /// - /// BMP280 - /// - BMP = 0x58, - /// - /// BME280 - /// - BME = 0x60 - } - } -} \ No newline at end of file diff --git a/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bme280/Driver/Bme280.CompensationData.cs b/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bme280/Driver/Bme280.CompensationData.cs deleted file mode 100644 index 755d2a615b..0000000000 --- a/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bme280/Driver/Bme280.CompensationData.cs +++ /dev/null @@ -1,30 +0,0 @@ -namespace Meadow.Foundation.Sensors.Atmospheric -{ - public partial class Bme280 - { - /// - /// Compensation data. - /// - struct CompensationData - { - public ushort T1; - public short T2; - public short T3; - public ushort P1; - public short P2; - public short P3; - public short P4; - public short P5; - public short P6; - public short P7; - public short P8; - public short P9; - public byte H1; - public short H2; - public byte H3; - public short H4; - public short H5; - public sbyte H6; - } - } -} diff --git a/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bme280/Driver/Bme280.Configuration.cs b/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bme280/Driver/Bme280.Configuration.cs deleted file mode 100644 index 1f8f933a9f..0000000000 --- a/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bme280/Driver/Bme280.Configuration.cs +++ /dev/null @@ -1,44 +0,0 @@ -namespace Meadow.Foundation.Sensors.Atmospheric -{ - public partial class Bme280 - { - /// - /// BME280 configuration class - /// - public class Configuration - { - /// - /// Temperature over sampling configuration - /// - public Oversample TemperatureOverSampling { get; set; } - - /// - /// Pressure over sampling configuration - /// - public Oversample PressureOversampling { get; set; } - - /// - /// Humidity over sampling configuration - /// - public Oversample HumidityOverSampling { get; set; } - - /// - /// Set the operating mode for the sensor - /// - public Modes Mode { get; set; } - - /// - /// Set the standby period for the sensor - /// - public StandbyDuration Standby { get; set; } - - /// - /// Determine the time constant for the IIR filter - /// - /// - /// See section 3.44 of the datasheet for more information - /// - public FilterCoefficient Filter { get; set; } - } - } -} \ No newline at end of file diff --git a/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bme280/Driver/Bme280.FilterCoefficient.cs b/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bme280/Driver/Bme280.FilterCoefficient.cs deleted file mode 100644 index 77e6cf9fc8..0000000000 --- a/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bme280/Driver/Bme280.FilterCoefficient.cs +++ /dev/null @@ -1,32 +0,0 @@ -namespace Meadow.Foundation.Sensors.Atmospheric -{ - public partial class Bme280 - { - /// - /// Valid filter co-efficient values - /// - public enum FilterCoefficient : byte - { - /// - /// Off - /// - Off = 0, - /// - /// 2x - /// - Two, - /// - /// 4c - /// - Four, - /// - /// 8x - /// - Eight, - /// - /// 16x - /// - Sixteen - } - } -} \ No newline at end of file diff --git a/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bme280/Driver/Bme280.Modes.cs b/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bme280/Driver/Bme280.Modes.cs deleted file mode 100644 index c881d4c5ab..0000000000 --- a/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bme280/Driver/Bme280.Modes.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace Meadow.Foundation.Sensors.Atmospheric -{ - public partial class Bme280 - { - /// - /// Valid values for the operating mode of the sensor. - /// - public enum Modes : byte - { - /// - /// no operation, all registers accessible, lowest power, selected after startup - /// - Sleep = 0, - /// - /// perform one measurement, store results and return to sleep mode - /// - Forced = 1, - /// - /// perpetual cycling of measurements and inactive periods. - /// - Normal = 3 - } - } -} diff --git a/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bme280/Driver/Bme280.Oversample.cs b/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bme280/Driver/Bme280.Oversample.cs deleted file mode 100644 index b2130f8af7..0000000000 --- a/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bme280/Driver/Bme280.Oversample.cs +++ /dev/null @@ -1,44 +0,0 @@ -namespace Meadow.Foundation.Sensors.Atmospheric -{ - public partial class Bme280 - { - /// - /// Valid oversampling values. - /// - /// - /// 000 - Data output set to 0x8000 - /// 001 - Oversampling x1 - /// 010 - Oversampling x2 - /// 011 - Oversampling x4 - /// 100 - Oversampling x8 - /// 101, 110, 111 - Oversampling x16 - /// - public enum Oversample : byte - { - /// - /// No sampling - /// - Skip = 0, - /// - /// 1x oversampling - /// - OversampleX1, - /// - /// 2x oversampling - /// - OversampleX2, - /// - /// 4x oversampling - /// - OversampleX4, - /// - /// 8x oversampling - /// - OversampleX8, - /// - /// 16x oversampling - /// - OversampleX16 - } - } -} diff --git a/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bme280/Driver/Bme280.Register.cs b/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bme280/Driver/Bme280.Register.cs deleted file mode 100644 index d490a4b996..0000000000 --- a/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bme280/Driver/Bme280.Register.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Meadow.Foundation.Sensors.Atmospheric -{ - public partial class Bme280 - { - internal enum Register : byte - { - ChipID = 0xd0, - Reset = 0xe0, - Humidity = 0xf2, - Status = 0xf3, - Measurement = 0xf4, - Configuration = 0xf5, - } - } -} \ No newline at end of file diff --git a/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bme280/Driver/Bme280.StandbyDuration.cs b/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bme280/Driver/Bme280.StandbyDuration.cs deleted file mode 100644 index ed76af6370..0000000000 --- a/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bme280/Driver/Bme280.StandbyDuration.cs +++ /dev/null @@ -1,44 +0,0 @@ -namespace Meadow.Foundation.Sensors.Atmospheric -{ - public partial class Bme280 - { - /// - /// Valid values for the inactive duration in normal mode. - /// - public enum StandbyDuration : byte - { - /// - /// 0.5 milliseconds - /// - MsHalf = 0, - /// - /// 62.5 milliseconds - /// - Ms62Half, - /// - /// 125 milliseconds - /// - Ms125, - /// - /// 250 milliseconds - /// - Ms250, - /// - /// 500 milliseconds - /// - Ms500, - /// - /// 1000 milliseconds - /// - Ms1000, - /// - /// 10 milliseconds - /// - Ms10, - /// - /// 20 milliseconds - /// - Ms20 - } - } -} diff --git a/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bme280/Datasheet/BST-BME280_DS001-10.pdf b/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bmx280/Datasheet/BST-BME280_DS001-10.pdf similarity index 100% rename from Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bme280/Datasheet/BST-BME280_DS001-10.pdf rename to Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bmx280/Datasheet/BST-BME280_DS001-10.pdf diff --git a/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bmx280/Datasheet/bst-bmp280-ds001.pdf b/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bmx280/Datasheet/bst-bmp280-ds001.pdf new file mode 100644 index 0000000000..a784d91908 Binary files /dev/null and b/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bmx280/Datasheet/bst-bmp280-ds001.pdf differ diff --git a/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bmx280/Driver/Bmx280.Enums.cs b/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bmx280/Driver/Bmx280.Enums.cs new file mode 100644 index 0000000000..75a1978836 --- /dev/null +++ b/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bmx280/Driver/Bmx280.Enums.cs @@ -0,0 +1,173 @@ +namespace Meadow.Foundation.Sensors.Atmospheric +{ + public abstract partial class Bmx280 + { + /// + /// Valid I2C addresses for the sensor + /// + public enum Addresses : byte + { + /// + /// Bus address 0x76 + /// + Address_0x76 = 0x76, + /// + /// Bus address 0x77 + /// + Address_0x77 = 0x77, + /// + /// Default bus address + /// + Default = Address_0x76 + } + + /// + /// BMx280 type to support both the BME280 and the BMP280 + /// + public enum ChipType : byte + { + /// + /// BMP280 + /// + BMP = 0x58, + /// + /// BME280 + /// + BME = 0x60 + } + + /// + /// Valid filter co-efficient values + /// + public enum FilterCoefficient : byte + { + /// + /// Off + /// + Off = 0, + /// + /// 2x + /// + Two, + /// + /// 4c + /// + Four, + /// + /// 8x + /// + Eight, + /// + /// 16x + /// + Sixteen + } + + /// + /// Valid values for the inactive duration in normal mode. + /// + public enum StandbyDuration : byte + { + /// + /// 0.5 milliseconds + /// + MsHalf = 0, + /// + /// 62.5 milliseconds + /// + Ms62Half, + /// + /// 125 milliseconds + /// + Ms125, + /// + /// 250 milliseconds + /// + Ms250, + /// + /// 500 milliseconds + /// + Ms500, + /// + /// 1000 milliseconds + /// + Ms1000, + /// + /// 10 milliseconds + /// + Ms10, + /// + /// 20 milliseconds + /// + Ms20 + } + + /// + /// Valid values for the operating mode of the sensor. + /// + public enum Modes : byte + { + /// + /// no operation, all registers accessible, lowest power, selected after startup + /// + Sleep = 0, + /// + /// perform one measurement, store results and return to sleep mode + /// + Forced = 1, + /// + /// perpetual cycling of measurements and inactive periods. + /// + Normal = 3 + } + + /// + /// Valid oversampling values. + /// + /// + /// 000 - Data output set to 0x8000 + /// 001 - Oversampling x1 + /// 010 - Oversampling x2 + /// 011 - Oversampling x4 + /// 100 - Oversampling x8 + /// 101, 110, 111 - Oversampling x16 + /// + public enum Oversample : byte + { + /// + /// No sampling + /// + Skip = 0, + /// + /// 1x oversampling + /// + OversampleX1, + /// + /// 2x oversampling + /// + OversampleX2, + /// + /// 4x oversampling + /// + OversampleX4, + /// + /// 8x oversampling + /// + OversampleX8, + /// + /// 16x oversampling + /// + OversampleX16 + } + + internal enum Register : byte + { + ChipID = 0xd0, + Reset = 0xe0, + Humidity = 0xf2, + Status = 0xf3, + Measurement = 0xf4, + Configuration = 0xf5, + } + } +} \ No newline at end of file diff --git a/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bmx280/Driver/Bmx280.cs b/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bmx280/Driver/Bmx280.cs new file mode 100644 index 0000000000..24d7faaff8 --- /dev/null +++ b/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bmx280/Driver/Bmx280.cs @@ -0,0 +1,106 @@ +using Meadow.Hardware; +using Meadow.Units; +using System; +using System.Threading.Tasks; +using HU = Meadow.Units.RelativeHumidity.UnitType; +using PU = Meadow.Units.Pressure.UnitType; +using TU = Meadow.Units.Temperature.UnitType; + +namespace Meadow.Foundation.Sensors.Atmospheric; + +/// +/// Shared logic for the BMx280 family of sensors +/// +partial class Bmx280 +{ + /// + /// Update the sensor information from the BMx280 + /// + internal static Task<(Units.Temperature? Temperature, RelativeHumidity? Humidity, Pressure? Pressure)> + ReadSensor(IByteCommunications bmx280Comms, Memory readBuffer, CompensationData compensationData) + { + (Units.Temperature? Temperature, RelativeHumidity? Humidity, Pressure? Pressure) conditions; + + bmx280Comms.ReadRegister(0xf7, readBuffer.Span[0..8]); + + var adcTemperature = (readBuffer.Span[3] << 12) | (readBuffer.Span[4] << 4) | ((readBuffer.Span[5] >> 4) & 0x0f); + var tvar1 = (((adcTemperature >> 3) - (compensationData.T1 << 1)) * compensationData.T2) >> 11; + var tvar2 = (((((adcTemperature >> 4) - compensationData.T1) * + ((adcTemperature >> 4) - compensationData.T1)) >> 12) * compensationData.T3) >> 14; + var tfine = tvar1 + tvar2; + + conditions.Temperature = new Units.Temperature((float)(((tfine * 5) + 128) >> 8) / 100, TU.Celsius); + + long pvar1 = tfine - 128000; + var pvar2 = pvar1 * pvar1 * compensationData.P6; + pvar2 += (pvar1 * compensationData.P5) << 17; + pvar2 += (long)compensationData.P4 << 35; + pvar1 = ((pvar1 * pvar1 * compensationData.P8) >> 8) + ((pvar1 * compensationData.P2) << 12); + pvar1 = ((((long)1 << 47) + pvar1) * compensationData.P1) >> 33; + if (pvar1 == 0) + { + conditions.Pressure = new Pressure(0, PU.Pascal); + } + else + { + var adcPressure = (readBuffer.Span[0] << 12) | (readBuffer.Span[1] << 4) | ((readBuffer.Span[2] >> 4) & 0x0f); + long pressure = 1048576 - adcPressure; + pressure = (((pressure << 31) - pvar2) * 3125) / pvar1; + pvar1 = (compensationData.P9 * (pressure >> 13) * (pressure >> 13)) >> 25; + pvar2 = (compensationData.P8 * pressure) >> 19; + pressure = ((pressure + pvar1 + pvar2) >> 8) + ((long)compensationData.P7 << 4); + conditions.Pressure = new Pressure((double)pressure / 256, PU.Pascal); + } + + var adcHumidity = (readBuffer.Span[6] << 8) | readBuffer.Span[7]; + var v_x1_u32r = tfine - 76800; + + v_x1_u32r = ((((adcHumidity << 14) - (compensationData.H4 << 20) - (compensationData.H5 * v_x1_u32r)) + + 16384) >> 15) * + ((((((((v_x1_u32r * compensationData.H6) >> 10) * + (((v_x1_u32r * compensationData.H3) >> 11) + 32768)) >> 10) + 2097152) * + compensationData.H2) + 8192) >> 14); + v_x1_u32r = v_x1_u32r - (((((v_x1_u32r >> 15) * (v_x1_u32r >> 15)) >> 7) * compensationData.H1) >> 4); + + v_x1_u32r = v_x1_u32r < 0 ? 0 : v_x1_u32r; + v_x1_u32r = v_x1_u32r > 419430400 ? 419430400 : v_x1_u32r; + + conditions.Humidity = new RelativeHumidity((v_x1_u32r >> 12) / 1024, HU.Percent); + + return Task.FromResult(conditions); + } + + /// + /// Reads the sensor compensation data + /// + internal static void ReadCompensationData(IByteCommunications bmx280Comms, Memory readBuffer, CompensationData compensationData) + { + // read the temperature and pressure data into the internal read buffer + bmx280Comms.ReadRegister(0x88, readBuffer.Span[0..24]); + + // Temperature + compensationData.T1 = (ushort)(readBuffer.Span[0] + (readBuffer.Span[1] << 8)); + compensationData.T2 = (short)(readBuffer.Span[2] + (readBuffer.Span[3] << 8)); + compensationData.T3 = (short)(readBuffer.Span[4] + (readBuffer.Span[5] << 8)); + // Pressure + compensationData.P1 = (ushort)(readBuffer.Span[6] + (readBuffer.Span[7] << 8)); + compensationData.P2 = (short)(readBuffer.Span[8] + (readBuffer.Span[9] << 8)); + compensationData.P3 = (short)(readBuffer.Span[10] + (readBuffer.Span[11] << 8)); + compensationData.P4 = (short)(readBuffer.Span[12] + (readBuffer.Span[13] << 8)); + compensationData.P5 = (short)(readBuffer.Span[14] + (readBuffer.Span[15] << 8)); + compensationData.P6 = (short)(readBuffer.Span[16] + (readBuffer.Span[17] << 8)); + compensationData.P7 = (short)(readBuffer.Span[18] + (readBuffer.Span[19] << 8)); + compensationData.P8 = (short)(readBuffer.Span[20] + (readBuffer.Span[21] << 8)); + compensationData.P9 = (short)(readBuffer.Span[22] + (readBuffer.Span[23] << 8)); + + // Humidity - read twice because it's in non-sequential registers + bmx280Comms.ReadRegister(0xa1, readBuffer.Span[0..1]); + compensationData.H1 = readBuffer.Span[0]; + bmx280Comms.ReadRegister(0xe1, readBuffer.Span[0..7]); + compensationData.H2 = (short)(readBuffer.Span[0] + (readBuffer.Span[1] << 8)); + compensationData.H3 = readBuffer.Span[2]; + compensationData.H4 = (short)((readBuffer.Span[3] << 4) + (readBuffer.Span[4] & 0xf)); + compensationData.H5 = (short)(((readBuffer.Span[4] & 0xf) >> 4) + (readBuffer.Span[5] << 4)); + compensationData.H6 = (sbyte)readBuffer.Span[6]; + } +} \ No newline at end of file diff --git a/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bmx280/Driver/CompensationData.cs b/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bmx280/Driver/CompensationData.cs new file mode 100644 index 0000000000..90a4963fd2 --- /dev/null +++ b/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bmx280/Driver/CompensationData.cs @@ -0,0 +1,23 @@ +namespace Meadow.Foundation.Sensors.Atmospheric; + +internal struct CompensationData +{ + public ushort T1; + public short T2; + public short T3; + public ushort P1; + public short P2; + public short P3; + public short P4; + public short P5; + public short P6; + public short P7; + public short P8; + public short P9; + public byte H1; + public short H2; + public byte H3; + public short H4; + public short H5; + public sbyte H6; +} \ No newline at end of file diff --git a/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bmx280/Driver/Configuration.cs b/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bmx280/Driver/Configuration.cs new file mode 100644 index 0000000000..6d8a60136e --- /dev/null +++ b/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bmx280/Driver/Configuration.cs @@ -0,0 +1,42 @@ +using static Meadow.Foundation.Sensors.Atmospheric.Bmx280; + +namespace Meadow.Foundation.Sensors.Atmospheric; + +/// +/// BME280 configuration class +/// +internal class Configuration +{ + /// + /// Temperature over sampling configuration + /// + public Oversample TemperatureOverSampling { get; set; } + + /// + /// Pressure over sampling configuration + /// + public Oversample PressureOversampling { get; set; } + + /// + /// Humidity over sampling configuration + /// + public Oversample HumidityOverSampling { get; set; } + + /// + /// Set the operating mode for the sensor + /// + public Modes Mode { get; set; } + + /// + /// Set the standby period for the sensor + /// + public StandbyDuration Standby { get; set; } + + /// + /// Determine the time constant for the IIR filter + /// + /// + /// See section 3.44 of the datasheet for more information + /// + public FilterCoefficient Filter { get; set; } +} \ No newline at end of file diff --git a/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bme280/Driver/Bme280.cs b/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bmx280/Driver/Drivers/Bme280.cs similarity index 64% rename from Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bme280/Driver/Bme280.cs rename to Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bmx280/Driver/Drivers/Bme280.cs index 702a5db41f..a04e719f9c 100644 --- a/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bme280/Driver/Bme280.cs +++ b/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bmx280/Driver/Drivers/Bme280.cs @@ -4,9 +4,7 @@ using Meadow.Units; using System; using System.Threading.Tasks; -using HU = Meadow.Units.RelativeHumidity.UnitType; -using PU = Meadow.Units.Pressure.UnitType; -using TU = Meadow.Units.Temperature.UnitType; +using static Meadow.Foundation.Sensors.Atmospheric.Bmx280; namespace Meadow.Foundation.Sensors.Atmospheric; @@ -91,7 +89,7 @@ event EventHandler> ISamplingSensor.Updated /// /// Sensor configuration /// - protected Configuration configuration; + private readonly Configuration configuration; /// /// The temperature from the last reading @@ -99,20 +97,19 @@ event EventHandler> ISamplingSensor.Updated public Units.Temperature? Temperature => Conditions.Temperature; /// - /// The pressure, in hectopascals (hPa), from the last reading. 1 hPa - /// is equal to one millibar, or 1/10th of a kilopascal (kPa)/centibar. + /// The pressure from the last reading /// public Pressure? Pressure => Conditions.Pressure; /// - /// The humidity, in percent relative humidity, from the last reading.. + /// The realtive humidity from the last reading /// public RelativeHumidity? Humidity => Conditions.Humidity; /// /// The default SPI bus speed for the device /// - public Frequency DefaultSpiBusSpeed => new Frequency(10000, Frequency.UnitType.Kilohertz); + public Frequency DefaultSpiBusSpeed => new(10000, Frequency.UnitType.Kilohertz); /// /// The SPI bus speed for the device @@ -142,7 +139,7 @@ public SpiClockConfiguration.Mode SpiBusMode /// public byte DefaultI2cAddress => (byte)Addresses.Default; - private IDigitalOutputPort? chipSelectPort; + private readonly IDigitalOutputPort? chipSelectPort; /// /// Initializes a new instance of the BME280 class @@ -184,7 +181,7 @@ public Bme280(ISpiBus spiBus, IDigitalOutputPort chipSelectPort) /// protected void Initialize() { - ReadCompensationData(); + Bmx280.ReadCompensationData(bme280Comms, readBuffer, compensationData); configuration.Mode = Modes.Sleep; configuration.Filter = FilterCoefficient.Off; @@ -227,7 +224,7 @@ protected override void RaiseEventsAndNotify(IChangeResult<(Units.Temperature? T /// protected override async Task<(Units.Temperature? Temperature, RelativeHumidity? Humidity, Pressure? Pressure)> ReadSensor() { - //TODO: set an update flag on the oversample properties and set + // TODO: set an update flag on the oversample properties and set // these once, unless the update flag has been set. configuration.TemperatureOverSampling = TemperatureSampleCount; configuration.PressureOversampling = PressureSampleCount; @@ -242,62 +239,15 @@ protected override void RaiseEventsAndNotify(IChangeResult<(Units.Temperature? T await Task.Delay(100); //give the BME280 time to read new values } - (Units.Temperature Temperature, RelativeHumidity Humidity, Pressure Pressure) conditions; - - bme280Comms.ReadRegister(0xf7, readBuffer.Span[0..8]); - - var adcTemperature = (readBuffer.Span[3] << 12) | (readBuffer.Span[4] << 4) | ((readBuffer.Span[5] >> 4) & 0x0f); - var tvar1 = (((adcTemperature >> 3) - (compensationData.T1 << 1)) * compensationData.T2) >> 11; - var tvar2 = (((((adcTemperature >> 4) - compensationData.T1) * - ((adcTemperature >> 4) - compensationData.T1)) >> 12) * compensationData.T3) >> 14; - var tfine = tvar1 + tvar2; - - conditions.Temperature = new Units.Temperature((float)(((tfine * 5) + 128) >> 8) / 100, TU.Celsius); - - long pvar1 = tfine - 128000; - var pvar2 = pvar1 * pvar1 * compensationData.P6; - pvar2 += (pvar1 * compensationData.P5) << 17; - pvar2 += (long)compensationData.P4 << 35; - pvar1 = ((pvar1 * pvar1 * compensationData.P8) >> 8) + ((pvar1 * compensationData.P2) << 12); - pvar1 = ((((long)1 << 47) + pvar1) * compensationData.P1) >> 33; - if (pvar1 == 0) - { - conditions.Pressure = new Pressure(0, PU.Pascal); - } - else - { - var adcPressure = (readBuffer.Span[0] << 12) | (readBuffer.Span[1] << 4) | ((readBuffer.Span[2] >> 4) & 0x0f); - long pressure = 1048576 - adcPressure; - pressure = (((pressure << 31) - pvar2) * 3125) / pvar1; - pvar1 = (compensationData.P9 * (pressure >> 13) * (pressure >> 13)) >> 25; - pvar2 = (compensationData.P8 * pressure) >> 19; - pressure = ((pressure + pvar1 + pvar2) >> 8) + ((long)compensationData.P7 << 4); - conditions.Pressure = new Pressure((double)pressure / 256, PU.Pascal); - } - - var adcHumidity = (readBuffer.Span[6] << 8) | readBuffer.Span[7]; - var v_x1_u32r = tfine - 76800; - - v_x1_u32r = ((((adcHumidity << 14) - (compensationData.H4 << 20) - (compensationData.H5 * v_x1_u32r)) + - 16384) >> 15) * - ((((((((v_x1_u32r * compensationData.H6) >> 10) * - (((v_x1_u32r * compensationData.H3) >> 11) + 32768)) >> 10) + 2097152) * - compensationData.H2) + 8192) >> 14); - v_x1_u32r = v_x1_u32r - (((((v_x1_u32r >> 15) * (v_x1_u32r >> 15)) >> 7) * compensationData.H1) >> 4); - - v_x1_u32r = v_x1_u32r < 0 ? 0 : v_x1_u32r; - v_x1_u32r = v_x1_u32r > 419430400 ? 419430400 : v_x1_u32r; - - conditions.Humidity = new RelativeHumidity((v_x1_u32r >> 12) / 1024, HU.Percent); - - return conditions; + return await Bmx280.ReadSensor(bme280Comms, readBuffer, compensationData); } + /// /// Update the configuration for the BME280. /// /// /// This method uses the data in the configuration properties in order to set up the - /// BME280. Ensure that the following are set correctly before calling this method: + /// BME280. Ensure that the following are set correctly before calling this method: /// - Standby /// - Filter /// - HumidityOverSampling @@ -305,11 +255,9 @@ protected override void RaiseEventsAndNotify(IChangeResult<(Units.Temperature? T /// - PressureOverSampling /// - Mode /// - protected void UpdateConfiguration(Configuration configuration) + private void UpdateConfiguration(Configuration configuration) { - // // Put to sleep to allow the configuration to be changed. - // bme280Comms.WriteRegister((byte)Register.Measurement, 0x00); var data = (byte)((((byte)configuration.Standby << 5) & 0xe0) | (((byte)configuration.Filter << 2) & 0x1c)); @@ -334,57 +282,10 @@ public void Reset() UpdateConfiguration(configuration); } - /// - /// Reads the compensation data. - /// - /// - /// The compensation data is written to the chip at the time of manufacture and cannot be changed. - /// This information is used to convert the readings from the sensor into actual temperature, - /// pressure and humidity readings. - /// From the data sheet, the register addresses and length are: - /// Temperature and pressure: start address 0x88, end address 0x9F (length = 24) - /// Humidity 1: 0xa1, length = 1 - /// Humidity 2 and 3: start address 0xe1, end address 0xe7, (length = 8) - /// - protected void ReadCompensationData() - { - // read the temperature and pressure data into the internal read buffer - bme280Comms.ReadRegister(0x88, readBuffer.Span[0..24]); - - // Temperature - compensationData.T1 = (ushort)(readBuffer.Span[0] + (readBuffer.Span[1] << 8)); - compensationData.T2 = (short)(readBuffer.Span[2] + (readBuffer.Span[3] << 8)); - compensationData.T3 = (short)(readBuffer.Span[4] + (readBuffer.Span[5] << 8)); - // Pressure - compensationData.P1 = (ushort)(readBuffer.Span[6] + (readBuffer.Span[7] << 8)); - compensationData.P2 = (short)(readBuffer.Span[8] + (readBuffer.Span[9] << 8)); - compensationData.P3 = (short)(readBuffer.Span[10] + (readBuffer.Span[11] << 8)); - compensationData.P4 = (short)(readBuffer.Span[12] + (readBuffer.Span[13] << 8)); - compensationData.P5 = (short)(readBuffer.Span[14] + (readBuffer.Span[15] << 8)); - compensationData.P6 = (short)(readBuffer.Span[16] + (readBuffer.Span[17] << 8)); - compensationData.P7 = (short)(readBuffer.Span[18] + (readBuffer.Span[19] << 8)); - compensationData.P8 = (short)(readBuffer.Span[20] + (readBuffer.Span[21] << 8)); - compensationData.P9 = (short)(readBuffer.Span[22] + (readBuffer.Span[23] << 8)); - - // read the humidity data. have to read twice because they're in different, - // non-sequential registers - - // first one - bme280Comms.ReadRegister(0xa1, readBuffer.Span[0..1]); - compensationData.H1 = readBuffer.Span[0]; - // 2-6 - bme280Comms.ReadRegister(0xe1, readBuffer.Span[0..7]); - compensationData.H2 = (short)(readBuffer.Span[0] + (readBuffer.Span[1] << 8)); - compensationData.H3 = readBuffer.Span[2]; - compensationData.H4 = (short)((readBuffer.Span[3] << 4) + (readBuffer.Span[4] & 0xf)); - compensationData.H5 = (short)(((readBuffer.Span[4] & 0xf) >> 4) + (readBuffer.Span[5] << 4)); - compensationData.H6 = (sbyte)readBuffer.Span[6]; - } - /// /// Get the chip ID /// - /// + /// The ID as a byte public byte GetChipID() { bme280Comms.ReadRegister((byte)Register.ChipID, readBuffer.Span[0..1]); diff --git a/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bmx280/Driver/Drivers/Bmp280.cs b/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bmx280/Driver/Drivers/Bmp280.cs new file mode 100644 index 0000000000..0db243bfb6 --- /dev/null +++ b/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bmx280/Driver/Drivers/Bmp280.cs @@ -0,0 +1,315 @@ +using Meadow.Hardware; +using Meadow.Peripherals.Sensors; +using Meadow.Peripherals.Sensors.Atmospheric; +using Meadow.Units; +using System; +using System.Threading.Tasks; +using static Meadow.Foundation.Sensors.Atmospheric.Bmx280; + +namespace Meadow.Foundation.Sensors.Atmospheric; + +/// +/// BMP280 Temperature and Pressure Sensor +/// +/// +/// This class implements the functionality necessary to read the temperature and pressure +/// from the Bosch BMP280 sensor +/// +public partial class Bmp280 : + PollingSensorBase<(Units.Temperature? Temperature, Pressure? Pressure)>, + ITemperatureSensor, IBarometricPressureSensor, ISpiPeripheral, II2cPeripheral, IDisposable +{ + private event EventHandler> _temperatureHandlers = default!; + private event EventHandler> _pressureHandlers = default!; + + event EventHandler> ISamplingSensor.Updated + { + add => _temperatureHandlers += value; + remove => _temperatureHandlers -= value; + } + + event EventHandler> ISamplingSensor.Updated + { + add => _pressureHandlers += value; + remove => _pressureHandlers -= value; + } + + /// + /// Is the object disposed + /// + public bool IsDisposed { get; private set; } + + /// + /// Did we create the port(s) used by the peripheral + /// + private readonly bool createdPort = false; + + /// + /// The read buffer + /// + protected Memory readBuffer = new byte[32]; + + /// + /// The write buffer + /// + protected Memory writeBuffer = new byte[32]; + + /// + /// Temperature oversample count + /// + public Oversample TemperatureSampleCount { get; set; } = Oversample.OversampleX8; + + /// + /// Pressure oversample count + /// + public Oversample PressureSampleCount { get; set; } = Oversample.OversampleX8; + + /// + /// Communication bus used to read and write to the BMP280 sensor + /// + private readonly IByteCommunications bmp280Comms; + + /// + /// Compensation data from the sensor + /// + private CompensationData compensationData; + + /// + /// Sensor configuration + /// + private readonly Configuration configuration; + + /// + /// The temperature from the last reading + /// + public Units.Temperature? Temperature => Conditions.Temperature; + + /// + /// The pressure from the last reading + /// + public Pressure? Pressure => Conditions.Pressure; + + /// + /// The default SPI bus speed for the device + /// + public Frequency DefaultSpiBusSpeed => new(10000, Frequency.UnitType.Kilohertz); + + /// + /// The SPI bus speed for the device + /// + public Frequency SpiBusSpeed + { + get => ((ISpiCommunications)bmp280Comms).BusSpeed; + set => ((ISpiCommunications)bmp280Comms).BusSpeed = value; + } + + /// + /// The default SPI bus mode for the device + /// + public SpiClockConfiguration.Mode DefaultSpiBusMode => SpiClockConfiguration.Mode.Mode0; + + /// + /// The SPI bus mode for the device + /// + public SpiClockConfiguration.Mode SpiBusMode + { + get => ((ISpiCommunications)bmp280Comms).BusMode; + set => ((ISpiCommunications)bmp280Comms).BusMode = value; + } + + /// + /// The default I2C address for the peripheral + /// + public byte DefaultI2cAddress => (byte)Addresses.Default; + + private readonly IDigitalOutputPort? chipSelectPort; + + /// + /// Initializes a new instance of the BMP280 class + /// + /// I2C Bus to use for communicating with the sensor + /// I2C address of the sensor (default = 0x76) + public Bmp280(II2cBus i2cBus, byte address = (byte)Addresses.Default) + { + bmp280Comms = new I2cCommunications(i2cBus, address); + configuration = new Configuration(); // here to avoid the warning + Initialize(); + } + + /// + /// Initializes a new instance of the BMP280 class + /// + /// The SPI bus connected to the BMP280 + /// The chip select pin + public Bmp280(ISpiBus spiBus, IPin chipSelectPin) : + this(spiBus, chipSelectPin.CreateDigitalOutputPort()) + { + createdPort = true; + } + + /// + /// Initializes a new instance of the BMP280 class + /// + /// The SPI bus connected to the BMP280 + /// The port for the chip select pin + public Bmp280(ISpiBus spiBus, IDigitalOutputPort chipSelectPort) + { + bmp280Comms = new SpiCommunications(spiBus, this.chipSelectPort = chipSelectPort, DefaultSpiBusSpeed, DefaultSpiBusMode); + configuration = new Configuration(); // here to avoid the warning + Initialize(); + } + + /// + /// Initialize the sensor + /// + protected void Initialize() + { + Bmx280.ReadCompensationData(bmp280Comms, readBuffer, compensationData); + + configuration.Mode = Modes.Sleep; + configuration.Filter = FilterCoefficient.Off; + UpdateConfiguration(configuration); + } + + /// + /// Raise events for subscribers and notify of value changes + /// + /// The updated sensor data + protected override void RaiseEventsAndNotify(IChangeResult<(Units.Temperature? Temperature, Pressure? Pressure)> changeResult) + { + if (changeResult.New.Temperature is { } temp) + { + _temperatureHandlers?.Invoke(this, new ChangeResult(temp, changeResult.Old?.Temperature)); + } + if (changeResult.New.Pressure is { } pressure) + { + _pressureHandlers?.Invoke(this, new ChangeResult(pressure, changeResult.Old?.Pressure)); + } + base.RaiseEventsAndNotify(changeResult); + } + + /// + /// Update the sensor information from the BME280. + /// + /// + /// Reads the raw temperature, pressure and humidity data from the BME280 and applies + /// the compensation data to get the actual readings. These are made available through the + /// Temperature, Pressure and Humidity properties. + /// All three readings are taken at once to ensure that the three readings are consistent. + /// Register locations and formulas taken from the Bosch BME280 datasheet revision 1.1, May 2015. + /// Register locations - section 5.3 Memory Map + /// Formulas - section 4.2.3 Compensation Formulas + /// The integer formulas have been used to try and keep the calculations per formant. + /// + protected override async Task<(Units.Temperature? Temperature, Pressure? Pressure)> ReadSensor() + { + // TODO: set an update flag on the oversample properties and set + // these once, unless the update flag has been set. + configuration.TemperatureOverSampling = TemperatureSampleCount; + configuration.PressureOversampling = PressureSampleCount; + + // if we're not in normal mode, set up the BMP280 for a one-time read + if (configuration.Mode != Modes.Normal) + { + configuration.Mode = Modes.Forced; + configuration.Filter = FilterCoefficient.Off; + UpdateConfiguration(configuration); + await Task.Delay(100); //give the BMP280 time to read new values + } + + var data = await Bmx280.ReadSensor(bmp280Comms, readBuffer, compensationData); + return (data.Temperature, data.Pressure); + } + + /// + /// Update the configuration for the BMP280. + /// + /// + /// This method uses the data in the configuration properties in order to set up the + /// BMP280. Ensure that the following are set correctly before calling this method: + /// - Standby + /// - Filter + /// - HumidityOverSampling + /// - TemperatureOverSampling + /// - PressureOverSampling + /// - Mode + /// + private void UpdateConfiguration(Configuration configuration) + { + // Put to sleep to allow the configuration to be changed. + bmp280Comms.WriteRegister((byte)Register.Measurement, 0x00); + + var data = (byte)((((byte)configuration.Standby << 5) & 0xe0) | (((byte)configuration.Filter << 2) & 0x1c)); + bmp280Comms.WriteRegister((byte)Register.Configuration, data); + data = (byte)((byte)configuration.HumidityOverSampling & 0x07); + bmp280Comms.WriteRegister((byte)Register.Humidity, data); + data = (byte)((((byte)configuration.TemperatureOverSampling << 5) & 0xe0) | + (((byte)configuration.PressureOversampling << 2) & 0x1c) | + ((byte)configuration.Mode & 0x03)); + bmp280Comms.WriteRegister((byte)Register.Measurement, data); + } + + /// + /// Reset the sensor. + /// + /// + /// Perform a full power-on-reset of the sensor and reset the configuration of the sensor. + /// + public void Reset() + { + bmp280Comms.WriteRegister((byte)Register.Reset, 0xb6); + UpdateConfiguration(configuration); + } + + /// + /// Get the chip ID + /// + /// The ID as a byte + public byte GetChipID() + { + bmp280Comms.ReadRegister((byte)Register.ChipID, readBuffer.Span[0..1]); + return readBuffer.Span[0]; + } + + /// + /// Start updating + /// + /// The update inverval + public override void StartUpdating(TimeSpan? updateInterval = null) + { + configuration.Mode = Modes.Normal; + UpdateConfiguration(configuration); + + base.StartUpdating(updateInterval); + } + + /// + public void Dispose() + { + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + + /// + /// Dispose of the object + /// + /// Is disposing + protected virtual void Dispose(bool disposing) + { + if (!IsDisposed) + { + if (disposing && createdPort) + { + chipSelectPort?.Dispose(); + } + + IsDisposed = true; + } + } + + async Task ISensor.Read() + => (await Read()).Temperature!.Value; + + async Task ISensor.Read() + => (await Read()).Pressure!.Value; +} \ No newline at end of file diff --git a/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bme280/Driver/Readme.md b/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bmx280/Driver/Readme.md similarity index 100% rename from Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bme280/Driver/Readme.md rename to Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bmx280/Driver/Readme.md diff --git a/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bme280/Driver/Sensors.Atmospheric.Bme280.csproj b/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bmx280/Driver/Sensors.Atmospheric.Bmx280.csproj similarity index 78% rename from Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bme280/Driver/Sensors.Atmospheric.Bme280.csproj rename to Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bmx280/Driver/Sensors.Atmospheric.Bmx280.csproj index 0cd312a8c3..cf49f600c6 100644 --- a/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bme280/Driver/Sensors.Atmospheric.Bme280.csproj +++ b/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bmx280/Driver/Sensors.Atmospheric.Bmx280.csproj @@ -1,4 +1,4 @@ - + 1.9.0 true @@ -6,14 +6,14 @@ Wilderness Labs, Inc netstandard2.1 Library - Bme280 + Bmx280 Wilderness Labs, Inc http://developer.wildernesslabs.co/Meadow/Meadow.Foundation/ - Meadow.Foundation.Sensors.Atmospheric.Bme280 + Meadow.Foundation.Sensors.Atmospheric.Bmx280 https://github.com/WildernessLabs/Meadow.Foundation - Meadow.Foundation, Atmospheric, BME280 + Meadow.Foundation,Atmospheric,BME280,BMP280 true - Bosch BME280 SPI and I2C absolute barometric pressure sensor + Bosch BMx280 SPI and I2C family of atmospheric sensor enable diff --git a/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bme280/Samples/Bme280_Sample/Bme280_Sample.csproj b/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bmx280/Samples/Bme280_Sample/Bme280_Sample.csproj similarity index 97% rename from Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bme280/Samples/Bme280_Sample/Bme280_Sample.csproj rename to Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bmx280/Samples/Bme280_Sample/Bme280_Sample.csproj index 410dc88451..1e10f83bde 100644 --- a/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bme280/Samples/Bme280_Sample/Bme280_Sample.csproj +++ b/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bmx280/Samples/Bme280_Sample/Bme280_Sample.csproj @@ -13,7 +13,7 @@ - + diff --git a/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bme280/Samples/Bme280_Sample/MeadowApp.cs b/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bmx280/Samples/Bme280_Sample/MeadowApp.cs similarity index 66% rename from Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bme280/Samples/Bme280_Sample/MeadowApp.cs rename to Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bmx280/Samples/Bme280_Sample/MeadowApp.cs index e5b9600c11..e2236e8687 100644 --- a/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bme280/Samples/Bme280_Sample/MeadowApp.cs +++ b/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bmx280/Samples/Bme280_Sample/MeadowApp.cs @@ -26,13 +26,14 @@ public override Task Initialize() }, filter: result => { - if (result.Old is { } old) + if (result.Old?.Temperature is { } oldTemp && + result.Old?.Humidity is { } oldHumidity && + result.New.Temperature is { } newTemp && + result.New.Humidity is { } newHumidity) { - return ( - (result.New.Temperature.Value - old.Temperature.Value).Abs().Celsius > 0.5 - && - (result.New.Humidity.Value - old.Humidity.Value).Percent > 0.05 - ); + return + (newTemp - oldTemp).Abs().Celsius > 0.5 && + (newHumidity - oldHumidity).Percent > 0.05; } return false; } @@ -41,9 +42,16 @@ public override Task Initialize() sensor.Updated += (sender, result) => { - Resolver.Log.Info($" Temperature: {result.New.Temperature?.Celsius:N2}C"); - Resolver.Log.Info($" Relative Humidity: {result.New.Humidity:N2}%"); - Resolver.Log.Info($" Pressure: {result.New.Pressure?.Millibar:N2}mbar ({result.New.Pressure?.Pascal:N2}Pa)"); + try + { + Resolver.Log.Info($" Temperature: {result.New.Temperature?.Celsius:N2}C"); + Resolver.Log.Info($" Relative Humidity: {result.New.Humidity:N2}%"); + Resolver.Log.Info($" Pressure: {result.New.Pressure?.Millibar:N2}mbar ({result.New.Pressure?.Pascal:N2}Pa)"); + } + catch (Exception ex) + { + Resolver.Log.Error(ex, "Error reading sensor"); + } }; return Task.CompletedTask; @@ -73,7 +81,7 @@ void CreateI2CSensor() Resolver.Log.Info("Create BME280 sensor with I2C..."); var i2c = Device.CreateI2cBus(); - sensor = new Bme280(i2c, (byte)Bme280.Addresses.Default); // SDA pulled up + sensor = new Bme280(i2c, (byte)Bmx280.Addresses.Default); // SDA pulled up } diff --git a/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bme280/Samples/Bme280_Sample/meadow.config.yaml b/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bmx280/Samples/Bme280_Sample/meadow.config.yaml similarity index 100% rename from Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bme280/Samples/Bme280_Sample/meadow.config.yaml rename to Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bmx280/Samples/Bme280_Sample/meadow.config.yaml diff --git a/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bmx280/Samples/Bmp280_Sample/Bmp280_Sample.csproj b/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bmx280/Samples/Bmp280_Sample/Bmp280_Sample.csproj new file mode 100644 index 0000000000..1e10f83bde --- /dev/null +++ b/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bmx280/Samples/Bmp280_Sample/Bmp280_Sample.csproj @@ -0,0 +1,23 @@ + + + https://github.com/WildernessLabs/Meadow.Foundation + Wilderness Labs, Inc + Wilderness Labs, Inc + true + netstandard2.1 + Library + App + + + 8.0 + + + + + + + + Always + + + diff --git a/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bmx280/Samples/Bmp280_Sample/MeadowApp.cs b/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bmx280/Samples/Bmp280_Sample/MeadowApp.cs new file mode 100644 index 0000000000..dd21f8dbe5 --- /dev/null +++ b/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bmx280/Samples/Bmp280_Sample/MeadowApp.cs @@ -0,0 +1,84 @@ +using Meadow; +using Meadow.Devices; +using Meadow.Foundation.Sensors.Atmospheric; +using System; +using System.Threading.Tasks; + +namespace Sensors.Atmospheric.BMP280_Sample +{ + public class MeadowApp : App + { + // + + Bmp280 sensor; + + public override Task Initialize() + { + Resolver.Log.Info("Initializing..."); + + //CreateSpiSensor(); + CreateI2CSensor(); + + var consumer = Bmp280.CreateObserver( + handler: result => + { + Resolver.Log.Info($"Observer: Temp changed by threshold; new temp: {result.New.Temperature?.Celsius:N2}C, old: {result.Old?.Temperature?.Celsius:N2}C"); + }, + filter: result => + { + if (result.Old?.Temperature is { } oldTemp && + result.New.Temperature is { } newTemp) + { + return (newTemp - oldTemp).Abs().Celsius > 0.5; + } + return false; + } + ); + sensor.Subscribe(consumer); + + sensor.Updated += (sender, result) => + { + try + { + Resolver.Log.Info($" Temperature: {result.New.Temperature?.Celsius:N2}C"); + Resolver.Log.Info($" Pressure: {result.New.Pressure?.Millibar:N2}mbar ({result.New.Pressure?.Pascal:N2}Pa)"); + } + catch (Exception ex) + { + Resolver.Log.Error(ex, "Error reading sensor"); + } + }; + + return Task.CompletedTask; + } + + public override async Task Run() + { + var conditions = await sensor.Read(); + Resolver.Log.Info("Initial Readings:"); + Resolver.Log.Info($" Temperature: {conditions.Temperature?.Celsius:N2}C"); + Resolver.Log.Info($" Pressure: {conditions.Pressure?.Bar:N2}hPa"); + + sensor.StartUpdating(TimeSpan.FromSeconds(1)); + } + + void CreateSpiSensor() + { + Resolver.Log.Info("Create BME280 sensor with SPI..."); + + var spi = Device.CreateSpiBus(); + sensor = new Bmp280(spi, Device.Pins.D00.CreateDigitalOutputPort()); + } + + void CreateI2CSensor() + { + Resolver.Log.Info("Create BME280 sensor with I2C..."); + + var i2c = Device.CreateI2cBus(); + sensor = new Bmp280(i2c, (byte)Bmx280.Addresses.Default); // SDA pulled up + + } + + // + } +} \ No newline at end of file diff --git a/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bmx280/Samples/Bmp280_Sample/meadow.config.yaml b/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bmx280/Samples/Bmp280_Sample/meadow.config.yaml new file mode 100644 index 0000000000..32363cb69c --- /dev/null +++ b/Source/Meadow.Foundation.Peripherals/Sensors.Atmospheric.Bmx280/Samples/Bmp280_Sample/meadow.config.yaml @@ -0,0 +1,2 @@ +MonoControl: + Options: --jit \ No newline at end of file diff --git a/Source/Meadow.Foundation.sln b/Source/Meadow.Foundation.sln index 119c73f3a0..277bbdc212 100644 --- a/Source/Meadow.Foundation.sln +++ b/Source/Meadow.Foundation.sln @@ -147,7 +147,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Bmp085", "Bmp085", "{C724C5 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{4A2BBDB4-6130-4C9D-9947-12F39309E8E9}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Bme280", "Bme280", "{449B29BE-7A04-4A6D-858B-77C8E81415DE}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Bmx280", "Bmx280", "{449B29BE-7A04-4A6D-858B-77C8E81415DE}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{21C511DD-43DD-486E-86D7-18D8F6810ACD}" EndProject @@ -279,9 +279,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hih6130_Sample", "Meadow.Fo EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sensors.Atmospheric.Bmp085", "Meadow.Foundation.Peripherals\Sensors.Atmospheric.Bmp085\Driver\Sensors.Atmospheric.Bmp085.csproj", "{728ECF00-EB02-4981-87F9-A72A163DB93C}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Bme280_Sample", "Meadow.Foundation.Peripherals\Sensors.Atmospheric.Bme280\Samples\Bme280_Sample\Bme280_Sample.csproj", "{59EED95C-1CA7-466C-853C-BF14E1EF43A9}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Bme280_Sample", "Meadow.Foundation.Peripherals\Sensors.Atmospheric.Bmx280\Samples\Bme280_Sample\Bme280_Sample.csproj", "{59EED95C-1CA7-466C-853C-BF14E1EF43A9}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sensors.Atmospheric.Bme280", "Meadow.Foundation.Peripherals\Sensors.Atmospheric.Bme280\Driver\Sensors.Atmospheric.Bme280.csproj", "{D94618E1-D812-4CFF-B539-54AE9559F827}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sensors.Atmospheric.Bmx280", "Meadow.Foundation.Peripherals\Sensors.Atmospheric.Bmx280\Driver\Sensors.Atmospheric.Bmx280.csproj", "{D94618E1-D812-4CFF-B539-54AE9559F827}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ili9341_Jpg_Sample", "Meadow.Foundation.Peripherals\Displays.TftSpi\Samples\Ili9341_Jpg_Sample\Ili9341_Jpg_Sample.csproj", "{A5F4A75B-20F6-4CDF-B986-635081894E43}" EndProject @@ -1483,6 +1483,14 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ina228_Sample", "Meadow.Fou EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ina260_Sample", "Meadow.Foundation.Peripherals\Sensors.Power.Ina2xx\Samples\Ina260_Sample\Ina260_Sample.csproj", "{E06A278F-282F-4FB4-9F4F-902833B0E6E2}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MicroJson_Complex_Sample", "Meadow.Foundation.Libraries_and_Frameworks\Serialization.MicroJson\Samples\MicroJson_Complex_Sample\MicroJson_Complex_Sample.csproj", "{6FD1D41A-1A80-4E04-B7FF-5EDCE6A526AC}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{4B7EFCFD-6454-45CF-9C95-C1BF94B17E27}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MicroJson.Unit.Tests", "Meadow.Foundation.Libraries_and_Frameworks\Serialization.MicroJson\Tests\MicroJson.Unit.Tests\MicroJson.Unit.Tests.csproj", "{BB121196-8958-466C-B899-025C12DC2B76}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Bmp280_Sample", "Meadow.Foundation.Peripherals\Sensors.Atmospheric.Bmx280\Samples\Bmp280_Sample\Bmp280_Sample.csproj", "{1A156008-49C6-4ADD-BED9-1CF98D8FB076}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -3583,6 +3591,20 @@ Global {E06A278F-282F-4FB4-9F4F-902833B0E6E2}.Release|Any CPU.ActiveCfg = Release|Any CPU {E06A278F-282F-4FB4-9F4F-902833B0E6E2}.Release|Any CPU.Build.0 = Release|Any CPU {E06A278F-282F-4FB4-9F4F-902833B0E6E2}.Release|Any CPU.Deploy.0 = Release|Any CPU + {6FD1D41A-1A80-4E04-B7FF-5EDCE6A526AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6FD1D41A-1A80-4E04-B7FF-5EDCE6A526AC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6FD1D41A-1A80-4E04-B7FF-5EDCE6A526AC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6FD1D41A-1A80-4E04-B7FF-5EDCE6A526AC}.Release|Any CPU.Build.0 = Release|Any CPU + {BB121196-8958-466C-B899-025C12DC2B76}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BB121196-8958-466C-B899-025C12DC2B76}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BB121196-8958-466C-B899-025C12DC2B76}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BB121196-8958-466C-B899-025C12DC2B76}.Release|Any CPU.Build.0 = Release|Any CPU + {1A156008-49C6-4ADD-BED9-1CF98D8FB076}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1A156008-49C6-4ADD-BED9-1CF98D8FB076}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1A156008-49C6-4ADD-BED9-1CF98D8FB076}.Debug|Any CPU.Deploy.0 = Debug|Any CPU + {1A156008-49C6-4ADD-BED9-1CF98D8FB076}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1A156008-49C6-4ADD-BED9-1CF98D8FB076}.Release|Any CPU.Build.0 = Release|Any CPU + {1A156008-49C6-4ADD-BED9-1CF98D8FB076}.Release|Any CPU.Deploy.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -4323,6 +4345,10 @@ Global {CCA3369C-469F-42C6-9BB3-AF124A8A2428} = {9E14DFF3-2BD0-4C3D-91A0-DD3063D5245F} {357E421A-0E86-4CB1-8210-B115951A6BE2} = {9E14DFF3-2BD0-4C3D-91A0-DD3063D5245F} {E06A278F-282F-4FB4-9F4F-902833B0E6E2} = {9E14DFF3-2BD0-4C3D-91A0-DD3063D5245F} + {6FD1D41A-1A80-4E04-B7FF-5EDCE6A526AC} = {CA32D6C5-DD19-4787-8EE1-B92415FEF903} + {4B7EFCFD-6454-45CF-9C95-C1BF94B17E27} = {B9C2605C-AC98-4BF1-8E3F-8F0F23A694C9} + {BB121196-8958-466C-B899-025C12DC2B76} = {4B7EFCFD-6454-45CF-9C95-C1BF94B17E27} + {1A156008-49C6-4ADD-BED9-1CF98D8FB076} = {21C511DD-43DD-486E-86D7-18D8F6810ACD} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {AF7CA16F-8C38-4546-87A2-5DAAF58A1520}