Rafael Tarcha
Rafael Tarcha

Reputation: 95

Serial Port Communication missing one byte

I'm responsible for creating a HMI which will use the USB port to communicate with the device.

I'm new to C# and WPF, so I started researching a bit and found several questions and topics in this site which helped me achieve the starting point I wanted: I'm able to read and write to the SerialPort class.

To test the device I have a UART that echoes back any message it receives. I have a simple form with a text box, a button and a label. Once the button is clicked it sends the text typed in the box to the device (if no text is entered I have a pre defined array of bytes to test). The label is update anytime the port receives bytes.

The code runs ok for the first message (doesn't matter which), but any message sent after that almost always comes back with missing bytes. I have no clue as to why this is happening, I tried discarding the buffers every time, but to no avail.

Here's my code:

using System.IO.Ports;

namespace LearningSteps
{
    /// <summary>
    /// Interaction logic for Comm.xaml
    /// </summary>
    /// 
    public partial class Comm : Window
    {
        SerialPort port;
        BackgroundWorker backgroundWorker1 = new BackgroundWorker();

        public delegate void AtualizaCallBack(string message);

        public Comm()
        {
            InitializeComponent();
            //InitializeBackgroundWorker();
            port = new SerialPort("COM4",115200,Parity.None,8,StopBits.One);
            port.RtsEnable = true;
            port.DataReceived +=
            new SerialDataReceivedEventHandler(Recebido);
            port.Open();

        }

        private void Recebido(object sender, SerialDataReceivedEventArgs e)
        {
            SerialPort sp = (SerialPort)sender;
            String indata = sp.ReadExisting();

            sp.DiscardOutBuffer();
            sp.DiscardInBuffer();

            my_label.Dispatcher.Invoke(new AtualizaCallBack(this.atualiza),new object[]{indata});

        }

        private void bt_Click(object sender, RoutedEventArgs e)
        {
            if (txt1.Text.Length == 0)
            {
                byte[] vetor = new byte[] { 0x40, 0x45, 0x2B, 0x5C, 0x10 };
                port.DiscardOutBuffer();
                port.Write(vetor, 0, 5);
            }
            else
            {
                port.DiscardOutBuffer();
                port.Write(txt1.Text);
            }
        }

        private void atualiza(string s)
        {
            my_label.Content = s;
        }

    }
}

And here's the XAML:

<Window x:Class="LearningSteps.Comm"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Comm" Height="346" Width="404">
    <Grid Background="#FF9E9E9E">
        <Label x:Name="my_label" HorizontalAlignment="Left" Height="40" Margin="80,200,0,0" VerticalAlignment="Top" Width="240" Background="#FFD1D18D" FontSize="14" FontWeight="Bold" Foreground="#FF1D83BD" HorizontalContentAlignment="Center" Content="Lalala"/>
        <TextBox x:Name="txt1" HorizontalAlignment="Left" Height="40" Margin="80,80,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="240" TextAlignment="Center" FontSize="14" FontWeight="Bold"/>
        <Button x:Name="bt" Content="Enviar" HorizontalAlignment="Left" Height="40" Margin="140,140,0,0" VerticalAlignment="Top" Width="120" FontWeight="Bold" Foreground="#FF4084BD" Click="bt_Click">
            <Button.Background>
                <LinearGradientBrush EndPoint="0,1" StartPoint="0,0">
                    <GradientStop Color="#FFF3F3F3" Offset="0"/>
                    <GradientStop Color="#FFE6C041"/>
                    <GradientStop Color="#FFE8C382" Offset="1"/>
                </LinearGradientBrush>
            </Button.Background>
        </Button>
    </Grid>
</Window>

Any ideas of what might be the problem here?

Upvotes: 5

Views: 4347

Answers (3)

Right leg
Right leg

Reputation: 16730

If someone happened to have the same problem, here is the workaround I picked. This is hideous, and does not explain at all the origin of that byte loss (which I had on my project too although using a different approach and no buffer flushing), but it can work as a quickfix.

Simply prepend a control character at the beginning of each message (in my case, I could use '0', but 0x00 or another one could be used as well). On the receiver side, if the message begins with that character, ignore the latter; else, the the control character has been dropped, and the message effectively received is directly the "payload".

So, in pseudo-code (sorry if my pseudo-code is Python), with '0' as control character:

// Sender
message = "hello"
send("0" + message)

// Receiver
message = read_port()
if message[0] == '0':
    message = message[1:]

Please note that this is not a viable solution, but only a hotfix.

Upvotes: 0

Hans Passant
Hans Passant

Reputation: 942099

        String indata = sp.ReadExisting();
        sp.DiscardOutBuffer();
        sp.DiscardInBuffer();

This is very unwise, the port will continue to receive data at the time you call sp.ReadExisting(). At the high baudrate you are using, there are non-zero odds that another byte was received when ReadExisting() returns. Your DiscardInBuffer() call will destroy it.

Remove all calls to DiscardOutBuffer and DiscardInBuffer. They do not flush, they'll only cause random data loss. They should only ever be used for a protocol reset to purge the driver buffers, you don't have a protocol.

Upvotes: 1

JeremiahDotNet
JeremiahDotNet

Reputation: 910

Serial port communication is subject to data transmission errors. You need to implement some kind of low layer protocol to ensure communication is successful.

For example, you could include a checksum with every data frame that is transmitted. If that checksum doesn't match a checksum on the data received then the receiving application can request that the previous frame be re-sent.

Upvotes: 0

Related Questions