Search code examples
c#serial-portcompact-frameworkwindows-ceopennetcf

How can I read a string from OpenNETCF.IO.Ports.SerialPort?


I write commands to a belt printer using OpenNETCF.IO.Ports.SerialPort this way:

public static bool SendCommandToPrinter(string cmd)
{
    bool success; // init'd to false by default
    try
    {
        SerialPort serialPort = new SerialPort();
        serialPort.BaudRate = 19200;
        serialPort.Handshake = Handshake.XOnXOff;
        serialPort.Open();
        serialPort.Write(cmd);
        serialPort.Close();
        success = true;
    }
    catch // may not need a try/catch block, as success defaults to false
    {
        success = false;
    }
    return success;
}

...now I need to read a value returned from a "get val" call, but I don't know how, and I haven't been able to find how, to do that. I have the following start, but don't know how to "fill in the blank":

string getDeviceLang = string.Format("! U1 getvar {0}device.languages{0}", quote); 

public static string GetSettingFromPrinter(string cmd)
{
    string settingVal = string.Empty;

    SerialPort serialPort = new SerialPort();
    serialPort.BaudRate = 19200;
    serialPort.Handshake = Handshake.XOnXOff;
    serialPort.Open();
    serialPort.Write(cmd); // do I even need this call to write?
    settingVal = serialPort.Read() // ???
    serialPort.Close();
    return settingVal;
}

OpenNETCF.IO.Ports.SerialPort's Read() method takes an array of bytes ("buffer"), followed by an int ("offset") and another int ("count"), and returns an int. I don't know what to use as args or what to do with the result int.

UPDATE

I tried this:

public static string GetSettingFromPrinter(string cmd)
{
    string settingVal = string.Empty;
    try
    {
        SerialPort serialPort = new SerialPort();
        serialPort.BaudRate = 19200;
        serialPort.Handshake = Handshake.XOnXOff;
        serialPort.Open();
        serialPort.Write(cmd);
        // testing...
        settingVal = serialPort.ReadLine();
        serialPort.Close();
        return settingVal;
    }
    catch (Exception x)
    {
        MessageBox.Show(x.ToString());
        return settingVal;
    }
}

...but that rewards me with the less-than-satisfactory, "System.NotSupportedException: not yet at OpenNETCF.IP.Ports.SerialPort.ReadTo(String value) at..."

UPDATE 2

I added this:

serialPort.ReadTimeout = 500;// made no difference

UPDATE 3

Based on this, I tried to get it to work this way:

public class PrintUtils
{
    static SerialPort _serialPort;
    static bool keepReading = true;
    static string settingVal = string.Empty;
    . . .

public static string GetSettingFromPrinter(string cmd)
{
    Thread readThread = new Thread(ReadSetting());
    try
    {
        _serialPort = new SerialPort();
        _serialPort.BaudRate = 19200;
        _serialPort.Handshake = Handshake.XOnXOff;
        _serialPort.Open();
        while (keepReading)
        {
            _serialPort.WriteLine(cmd);
        }
        _serialPort.Close();
        return settingVal;
    }
    catch (Exception x)
    {
        MessageBox.Show(x.ToString());
        return settingVal;
    }
}

public static void ReadSetting()
{
    while (keepReading)
    {
        try
        {
            settingVal = _serialPort.ReadLine();
            keepReading = settingVal.Equals(string.Empty);
        }
        catch (Exception ex) { MessageBox.Show(ex.ToString());}
    }
}

...but I get, "Method 'PDAClient.PrintUtils.Read()' referenced without parentheses" on this line:

Thread readThread = new Thread(Read);

If I do give it parens:

Thread readThread = new Thread(ReadSetting());

...it tells me, "Argument '1': cannot convert from 'void' to System.Threading.ThreadStart'"

-and:

"The best overloaded method match for System.Threading.Thread.Thread(System.Threading.ThreadStart)' has some invalid arguments"

UPDATE 4

And with this code:

public static string GetSettingFromPrinter(string cmd)
{
    string setting = string.Empty;
    byte[] buffer = null;
    try
    {
        SerialPort serialPort = new SerialPort();
        serialPort = new SerialPort();
        serialPort.BaudRate = 19200;
        serialPort.Handshake = Handshake.XOnXOff;
        serialPort.ReadTimeout = 500;
        serialPort.Open();
        serialPort.Write(cmd);
        setting = Convert.ToString(serialPort.Read(buffer, 0, buffer.Length));
        serialPort.Close();
        return setting;
    }
    catch (Exception x)
    {
        MessageBox.Show(x.ToString());
        return setting;
    }
}

I get an NRE.

UPDATE 5

This attempt:

serialPort.WriteLine(cmd);
setting = serialPort.ReadExisting();

...ends very similary to the first Update ("System.NotSupportedException: not yet at OpenNETCF.IP.Ports.SerialPort.ReadExisting() at...")

UPDATE 6

Okay, I incorporated the pseudocode in ctacke's comment, and so now I've got this:

const string quote = "\"";
. . .
string getDeviceLang = string.Format("! U1 getvar {0}device.languages{0}", quote);
. . .
String deviceLanguage = PrintUtils.GetSettingFromPrinter(getDeviceLang);

public static string GetSettingFromPrinter(string cmd)
{
    string setting = string.Empty;
    try
    {
        SerialPort serialPort = new SerialPort();
        serialPort = new SerialPort();
        serialPort.BaudRate = 19200;
        serialPort.Handshake = Handshake.XOnXOff;
        serialPort.ReadTimeout = 500;
        serialPort.Open();
        serialPort.WriteLine(cmd); // or Write()

        // get a synchronous, newline-delimited response
        StringBuilder response = new StringBuilder();
        char c;
        do
        {
            c = (char)serialPort.ReadByte();
            response.Append(c);
        } while(c != '\n');

        serialPort.Close();
        return setting;
    }
    catch (Exception x)
    {
        MessageBox.Show(x.ToString());
        return setting;
    }

...but it "hangs" - there seems to be an infinite loop in the do...while loop.

UPDATE 7

I added this after the "ReadByte":

MessageBox.Show(c.ToString());

...and all it shows me is nothing (a MessageBox with no "inner text"); I let it pop up 32 times before I warm-booted the device.

UPDATE 8

Okay, then, I stand corrected - I was not using the OpenNETCF SerialPort, as there is no such thing. So I am trying to and have this, based on the pseudocode ctacke provided. I'm now trying to use:

Port serialPort = new Port();
serialPort.Settings = whateverPortSettingsYouNeed;
serialPort.Open();
serialPort.Send(cmd);

...but I don't know what "whateverPortSettingsYouNeed" should be, there is not "Send" method, and I don't know what to use in place of ReadByte()

UPDATE 9

I have this now for settings:

BasicPortSettings bps = new BasicPortSettings();
bps.BaudRate = 19200;
//bps.ByteSize = ?;
//bps.Parity = None;
//bps.StopBits = 1;
Port serialPort = new Port();
serialPort.Settings = bps;

...but, as you can see, I don't know what most of the values should be.

UPDATE 10

This compiles:

public static string GetSettingFromPrinter(string cmd)
{
    string setting = string.Empty;
    try
    {
        BasicPortSettings bps = new BasicPortSettings();
        bps.BaudRate = BaudRates.CBR_19200;
        bps.Parity = OpenNETCF.IO.Serial.Parity.none;
        bps.StopBits = OpenNETCF.IO.Serial.StopBits.one; //bps.StopBits.one;
        Port serialPort = new Port("COM1:", bps);
        byte[] outputBytes = System.Text.Encoding.ASCII.GetBytes(cmd);
        serialPort.Output = outputBytes;
        serialPort.Open();
        byte[] bites = serialPort.Input;
        setting = bites.ToString();
        serialPort.Close();
        return setting;
    }
    catch (Exception x)
    {
        MessageBox.Show(x.ToString());
        return setting;
    }
}

...Now I'll see if it actually works...

UPDATE 11

I found out (the hard way) that I had to Open the port before setting the Output val; it's now:

serialPort.Open();
byte[] outputBytes = Encoding.ASCII.GetBytes(cmd);
serialPort.Output = outputBytes;
byte[] inputBytes = serialPort.Input;
setting = inputBytes.ToString();

...but what is read into setting is "System.Byte[]"

What do I need to actually get at the value in inputBytes, rather than just see a representation of its type?


Solution

  • The first thing I notice is that you're using a class called SerialPort which means you're using code from SDF 1.x which dates to circa 2005 at the most recent. Not sure I'd be overly enthused about that. I created an open source library that contains just a Port class after that code but it works way different - it was modeled after the VB6 serial port object model. I honestly don't even recall who wrote the code in SDF 1.x - might have been me, might not have. I think not just judging by the code style I see.

    I was going to question why you didn't just use the built-in CF serial stuff, but if you're using SDF 1.x, you're likely using a CF version that pre-dates inclusion of a serial port API.

    As for the one you're using now, I would think that it somewhat depends on how the data coming back is expected, but the pseudocode would look something like this:

    var port = new SerialPort;
    port.Settings = whateverPortSettingsYouNeed;
    port.Open();
    
    ....
    // send the command
    port.Send(myCommand);
    
    // get a synchronous, newline-delimited response
    var response = new StringBuilder();
    char c;
    do
    {
        c = (char)port.ReadByte();
        // see if it actually read anything
        if(c == '\0')
        {
            // wait a little bit, then try again - you will want to put in a timeout here
            Thread.Sleep(10);
            continue;
        } 
        response.Append(c);
        // you may want to check for response "reasonableness" here
    } while(c != \n');
    
    DoSomethingWithResponse(response);