By default, SerialPort uses ASCIIEncoding to encode the characters

You're confusing methods, which read/write strings or chars, with methods, which read/write bytes.

E.g., when you'll call this:

port.Write("абв")

you'll get "???" (0x3F 0x3F 0x3F) in the port buffer by default. On the other hand, this call:

// this is equivalent of sending "абв" in Windows-1251 encoding
port.Write(new byte[] { 0xE0, 0xE1, 0xE2 }, 0, 3)

will write sequence 0xE0 0xE1 0xE2 directly, without replacing bytes to 0x3F value.

UPD.

Let's look into source code:

public void Write(string text)
{
    // preconditions checks are omitted

    byte[] bytes = this.encoding.GetBytes(text);
    this.internalSerialStream.Write(bytes, 0, bytes.Length, this.writeTimeout);
}

public void Write(byte[] buffer, int offset, int count)
{
    // preconditions checks are omitted

    this.internalSerialStream.Write(buffer, offset, count, this.writeTimeout);
}

Do you see the difference?
Method, that accepts string, converts strings to a byte array, using current encoding for port. Method, that accepts byte array, writes it directly to a stream, which is wrapper around native API.

And yes, documentation fools you.

Answer from Dennis on Stack Overflow
🌐
Lucidar
lucidar.me › en › serialib › read-and-write-array-of-bytes-on-serial-port-in-c-cpp
How to read and write arrays of bytes on serial port in C/C++ |
// Serial object serialib serial; ... bytes unsigned char prime[8] = { 1, 3, 5, 7, 11, 13, 17, 19 }; // Write the array on the serial device serial.writeBytes(prime, 8); printf ("Data sent\n"); // Read the bytes unsigned char received[8]; serial.readBytes(received, 8, 2000, 1000); ...
Top answer
1 of 2
8

By default, SerialPort uses ASCIIEncoding to encode the characters

You're confusing methods, which read/write strings or chars, with methods, which read/write bytes.

E.g., when you'll call this:

port.Write("абв")

you'll get "???" (0x3F 0x3F 0x3F) in the port buffer by default. On the other hand, this call:

// this is equivalent of sending "абв" in Windows-1251 encoding
port.Write(new byte[] { 0xE0, 0xE1, 0xE2 }, 0, 3)

will write sequence 0xE0 0xE1 0xE2 directly, without replacing bytes to 0x3F value.

UPD.

Let's look into source code:

public void Write(string text)
{
    // preconditions checks are omitted

    byte[] bytes = this.encoding.GetBytes(text);
    this.internalSerialStream.Write(bytes, 0, bytes.Length, this.writeTimeout);
}

public void Write(byte[] buffer, int offset, int count)
{
    // preconditions checks are omitted

    this.internalSerialStream.Write(buffer, offset, count, this.writeTimeout);
}

Do you see the difference?
Method, that accepts string, converts strings to a byte array, using current encoding for port. Method, that accepts byte array, writes it directly to a stream, which is wrapper around native API.

And yes, documentation fools you.

2 of 2
2

This

port.Encoding = System.Text.Encoding.UTF8;

string testStr = "TEST";

port.Write(testStr);

and this

byte[] buf = System.Text.Encoding.UTF8.GetBytes(testStr);

port.Write(buf, 0, buf.Length);

will result in the same bytes being transmitted. In the latter one the Encoding of the serial port could be anything.

The serial port encoding only matters for methods that read or write strings.

Discussions

Sending one byte at a time to serial port.
The arduino will read this byte, ... after write this byte, discard it and wait for the next byte to be written until the whole C# byte array is read. I know this sounds like a stupid question, and I sincerely apologize to the community for this. But I really can't think of a for loop algorithm that does this. As I said before, the Arduino part I will still do. This is my failed attempt to send one byte at a time from the Array to the Arduino's serial port... More on c-sharpcorner.com
🌐 c-sharpcorner.com
0
June 29, 2021
arduino uno - Write array to serial port - Arduino Stack Exchange
Input is a series of ASCII-encoded hex digits and getting the output to print the byte values of the hex. ... Ok, so for a (say) 20 character string you have clen=10. More on arduino.stackexchange.com
🌐 arduino.stackexchange.com
May 15, 2017
c# - Serial Port Communication - Code Review Stack Exchange
Calls like rotate() and stop() are commands that write data into a 9-byte-array and write bytearray to serial port. More on codereview.stackexchange.com
🌐 codereview.stackexchange.com
December 16, 2016
How could I send an array to serial port?
Hi, I need to send this array test[121]={33, 20, 23, 20, 27, 27, 25, 29, 14, 12, 10, 74, 24, 29, 44, 57, 63, 62, 61, 42, 30, 30, 185, 178, 93, 50, 68, 74, 76... More on forum.qt.io
🌐 forum.qt.io
7
0
December 21, 2018
Top answer
1 of 1
1

Yes, there are things wrong with it.

  1. serialOpen() returns -1 if the operation fails, with errno set to indicate the error.

    Make sure you include <stdlib.h>, <stdio.h>, <string.h>, <errno.h>, and <wiringSerial.h> at the beginning of your program, and open the serial port using e.g.

        int fd;
    
        fd = serialOpen("/dev/ttyS0", 115200);
        if (fd == -1) {
            fprintf(stderr, "Cannot open /dev/ttyS0: %s.\n", strerror(errno));
            exit(EXIT_FAILURE);
        }
    
  2. write() may return a short count. You cannot just assume it sent everything successfully. I recommend you use a helper function, for example

    #ifdef __cplusplus
    extern "C" {
    #endif
    
    int writeall(const int fd, const void *data, const size_t len)
    {
        const char        *ptr = (const char *)data;
        const char *const  end = (const char *)data + len;
        ssize_t            n;
    
        while (ptr < end) {
            n = write(fd, ptr, (size_t)(end - ptr));
            if (n > 0)
                ptr += n;
            else
            if (n != -1)
                return errno = EIO;
            else
            if (errno != EINTR)
                return errno;
        }
    
        return 0;
    }
    
    #ifdef __cplusplus
    }
    #endif
    

    which returns 0 for success, and nonzero for errors. You can use it thus:

        if (writeall(fd, "\x3E\x52\x01\x53\x01\x01", 6)) {
            fprintf(stderr, "Serial write error: %s.\n", strerror(errno));
            /* If this is fatal, do exit(EXIT_FAILURE) here.
               If the error is not a fatal one, let the program continue.
            */
        }
    
  3. Don't do sleep(). At minimum, make sure you include <termios.h>, and do

        tcdrain(fd);
    

    to ensure the kernel sends all serial data; it will return only after everything written thus far to fd (fd being open to a serial port or tty).

Also, I recommend against using serialPutchar(), serialPuts(), serialPrintf(), and serialGetchar(), because they do not have any kind of error checking.

🌐
Wordpress
batchloaf.wordpress.com › 2013 › 02 › 13 › writing-bytes-to-a-serial-port-in-c
Writing bytes to a serial port in C | ad hocumentation • n. fast documentation of ideas and solutions.
February 13, 2013 - This is a (relatively) simple example of a C program to send five bytes to a serial port in Windows. In this case, I’m sending the five characters “hello” via COM22 at 38400 baud,…
🌐
C# Corner
c-sharpcorner.com › home › forums › visual basic .net › sending one byte at a time to serial port.
Sending one byte at a time to serial port.
June 29, 2021 - The arduino will read this byte, write it to the respective address on the memory card, after write this byte, discard it and wait for the next byte to be written until the whole C# byte array is read. I know this sounds like a stupid question, and I sincerely apologize to the community for this. But I really can't think of a for loop algorithm that does this. As I said before, the Arduino part I will still do. This is my failed attempt to send one byte at a time from the Array to the Arduino's serial port.
Top answer
1 of 1
2

There's some logic slightly askew with your length calculations:

int clen = input.length()/2;

Ok, so for a (say) 20 character string you have clen=10.

for (int i=0; i < clen; i+=2)

Ok, so for the first 10 characters of your 20 character string you take alternate ones (0, 2, 4, 6, 8)

What about the last 10 characters? clen is 10, not 20, since you divided it by 2 at the start.

Decide if you want clen to be half the string length, then count double, or is the whole string length, and you divide it by 2 where you want the final string length. Or have two variables - one before and one after dividing by 2.

Oh, and you should pass your String parameter by reference so you don't duplicate the object and end up fragmenting your heap.

void SendCTRL (String &input) 

I haven't gone into depth about the working of your dehex function, but this is the function I always use and works well:

unsigned char h2d(char hex)
{
    if(hex > 0x39) hex -= 7; // adjust for hex letters upper or lower case
    return(hex & 0xf);
}

Also you should or your values together, not add them:

cmd[i/2] = (dehex(hin[i])<<4) | dehex(hin[i+1]);

This would go some way to fixing your current issue, since | has a different precedence to +. At the moment you have effectively:

cmd[i/2] = dehex(hin[i]) << (4 + dehex(hin[i+1]));

since the + happens before the <<, whereas what you want is:

cmd[i/2] = (dehex(hin[i]) << 4) + dehex(hin[i+1]);

However with | instead you get

cmd[i/2] = (dehex(hin[i]) << 4) | dehex(hin[i+1]);

because the << happens before the |

Here is your code written in pure C to run on a PC (faster to debug and test than an Arduino when you're on Linux):

#include <stdio.h>
#include <string.h>

unsigned char dehex(char c)
{
  // Get nibble value 0...15 from character c
  // Treat digit if c<'A', else letter
  return c<'A'? c & 0xF : 9 + (c & 0xF);
}

void SendCTRL (const char *hin)
{
  int ilen = strlen(hin);
  int clen = ilen / 2;
  unsigned char cmd[clen+1]; // Leave a byte for null terminator
  for (int i=0; i < ilen; i+=2)
  {
    cmd[i/2] = dehex(hin[i])<<4 | dehex(hin[i+1]);
    printf("%02x\n", cmd[i/2]);
  }
  cmd[clen] = 0;                     // Null-byte terminator
  for (int i = 0; i < clen; i++) {
    printf("%c", cmd[i]);
  }
}

void main() {
    SendCTRL("010307E40001C549");
}
Top answer
1 of 2
3

I guess you thought about avoiding of Thread.Sleep() method, not Wait() you mentioned in title. I can't test your code because I don't have either COM port nor motor. I would like to give you some general tips how to improve your code. Let's implement some SOLID and DRY principles. I would start with class Motor which will represent your device. You can implement for example singleton class for this purpose. This helps you to separate your motor logic from other logic such is user interface. I can also imagine your port settings as a part of application configuration.

Motor.cs

public sealed class Motor : IDisposable
{
    public static readonly Motor Instance = new Motor();

    private Motor()
    {
        MotorPort = new SerialPort();
        MotorPort.PortName = "COM6";
        MotorPort.Handshake = Handshake.RequestToSend;
        MotorPort.ReceivedBytesThreshold = 8;
        MotorPort.DataReceived += new SerialDataReceivedEventHandler(dataRecievedHandler);
        MotorPort.Open();
    }

    public SerialPort MotorPort { get; private set; }
    public decimal Position { get; private set; }
    public void StopRotate()    { }
    public void StartRotate(string speed) { }
    public void MoveToPosition(int position) { }
    private void dataRecievedHandler(object sender, SerialDataReceivedEventArgs e)
    {
    }
    public void Dispose()
    {
        if (MotorPort.IsOpen)
        {
            MotorPort.Close();
        }
    }
}

Instead of using global fields (M, buf), encapsulate them into object and set the proper access modifier or better create properties. Use meaningful names your fields and properties. Use Capitalization Convention.

You can isolate your repeating strings into separate static class to make your string constant repository which would be more easily maintainable against direct using of particular string.

StringConstants.cs

static class StringConstants
{
    public const string Prefix = "SR,00,002,";
    public const string PrefixCrLf = "SR,00,002\r\n";
}

Extension methods can simplify your code and make it more readable.

Extensions.cs

static class Extensions
{
    public static decimal GetDecimalOutput(this SerialPort port)
    {
        return decimal.Parse(Regex.Split(port.ReadLine(), StringConstants.Prefix)[1]);
    }
    public static void SendCommand(this SerialPort port)
    {
        port.WriteLine(StringConstants.PrefixCrLf);
    }
}

If you have implemented Motor.cs methods, you can use it or extend it.

Form1.cs

private void btnTest_Click(object sender, EventArgs e)
{
    Motor.Instance.StartRotate(speed);
    Thread.Sleep(5000);
    Motor.Instance.StopRotate();
    Motor.Instance.MoveToPosition(0);

    //read sensor
    serialPort2.SendCommand();
    decimal caliber = serialPort2.GetDecimalOutput();

    do
    {
        serialPort2.SendCommand();
        actualValue = serialPort2.GetDecimalOutput();
    }
    while (actualValue <= caliber);
}

I am not experienced in serial port programming, but I am pretty sure that event SerialPort.DataReceived should be used to invoke subscribed method as you do with your serialPort3. I don't know exactly why you are using Thread.Sleep(), because I cannot see getPosi(C); method.

2 of 2
3

Just a minor thing that jumped out at me:

private void rotate(byte[] A, string Test) //Rotate Motor Right
{
    A = MB.TMCL_RMR(Test);
    serialPort3.Write(A, 0, A.Length);
}

Why are you passing in a byte array to just replace it with something else on the first line?

private void SendRotateCommand(string speed)
{
    var commandBytes = MB.TMCL_RMR(speed);
    motorPort.Write(commandBytes, 0, commandBytes.Length); 
}

I've guessed that the Test parameter was actually the rotation speed from a comment in the method that calls it.

You need to improve your naming throughout. Your code is very simple but is very difficult to follow simply because the naming is so poor.


Another small thing:

public byte[] GET_POSI()
{
    byte[] E = new byte[9];
    E[0] = 0x1;
    E[1] = 0x6;
    E[2] = 0x1;
    E[3] = 0x0;
    E[4] = 0x0;
    E[5] = 0x0;
    E[6] = 0x0;
    E[7] = 0x0;
    E[8] = 0x08;
    return E;
}

Could be written more simply as:

public byte[] GET_POSI()
{
    return new byte[] { 1, 6, 1, 0, 0, 0, 0, 0, 8 };
}

I can't really understand what you're trying to save the position for. Or even how you're attempting to do it. You are sending messages to the motor and it's sending things back. You get a message, you figure out what it means and act on it.

You're also mixing DataReceived with SerialPort.ReadLine they both use the same underlying memory stream and you should be using one or the other - not both.

You're also freezing your UI for large chunks of time while you wait for operations to complete. It's fine to accept the command from the user and then update the UI as the action progresses. E.g. show the current position as the motor is rotating, when it gets to the right point, stop it.

Find elsewhere
🌐
Energia
energia.nu › reference › en › language › functions › communication › serial › write
Serial.write()
void setup(){ Serial.begin(9600); } void loop(){ Serial.write(45); // send a byte with the value 45 int bytesSent = Serial.write(“hello”); //send the string “hello” and return the length of the string. } ... Corrections, suggestions, and new documentation are very welcomed.
Top answer
1 of 1
6

There are a number of strange things going on here...

First, you have this:

//Buffer with data
byte[] data = HexStringToByteArray(mensage);

//Handle data
comport.Read(data, 0, data.Length);

The first line takes the mensage (message?) and turns it into a byte array, but then you immediately overwrite the data by reading into the same buffer. In essence, you are using HexStringToByteArray simply to create a new array of some length (3 bytes?).

Based on how you are using HexStringToByteArray, it certainly does not take a hex string and turn it into a byte array. mensage and mensage2 contain things other than valid hex characters. Maybe something more descriptive like StringToByteArray or StringToHexByteArray would be better. It does look like you are trying to convert a "hex" string input to byte data, but given the inputs I can't see how you can run this and not get an exception. The other problem with this method is that it assumes you have an input string length divisible by 2, and you are not checking that (nor are your values passed in adhering to that).

Since you are not doing anything with the data and data2 arrays, you are basically using them to flush some data out of the read buffer. I would have to assume that the code you posted is not complete, otherwise you could do something like SerialPort.Flush() instead.

Now, lets look at your variables:

string start = "?";
string carriageReturn = "\r";
string text = string.Empty;

string mensage = "@" + "r" + "\r";
string mensage2 = "@" + "{" + texto + "\r";

I would advise against using the carriageReturn and either call it something like messageDelimiter or just don't use it. It would be akin to defining something like:

int zero = 0;

Zero will always equal zero, so you shouldn't have variables for them. The name text doesn't really help here, you should give it a descriptive name like emptyMessage since it doesn't look like it ever changes. mensage and mensage2 are also not descriptive (or spelled correctly), maybe something like doorOpenQuery and doorCloseCommand. (And in addition, since texto isn't defined, I don't see this even compiling...)

The last problem I see is that you have an un-throttled while(true) loop in your program with no exit conditions other than something throwing an exception. How many times do you want to run this loop? You shouldn't have an unbounded while, you are going to have a not-responding program if you let it run for about 10 seconds.

From what I see I don't believe that this is functioning code. Have you run this through the debugger? If you have functionality problems this probably isn't the StackExchange site for that.

🌐
Electronic Team, Inc
help.electronic.us › support › solutions › articles › 44002288858-writearray-method
WriteArray Method in Virtual Serial Port ActiveX Control : Electronic Team, Inc
December 8, 2021 - The WriteArray method writes data to virtual serial port as a BYTE array of VARIANT type. LONG WriteArray( VARIANT Buffer, // data buffer ); Parameters: Buffer [in] : This is variable of VARIANT type that contains an array ...
Top answer
1 of 2
2

By modifying the code in Rion's answer below, I was able to test this and got some surprising results. I used the following code (modified from Rion, thanks):

    class Program {
    static void Main(string[] args) {
        // Create a stopwatch for performance testing
        var stopwatch = new Stopwatch();
        // Test content
        var data = GetTestingBytes();

        var ports = SerialPort.GetPortNames();
        using (SerialPort port = new SerialPort(ports[0], 115200, Parity.None, 8, StopBits.One)) {
            port.Open();

            // Looping Test
            stopwatch.Start();
            foreach (var item in data) {
                port.BaseStream.WriteByte(item);
            }
            stopwatch.Stop();
            Console.WriteLine($"Loop Test: {stopwatch.Elapsed}");

            stopwatch.Start();
            port.Write(data, 0, data.Length);
            stopwatch.Stop();
            Console.WriteLine($"All At Once Test: {stopwatch.Elapsed}");
        }

        Console.Read();
    }

    static byte[] GetTestingBytes() {
        var str = String.Join(",", Enumerable.Range(0, 1000).Select(x => Guid.NewGuid()).ToArray());
        byte[] bytes = new byte[str.Length * sizeof(char)];
        System.Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length);
        return bytes;
    }
}

The results were extremely surprising; using the method that takes an array of bytes took almost exactly twice as long at 12.5818728 seconds compared to 6.2935748 seconds when just calling WriteByte repeatedly. This was the opposite result than I was expecting. Either way, I wasn't expecting one method to be twice as fast as the other!

If anyone can figure out why this is the case, I would love to know!

2 of 2
0

Based on a quick glance at the source, it looks like the SerialPort.Write() method actually just points to the Write() method of the underlying stream anyways :

public void Write(byte[] buffer, int offset, int count)
{
            if (!IsOpen)
                throw new InvalidOperationException(SR.GetString(SR.Port_not_open));
            if (buffer==null)
                throw new ArgumentNullException("buffer", SR.GetString(SR.ArgumentNull_Buffer));
            if (offset < 0)
                throw new ArgumentOutOfRangeException("offset", SR.GetString(SR.ArgumentOutOfRange_NeedNonNegNumRequired));
            if (count < 0)
                throw new ArgumentOutOfRangeException("count", SR.GetString(SR.ArgumentOutOfRange_NeedNonNegNumRequired));
            if (buffer.Length - offset < count)
                throw new ArgumentException(SR.GetString(SR.Argument_InvalidOffLen));
            if (buffer.Length == 0) return;

            internalSerialStream.Write(buffer, offset, count, writeTimeout);
}

If I had to wager a guess, I would assume that the difference between the two performance wise might be negligible. I suppose if you had some test data, you could create a StopWatch to actually time the differences between both approaches.

Update with Performance Tests

I didn't test this using SerialPort objects, but instead opted for basic MemoryStream ones as the real heart of the question seemed to be if writing bytes in a loop is more performant than writing them using a byte[].

For test data, I simply generated 1000 random Guid objects and concatenated them into a string :

static byte[] GetTestingBytes()
{
        var str = String.Join(",", Enumerable.Range(0, 1000).Select(x => Guid.NewGuid()).ToArray());
        byte[] bytes = new byte[str.Length * sizeof(char)];
        System.Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length);
        return bytes;
}

As far as the tests themselves go :

// Create a stopwatch for performance testing
var stopwatch = new Stopwatch();
 // Test content
var data = GetTestingBytes();

// Looping Test
using (var loop = new MemoryStream())
{
      stopwatch.Start();
      foreach (var item in data)
      {
            loop.WriteByte(item);
      }
      stopwatch.Stop();
      Console.WriteLine($"Loop Test: {stopwatch.Elapsed}");
}
// Buffered Test
using (var buffer = new MemoryStream())
{
      stopwatch.Start();
      buffer.Write(data, 0, data.Length);
      stopwatch.Stop();
      Console.WriteLine($"Buffer Test: {stopwatch.Elapsed}");
}

After running the tests a few times, the averages after 1000 tests broke down as follows :

  LOOP: 00:00:00.0976584
BUFFER: 00:00:00.0976629

So the looping approach, at least in the context of using MemoryStream objects, appears to be the winner.

You can see the entire testing code here if you want to run it yourself.

🌐
Particle
docs.particle.io › reference › device-os › api › serial › write
write() - Serial | Reference | Particle
Writes binary data to the serial port. This data is sent as a byte or series of bytes; to send the characters representing the digits of a number use the print() function instead.
🌐
Mbed
os.mbed.com › questions › 4830 › write-int-array-or-byte-array-to-Serial-
write int array or byte array to Serial port - Question | Mbed
October 8, 2014 - almost all API is byte oriented at the moment, the new version of mbed should change this approach. posted by Martin Kojtal 08 Oct 2014 ... It seems like a very simple method to add to the Serial object really. It would just need a for loop to individually write all bytes or ints to the port.
🌐
Stack Overflow
stackoverflow.com › questions › 56741805 › how-to-write-binary-representation-through-serial-port-connection-in-c
casting - how to write binary representation through serial port connection in c - Stack Overflow
Since c is a single byte, it should be write(port, &c, 1). The call write(port, c, 8) should crash the program. ... You are writing a string of 8 '1' characters. Since your data is 8 bits and a char is 8 bits long, you can store it in a single char.
🌐
Arduino Forum
forum.arduino.cc › projects › programming
Java/C# Serial port write to Arduino reading wrong bytes - Programming - Arduino Forum
April 29, 2015 - I have a program I wrote originally in Java that writes to a serial port that I have an arduino pro mini plugged into. Each chunk of data is 6 bytes, and when the arduino receivies the data, it just spits it back out to …
🌐
Microsoft Learn
learn.microsoft.com › en-us › dotnet › api › system.io.ports.serialport.write
SerialPort.Write Method (System.IO.Ports) | Microsoft Learn
June 13, 2022 - Writes a specified number of bytes to the serial port using data from a buffer. public: void Write(cli::array <System::Byte> ^ buffer, int offset, int count);