We are implementing temperature reading using PT100 (3 wires) and Raspberry 4 with .NET 6.
We are able to read the temperature using as reference this article.
SpiConnectionSettings settings = new(0, 0)
{
ClockFrequency = Max31865.SpiClockFrequency,
Mode = Max31865.SpiMode1,
DataFlow = Max31865.SpiDataFlow
};
using (SpiDevice device = SpiDevice.Create(settings))
{
using (Max31865 sensor = new(device, PlatinumResistanceThermometerType.Pt100, ResistanceTemperatureDetectorWires.ThreeWire, ElectricResistance.FromOhms(430)))
{
while (true)
{
Console.WriteLine(sensor.Faults.ToString());
Thread.Sleep(1000);
}
}
}
Actually to work the script need the CS pin of Max31865 connected to pin 12 of raspberry GPIO. We would like to connect more than one Max31865 in order to record some temperatures from different Pt100.
How we can specify the right/specific GPIO port in the C# script for every Max31865 CS output pin? Actually seems there are no properties to change that pin but this feature will permit us to read more than one sensor.
The easiest thing is to do the CS handling manually, as automatic CS handling with multiple devices on the same bus is not fully implemented for the Raspberry Pi.
var controller = new GpioController();
controller.OpenPin(CsPinOfSensor1);
controller.OpenPin(CsPinOfSensor2);
controller.Write(CsPinOfSensor1, PinValue.Low); // CS is low-active!
controller.Write(CsPinOfSensor2, PinValue.High);
using Max31865 sensor1 = new(device, PlatinumResistanceThermometerType.Pt100, ResistanceTemperatureDetectorWires.ThreeWire, ElectricResistance.FromOhms(430));
controller.Write(CsPinOfSensor1, PinValue.High); // CS is low-active!
controller.Write(CsPinOfSensor2, PinValue.Low);
using Max31865 sensor2 = new(device, PlatinumResistanceThermometerType.Pt100, ResistanceTemperatureDetectorWires.ThreeWire, ElectricResistance.FromOhms(430));
{
while (true)
{
controller.Write(CsPinOfSensor1, PinValue.Low); // CS is low-active!
controller.Write(CsPinOfSensor2, PinValue.High);
Console.WriteLine(sensor1.Temperature.ToString());
controller.Write(CsPinOfSensor1, PinValue.High);
controller.Write(CsPinOfSensor2, PinValue.Low);
Console.WriteLine(sensor2.Temperature.ToString());
Thread.Sleep(1000);
}
}
There are multiple pins reserved as CS pins for each SPI bus, but I honestly do not even know how they would need to be selected at the OS level.
Alternatively, you can of course also connect the second sensor to a second SPI bus. The Raspberry Pi4 has a total of 6 SPI channels.
Related
I'm attempting to use HidSharp to read reports from a Vendor USB HID device using the below code, but I never manage to poll any input.
var device = DeviceList.Local.GetHidDevices().Where(d => /* My Vendor stuff */).First();
var deviceDescriptor = device.GetReportDescriptor();
if (device.TryOpen(out var inputStream))
{
var inputReport = deviceDescriptor.InputReports.First();
var inputReportParser = inputReport.DeviceItem.CreateDeviceItemInputParser();
var inputReportReciever = deviceDescriptor.CreateHidDeviceInputReceiver();
var inputReportBuffer = new byte[inputReport.Length];
while (inputReportReciever.IsRunning)
{
if (inputReportReciever.WaitHandle.WaitOne(10000))
{
while (inputReportReciever.TryRead(inputReportBuffer, 0, out var report))
{
if (inputReportParser.TryParseReport(inputReportBuffer, 0, report))
{
if (inputReportParser.HasChanged)
{
var data = inputReportParser.GetValue(inputReportParser.GetNextChangedIndex());
Console.WriteLine("Data: " + data.ToString());
}
}
}
}
}
}
I can see in the device manager that the device is recognized as an HID USB device, and Wireshark shows that I am sending periodic reports like so:
(I'm a java developer so I'm really struggling with Visual Studio) I can't step in to the library to see what's happening, but there are no errors being thrown and the 'test' included with the project parses reports from at least one other USB device (a headset) so the library seems ok. I'm not at all sure how to proceed with debugging further. Any direction on this library or USB is appreciated.
For a project, I have to communicate with a Raspberry Pi Zero from a UWP-APP via TCP. Because both, the Raspberry and the computer with the interface, have got a private IP, I have to use a server to forward messages from one client to the other one. This part already works but now my problem is that I have to implement video streaming from the Raspberry to the UWP-APP.
Because my partner is in charge of creating and designing the UWP-APP, I have made myself a little Test-Interface with WindowsForms. I have tried several techniques like Netcat the video output over the server to the client or direct TCP-streaming with raspivid, but the best solution so far is the one I found in this project here. But instead of using the Eneter.Messaging-library I use my own class for communication with TcpClients.
I use mono to run my C# script on the Raspberry and the code to stream the Video looks like this:
while (true)
{
//Wait with streaming until the Interface is connected
while (!RemoteDeviceConnected || VideoStreamPaused)
{
Thread.Sleep(500);
}
//Check if Raspivid-Process is already running
if(!Array.Exists(Process.GetProcesses(), p => p.ProcessName.Contains("raspivid")))
raspivid.Start();
Thread.Sleep(2000);
VideoData = new byte[VideoDataLength];
try
{
while (await raspivid.StandardOutput.BaseStream.ReadAsync(VideoData, 0, VideoDataLength) != -1 && !VideoChannelToken.IsCancellationRequested && RemoteDeviceConnected && !VideoStreamPaused)
{
// Send captured data to connected clients.
VideoConnection.SendByteArray(VideoData, VideoDataLength);
}
raspivid.Kill();
Console.WriteLine("Raspivid killed");
}
catch(ObjectDisposedException)
{
}
}
Basically, this method just reads the h264 data from the Standard-Output-Stream of the raspivid process in chunks and sends it to the server.
The next method runs on the server and just forwards the byte array to the connected interface-client.
while (RCVVideo[id].Connected)
{
await RCVVideo[id].stream.ReadAsync(VideoData, 0, VideoDataLength);
if (IFVideo[id] != null && IFVideo[id].Connected == true)
{
IFVideo[id].SendByteArray(VideoData, VideoDataLength);
}
}
SendByteArray() uses the NetworkStream.Write() Method.
On the interface, I write the received byte[] to a named pipe, to which the VLC-Control connects to:
while (VideoConnection.Connected)
{
await VideoConnection.stream.ReadAsync(VideoData, 0, VideoDataLength);
if(VideoPipe.IsConnected)
{
VideoPipe.Write(VideoData, 0, VideoDataLength);
}
}
Following code initializes the pipe-server:
// Open pipe that will be read by VLC.
VideoPipe = new NamedPipeServerStream(#"\raspipipe",
PipeDirection.Out, 1,
PipeTransmissionMode.Byte,
PipeOptions.WriteThrough, 0, 10000);
And for VLC:
LibVLC libVLC = new LibVLC();
videoView1.MediaPlayer = new MediaPlayer(libVLC);
videoView1.MediaPlayer.Play(new Media(libVLC, #"stream/h264://\\\.\pipe\raspipipe", FromType.FromLocation));
videoView1.MediaPlayer.EnableHardwareDecoding = true;
videoView1.MediaPlayer.FileCaching = 0;
videoView1.MediaPlayer.NetworkCaching = 300;
This works fine on the Windowsforms-App and I can get the delay down to 2 or 3 seconds (It should be better in the end but it is acceptable). But on the UWP-App I can't get it to work even after adding /LOCAL/ to the pipe name. It shows that the VLC-Control connects to the pipe, and I can see that data is written to the pipe but it doesn't display video.
So my question is:
How can I get this to work with the VLC-Control (LibVLCSharp) in UWP? Am I missing something fundamental?
Or is there even a better way to stream the video in this case?
I have researched a bit on the UWP-MediaPlayerElement to but I can't find a way to get my byte[] into it.
First of all, thank you for your quick responses and interesting ideas!
I took a look into Desktop Bridge but it is not really what I wanted, because my colleague has already put in a lot of effort to design the UWP-APP and my Windows-Form is just a botch to try things out.
But the thing that really worked for me was StreamMediaInput . I have no idea how I missed this before. This way I just passed my NetworkStream directly to the MediaPlayer without using a Named-Pipe.
LibVLC libVLC = new LibVLC();
videoView1.MediaPlayer = new MediaPlayer(libVLC);
Media streamMedia = new Media(libVLC, new StreamMediaInput(Client.Channels.VideoConnection.stream), ":demux=h264");
videoView1.MediaPlayer.EnableHardwareDecoding = true;
videoView1.MediaPlayer.FileCaching = 0;
videoView1.MediaPlayer.NetworkCaching = 500;
videoView1.MediaPlayer.Play(streamMedia);
This solution is now working for me both, in UWP and in Windows-Forms.
I'm trying to get a tow-way communication between a c# application and a python script that c# will call.
I have some input channels in c# that changes constantly at high frequency (5000-1000 data/s) for let's say a minute. On every change of those inputs,results are calculated and assigned to output variables. What i'm trying to do is to move the logic to a python script. For instance:
Inputs: double x,y
Output: double z
So the pyhton script should be capable of read the inputs, perform the logic and write the results at a symilar frequency.
Any recomendations? Has anyone did anything similar before?
First I tried to call the script on every change and read the console output. But the code in the script is not as simple as z=x*y and variables that store values are required in the pyhon script. For example, the script mught want to save the maximum value of x and y reached.
I had a look to ZeroMQ library for the communication, not sure how to use it though.
Here is a solution:
Simple C# program: client which sends data and receive
using System;
using ZeroMQ;
namespace ZeroMQ_Client
{
class Program
{
static void Main(string[] args)
{
using (var requester = new ZSocket(ZSocketType.REQ))
{
// Connect
requester.Connect("tcp://127.0.0.1:5555");
for (int n = 0; n < 10; ++n)
{
string requestText = "Hello";
Console.Write("Sending {0}...", requestText);
// Send
requester.Send(new ZFrame(requestText));
// Receive
using (ZFrame reply = requester.ReceiveFrame())
{
Console.WriteLine(" Received: {0} {1}!", requestText, reply.ReadString());
}
}
}
}
}
}
python program, you have to install pyzmq:
#
# Hello World server in Python
# Binds REP socket to tcp://*:5555
# Expects b"Hello" from client, replies with b"World"
#
import time
import zmq
context = zmq.Context()
socket = context.socket(zmq.REP)
socket.bind("tcp://*:5555")
while True:
# Wait for next request from client
message = socket.recv()
print("Received request: %s" % message)
# Do some 'work'
time.sleep(1)
# Send reply back to client
socket.send(b"World")
I configured the Raspberry PI 3 with Q4XTBLAF300-Q8 this sensor and it is connected to GPIO5 for reading the value based on whenever something is in range of the sensor the input will be high. When it is out of range, the sensor will be low. But I don’t know to how to write the code for reading the value from GPIO5 pin based on Q4XTBLAF300-Q8 this sensor status.
So, can you please tell me how to read value from GPIO5 pin of Raspberry PI 3?
Here is a code snippet you can reference:
using Windows.Devices.Gpio;
private const int GPIO_PIN_NUM = 5;
//Initialize gpio
pin = GpioController.GetDefault().OpenPin(GPIO_PIN_NUM);
pin.SetDriveMode(GpioPinDriveMode.Input);
//Read gpio value
var pinValue = pin.Read();
For controlling GPIO on the raspberry pi with windows 10 iot core you can check this tutorial.
More samples are here.
using Windows.Devices.Gpio;
public void GPIO()
{
// Get the default GPIO controller on the system
GpioController gpio = GpioController.GetDefault();
if (gpio == null)
return; // GPIO not available on this system
// Open GPIO 5
using (GpioPin pin = gpio.OpenPin(5))
{
// Latch HIGH value first. This ensures a default value when the pin is set as output
pin.Write(GpioPinValue.High);
// Set the IO direction as output
pin.SetDriveMode(GpioPinDriveMode.Output);
} // Close pin - will revert to its power-on state
}
I'm attempting to write a C# library which looks at all available USB serial ports on a Raspberry Pi so that I can enumerate, identify and communicate with a set of Arduinos connected to the Pi via a USB hub.
I am able to make this work on my windows machine (several Arduinos connected to my desktop computer) and have even been able to make it work on my Pi however, I am struggling to understand how to generalize the fix.
If I attempt to run the program by itself on the Pi, I am able to open the serial port and send data however, I cannot receive anything from the Arduinos: I get timeout exceptions. I understand that Mono's implementation of SerialPort is limited and I must use SerialPort.ReadByte() instead of Readline() and the data received events (my solution is based on code from HowToSystemIOPorts). My Serial port enumeration is using a method outlined in another stack exchange response here.
My timeout is currently set to 4 seconds, which is several orders of magnitude longer than I expect to receive the message.
After a lot of googling, I came across mention of using minicom to initialize the serial port here, which to my surprise allowed me to receive data from the Arduino. The biggest drawback is that I need to initialize the port using minicom and leave the process opening each time I boot the Pi. I also can't seem to figure out how to make this work with multiple Arduinos.
Here is what I have tried so far:
Updated the Pi firmware and software to their latest versions
Attempted to use both an Arduino MEGA 2560 R3 and Arduino UNO
Changed the owner of the tty* ports (ttyACM0 and ttyUSB0 in this case) to both my user and group
Successfully configured the port via minicom, left the process running and start the program and read/wrote data. A manual process which only seems to work for one Arduino at a time
Successfully run the program in Windows without fault
Verified the Arduinos are recognized by the Pi running "dmesg | grep tty"
Here is what I hope to solve:
Automatic setup/initialization of the Arduino serial ports. Whether through a shell script run before the main program or within Mono code so that the code below can run as intended.
Here is my connection code:
public bool StartArduinoComms()
{
string[] ports = GetPortNames();
foreach (string port in ports)
{
mLogger.LogMessage(ProsthesisCore.Utility.Logger.LoggerChannels.Arduino, string.Format("Found serial port {0}", port));
}
bool foundCorrectArduino = false;
var idPacket = new ArduinoMessageBase();
idPacket.ID = ArduinoMessageValues.kIdentifyValue;
string jsonOutput = Newtonsoft.Json.JsonConvert.SerializeObject(idPacket);
foreach (string port in ports)
{
SerialPort serialPort = new SerialPort(port, kArduinoCommsBaudRate);
serialPort.Parity = Parity.None;
serialPort.DataBits = 8;
serialPort.StopBits = StopBits.One;
//Only check unopened ports
if (!serialPort.IsOpen)
{
serialPort.Open();
//Disable telemtry just incase
var toggle = new { ID = ArduinoMessageValues.kTelemetryEnableValue, EN = false };
string disableTelem = Newtonsoft.Json.JsonConvert.SerializeObject(toggle);
serialPort.Write(disableTelem);
//Discard any built up data
serialPort.DiscardInBuffer();
serialPort.Write(jsonOutput);
serialPort.ReadTimeout = kIDTimeoutMilliseconds;
string response = string.Empty;
for (int i = 0; i < kNumRetries; ++i)
{
try
{
//This is guaranteed to timeout if not configured through minicom
response = ReadLine(serialPort);
break;
}
//Catch case where the serial port is unavailable. MOve to next port
catch (TimeoutException)
{
continue;
}
}
if (!string.IsNullOrEmpty(response))
{
//Perform response validation
}
else
{
//Got no response
}
if (!foundCorrectArduino)
{
serialPort.Close();
}
}
}
return foundCorrectArduino;
}
/// <summary>
/// From https://stackoverflow.com/questions/434494/serial-port-rs232-in-mono-for-multiple-platforms
/// </summary>
/// <returns></returns>
private static string[] GetPortNames()
{
int p = (int)Environment.OSVersion.Platform;
List<string> serial_ports = new List<string>();
// Are we on Unix?
if (p == 4 || p == 128 || p == 6)
{
string[] ttys = System.IO.Directory.GetFiles("/dev/", "tty*");
foreach (string dev in ttys)
{
//Arduino MEGAs show up as ttyACM due to their different USB<->RS232 chips
if (dev.StartsWith("/dev/ttyS") || dev.StartsWith("/dev/ttyUSB") || dev.StartsWith("/dev/ttyACM"))
{
serial_ports.Add(dev);
}
}
}
else
{
serial_ports.AddRange(SerialPort.GetPortNames());
}
return serial_ports.ToArray();
}
Have a look at stty command. It will let you set/read teminal settings
http://linux.about.com/od/lna_guide/a/gdelna38t01.htm will give a rundown on it's use.
It would be easier to call out to than minicom, and the settings stay on the device.
I have done something like the same as you before.
I had to read and write data through USB Serial adapter, and didnt use minicom.
It may not be god code but i found that inorder to read the data I could create a new thread and have that check for data, my code include a lot of stuff but basicly i did this:
System.Threading.Thread newThread;
newThread = new System.Threading.Thread(this.check_get_data);
and the check_get_data method
public void check_get_data ()
{
byte tmpByte = 0;
while (m_objSerialPort.BytesToRead != 0) {
tmpByte = (byte)m_objSerialPort.ReadByte ();
DoSomethingWithByte(tmpByte);
Thread.Sleep(20);
}
}
this is currently running with two usbserials. dont know if it helps but hope you find your solution