FEZ is Sick!

I must compliment the GHI team on a wonderful platform!

My task was to create a Bluetooth wireless link to a bi-directional USB printer. I was able to make a functioning prototype in about 2 days and this included learning the USB specification for printers.

After it was working I had to pinch myself…it was too easy!

I hate to think about how long that would have taken me without the USBizi chip. Now I just have to wait for the hibernate functionality to be included in a new release and I can conquer the world!!!

Thanks again. -AP

Glad to hear you like FEZ (USBizi). Power modes is next on the list…we need 4.1 and VS2010 first :wink:

And that’s exactly why we are such hardcore FEZ/GHI fans :smiley:

I don’t know other NetMF companies/boards but I have to admit that this one and the people who are involved (GHI/FEZ) are very good. :slight_smile:

@ Andrew2

Could you share the code with others and post it here ? I think that some people like me would like to have a look or use some parts of it.

I spend so much time on this forum for a reason :wink:

I love my Domino and Cobra. Easily some of the best MCU’s I have ever had the pleasure of using and Visual Studio makes for a really bulletproof IDE.

You asked for it…

Here is a program file. It’s still pretty much a shell, but it functions well. The purpose of this routine is to take the printer language (in this case a manufacturers propritary set not unlike PCL or ESC/POS) and port it through a bluetooth link via SPP and to a printer that utilizes a USB connection. The printer protocol requires bi-directional data because the printer sends back a variety of status data and fonts etc etc. The USB Printer class would not work for this as it only sends back IEEE 1284 printer status (parallel port emulation).

Now some might ask why not just use a serial printer…well the problem is that serial printers tend to communicate at slower baud rates. With this setup I can communicate at 921.6kbps over bluetooth to the FEZ and from there at the full USB 2.0 speeds which are much faster.

Next I’d like to use the bluetooth module (or one from a different manufacturer) and utilize some other method of data transfer to get up to 3mbps. Have not really explored this fully yet, but it seems plausable. Anybody have experience with this?

using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using System;
using System.IO.Ports;
using System.Threading;
using System.Text;
using GHIElectronics.NETMF.FEZ;
using GHIElectronics.NETMF.USBHost;
using GHIElectronics.NETMF.System; 
using GHIElectronics.NETMF.Hardware;

namespace innoagg
{
    public class Program
    {
        //pointersd
        static SerialPort UART; //serial port
        static USBH_RawDevice USB; //usb device
        static USBH_RawDevice.Pipe USB_PIPE_IN; //data pipe
        static USBH_RawDevice.Pipe USB_PIPE_OUT; //data pipe
        static Thread USB_Data_In_Handler;  //thread for data to the device

        //set DIO's
        static OutputPort DTR = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.UEXT9, true); //dtr pin output to serial device
        static InterruptPort DSR = new InterruptPort((Cpu.Pin)FEZ_Pin.Digital.UEXT10, false, Port.ResistorMode.PullUp, Port.InterruptMode.InterruptEdgeBoth); //dsr input pin from serial device
        static OutputPort LED_Act = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.LED, false); //board LED
        
        //flags
        static bool COMMAND_MODE = false; //command mode status flag
        const int COMMAND_MODE_TIMEOUT = 2000; //command input time window
        Thread CMD_TIMEOUT; //allows for command input window

        //usb sepcification constants
        const byte bRequest_GET_STATUS = 0x00;
        const byte bRequest_CLEAR_FEATURE = 0x01;
        const byte bRequest_SET_FEATURE = 0x03;
        const byte bRequest_SET_ADDRESS = 0x05;
        const byte bRequest_GET_DESCRIPTOR = 0x06;
        const byte bRequest_SET_DESCRIPTOR = 0x07;
        const byte bRequest_GET_CONFIGURATION = 0x08;
        const byte bRequest_SET_CONFIGURATION = 0x09;
        const byte bRequest_GET_INTERFACE = 0x0A;
        const byte bRequest_SET_INTERFACE = 0x0B;
        const byte bRequest_SYNCH_FRAME = 0x0C;
        const byte bRequest_GET_PORT_STATUS = 0x01;
        const byte bmRequestType_GET_PORT_STATUS = 0xA1;

        //data handling settings
        const int USB_CHUNK_SIZE = 4 * 1024;
        const int USB_CHUNK_TIMEOUT = 500;
        const int USB_CHUNK_RETRIES = 5;
        const int USB_CHUNK_NORMAL_DELAY = 0;
        const int USB_CHUNK_FAILED_DELAY = 250;
        const int USB_READ_INTERVAL = 100;

        const int UART_CHUNK_SIZE = 8;
        const int UART_CHUNK_TIMEOUT = 500;
        const int UART_CHUNK_RETRIES = 5;
        const int UART_CHUNK_NORMAL_DELAY = 0;
        const int UART_CHUNK_FAILED_DELAY = 250;

        const bool PIN_ON = false;
        const bool PIN_OFF = true;


        public static void Mainx()
        {
            DTR.Write(true);
            DTR.Write(false);
            DTR.Write(true);
        }

        public static void Main()
        {


            //subscribe to usb host events
            USBHostController.DeviceConnectedEvent += OnDeviceConnected;
            USBHostController.DeviceDisconnectedEvent += OnDeviceDisconnected;
            
            try
            {
                //configure the UART connection
                UART = new SerialPort("COM2");
                UART.BaudRate = 230400;
                UART.DataBits = 8;
                UART.Parity = Parity.None;
                UART.StopBits = StopBits.One;
                UART.Handshake = Handshake.RequestToSend;
                UART.Open();
                if (UART.IsOpen)
                {

                    //subscribe to UART events
                    UART.DataReceived += new SerialDataReceivedEventHandler(OnRecvUARTData);
                    UART.ErrorReceived += new SerialErrorReceivedEventHandler(OnUARTError);

                    Debug.Print("COM Port Opened");

                    //set the event handler for DSR
                    DSR.OnInterrupt += new NativeEventHandler(OnDSR);
                }

            }
            catch (Exception ex)
            {
                Debug.Print(ex.Message + "\r\n" + ex.ToString());
            }

            LED_Act.Write(false);
            Thread.Sleep(Timeout.Infinite);
        }

        static void OnDeviceDisconnected(USBH_Device device)
        {
            Debug.Print("Device disconnected");
            if (USB_Data_In_Handler.ThreadState == ThreadState.Running)
            {
                USB_Data_In_Handler.Abort();
            }
            DTR.Write(PIN_OFF);
        }

        static void OnDeviceConnected(USBH_Device device)
        {
            //configure the USB Host connection to device
            //create the device
            Debug.Print("Device Detected");

            try
            {
                //check for cases not to attempt a device connection

                //if there is no UART open at this point then no sense connecting to USB.
                while (!UART.IsOpen)
                {
                    Thread.Sleep(100); //sleep a little to see if we can break out here...
                    if (UART.IsOpen) { break; }
                }

                if (device.TYPE == USBH_DeviceType.Printer)
                {
                    Debug.Print("Printer Detected");
                    USB = new USBH_RawDevice(device);

                    // Get descriptors
                    //USB_Descriptors.DeviceDescriptor dd = USB.GetDeviceDescriptor();
                    USB_Descriptors.ConfigurationDescriptor cd = USB.GetConfigurationDescriptors(0);

                    // communication endpoints
                    USB_Descriptors.EndpointDescriptor USB_EP_IN = null; //for data from device
                    USB_Descriptors.EndpointDescriptor USB_EP_OUT = null; //for data to device

                    // BULK OUT
                    USB_EP_OUT = cd.interfaces[0].endpoints[1];   // get endpoint
                    USB_PIPE_OUT = USB.OpenPipe(USB_EP_OUT);
                    USB_PIPE_OUT.TransferTimeout = USB_CHUNK_TIMEOUT;
                    Debug.Print("BULK OUT pipe set");

                    // BULK IN
                    USB_EP_IN = cd.interfaces[0].endpoints[0];   // get endpoint
                    USB_PIPE_IN = USB.OpenPipe(USB_EP_IN);
                    USB_PIPE_IN.TransferTimeout = USB_CHUNK_TIMEOUT;
                    Debug.Print("BULK IN pipe Set");

                    //set configuration
                    USB.SendSetupTransfer(0x00, bRequest_SET_CONFIGURATION, cd.bConfigurationValue, 0x00);

                    //setup thread to handle data inbound from device
                    USB_Data_In_Handler = new Thread(usb_DataIn);
                    USB_Data_In_Handler.Priority = ThreadPriority.Normal;

                    //flush the buffers before we indicate ready
                    UART.DiscardInBuffer();
                    UART.DiscardOutBuffer();
                    UART.Flush();

                    //signal to the PC host that the device is OK with DTR signal
                    DTR.Write(PIN_ON);

                    //start the usb pipe in handler
                    USB_Data_In_Handler.Start();
                }
            }
            catch (Exception ex)
            {
                Debug.Print(ex.Message + "\r\n" + ex.ToString());
            }
            LED_Act.Write(false);
        }

        static void usb_dataOut(byte[] data)
        {
            //check for condition where a write operation can't happen
            if (DTR.Read())
            {
                Debug.Print("Can't write data to USB pipe when device is not ready");
                return;
            }

            //send the data out to the usb bulk out pipe
            LED_Act.Write(true);
            try
            {
                Debug.Print("Sending " + data.Length + " bytes out USB out pipe");
                int offset = 0;
                int retries = USB_CHUNK_RETRIES;
                while (offset < data.Length && retries > 0)
                {
                    int count = System.Math.Min(USB_CHUNK_SIZE, data.Length - offset);
                    int chunkEnd = offset + count;

                    retries = USB_CHUNK_RETRIES;
                    while (offset < chunkEnd && retries-- > 0)
                    {
                        int writeCount = 0;
                        try
                        {
                            writeCount += USB_PIPE_OUT.TransferData(data, offset, chunkEnd - offset);
                            offset += writeCount;
                        }
                        catch { LED_Act.Write(false); }

                        if (writeCount == 0)
                        {
                            Debug.Print("USB chunk failed to send at offset " + offset.ToString() + ", len " + (chunkEnd - offset).ToString());
                            Thread.Sleep(USB_CHUNK_FAILED_DELAY);
                        }
                        else
                        {
                            Thread.Sleep(USB_CHUNK_NORMAL_DELAY);
                        }
                    }
                }

                if (retries > 0)
                {
                    Debug.Print("out bulk completed");
                }
                else
                {
                    Debug.Print("out bulk failed");
                }
            }
            catch (Exception ex)
            {
                Debug.Print(ex.Message + "\r\n" + ex.ToString());

            }
            LED_Act.Write(false);
        }

        static void uart_datOut(byte[] data)
        {
            //send data out the UART to the host PC
            LED_Act.Write(true);
            try
            {
                int offset = 0;
                int retries = UART_CHUNK_RETRIES;
                while (offset < data.Length && retries > 0)
                {
                    int count = System.Math.Min(UART_CHUNK_SIZE, data.Length - offset);
                    int chunkEnd = offset + count;
                    Debug.Print("Sending " + count + " bytes out UART");

                    retries = UART_CHUNK_RETRIES;
                    while (offset < chunkEnd && retries-- > 0)
                    {
                        int writeCount = 0;
                        try
                        {
                            writeCount = UART.Write(data, offset, chunkEnd - offset);
                            offset += writeCount;
                        }
                        catch { LED_Act.Write(false); }

                        if (writeCount == 0)
                        {
                            Debug.Print("UART chunk failed to send at offset " + offset.ToString() + ", len " + (chunkEnd - offset).ToString());
                            Thread.Sleep(UART_CHUNK_FAILED_DELAY);
                        }
                        else
                        {
                            Thread.Sleep(UART_CHUNK_NORMAL_DELAY);
                        }
                    }
                }

                if (retries > 0)
                {
                    Debug.Print("uart out completed");
                }
                else
                {
                    Debug.Print("uart out failed");
                }
            }
            catch (Exception ex)
            {
                Debug.Print(ex.Message + "\r\n" + ex.ToString());

            }
            LED_Act.Write(false);
        }

        static void usb_DataIn()
        {
            //data returned on the BULK IN pipe gets sent out the UART
            //Maximum data is wMaxPacketSize
            byte[] usbData = new byte[USB_PIPE_IN.PipeEndpoint.wMaxPacketSize];
            int count = 0;

            //Read every USB_READ_INTERVAL
            while (true)
            {
                Thread.Sleep(USB_READ_INTERVAL);

                try
                {
                    count = USB_PIPE_IN.TransferData(usbData, 0, usbData.Length);
                }
                catch (Exception ex)
                {
                    //usbData = null;
                    Debug.Print(ex.Message + "\r\n" + ex.ToString());
                }

                //TODO: process the outbound data with an additional formatted status bytes
                //from various sensors and local processing
                //need to devise a method to make multiple channels of data
                
           
                if (UART.IsOpen)
                {
                    if (count > 0)
                    {
                        try
                        {
                            byte[] pData = new byte[count];
                            Array.Copy(usbData, pData, count);
                            uart_datOut(pData);
                            pData = null;
                            
                            Debug.Print("Data Relayed");
                        }
                        catch (Exception ex)
                        {
                            Debug.Print(ex.Message + "\r\n" + ex.ToString());
                        }
                    }
                } 
            }
        }

        static void OnDSR(uint port, uint state, DateTime time)
        {
            Debug.Print("DSR State:" + state.ToString());
            //use this as a trigger to toggle command mode

            if (state == 1) //PC host disconnect
            {
                COMMAND_MODE = true;
                Debug.Print("Command mode ON");
                Thread CMD_TIMEOUT = new Thread(ResetCommandMode);
                CMD_TIMEOUT.Start();
            }
        }

        static void ResetCommandMode()
        {
            Thread.Sleep(COMMAND_MODE_TIMEOUT);
            COMMAND_MODE = false;
            Debug.Print("Command mode OFF");
        }
       
        static void OnRecvUARTData(object sender, SerialDataReceivedEventArgs e)
        {
            //Process inbound UART data from PC host.
            try
            {
                byte[] inBuffer = new byte[UART.BytesToRead];
                int count = 0;
                
                //check for command mode status based on DSR pin
                if (COMMAND_MODE)
                {
                    //read command
                    count = UART.Read(inBuffer, 0, inBuffer.Length);
                    if (count > 0) 
                    {
                        //echo command the data back to PC host
                        UART.Write(inBuffer, 0, inBuffer.Length);

                        //TODO: add command processing here
                    }
                } 
                else
                {
                    //Relay data to the USB host processor
                    count = UART.Read(inBuffer, 0, inBuffer.Length);
                    if (count > 0) {usb_dataOut(inBuffer);}
                }
            }
            catch (Exception ex) 
            { 
                Debug.Print(ex.Message + "\r\n" + ex.ToString());
            }
        }

        static void OnUARTError(object sender, SerialErrorReceivedEventArgs e)
        {
            Debug.Print("UART Error");
        }
    }
}