FilipDominec / rp2daq

Raspberry Pi Pico firmware for universal hardware control & measurement, along with a user-friendly Python frontend
MIT License
27 stars 4 forks source link

Interfacing With C#.NET code #3

Closed epsi1on closed 1 year ago

epsi1on commented 1 year ago

Hello there... Thanks for the great library. I am trying to interface your firmware with my C# code. In C# I need to read the ADC values of RPI pico. I was wondering if you please can give me some direction on:

It is more about the communication protocol. I'm trying to make a super simple oscilloscope with WPF and C#.

Thanks again.

FilipDominec commented 1 year ago

Hi, you touch one of the Q&A in README.md:

Q: Can a RP2DAQ device be controlled from other language than Python 3.6+?

A: Perhaps, but it would be rather hard. The firmware and computer communicate over a binary interface that would have to be ported to this language. One of the advantages of RP2DAQ is that the interface on the computer side is autogenerated; the corresponding C-code parser would have to be rewritten. Hard-coding the messages in another language would be a quicker option, but it would be bound to a single firmware version.

Python is fine.

So if your interest is truly limited to the ADC, the easiest way is to stick to the current firmware version and hard-code the binary message in C#. If you manually select the virtual COM0/COM1 (...) port your RP2 is connected to, you can just transmit this binary struct from your computer:

    struct __attribute__((packed)) {
        uint8_t message_type; // FOR CURRENT FIRMWARE THIS IS 0x04
        uint8_t channel_mask;       // default=1        min=0       max=31 Masks 0x01, 0x02, 0x04 are GPIO26, 27, 28; mask 0x08 internal reference, 0x10 temperature sensor
        uint16_t blocksize;         // default=1000     min=1       max=8192 Number of sample points until a report is sent
        uint8_t infinite;           // default=0        min=0       max=1  Disables blocks_to_send countdown (reports keep coming until explicitly stopped)
        uint16_t blocks_to_send;    // default=1        min=0                Number of reports to be sent (if not infinite)
        uint16_t clkdiv;            // default=96       min=96      max=65535 Sampling rate is 48MHz/clkdiv (e.g. 96 gives 500 ksps; 48000 gives 1000 sps etc.)
    } * command = (void*)(command_buffer+1);

And after RP2 gets the data, you receive these 24 bytes forming such a binary struct:

 struct __attribute__((packed)) {
     uint8_t report_code;  // again this is 0x04
     uint16_t _data_count; 
     uint8_t _data_bitwidth;
    uint64_t start_time_us;
    uint64_t end_time_us;
     uint8_t channel_mask;
     uint16_t blocks_to_send;
     uint8_t block_delayed_by_usb;
 } adc_report;

This struct is immediately followed by the actual measured payload. If you opt for 2000 values each occupying 12 bits, they become bit-packed into extra 3000 bytes, please look at https://github.com/FilipDominec/rp2daq/blob/main/rp2daq.py#L144 how to unpack them into a true 2000 array of uint16 in your computer.

Also please notice that reliably receiving uninterrupted stream of data required implementing of multi-threading and multi-processing at the computer side. There is a lot of trial and testing in my C & Python codes to ensure reliable communication! Your C# application will be more efficient than Python, but if you still run into USB-caused delays, I suggest to somehow separate the data-receiving and -presenting processes.

By the way, a practically usable, two channel 100 Msps oscilloscope with a nice GUI in Python is one of the high-priority plans for rp2daq. But certainly you can make a scope independently. Just keep one version of firmware, if you hardcode you ABI.

epsi1on commented 1 year ago

Thanks,

By the way, a practically usable, two channel 100 Msps oscilloscope with a nice GUI in Python is one of the high-priority plans for rp2daq. But certainly you can make a scope independently. Just keep one version of firmware, if you hardcode you ABI.

Yes certainly i'll use separated threads for readings and processings and drawings. But how is it possible to transfer such volume of data (like 100Msps) to the PC via pico's USB 1.1? I am not so much familiar with the python, but i can help on GUI of osciloscope with WPF and C#. There is a high performance bitmap library for WPF named WriteableBitmapEx which is capable of rendering at least 20-30 FPS for massive data such as ADC.

epsi1on commented 1 year ago

Thanks for information, I was finaly able to connect to device via serial port. After sending a Command to the PICO, like this:

command

in raw 9 bytes: 0x04,0x10,0xe8,0x03,0x00,0x01,0x00,0x60,0x00

i do receive 68 bytes of data, which i assume first 24 bytes is a adc_report. After deserialization, i think the data is sort of non sensing:

ADC Report

in raw 24 bytes: 0x00,0x1e,0x00,0x08,0x72,0x70,0x32,0x64,0x61,0x71,0x5f,0x32,0x33,0x30,0x37,0x30,0x39,0x5f,0x45,0x36,0x36,0x30,0x42,0x34

As you've noted, I was expecting to have a large payload after first 24 bytes, currently only 44 bytes as payload.

here is rest of data which is 44 bytes:

0x34,0x30,0x30,0x37,0x34,0x32,0x32,0x46,0x33,0x32,0x00,0x1e,0x00,0x08,0x72,0x70,0x32,0x64,0x61,0x71,0x5f,0x32,0x33,0x30,0x37,0x30,0x39,0x5f,0x45,0x36,0x36,0x30,0x42,0x34,0x34,0x30,0x30,0x37,0x34,0x32,0x32,0x46,0x33,0x32

here is the code so far: https://github.com/epsi1on/SimpleOscilloscope/blob/main/SimpleOsciloscope.UI/DaqInterface.cs

epsi1on commented 1 year ago

Finally managed to get the data from RPI Pico. Issue fixed. here is the code: https://github.com/epsi1on/SimpleOscilloscope/blob/main/SimpleOsciloscope.UI/DaqInterface.cs

Thanks

epsi1on commented 1 year ago

Sorry for re opening the issue. I am working on an oscilloscope. I think in order to show a single part of signal, first i need to find the signal frequency. I am not familiar with the math stuff for electronics, probably do you have any idea on how can I find the frequency? seems FFT is not good enough (link) i did not understand it... Thanks

FilipDominec commented 1 year ago

Hi, sorry for my delay due to personal duties.

I can confirm that performing FFT and finding the highest peak in the spectrum is a reliable approach to find the fundamental signal frequency. Note however that simply finding the single maximum point is also somewhat inaccurate as the actual frequency peak may be centered between two spectral point. So you may want to use some linear interpolation around the maximum in spectrum, e.g. like https://github.com/FilipDominec/nihilnovi/blob/master/my_snippets/analysis/peak_width_half_maximum.py.

For low frequency signal, more accurate approach is simply to find the signal period t (zero crossings on smoothed curve?), and trivially compute f = 1/t.

The most professional solution is to get along without Fourier transform and timing measurements, and run https://github.com/NanoComp/harminv. Not sure how it easily it integrates into C#.

FilipDominec commented 1 year ago

Note: If you go the FFT way, you will get two distinct peaks even for a perfect sinewave - this is due to processing real-valued signal into equal positive- and negative-frequency components. Feel free to trash upper half of the data you get from a typical FFT implementation.

epsi1on commented 1 year ago

Thanks. Something like UI of oscilloscope could be complicated. If you would like, we could do the design of the code together. Regardless of what language (C# or python). then Implementation will be much easier, faster and better. I mean design architecture, classes and software parts and UI look. It would be fun... I did it before for other projects and it really worths...

Here is start point: https://github.com/epsi1on/SimpleOscilloscope/blob/main/docs/developer/phase0/threads.png

FilipDominec commented 1 year ago

Now I can see there is a need for external trigger for ADC block! I will implement it with a low-jitter interrupt handler in the firmware, then I can sketch up my idea of a minimalist digital oscilloscope using rp2daq + some 300 lines of Python. Stay tuned.

epsi1on commented 1 year ago

my idea of a minimalist digital oscilloscope using rp2daq + some 300 lines of Python.

Cool. Sure waiting for that. Even making architecture before code would works great. By the way, which features of this scope. you think is needed to implement:

image

I am not so much familiar with electronics, to be honest I never worked with an oscilloscope, but some experiences with high performance C# & WPF. I think starting point could be defining the main window (like above one) and define which features do we want to implement. I think even processing giga samples are possible on a desktop with high performance C# code and WPF.

epsi1on commented 1 year ago

Another question. For finding the frequency, how to choose the window width (sample count) to use with FFT. For example code does store samples from last 10 secs, for 500Ksps it will be 5Ms, i.e. an array with 5M members. How to choose the slice of this array in order to send to FFT? Thanks...

FilipDominec commented 1 year ago

You can certainly run FFT on 5M samples without much hassle. But maybe you are searching for much lower frequencies than 500 kHz - then you can accordingly use much lower sampling rate, e.g. 1 kSps. ------- Original Message ------- On Monday, August 14th, 2023 at 6:02 AM, epsi1on @.***> wrote:

Another question. For finding the frequency, how to choose the window width (sample count) to use with FFT. For example code does store samples from last 10 secs, for 500Ksps it will be 5Ms, i.e. an array with 5M members. How to choose the slice of this array in order to send to FFT? Thanks...

— Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you commented.Message ID: @.***>

epsi1on commented 1 year ago

Thanks for the information. I tried to implement the FFT way, but no luck. Here is the code so far: https://github.com/epsi1on/SimpleOscilloscope/blob/main/src/POC/SimpleOsciloscope.UI/FftFrequencyDetector.cs it start from line 22 to 57

there is a class named FftFrequencyDetector with a sinle function TryGetFrequency. I've tried to do as said here, but did not get the test results. I wish you had an working example which i could translate to C# code.

FilipDominec commented 1 year ago

Excuse me, I am somewhat busy now, but will take a look at that within next weeks.

epsi1on commented 1 year ago

Excuse me, I am somewhat busy now, but will take a look at that within next weeks.

Thanks anyways. if you have python code which simply gets an array and value of sample rate, and does the FFT stuff and return the frequency, i can port it to C#. Thanks

FilipDominec commented 1 year ago

Not exactly that, but this may be another inspiration for you: https://gist.github.com/FilipDominec/0625e89c171e402741820dcaf11db77e

epsi1on commented 1 year ago

for future reference, here is the working code for interfacing with C#: https://github.com/epsi1on/SimpleOscilloscope/blob/main/src/POC/SimpleOsciloscope.UI/HardwareInterface/RpiPicoDaqInterface.cs

thanks