/// <summary> /// Try to gets the current distance, , usual range from 2 cm to 400 cm /// </summary> /// <param name="result">Length</param> /// <returns>True if success</returns> public bool TryGetDistance(out Length result) { // Time when we give up on looping and declare that reading failed // 100ms was chosen because max measurement time for this sensor is around 24ms for 400cm // additionally we need to account 60ms max delay. // Rounding this up to a 100 in case of a context switch. long hangTicks = DateTime.Now.Ticks + 100 * TimeSpan.TicksPerMillisecond; _timer.Reset(); // Measurements should be 60ms apart, in order to prevent trigger signal mixing with echo signal // ref https://components101.com/sites/default/files/component_datasheet/HCSR04%20Datasheet.pdf while (DateTime.Now.Ticks - _lastMeasurment < 60 * TimeSpan.TicksPerMillisecond) { Thread.Sleep(TimeSpan.FromTicks(DateTime.Now.Ticks - _lastMeasurment)); } _lastMeasurment = DateTime.Now.Ticks; // Trigger input for 10uS to start ranging _controller.Write(_trigger, PinValue.High); DelayHelper.DelayMicroseconds(10, true); _controller.Write(_trigger, PinValue.Low); // Wait until the echo pin is HIGH (that marks the beginning of the pulse length we want to measure) while (_controller.Read(_echo) == PinValue.Low) { if (DateTime.Now.Ticks - hangTicks > 0) { result = default; return(false); } } _timer.Start(); // Wait until the pin is LOW again, (that marks the end of the pulse we are measuring) while (_controller.Read(_echo) == PinValue.High) { if (DateTime.Now.Ticks - hangTicks > 0) { result = default; return(false); } } _timer.Stop(); // distance = (time / 2) × velocity of sound (34300 cm/s) result = Length.FromCentimeters((_timer.Elapsed.TotalMilliseconds / 2.0) * 34.3); if (result.Value > 400) { // result is more than sensor supports // something went wrong result = default; return(false); } return(true); }
/// <summary> /// The CCS811 sensor constructor /// </summary> /// <param name="i2cDevice">A valid I2C device</param> /// <param name="gpioController">An optional controller, either the default one will be used, either none will be created if any pin is used</param> /// <param name="pinWake">An awake pin, it is optional, this pin can be set to the ground if the sensor is always on</param> /// <param name="pinInterruption">An interruption pin when a measurement is ready, best use when you specify a threshold</param> /// <param name="pinReset">An optional hard reset pin</param> /// <param name="shouldDispose">Should the GPIO controller be disposed at the end</param> public Ccs811Sensor(I2cDevice i2cDevice, GpioController?gpioController = null, int pinWake = -1, int pinInterruption = -1, int pinReset = -1, bool shouldDispose = true) { _i2cDevice = i2cDevice; _pinWake = pinWake; _pinInterruption = pinInterruption; _pinReset = pinReset; _shouldDispose = shouldDispose; // We need a GPIO controller only if we are using any of the pin if ((_pinInterruption >= 0) || (_pinReset >= 0) || (_pinWake >= 0)) { _shouldDispose = _shouldDispose || gpioController == null; _controller = gpioController ?? new GpioController(); } if (_controller is object) { _controller.OpenPin(_pinWake, PinMode.Output); _controller.Write(_pinWake, PinValue.High); } if (_controller is object && _pinReset >= 0) { _controller.OpenPin(_pinReset, PinMode.Output); _controller.Write(_pinReset, PinValue.Low); // Delays from documentation CCS811-Datasheet.pdf page 8 // 15 micro second DelayHelper.DelayMicroseconds(15, true); _controller.Write(_pinReset, PinValue.High); // Need to wait at least 2 milliseconds before executing anything I2C Thread.Sleep(2); } // Initialization flow page 29 // https://www.sciosense.com/wp-content/uploads/2020/01/CCS811-Application-Note-Programming-and-interfacing-guide.pdf // do a soft reset Span <byte> toReset = stackalloc byte[4] { 0x11, 0xE5, 0x72, 0x8A }; WriteRegister(Register.SW_RESET, toReset); // Wait 2 milliseconds as per documentation Thread.Sleep(2); if (HardwareIdentification != 0x81) { throw new IOException($"CCS811 does not have a valid ID: {HardwareIdentification}. ID must be 0x81."); } if ((HardwareVersion & 0xF0) != 0x10) { throw new IOException($"CCS811 does not have a valid version: {HardwareVersion}, should be 0x1X where any X is valid."); } // Read status if (!Status.HasFlag(Status.APP_VALID)) { throw new IOException($"CCS811 has no application firmware loaded."); } // Switch to app mode and wait 1 millisecond according to doc WriteRegister(Register.APP_START); Thread.Sleep(1); if (!Status.HasFlag(Status.FW_MODE)) { throw new IOException($"CCS811 is not in application mode."); } // Set interrupt if the interruption pin is valid if (_controller is object && _pinInterruption >= 0) { _controller.OpenPin(_pinInterruption, PinMode.Input); byte mode = 0b0000_1000; WriteRegister(Register.MEAS_MODE, mode); _controller.RegisterCallbackForPinValueChangedEvent(_pinInterruption, PinEventTypes.Falling, InterruptReady); _running = true; // Start a new thread to monitor the events new Thread(() => { _isRunning = true; while (_running) { var res = _controller.WaitForEvent(_pinInterruption, PinEventTypes.Falling, new TimeSpan(0, 0, 0, 0, 50)); if ((!res.TimedOut) && (res.EventTypes != PinEventTypes.None)) { InterruptReady(_controller, new PinValueChangedEventArgs(res.EventTypes, _pinInterruption)); // We know we won't get any new measurement in next 250 milliseconds at least // Waiting to make sure the sensor will have time to remove the interrupt pin Thread.Sleep(50); } } _isRunning = false; }).Start(); } }
private void Initialize() { // Prep the pins _controller.OpenPin(_rsPin, PinMode.Output); if (_rwPin != -1) { _controller.OpenPin(_rwPin, PinMode.Output); // Set to write. Once we enable reading have reading pull high and reset // after reading to give maximum performance to write (i.e. assume that // the pin is low when writing). _controller.Write(_rwPin, PinValue.Low); } if (_backlight != -1) { _controller.OpenPin(_backlight, PinMode.Output); if (_backlightBrightness > 0) { // Turn on the backlight _controller.Write(_backlight, PinValue.High); } } _controller.OpenPin(_enablePin, PinMode.Output); for (int i = 0; i < _dataPins.Length; ++i) { _controller.OpenPin(_dataPins[i], PinMode.Output); } // The HD44780 self-initializes when power is turned on to the following settings: // // - 8 bit, 1 line, 5x7 font // - Display, cursor, and blink off // - Increment with no shift // // It is possible that the initialization will fail if the power is not provided // within specific tolerances. As such, we'll always perform the software based // initialization as described on pages 45/46 of the HD44780 data sheet. We give // a little extra time to the required waits as described. if (_dataPins.Length == 8) { // Init to 8 bit mode (this is the default, but other drivers // may set the controller to 4 bit mode, so reset to be safe.) DelayHelper.DelayMilliseconds(50, allowThreadYield: true); WriteBits(0b0011_0000, 8); DelayHelper.DelayMilliseconds(5, allowThreadYield: true); WriteBits(0b0011_0000, 8); DelayHelper.DelayMicroseconds(100, allowThreadYield: true); WriteBits(0b0011_0000, 8); } else { // Init to 4 bit mode, setting _rspin to low as we're writing 4 bits directly. // (Send writes the whole byte in two 4bit/nybble chunks) _controller.Write(_rsPin, PinValue.Low); DelayHelper.DelayMilliseconds(50, allowThreadYield: true); WriteBits(0b0011, 4); DelayHelper.DelayMilliseconds(5, allowThreadYield: true); WriteBits(0b0011, 4); DelayHelper.DelayMicroseconds(100, allowThreadYield: true); WriteBits(0b0011, 4); WriteBits(0b0010, 4); } // The busy flag can NOT be checked until this point. }
/// <summary> /// Read through One-Wire /// </summary> internal virtual void ReadThroughOneWire() { byte readVal = 0; uint count; // keep data line HIGH _controller.SetPinMode(_pin, PinMode.Output); _controller.Write(_pin, PinValue.High); DelayHelper.DelayMilliseconds(20, true); // send trigger signal _controller.Write(_pin, PinValue.Low); // wait at least 18 milliseconds // here wait for 18 milliseconds will cause sensor initialization to fail DelayHelper.DelayMilliseconds(20, true); // pull up data line _controller.Write(_pin, PinValue.High); // wait 20 - 40 microseconds DelayHelper.DelayMicroseconds(30, true); _controller.SetPinMode(_pin, PinMode.InputPullUp); // DHT corresponding signal - LOW - about 80 microseconds count = _loopCount; while (_controller.Read(_pin) == PinValue.Low) { if (count-- == 0) { IsLastReadSuccessful = false; return; } } // HIGH - about 80 microseconds count = _loopCount; while (_controller.Read(_pin) == PinValue.High) { if (count-- == 0) { IsLastReadSuccessful = false; return; } } // the read data contains 40 bits for (int i = 0; i < 40; i++) { // beginning signal per bit, about 50 microseconds count = _loopCount; while (_controller.Read(_pin) == PinValue.Low) { if (count-- == 0) { IsLastReadSuccessful = false; return; } } // 26 - 28 microseconds represent 0 // 70 microseconds represent 1 _stopwatch.Restart(); count = _loopCount; while (_controller.Read(_pin) == PinValue.High) { if (count-- == 0) { IsLastReadSuccessful = false; return; } } _stopwatch.Stop(); // bit to byte // less than 40 microseconds can be considered as 0, not necessarily less than 28 microseconds // here take 30 microseconds readVal <<= 1; if (!(_stopwatch.ElapsedTicks * 1000000F / Stopwatch.Frequency <= 30)) { readVal |= 1; } if (((i + 1) % 8) == 0) { _readBuff[i / 8] = readVal; } } _lastMeasurement = Environment.TickCount; if ((_readBuff[4] == ((_readBuff[0] + _readBuff[1] + _readBuff[2] + _readBuff[3]) & 0xFF))) { IsLastReadSuccessful = (_readBuff[0] != 0) || (_readBuff[2] != 0); } else { IsLastReadSuccessful = false; } }