private static void ThrowExceptionIfPortIsUsed(GrovePort port) { if (_sensors.Any(sen => sen.Port == port)) { throw new Exception("The port '" + port + "' is already used."); } }
/// <summary> /// Read a digital pin, equivalent of digitalRead on Arduino /// </summary> /// <param name="pin">The GroovePi pin to read</param> /// <returns>Returns the level either High or Low</returns> public PinValue DigitalRead(GrovePort pin) { WriteCommand(GrovePiCommand.DigitalRead, pin, 0, 0); byte tries = 0; IOException innerEx = null; // When writing/reading to the I2C port, GrovePi doesn't respond on time in some cases // So we wait a little bit before retrying // In most cases, the I2C read/write can go thru without waiting while (tries < MaxRetries) { try { return((PinValue)_i2cDevice.ReadByte()); } catch (IOException ex) { // Give it another try innerEx = ex; tries++; Thread.Sleep(10); } } throw new IOException($"{nameof(DigitalRead)}: Failed to read byte with command {GrovePiCommand.DigitalRead}", innerEx); }
/// <summary> /// Write a GrovePi command /// </summary> /// <param name="command">The GrovePi command</param> /// <param name="pin">The pin to write the command</param> /// <param name="param1">First parameter</param> /// <param name="param2">Second parameter</param> public void WriteCommand(GrovePiCommand command, GrovePort pin, byte param1, byte param2) { Span <byte> outArray = stackalloc byte[4] { (byte)command, (byte)(pin), param1, param2 }; byte tries = 0; IOException innerEx = null; // When writing/reading to the I2C port, GrovePi doesn't respond on time in some cases // So we wait a little bit before retrying // In most cases, the I2C read/write can go thru without waiting while (tries < MaxRetries) { try { _i2cDevice.Write(outArray); return; } catch (IOException ex) { // Give it another try innerEx = ex; tries++; Thread.Sleep(10); } } throw new IOException($"{nameof(WriteCommand)}: Failed to write command {command}", innerEx); }
/// <summary> /// Conduct an I2C transaction /// </summary> /// <param name="port">The grove port. GROVE_1 or GROVE_2</param> /// <param name="addr">The I2C address of the slave to be addressed.</param> /// <param name="arrayToSend">An array of bytes to send.</param> /// <param name="inBytes">The number of bytes to read.</param> /// <returns>Returns a byte array with what has been read from the I2C element</returns> public byte[] GroveI2cTransfer(GrovePort port, byte addr, byte[] arrayToSend, byte inBytes = 0) { Stopwatch stopwatch = Stopwatch.StartNew(); var timeout = stopwatch.ElapsedMilliseconds + 5; while (true) { try { GroveI2cStart(port, addr, arrayToSend, inBytes); break; } catch (IOException ex) { if (stopwatch.ElapsedMilliseconds > timeout) { throw new IOException($"{nameof(GroveI2cTransfer)} error: timeout while transfering the I2C data", ex); } } } // Wait for the sensors to be read // In theory 115µs per byte sent int towait = 0; if (arrayToSend != null) { if (arrayToSend.Length != 0) { towait += 1 + arrayToSend.Length; } } if (inBytes > 0) { towait += 1 + inBytes; } timeout += (int)(0.115 * towait); // but make sure we wait a minimum of 1 ms if (towait > 0) { timeout = Math.Clamp(timeout, 1, 32); Thread.Sleep((int)timeout); } timeout = stopwatch.ElapsedMilliseconds + 5; while (true) { try { return(GetGroveValue(port)); } catch (IOException ex) { throw new IOException($"{nameof(GroveI2cTransfer)} error: timeout while transfering the I2C data", ex); } } }
/// <summary> /// Constructor for the Grove sensor part of the main GoPiGo3 class /// </summary> /// <param name="GrovePort">The Grove port, either Grove1 or Grove2</param> public GroveSensor(GrovePort GrovePort) { if ((GrovePort != GrovePort.Grove1) && (GrovePort != GrovePort.Grove2)) { throw new ArgumentException("Grove sensor can only be on Port 1 or Port 2"); } SensorType = GroveSensorType.None; Port = GrovePort; }
/// <summary> /// UltrasonicSensor constructor /// </summary> /// <param name="grovePi">The GrovePi class</param> /// <param name="port">The grove Port, need to be in the list of SupportedPorts</param> public UltrasonicSensor(GrovePi grovePi, GrovePort port) { if (!SupportedPorts.Contains(port)) { throw new ArgumentException($"Grove port {port} not supported.", nameof(port)); } _grovePi = grovePi; _port = port; }
protected GrovePiSensor(string name, string unit, int sensorId, SensorType sensorType, GrovePort port, bool rgbDisplay) { Name = name; Unit = unit; SensorId = sensorId; SensorType = sensorType; Port = port; RgbDisplay = rgbDisplay; }
/// <summary> /// Set grove output pin PWM /// </summary> /// <param name="port">The Grove Pin, can be any combination of Grove1Pin1, Grove1Pin2, Grove2Pin1 and/or Grove2Pin2</param> /// <param name="duty">The PWM duty cycle in percent from 0.0 to 100.0, 1 floating point precision</param> public void SetGrovePwmDuty(GrovePort port, double duty) { duty = Math.Clamp(duty, (byte)0, (byte)100); var duty_value = (UInt16)(duty * 10.0); byte[] outArray = { SpiAddress, (byte)SpiMessageType.SetGrovePwmDuty, (byte)port, (byte)((duty_value >> 8) & 0xFF), (byte)(duty_value & 0xFF) }; SpiTransferArray(outArray); }
/// <summary> /// AnalogSensor constructor /// </summary> /// <param name="grovePi">The GrovePi class</param> /// <param name="port">The grove Port, need to be in the list of SupportedPorts</param> public AnalogSensor(GrovePi grovePi, GrovePort port) { if (!SupportedPorts.Contains(port)) { throw new ArgumentException($"Grove port {port} not supported.", nameof(port)); } _grovePi = grovePi; _port = port; _grovePi.PinMode(_port, PinMode.Input); }
/// <summary> /// /// </summary> /// <param name="goPiGo">The GoPiGo3 class</param> /// <param name="port">The Grove Port, need to be in the list of SupportedPorts</param> public UltraSonicSensor(GoPiGo goPiGo, GrovePort port) { if (!SupportedPorts.Contains(port)) { throw new ArgumentException($"Error: Grove Port not supported"); } _goPiGo = goPiGo; Port = port; _goPiGo.SetGroveType(port, GroveSensorType.Ultrasonic); }
/// <summary> /// Read data from GrovePi /// </summary> /// <param name="command">The GrovePi command</param> /// <param name="pin">The pin to read</param> /// <returns></returns> public byte[] ReadCommand(GrovePiCommand command, GrovePort pin) { int numberBytesToRead = 0; switch (command) { case GrovePiCommand.DigitalRead: numberBytesToRead = 1; break; case GrovePiCommand.AnalogRead: case GrovePiCommand.UltrasonicRead: case GrovePiCommand.LetBarGet: numberBytesToRead = 3; break; case GrovePiCommand.Version: numberBytesToRead = 4; break; case GrovePiCommand.DhtTemp: numberBytesToRead = 9; break; // No other commands are for read default: return(null); } byte[] outArray = new byte[numberBytesToRead]; byte tries = 0; IOException innerEx = null; // When writing/reading the I2C port, GrovePi doesn't respond on time in some cases // So we wait a little bit before retrying // In most cases, the I2C read/write can go thru without waiting while (tries < MaxRetries) { try { _i2cDevice.Read(outArray); return(outArray); } catch (IOException ex) { // Give it another try innerEx = ex; tries++; Thread.Sleep(10); } } throw new IOException($"{nameof(ReadCommand)}: Failed to write command {command}", innerEx); }
/// <summary> /// PwmOutput constructor /// </summary> /// <param name="grovePi">The GrovePi class</param> /// <param name="port">The grove Port, need to be in the list of SupportedPorts</param> public PwmOutput(GrovePi grovePi, GrovePort port) { if (!SupportedPorts.Contains(port)) { throw new ArgumentException($"Grove port {port} not supported.", nameof(port)); } _grovePi = grovePi; _port = port; _grovePi.PinMode(_port, PinMode.Output); Value = 0; }
/// <summary> /// DigitalInput constructor /// </summary> /// <param name="grovePi">The GrovePi class</param> /// <param name="port">The grove Port, need to be in the list of SupportedPorts</param> public DigitalInput(GrovePi grovePi, GrovePort port) { if (!SupportedPorts.Contains(port)) { throw new ArgumentException(nameof(port), "Grove port not supported"); } _grovePi = grovePi; Port = port; _grovePi.PinMode(Port, PinMode.Input); }
public static ISensor CreateSensor(SensorType sensorType, GrovePort port, string name, int sensorId, bool rgbDisplay) { #if DEBUG var fakeSensor = new GrovePiFakeSensor(sensorType, name, sensorId); refresher.AddSensor(fakeSensor); return(fakeSensor); #endif var grovePi = GrovePiDevice.GetGrovePi(); switch (sensorType) { case SensorType.AnalogSensor: ThrowExceptionIfPortIsUsed(port); var analogSensor = new GrovePiAnalogSensor(new AnalogSensor(grovePi, port), name, sensorId, port, rgbDisplay); refresher.AddSensor(analogSensor); _sensors.Add(analogSensor); return(analogSensor); case SensorType.DhtTemperatureSensor: return(GetDhtSensor(grovePi, port, SensorType.DhtTemperatureSensor, name, sensorId, rgbDisplay)); case SensorType.DhtHumiditySensor: return(GetDhtSensor(grovePi, port, SensorType.DhtHumiditySensor, name, sensorId, rgbDisplay)); case SensorType.PotentiometerSensor: ThrowExceptionIfPortIsUsed(port); var potentiometreSensor = new GrovePiAnalogPotentiometer(new PotentiometerSensor(grovePi, port), name, sensorId, port, rgbDisplay); refresher.AddSensor(potentiometreSensor); _sensors.Add(potentiometreSensor); return(potentiometreSensor); case SensorType.UltrasonicSensor: ThrowExceptionIfPortIsUsed(port); var ultrasonicSensor = new GrovePiAnalogUltrasonic(new UltrasonicSensor(grovePi, port), name, sensorId, port, rgbDisplay); refresher.AddSensor(ultrasonicSensor); _sensors.Add(ultrasonicSensor); return(ultrasonicSensor); case SensorType.GrooveTemperartureSensor: ThrowExceptionIfPortIsUsed(port); var temperatureSensor = new GrovePiAnalogTemperature(new GroveTemperatureSensor(grovePi, port), name, sensorId, port, rgbDisplay); refresher.AddSensor(temperatureSensor); _sensors.Add(temperatureSensor); return(temperatureSensor); case SensorType.LightSensor: case SensorType.SoundSensor: default: throw new Exception("GrovePiSensor: unsupported sensor type: " + sensorType); } }
/// <summary> /// LedBar constructor /// </summary> /// <param name="grovePi">The GrovePi class</param> /// <param name="port">The grove Port, need to be in the list of SupportedPorts</param> /// <param name="orientation">Orientation, Green to red is default</param> public LedBar(GrovePi grovePi, GrovePort port, LedBarOrientation orientation) { if (!SupportedPorts.Contains(port)) { throw new ArgumentException($"Grove port {port} not supported.", nameof(port)); } _grovePi = grovePi; _port = port; _orientation = orientation; _grovePi.WriteCommand(GrovePiCommand.LedBarInitialization, port, (byte)_orientation, 0); _level = 0; }
/// <summary> /// /// </summary> /// <param name="goPiGo">The GoPiGo3 class</param> /// <param name="port">The Grove Port, need to be in the list of SupportedPorts</param> public DigitalInput(GoPiGo goPiGo, GrovePort port) { if (!SupportedPorts.Contains(port)) { throw new ArgumentException($"Error: Grove Port not supported"); } _goPiGo = goPiGo; Port = port; _goPiGo.SetGroveType(port, GroveSensorType.Custom); _mode = (port == GrovePort.Grove1) ? GrovePort.Grove1Pin1 : GrovePort.Grove2Pin1; _goPiGo.SetGroveMode(_mode, GroveInputOutput.InputDigital); }
/// <summary> /// Initialize the DHT Sensor class /// </summary> /// <param name="grovePi">The GrovePi class</param> /// <param name="port">The grove Port, need to be in the list of SupportedPorts</param> /// <param name="dhtType">The DHT type</param> public DhtSensor(GrovePi grovePi, GrovePort port, DhtType dhtType) { if (!SupportedPorts.Contains(port)) { throw new ArgumentException($"Grove port {port} not supported.", nameof(port)); } _grovePi = grovePi; DhtType = dhtType; _port = port; // Ask for the temperature so we will have one in cache _grovePi.WriteCommand(GrovePiCommand.DhtTemp, _port, (byte)DhtType, 0); }
/// <summary> /// Constructor for the generic Analog Sensor /// </summary> /// <param name="goPiGo">The GoPiGo3 class</param> /// <param name="port">The Grove Port, need to be in the list of SupportedPorts</param> public AnalogSensor(GoPiGo goPiGo, GrovePort port) { if (!SupportedPorts.Contains(port)) { throw new ArgumentException(nameof(port), "Grove port not supported"); } _goPiGo = goPiGo; Port = port; _goPiGo.SetGroveType(port, GroveSensorType.Custom); _goPiGo.SetGroveMode(port, GroveInputOutput.InputAnalog); _mode = Port == GrovePort.Grove1 ? GrovePort.Grove1Pin1 : GrovePort.Grove2Pin1; }
/// <summary> /// Read an analog value on a pin, equivalent of analogRead on Arduino /// </summary> /// <param name="pin">The GroovePi pin to read</param> /// <returns></returns> public int AnalogRead(GrovePort pin) { WriteCommand(GrovePiCommand.AnalogRead, pin, 0, 0); try { var inArray = ReadCommand(GrovePiCommand.AnalogRead, pin); return(BinaryPrimitives.ReadInt16BigEndian(inArray.AsSpan(1, 2))); } catch (IOException) { return(-1); } }
/// <summary> /// Read an analog value on a pin, equivalent of analogRead on Arduino /// </summary> /// <param name="pin">The GroovePi pin to read</param> /// <returns></returns> public int AnalogRead(GrovePort pin) { WriteCommand(GrovePiCommand.AnalogRead, pin, 0, 0); try { var inArray = ReadCommand(GrovePiCommand.AnalogRead, pin); return(BinaryPrimitives.ReadInt16BigEndian(inArray.AsSpan(1, 2))); } catch (IOException ex) { Console.WriteLine("Error at reading analog channel : " + ex.Message); return(-1); } }
/// <summary> /// Set grove type /// </summary> /// <param name="port">The grove port(s). Grove1 and/or Grove2</param> /// <param name="type">The grove device type, refer to GroveSensorType</param> public void SetGroveType(GrovePort port, GroveSensorType type) { if ((port == GrovePort.Grove1) || (port == GrovePort.Both)) { GroveSensor[0].SensorType = type; } if ((port == GrovePort.Grove2) || (port == GrovePort.Both)) { GroveSensor[1].SensorType = type; } byte[] outArray = { SpiAddress, (byte)SpiMessageType.SetGroveType, (byte)port, (byte)type }; SpiTransferArray(outArray); }
/// <summary> /// /// </summary> /// <param name="goPiGo">The GoPiGo3 class</param> /// <param name="port">The Grove Port, need to be in the list of SupportedPorts</param> /// <param name="duty">The PWM duty to use to generate the sound from 0 to 100</param> public Buzzer(GoPiGo goPiGo, GrovePort port, byte duty) { if (!SupportedPorts.Contains(port)) { throw new ArgumentException($"Error: Grove Port not supported"); } _goPiGo = goPiGo; Port = port; _goPiGo.SetGroveType(port, GroveSensorType.Custom); _mode = (port == GrovePort.Grove1) ? GrovePort.Grove1Pin1 : GrovePort.Grove2Pin1; _goPiGo.SetGroveMode(_mode, GroveInputOutput.OutputPwm); Duty = duty; Value = 24_000; //The default value Stop(); }
/// <summary> /// Start an I2C transaction /// </summary> /// <param name="port">The Grove Port, one at the time Grove1 or Grove2</param> /// <param name="addr">The I2C address of the slave to be addressed.</param> /// <param name="arrayToSend">An array of bytes to send.</param> /// <param name="inBytes">The number of bytes to read.</param> public void GroveI2cStart(GrovePort port, byte addr, byte[] arrayToSend, byte inBytes = 0) { SpiMessageType message_type; byte port_index; if (port == GrovePort.Grove1) { message_type = SpiMessageType.StartGrove1I2c; port_index = 0; } else if (port == GrovePort.Grove2) { message_type = SpiMessageType.StartGrove2I2c; port_index = 1; } else { throw new ArgumentException($"{nameof(GroveI2cStart)} error: Port unsupported. Must be either Grove 1 or Grove 2."); } var address = ((addr & 0x7F) << 1); if (inBytes > GroveI2cLengthLimit) { throw new ArgumentException($"{nameof(GroveI2cStart)} error: Read length error. Up to {GroveI2cLengthLimit} bytes can be read in a single transaction."); } if (arrayToSend.Length > GroveI2cLengthLimit) { throw new ArgumentException($"{nameof(GroveI2cStart)} error:Write length error. Up to {GroveI2cLengthLimit} bytes can be written in a single transaction."); } byte[] outArray = { SpiAddress, (byte)message_type, (byte)address, inBytes, (byte)arrayToSend.Length }; Array.Resize(ref outArray, outArray.Length + arrayToSend.Length); Array.Copy(arrayToSend, 0, outArray, outArray.Length - arrayToSend.Length, arrayToSend.Length); var reply = SpiTransferArray(outArray); GroveSensor[port_index].I2cDataLength = inBytes; if (reply[3] != SpiCorrectDataReturned) { throw new IOException($"{nameof(GroveI2cStart)} error: No SPI response"); } if (reply[4] != I2cCorrectData) { throw new IOException($"{nameof(GroveI2cStart)} error: Not ready to start I2C transaction"); } }
/// <summary> /// Get a grove input pin analog voltage /// </summary> /// <param name="port">The Grove Pin, one at the time Grove1Pin1, Grove1Pin2, Grove2Pin1 or Grove2Pin2</param> /// <returns>Returns the voltage in V</returns> public double GetGroveVoltage(GrovePort port) { SpiMessageType message_type; if (port == GrovePort.Grove1Pin1) { message_type = SpiMessageType.GetGrove1Pin1Voltage; } else if (port == GrovePort.Grove1Pin2) { message_type = SpiMessageType.GetGrove1Pin2Voltage; } else if (port == GrovePort.Grove2Pin1) { message_type = SpiMessageType.GetGrove2Pin1Voltage; } else if (port == GrovePort.Grove2Pin2) { message_type = SpiMessageType.GetGrove2Pin2Voltage; } else { throw new ArgumentException($"{nameof(GetGroveVoltage)} error: Pin(s) unsupported. Must get one at a time."); } byte[] outArray = { SpiAddress, (byte)message_type, 0, 0, 0, 0, 0 }; var reply = SpiTransferArray(outArray); if (reply[3] == SpiCorrectDataReturned) { if (reply[4] == (byte)GroveSensorState.ValidData) { return(((reply[5] << 8) + reply[6]) / 1000.0); } else { throw new IOException($"{nameof(GetGroveVoltage)} error: Invalid value"); } } else { throw new IOException($"{nameof(GetGroveVoltage)} error: Grove type mismatch"); } }
/// <summary> /// Get a grove port value /// </summary> /// <param name="port">The Grove Port, one at the time Grove1 or Grove2</param> /// <returns>Returns a byte array containing the read data</returns> public byte[] GetGroveValue(GrovePort port) { string ErrorSPI = $"{nameof(GetGroveValue)} error: No SPI response"; string ErrorInvalidValue = $"{nameof(GetGroveValue)} error: Invalid value"; SpiMessageType message_type; byte port_index; if (port == GrovePort.Grove1) { message_type = SpiMessageType.GetGrove1Value; port_index = 0; } else if (port == GrovePort.Grove2) { message_type = SpiMessageType.GetGrove2Value; port_index = 1; } else { throw new ArgumentException($"{nameof(GroveI2cStart)} error: Port unsupported. Must be either Grove 1 or Grove 2."); } byte[] outArray = null; byte[] reply = null; switch (GroveSensor[port_index].SensorType) { case GroveSensorType.InfraredRemote: outArray = new byte[] { SpiAddress, (byte)message_type, 0, 0, 0, 0, 0 }; reply = SpiTransferArray(outArray); if (reply[3] == SpiCorrectDataReturned) { if ((reply[4] == (byte)GroveSensor[port_index].SensorType) && (reply[5] == I2cCorrectData)) { return new byte[] { reply[6] } } } ; else { throw new IOException(ErrorInvalidValue); }
/// <summary> /// Read data from GrovePi /// </summary> /// <param name="command">The GrovePi command</param> /// <param name="pin">The pin to read</param> /// <returns></returns> public byte[]? ReadCommand(GrovePiCommand command, GrovePort pin) { int numberBytesToRead = command switch { GrovePiCommand.DigitalRead => 1, GrovePiCommand.AnalogRead or GrovePiCommand.UltrasonicRead or GrovePiCommand.LetBarGet => 3, GrovePiCommand.Version => 4, GrovePiCommand.DhtTemp => 9, _ => 0, }; if (numberBytesToRead == 0) { return(null); } byte[] outArray = new byte[numberBytesToRead]; byte tries = 0; IOException?innerEx = null; // When writing/reading the I2C port, GrovePi doesn't respond on time in some cases // So we wait a little bit before retrying // In most cases, the I2C read/write can go thru without waiting while (tries < MaxRetries) { try { _i2cDevice.Read(outArray); return(outArray); } catch (IOException ex) { // Give it another try innerEx = ex; tries++; Thread.Sleep(10); } } throw new IOException($"{nameof(ReadCommand)}: Failed to write command {command}", innerEx); }
/// <summary> /// Led constructor /// </summary> /// <param name="grovePi">The GrovePi class</param> /// <param name="port">The grove Port, need to be in the list of SupportedPorts</param> public Led(GrovePi grovePi, GrovePort port) : base(grovePi, port) { }
/// <summary> /// Constructor for the LedPwm class /// </summary> /// <param name="goPiGo">The GoPiGo3 class</param> /// <param name="port">The Grove Port, need to be in the list of SupportedPorts</param> /// <param name="duty">The duty cycle for the led, 100 = full bright, 0 = full dark</param> public LedPwm(GoPiGo goPiGo, GrovePort port, byte duty) : base(goPiGo, port, duty) { }
/// <summary> /// Constructor for the LedPwm class /// </summary> /// <param name="goPiGo">The GoPiGo3 class</param> /// <param name="port">The Grove Port, need to be in the list of SupportedPorts</param> public LedPwm(GoPiGo goPiGo, GrovePort port) : this(goPiGo, port, 0) { }
/// <summary> /// Relay constructor /// </summary> /// <param name="grovePi">The GrovePi class</param> /// <param name="port">The grove Port, need to be in the list of SupportedPorts</param> /// <param name="inverted">If inverted, the relay is on when output is low and off when output is high</param> public Relay(GrovePi grovePi, GrovePort port, bool inverted) : base(grovePi, port) { IsInverted = inverted; }