BluetoothWrite - locking up NXT

Discussion specific to NXT-G, NXC, NBC, RobotC, Lejos, and more.
Post Reply
spiked33
Posts: 14
Joined: 16 Feb 2012, 17:20

BluetoothWrite - locking up NXT

Post by spiked33 »

As a continuation of a previous discussion, I just did some testing and here is what I am experiencing;

Image

ignore all but the bottom right black window - that is a 'terminal' connected via bluetooth to the NXT. It uses the following code (in C#);

Code: Select all

using System;
using System.Collections.Generic;
using System.Linq;
using System.Data.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.IO.Ports;
using System.ComponentModel;
using System.Threading;
using System.Windows.Threading;

namespace WpfApplication1
{
    /// <summary>
    ///   Handles and displays remotes messages
    /// </summary>
    public class RConsole
    {
        SerialPort BtConn;
        Brush DefaultColor = Brushes.LightGreen;
        Thread ReceiveThread;
        ListBox listBox;

        Dictionary<byte, string> StatusCodeMap;

        byte[] receiveBuff = new byte[1024];
        bool isNxtMail { get { return ((receiveBuff[2] == 0x02) && (receiveBuff[3] == 0x13)); } }

        public RConsole(string Port, ListBox _ListBox)
        {
            BtConn = new SerialPort();
            listBox = _ListBox;

            BtConn.ReadTimeout = 1000;
            BtConn.WriteTimeout = 1000;
            BtConn.PortName = Port;
        }

        public void GetMail()
        {
            if (!BtConn.IsOpen)
            {
                ConsolePrintLine(DefaultColor, "##connection not open, get mail ignored");
                return;
            }
            byte[] request = { 0x0, 0x0, 0x00, 0x13, 0x0A, 0x00, 0x01 }; // read slave mailbox 1 (1+9 = 0x0a), delete message ([5]=1)
            request[0] = (byte)(request.Length - 2);
            try
            {
                BtConn.Write(request, 0, request.Length);
            }
            catch (TimeoutException)
            {
                ConsolePrintLine(DefaultColor, "##write timeout");
            }
        }

        public void OpenConnection()
        {
            try
            {
                BtConn.Open();
            }
            catch (Exception ex)
            {
                ConsolePrintLine(Brushes.Red, "##Connection open exception");
                ConsolePrintLine(Brushes.Gray, ex.Message);
            }

            if (BtConn.IsOpen)
            {
                ConsolePrintLine(DefaultColor, "##Got connection");
                BeginReceive();
            }
            else
                ConsolePrintLine(Brushes.Red, "##Connection failed");
        }

        public void BeginReceive()
        {
            if (!BtConn.IsOpen)
            {
                ConsolePrintLine(DefaultColor, "##No connection, BeginReceive ignored");
                return;
            }

            ReceiveThread = new Thread(new ThreadStart(ReceiveWorker));
            ReceiveThread.Start();
            ConsolePrintLine(DefaultColor, "##Receive thread started");
        }

        void ReceiveWorker()
        {
            try
            {
                while (true)
                {
                    if (!BtConn.IsOpen)
                    {
                        ConsolePrintLine(Brushes.Yellow, "##Lost connection");
                        return;
                    }
                    if (BtConn.BytesToRead > 0)
                    {
                        Receive();
                    }
                    System.Threading.Thread.Sleep(100); // throttle
                }
            }
            catch (ThreadAbortException)
            {
            }
        }

        void Receive()
        {
            StringBuilder t = new StringBuilder();
            int bytesRead = BtConn.Read(receiveBuff, 0, receiveBuff.Length);
            int msgLen = receiveBuff[0] + (receiveBuff[1] * 256); // just FYI

            if (isNxtMail)
            {
                // mailboxes empty  ?
                if (receiveBuff[4] == 0) // status ok
                    DoGetMail();
                else
                    if (receiveBuff[4] == 0xEC || receiveBuff[4] == 0x40)
                    {
                        Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
                        {
                            MainWindow.MessageText = StatusCodeToString(receiveBuff[4]);
                        }));

                        return;   // no active program or no mail
                    }
                    else
                    {
                        InvokedPrintLine(DefaultColor, string.Format("##unexpected status from in NxtMail: {0}", StatusCodeToString(receiveBuff[4])));
                        DisplayBytes(receiveBuff, bytesRead);
                        return;
                    }
            }
            else
            {
                DisplayBytes(receiveBuff, bytesRead);   // +++ for now, dump the line
                //while (receiveBuff[i] != 0x0)
                //    t.Append((char)receiveBuff[i++]);   // not mail, print it as a string
                //InvokedPrintLine(Brushes.Gray, t.ToString());
            }        
        }

        public void Closing()
        {
            if (BtConn.IsOpen)
                BtConn.Close();
            if (ReceiveThread != null)
                ReceiveThread.Abort();
        }

        string StatusCodeToString(byte c)
        {
            if (StatusCodeMap == null)
                StatusCodeMapInit();
            if (StatusCodeMap.ContainsKey(c))
                return StatusCodeMap[c];
            else
                return string.Format("invalid status code {02X}", c);
        }

        void StatusCodeMapInit()
        {
            StatusCodeMap = new Dictionary<byte, string>();
            StatusCodeMap.Add(0x20, "Pending communication transaction in progress");
            StatusCodeMap.Add(0x40, "Specified mailbox queue is empty");
            StatusCodeMap.Add(0xBD, "Request failed (i.e. specified file not found)");
            StatusCodeMap.Add(0xBE, "Unknown command opcode");
            StatusCodeMap.Add(0xC0, "Data contains out-of-range values");
            StatusCodeMap.Add(0xDD, "Communication bus error ");
            StatusCodeMap.Add(0xDE, "No free memory in communication buffer");
            StatusCodeMap.Add(0xDF, "Specified channel/connection is not valid");
            StatusCodeMap.Add(0xE0, "Specified channel/connection not configured or busy");
            StatusCodeMap.Add(0xEC, "No active program");
            StatusCodeMap.Add(0xED, "Illegal size specified");
            StatusCodeMap.Add(0xEE, "Illegal mailbox queue ID specified");
            StatusCodeMap.Add(0xEF, "Attempted to access invalid field of a structure");
            StatusCodeMap.Add(0xF0, "Bad input or output specified");
            StatusCodeMap.Add(0xFB, "Insufficient memory available");
            StatusCodeMap.Add(0xFF, "Bad arguments");
        }

        private void DisplayBytes(byte[] Buff, int Length)
        {
            StringBuilder displayLines = new StringBuilder();
            for (int i = 0; i < Length; i++)
            {
                if ((i) % 24 == 0)
                    displayLines.Append('\n');
                else if ((i) % 4 == 0)
                    displayLines.Append(' ');
                displayLines.Append(Buff[i].ToString("X2"));
            }

            if ((Length + 1) % 24 != 0)
                displayLines.Append('\n');

            string[] lines = displayLines.ToString().Split('\n');
            foreach (string line in lines)
                InvokedPrintLine(Brushes.Gray, line);
        }

        void DoGetMail()
        {
            string t;
            Brush color = Brushes.Gray;

            int textLength = receiveBuff[6] - 1; //cause it includes terminating 0x00
            StringBuilder s = new StringBuilder(textLength);

            Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
            {
                MainWindow.MessageText = string.Format("you got NXTG mail length({0}):", textLength);
            }));

            for (int i = 0; i < textLength; i++)
                s.Append((char)receiveBuff[7 + i]);
            t = s.ToString();

            if (t.Substring(0, 1) == "]")
            {
                Console.Clear();
                return;
            }

            // if the second character is a ':' assume the first is a msg level
            if (t.Substring(1, 1) == ":")
            {
                switch (t.Substring(0, 1))
                {
                    case "W":
                        color = Brushes.Yellow;
                        break;
                    case "I":
                        color = Brushes.Cyan;
                        break;
                    case "E":
                        color = Brushes.Red;
                        break;
                }
            }

            InvokedPrintLine(color, t);
        }

        public void ConsolePrintLine(Brush Color, string T)
        {
            ScrollViewer sv;
            TextBlock t = new TextBlock();
            t.Text = T;
            t.Foreground = Color;
            int i = listBox.Items.Add(t);
            sv = FindVisualChild<ScrollViewer>(listBox);
            if (sv != null)
                sv.ScrollToBottom();
        }

        void InvokedPrintLine(Brush color, string t)    // run on the GUI thread
        {
            Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
            {
                ConsolePrintLine(color, t);
            }));
        }

        // helps to find if scrollbar in a listbox
        childItem FindVisualChild<childItem>(DependencyObject obj) where childItem : DependencyObject
        {
            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
            {
                DependencyObject child = VisualTreeHelper.GetChild(obj, i);
                if (child != null && child is childItem)
                {
                    return (childItem)child;
                }
                else
                {
                    childItem childOfChild = FindVisualChild<childItem>(child);
                    if (childOfChild != null)
                    {
                        return childOfChild;
                    }
                }
            }
            return null;
        }
    }
}
When I execute

Image

in NXT-G and call the GetMail function, messages arrive depending on timing (some get lost).

Now when I execute this NXC program

Image

I receive 6 bytes of data and the NXT get unresponsive. By that I mean I can not run the program again and see any data. And even turning the NXT off delays several minutes before it actually disconnects from USB. Menus still work, but that is about it.

So seeing how the PC code runs fine for one program, and the NXC code snippet is tiny and cause brick failure, I am at a loss.

Any help would be appreciated.

Mike

PS - did update the 'length' in the send buffer to 6 - that was an earlier screen shot in the first pic and wasn't the code that was running anyhow.
afanofosc
Site Admin
Posts: 1256
Joined: 26 Sep 2010, 19:36
Location: Nashville, TN
Contact:

Re: BluetoothWrite - locking up NXT

Post by afanofosc »

You should not be changing the Bluetooth data mode. Did someone tell you to do that? All bluetooth communication should be done in the firmware's default mode (DATA_MODE_NXT). The firmware adds the two bytes of length to every bit of data that you send out via Bluetooth so you should not add them yourself. If you want to write "Hello" then just write "Hello" as a string using BluetoothWrite.

What happens when you write your messages in NXC using SendRemoteString?

John Hansen
Multi-platform LEGO MINDSTORMS programming
http://bricxcc.sourceforge.net/
afanofosc
Site Admin
Posts: 1256
Joined: 26 Sep 2010, 19:36
Location: Nashville, TN
Contact:

Re: BluetoothWrite - locking up NXT

Post by afanofosc »

BTW, if you aren't sure how to update to the latest compiler just ask. There's no need to figure it out on your own. Install the latest official release (3.3.8.9) and then extract the latest test release from http://bricxcc.sourceforge.net/test_releases/ over the existing install folder. If you still have problems with errors and line numbers please post a simple program that demonstrates the problem and I will try to fix it.

I took a closer look at your NXT-G program. It looks like you are a little confused about the whole "mailbox" and "connection" thing. You are wiring into the Connection slot the value read from a variable that you called "Mailbox". The mailbox slot on the send message block is the second one down with the # symbol and the open envelope icon. If your PC creates the connection to the NXT then it will be on connection 0. If the NXT creates the connection then the PC will be on connection 1, 2, or 3 (probably 1). The send message blocks in your program appear to all be configured for mailbox 1 but 3 of the 4 send message blocks show connection 0 while the first of the 4 blocks shows what appears to be connection 1 when I zoom way in on the image.

Questions:

1) is your PC the master or a slave, i.e., does it establish the connection or does the NXT establish the connection. You can see what connection slot your PC is in via the on-brick menu system.

2) Does your PC program poll for data over the bluetooth connection by sending a MessageRead direct command or does it simply read raw bytes from the open port?

The second question is important because the last 3 send message blocks which all appear to be sending data to connection 0 will do no actual bluetooth sending. All this block does in NXT-G when you tell it to send a message to connection zero is write the message to a response mailbox (the specified mailbox number + 10). So the only way you could receive the messages on your PC that were sent by the last 3 send message blocks is if your PC is polling for data, i.e., sending a MessageRead direct command to the NXT and then processing its response. The first block which appears to be configured to send over connection 1 would do nothing if your PC is actually the master (i.e., connected in slot 0) or if your PC is actually connected as a slave to your NXT in slot 1 then this block would actually call the BluetoothWrite system call and it would send out a MessageWrite direct command to your PC. If your PC is connected in slot 1 then all 3 other send message blocks would do nothing at all.

John Hansen
Multi-platform LEGO MINDSTORMS programming
http://bricxcc.sourceforge.net/
afanofosc
Site Admin
Posts: 1256
Joined: 26 Sep 2010, 19:36
Location: Nashville, TN
Contact:

Re: BluetoothWrite - locking up NXT

Post by afanofosc »

FYI, the OP is really mad at me for trying to help him and for using a horrible programming language like Delphi. And for being from Tennessee. LOL.

Oh, well.

I didn't bother to look at his C# code, btw, since from what he had said previously he had given me the impression that it was simply opening a serial port and reading data from it. Apparently that is not the case. He's polling using a MessageRead direct command, he tells me via a nasty PM. I don't know how he thought that he would get a successful response from an NXC program that was not simply writing to a local mailbox using MessageWrite. But that's not bluetooth communication and I had wrongly gotten the impression that he wanted his programs to send data over an existing bluetooth connection rather than just writing to a local mailbox.

John Hansen
Multi-platform LEGO MINDSTORMS programming
http://bricxcc.sourceforge.net/
afanofosc
Site Admin
Posts: 1256
Joined: 26 Sep 2010, 19:36
Location: Nashville, TN
Contact:

Re: BluetoothWrite - locking up NXT

Post by afanofosc »

And I guess while I am at it I will publicly apologize to the OP for saying to him in a PM

"It sounds like you should switch to RobotC or write your own IDE and compiler and documentation since mine are such cr[ud]". (lightly edited to avoid offending anyone)

He was quite upset that I thought that he thought that my work was sub-par. I guess he was just trying to give me some constructive criticism and I did not take it well - possibly due to the fact that I just don't have a good side and that if I'm not careful and lose my current job that I don't stand a chance anywhere else. He may well be right about that.

John Hansen
Multi-platform LEGO MINDSTORMS programming
http://bricxcc.sourceforge.net/
Post Reply

Who is online

Users browsing this forum: Semrush [Bot] and 0 guests