Example #1
0
        // server
        private IRemoteAcuriteData HttpSendAsync(Options options)
        {
            var         http    = new HttpAcuriteData(options.Port, options.Protocol, listenLocal: false);
            AcuriteData current = new AcuriteData();

            // get the latest data
            OnPolled += (data) =>
            {
                current = data;
            };

            // serialize upon request
            http.OnSend += () =>
            {
                // reset the stopwatch and restart
                LastNetQuery.Reset();
                LastNetQuery.Start();

                // serialize the most current data
                var json = System.Text.Json.JsonSerializer.Serialize <AcuriteData>(current);
                return(json);
            };

            // start listening
            http.SendAsync();

            return(http);
        }
Example #2
0
        private static void AcuriteDataTest()
        {
            var a = new AcuriteData();

            a.utcDate  = new DateTime(2020, 1, 1);
            a.pressure = 30;
            var b = new AcuriteData();

            b.utcDate  = new DateTime(2021, 1, 1);
            b.pressure = 31;

            var c = a + b;

            Console.WriteLine($"{c.pressure} {(c.pressureTrend == null ? "null" : c.pressureTrend[0]+"")}");

            var d = new AcuriteData();

            d.utcDate  = new DateTime(2021, 2, 1);
            d.pressure = 32;

            var e = c + d;

            Console.WriteLine($"{e.pressure} {(e.pressureTrend == null ? "null" : e.pressureTrend[0] + "")}");
        }
Example #3
0
        public AcuriteData Read(int vendorid, int productid)
        {
            var result = new AcuriteData()
            {
                utcDate = DateTime.UtcNow
            };
            IUsbDevice wholeUsbDevice;

            // find the device
            var device = Context.Find((usb) => usb.ProductId == productid && usb.VendorId == vendorid);

            // check if we are unable to return a result
            if (device == null)
            {
                return(result);
            }

            try
            {
                // open the device for communication
                device.Open();

                // on Linux we need to ask the kernel to detach the interface first
                if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
                {
                    var key = ((long)productid << 32) | ((long)vendorid);
                    if (!HasConfigured.Contains(key))
                    {
                        if (device.DeviceHandle == null)
                        {
                            throw new Exception("failed to properly open device");
                        }
                        var ec = DetachKernelDriver(device.DeviceHandle, 0);
                        if (ec != Error.Success)
                        {
                            Console.WriteLine($"failed to detach kernel from device : {ec}");
                        }
                        HasConfigured.Add(key);
                    }
                }

                // If this is a "whole" usb device (libusb-win32, linux libusb-1.0)
                // it exposes an IUsbDevice interface. If not (WinUSB) the
                // 'wholeUsbDevice' variable will be null indicating this is
                // an interface of a device; it does not require or support
                // configuration and interface selection.
                wholeUsbDevice = device as IUsbDevice;
                if (!ReferenceEquals(wholeUsbDevice, null))
                {
                    // This is a "whole" USB device. Before it can be used,
                    // the desired configuration and interface must be selected.

                    // Select config #1
                    wholeUsbDevice.SetConfiguration(1);

                    // Claim interface #0.
                    wholeUsbDevice.ClaimInterface(0);
                }

                // read reports
                if (!ReadReport1(device, ref result))
                {
                    Console.WriteLine($"{DateTime.Now:o}: Failed to access report 1");
                }
                if (!ReadReport2(device, ref result))
                {
                    Console.WriteLine($"{DateTime.Now:o}: Failed to access report 2");
                }
            }
            finally
            {
                if (device.IsOpen)
                {
                    // If this is a "whole" usb device (libusb-win32, linux libusb-1.0)
                    // it exposes an IUsbDevice interface. If not (WinUSB) the
                    // 'wholeUsbDevice' variable will be null indicating this is
                    // an interface of a device; it does not require or support
                    // configuration and interface selection.
                    wholeUsbDevice = device as IUsbDevice;
                    if (!ReferenceEquals(wholeUsbDevice, null))
                    {
                        // Release interface #0.
                        wholeUsbDevice.ReleaseInterface(0);
                    }

                    device.Close();
                }
            }

            return(result);
        }
Example #4
0
        private bool ReadReport2(IUsbDevice device, ref AcuriteData result)
        {
            var buffer = new byte[25];

            if (!Read(device, report: 2, buffer))
            {
                Console.WriteLine($"{DateTime.Now:o}: Failed to read report 2");
                return(false);
            }

            // ensure valid
            if (buffer.Length != 25 || buffer[0] != 0x02)
            {
                Console.WriteLine($"{DateTime.Now:o}: Invalid data in report 2");
                return(false);
            }

            // constants
            var c1 = ((buffer[3] << 8) + buffer[4]);
            var c2 = ((buffer[5] << 8) + buffer[6]);
            var c3 = ((buffer[7] << 8) + buffer[8]);
            var c4 = ((buffer[9] << 8) + buffer[10]);
            var c5 = ((buffer[11] << 8) + buffer[12]);
            var c6 = ((buffer[13] << 8) + buffer[14]);
            var c7 = ((buffer[15] << 8) + buffer[16]);
            var a  = buffer[17];
            var b  = buffer[18];
            var c  = buffer[19];
            var d  = buffer[20];

            if (c1 == 0x8000 && c2 == c3 && c3 == 0x0 &&
                c4 == 0x0400 && c5 == 0x1000 && c6 == 0x0 &&
                c7 == 0x0960 && a == b && b == c && c == d && d == 0x1)
            {
                // MS5607 sensor, typical in 02032 consoles
                var d2 = (float)(((buffer[21] & 0x0f) << 8) + buffer[22]);
                if (d2 >= 0x0800)
                {
                    d2 -= 0x1000;
                }
                var d1 = (float)((buffer[23] << 8) + buffer[24]);

                // decode

                // pressure
                result.pressure  = (d1 / 16.0f) - (208f); // mbar
                result.pressure *= 0.029529983071f;       // inHg
                result.pressure += 0.08106f;              // hack (the hub seems this much off this conversion)

                // inside temperature
                result.inTemperature = 25.0f + (0.05f * d2);                // celsius
                result.inTemperature = (result.inTemperature * 1.8f) + 32f; // fahrenheit
            }
            else
            {
                Console.WriteLine($"{DateTime.Now:o}: Failed to receive the right constants in report 2");
                return(false);
            }

            return(true);
        }
Example #5
0
        private bool ReadReport1(IUsbDevice device, ref AcuriteData result)
        {
            var buffer = new byte[10];

            if (!Read(device, report: 1, buffer))
            {
                Console.WriteLine($"{DateTime.Now:o}: Failed to read report 1");
                return(false);
            }

            // ensure valid
            if (buffer.Length != 10 || buffer[0] != 0x01)
            {
                Console.WriteLine($"{DateTime.Now:o}: Invalid data from hub");
                return(false);
            }

            // check for errors
            if ((buffer[1] & 0x0f) == 0x0f && buffer[3] == 0xff)
            {
                // no sensor data
                Console.WriteLine($"{DateTime.Now:o}: No sensor data");
                return(false);
            }
            else if ((buffer[3] & 0x0f) != 1 && (buffer[3] & 0x0f) != 8)
            {
                // bogus message flavor
                Console.WriteLine($"{DateTime.Now:o}: Bogus message flavor");
                return(false);
            }
            else if (buffer[9] != 0xff && buffer[9] != 0x0)
            {
                // bogus final byte
                Console.WriteLine($"{DateTime.Now:o}: Bogus final byte");
                return(false);
            }
            else if ((buffer[8] & 0x0f) < 0 || (buffer[8] & 0x0f) > 3)
            {
                // bogus signal strength
                Console.WriteLine($"{DateTime.Now:o}: Bogus signal strength");
                return(false);
            }

            // decode

            // channel (todo A B C)
            result.channel = (ChannelName)(buffer[1] & 0xf0);

            // sensor id
            result.sensorId = ((buffer[1] & 0x0f) << 8) | buffer[2];

            // signal strength
            result.signal = (SignalStrength)(buffer[8] & 0x0f);

            // stale data
            if (result.signal == SignalStrength.None)
            {
                Console.WriteLine($"{DateTime.Now:o}: Invalid signal strength");
            }

            // low battery
            result.lowBattery = ((buffer[3] & 0xf0) >> 4) != 0x7;

            // wind speed
            result.windSpeed = (float)(((buffer[4] & 0x1f) << 3) | ((buffer[5] & 0x70) >> 4));
            if (result.windSpeed > 0)
            {
                result.windSpeed = (result.windSpeed * 0.8278f) + 1.0f; // kph
            }
            result.windSpeed *= 0.62137f;                               // mph

            if ((buffer[3] & 0xf) == 1)
            {
                // wind direction
                var dir = buffer[5] & 0x0f;
                result.windDirection = WindDirection[(dir >= 0 && dir < WindDirection.Length) ? dir : 0]; // compass degrees

                // rain total
                result.rainTotal = (float)(((buffer[6] & 0x3f) << 7) | (buffer[7] & 0x7f)) * 0.01f; // inch
            }
            else
            {
                // outside temperature
                result.outTemperature = (float)(((buffer[5] & 0x0f) << 7) | (buffer[6] & 0x7f)) / 18.0f - 40.0f; // celsius
                result.outTemperature = (result.outTemperature * 1.8f) + 32f;                                    // fahrenheit

                // outside humidity
                result.outHumidity = buffer[7] & 0x7f; // percentage
            }

            return(true);
        }
Example #6
0
        private async void FrameUpdate(object state)
        {
            if (Canvas == null)
            {
                throw new Exception("must have a valid canvas to draw too");
            }

            // the timer is reentrant, so only allow one instance to run
            if (System.Threading.Interlocked.CompareExchange(ref FrameLock, 1, 0) != 0)
            {
                return;
            }

            // grab predictions
            var weather = await Prediction.CurrentWeather();

            var weatherStation = await Prediction.CurrentWeatherStation();

            var rowheight = 26f * Ratio;
            var fontsize  = 18f * Ratio;
            var fontname  = "Courier New";
            var point     = new Point()
            {
                X = 0f, Y = 0f
            };
            var avoiddups = new HashSet <string>();

            try
            {
                await Canvas.SuspendLayout();

                // clear
                Canvas.Clear(RGBA.Black);

                // current weather
                point.Y = (rowheight * 0);
                Canvas.Text(RGBA.White, point, "Local weather", fontsize, fontname);

                foreach (var w in weather.OrderByDescending(wr => wr.Date))
                {
                    if (avoiddups.Contains(w.Type))
                    {
                        continue;
                    }

                    switch (w.Type)
                    {
                    case "temperature":
                        point.X = 0f;
                        point.Y = (rowheight * 1);
                        Canvas.Text(RGBA.White, point, $"current: {w.Value}°", fontsize, fontname);
                        break;

                    case "temperaturetrend":
                        point.X = (rowheight * 6);
                        point.Y = (rowheight * 1);
                        Canvas.Text(RGBA.White, point, $"[{w.StrValue.ToLower()}]", fontsize, fontname);
                        break;

                    case "temperaturelow":
                        point.X = 0f;
                        point.Y = (rowheight * 2);
                        Canvas.Text(RGBA.White, point, $"low:     {w.Value}°", fontsize, fontname);
                        break;

                    case "temperaturehigh":
                        point.X = 0f;
                        point.Y = (rowheight * 3);
                        Canvas.Text(RGBA.White, point, $"high:    {w.Value}°", fontsize, fontname);
                        break;

                    case "winddirection":
                        point.X = 0f;
                        point.Y = (rowheight * 4);
                        Canvas.Text(RGBA.White, point, $"wind:    {w.StrValue}", fontsize, fontname);
                        break;

                    case "windspeed":
                        point.X = (rowheight * 6);
                        point.Y = (rowheight * 4);
                        Canvas.Text(RGBA.White, point, $"{w.StrValue.ToLower()}", fontsize, fontname);
                        break;

                    case "shortforecast":
                        point.X = 0f;
                        point.Y = (rowheight * 5);
                        var description = SplitString(w.StrValue, maxlength: 23);
                        Canvas.Text(RGBA.White, point, $"{description.Item1}", fontsize, fontname);
                        point.Y = (rowheight * 6);
                        Canvas.Text(RGBA.White, point, $"{description.Item2}", fontsize, fontname);
                        // debug
                        if (true)
                        {
                            point.X = 0f;
                            point.Y = (rowheight * 7);
                            Canvas.Text(RGBA.White, point, $"{w.Date.ToLocalTime():yyyy/MM/dd hh:mm}", fontsize * 0.5f, fontname);
                        }
                        break;
                    }

                    avoiddups.Add(w.Type);
                }

                // weather station
                if (weatherStation.Count > 0)
                {
                    var padding = (rowheight * 10);
                    point.Y = (rowheight * 0);
                    point.X = padding;
                    Canvas.Text(RGBA.White, point, $"Weather Station", fontsize, fontname);

                    // capture all the values
                    var data = new AcuriteData();
                    foreach (var w in weatherStation.OrderByDescending(s => s.Date))
                    {
                        if (data.utcDate == default(DateTime))
                        {
                            data.utcDate = w.Date;
                        }

                        switch (w.Type)
                        {
                        case "outtemperature": if (!data.outTemperature.HasValue)
                            {
                                data.outTemperature = w.Value;
                            }
                            break;

                        case "outhumidity": if (!data.outHumidity.HasValue)
                            {
                                data.outHumidity = w.Value;
                            }
                            break;

                        case "pressure": if (!data.pressure.HasValue)
                            {
                                data.pressure = w.Value;
                            }
                            break;

                        case "pressuretrend": if (data.pressureTrend == null)
                            {
                                data.pressureTrend = w.Values;
                            }
                            break;

                        case "winddirection": if (!data.windDirection.HasValue)
                            {
                                data.windDirection = w.Value;
                            }
                            break;

                        case "windspeed": if (!data.windSpeed.HasValue)
                            {
                                data.windSpeed = w.Value;
                            }
                            break;

                        case "raintotal": if (!data.rainTotal.HasValue)
                            {
                                data.rainTotal = w.Value;
                            }
                            break;

                        case "raintotaltrend": if (data.rainTotalTrend == null)
                            {
                                data.rainTotalTrend = w.Values;
                            }
                            break;
                        }
                    }

                    // display the data
                    if (data.outTemperature.HasValue)
                    {
                        point.X = padding;
                        point.Y = (rowheight * 1);
                        Canvas.Text(RGBA.White, point, $"current:  {data.outTemperature:f0}°", fontsize, fontname);
                    }
                    if (data.outHumidity.HasValue)
                    {
                        point.X = padding;
                        point.Y = (rowheight * 2);
                        Canvas.Text(RGBA.White, point, $"humidity: {data.outHumidity:f0}%", fontsize, fontname);
                    }
                    if (data.pressure.HasValue)
                    {
                        point.X = padding;
                        point.Y = (rowheight * 3);
                        Canvas.Text(RGBA.White, point, $"pressure: {data.pressure:f2} inHg", fontsize, fontname);
                        if (data.pressureTrend != null)
                        {
                            point.X = padding;
                            point.Y = (rowheight * 4);
                            var trend  = ReadPressureTrend(data.pressure.Value, data.pressureTrend);
                            var change = trend.Item2 == PressureChange.Failing ? "↓" : (trend.Item2 == PressureChange.Rising ? "↑" : "-");
                            Canvas.Text(RGBA.White, point, $"          {trend.Item3} {change}", fontsize, fontname);
                        }
                    }
                    if (data.windDirection.HasValue)
                    {
                        point.X = padding;
                        point.Y = (rowheight * 5);
                        Canvas.Text(RGBA.White, point, $"wind:     {DegreesToCompass(data.windDirection.Value)}", fontsize, fontname);
                    }
                    if (data.windSpeed.HasValue)
                    {
                        point.X = padding + (rowheight * 6);
                        point.Y = (rowheight * 5);
                        Canvas.Text(RGBA.White, point, $"{data.windSpeed:f0} mph", fontsize, fontname);
                    }
                    if (data.rainTotal.HasValue)
                    {
                        point.X = padding;
                        point.Y = (rowheight * 6);
                        var rainTotal = data.rainTotal;
                        if (data.rainTotalTrend != null && data.rainTotalTrend.Length >= 1)
                        {
                            rainTotal -= data.rainTotalTrend[data.rainTotalTrend.Length - 1];
                        }
                        Canvas.Text(RGBA.White, point, $"rain:     {rainTotal:f2} in", fontsize, fontname);

                        // debug
                        if (true)
                        {
                            point.X = padding;
                            point.Y = (rowheight * 7);
                            Canvas.Text(RGBA.White, point, $"{data.utcDate.ToLocalTime():yyyy/MM/dd hh:mm}", fontsize * 0.5f, fontname);
                        }
                    }
                }
            }
            finally
            {
                await Canvas.ResumeLayout();
            }

            // fire that the frame is done
            if (OnRendered != null)
            {
                OnRendered();
            }

            // set state back to not running
            System.Threading.Volatile.Write(ref FrameLock, 0);
        }
Example #7
0
        //
        // Common
        //
        private void PollStation(Options options)
        {
            // setup
            var station  = new AcuriteStation();
            var combined = new AcuriteData();

            // poll the station until asked to stop
            var failCount = 0;

            while (true)
            {
                // check if the poll timeout has occured (in which case, skip this station read)
                if (LastNetQuery.ElapsedMilliseconds < options.SleepPolling)
                {
                    // read data
                    AcuriteData current;
                    if (options.VendorId.HasValue && options.ProductId.HasValue)
                    {
                        current = station.Read(options.VendorId.Value, options.ProductId.Value);
                    }
                    else
                    {
                        current = station.Read();
                    }

                    // send if there is data
                    if (!current.HasValue())
                    {
                        failCount++;
                        Console.WriteLine($"{DateTime.Now:o}: failed to get a station reading");
                        if (failCount >= options.MaxPollFailures)
                        {
                            throw new Exception($"Failed to get station data {failCount} in a row");
                        }
                    }
                    else
                    {
                        // reset fail count (if set)
                        failCount = 0;

                        // combine the data into a view of the latest
                        combined = combined + current;

                        // display
                        var data = combined;
                        if (options.RawData)
                        {
                            data = current;
                        }
                        Console.WriteLine($"{DateTime.Now:o}:{(options.RawData ? " [raw]" : "")} {data.channel},{data.sensorId},{data.signal},{data.lowBattery},{data.windSpeed},{data.windDirection},{data.rainTotal},{data.outTemperature},{data.outHumidity},{data.pressure},{data.inTemperature},{(data.pressureTrend != null ? data.pressureTrend.Length : 0)},{(data.rainTotalTrend != null ? data.rainTotalTrend.Length : 0)}");

                        // notifiy of data
                        if (OnPolled != null)
                        {
                            OnPolled(data);
                        }
                    }
                }

                System.Threading.Thread.Sleep(options.Interval);
            }
        }